1.6.X 버전에는 recoverTransaction에 대응되는 function이 없나요?

안녕하세요?
Klaytn을 공부하는 중에 문의 사항이 생겨 이렇게 글 올립니다.

1.4.X 버전에는
caver.klay.accounts.recoverTransaction 이 있던데요.
raw transaction에 서명한 address를 찾아내는 function으로 알고 있습니다.
1.6.X버전에서 여기에 대응되는 function은 어떤 건지요?

대응되는 function이 없다면
서명된 transaction을 입력받아 서명 주체가 누군지(address) 알아내는 function 혹은 방법이 있다면 알려주시면 감사하겠읍니다.

감사합니다.

안녕하세요 질문 올려주셔서 감사합니다.

caver.klay.accounts.recoverTrasnaction은 현재 LegacyTransanction 타입만 지원하고 있습니다.
현재 caver.transaction 레이어에 각 트랜잭션 타입 별로 signatures와 feePayerSignatures로부터 서명에 사용된 publicKey를 recover하는 함수를 구현하고 있으며, 해당 기능은 caver-js v1.6.3부터 제공될 예정입니다.

6월 말에 배포 예정되어 있으며, 해당 기능이 배포되면 다시 이 글에 답변 남기도록 하겠습니다. 감사합니다.

옙 상세한 답변 정말 감사드립니다.

혹시 임시적으로라도 하기의 function을 사용해서 signer address를 알 수는 없나요?

caver.transaction.decode(rlpEncoded)

감사합니다.

임시적인 함수는 없고 직접 구현하셔야 합니다
아래 코드 참고 부탁드립니다.
또한 Klaytn의 경우 AccountKey가 update될 수 있으므로 public key로부터 파생된 address가 실제 서명한 Klaytn 계정의 주소가 아닐 수 있습니다.
그러므로 signatures를 검증하실 때에는 public key를 recover한 뒤, caver.rpc.klay.getAccountKey로 계정키를 들고와 비교하여 검증하는 것을 권장드립니다.
해당 signatures와 feePayerSignatures를 검증하는 함수도 추후 1.6.3에 함께 배포될 예정입니다

    const Bytes = require('eth-lib/lib/bytes')
    const Hash = require('eth-lib/lib/hash')

    const rawTx = '0x08f9010e808505d21dba008402faf0809459177716c34ac6e49e295a0e78e33522f14d61ee0194f21460730845e3652aa3cc9bc13b345e4f53984af8d5f845820feaa02b5934c6d26bb3e65edf099d79c57c743d2f70744ca09d3ba9a1099edff9f173a00797886edff4b449c1a599943e3a6003ae9e46b3f3f34862ced327e43fba3a6af845820fe9a063177648732ef855f800eb9f80f68501abb507f84c0d660286a6e0801334a1d2a0620a996623c114f2df35b11ec8ac4f3758d3ad89cf81ba13614e51908cfe9218f845820fe9a086c8ecbfd892be41d48443a2243274beb6daed3f72895045965a3baede4c350ea069ea748aff6e4c106d3a8ba597d8f134745b76f12dacb581318f9da07351511a'
    const tx = caver.transaction.decode(rawTx)

    const recovery = Bytes.toNumber(tx.signatures[0].v)
    const chainId = recovery < 35 ? Bytes.fromNat('0x1') : Bytes.fromNumber((recovery - 35) >> 1)
    if (!tx.chainId) tx.chainId = chainId
    const signingDataHex = tx.getRLPEncodingForSignature()
    const hasedSigningData = Hash.keccak256(signingDataHex)

    const publicKeys = []
    for (const sig of tx.signatures) {
        publicKeys.push(caver.utils.recoverPublicKey(hasedSigningData, sig, true))
    }

    const addresses = []
    for (const pub of publicKeys) {
        const publicHash = Hash.keccak256(pub)
        const addr = `0x${publicHash.slice(-40)}`
    
        const addressHash = Hash.keccak256s(addr.slice(2))
        let checksumAddress = '0x'
        for (let i = 0; i < 40; i++) checksumAddress += parseInt(addressHash[i + 2], 16) > 7 ? addr[i + 2].toUpperCase() : addr[i + 2]
        addresses.push(checksumAddress)
    }
    console.log(publicKeys)
    console.log(addresses)
1 Like

caver-js v1.6.3-rc.1 배포되었습니다.

tx.recoverPublicKeys 를 통해서 서명한 퍼블릭 키를 뽑을 수 있고, caver.utils.publicKeyToAddress를 하면 public key로부터 파생된 주소를 구할 수 있습니다.

또한 caver.validator.validateTransaction은 signatures와 feePayerSignatures의 서명값들이 각각 from, fee payer의 키로 서명된 것이 맞는지 검증하는 함수인데, 이 함수를 사용하셔도 됩니다.

자세한 내용은 release note 참고해 주시기 바랍니다

안녕하세요?

친절하고 빠른 답변에 감사드립니다. 확인하겠습니다.

감사드립니다.

2021년 6월 22일 (화) 오전 11:11, Klaytn Developers Forum를 통한 김지민(Jasmine) <klaytn@discoursemail.com>님이 작성:

1 Like

안녕하세요?
npm update를 통해 caver-js가 1.6.3으로 업데이트가 되지 않아 문의드립니다.

node_modules에 기존에 설치되어 있는 caver-js를 지우고
release-note에 첨부되어 있는 소스코드를 받아서 압축을 푼 후
node_modules에 넣어서 사용하면 될까요?

감사합니다.

caver-js v1.6.3은 아직 배포되지 않았습니다. caver-js v1.6.3-rc.3까지만 배포되었으므로 해당 버전 사용 부탁드립니다.

1 Like

안녕하세요?

caver-js v1.6.3-rc.3를 적용하여 recoverPublicKeys() 와 publicKeyToAddress() 기능을 확인해 보려는 중에 예상과 다른 결과값이 나와서 문의 드립니다.

from: “0x101721df2da08ae23d0e21ad4e4d8a2ca087a588”
to: “0x7b3f9ff0f4215c41b3e4a49510b540e80d0c790b”
value: “1000000000000000000” (1KLAY)

위 transaction을 sign한 rlpEncoding 값을 다시 decode한 후 recoverPublicKeys()를 호출하여 public key를 뽑고, 그 public key에서 address를 복원하려고 했는데

결과 값이 0x101721df2da08ae23d0e21ad4e4d8a2ca087a588 이 나와야 할 거 같은데 다른 값이 나옵니다. (0x0DdB6B873d16D7338CF2961c2eDaEf0703225F0c)

서명자는 legacy keyring으로도 수행하였고, roleBased keyring으로도 수행해봤는데 결과는 다 서명자 주소와 다른 값이 출력되었습니다.

아래는 코드 원문입니다.


let testFunc = async function() {
    try {
        let keystore = require('./element01.json');
        let keyring  = await caver.wallet.keyring.decrypt(keystore, "password");
        await caver.wallet.add(keyring)
        let rawTx = {
            from: keyring.address,
            to: "0x7b3f9ff0f4215c41b3e4a49510b540e80d0c790b",
            value: "1000000000000000000"};
        rawTx.nonce = caver.utils.toHex(await caver.rpc.klay.getTransactionCount(rawTx.from));
        rawTx.gas = 100000;
        const tx = caver.transaction.valueTransfer.create(rawTx);
        let signed = await caver.wallet.sign(keyring.address, tx);
        let rlpEncoded = await signed.getRLPEncoding();
        let decodedTx = await caver.transaction.decode(rlpEncoded);
        let recoveredPubKey = await decodedTx.recoverPublicKeys();
        let recoveredAddr = await caver.utils.publicKeyToAddress(recoveredPubKey)
        console.log(recoveredAddr)
    } catch(error) {
        console.log(colors.red(error));
        process.exit(1);
    }
}
testFunc();

감사합니다.

caver.utils.publicKeyToAddress는 단순하게 public key로부터 derive된 address를 단순하게 리턴하는 함수이므로, 실제 Klaytn account의 key가 업데이트 된 경우 public key에서 derive된 address가 다를 수 있습니다. 아래와 같이 제가 따로 해보았을 때에는 정상적인 결과가 나오는 것을 확인했습니다. 사용하시는 keystore가 어떤 키링인지 알 수가 없어서 정확하게 답변 드리기가 어렵네요. account update를 하신 경우 caver.utils.publicKeyToAddress를 사용하면 무의미한 주소값을 얻게되므로 검증할 때에는 public key를 사용하여 검증하십시오.

const keyring = caver.wallet.keyring.generate()
caver.wallet.add(keyring)
console.log(`keyring address: ${keyring.address}`)

const tx = caver.transaction.valueTransfer.create({from: keyring.address, to: keyring.address, value: 1, gas: 25000})
await caver.wallet.sign(keyring.address, tx)

console.log(tx)

const publicKeys = tx.recoverPublicKeys()
console.log(`public keys: ${publicKeys.toString()}`)

const derivedAddressFromPublicKey = caver.utils.publicKeyToAddress(publicKeys[0])
console.log(`Address compare : ${keyring.address.toLowerCase()} / ${derivedAddressFromPublicKey.toLowerCase()}`)
1 Like

답변 정말 감사드립니다.

예제로 보내주신 코드를 돌려보니 결과가 맞게 나옵니다.

저의 예제코드와 비교해봤을 때 차이가

//// 이상하게 나오는 코드
const derivedAddressFromPublicKey = caver.utils.publicKeyToAddress(publicKeys)

//// 정상적으로 나오는 코드
const derivedAddressFromPublicKey = caver.utils.publicKeyToAddress(publicKeys[0])

였네요.

감사합니다.