안녕하세요? 평소에 "klaytn forum"을 통해 많은 도움을 받고 있습니다.
포럼 게시글 중 아래 링크
에 대한 글을 읽고, 노드에 전달되는 transaction들의 nonce ordering에 대한 확인 필요성을 느껴,
기존 코드에서 sendRawTransaction 하기 전에 nonce값들을 찍어봤더니 역시나 순차적이지 않았습니다.
그래서 코드를 아래와 같이 수정하여 sendRawTransaction 시 nonce는 반드시 순차적으로 들어가도록 하였습니다. 아래는 코드 전체 내용입니다.
/**
* @file ExamMyERC20.js
* @notice MyERC20 token deploy 동작 확인
* @author jhhong
*/
//// COMMON
const colors = require('colors/safe'); // 콘솔 Color 출력
const fs = require("fs"); // keystore 파일 처리를 위함
const path = require('path'); // .env 경로 추출을 위함
//// DOTENV
require('dotenv').config({ path: path.join(__dirname, './.env') }); // 지정된 경로의 환경변수 사용 (.env 파일 참조)
//// LOGs
const initLog = require(`./libs/libWinston`).initLog; // 로그 초기화 함수 (winston)
const logToFile = require(`./libs/libWinston`).enableLogFile;
const Log = require(`./libs/libWinston`).Log;
const RED = require('./libs/libWinston.js').consoleRed; // 콘솔 컬러 출력: RED
const GREEN = require('./libs/libWinston.js').consoleGreen; // 콘솔 컬러 출력: GREEN
//// caver
const Caver = require('caver-js');
const caver = new Caver(`http://${process.env.PROVIDER_INFO}`);
//// ABI & ByteCode
const abi = require(`./build/contracts/MyERC20.json`).abi; // 컨트랙트 ABI
const byteCode = require(`./build/contracts/MyERC20.json`).bytecode; // 컨트랙트 bytecode
//// GLOBAL
let keyring = undefined;
let nonce = undefined;
let curNonce = undefined;
let txMap = {};
/**
* ms 단위로 딜레이를 준다.
* @param {*} ms 딜레이 타임 (ms)
*/
let delay = function(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
/**
* @notice deploy-event 이벤트 처리함수
* @param idx 인덱스 번호
* @author jhhong
*/
process.on('deploy-event', function(index) {
Log('DEBUG', `Receive deploy-event! index:[%s]`, index);
if(nonce == undefined) {
Log('DEBUG', `nonce does not initialized`);
return;
}
let param = nonce++;
process.emit('proc-deploy', index, param);
});
/**
* @notice deploy tx 생성함수
* @param keyring address keyring 정보
* @param nonce address 논스값
* @param index 호출 인덱스
* @author jhhong
*/
let makeTxDeploy = async function(keyring, nonce, index) {
try {
let name = 'My ERC20 Token';
let symbol = 'MET';
let totalSupply = '100000000000000000000000000';
let myErc20 = caver.contract.create(abi);
let data = await myErc20.deploy({data: byteCode, arguments:[name, symbol, totalSupply]}).encodeABI();
let gas = 2000000;
let rawTx = { from: keyring.address, gas: gas, nonce: nonce, data: data };
const tx = caver.transaction.smartContractDeploy.create(rawTx);
await caver.wallet.sign(keyring.address, tx);
Log('DEBUG', `생성!! Idx:[${index}], nonce:[${nonce}]`);
let obj = new Object();
obj.contents = tx;
obj.created = true;
obj.index = index;
txMap[nonce] = obj;
} catch(error) {
console.log(colors.red(error));
return false;
}
}
/**
* @notice sendKlay transaction 전송함수
* @param nonce 트랜젝션 논스
* @param obj 서명된 트랜젝션이 포함된 구조체
* @author jhhong
*/
let sendTxDeploy = function(nonce, obj) {
Log('DEBUG', `전송!!!!!!! Idx:[${obj.index}], nonce:[${nonce}]`);
caver.rpc.klay.sendRawTransaction(obj.contents)
.on('transactionHash', function(txHash) {
Log('DEBUG', `[transactionHash] Idx:[${obj.index}], Deploy TX:['${GREEN(txHash)}']`);
})
.on('receipt', function(receipt) {
Log('DEBUG', `[receipt] Idx:[${obj.index}], Deploy TX:['${GREEN(receipt.transactionHash)}'], BlockNumber:[${GREEN(parseInt(receipt.blockNumber))}]`);
delete obj;
});
}
/**
* @notice 테스트 함수
* @author jhhong
*/
let RunProc = async function() {
try {
await initLog();
await logToFile(`deploy`);
let passwd = `${process.env.PASSWORD}`;
let keystore = await JSON.parse(fs.readFileSync('./keystore.json', 'utf-8'));
if (keystore == null) {
throw new Error("Keystore File does not exist!");
}
keyring = await caver.wallet.keyring.decrypt(keystore, passwd);
nonce = await caver.rpc.klay.getTransactionCount(keyring.address);
curNonce = parseInt(nonce);
await caver.wallet.add(keyring);
/**
* @notice proce-deploy 이벤트 처리함수
* @param idx 인덱스 번호
* @param nonce keyring address 논스값
* @author jhhong
*/
process.on('proc-deploy', async function(idx, nonce){
try {
Log('DEBUG', `start proc-deploy :[${idx}], nonce:[${nonce}]`);
await makeTxDeploy(keyring, nonce, idx);
Log('DEBUG', `nonce:[${nonce}], curNonce:[${curNonce}]`);
if(nonce == curNonce) {
while(txMap[curNonce]) {
sendTxDeploy(curNonce, txMap[curNonce]);
curNonce++;
}
}
} catch(error) {
Log('ERROR', `${RED(error)}`);
}
});
for(let i = 0; i < 2000; i++) {
process.emit('deploy-event', i);
}
await delay(100000);
} catch(error) {
Log('ERROR', `${RED(error)}`);
}
}
RunProc();
위 코드를 실행해본 결과 ordering이 정상적으로 이루어지는 건 확인하였으나, invalide response: null은 여전히 발생합니다.
2021-11-12 15:53:36 debug: <ExMyERC20Deploy2.js:91> 전송!!!!!!! Idx:[1999], nonce:[25164]
(node:2833) UnhandledPromiseRejectionWarning: Error: Invalid response: null
at Object.InvalidResponse (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-helpers/src/errors.js:90:37)
at XMLHttpRequest.request.onreadystatechange (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-requestmanager/caver-providers-http/src/index.js:103:32)
at XMLHttpRequestEventTarget.dispatchEvent (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:22)
at XMLHttpRequest._setReadyState (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:208:14)
at XMLHttpRequest._onHttpRequestError (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:349:14)
at ClientRequest.<anonymous> (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:252:61)
at ClientRequest.emit (events.js:376:20)
at Socket.socketErrorListener (_http_client.js:475:9)
at Socket.emit (events.js:376:20)
at emitErrorNT (internal/streams/destroy.js:106:8)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:2833) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:2833) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
(node:2833) UnhandledPromiseRejectionWarning: Error: Invalid response: null
at Object.InvalidResponse (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-helpers/src/errors.js:90:37)
at XMLHttpRequest.request.onreadystatechange (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-requestmanager/caver-providers-http/src/index.js:103:32)
at XMLHttpRequestEventTarget.dispatchEvent (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request-event-target.js:34:22)
at XMLHttpRequest._setReadyState (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:208:14)
at XMLHttpRequest._onHttpRequestError (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:349:14)
at ClientRequest.<anonymous> (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/xhr2-cookies/dist/xml-http-request.js:252:61)
at ClientRequest.emit (events.js:376:20)
at Socket.socketErrorListener (_http_client.js:475:9)
at Socket.emit (events.js:376:20)
at emitErrorNT (internal/streams/destroy.js:106:8)
(node:2833) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:2833) UnhandledPromiseRejectionWarning: Error: Invalid response: null
at Object.InvalidResponse (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-helpers/src/errors.js:90:37)
at XMLHttpRequest.request.onreadystatechange (/Users/hongjonghyeon/study/myWorks/klaytn-zeppelin/node_modules/caver-js/packages/caver-core-requestmanager/caver-providers-http/src/index.js:103:32)
위 문제의 원인에 대해 알고 싶습니다.
검토해주시면 감사하겠읍니다.