package org.consenlabs.tokencore.wallet; import com.google.common.base.Strings; import com.google.common.io.Files; import com.google.protobuf.CodedOutputStream; import junit.framework.Assert; import org.bitcoinj.core.Base58; import org.bitcoinj.core.ECKey; import org.consenlabs.tokencore.foundation.crypto.Hash; import org.consenlabs.tokencore.foundation.utils.ByteUtil; import org.consenlabs.tokencore.foundation.utils.NumericUtil; import org.consenlabs.tokencore.wallet.keystore.EOSKeystore; import org.consenlabs.tokencore.wallet.model.BIP44Util; import org.consenlabs.tokencore.wallet.model.ChainId; import org.consenlabs.tokencore.wallet.model.ChainType; import org.consenlabs.tokencore.wallet.model.KeyPair; import org.consenlabs.tokencore.wallet.model.Messages; import org.consenlabs.tokencore.wallet.model.Metadata; import org.consenlabs.tokencore.wallet.model.MnemonicAndPath; import org.consenlabs.tokencore.wallet.model.Network; import org.consenlabs.tokencore.wallet.model.TokenException; import org.consenlabs.tokencore.wallet.transaction.EOSSign; import org.consenlabs.tokencore.wallet.transaction.EOSTransaction; import org.consenlabs.tokencore.wallet.transaction.TxMultiSignResult; import org.consenlabs.tokencore.wallet.transaction.TxSignResult; import org.junit.Test; import org.spongycastle.crypto.digests.RIPEMD160Digest; import java.io.BufferedReader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.math.BigInteger; import java.net.URL; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Created by xyz on 2018/4/18. */ public class EOSWalletTest extends WalletSupport { public static final String WIF = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"; static final String ACCOUNT_NAME = "imtoken1"; static final String PUBLIC_KEY = "EOS6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV"; @Test public void generatePrvPubKey() { byte[] prvWIF = Base58.decode(WIF); // have omitted the checksum verification prvWIF = Arrays.copyOfRange(prvWIF, 1, prvWIF.length - 4); // use the privateKey to calculate the compressed public key directly ECKey ecKey = ECKey.fromPrivate(new BigInteger(1, prvWIF)); byte[] pubKeyData = ecKey.getPubKey(); RIPEMD160Digest digest = new RIPEMD160Digest(); digest.update(pubKeyData, 0, pubKeyData.length); byte[] out = new byte[20]; digest.doFinal(out, 0); byte[] checksumBytes = Arrays.copyOfRange(out, 0, 4); pubKeyData = ByteUtil.concat(pubKeyData, checksumBytes); String eosPK = "EOS" + Base58.encode(pubKeyData); Assert.assertEquals(PUBLIC_KEY, eosPK); } @Test public void serializeToBinary() { ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); CodedOutputStream codedOutputStream = CodedOutputStream.newInstance(outputStream); try { codedOutputStream.writeStringNoTag("jc"); codedOutputStream.writeStringNoTag("dan"); codedOutputStream.writeInt32NoTag(1); codedOutputStream.writeStringNoTag("abc"); codedOutputStream.writeStringNoTag(""); codedOutputStream.writeByteArrayNoTag(NumericUtil.hexToBytes("0f0f0f")); codedOutputStream.flush(); ByteBuffer byteBuffer = ByteBuffer.allocate(100); } catch (IOException e) { e.printStackTrace(); } } @Test public void signTransaction() { String wif = "5HxQKWDznancXZXm7Gr2guadK7BhK9Zs8ejDhfA9oEBM89ZaAru"; Metadata metadata = new Metadata(); metadata.setChainType("EOS"); metadata.setSource(Metadata.FROM_WIF); metadata.setWalletType(Metadata.V3); Wallet wallet = WalletManager.importWalletFromPrivateKey(metadata, "account_name", wif, SampleKey.PASSWORD, false); EOSTransaction transaction = new EOSTransaction(NumericUtil.hexToBytes("c578065b93aec6a7c811000000000100a6823403ea3055000000572d3ccdcd01000000602a48b37400000000a8ed323225000000602a48b374208410425c95b1ca80969800000000000453595300000000046d656d6f00")); TxSignResult ret = transaction.signTransaction(ChainId.EOS_MAINNET, SampleKey.PASSWORD, wallet); Assert.assertEquals("SIG_K1_KUzLctwEZJnbZBPbZiTiwzxSuMVp5ik8CbJTsusbBaDk9yKHuuw9D9jUj4fMaWKdnbcmqxj8BJCvJkoR4GtkVhD8msihFj", ret.getSignedTx()); } @Test public void sighHashTest() { byte[] dataSha256 = NumericUtil.hexToBytes("6cb75bc5a46a7fdb64b92efefca01ed7b060ab5e0d625226e8efbc0980c3ddc1"); String result = EOSSign.sign(dataSha256, "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"); } private Metadata eosMetadata() { Metadata metadata = new Metadata(); metadata.setChainType(ChainType.EOS); metadata.setSource(Metadata.FROM_MNEMONIC); metadata.setWalletType(Metadata.HD_SHA256); return metadata; } @Test public void eosSignTransactions() { // import eos wallet Wallet wallet = WalletManager.importWalletFromMnemonic(eosMetadata(), SampleKey.MNEMONIC, BIP44Util.EOS_LEDGER, SampleKey.PASSWORD, true); // construct to sign objects List<EOSTransaction.ToSignObj> toSignObjs = new ArrayList<>(); EOSTransaction.ToSignObj toSignObj = new EOSTransaction.ToSignObj(); toSignObj.setPublicKeys(Collections.singletonList("EOS88XhiiP7Cu5TmAUJqHbyuhyYgd6sei68AU266PyetDDAtjmYWF")); toSignObj.setTxHex("c578065b93aec6a7c811000000000100a6823403ea3055000000572d3ccdcd01000000602a48b37400000000a8ed323225000000602a48b374208410425c95b1ca80969800000000000453595300000000046d656d6f00"); toSignObjs.add(toSignObj); EOSTransaction eosTransaction = new EOSTransaction(toSignObjs); List<TxMultiSignResult> signResults = eosTransaction.signTransactions(ChainId.EOS_MAINNET, SampleKey.PASSWORD, wallet); Assert.assertEquals(1, signResults.size()); TxMultiSignResult actualResult = signResults.get(0); Assert.assertEquals(1, actualResult.getSigned().size()); Assert.assertEquals("SIG_K1_KjZXm86HMVyUd59E15pCkrpn5uUPAAsjTxjEVRRueEvGciinxRS3sATmEEWdkb8hRNHhf6SXofsz4qzPdD6mfZ67FoqLxh", actualResult.getSigned().get(0)); Assert.assertEquals("6af5b3ae9871c25e2de195168ed7423f455a68330955701e327f02276bb34088", actualResult.getTxHash()); } @Test public void importEOSWalletByMnemonic() { Wallet wallet = WalletManager.importWalletFromMnemonic(eosMetadata(), SampleKey.MNEMONIC, BIP44Util.EOS_LEDGER, SampleKey.PASSWORD, true); Assert.assertEquals(ChainType.EOS, wallet.getMetadata().getChainType()); Assert.assertEquals(Metadata.FROM_MNEMONIC, wallet.getMetadata().getSource()); Assert.assertTrue(Strings.isNullOrEmpty(wallet.getAddress())); MnemonicAndPath mnemonicAndPath = wallet.exportMnemonic(SampleKey.PASSWORD); Assert.assertEquals(SampleKey.MNEMONIC, mnemonicAndPath.getMnemonic()); Assert.assertEquals(BIP44Util.EOS_LEDGER, mnemonicAndPath.getPath()); Assert.assertNotSame(0L, wallet.getCreatedAt()); Assert.assertTrue(Strings.isNullOrEmpty(wallet.getAddress())); List<String> expectedPubKeys = new ArrayList<>(); expectedPubKeys.add("EOS88XhiiP7Cu5TmAUJqHbyuhyYgd6sei68AU266PyetDDAtjmYWF"); List<String> publicKeys = new ArrayList<>(2); publicKeys.add(wallet.getKeyPathPrivates().get(0).getPublicKey()); Assert.assertTrue(Arrays.equals(expectedPubKeys.toArray(), publicKeys.toArray())); } @Test public void importEOSWalletByMnemonicMultiPermissions() { List<EOSKeystore.PermissionObject> permissionObjects = new ArrayList<>(); EOSKeystore.PermissionObject permObj = new EOSKeystore.PermissionObject(); permObj.setPublicKey("EOS88XhiiP7Cu5TmAUJqHbyuhyYgd6sei68AU266PyetDDAtjmYWF"); permObj.setPermission("active"); permissionObjects.add(permObj); // // permObj = new EOSKeystore.PermissionObject(); // permObj.setPublicKey("EOS7uUZkJKheG9Ag5C1TA78LX74fWY28sBEfFjP49Cae8Ski7cvVR"); // permObj.setPermission("sns"); // permissionObjects.add(permObj); Wallet wallet = WalletManager.importWalletFromMnemonic(eosMetadata(), ACCOUNT_NAME, SampleKey.MNEMONIC, BIP44Util.EOS_LEDGER, permissionObjects, SampleKey.PASSWORD, true); Assert.assertEquals(1, wallet.getKeyPathPrivates().size()); List<String> expectedPubKeys = new ArrayList<>(); expectedPubKeys.add("EOS88XhiiP7Cu5TmAUJqHbyuhyYgd6sei68AU266PyetDDAtjmYWF"); List<String> publicKeys = new ArrayList<>(3); publicKeys.add(wallet.getKeyPathPrivates().get(0).getPublicKey()); Assert.assertTrue(Arrays.equals(expectedPubKeys.toArray(), publicKeys.toArray())); } @Test public void importEOSWalletFailedWhenDerivedPubKeyNotSame() { try { List<EOSKeystore.PermissionObject> permissionObjects = new ArrayList<>(); EOSKeystore.PermissionObject permObj = new EOSKeystore.PermissionObject(); // this pubkey is wrong, the last letter should be w not W permObj.setPublicKey("EOS7tpXQ1thFJ69ZXDqqEan7GMmuWdcptKmwgbs7n1cnx3hWPw3jW"); permObj.setPermission("owner"); permissionObjects.add(permObj); permObj = new EOSKeystore.PermissionObject(); permObj.setPublicKey("EOS5SxZMjhKiXsmjxac8HBx56wWdZV1sCLZESh3ys1rzbMn4FUumU"); permObj.setPermission("active"); permissionObjects.add(permObj); WalletManager.importWalletFromMnemonic(eosMetadata(), ACCOUNT_NAME, SampleKey.MNEMONIC, BIP44Util.EOS_LEDGER, permissionObjects, SampleKey.PASSWORD, true); Assert.fail("Should throw exception"); } catch (TokenException ex) { Assert.assertEquals(Messages.EOS_PRIVATE_PUBLIC_NOT_MATCH, ex.getMessage()); } } @Test public void importEOSWalletBySinglePrvKey() { // owner key: 5Jnx4Tv6iu5fyq9g3aKmKsEQrhe7rJZkJ4g3LTK5i7tBDitakvP // active key: 5JK2n2ujYXsooaqbfMQqxxd8P7xwVNDaajTuqRagJNGPi88yPGw // active key: 5J25CphXSMh2SUdjspX7M4sLT5QATkTXJhiGSMn4nwg1HbhHLRe List<String> prvKeys = new ArrayList<>(3); prvKeys.add("5Jnx4Tv6iu5fyq9g3aKmKsEQrhe7rJZkJ4g3LTK5i7tBDitakvP"); prvKeys.add("5JK2n2ujYXsooaqbfMQqxxd8P7xwVNDaajTuqRagJNGPi88yPGw"); prvKeys.add("5J25CphXSMh2SUdjspX7M4sLT5QATkTXJhiGSMn4nwg1HbhHLRe"); Metadata meta = eosMetadata(); meta.setSource(Metadata.FROM_WIF); List<EOSKeystore.PermissionObject> permissionObjects = new ArrayList<>(); EOSKeystore.PermissionObject permObj = new EOSKeystore.PermissionObject(); permObj.setPublicKey("EOS621QecaYWvdKdCvHJRo76fvJwTo1Y4qegPnKxsf3FJ5zm2pPru"); permObj.setPermission("owner"); permissionObjects.add(permObj); permObj = new EOSKeystore.PermissionObject(); permObj.setPublicKey("EOS6qTGVvgoT39AAJp1ykty8XVDFv1GfW4QoS4VyjfQQPv5ziMNzF"); permObj.setPermission("active"); permissionObjects.add(permObj); permObj = new EOSKeystore.PermissionObject(); permObj.setPublicKey("EOS877B3gaJytVzFizhWPD26SefS9QV1qYTZT2QCcXueQfV4PAN8h"); permObj.setPermission("sns"); permissionObjects.add(permObj); Wallet wallet = WalletManager.importWalletFromPrivateKeys(meta, ACCOUNT_NAME, prvKeys, permissionObjects, SampleKey.PASSWORD, true); Assert.assertEquals(ChainType.EOS, wallet.getMetadata().getChainType()); Assert.assertEquals(Metadata.FROM_WIF, wallet.getMetadata().getSource()); Assert.assertNotSame(0L, wallet.getCreatedAt()); Assert.assertEquals(ACCOUNT_NAME, wallet.getAddress()); List<String> expectedPubKeys = new ArrayList<>(); expectedPubKeys.add("EOS621QecaYWvdKdCvHJRo76fvJwTo1Y4qegPnKxsf3FJ5zm2pPru"); expectedPubKeys.add("EOS6qTGVvgoT39AAJp1ykty8XVDFv1GfW4QoS4VyjfQQPv5ziMNzF"); expectedPubKeys.add("EOS877B3gaJytVzFizhWPD26SefS9QV1qYTZT2QCcXueQfV4PAN8h"); List<String> publicKeys = new ArrayList<>(3); for (EOSKeystore.KeyPathPrivate keyPathPrivate : wallet.getKeyPathPrivates()) { publicKeys.add(keyPathPrivate.getPublicKey()); } Assert.assertTrue(Arrays.equals(expectedPubKeys.toArray(), publicKeys.toArray())); } @Test public void importEOSWalletByPrvKeysShouldFailedWhenDerivedPubKeyNotSame() { try { List<EOSKeystore.PermissionObject> permissionObjects = new ArrayList<>(); EOSKeystore.PermissionObject permObj = new EOSKeystore.PermissionObject(); // this pubkey is wrong, the last letter should be u not U permObj.setPublicKey("EOS621QecaYWvdKdCvHJRo76fvJwTo1Y4qegPnKxsf3FJ5zm2pPrU"); permObj.setPermission("owner"); permissionObjects.add(permObj); permObj = new EOSKeystore.PermissionObject(); permObj.setPublicKey("EOS6qTGVvgoT39AAJp1ykty8XVDFv1GfW4QoS4VyjfQQPv5ziMNzF"); permObj.setPermission("active"); permissionObjects.add(permObj); List<String> prvKeys = new ArrayList<>(3); prvKeys.add("5Jnx4Tv6iu5fyq9g3aKmKsEQrhe7rJZkJ4g3LTK5i7tBDitakvP"); prvKeys.add("5JK2n2ujYXsooaqbfMQqxxd8P7xwVNDaajTuqRagJNGPi88yPGw"); Metadata meta = eosMetadata(); meta.setSource(Metadata.FROM_WIF); WalletManager.importWalletFromPrivateKeys(meta, ACCOUNT_NAME, prvKeys, permissionObjects, SampleKey.PASSWORD, true); Assert.fail("Should throw exception"); } catch (TokenException ex) { Assert.assertEquals(Messages.EOS_PRIVATE_PUBLIC_NOT_MATCH, ex.getMessage()); } } @Test public void exportWalletPrvKeys() { Wallet wallet = WalletManager.importWalletFromMnemonic(eosMetadata(), SampleKey.MNEMONIC, BIP44Util.EOS_LEDGER, SampleKey.PASSWORD, true); List<KeyPair> prvKeys = WalletManager.exportPrivateKeys(wallet.getId(), SampleKey.PASSWORD); List<KeyPair> expectedPrvKeys = new ArrayList<>(); expectedPrvKeys.add(new KeyPair("5KAigHMamRhN7uwHFnk3yz7vUTyQT1nmXoAA899XpZKJpkqsPFp", "EOS88XhiiP7Cu5TmAUJqHbyuhyYgd6sei68AU266PyetDDAtjmYWF")); Assert.assertTrue(Arrays.equals(prvKeys.toArray(), expectedPrvKeys.toArray())); } @Test public void accountName() { Wallet wallet = WalletManager.importWalletFromMnemonic(eosMetadata(), SampleKey.MNEMONIC, BIP44Util.EOS_PATH, SampleKey.PASSWORD, true); Assert.assertTrue(Strings.isNullOrEmpty(wallet.getAddress())); try { String accountName = "AccountName"; wallet = WalletManager.setAccountName(wallet.getId(), accountName); Assert.fail("Account name can't contains uppercase char"); } catch (TokenException ex) { Assert.assertEquals(Messages.EOS_ACCOUNT_NAME_INVALID, ex.getMessage()); } try { String accountName = "accountnameaccountname"; wallet = WalletManager.setAccountName(wallet.getId(), accountName); Assert.fail("Account name's length can't greater than 12"); } catch (TokenException ex) { Assert.assertEquals(Messages.EOS_ACCOUNT_NAME_INVALID, ex.getMessage()); } try { String accountName = "accountname6"; wallet = WalletManager.setAccountName(wallet.getId(), accountName); Assert.fail("Account name can't contain number 6~9 and 0"); } catch (TokenException ex) { Assert.assertEquals(Messages.EOS_ACCOUNT_NAME_INVALID, ex.getMessage()); } String accountName = "imtoken.1111"; wallet = WalletManager.setAccountName(wallet.getId(), accountName); Assert.assertEquals(accountName, wallet.getAddress()); Wallet foundedWallet = WalletManager.findWalletByAddress(ChainType.EOS, accountName); Assert.assertEquals(wallet.getId(), foundedWallet.getId()); try { WalletManager.setAccountName(wallet.getId(), "NewAccountName"); Assert.fail("EOS wallet only can change the accountName once"); } catch (TokenException ex) { Assert.assertTrue(true); } } @Test public void compatibilityV3Sign() { String wif = "5HxQKWDznancXZXm7Gr2guadK7BhK9Zs8ejDhfA9oEBM89ZaAru"; Metadata metadata = new Metadata(); metadata.setChainType("EOS"); metadata.setSource(Metadata.FROM_WIF); metadata.setWalletType(Metadata.V3); Wallet wallet = WalletManager.importWalletFromPrivateKey(metadata, "account.name", wif, SampleKey.PASSWORD, false); List<EOSTransaction.ToSignObj> toSignObjs = new ArrayList<>(); EOSTransaction.ToSignObj toSignObj = new EOSTransaction.ToSignObj(); toSignObj.setPublicKeys(Collections.singletonList("EOS5SxZMjhKiXsmjxac8HBx56wWdZV1sCLZESh3ys1rzbMn4FUumU")); toSignObj.setTxHex("c578065b93aec6a7c811000000000100a6823403ea3055000000572d3ccdcd01000000602a48b37400000000a8ed323225000000602a48b374208410425c95b1ca80969800000000000453595300000000046d656d6f00"); toSignObjs.add(toSignObj); EOSTransaction eosTransaction = new EOSTransaction(toSignObjs); List<TxMultiSignResult> signResults = eosTransaction.signTransactions(ChainId.EOS_MAINNET, SampleKey.PASSWORD, wallet); Assert.assertEquals(1, signResults.size()); Assert.assertEquals("SIG_K1_KUzLctwEZJnbZBPbZiTiwzxSuMVp5ik8CbJTsusbBaDk9yKHuuw9D9jUj4fMaWKdnbcmqxj8BJCvJkoR4GtkVhD8msihFj", signResults.get(0).getSigned().get(0)); } @Test public void importEOSWalletWhenHasEmptyAddressWallet() { // regression testing // if keystoreMap has a empty address keystore, then import the second keystore will produce a 'null object reference' exception Wallet wallet = WalletManager.importWalletFromMnemonic(eosMetadata(), SampleKey.MNEMONIC, BIP44Util.EOS_PATH, SampleKey.PASSWORD, true); WalletManager.importWalletFromMnemonic(eosMetadata(), ACCOUNT_NAME, SampleKey.MNEMONIC, BIP44Util.EOS_PATH, null, SampleKey.PASSWORD, true); } @Test public void testEOSSign() { long start = System.currentTimeMillis(); URL url = getClass().getClassLoader().getResource("EOSSignTestcase.txt"); try { BufferedReader reader = new BufferedReader(new FileReader(url.getFile())); String line; while ((line = reader.readLine()) != null) { String[] strs = line.split(","); Assert.assertEquals(strs[0], strs[1], EOSSign.sign(Hash.sha256(strs[0].getBytes()), "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3")); } } catch (Exception ex) { Assert.fail(ex.getMessage()); } System.out.println(String.format("run 'EOSSignTestcase' test take %d ms", (System.currentTimeMillis() - start))); } @Test public void testLegacyEOSExport() { Metadata metadata = new Metadata(); metadata.setSource(Metadata.FROM_WIF); metadata.setChainType(ChainType.EOS); Wallet wallet = WalletManager.importWalletFromPrivateKey(metadata, ACCOUNT_NAME, WIF, SampleKey.PASSWORD, true); List<KeyPair> keyPairs = wallet.exportPrivateKeys(SampleKey.PASSWORD); Assert.assertEquals(1, keyPairs.size()); KeyPair keyPair = keyPairs.get(0); Assert.assertEquals(WIF, keyPair.getPrivateKey()); Assert.assertEquals(PUBLIC_KEY, keyPair.getPublicKey()); } }