以太坊钱包

### android钱包笔记 ###

1、创建钱包之助记词生成

//12个助记词
ArrayList words = new ArrayList<>();
MnemonicCode mnemonicCode = new MnemonicCode(context.getAssets().open("english.txt"), null);
SecureRandom secureRandom = SecureRandomUtils.secureRandom();
byte[] initialEntropy = new byte[16];//算法需要,必须是被4整除
secureRandom.nextBytes(initialEntropy);
List wd = mnemonicCode.toMnemonic(initialEntropy);

if (wd == null || wd.size() != 12)
    throw new RuntimeException("generate word error");
else {
    words.clear();
    words.addAll(wd);
    StringBuilder save = new StringBuilder();
    for (String word : words) {
        save.append(word).append(" ");
    }
    //真正的助记词钱包地址创建流程
    DeterministicSeed deterministicSeed = new DeterministicSeed(save.toString().substring(0, save.toString().length() - 1), null, "", 0);
    List ls = deterministicSeed.getMnemonicCode();
    DeterministicKeyChain deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build();
    //DERIVATION_PATH = "m/44'/60'/0'/0/0"此值使用BIP44协议固定,导入钱包可用到,其他的PATH未尝试
    List keyPath = HDUtils.parsePath("M/44H/60H/0H/0/0");
    //私钥
    BigInteger privateKey = deterministicKeyChain.getKeyByPath(keyPath, true).getPrivKey();
    ECKeyPair ecKeyPair = ECKeyPair.create(privateKey);
    //钱包地址,不带0x头
    String address = Keys.getAddress(ecKeyPair);
}

2、导入钱包之助记词导入

/**
 * 通用的以太坊基于bip44协议的助记词路径 (imtoken jaxx Metamask myetherwallet)
 */
public static String ETH_JAXX_TYPE = "m/44'/60'/0'/0/0";

/**
 * import mnemonic wallet
 *
 * @param mnemonic 格式为单词加空格的字符串"a b c d e f g h i j k l"
 */
private void importWallet(String mnemonic) {
    String[] pathArray = ETH_JAXX_TYPE.split("/");
    if (pathArray.length <= 1) {
        //内容不对
        return;
    }
    String passphrase = "";
    long creationTimeSeconds = System.currentTimeMillis() / 1000;
    //助记词种子
    DeterministicSeed ds = new DeterministicSeed(Arrays.asList(mnemonic.split(" ")), null, passphrase, creationTimeSeconds);
    createWallet(ds, pathArray);
}

/**
 * 导入钱包
 */
private void createWallet(DeterministicSeed ds, String[] pathArray) {
    byte[] seedBytes = ds.getSeedBytes();
    List mnemonicCode = ds.getMnemonicCode();

    DeterministicKey masterPrivateKey = HDKeyDerivation.createMasterPrivateKey(seedBytes);

    for (int i = 1; i < pathArray.length; i++) {
        ChildNumber childNumber;
        if (pathArray[i].endsWith("'")) {
            int number = Integer.parseInt(pathArray[i].substring(0, pathArray[i].length() - 1));
            childNumber = new ChildNumber(number, true);
        } else {
            int number = Integer.parseInt(pathArray[i]);
            childNumber = new ChildNumber(number, false);
        }

        masterPrivateKey = HDKeyDerivation.deriveChildKey(masterPrivateKey, childNumber);

    }
    Credentials credentials = Credentials.create(masterPrivateKey.getPrivKey().toString(16));
    ECKeyPair ecKeyPair = ECKeyPair.create(masterPrivateKey.getPrivKeyBytes());
    //钱包地址导入成功
    String walletAddress = OwnWalletUtils.generateWalletFile(mDataHandler.pwd.get(), ecKeyPair, getFilesDir(), false);

}

//导入钱包生成地址
public String generateWalletFile(
        String password, ECKeyPair ecKeyPair, File destinationDirectory, boolean useFullScrypt)
        throws CipherException, IOException {

    WalletFile walletFile;
    if (useFullScrypt) {
        walletFile = Wallet.createStandard(password, ecKeyPair);
    } else {
        walletFile = Wallet.createLight(password, ecKeyPair);
    }
    String fileName = walletFile.getAddress();
    File destination = new File(destinationDirectory, fileName);

    ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();
    objectMapper.writeValue(destination, walletFile);

    return fileName;
}

3、由于ETH节点获取的nonce值转账失败

ETH节点要求每笔交易必须有一个nonce数值,同一个节点发起交易时,nonce会递增;

请求nonce接口文档

https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_gettransactioncount

参数

"latest"获取最后一次交易的nonce

"earliest"没试过

"pending"获取最后一次交易中状态的nonce

nonce太小会被拒绝

nonce太大区块确认时间会很长

nonce会引起的问题:

1、转账时间变很慢

2、转账提交不成功

解决方法

1、取latest值,然后设置合适的gaslimit会降低交易单发起失败的概率

2、设置合适的默认gasprice会加快交易单的确认时间

3、本地对nonce的维护(不建议,多人同时操作一个钱包的时候会出现拥堵,此时没有补齐节点nonce和本地nonce的差值时交易将不被确认)

4、ETH转账签名

 /**
 * 离线签名eth
 * @parame thWalletInfo.wordsw为助记词字符串
 * @param to//转账的钱包地址
 * @param nonce//获取到的交易次数
 * @param gasPrice //gasPrice
 * @param gasLimit //gasLimit
 * @param value   //转账的值
 * @return
 */
public String signedEthTransactionData(String coinName, String to, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String value) throws Exception {
    if (TextUtils.isEmpty(ethWalletInfo.words)) {
        closeProgressDialog();
        throw new RuntimeException("please generateMnemonic first");
    }
        //为ETH的时候直接填入
        //把十进制的转换成ETH的Wei, 1ETH = 10^18 Wei
        BigDecimal realValue = Convert.toWei(value, Convert.Unit.ETHER);
        RawTransaction rawTransaction = RawTransaction.createEtherTransaction(nonce, gasPrice, gasLimit, to, realValue.toBigIntegerExact());

        //获取个人钱包信息 此处用到助记词签名,实际可通过其他的签名
        DeterministicSeed seed = new DeterministicSeed(ethWalletInfo.words, null, "", System.currentTimeMillis());
        DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).build();
        List keyPath = HDUtils.parsePath("M/44H/60H/0H/0/0");
        DeterministicKey key = chain.getKeyByPath(keyPath, true);
        BigInteger privKey = key.getPrivKey();
        Credentials credentials = Credentials.create(privKey.toString(16));
        //使用TransactionEncoder对RawTransaction进行签名操作
        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
        转换成0x开头的字符串
    return Numeric.toHexString(signedMessage);
}   

5、ETH代币转账签名

/**
 * 离线签名eth
 * @parame thWalletInfo.wordsw为助记词字符串
 * @param to//转账的钱包地址
 * @param nonce//获取到的交易次数
 * @param gasPrice //建议提交前获取最新的gasPrice
 * @param gasLimit //建议提交前获取最新的gasLimit  
 * @param value   //转账的值  
 * @return
 */
public String signedEthTransactionData(String coinName, String to, BigInteger nonce, BigInteger gasPrice, BigInteger gasLimit, String value) throws Exception {
    if (TextUtils.isEmpty(ethWalletInfo.words)) {
        closeProgressDialog();
        throw new RuntimeException("please generateMnemonic first");
    }

    //为代币的时候要乘以100,此处规则需看代币的规定 * new BigInteger("100").doubleValue()
    //因为每个代币可以规定自己的小数位, 所以实际的转账值=数值 * 10^小数位
    BigDecimal realValue = BigDecimal.valueOf(new BigDecimal(value).doubleValue() * new BigInteger("100").doubleValue() );

    //0xa9059cbb代表某个代币的转账方法hex(transfer) + 对方的转账地址hex + 转账的值的hex
    String data = "0xa9059cbb" + Numeric.toHexStringNoPrefixZeroPadded(Numeric.toBigInt(to), 64) + Numeric.toHexStringNoPrefixZeroPadded(realValue.toBigInteger(), 64);
    RawTransaction rawTransaction = RawTransaction.createTransaction(nonce, gasPrice, gasLimit.add(new BigInteger("0")), coinInfo.contractAddress, data);

    //获取个人钱包信息  此处用到助记词签名,实际可通过其他的签名
    DeterministicSeed seed = new DeterministicSeed(ethWalletInfo.words, null, "", System.currentTimeMillis());
    DeterministicKeyChain chain = DeterministicKeyChain.builder().seed(seed).build();
    List keyPath = HDUtils.parsePath("M/44H/60H/0H/0/0");
    DeterministicKey key = chain.getKeyByPath(keyPath, true);
    BigInteger privKey = key.getPrivKey();
    Credentials credentials = Credentials.create(privKey.toString(16));
    //使用TransactionEncoder对RawTransaction进行签名操作
    byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
    //转换成0x开头的字符串
    return Numeric.toHexString(signedMessage);
}   

你可能感兴趣的:(android以太坊钱包开发)