33import static org .junit .Assert .assertArrayEquals ;
44import static org .junit .Assert .assertEquals ;
55import static org .junit .Assert .assertNotNull ;
6+ import static org .junit .Assert .assertTrue ;
67
78import com .fasterxml .jackson .databind .DeserializationFeature ;
89import com .fasterxml .jackson .databind .ObjectMapper ;
910import java .io .File ;
11+ import java .io .InputStream ;
12+ import org .junit .Rule ;
1013import org .junit .Test ;
14+ import org .junit .rules .TemporaryFolder ;
1115import org .tron .common .crypto .SignInterface ;
1216import org .tron .common .crypto .SignUtils ;
17+ import org .tron .common .utils .ByteArray ;
1318import org .tron .common .utils .Utils ;
1419
1520/**
1621 * Cross-implementation compatibility tests.
17- * Verifies that keystore files can survive a roundtrip through the
18- * Java implementation (encrypt → serialize → deserialize → decrypt).
1922 *
20- * Also verifies that keystore files generated by legacy --keystore-factory
21- * code are compatible with the new library.
23+ * <p>Static fixtures (web3j-pbkdf2.json, web3j-scrypt.json) are standard
24+ * Ethereum test vectors from the Web3 Secret Storage specification.
25+ * Password and private key are publicly documented — see fixtures/README.md.
26+ *
27+ * <p>Format compatibility with geth/other clients is verified dynamically:
28+ * generate keystore at test time, serialize to file, deserialize back,
29+ * verify private key and address survive the roundtrip. No static secrets
30+ * stored in the repository.
2231 */
2332public class CrossImplTest {
2433
2534 private static final ObjectMapper MAPPER = new ObjectMapper ()
2635 .configure (DeserializationFeature .FAIL_ON_UNKNOWN_PROPERTIES , false );
2736
37+ @ Rule
38+ public TemporaryFolder tempFolder = new TemporaryFolder ();
39+
40+ // --- Standard Ethereum test vectors (public, from Web3 Secret Storage spec) ---
41+
42+ private static final String WEB3J_PASSWORD = "Insecure Pa55w0rd" ;
43+ private static final String WEB3J_SECRET =
44+ "a392604efc2fad9c0b3da43b5f698a2e3f270f170d859912be0d54742275c5f6" ;
45+
46+ @ Test
47+ public void testDecryptWeb3jPbkdf2Fixture () throws Exception {
48+ WalletFile walletFile = loadFixture ("keystore-fixtures/web3j-pbkdf2.json" );
49+ SignInterface recovered = Wallet .decrypt (WEB3J_PASSWORD , walletFile , true );
50+ assertEquals ("Private key must match web3j test vector" ,
51+ WEB3J_SECRET , ByteArray .toHexString (recovered .getPrivateKey ()));
52+ }
53+
2854 @ Test
29- public void testLightKeystoreRoundtrip () throws Exception {
30- String password = "testpassword123" ;
55+ public void testDecryptWeb3jScryptFixture () throws Exception {
56+ WalletFile walletFile = loadFixture ("keystore-fixtures/web3j-scrypt.json" );
57+ SignInterface recovered = Wallet .decrypt (WEB3J_PASSWORD , walletFile , true );
58+ assertEquals ("Private key must match web3j test vector" ,
59+ WEB3J_SECRET , ByteArray .toHexString (recovered .getPrivateKey ()));
60+ }
61+
62+ // --- Dynamic format compatibility (no static secrets) ---
63+
64+ @ Test
65+ public void testKeystoreFormatCompatibility () throws Exception {
66+ // Dynamically generate, serialize, deserialize — verifies the JSON format
67+ // is correct and can survive a roundtrip (same format geth/web3j produce)
3168 SignInterface keyPair = SignUtils .getGeneratedRandomSign (Utils .getRandom (), true );
3269 byte [] originalKey = keyPair .getPrivateKey ();
70+ String password = "dynamicTest123" ;
3371
34- // Create keystore → write to temp file → read back → decrypt
35- WalletFile walletFile = Wallet .createLight (password , keyPair );
36- File tempFile = File .createTempFile ("keystore-test-" , ".json" );
37- tempFile .deleteOnExit ();
38- MAPPER .writeValue (tempFile , walletFile );
72+ WalletFile walletFile = Wallet .createStandard (password , keyPair );
3973
74+ // Verify the generated file has correct Web3 Secret Storage structure
75+ assertEquals ("version must be 3" , 3 , walletFile .getVersion ());
76+ assertNotNull ("must have address" , walletFile .getAddress ());
77+ assertNotNull ("must have crypto" , walletFile .getCrypto ());
78+ assertEquals ("cipher must be aes-128-ctr" ,
79+ "aes-128-ctr" , walletFile .getCrypto ().getCipher ());
80+ assertTrue ("kdf must be scrypt or pbkdf2" ,
81+ "scrypt" .equals (walletFile .getCrypto ().getKdf ())
82+ || "pbkdf2" .equals (walletFile .getCrypto ().getKdf ()));
83+
84+ // Write to file, read back — simulates cross-process interop
85+ File tempFile = new File (tempFolder .getRoot (), "compat-test.json" );
86+ MAPPER .writeValue (tempFile , walletFile );
4087 WalletFile loaded = MAPPER .readValue (tempFile , WalletFile .class );
41- SignInterface recovered = Wallet .decrypt (password , loaded , true );
4288
43- assertArrayEquals ("File roundtrip must preserve private key" ,
89+ // Decrypt the deserialized file
90+ SignInterface recovered = Wallet .decrypt (password , loaded , true );
91+ assertArrayEquals ("Key must survive file roundtrip" ,
4492 originalKey , recovered .getPrivateKey ());
93+
94+ // Verify TRON address format
95+ byte [] tronAddr = recovered .getAddress ();
96+ assertEquals ("TRON address must be 21 bytes" , 21 , tronAddr .length );
97+ assertEquals ("First byte must be TRON prefix" , 0x41 , tronAddr [0 ] & 0xFF );
4598 }
4699
47100 @ Test
48- public void testStandardKeystoreRoundtrip () throws Exception {
49- String password = "testpassword456" ;
101+ public void testLightScryptFormatCompatibility () throws Exception {
50102 SignInterface keyPair = SignUtils .getGeneratedRandomSign (Utils .getRandom (), true );
51103 byte [] originalKey = keyPair .getPrivateKey ();
104+ String password = "lightCompat456" ;
52105
53- WalletFile walletFile = Wallet .createStandard (password , keyPair );
54- File tempFile = File .createTempFile ("keystore-std-" , ".json" );
55- tempFile .deleteOnExit ();
106+ WalletFile walletFile = Wallet .createLight (password , keyPair );
107+ File tempFile = new File (tempFolder .getRoot (), "light-compat.json" );
56108 MAPPER .writeValue (tempFile , walletFile );
57-
58109 WalletFile loaded = MAPPER .readValue (tempFile , WalletFile .class );
59- SignInterface recovered = Wallet .decrypt (password , loaded , true );
60110
61- assertArrayEquals ("Standard scrypt file roundtrip must preserve private key" ,
111+ SignInterface recovered = Wallet .decrypt (password , loaded , true );
112+ assertArrayEquals ("Key must survive light scrypt file roundtrip" ,
62113 originalKey , recovered .getPrivateKey ());
63114 }
64115
@@ -85,30 +136,22 @@ public void testLoadCredentialsIntegration() throws Exception {
85136 byte [] originalKey = keyPair .getPrivateKey ();
86137 String originalAddress = Credentials .create (keyPair ).getAddress ();
87138
88- // Use WalletUtils full flow
89- File tempDir = new File (System .getProperty ("java.io.tmpdir" ), "keystore-test-" +
90- System .currentTimeMillis ());
91- tempDir .mkdirs ();
92- try {
93- String fileName = WalletUtils .generateWalletFile (password , keyPair , tempDir , false );
94- assertNotNull (fileName );
95-
96- File keystoreFile = new File (tempDir , fileName );
97- Credentials loaded = WalletUtils .loadCredentials (password , keystoreFile , true );
98-
99- assertEquals ("Address must survive full WalletUtils roundtrip" ,
100- originalAddress , loaded .getAddress ());
101- assertArrayEquals ("Key must survive full WalletUtils roundtrip" ,
102- originalKey , loaded .getSignInterface ().getPrivateKey ());
103- } finally {
104- // Cleanup
105- File [] files = tempDir .listFiles ();
106- if (files != null ) {
107- for (File f : files ) {
108- f .delete ();
109- }
110- }
111- tempDir .delete ();
112- }
139+ File tempDir = tempFolder .newFolder ("wallet-integration" );
140+ String fileName = WalletUtils .generateWalletFile (password , keyPair , tempDir , false );
141+ assertNotNull (fileName );
142+
143+ File keystoreFile = new File (tempDir , fileName );
144+ Credentials loaded = WalletUtils .loadCredentials (password , keystoreFile , true );
145+
146+ assertEquals ("Address must survive full WalletUtils roundtrip" ,
147+ originalAddress , loaded .getAddress ());
148+ assertArrayEquals ("Key must survive full WalletUtils roundtrip" ,
149+ originalKey , loaded .getSignInterface ().getPrivateKey ());
150+ }
151+
152+ private WalletFile loadFixture (String resourcePath ) throws Exception {
153+ InputStream is = getClass ().getClassLoader ().getResourceAsStream (resourcePath );
154+ assertNotNull ("Fixture not found: " + resourcePath , is );
155+ return MAPPER .readValue (is , WalletFile .class );
113156 }
114157}
0 commit comments