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'));
          });
      });

  

 

posted @ 2022-02-12 10:53  走走停停走走  Views(356)  Comments(0Edit  收藏  举报