Skip to content
This repository has been archived by the owner on Nov 14, 2023. It is now read-only.

Commit

Permalink
Merge pull request #131 from hangts/develop
Browse files Browse the repository at this point in the history
HD wallet
  • Loading branch information
zhangyelong authored Jul 15, 2020
2 parents b39a5a0 + e11e4af commit 76d012a
Show file tree
Hide file tree
Showing 8 changed files with 219 additions and 63 deletions.
4 changes: 2 additions & 2 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"signature": "tendermint/SignatureSecp256k1",
"pubKey": "tendermint/PubKeySecp256k1"
},
"bip39Path": "44'/118'/0'/0/0",
"bip39Path": "m/44'/118'/0'/0/0",
"keystore": {
"kdf": "pbkdf2",
"cipherAlg": "aes-128-ctr",
Expand Down Expand Up @@ -130,7 +130,7 @@
"signature": "tendermint/SignatureSecp256k1",
"pubKey": "tendermint/PubKeySecp256k1"
},
"bip39Path": "44'/118'/0'/0/0"
"bip39Path": "m/44'/118'/0'/0/0"
},
"language": {
"cn": "chinese_simplified",
Expand Down
6 changes: 5 additions & 1 deletion src/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,16 @@ class Builder {
case Config.chain.iris: {
return require('./chains/iris/builder')();
}
break;
case Config.chain.ethermint: {
return require('./chains/ethermint/ethermint_builder')();
throw new Error("not implement");
// return require('./chains/ethermint/ethermint_builder')();
}
break;
case Config.chain.cosmos: {
return require('./chains/cosmos/builder')();
}
break;
default: {
throw new Error("not correct chain");
}
Expand Down
34 changes: 29 additions & 5 deletions src/chains/cosmos/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,32 @@ class CosmosCrypto extends Crypto {
* @param language
* @returns {*}
*/
create(language) {
let keyPair = CosmosKeypair.create(switchToWordList(language));
create(language, mnemonicLength = 24) {
let keyPair = CosmosKeypair.create(switchToWordList(language), mnemonicLength);
if (keyPair) {
return encode({
address: keyPair.address,
phrase: keyPair.secret,
privateKey: keyPair.privateKey,
publicKey: keyPair.publicKey
publicKey: keyPair.publicKey,
});
}
return keyPair;
}

recover(secret, language) {
let keyPair = CosmosKeypair.recover(secret,switchToWordList(language));
/**
*
* @param language
* @param mnemonicLength 12/15/18/21/24
* @returns mnemonics
*/
generateMnemonic(language, mnemonicLength = 24) {
return CosmosKeypair.generateMnemonic(switchToWordList(language), mnemonicLength);
}

recover(secret, language, path) {
path = path || Config.cosmos.bip39Path;
let keyPair = CosmosKeypair.recover(secret,switchToWordList(language), path);
if (keyPair) {
return encode({
address: keyPair.address,
Expand Down Expand Up @@ -60,11 +71,24 @@ class CosmosCrypto extends Crypto {
}

getAddress(publicKey) {
if (Codec.Bech32.isBech32(Config.cosmos.bech32.accPub, publicKey)) {
publicKey = Codec.Bech32.fromBech32(publicKey);
}
let pubKey = Codec.Hex.hexToBytes(publicKey);
let address = CosmosKeypair.getAddress(pubKey);
address = Codec.Bech32.toBech32(Config.cosmos.bech32.accAddr, address);
return address;
}

encodePublicKey(publicKey){
let pubkey = publicKey;
if (Codec.Bech32.isBech32(Config.cosmos.bech32.accPub, pubkey)) {
pubkey = Codec.Bech32.toBech32(Config.cosmos.bech32.accPub, Codec.Bech32.fromBech32(pubkey));
}else if (Codec.Hex.isHex(pubkey)){
pubkey = Codec.Bech32.toBech32(Config.cosmos.bech32.accPub, pubkey);
}
return pubkey;
}
}

function encode(acc){
Expand Down
77 changes: 54 additions & 23 deletions src/chains/cosmos/keypair.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ const Amino = require('../base');

class CosmosKeypair {

static getPrivateKeyFromSecret(mnemonicS) {
static getPrivateKeyFromSecret(mnemonicS, path) {
let seed = Bip39.mnemonicToSeed(mnemonicS);
let master = Hd.ComputeMastersFromSeed(seed);
let derivedPriv = Hd.DerivePrivateKeyForPath(master.secret,master.chainCode,Config.cosmos.bip39Path);
let derivedPriv = Hd.DerivePrivateKeyForPath(master.secret,master.chainCode, path);
return derivedPriv;
}

Expand All @@ -43,18 +43,11 @@ class CosmosKeypair {
return addr.digest('hex').toUpperCase();
}

static create(language) {
//生成24位助记词
let entropySize = 24 * 11 - 8;
let entropy = Random(entropySize / 8);
let mnemonicS = Bip39.entropyToMnemonic(entropy,language);
while (Util.hasRepeatElement(mnemonicS," ")){
entropy = Random(entropySize / 8);
mnemonicS = Bip39.entropyToMnemonic(entropy,language);
}
static create(language, mnemonicLength){
let mnemonicS = this.generateMnemonic(language, mnemonicLength);

//生成私钥
let secretKey = this.getPrivateKeyFromSecret(mnemonicS);
let secretKey = this.getPrivateKeyFromSecret(mnemonicS, Config.cosmos.bip39Path);
//构造公钥
let pubKey = Secp256k1.publicKeyCreate(secretKey);
pubKey = Amino.MarshalBinary(Config.cosmos.amino.pubKey,pubKey);
Expand All @@ -63,18 +56,43 @@ class CosmosKeypair {
"secret": mnemonicS,
"address": this.getAddress(pubKey),
"privateKey": Codec.Hex.bytesToHex(secretKey),
"publicKey": Codec.Hex.bytesToHex(pubKey)
"publicKey": Codec.Hex.bytesToHex(pubKey),
};
}

static recover(mnemonic,language){
static generateMnemonic(language, mnemonicLength) {
//默认生成24位助记词
let entropySize = 24 * 11 - 8;
switch(mnemonicLength){
case 12:
entropySize = 12 * 11 - 4;
break;
case 15:
entropySize = 15 * 11 - 5;
break;
case 18:
entropySize = 18 * 11 - 6;
break;
case 21:
entropySize = 21 * 11 - 7;
break;
}
let entropy = Random(entropySize / 8);
let mnemonicS = Bip39.entropyToMnemonic(entropy,language);
while (Util.hasRepeatElement(mnemonicS," ")){
entropy = Random(entropySize / 8);
mnemonicS = Bip39.entropyToMnemonic(entropy,language);
}
return mnemonicS;
}

static recover(mnemonic,language, path){
this.checkSeed(mnemonic,language);
//生成私钥
let secretKey = this.getPrivateKeyFromSecret(mnemonic);
let secretKey = this.getPrivateKeyFromSecret(mnemonic, path);
//构造公钥
let pubKey = Secp256k1.publicKeyCreate(secretKey);
pubKey = Amino.MarshalBinary(Config.cosmos.amino.pubKey,pubKey);

return {
"secret": mnemonic,
"address": this.getAddress(pubKey),
Expand All @@ -85,8 +103,8 @@ class CosmosKeypair {

static checkSeed(mnemonic,language){
const seed = mnemonic.split(" ");
if(seed.length != 12 && seed.length != 24){
throw new Error("seed length must be equal 12 or 24");
if(seed.length != 12 && seed.length != 15 && seed.length != 18 && seed.length != 21 && seed.length != 24){
throw new Error("seed length must be equal 12、15、18、21、24");
}
if (!Bip39.validateMnemonic(mnemonic,language)){
throw new Error("seed is invalid");
Expand Down Expand Up @@ -124,9 +142,20 @@ class Hd {
}

static DerivePrivateKeyForPath(privKeyBytes, chainCode, path) {
if (path === 'm' || path === 'M' || path === "m'" || path === "M'") {
return privKeyBytes;
}

let data = privKeyBytes;
let parts = path.split("/");
parts.forEach(function (part) {
parts.forEach(function (part, index) {
if (index === 0) {
if(!(/^[mM]{1}/.test(part))){
throw new Error('Path must start with "m" or "M"');
}
return;
}

let harden = part.slice(part.length-1,part.length) === "'";
if (harden) {
part = part.slice(0,part.length -1);
Expand All @@ -153,7 +182,9 @@ class Hd {

static DerivePrivateKey(privKeyBytes, chainCode, index, harden) {
let data;
let indexBuffer = Buffer.from([index]);
// let indexBuffer = Buffer.from([index]);
let indexBuffer = Buffer.allocUnsafe(4);
indexBuffer.writeUInt32BE(index, 0);
if(harden){
let c = new BN(index).or(new BN(0x80000000));
indexBuffer = c.toBuffer();
Expand All @@ -163,9 +194,9 @@ class Hd {
data = Buffer.concat([data,privKeyBuffer]);
}else{
const pubKey =Secp256k1.publicKeyCreate(privKeyBytes);
if (index ==0){
indexBuffer = Buffer.from([0,0,0,0]);
}
// if (index ==0){
// indexBuffer = Buffer.from([0,0,0,0]);
// }
data = pubKey
}
data = Buffer.concat([data,indexBuffer]);
Expand Down
34 changes: 29 additions & 5 deletions src/chains/iris/crypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,32 @@ class IrisCrypto extends Crypto {
* @param language
* @returns {*}
*/
create(language) {
let keyPair = IrisKeypair.create(switchToWordList(language));
create(language, mnemonicLength = 24) {
let keyPair = IrisKeypair.create(switchToWordList(language), mnemonicLength);
if (keyPair) {
return encode({
address: keyPair.address,
phrase: keyPair.secret,
privateKey: keyPair.privateKey,
publicKey: keyPair.publicKey
publicKey: keyPair.publicKey,
});
}
return keyPair;
}

recover(secret, language) {
let keyPair = IrisKeypair.recover(secret,switchToWordList(language));
/**
*
* @param language
* @param mnemonicLength 12/15/18/21/24
* @returns mnemonics
*/
generateMnemonic(language, mnemonicLength = 24) {
return IrisKeypair.generateMnemonic(switchToWordList(language), mnemonicLength);
}

recover(secret, language, path) {
path = path || Config.iris.bip39Path;
let keyPair = IrisKeypair.recover(secret,switchToWordList(language), path);
if (keyPair) {
return encode({
address: keyPair.address,
Expand Down Expand Up @@ -62,12 +73,25 @@ class IrisCrypto extends Crypto {
}

getAddress(publicKey) {
if (Codec.Bech32.isBech32(Config.iris.bech32.accPub, publicKey)) {
publicKey = Codec.Bech32.fromBech32(publicKey);
}
let pubKey = Codec.Hex.hexToBytes(publicKey);
let address = IrisKeypair.getAddress(pubKey);
address = Codec.Bech32.toBech32(Config.iris.bech32.accAddr, address);
return address;
}

encodePublicKey(publicKey){
let pubkey = publicKey;
if (Codec.Bech32.isBech32(Config.iris.bech32.accPub, pubkey)) {
pubkey = Codec.Bech32.toBech32(Config.iris.bech32.accPub, Codec.Bech32.fromBech32(pubkey));
}else if (Codec.Hex.isHex(pubkey)){
pubkey = Codec.Bech32.toBech32(Config.iris.bech32.accPub, pubkey);
}
return pubkey;
}

// @see:https://github.com/binance-chain/javascript-sdk/blob/master/src/crypto/index.js
exportKeystore(privateKeyHex,password,opts = {}){
if (Utils.isEmpty(password) || password.length < 8){
Expand Down
Loading

0 comments on commit 76d012a

Please sign in to comment.