Skip to content

Commit 7137834

Browse files
authored
feat: add signExtraSignersPayload to sign extra signers payloads in Transaction class. (#713)
1 parent 2c3b28e commit 7137834

File tree

3 files changed

+248
-0
lines changed

3 files changed

+248
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Update:
66
- feat: add `isValidSignedPayload` to `StrKey` class, this function can be used to validate the ed25519 signed payload. ([#712](https://github.com/stellar/java-stellar-sdk/pull/712))
7+
- feat: add `signExtraSignersPayload` to sign extra signers payloads in `Transaction` class. ([#713](https://github.com/stellar/java-stellar-sdk/pull/713))
78

89
### Breaking changes:
910
- feat: add `org.stellar.sdk.SignerKey` for enhanced signer key handling. ([#712](https://github.com/stellar/java-stellar-sdk/pull/712))

src/main/java/org/stellar/sdk/Transaction.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.stellar.sdk.xdr.HashIDPreimage;
1919
import org.stellar.sdk.xdr.Int64;
2020
import org.stellar.sdk.xdr.SequenceNumber;
21+
import org.stellar.sdk.xdr.SignerKeyType;
2122
import org.stellar.sdk.xdr.SorobanTransactionData;
2223
import org.stellar.sdk.xdr.TransactionEnvelope;
2324
import org.stellar.sdk.xdr.TransactionSignaturePayload;
@@ -171,6 +172,30 @@ public String getClaimableBalanceId(int index) {
171172
}
172173
}
173174

175+
/**
176+
* Signs the extra signers payloads for this transaction using the provided signer.
177+
*
178+
* @param signer the KeyPair of the signer to use for signing the extra signers payloads.
179+
*/
180+
public void signExtraSignersPayload(@NonNull KeyPair signer) {
181+
if (preconditions == null || preconditions.getExtraSigners().isEmpty()) {
182+
// No extra signers to sign for this transaction
183+
return;
184+
}
185+
for (SignerKey extraSigner : preconditions.getExtraSigners()) {
186+
if (extraSigner.getType() != SignerKeyType.SIGNER_KEY_TYPE_ED25519_SIGNED_PAYLOAD) {
187+
continue;
188+
}
189+
SignerKey.Ed25519SignedPayload ed25519SignedPayload = extraSigner.toEd25519SignedPayload();
190+
if (!Arrays.equals(ed25519SignedPayload.getEd25519PublicKey(), signer.getPublicKey())) {
191+
// This extra signer is not the one we are signing for
192+
continue;
193+
}
194+
DecoratedSignature signature = signer.signPayloadDecorated(ed25519SignedPayload.getPayload());
195+
signatures.add(signature);
196+
}
197+
}
198+
174199
/** Generates Transaction XDR object. */
175200
private TransactionV0 toXdr() {
176201
// fee

src/test/java/org/stellar/sdk/TransactionTest.java

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -738,4 +738,226 @@ public void testIntegrationPaymentToContractTransactionWithDifferentSource() thr
738738

739739
server.close();
740740
}
741+
742+
@Test
743+
public void testSignExtraSignersPayloadOneExtraSigner() {
744+
KeyPair source =
745+
KeyPair.fromSecretSeed("SCCS5ZBI7WVIJ4SW36WGOQQIWJYCL3VOAULSXX3FB57USIO25EDOYQHH");
746+
KeyPair signer1 =
747+
KeyPair.fromSecretSeed("SDHOAMBNLGCE2MV5ZKIVZAQD3VCLGP53P3OBSBI6UN5L5XZI5TKHFQL4");
748+
String destination = "GDJJRRMBK4IWLEPJGIE6SXD2LP7REGZODU7WDC3I2D6MR37F4XSHBKX2";
749+
BigDecimal amount = new BigDecimal("1000.0");
750+
long sequence = 1;
751+
long fee = 200;
752+
Asset asset = new AssetTypeNative();
753+
754+
List<SignerKey> extraSigners = new ArrayList<>();
755+
extraSigners.add(
756+
SignerKey.fromEd25519SignedPayload(signer1.getAccountId(), "cat!!!".getBytes()));
757+
TransactionPreconditions cond =
758+
TransactionPreconditions.builder().extraSigners(extraSigners).build();
759+
760+
PaymentOperation op =
761+
PaymentOperation.builder().destination(destination).asset(asset).amount(amount).build();
762+
763+
Transaction tx =
764+
new Transaction(
765+
source.getAccountId(),
766+
fee,
767+
sequence,
768+
new org.stellar.sdk.operations.Operation[] {op},
769+
null,
770+
cond,
771+
null,
772+
Network.PUBLIC);
773+
774+
tx.signExtraSignersPayload(signer1);
775+
assertEquals(1, tx.getSignatures().size());
776+
assertEquals(tx.getSignatures().get(0), signer1.signPayloadDecorated("cat!!!".getBytes()));
777+
}
778+
779+
@Test
780+
public void testSignExtraSignersPayloadTwoExtraSigners() {
781+
KeyPair source =
782+
KeyPair.fromSecretSeed("SCCS5ZBI7WVIJ4SW36WGOQQIWJYCL3VOAULSXX3FB57USIO25EDOYQHH");
783+
KeyPair signer1 =
784+
KeyPair.fromSecretSeed("SDHOAMBNLGCE2MV5ZKIVZAQD3VCLGP53P3OBSBI6UN5L5XZI5TKHFQL4");
785+
String destination = "GDJJRRMBK4IWLEPJGIE6SXD2LP7REGZODU7WDC3I2D6MR37F4XSHBKX2";
786+
BigDecimal amount = new BigDecimal("1000.0");
787+
long sequence = 1;
788+
long fee = 200;
789+
Asset asset = new AssetTypeNative();
790+
791+
List<SignerKey> extraSigners = new ArrayList<>();
792+
extraSigners.add(
793+
SignerKey.fromEd25519SignedPayload(signer1.getAccountId(), "cat!!!".getBytes()));
794+
extraSigners.add(SignerKey.fromEd25519SignedPayload(signer1.getAccountId(), "cat".getBytes()));
795+
796+
TransactionPreconditions cond =
797+
new TransactionPreconditions(null, null, null, BigInteger.ZERO, 0, extraSigners);
798+
799+
PaymentOperation op =
800+
PaymentOperation.builder().destination(destination).asset(asset).amount(amount).build();
801+
802+
Transaction tx =
803+
new Transaction(
804+
source.getAccountId(),
805+
fee,
806+
sequence,
807+
new org.stellar.sdk.operations.Operation[] {op},
808+
null,
809+
cond,
810+
null,
811+
Network.PUBLIC);
812+
813+
tx.signExtraSignersPayload(signer1);
814+
assertEquals(2, tx.getSignatures().size());
815+
assertEquals(tx.getSignatures().get(0), signer1.signPayloadDecorated("cat!!!".getBytes()));
816+
assertEquals(tx.getSignatures().get(1), signer1.signPayloadDecorated("cat".getBytes()));
817+
}
818+
819+
@Test
820+
public void testSignExtraSignersPayloadTwoExtraSignersNotSameSigner() {
821+
KeyPair source =
822+
KeyPair.fromSecretSeed("SCCS5ZBI7WVIJ4SW36WGOQQIWJYCL3VOAULSXX3FB57USIO25EDOYQHH");
823+
KeyPair signer1 =
824+
KeyPair.fromSecretSeed("SDHOAMBNLGCE2MV5ZKIVZAQD3VCLGP53P3OBSBI6UN5L5XZI5TKHFQL4");
825+
KeyPair signer2 =
826+
KeyPair.fromSecretSeed("SAUQGMBSYKZ73CZCVEOE44Z7FDSZWP4VORDVQMGOTBADVT3K5SQXBFTY");
827+
String destination = "GDJJRRMBK4IWLEPJGIE6SXD2LP7REGZODU7WDC3I2D6MR37F4XSHBKX2";
828+
BigDecimal amount = new BigDecimal("1000.0");
829+
long sequence = 1;
830+
long fee = 200;
831+
Asset asset = new AssetTypeNative();
832+
833+
List<SignerKey> extraSigners = new ArrayList<>();
834+
extraSigners.add(
835+
SignerKey.fromEd25519SignedPayload(signer1.getAccountId(), "cat!!!".getBytes()));
836+
extraSigners.add(SignerKey.fromEd25519SignedPayload(signer2.getAccountId(), "cat".getBytes()));
837+
838+
TransactionPreconditions cond =
839+
new TransactionPreconditions(null, null, null, BigInteger.ZERO, 0, extraSigners);
840+
841+
PaymentOperation op =
842+
PaymentOperation.builder().destination(destination).asset(asset).amount(amount).build();
843+
844+
Transaction tx =
845+
new Transaction(
846+
source.getAccountId(),
847+
fee,
848+
sequence,
849+
new org.stellar.sdk.operations.Operation[] {op},
850+
null,
851+
cond,
852+
null,
853+
Network.PUBLIC);
854+
855+
tx.signExtraSignersPayload(signer1);
856+
assertEquals(1, tx.getSignatures().size());
857+
assertEquals(tx.getSignatures().get(0), signer1.signPayloadDecorated("cat!!!".getBytes()));
858+
859+
tx.signExtraSignersPayload(signer2);
860+
assertEquals(2, tx.getSignatures().size());
861+
assertEquals(tx.getSignatures().get(1), signer2.signPayloadDecorated("cat".getBytes()));
862+
}
863+
864+
@Test
865+
public void testSignExtraSignersPayloadNoExtraSigner() {
866+
KeyPair source =
867+
KeyPair.fromSecretSeed("SCCS5ZBI7WVIJ4SW36WGOQQIWJYCL3VOAULSXX3FB57USIO25EDOYQHH");
868+
KeyPair signer1 =
869+
KeyPair.fromSecretSeed("SDHOAMBNLGCE2MV5ZKIVZAQD3VCLGP53P3OBSBI6UN5L5XZI5TKHFQL4");
870+
String destination = "GDJJRRMBK4IWLEPJGIE6SXD2LP7REGZODU7WDC3I2D6MR37F4XSHBKX2";
871+
BigDecimal amount = new BigDecimal("1000.0");
872+
long sequence = 1;
873+
long fee = 200;
874+
Asset asset = new AssetTypeNative();
875+
876+
TransactionPreconditions cond =
877+
new TransactionPreconditions(
878+
null, null, null, BigInteger.ZERO, 0, new ArrayList<SignerKey>());
879+
880+
PaymentOperation op =
881+
PaymentOperation.builder().destination(destination).asset(asset).amount(amount).build();
882+
883+
Transaction tx =
884+
new Transaction(
885+
source.getAccountId(),
886+
fee,
887+
sequence,
888+
new org.stellar.sdk.operations.Operation[] {op},
889+
null,
890+
cond,
891+
null,
892+
Network.PUBLIC);
893+
894+
tx.signExtraSignersPayload(signer1);
895+
assertEquals(0, tx.getSignatures().size());
896+
}
897+
898+
@Test
899+
public void testSignExtraSignersCondIsNull() {
900+
KeyPair source =
901+
KeyPair.fromSecretSeed("SCCS5ZBI7WVIJ4SW36WGOQQIWJYCL3VOAULSXX3FB57USIO25EDOYQHH");
902+
KeyPair signer1 =
903+
KeyPair.fromSecretSeed("SDHOAMBNLGCE2MV5ZKIVZAQD3VCLGP53P3OBSBI6UN5L5XZI5TKHFQL4");
904+
String destination = "GDJJRRMBK4IWLEPJGIE6SXD2LP7REGZODU7WDC3I2D6MR37F4XSHBKX2";
905+
BigDecimal amount = new BigDecimal("1000.0");
906+
long sequence = 1;
907+
long fee = 200;
908+
Asset asset = new AssetTypeNative();
909+
910+
PaymentOperation op =
911+
PaymentOperation.builder().destination(destination).asset(asset).amount(amount).build();
912+
913+
Transaction tx =
914+
new Transaction(
915+
source.getAccountId(),
916+
fee,
917+
sequence,
918+
new org.stellar.sdk.operations.Operation[] {op},
919+
null,
920+
null,
921+
null,
922+
Network.PUBLIC);
923+
924+
tx.signExtraSignersPayload(signer1);
925+
assertEquals(0, tx.getSignatures().size());
926+
}
927+
928+
@Test
929+
public void testSignExtraSignersPayloadIsNotSignedExtraSigner() {
930+
KeyPair source =
931+
KeyPair.fromSecretSeed("SCCS5ZBI7WVIJ4SW36WGOQQIWJYCL3VOAULSXX3FB57USIO25EDOYQHH");
932+
KeyPair signer1 =
933+
KeyPair.fromSecretSeed("SDHOAMBNLGCE2MV5ZKIVZAQD3VCLGP53P3OBSBI6UN5L5XZI5TKHFQL4");
934+
String destination = "GDJJRRMBK4IWLEPJGIE6SXD2LP7REGZODU7WDC3I2D6MR37F4XSHBKX2";
935+
BigDecimal amount = new BigDecimal("1000.0");
936+
long sequence = 1;
937+
long fee = 200;
938+
Asset asset = new AssetTypeNative();
939+
940+
List<SignerKey> extraSigners = new ArrayList<>();
941+
extraSigners.add(SignerKey.fromEd25519PublicKey(signer1.getAccountId()));
942+
943+
TransactionPreconditions cond =
944+
new TransactionPreconditions(null, null, null, BigInteger.ZERO, 0, extraSigners);
945+
946+
PaymentOperation op =
947+
PaymentOperation.builder().destination(destination).asset(asset).amount(amount).build();
948+
949+
Transaction tx =
950+
new Transaction(
951+
source.getAccountId(),
952+
fee,
953+
sequence,
954+
new org.stellar.sdk.operations.Operation[] {op},
955+
null,
956+
cond,
957+
null,
958+
Network.PUBLIC);
959+
960+
tx.signExtraSignersPayload(signer1);
961+
assertEquals(0, tx.getSignatures().size());
962+
}
741963
}

0 commit comments

Comments
 (0)