3333import org .xrpl .xrpl4j .model .flags .TransactionFlags ;
3434import org .xrpl .xrpl4j .model .jackson .ObjectMapperFactory ;
3535import org .xrpl .xrpl4j .model .transactions .Address ;
36+ import org .xrpl .xrpl4j .model .transactions .Memo ;
37+ import org .xrpl .xrpl4j .model .transactions .MemoWrapper ;
3638import org .xrpl .xrpl4j .model .transactions .Payment ;
3739import org .xrpl .xrpl4j .model .transactions .XrpCurrencyAmount ;
3840
41+ import java .util .Arrays ;
42+ import java .util .Collections ;
43+
3944/**
4045 * Unit tests for {@link SingleSignedTransaction}.
4146 */
4247class SignedTransactionTest {
4348
4449 /**
45- * This test constructs the transaction found here:
46- * https://livenet.xrpl.org/transactions/A7AE53FE15B02E6E2F3C610FB4BA30B12392EB110F1D5E8C20880555E8639B05 to check
47- * that the hash that's on livenet matches what this library computes. The hash you see in this test is different than
48- * the hash found on livenet because the real transaction did not set any flags on the transaction and {@link Payment}
49- * requires a flags field (Even if you set flags to 0, it affects the hash). However, we made {@link Payment#flags()}
50- * nullable during development and verified that the hashes match, so we are confident that our hash calculation is
51- * accurate.
50+ * This test constructs the transaction with hash A7AE53FE15B02E6E2F3C610FB4BA30B12392EB110F1D5E8C20880555E8639B05 to
51+ * check that the hash that's on livenet matches what this library computes. The hash you see in this test is
52+ * different from the hash found on livenet because the real transaction did not set any flags on the transaction and
53+ * {@link Payment} requires a flags field (Even if you set flags to 0, it affects the hash). However, we made
54+ * {@link Payment#flags()} nullable during development and verified that the hashes match, so we are confident that
55+ * our hash calculation is accurate.
56+ *
57+ * @see "https://livenet.xrpl.org/transactions/A7AE53FE15B02E6E2F3C610FB4BA30B12392EB110F1D5E8C20880555E8639B05"
5258 */
5359 @ Test
5460 public void computesCorrectTransactionHash () throws JsonProcessingException {
@@ -65,31 +71,132 @@ public void computesCorrectTransactionHash() throws JsonProcessingException {
6571 .destinationTag (UnsignedInteger .valueOf (371969 ))
6672 .build ();
6773
74+ final Signature signature = Signature .fromBase16 (
75+ "304502210093257D8E88D2A92CE55977641F72CCD235AB76B1AE189BE3377F30A69B131C49" +
76+ "02200B79836114069F0D331418D05818908D85DE755AE5C2DDF42E9637FE1C11754F"
77+ );
78+
6879 final Payment signedPayment = Payment .builder ().from (unsignedTransaction )
69- .transactionSignature (Signature .fromBase16 (
70- "304502210093257D8E88D2A92CE55977641F72CCD235AB76B1AE189BE3377F30A6" +
71- "9B131C4902200B79836114069F0D331418D05818908D85DE755AE5C2DDF42E9637FE1C11754F"
80+ .transactionSignature (signature )
81+ .build ();
82+
83+ SingleSignedTransaction <Payment > signedTransaction = SingleSignedTransaction .<Payment >builder ()
84+ .signedTransaction (signedPayment )
85+ .signature (signature )
86+ .unsignedTransaction (unsignedTransaction )
87+ .build ();
88+
89+ String expectedHash = "F847C96B2EEB0609F16C9DB9D74A0CB123B5EAF5B626207977335BF0A1EF53C3" ;
90+ assertThat (signedTransaction .hash ().value ()).isEqualTo (expectedHash );
91+ assertThat (signedTransaction .unsignedTransaction ()).isEqualTo (unsignedTransaction );
92+ assertThat (signedTransaction .signedTransaction ()).isEqualTo (signedPayment );
93+ assertThat (signedTransaction .signedTransactionBytes ().hexValue ()).isEqualTo (
94+ XrplBinaryCodec .getInstance ().encode (ObjectMapperFactory .create ().writeValueAsString (signedPayment ))
95+ );
96+ }
97+
98+ /**
99+ * This test constructs the transaction with hash 1A1953AC3BA3123254AA912CE507514A6AAD05EED8981A870B45F604936F0997 to
100+ * check that the hash that's on livenet matches what this library computes.
101+ *
102+ * @see "https://livenet.xrpl.org/transactions/1A1953AC3BA3123254AA912CE507514A6AAD05EED8981A870B45F604936F0997"
103+ */
104+ @ Test
105+ public void computesCorrectTransactionHashWithUnsetFlags () throws JsonProcessingException {
106+ final Payment unsignedTransaction = Payment .builder ()
107+ .account (Address .of ("rGWx7VAsnwVKRbPFPpvy8Lo4nFf5xjj6Zb" ))
108+ .amount (XrpCurrencyAmount .ofDrops (1 ))
109+ .destination (Address .of ("rxRpSNb1VktvzBz8JF2oJC6qaww6RZ7Lw" ))
110+ .fee (XrpCurrencyAmount .ofDrops (12 ))
111+ .flags (PaymentFlags .of (TransactionFlags .UNSET .getValue ())) // 0
112+ .lastLedgerSequence (UnsignedInteger .valueOf (86481544 ))
113+ .memos (Collections .singletonList (
114+ MemoWrapper .builder ()
115+ .memo (Memo .builder ()
116+ .memoData ("7B226F70223A226D696E74222C22616D6F756E74223A22313030303030303030222C22677061223A2230227D" )
117+ .build ())
118+ .build ()
72119 ))
120+ .sequence (UnsignedInteger .valueOf (84987644 ))
121+ .signingPublicKey (
122+ PublicKey .fromBase16EncodedPublicKey ("ED05DC98B76FCD734BD44CDF153C34F79728485D2F24F9381CF7A284223EA258CE" )
123+ )
73124 .build ();
74125
75- final Signature signature = Signature .builder ().value (
76- UnsignedByteArray .of (BaseEncoding .base16 ()
77- .decode ("304502210093257D8E88D2A92CE55977641F72CCD235AB76B1AE189BE3377F30A69B131C49" +
78- "02200B79836114069F0D331418D05818908D85DE755AE5C2DDF42E9637FE1C11754F" ))
79- ).build ();
126+ final Signature signature = Signature .fromBase16 (
127+ "ED6F91CCF14EE94EB072C7671A397A313E3E5CBDAFE773BB6B2F07A0E75A7E65F84B5516268DAEE12902265256" +
128+ "EA1EF046B200148E14FF4E720C06519FD7F40F"
129+ );
130+
131+ final Payment signedPayment = Payment .builder ().from (unsignedTransaction )
132+ .transactionSignature (signature )
133+ .build ();
80134
81135 SingleSignedTransaction <Payment > signedTransaction = SingleSignedTransaction .<Payment >builder ()
82136 .signedTransaction (signedPayment )
83137 .signature (signature )
84138 .unsignedTransaction (unsignedTransaction )
85139 .build ();
86140
87- String expectedHash = "F847C96B2EEB0609F16C9DB9D74A0CB123B5EAF5B626207977335BF0A1EF53C3 " ;
141+ String expectedHash = "1A1953AC3BA3123254AA912CE507514A6AAD05EED8981A870B45F604936F0997 " ;
88142 assertThat (signedTransaction .hash ().value ()).isEqualTo (expectedHash );
89143 assertThat (signedTransaction .unsignedTransaction ()).isEqualTo (unsignedTransaction );
90144 assertThat (signedTransaction .signedTransaction ()).isEqualTo (signedPayment );
91145 assertThat (signedTransaction .signedTransactionBytes ().hexValue ()).isEqualTo (
92146 XrplBinaryCodec .getInstance ().encode (ObjectMapperFactory .create ().writeValueAsString (signedPayment ))
93147 );
94148 }
149+
150+ /**
151+ * This test constructs the transaction with hash 1A1953AC3BA3123254AA912CE507514A6AAD05EED8981A870B45F604936F0997 to
152+ * check that the hash that's on livenet _does not_ match when the signature is supplied incorrectly (i.e., this test
153+ * validates that a transaction's signature is always used to compute a transaction hash).
154+ *
155+ * @see "https://livenet.xrpl.org/transactions/1A1953AC3BA3123254AA912CE507514A6AAD05EED8981A870B45F604936F0997"
156+ */
157+ @ Test
158+ public void computesIncorrectTransactionHashWithoutSignature () throws JsonProcessingException {
159+ final Payment unsignedTransaction = Payment .builder ()
160+ .account (Address .of ("rGWx7VAsnwVKRbPFPpvy8Lo4nFf5xjj6Zb" ))
161+ .amount (XrpCurrencyAmount .ofDrops (1 ))
162+ .destination (Address .of ("rxRpSNb1VktvzBz8JF2oJC6qaww6RZ7Lw" ))
163+ .fee (XrpCurrencyAmount .ofDrops (12 ))
164+ .flags (PaymentFlags .of (TransactionFlags .UNSET .getValue ())) // 0
165+ .lastLedgerSequence (UnsignedInteger .valueOf (86481544 ))
166+ .memos (Collections .singletonList (
167+ MemoWrapper .builder ()
168+ .memo (Memo .builder ()
169+ .memoData ("7B226F70223A226D696E74222C22616D6F756E74223A22313030303030303030222C22677061223A2230227D" )
170+ .build ())
171+ .build ()
172+ ))
173+ .sequence (UnsignedInteger .valueOf (84987644 ))
174+ .signingPublicKey (
175+ PublicKey .fromBase16EncodedPublicKey ("ED05DC98B76FCD734BD44CDF153C34F79728485D2F24F9381CF7A284223EA258CE" )
176+ )
177+ .build ();
178+
179+ final Signature emptySignature = Signature .fromBase16 ("" );
180+
181+ final Payment signedPayment = Payment .builder ().from (unsignedTransaction )
182+ .transactionSignature (emptySignature )
183+ .build ();
184+
185+ SingleSignedTransaction <Payment > signedTransaction = SingleSignedTransaction .<Payment >builder ()
186+ .signedTransaction (signedPayment )
187+ .signature (emptySignature )
188+ .unsignedTransaction (unsignedTransaction )
189+ .build ();
190+
191+ String expectedHash = "1A1953AC3BA3123254AA912CE507514A6AAD05EED8981A870B45F604936F0997" ;
192+ assertThat (signedTransaction .hash ().value ()).isNotEqualTo (expectedHash );
193+ assertThat (signedTransaction .hash ().value ()).isEqualTo (
194+ "8E0EDE65ECE8A03ABDD7926B994B2F6F14514FDBD46714F4F511143A1F01A6D0"
195+ );
196+ assertThat (signedTransaction .unsignedTransaction ()).isEqualTo (unsignedTransaction );
197+ assertThat (signedTransaction .signedTransaction ()).isEqualTo (signedPayment );
198+ assertThat (signedTransaction .signedTransactionBytes ().hexValue ()).isEqualTo (
199+ XrplBinaryCodec .getInstance ().encode (ObjectMapperFactory .create ().writeValueAsString (signedPayment ))
200+ );
201+ }
95202}
0 commit comments