multipleKeyring에 포함된 private key로 transaction에 서명을 하려는데 실패가 발생합니다. 조언 부탁드립니다

안녕하세요? caver의 account와 wallet.keyring 관련 테스트 하는 중에 난관에 봉착하여 이렇게 문의 드립니다.
테스트 하고자 하는 내용은 아래와 같습니다.

  • single keyring 2개를 생성하여 해당 key값 (key1, key2)으로 multiple keyring 하나 생성

  • multiple keyring address로 다른 주소에게 klay를 송금하는 transaction 생성

  • 생성한 transaction에 대한 서명을 key1으로 수행해봄

  • 생성한 transaction에 대한 서명을 key2로 수행해봄

  • multiple keyring address는 첫번째 single keyring의 address (key1의 address)로 하였습니다.

수행 결과 key1으로 수행 시 정상동작 합니다.
key2로 수행하면 아래와 같은 에러가 발생합니다.

Error: The from address of the transaction is different with the address of the keyring to use.
at ValueTransfer.sign (/Users/hongjonghyeon/klaytn/programs/ex-caverjs/node_modules/caver-js/packages/caver-transaction/src/transactionTypes/abstractTransaction.js:200:19)
at testFunc (/Users/hongjonghyeon/klaytn/programs/ex-caverjs/exams/ExamCaverWallet.js:155:18)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async RunProc (/Users/hongjonghyeon/klaytn/programs/ex-caverjs/exams/ExamCaverWallet.js:236:9)

kscn 콘솔에서 klay.getAccount 명령으로 생성한 multi keyring의 주소를 조회해보니 아래와 같았습니다.

klay.getAccount(“0x916de40b67f4b570904b2756f80673d4f01d9f99”)

{
accType: 1,
account: {
balance: “0x3782dace9d900000”,
humanReadable: false,
key: {
key: {},
keyType: 1
},
nonce: 1
}
}

keyType이 1 (legacy) 이라서 혹시 문제가 될까요?

아래는 소스코드 전문입니다.

let testFunc = async function() {
try {
let keyring1 = await caver.wallet.keyring.generate();
let keyring2 = await caver.wallet.keyring.generate();
let newKeyring = await caver.wallet.keyring.create(
keyring1.address,
[keyring1.key.privateKey, keyring2.key.privateKey]);
console.log(keyring1)
console.log(keyring2)
console.log(newKeyring)
let keystore = await exportKeystore(newKeyring, “password”);
str = JSON.stringify(keystore, null, 4)
console.log(str)
fs.writeFileSync(‘./test01.json’, str, ‘utf-8’);
// 생성된 계정에 충분한 양의 KLAY를 송금해 둔 상태
let keystore2 = require(‘./test01.json’)
let keyring = await caver.wallet.keyring.decrypt(keystore2, ‘password’)
console.log(keyring)
let toAddr = “0x7b3f9ff0f4215c41b3e4a49510b540e80d0c790b”; // test용 주소
let amount = “1000000000000000000”; // 1 Klay
let rawTx = {
from: keyring.address,
to: toAddr,
gas: 25000,
value: amount};
//let privKey = keyring.keys[0].privateKey;
let privKey = keyring.keys[1].privateKey;
console.log(privKey)
const tx = caver.transaction.valueTransfer.create(rawTx);
await tx.sign(privKey);
await caver.rpc.klay.sendRawTransaction(tx)

} catch(error) {
    console.log(colors.red(error));
}

}

안녕하세요.
처음 생성한 계정의 account key는 AccountKeyLegacy입니다.
멀티시그를 사용하기 위해서는 이를 AccountKeyWeightedMultiSig로 업데이트 해주어야 합니다.
업데이트 하기 위해서는 AccountUpdate 트랜잭션을 네트워크에 전송해야 합니다.

이 문서에 보면 각 AccountKey 별로 어떻게 업데이트 해야 하는지 나와 있습니다.

아래 코드 참고 부탁드립니다.

// test.js
const Caver = require('caver-js')
const caver = new Caver('https://your.en.url:8651/')

async function testFunction() {
    // 아래 sender가 업데이트할 계정입니다. KLAY를 소유하고 있어야 합니다.
    let sender = caver.wallet.keyring.createFromPrivateKey('0x{private key}')
    caver.wallet.add(sender)

    // Create an account instance with three private keys using AccountKeyWeightedMultiSig
    const newPrivateKeys = caver.wallet.keyring.generateMultipleKeys(3)
    // 아래의 키링이 새롭게 사용할 private keys를 갖고 있는 키링입니다. 이를 사용하여 업데이트에 필요한 오브젝트를 만들고, 업데이트가 모두 완료된 이후 `caver.wallet.updateKeyring`을 사용하여 sender.address 에서 사용하는 키를 업데이트 하면 됩니다.
    const newKeyring = caver.wallet.keyring.createWithMultipleKey(sender.address, newPrivateKeys)

    // threshold = 3, the weights of the three keys = [1, 2, 1]
    const options = new caver.account.weightedMultiSigOptions(3, [1, 2, 1])
    const account = newKeyring.toAccount(options)

    const updateTx = caver.transaction.accountUpdate.create({
        from: sender.address,
        account: account,
        gas: 50000,
    })
    await caver.wallet.sign(sender.address, updateTx)
    const receipt = await caver.rpc.klay.sendRawTransaction(updateTx)
    console.log(receipt)

    // Update the keyring in caver.wallet for signing afterward.
    sender = caver.wallet.updateKeyring(newKeyring)
}

testFunction()
1개의 좋아요

안녕하세요?
어제 reference 주셨던 example code를 수행한 결과 account Type 이 정상적으로 업데이트 되었음은 확인했습니다.

klay.getAccount(“0x2317816e54c5066a1106a202c6304d9a39d03e5b”)
{
accType: 1,
account: {
balance: “0x4563918244f40000”,
humanReadable: false,
key: {
key: {
keys: […],
threshold: 1
},
keyType: 4
},
nonce: 1
}
}

reference 주신 예제 코드에서 가중치 부여하는 옵션 코드를 제거하여 threshold: 1이 되도록 했습니다. 한번만 서명하면 transaction이 발행될 수 있도록이요.

이후 KLAY 전송을 위해 multiple keyring 을 구성하는 private key들로 서명을 해봤는데 모두 다 아래의 에러코드를 반환하며 실패했습니다.
Error: The from address of the transaction is different with the address of the keyring to use.
at ValueTransfer.sign (/Users/hongjonghyeon/klaytn/programs/ex-caverjs/node_modules/caver-js/packages/caver-transaction/src/transactionTypes/abstractTransaction.js:200:19)
at testFunc (/Users/hongjonghyeon/klaytn/programs/ex-caverjs/exams/ExamCaverWallet.js:264:18)
at RunProc (/Users/hongjonghyeon/klaytn/programs/ex-caverjs/exams/ExamCaverWallet.js:324:15)
at Object. (/Users/hongjonghyeon/klaytn/programs/ex-caverjs/exams/ExamCaverWallet.js:329:1)
at Module._compile (internal/modules/cjs/loader.js:1068:30)
at Object.Module._extensions…js (internal/modules/cjs/loader.js:1097:10)
at Module.load (internal/modules/cjs/loader.js:933:32)
at Function.Module._load (internal/modules/cjs/loader.js:774:14)
at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
at internal/main/run_main_module.js:17:47

아래는 KLAY 전송코드 원문입니다.

let testFunc = async function() {
try {
let fromAddr = “0x2317816e54c5066a1106a202c6304d9a39d03e5b”; // 생성한 multikeyring의 address
let toAddr = “0x7b3f9ff0f4215c41b3e4a49510b540e80d0c790b”; // test용 주소
let amount = “1000000000000000000”; // 1 Klay
let rawTx = {
from: fromAddr,
to: toAddr,
gas: 25000,
value: amount};
//let privKey = keyring.keys[0].privateKey;
let privKey = “0x7bb3ca9638974d9bea2656780d2033e79b1291b58c1bbc3e655f436bd84ca198”; // multikeyring의 private key들을 번갈아 넣어봤습니다.
console.log(privKey)
const tx = caver.transaction.valueTransfer.create(rawTx);
await tx.sign(privKey);
await caver.rpc.klay.sendRawTransaction(tx)

} catch(error) {
    console.log(colors.red(error));
}

}

감사합니다.

계정키가 업데이트 되면 private key - public key - address 의 강결합이 끊기기 때문에 private key만으로 서명할 수 없습니다.

위에 예제 드린 것처럼 서명을 진행해 주세요.

1개의 좋아요

확인했습니다. 감사드립니다.

서로 다른 private key를 가진 각자가 하나의 address를 공유하는 개념으로 생각했는데 제가 좀 잘못 생각하고 있었던 듯 합니다.

그런데 한가지 더 질문사항이 생겼는데, 가중치를 부여하여 multi signature가 이루어져야 하는 경우, private key 별로 가중치가 부여되는 개념이 아닌가요?

위 예제에서는 생성한 multiple keyring을 wallet에 add 시켜 wallet.sign 으로 서명을 하는 구조인데
이 경우, 각 private key 별 가중치를 어떻게 구분할 수 있는지 궁금합니다.

감사합니다.

caver에서 서명할 때에는 가중치 구별을 하지 않습니다.
가중치에 대한 검증은 트랜잭션을 실제 실행할 때 플랫폼에서 수행됩니다.

1개의 좋아요

답변 감사드립니다.

질문 하나만 더 올리겠습니다. (__)
RoleBased Keyring에서도 Multiple Keyring과 동일한 방식인지요?

각 역할을 수행할 private key들을 생성하여 RoleBased Keyring이 생성되면
이를 Wallet에 등록해두고
일반 transaction 서명이든, Account Update 서명이든 Fee Payer 서명이든 동일하게
caver.wallet.sign(address, rawTransaction) 으로 서명하는 개념인가요?

감사드립니다.

네 그러면 자동으로 caver.wallet.sign 내부에서 해당 역할에 맞는 role key로 서명하게 됩니다.

1개의 좋아요