uint public nonce; // (only) mutable state uint public threshold; // immutable state mapping (address => bool) isOwner; // immutable state address[] public ownersArr; // immutable state
// Note that owners_ must be strictly increasing, in order to prevent duplicates constructor(uint threshold_, address[] owners_) public { require(owners_.length <= 10 && threshold_ <= owners_.length && threshold_ >= 0);
address lastAdd = address(0); // cannot have address(0) as an owner for (uint i = 0; i < threshold; i++) { address recovered = ecrecover(txHash, sigV[i], sigR[i], sigS[i]); require(recovered > lastAdd && isOwner[recovered]); lastAdd = recovered; }
// If we make it here all signatures are accounted for. // The address.call() syntax is no longer recommended, see: // https://github.com/ethereum/solidity/issues/2884 nonce = nonce + 1; bool success = false; assembly { success := call(gas, destination, value, add(data, 0x20), mload(data), 0, 0) } require(success); }
function () payablepublic{} }
컨트랙트 소스 컴파일
solidity 소스를 컴파일 하는 방법은 다양하지만, 여기서는 node 에서 solc 모듈을 사용하여 컴파일하였습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13
var fs = require('fs'); var path = require('path'); var solc = require('solc');
// 1. 컨트랙트 소스 파일 읽기 var contractSource = fs.readFileSync(path.resolve(__dirname, 'SimpleMultiSig.sol'), 'utf8');
// 2. solc를 사용하여 컴파일 var compiledCode = solc.compile(contractSource.toString(), 1).contracts[':SimpleMultiSig'];
// 3. 컴파일 결과물에서 바이트코드와 abi 정보 조회 var bytecode = compiledCode.bytecode; var abiDefinition = JSON.parse(compiledCode.interface);
매번 소스코드를 읽어와서 컴파일하면 시간이 오래걸리므로, 컴파일된 코드를 파일로 저장하여 사용하는 것을 권장합니다.
이더리움 블록체인에 컨트랙트 배포
컨트랙트를 배포하기 위한 과정을 차례대로 알아봅니다.
1. 개인키 생성
우선 서명하기 위한 개인키가 필요합니다. 개인키 생성은 Bitcore 모듈을 이용하였습니다.
1 2 3 4 5 6 7
var Bitcore = require('bitcore-lib'); var ethUtil = require('ethereumjs-util');
// 개인키 생성 var privateKey = new Bitcore.PrivateKey().toBuffer(); // 이미 테스트 중인 개인키가 있다면 아래와 같이 사용가능 // var privateKey = new Buffer("f6fd01....fb41", "hex");
2. 트랜잭션 생성
트랜잭션은 nonce, to, value, gasPrice, gasLimit, data, v, r, s 로 구성됩니다. gasPrice 는 web3.eth.gasPrice 를 이용하여 이더리움 블록체인의 평균 gas 가격 조회가 가능합니다.
// 1. 개인키에 해당하는 이더리움 주소 var fromAddress = '0x' + ethUtil.privateToAddress(privkey).toString('hex');
// 2. 잔액 조회 var balance = web3.eth.getBalance(fromAddress); console.log('balance: ' + web3.fromWei(balance, 'ether').toString(10) + ' ETH'); // 컨트랙트를 배포하기 위해서는 gas비용이 필요하기 때문에 ether가 있어야한다.
// 3. 컨트랙트 생성자(constructor)에 필요한 데이터 var threshold = 2; // 필요한 서명 갯수 var owners = ["<address1>", "<address2>", "<address3>"]; // 서명자 주소들
// 4. 컨트랙트를 배포하기 위한 데이터 생성 var contract = web3.eth.contract(abiDefinition); //컨트랙트 인스턴스 생성 var txInputData = '0x' + contract.new.getData(threshold, owners, {data: bytecode});
// 5. 트랜잭션 Nonce값 조회 var nonce = web3.eth.getTransactionCount(fromAddress); // 발생한 트랜잭션이 없다면 0이 조회됨
// 6. 트랙잭션 데이터 생성 const rawTx = { nonce: web3.toHex(nonce), //to: '0x0', //value: '0x0', gasPrice: web3.toHex(1), // 가스 가격(wei 단위) gasLimit: web3.toHex(3000000), // 가스 최대 사용량 data: txInputData, // 컨트랙트 배포 데이터 chainId: 3, //네트워크 ID(3=Ropsten Tesetnet) } var tx = new ethTx(rawTx);
// 7. 트랜잭션 서명하기 tx.sign(privateKey);
3. 트랜잭션 전송
컨트랙트를 배포를 위한 트랜잭션을 아래와 같이 이더리움 블록체인에 전송합니다.
1 2 3 4 5 6 7 8 9
// 1. hex serialize binary 생성 var serializedTx = tx.serialize();