Web钱包私钥导出新旧两种地址格式(加班到深夜修改的一个功能点)
前言:
这个问题是中午的时候,被用户反馈,当时还不觉得有问题。
调到最后发现一个小坑是:私钥地址,带0x和不带0x是有区别的。我们的Web钱包不要带0x,否则会还原出另一个新地址。
1. jssdk修改
(1)用私钥还原地址
/** 新地址
* Get address from private key.
* @param {string} privateKeyHex the private key hexstring
* @param {string} prefix address prefix
* @return {string} address
*/
export const getAddressFromPrivateKey = (privateKeyHex, prefix) => {
return getAddressFromPubKey(getPubKeyHexFromPrivateKey(privateKeyHex), prefix)
}
// 旧地址 Legacy
export const getAddressFromPrivateKeyLegacy = (privateKeyHex, prefix) => {
return getAddressFromPublicKeyLegacy(getPubKeyHexFromPrivateKey(privateKeyHex), prefix)
}
/** 新方法
* Gets address from pubKey with hex format.
* @param {string} publicKey publicKey hexstring
* @param {string} prefix address prefix
* @return {string} address
*/
export const getAddressFromPubKey = (publicKey, prefix) => {
publicKey = publicKey.slice(0, 2) === '0x' ? publicKey.slice(2) : publicKey
const publicKey1 = Buffer.from(publicKey, 'hex')
publicKey = Buffer.from(secp256k1.publicKeyConvert(new Uint8Array(publicKey1), false)).slice(1)
const hash = createKeccakHash('keccak256').update(publicKey).digest()
return encodeAddressToBech32(hash.slice(-20).toString('hex'), prefix)
}
// 旧的方法Legacy
export const getAddressFromPublicKeyLegacy = (publicKeyHex, prefix) => {
// const pubKey = ec.keyFromPublic(publicKeyHex, "hex")
// const pubPoint = pubKey.getPublic()
// const compressed = pubPoint.encodeCompressed()
// const hexed = ab2hexstring(compressed)
const hash = sha256ripemd160(publicKeyHex) // https://git.io/fAn8N
const address = encodeAddress(hash, prefix)
return address
}
export const encodeAddress = (value, prefix = "ex", type = "hex") => {
const words = bech32.toWords(Buffer.from(value, type))
return bech32.encode(prefix, words)
}
(2)新旧两种地址在转账交易类型
设置账户信息,也要体现两种地址
/**
* @param {string} privateKey
* @param {string} prefix
* @return {OKEXChainClient}
*/
async setAccountInfo(privateKey, prefix = "ex", isPrivatekeyOld = 0) {
if(!privateKey) {
const address = await wallet.getAddress();
if (!address) throw new Error("invalid privateKey: " + privateKey)
await this.setAccountInfoByWallet(address);
return this;
}
if (privateKey !== this.privateKey) {
let address = crypto.getAddressFromPrivateKey(privateKey, prefix)
if (isPrivatekeyOld) {
address = crypto.getAddressFromPrivateKeyLegacy(privateKey, prefix)
}
if (!address) throw new Error("invalid privateKey: " + privateKey)
if (address === this.address) return this
this.privateKey = privateKey
this.address = address
const data = await this.getAccount(address)
this.account_number = this.getAccountNumberFromAccountInfo(data)
}
return this
}
构造两种交易:"tendermint/PubKeySecp256k1” 和"ethermint/PubKeyEthSecp256k1",及两种不同的签名。
// PrivatekeyHex
if (typeof privateKeyHexOrSigner === 'string') {
const jsonStr = JSON.stringify(signMsg)
const signBytes = Buffer.from(jsonStr)
const privateKey = Buffer.from(privateKeyHexOrSigner, "hex")
const signature = isPrivatekeyOldAddress ? crypto.signPrivateKeyOldAddress(signBytes.toString("hex"), privateKey)
: crypto.sign(signBytes.toString("hex"), privateKey)
const pubKey = crypto.encodePubKeyToCompressedBuffer(crypto.getPubKeyFromPrivateKey(privateKey))
signatures = [{
pub_key: {
type: isPrivatekeyOldAddress ? "tendermint/PubKeySecp256k1" : "ethermint/PubKeyEthSecp256k1",
value:pubKey},
signature: signature,
}]
}
/** 旧签名
* Generates a signature (64 byte <r,s>) for a transaction based on given private key.
* @param {string} signBytesHex - Unsigned transaction sign bytes hexstring.
* @param {string | Buffer} privateKey - The private key.
* @return {Buffer} Signature. Does not include tx.
*/
export const signPrivateKeyOldAddress = (signBytesHex, privateKey) => {
const msgHash = sha256(signBytesHex)
const msgHashHex = Buffer.from(msgHash, "hex")
const signature = ecc.sign(msgHashHex, Buffer.from(privateKey, "hex")) // enc ignored if buffer
return signature
}
/** 新签名
* Sign msg with privateKey and Msg in hex format.
* @param {string} msgHex msg in hex format.
* @param {string} privateKey - The private key in hex format.
* @return {Buffer} Signature.
*/
export const sign = (msgHex, privateKey) => {
const msg = Buffer.from(msgHex, "hex")
const msgHash = createKeccakHash('keccak256').update(msg).digest()
const signature = ecc.sign(msgHash, Buffer.from(privateKey, "hex")) // enc ignored if buffer
return signature
}
2. Web前端修改
(1)还原地址处
validatePrivateKey = async () => {
try {
let { privateKey, password } = this.state;
privateKey = privateKey.replace(/^(0x)/, '');
if (!/^[\d|a-f]{64}$/.test(privateKey)) {
throw new Error('not pass the reg');
}
const oldAddress = crypto.getAddressFromPrivateKeyLegacy(privateKey);
const newAddress = crypto.getAddressFromPrivateKey(privateKey);
const oldBalance = await this.fetchAcount(oldAddress);
const newBalance = await this.fetchAcount(newAddress);
this.setState({
isValidatedPrivateKey: true,
buttonLoading: false,
isNone: false,
oldAddress,
newAddress: crypto.convertBech32ToHex(newAddress)[0],
oldBalance: calc.showFloorTruncation(oldBalance),
newBalance: calc.showFloorTruncation(newBalance),
step: 2,
});
const keyStore = crypto.generateKeyStore(privateKey, password);
walletUtil.setUserInSessionStroage(privateKey, keyStore);
this.props.commonAction.setPrivateKey(privateKey);
// util.go(DesktopTypeMenu.current ? DesktopTypeMenu.current.url : void 0);
} catch (e) {
this.setState({
isValidatedPrivateKey: false,
buttonLoading: false,
isNone: false,
});
}
};
// 保留最后的尊严,哈哈哈
selectPathType = (type) => {
window.localStorage.setItem(env.envConfig.privatekeyPathType, type);
if (type === 'old') {
window.localStorage.setItem(
env.envConfig.privatekeyOldPathPrivatekey,
this.state.privateKey
);
} else {
window.localStorage.setItem(
env.envConfig.privatekeyOldPathPrivatekey,
''
);
}
this.setState({
pathType: type,
});
};
账户部分
setUserInSessionStroageByWalletConnect(addr) {
const user = {
addr,
};
window.localStorage.setItem(env.envConfig.dexUser, JSON.stringify(user));
window.OK_GLOBAL.senderAddr = addr;
window.OK_GLOBAL.isLogin = true;
},
// 重点是
setUserInSessionStroage(privateKey, keyStore) {
const privatekeyPathType = window.localStorage.getItem(
env.envConfig.privatekeyPathType
);
let addr = crypto.getAddressFromPrivateKey(privateKey);
if (privatekeyPathType === 'old') {
addr = crypto.getAddressFromPrivateKeyLegacy(privateKey);
}
const user = {
addr,
info: keyStore,
};
window.localStorage.setItem(env.envConfig.dexUser, JSON.stringify(user));
window.OK_GLOBAL.senderAddr = addr;
window.OK_GLOBAL.generalAddr = crypto.convertBech32ToHex(addr)[0];
window.OK_GLOBAL.isLogin = true;
},
还有URL部分,涉及到跨域。
export const DEFAULT = window.okGlobal.mainDomain || 'https://www.okx.com';
export function getApiUrl(apiUrl = DEFAULT) {
const protocol = window.location.protocol;
if (/file/.test(protocol) || window.location.hostname === '127.0.0.1')
return apiUrl;
return `${window.location.protocol}//${window.location.host}`;
}
export function getCurrentApiUrl() {
// let url = getApiUrl();
let url = 'https://www.okx.com';
const currentNode = storage.get('currentNode');
if (currentNode && currentNode.httpUrl) url = currentNode.httpUrl;
return url;
}
(2)发送交易处
const isPrivatekeyOld = oldPrivate ? 1 : 0;
okexchainClient
.setAccountInfo(privateKey, 'ex', isPrivatekeyOld)
.then(() => {
okexchainClient
.sendSendTransaction(
address,
amountStr,
symbol,
note,
null,
isPrivatekeyOld
)
.then((res) => {
if (res.result.code) {
this._transferErr(
toLocale(`error.code.${res.result.code}`) ||
toLocale('trans_fail')
);
} else {
this._transferSuccess(onSuccess);
}
})
.catch(() => {
this._transferErr(toLocale('trans_fail'));
});
});

浙公网安备 33010602011771号