以太坊钱包
### 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 = https://www.it610.com/article/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 = https://www.it610.com/article/BigDecimal.valueOf(new BigDecimal(value).doubleValue() * new BigInteger("100").doubleValue() );
//0xa9059cbb代表某个代币的转账方法hex(transfer) + 对方的转账地址hex + 转账的值的hex
String data = "https://www.it610.com/article/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);
}
推荐阅读
- 以太坊中的计量单位及相互转换
- 区块链开发平台(以太坊)
- 来到“社会磨坊”的第二天
- gcb雷达钱包全球付,没有国家主权,只有全民主权!
- “神医”(微小说)
- 家公捡到钱包了
- 诗萱言币|诗萱言币 11.9早间比特币以太坊盘整蓄力 多空转换等待破位
- 精品课程|精品课程||情绪管理与性教育??1+1亲子关系工作坊
- DApp情况比拼(以太坊、IOST、TRON和EOS(2))
- 胡因梦工作坊之十四——活出北交狮子活出自己