前面说了一些关于区块链生成钱包地址的方法说了一下, 那么该如何用代码去生成钱包地址呢? 之前我在做钱包的时候, 搜了很多资料, 很少有写如何去生成钱包地址, 当然英文资料也不多. 最后还是去gayhub里面看了开源钱包, 才搞清楚了一些方法. 我们这里使用的是bitcoinj来生成. 一般都是分几个步骤: 生成助记词, 根据助记词生成地址

配置环境

首先我们这里使用的Android studio 3.0, 直接在app/build.gradle里面添加依赖

1
implementation group: 'org.bitcoinj', name: 'bitcoinj-core', version: '0.14.6'

先填坑, 生成助记词需要使用到MnemonicUtils这个类, 但是有坑, 加载助记词列表文件的方式在Android上面根本不行, 会导致Crash

1
2
3
4
5
6
7
8
9
private static List<String> populateWordList() {
URL url = Thread.currentThread().getContextClassLoader()
.getResource("en-mnemonic-word-list.txt");
try {
return readAllLines(url.toURI().getSchemeSpecificPart());
} catch (Exception e) {
return Collections.emptyList();
}
}

懂的人都看出来了吧, 这是java的加载资源方式, 但是安卓需要做平台适配. 我们把en-mnemonic-word-list.txt这个文件放到assets之下, 然后用符合安卓的姿势加载. Good, 没问题了

1
2
3
4
5
6
7
8
9
10
private fun populateWordList(): List<String> {
try {
val fis = App.instance.assets?.open("en-mnemonic-word-list.txt")
return readAllLines(fis!!)
} catch (e: IOException) {
e.printStackTrace()
}

return emptyList()
}

生成助记词

生成助记词需要使用到MnemonicUtils这个类, 要生成助记词很简单, 需要如下代码生成助记词, 助记词的格式就是12个单词

1
2
3
4
5
6
7
//average green proud remember advance trick estate oblige trouble when cube person
private val secureRandom = MySecureRandomUtils.secureRandom()
fun makeMnemonic(): String {
val initialEntropy = ByteArray(16)
secureRandom.nextBytes(initialEntropy)
return MyMnemonicUtils.generateMnemonic(initialEntropy)
}

生成BTC地址

先根据生成的助记词, 生成一些列的种, 运用了 BIP32确定性钱包算法(deterministic wallet algorithm)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private fun createBTCWalletFromWords(words: String): TianWallet {
//把助记词切割成数组
val wordsList = Arrays.asList(*words.split("\\s+".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
val deterministicSeed = DeterministicSeed(wordsList, null, "", 0)
val deterministicKeyChain = DeterministicKeyChain.builder().seed(deterministicSeed).build()
//这里运用了BIP44里面提到的算法, 44'是固定的, 后面的一个0'代表的是币种BTC
var privKeyBTC = deterministicKeyChain.getKeyByPath(parsePath("m/44'/0'/0'/0/0"), true).privKey
//如果是调试模式, 第二个字符串应该填1'
if (BuildConfig.DEBUG) {
privKeyBTC = deterministicKeyChain.getKeyByPath(parsePath("m/44'/1'/0'/0/0"), true).privKey
}

val ecKey = ECKey.fromPrivate(privKeyBTC)
val publickey = Numeric.toHexStringNoPrefixZeroPadded(BigInteger(ecKey.pubKey), 66)
//正式环境应该是主网参数
var privateKey = ecKey.getPrivateKeyEncoded(MainNetParams.get()).toString()
//如果是测试环境, 应该调用测试网参数
if (BuildConfig.DEBUG) {
privateKey = ecKey.getPrivateKeyEncoded(TestNet3Params.get()).toString()
return TianWallet(ecKey.toAddress(TestNet3Params.get()).toString(), publickey, privateKey, words)
}
return TianWallet(ecKey.toAddress(MainNetParams.get()).toString(), publickey, privateKey, words)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

//"m/44'/60'/0'/0"
private fun parsePath(str: String): ImmutableList<ChildNumber> {
val split = str.split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
val arrayList = ArrayList<ChildNumber>()
for (numberStr in split) {
if (!"m".equals(numberStr, ignoreCase = true)) {
val z = numberStr[numberStr.length - 1] == '\''
var newNumberStr = numberStr
if (z) {
newNumberStr = numberStr.substring(0, numberStr.length - 1)
}
arrayList.add(ChildNumber(Integer.parseInt(newNumberStr), z))
}
}
return ImmutableList.copyOf(arrayList)
}

下一篇文章我将介绍一下ETH的地址生成方式.

区块链安卓开发群:区块链安卓开发 431969409