Klaytn Offline Signing Using Caver-js

์•ˆ๋…•ํ•˜์„ธ์š”,
caver-js ์ด์šฉํ•ด์„œ raw transaction data ์ƒ์„ฑ ํ›„ ์˜คํ”„๋ผ์ธ ์„œ๋ช…์„ ํ•˜๊ณ  ์‹ถ์€๋ฐ docs ์— ๋‚˜์˜จ ์„ค๋ช…์— ๋”ฐ๋ผ valueTransfer transaction ์„ hash ํ•˜๊ณ  signature ์ƒ์„ฑ ํ›„ ์ „์†กํ•˜๋Š” ๊ณผ์ •์ด ์ž˜ ๋˜์ง€ ์•Š์•„ ์งˆ๋ฌธ ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค.

์•„๋ž˜๋Š” ์ œ๊ฐ€ ํ…Œ์ŠคํŠธํ•˜๊ณ  ์žˆ๋Š” ์ฝ”๋“œ ์ „๋ฌธ์ž…๋‹ˆ๋‹ค.

const Caver = require('caver-js')
const { numberToHex } = require('caver-js/packages/caver-utils')
const Hash = require('eth-lib/lib/hash')
const RLP = require('eth-lib/lib/rlp')

const caver = new Caver('https://klaytn-baobab.blockpi.network/v1/rpc/public') // Boabob testnet

async function main() {
    x = numberToHex('94615564755326464618865695935486540640291201192934633168530032620020725956813')
    y = numberToHex('23139757045517286211017265994855088265935370642351252538574909408042960511313')
    pub = x + y.slice(2)
    sender = caver.utils.publicKeyToAddress(pub)  // test sender
    console.log("sender address:", sender)
    to_address = '0x2bb947ba1ae588e043e0f7ee166748766b4d605b'   // baobab wallet

    chainId = 1001 // baobob
    const tx = caver.transaction.valueTransfer.create({
        // type: '0x08', // 'TxTypeValueTransfer'
        nonce: 0,
        gasPrice: '0x5d21dba00',
        gas: '0x0a',
        to: to_address,
        value: caver.utils.toPeb(10, 'KLAY'),
        from: sender, // '0x14E8d03a51F2b77a06cf4AA6600D608aE8361C27',
        chainId: numberToHex(chainId),
    })
    const encoded = tx.getCommonRLPEncodingForSignature()
    const sigHash = Hash.keccak256(encoded)
    // const sigHash = Hash.keccak256("\x19Klaytn Signed Message:\n" + encoded.length + encoded)
    console.log("msg hash:",sigHash) // msg

    /* offline signing - make signature(v, r, s)*/

    v = numberToHex(chainId*2 + 35 + 0)
    r = numberToHex('15889454641529804590699241749915670778420230958983654827606059186073800710978');
    s = numberToHex('39389016946565651388873849274764251362165017942396879121292298320138309110729');
    // tx.appendSignatures([v, r, s])
    tx.signatures = [v, r, s]
    txHashRLP = tx.getRLPEncoding()

    pubkey = tx.recoverPublicKeys()
    console.log("recovered address:",caver.utils.publicKeyToAddress(pubkey))
    // const receipt = await caver.klay.sendSignedTransaction(txHashRLP)
    const receipt = await caver.rpc.klay.sendRawTransaction(txHashRLP)
    console.log(receipt)
}

main();

  1. ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋กœ ์ƒ์„ฑํ•œ x, y ๊ฐ’์œผ๋กœ create sender address
  2. create valueTransfer transaction object
    • RLP encoding-> Hash ํ•˜์—ฌ Sign ํ•  msg ์ƒ์„ฑ
  3. signature data ์‚ฝ์ž…
  4. recovered address ํ™•์ธ
  5. ์ „์†ก

์œ„ ๊ณผ์ •์— ๋”ฐ๋ผ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๊ณ  ์ด๋”๋ฆฌ์›€ ํŠธ๋žœ์žญ์…˜์„ ์ƒ์„ฑํ•˜๊ณ  ์„œ๋ช…ํ•˜๋Š” ๊ฒƒ์—๋Š” ์„ฑ๊ณตํ–ˆ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•ด๋ณด๋ฉด, ์•„๋ž˜์™€ ๊ฐ™์ด ๋ณต๊ตฌ์— ์‚ฌ์šฉํ•˜๋ ค๋Š” public key ๊ฐ€ ์ž˜๋ชป๋๋‹ค๋Š” ์—๋Ÿฌ๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.

(node:34523) UnhandledPromiseRejectionWarning: Error: Invalid public key: 0x1d53faa428eabd2db08bbe3c3256b6a00ea4246bdbfdde6cf3f4fe65a3b613cf475c7f276b75f2947220ee1cb2c8fff00a835e4028502e735c0c056847e7ae8b
    at Object.publicKeyToAddress (/Users/hoodie/klaytn/caver-js/packages/caver-utils/src/utils.js:1484:59)
    at main (/Users/hoodie/klaytn/tx.js:49:50)
    at Object.<anonymous> (/Users/hoodie/klaytn/tx.js:55:1)
    at Module._compile (internal/modules/cjs/loader.js:1063:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1092:10)
    at Module.load (internal/modules/cjs/loader.js:928:32)
    at Function.Module._load (internal/modules/cjs/loader.js:769:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
    at internal/main/run_main_module.js:17:47
(Use `node --trace-warnings ...` to show where the warning was created)
....

์ฃผ์†Œ ๋ณต๊ตฌ ์ฝ”๋“œ๋ฅผ ์ฃผ์„์ฒ˜๋ฆฌํ•˜๊ณ  ์‹คํ–‰ํ•˜๋ฉด signature ๊ฐ€ ์ž˜๋ชป๋๋‹ค๋Š” ์—๋Ÿฌ๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.

UnhandledPromiseRejectionWarning: Error: Returned error: invalid transaction v, r, s values of the sender
...

3๋ฒˆ ๊ณผ์ •์ด ์ด๋”๋ฆฌ์›€์„ ์ „์†กํ•  ๋•Œ์—๋„ ๋ฌธ์ œ๊ฐ€ ๋๋˜ ๋ถ€๋ถ„์ด๊ณ , Klaytn docs ์˜ RLP Encoding for Signature ์— ์ ํ˜€์žˆ๋Š” ์•„๋ž˜ ์ˆ˜๋„์ฝ”๋“œ๋ฅผ ์ฐธ๊ณ ํ–ˆ์ง€๋งŒ ์ ์ ˆํžˆ ๊ตฌํ˜„์ด ๋œ ๊ฒƒ์ธ์ง€ ๊ฒ€์ฆํ•˜์ง€ ๋ชปํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— sign ์— msg๋ฅผ ์ ์ ˆํžˆ ์‚ฌ์šฉํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ์ง€ ์•Š๋‚˜ ์ƒ๊ฐํ•ฉ๋‹ˆ๋‹ค. ๋ฌผ๋ก  ๋‹ค๋ฅธ ์›์ธ์ด ์ถ”๊ฐ€๋กœ ์žˆ์„ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค๋งŒ ๋ฐœ๊ฒฌํ•˜์ง€ ๋ชปํ•˜๊ณ  ์žˆ์–ด์„œ ๋‹ค์–‘ํ•œ ์‹œ๊ฐ์—์„œ ์ „๋ฌธ๊ฐ€๋ถ„๋“ค์˜ ๋„์›€์„ ์š”์ฒญ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

SigRLP = encode([encode([type, nonce, gasPrice, gas, to, value, from]), chainid, 0, 0])
SigHash = keccak256(SigRLP)
Signature = sign(SigHash, <private key>)

์ฝ”๋“œ๊ฐ€ ์ž˜๋ชป๋œ ๋ถ€๋ถ„์ด ์žˆ๋‹ค๋ฉด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ˆ˜์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.
์‹œ๊ฐ„ ๋‚ด์–ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋ง๋ถ™์ด์ž๋ฉด ์„œ๋ช…์—๋Š” Alice TSS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(ECDSA)๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. private key๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์—†์–ด์„œ public key ๋กœ ์ง์ ‘ ์ฃผ์†Œ๋ฅผ ๋งŒ๋“ค์–ด ์‚ฌ์šฉํ•˜๋ ค๊ณ  ํ–ˆ์Šต๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š”, ํ•ด๋‹น ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด๋ณธ ๊ฒฐ๊ณผ ์™ธ๋ถ€์—์„œ ์ƒ์„ฑํ•œ x, y๊ฐ’์œผ๋กœ ๋งŒ๋“  public key์™€, ์•„๋ž˜์—์„œ tx๋กœ๋ถ€ํ„ฐ recoverํ•œ public key๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

recoverPublicKeys()๋Š” ํŠธ๋žœ์žญ์…˜ ํ•„๋“œ ์ค‘ signature์œผ๋กœ๋ถ€ํ„ฐ public key๋ฅผ recoverํ•˜๋Š”๋ฐ์š”, ์ด ๊ฐ’์ด ์›๋ณธ ๊ฐ’๊ณผ ๋‹ค๋ฅด๋‹ค๋Š” ๊ฒƒ์€ ์ž‘์„ฑ์ž๋‹˜์ด ๋„ฃ์–ด์ค€ signature (v, r, s)๊ฐ’์ด ์ž˜๋ชป๋๋‹ค๊ณ  ๋ณผ ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

private key๋ฅผ ๋‹ค๋ฃฐ ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์ด ์–ด๋–ค ์˜๋ฏธ์ด์‹ ์ง€ ๋ชจ๋ฅด๊ฒ ์ง€๋งŒ ์ด๋ฏธ signature๊ฐ€ ์žˆ๋‹ค๋Š” ๊ฒƒ์€ private key๋กœ signing์„ ํ•˜์‹  ๊ฒƒ์ด ์•„๋‹ˆ์‹ ๊ฐ€์š”? ๊ทธ๋ ‡๋‹ค๋ฉด caver๋กœ wallet instance๋ฅผ ์ƒ์„ฑ ํ›„ signing์„ ํ•˜์‹œ๊ณ , ํ•ด๋‹น ํŠธ๋žœ์žญ์…˜์„ ์ „์†กํ•˜๋Š” ๊ฒƒ์ด ๋ณดํ†ต์˜ ํ”„๋กœ์„ธ์Šค๋ผ๊ณ  ์ƒ๊ฐ๋ฉ๋‹ˆ๋‹ค.

์ผ๋‹จ ํ•ด๋‹น ๋ฐฉ์‹์„ ๊ผญ ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š” ์ด์œ ๊ฐ€ ์žˆ์œผ์‹œ๋‹ค๋ฉด, ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ†ตํ•ด ์ƒ์„ฑํ•˜์‹  v, r ,s๊ฐ’์ด ์˜ฌ๋ฐ”๋ฅด์‹ ์ง€ ํ™•์ธํ•ด๋ณด์‹œ๊ณ , ๊ทธ๊ฒƒ์ด ์•„๋‹ˆ๋ผ๋ฉด private key๋Š” env๋‚˜ secret.jsonํŒŒ์ผ๋“ฑ์„ ์‚ฌ์šฉํ•ด์„œ ๋กœ์ปฌ์—์„œ ๊ด€๋ฆฌํ•˜๋ฉด์„œ ํ•„์š”ํ•œ ๊ณณ์— importํ•˜์‹  ํ›„ signing์„ ํ•˜์‹œ๋Š” ๊ฒƒ์ด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๊ด€๋ จํ•ด์„œ ์ฐธ๊ณ ํ•˜์‹ค๋งŒํ•œ ๋งํฌ ๊ณต์œ ๋“œ๋ฆฝ๋‹ˆ๋‹ค. [๋งํฌ]

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š” ์šฐ์„  ๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

private key ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๋‹ค๋Š” ๊ฒƒ์€ ์ œ๊ฐ€ TSS ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์ด๋Š” ๋‹ค์ˆ˜๊ฐ€ share ๋ฅผ ๊ฐ€์ง€๊ณ  ํ•˜๋‚˜์˜ ์„œ๋ช…์— ์ฐธ๊ฐ€ํ•˜๋Š” ๋ฐฉ์‹์ด๋ฏ€๋กœ ์ผ๋ฐ˜์ ์ธ ์„œ๋ช… ๊ณผ์ •์—์„œ ์‚ฌ์šฉํ•˜๋Š” ํ•˜๋‚˜์˜ private key ๋ฅผ ๋„์ถœํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ private key๋ฅผ ์ €์žฅํ•ด์„œ ์ด์šฉํ•˜๋Š” ๋ฐฉ์‹์€ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†๊ณ  tx data๋ฅผ hash ํ•˜์—ฌ ์„œ๋ช…ํ•˜๊ณ  ์ƒ์„ฑ๋œ signature ๋ฅผ ์ง์ ‘ ์ง‘์–ด๋„ฃ์–ด ์ „์†กํ•˜๋Š” ๋ฐฉ์‹์ด ์ ํ•ฉํ•ฉ๋‹ˆ๋‹ค.

์–ธ๊ธ‰ํ•ด์ฃผ์‹ ๋Œ€๋กœ, ์ œ๊ฐ€ public key๋กœ ๋„์ถœํ•œ address ์™€ tx์— ์‚ฌ์šฉํ•œ signature ์—์„œ ๋ณต๊ตฌํ•œ address ๊ฐ€ ๋‹ค๋ฅด๋‹ค๋Š” ๊ฒƒ์€ ์•Œ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ์„œ๋ช…๋ฐฉ์‹์ด ์ž˜๋ชป๋˜์–ด์„œ invalid signature ๋ฅผ ๋งŒ๋“ค์—ˆ๋‹ค๋Š” ๊ฒƒ์ด ์ฃผ์š”ํ•œ ๋ฌธ์ œ๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ์š”, ๊ทธ ์›์ธ์œผ๋กœ ๋‘๊ฐ€์ง€ ๊ฐ€๋Šฅ์„ฑ์„ ์ œ์‹œํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  1. ์„œ๋ช… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์ž˜๋ชป๋˜์—ˆ๋‹ค.
    โ†’ ๊ทธ๋Ÿฌ๋‚˜ ์ œ๊ฐ€ ์ง€๊ธˆ TSS๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๋งŒ๋“ค์–ด๋‚ธ address ๋กœ ์ด๋”๋ฆฌ์›€ ์„œ๋ช…์„ ์„ฑ๊ณต์ ์œผ๋กœ ๋ณด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ์งˆ๋ฌธ ๊ธ€์—๋„ ๋งํ–ˆ๋“ฏ์ด sign ๊ณผ์ •์— ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ๊ฐ€๋Šฅ์„ฑ์€ ๋‚ฎ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ์Šต๋‹ˆ๋‹ค.

  2. ์„œ๋ช…์— ์‚ฌ์šฉํ•œ msg hash ๊ฐ€ ์ž˜๋ชป๋˜์—ˆ๋‹ค.
    โ†’ ์ด ๊ฐ€๋Šฅ์„ฑ์ด ๊ฐ€์žฅ ํฌ๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋Š”๋ฐ์š”, ์ด์œ ๋Š” ์ œ๊ฐ€ ์ด๋”๋ฆฌ์›€ ์„œ๋ช…์— ์‹คํŒจํ–ˆ์„๋•Œ ์œ„์™€ ๋น„์Šทํ•˜๊ฒŒ invalid signature ๋˜๋Š” insufficient gas error๊ฐ€ ๋ฐœ์ƒํ–ˆ์—ˆ๊ณ  ์ด๊ฒƒ์ด ์ง€์ ํ•˜์‹ 

public key๋กœ ๋„์ถœํ•œ address ์™€ tx์— ์‚ฌ์šฉํ•œ signature ์—์„œ ๋ณต๊ตฌํ•œ address ๊ฐ€ ๋‹ค๋ฅด๋‹ค

์™€ ๊ฐ™์€ ๋ฌธ์ œ์˜€์œผ๋ฉฐ ๊ฒฐ๊ณผ์ ์œผ๋กœ ์ด ๋ฌธ์ œ๋Š”

tx data๋ฅผ ์ž˜๋ชป๋œ ๋ฐฉ๋ฒ•์œผ๋กœ hashํ•˜์—ฌ ์„œ๋ช…ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

    const encoded = tx.getCommonRLPEncodingForSignature()
    const sigHash = Hash.keccak256(encoded)

๋”ฐ๋ผ์„œ ์œ„์— ์ฝ”๋“œ์—์„œ ์‚ฌ์šฉํ•œ hash ๋ถ€๋ถ„์ด ์ ์ ˆํžˆ ์‚ฌ์šฉ๋˜์—ˆ๋Š”์ง€ ๋‹ค์‹œ ํ•œ ๋ฒˆ ์—ฌ์ญค๋ณด๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

๋˜ ๋‹ค๋ฅธ ๋ฌธ์ œ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š”์ง€๋„ ๊ฒ€ํ† ํ•ด์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

const encoded = tx.getCommonRLPEncodingForSignature()
const sigHash = utils.hashMessage(encoded)

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋‹ค๋ฅธ hash ๊ฐ’์ด ๋‚˜์™€์„œ ์‹œ๋„ํ•ด๋ดค๋Š”๋ฐ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค

๋„ค, ์•„๋งˆ ํŠธ๋žœ์žญ์…˜์„ ํ•ด์‹ฑํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ฌธ์ œ๊ฐ€ ์žˆ์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

caver-js์—์„œ ์ œ๊ณตํ•˜๋Š” TransactionHasher.getHashForSignature๋ฅผ ์‚ฌ์šฉํ•ด๋ณด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋‹ต๋ณ€ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์•Œ๋ ค์ฃผ์‹  ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•ด๋„ ๊ฐ™์€ ์—๋Ÿฌ๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.

ํ˜น์‹œ ์ด๋”๋ฆฌ์›€๊ณผ ํด๋ ˆ์ดํŠผ์— ์‚ฌ์šฉ๋˜๋Š” ์„œ๋ช… ๋ฐฉ์‹์ด ๋‹ค๋ฅธ ์ ์ด ์žˆ์„๊นŒ์š”?

๋ฉ”์„ธ์ง€ ํ•ด์‹ฑ ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋ผ๋Š” ๊ฐ€๋Šฅ์„ฑ๋„ ์ƒ๊ฐํ•ด๋ณด๋ ค ํ•ฉ๋‹ˆ๋‹ค.

์„œ๋ช… ๋ฐฉ์‹์€ ๊ธฐ๋ณธ์ ์œผ๋กœ ๋™์ผํ•˜๋‚˜, ํด๋ ˆ์ดํŠผ์—๋Š” ์ด๋”๋ฆฌ์›€๊ณผ ๋‹ค๋ฅด๊ฒŒ ๋‹ค์–‘ํ•œ ํŠธ๋žœ์žญ์…˜ ํƒ€์ž…์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

์ง€๊ธˆ ์ œ๊ฐ€ ์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊นŒ์ง€ ํ™•์ธํ•˜์ง„ ์•Š์•„์„œ ํ™•์‹คํ•˜์ง„ ์•Š์ง€๋งŒ, ์ง€๊ธˆ ์ง„ํ–‰ํ•˜์‹œ๋ ค๋Š” ํŠธ๋žœ์žญ์…˜ ํƒ€์ž…๊ณผ๋Š” ํฐ ๊ด€๊ณ„๊ฐ€ ์—†์„ ๊ฒƒ ๊ฐ™๊ธด ํ•ฉ๋‹ˆ๋‹ค.

ํ˜น์‹œ ๋ชจ๋ฅด๋‹ˆ ์ฐธ๊ณ  ์ž๋ฃŒ ์ฒจ๋ถ€ํ•ด๋“œ๋ฆฝ๋‹ˆ๋‹ค.[๋งํฌ] ํ™•์ธํ•ด๋ณด์‹œ๊ณ , ๋งŒ์•ฝ ํ•ด๊ฒฐํ•˜์‹œ๋ฉด ๋‹ค๋ฅธ ๋ถ„๋“ค์„ ์œ„ํ•ด ์—ฌ๊ธฐ์—๋„ ๊ณต์œ  ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค:)

๊ด€๋ จํ•˜์—ฌ ์œ ์‚ฌํ•œ ๋ฌธ์ œ๋ฅผ ๊ฒช์—ˆ๋˜(ํ•ด์‹ฑ ๊ณผ์ •์—์„œ) ์งˆ๋ฌธ๋„ ์ฒจ๋ถ€๋“œ๋ฆฝ๋‹ˆ๋‹ค. [๋งํฌ]

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

1๊ฐœ์˜ ์ข‹์•„์š”

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

์ฐธ๊ณ ํ•ด์„œ ๊ณ„์† ์ง„ํ–‰ํ•ด๋ณด๊ณ  ํ•ด๊ฒฐํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋‹ค๋ฅธ ๋ถ„๋“ค ์˜๊ฒฌ์ด๋‚˜ ์ƒˆ๋กœ์šด ๊ฐ€๋Šฅ์„ฑ ์ œ์‹œํ•ด์ฃผ์‹œ๋Š” ๊ฒƒ๋„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค.

์•ˆ๋…•ํ•˜์„ธ์š” @Lewis ๋ฌธ์ œ ํ•ด๊ฒฐํ•ด์„œ ๋‹ต๊ธ€ ๋‚จ๊น๋‹ˆ๋‹ค.

๋จผ์ € ๋ฌธ์ œ๋Š” tx data hash ์™€ ๊ด€๋ จ์ด ์žˆ์—ˆ๋˜ ๊ฒƒ์ด ๋งž์•˜์ง€๋งŒ, ๋ฐฉ๋ฒ•์ด ์ž˜๋ชป๋๋˜ ๊ฒƒ์€ ์•„๋‹ˆ๊ณ  hex prefix ๋•Œ๋ฌธ์— ์ƒ๊ธฐ๋Š” ์ฒ˜๋ฆฌ ์˜ค๋ฅ˜์˜€์Šต๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ ๊ธฐ์กด ์ฝ”๋“œ์—์„œ utils์— ์žˆ๋Š” ํ•จ์ˆ˜๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๊ฟ” ํ•ด๊ฒฐํ•˜์˜€์Šต๋‹ˆ๋‹ค.

    const { toHex, stripHexPrefix } = require('caver-js/packages/caver-utils')
    const { getHashForSignature } = require('caver-js/packages/caver-transaction/src/transactionHasher/transactionHasher')
    const Hash = require('eth-lib/lib/hash')

    // txdata hash ํ•˜๋Š” ๊ธฐ์กด ๋ฐฉ๋ฒ•
    const encoded = tx.getRLPEncodingForSignature()
    const sigHash = Hash.keccak256(encoded)
    console.log("msg hash:",stripHexPrefix(sigHash)) 

    // ์œ„์™€ ๊ฐ™์€ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•˜๋Š” ํ•จ์ˆ˜
    const hash = getHashForSignature(tx)
    console.log("hasher:",stripHexPrefix(hash))

์œ„์˜ ๋‘๊ฐ€์ง€ ๋ฐฉ๋ฒ• ๋ชจ๋‘ ์˜ณ์€ ๊ฒฐ๊ณผ๋ฅผ ์ถœ๋ ฅํ•˜๋‹ˆ ์ด ๊ธ€์„ ๋ณด๋Š” ๋‹ค๋ฅธ ๋ถ„๋“ค์ด hash ํ•˜์‹ค ๋•Œ ์ฐธ๊ณ ๊ฐ€ ๋˜์—ˆ์œผ๋ฉด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

๋„์›€์ฃผ์…”์„œ ๋‹ค์‹œ ํ•œ ๋ฒˆ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

4๊ฐœ์˜ ์ข‹์•„์š”