From b8041e3dca273cc226018b6f0075ea8100518f8b Mon Sep 17 00:00:00 2001 From: terence tsao Date: Fri, 30 Nov 2018 07:12:23 -0800 Subject: [PATCH] Update Registration Contract in Solidity (#996) --- .soliumrc.json | 2 + .../validator-registration-contract/README.md | 19 +- .../validator_registration.go | 474 ++++++++++++++---- .../validator_registration.sol | 119 +++-- .../validator_registration_test.go | 165 +++--- 5 files changed, 579 insertions(+), 200 deletions(-) diff --git a/.soliumrc.json b/.soliumrc.json index f85639893..3e214107b 100644 --- a/.soliumrc.json +++ b/.soliumrc.json @@ -8,6 +8,8 @@ "error", "double" ], + "security/no-inline-assembly": ["warning"], + "indentation": [ "error", 4 diff --git a/contracts/validator-registration-contract/README.md b/contracts/validator-registration-contract/README.md index 18c0e24b2..d7a50e9ca 100644 --- a/contracts/validator-registration-contract/README.md +++ b/contracts/validator-registration-contract/README.md @@ -1,10 +1,23 @@ ## Validator Registration Contract -For beacon chain design, a validator will deposit 32 ETH to the main chain smart contract. +A validator will deposit 32 ETH to the registration +contract. The contract will generate a receipt showing the validator as a +qualified validator. The deposit is considered to be burned. As you burn the 32 ETH to participate, the beacon chain will see it and will credit the validator with the validator bond, -and the validator can begin to validate. At some point in the future, after a hard fork, -the original deposit + interest can be withdrawn back on one of the shards. +At some point in the future, after a hard fork, +the original deposit + interest can be withdrawn back on one of the shards. +To call the `registration` function, it takes arguments of `pubkey`, +`proof_of_possession`, `withdrawal_credentials` and `randao_commitment`. +If the user wants to deposit more than `DEPOSIT_SIZE` ETH, they would +need to make multiple `registration` calls. +When the contract publishes the `ChainStart` log, beacon nodes will +start off the beacon chain with slot 0 and last recorded `block.timestamp` +as beacon chain genesis time. +The registration contract generate receipts with the various arguments +for consumption by beacon nodes. It does not validate `proof_of_possession` +and `withdrawal_credentials`, pushing the validation logic to the +beacon chain. ## How to execute tests diff --git a/contracts/validator-registration-contract/validator_registration.go b/contracts/validator-registration-contract/validator_registration.go index 41545250c..19d2b3dfb 100644 --- a/contracts/validator-registration-contract/validator_registration.go +++ b/contracts/validator-registration-contract/validator_registration.go @@ -16,10 +16,10 @@ import ( ) // ValidatorRegistrationABI is the input ABI used to generate the binding from. -const ValidatorRegistrationABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"VALIDATOR_DEPOSIT\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"bytes32\"}],\"name\":\"usedHashedPubkey\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_pubkey\",\"type\":\"bytes\"},{\"name\":\"_withdrawalShardID\",\"type\":\"uint256\"},{\"name\":\"_withdrawalAddressbytes32\",\"type\":\"address\"},{\"name\":\"_randaoCommitment\",\"type\":\"bytes32\"}],\"name\":\"deposit\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"hashedPubkey\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"withdrawalShardID\",\"type\":\"uint256\"},{\"indexed\":true,\"name\":\"withdrawalAddressbytes32\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"randaoCommitment\",\"type\":\"bytes32\"}],\"name\":\"ValidatorRegistered\",\"type\":\"event\"}]" +const ValidatorRegistrationABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"MIN_TOPUP_SIZE\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"GWEI_PER_ETH\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"MERKLE_TREE_DEPTH\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"DEPOSIT_SIZE\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"getReceiptRoot\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"name\":\"receiptTree\",\"outputs\":[{\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"SECONDS_PER_DAY\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"depositParams\",\"type\":\"bytes\"}],\"name\":\"deposit\",\"outputs\":[],\"payable\":true,\"stateMutability\":\"payable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"DEPOSITS_FOR_CHAIN_START\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalDepositCount\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"previousReceiptRoot\",\"type\":\"bytes\"},{\"indexed\":false,\"name\":\"data\",\"type\":\"bytes\"},{\"indexed\":false,\"name\":\"totalDepositcount\",\"type\":\"uint256\"}],\"name\":\"HashChainValue\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"receiptRoot\",\"type\":\"bytes\"},{\"indexed\":false,\"name\":\"time\",\"type\":\"bytes\"}],\"name\":\"ChainStart\",\"type\":\"event\"}]" // ValidatorRegistrationBin is the compiled bytecode used for deploying new contracts. -const ValidatorRegistrationBin = `0x608060405234801561001057600080fd5b50610406806100206000396000f3006080604052600436106100565763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663441d92cc811461005b5780638618d77814610082578063878df5ac146100ae575b600080fd5b34801561006757600080fd5b5061007061011e565b60408051918252519081900360200190f35b34801561008e57600080fd5b5061009a60043561012b565b604080519115158252519081900360200190f35b6040805160206004803580820135601f810184900484028501840190955284845261011c9436949293602493928401919081908401838280828437509497505084359550505050602082013573ffffffffffffffffffffffffffffffffffffffff1691604001359050610140565b005b6801bc16d674ec80000081565b60006020819052908152604090205460ff1681565b6000346801bc16d674ec800000146101b957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f496e636f72726563742076616c696461746f72206465706f7369740000000000604482015290519081900360640190fd5b845160301461022957604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f5075626c6963206b6579206973206e6f74203438206279746573000000000000604482015290519081900360640190fd5b846040516020018082805190602001908083835b6020831061025c5780518252601f19909201916020918201910161023d565b6001836020036101000a0380198251168184511680821785525050505050509050019150506040516020818303038152906040526040518082805190602001908083835b602083106102bf5780518252601f1990920191602091820191016102a0565b51815160209384036101000a60001901801990921691161790526040805192909401829003909120600081815291829052929020549194505060ff1615915061036b905057604080517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f5075626c6963206b657920616c72656164792075736564000000000000000000604482015290519081900360640190fd5b60008181526020818152604091829020805460ff1916600117905581518681529151849273ffffffffffffffffffffffffffffffffffffffff87169285927f7b0678aab009b61a805f5004869728b53a444f9a3e6bb9e22b8537c89af512749281900390910190a450505050505600a165627a7a72305820f94ed1bea88aec62badbde73ec4adcd500e067fa0245052ea42e662c529b94870029` +const ValidatorRegistrationBin = `0x608060405234801561001057600080fd5b50610c47806100206000396000f3006080604052600436106100a35763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166320d24cb781146100a857806324eda209146100d35780633568cda0146100e857806336bf3325146100fd5780634213155f14610112578063701f82121461013457806374f0314f1461015457806398b1e06a14610169578063cac0057c1461017e578063d634386714610193575b600080fd5b3480156100b457600080fd5b506100bd6101a8565b6040516100ca9190610b66565b60405180910390f35b3480156100df57600080fd5b506100bd6101b4565b3480156100f457600080fd5b506100bd6101bc565b34801561010957600080fd5b506100bd6101c1565b34801561011e57600080fd5b506101276101ce565b6040516100ca9190610b0f565b34801561014057600080fd5b5061012761014f366004610a01565b61028a565b34801561016057600080fd5b506100bd610324565b61017c6101773660046109c4565b61032b565b005b34801561018a57600080fd5b506100bd610812565b34801561019f57600080fd5b506100bd610817565b670de0b6b3a764000081565b633b9aca0081565b601081565b6801bc16d674ec80000081565b6001600081815260209081527fada5013122d395ba3c54772283fb069b10426056ef8ca54750cb9bb552a59e7d805460408051600295831615610100026000190190921694909404601f8101849004840282018401909452838152606093909283018282801561027f5780601f106102545761010080835404028352916020019161027f565b820191906000526020600020905b81548152906001019060200180831161026257829003601f168201915b505050505090505b90565b600060208181529181526040908190208054825160026001831615610100026000190190921691909104601f81018590048502820185019093528281529290919083018282801561031c5780601f106102f15761010080835404028352916020019161031c565b820191906000526020600020905b8154815290600101906020018083116102ff57829003601f168201915b505050505081565b6201518081565b600154620100000160608080600080826103443461081d565b955061034f4261081d565b945061036461035e878761084e565b8961084e565b93506000806001815260200190815260200160002060405180828054600181600116156101000203166002900480156103d45780601f106103b25761010080835404028352918201916103d4565b820191906000526020600020905b8154815290600101906020018083116103c0575b505091505060405180910390207fd60002d8f2bba463e7d533a8fa9b84d9d7451dc0bb7e9da868104601a5d2b9ef89600154604051610414929190610b20565b60405180910390a2836040518082805190602001908083835b6020831061044c5780518252601f19909201916020918201910161042d565b51815160209384036101000a60001901801990921691161790526040805192909401829003822082820152835180830382018152918401845260008d81528082529390932081516104a5965090945092019190506108c5565b50600092505b601083101561068b5760028704600281810260009081526020818152604091829020805483516001821615610100026000190190911694909404601f8101839004830285018301909352828452939a506105f79391908301828280156105525780601f1061052757610100808354040283529160200191610552565b820191906000526020600020905b81548152906001019060200180831161053557829003601f168201915b50505050506002898102600190810160009081526020818152604091829020805483519481161561010002600019011694909404601f81018290048202840182019092528183529192918301828280156105ed5780601f106105c2576101008083540402835291602001916105ed565b820191906000526020600020905b8154815290600101906020018083116105d057829003601f168201915b505050505061084e565b6040518082805190602001908083835b602083106106265780518252601f199092019160209182019101610607565b51815160209384036101000a60001901801990921691161790526040805192909401829003822082820152835180830382018152918401845260008d815280825293909320815161067f965090945092019190506108c5565b506001909201916104ab565b6801bc16d674ec8000003411156106d7576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106ce90610b56565b60405180910390fd5b670de0b6b3a7640000341015610719576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106ce90610b40565b6801bc16d674ec8000003414156107335760018054810190555b60086001541415610808576201518080420642030191506107538261081d565b90506000806001815260200190815260200160002060405180828054600181600116156101000203166002900480156107c35780601f106107a15761010080835404028352918201916107c3565b820191906000526020600020905b8154815290600101906020018083116107af575b505091505060405180910390207fb24eb1954aa12b3433be2b11740f2f73c8673453064575f93f223a85f1215f3a826040516107ff9190610b0f565b60405180910390a25b5050505050505050565b600881565b60015481565b6040805160208082528183019092526060918291908082016104008038833950505060208101939093525090919050565b815181516040518183018082526060939290916020601f8086018290049301049060005b8381101561088e57600101602081028981015190830152610872565b5060005b828110156108b0576001016020810288810151908701830152610892565b50928301602001604052509095945050505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061090657805160ff1916838001178555610933565b82800160010185558215610933579182015b82811115610933578251825591602001919060010190610918565b5061093f929150610943565b5090565b61028791905b8082111561093f5760008155600101610949565b6000601f8201831361096e57600080fd5b813561098161097c82610b9b565b610b74565b9150808252602083016020830185838301111561099d57600080fd5b6109a8838284610bc7565b50505092915050565b60006109bd8235610287565b9392505050565b6000602082840312156109d657600080fd5b813567ffffffffffffffff8111156109ed57600080fd5b6109f98482850161095d565b949350505050565b600060208284031215610a1357600080fd5b60006109f984846109b1565b6000610a2a82610bc3565b808452610a3e816020860160208601610bd3565b610a4781610c03565b9093016020019392505050565b602c81527f4465706f7369742063616e2774206265206c6573736572207468616e204d494e60208201527f5f544f5055505f53495a452e0000000000000000000000000000000000000000604082015260600190565b602b81527f4465706f7369742063616e27742062652067726561746572207468616e20444560208201527f504f5349545f53495a452e000000000000000000000000000000000000000000604082015260600190565b610b0981610287565b82525050565b602080825281016109bd8184610a1f565b60408082528101610b318185610a1f565b90506109bd6020830184610b00565b60208082528101610b5081610a54565b92915050565b60208082528101610b5081610aaa565b60208101610b508284610b00565b60405181810167ffffffffffffffff81118282101715610b9357600080fd5b604052919050565b600067ffffffffffffffff821115610bb257600080fd5b506020601f91909101601f19160190565b5190565b82818337506000910152565b60005b83811015610bee578181015183820152602001610bd6565b83811115610bfd576000848401525b50505050565b601f01601f1916905600a265627a7a723058204d491b9e875a7e6e11d109c2cedde9996656182f359ec144cc1a353afe420c4b6c6578706572696d656e74616cf50037` // DeployValidatorRegistration deploys a new Ethereum contract, binding an instance of ValidatorRegistration to it. func DeployValidatorRegistration(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *ValidatorRegistration, error) { @@ -176,82 +176,264 @@ func (_ValidatorRegistration *ValidatorRegistrationTransactorRaw) Transact(opts return _ValidatorRegistration.Contract.contract.Transact(opts, method, params...) } -// VALIDATORDEPOSIT is a free data retrieval call binding the contract method 0x441d92cc. +// DEPOSITSFORCHAINSTART is a free data retrieval call binding the contract method 0xcac0057c. // -// Solidity: function VALIDATOR_DEPOSIT() constant returns(uint256) -func (_ValidatorRegistration *ValidatorRegistrationCaller) VALIDATORDEPOSIT(opts *bind.CallOpts) (*big.Int, error) { +// Solidity: function DEPOSITS_FOR_CHAIN_START() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCaller) DEPOSITSFORCHAINSTART(opts *bind.CallOpts) (*big.Int, error) { var ( ret0 = new(*big.Int) ) out := ret0 - err := _ValidatorRegistration.contract.Call(opts, out, "VALIDATOR_DEPOSIT") + err := _ValidatorRegistration.contract.Call(opts, out, "DEPOSITS_FOR_CHAIN_START") return *ret0, err } -// VALIDATORDEPOSIT is a free data retrieval call binding the contract method 0x441d92cc. +// DEPOSITSFORCHAINSTART is a free data retrieval call binding the contract method 0xcac0057c. // -// Solidity: function VALIDATOR_DEPOSIT() constant returns(uint256) -func (_ValidatorRegistration *ValidatorRegistrationSession) VALIDATORDEPOSIT() (*big.Int, error) { - return _ValidatorRegistration.Contract.VALIDATORDEPOSIT(&_ValidatorRegistration.CallOpts) +// Solidity: function DEPOSITS_FOR_CHAIN_START() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationSession) DEPOSITSFORCHAINSTART() (*big.Int, error) { + return _ValidatorRegistration.Contract.DEPOSITSFORCHAINSTART(&_ValidatorRegistration.CallOpts) } -// VALIDATORDEPOSIT is a free data retrieval call binding the contract method 0x441d92cc. +// DEPOSITSFORCHAINSTART is a free data retrieval call binding the contract method 0xcac0057c. // -// Solidity: function VALIDATOR_DEPOSIT() constant returns(uint256) -func (_ValidatorRegistration *ValidatorRegistrationCallerSession) VALIDATORDEPOSIT() (*big.Int, error) { - return _ValidatorRegistration.Contract.VALIDATORDEPOSIT(&_ValidatorRegistration.CallOpts) +// Solidity: function DEPOSITS_FOR_CHAIN_START() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCallerSession) DEPOSITSFORCHAINSTART() (*big.Int, error) { + return _ValidatorRegistration.Contract.DEPOSITSFORCHAINSTART(&_ValidatorRegistration.CallOpts) } -// UsedHashedPubkey is a free data retrieval call binding the contract method 0x8618d778. +// DEPOSITSIZE is a free data retrieval call binding the contract method 0x36bf3325. // -// Solidity: function usedHashedPubkey( bytes32) constant returns(bool) -func (_ValidatorRegistration *ValidatorRegistrationCaller) UsedHashedPubkey(opts *bind.CallOpts, arg0 [32]byte) (bool, error) { +// Solidity: function DEPOSIT_SIZE() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCaller) DEPOSITSIZE(opts *bind.CallOpts) (*big.Int, error) { var ( - ret0 = new(bool) + ret0 = new(*big.Int) ) out := ret0 - err := _ValidatorRegistration.contract.Call(opts, out, "usedHashedPubkey", arg0) + err := _ValidatorRegistration.contract.Call(opts, out, "DEPOSIT_SIZE") return *ret0, err } -// UsedHashedPubkey is a free data retrieval call binding the contract method 0x8618d778. +// DEPOSITSIZE is a free data retrieval call binding the contract method 0x36bf3325. // -// Solidity: function usedHashedPubkey( bytes32) constant returns(bool) -func (_ValidatorRegistration *ValidatorRegistrationSession) UsedHashedPubkey(arg0 [32]byte) (bool, error) { - return _ValidatorRegistration.Contract.UsedHashedPubkey(&_ValidatorRegistration.CallOpts, arg0) +// Solidity: function DEPOSIT_SIZE() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationSession) DEPOSITSIZE() (*big.Int, error) { + return _ValidatorRegistration.Contract.DEPOSITSIZE(&_ValidatorRegistration.CallOpts) } -// UsedHashedPubkey is a free data retrieval call binding the contract method 0x8618d778. +// DEPOSITSIZE is a free data retrieval call binding the contract method 0x36bf3325. // -// Solidity: function usedHashedPubkey( bytes32) constant returns(bool) -func (_ValidatorRegistration *ValidatorRegistrationCallerSession) UsedHashedPubkey(arg0 [32]byte) (bool, error) { - return _ValidatorRegistration.Contract.UsedHashedPubkey(&_ValidatorRegistration.CallOpts, arg0) +// Solidity: function DEPOSIT_SIZE() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCallerSession) DEPOSITSIZE() (*big.Int, error) { + return _ValidatorRegistration.Contract.DEPOSITSIZE(&_ValidatorRegistration.CallOpts) } -// Deposit is a paid mutator transaction binding the contract method 0x878df5ac. +// GWEIPERETH is a free data retrieval call binding the contract method 0x24eda209. // -// Solidity: function deposit(_pubkey bytes, _withdrawalShardID uint256, _withdrawalAddressbytes32 address, _randaoCommitment bytes32) returns() -func (_ValidatorRegistration *ValidatorRegistrationTransactor) Deposit(opts *bind.TransactOpts, _pubkey []byte, _withdrawalShardID *big.Int, _withdrawalAddressbytes32 common.Address, _randaoCommitment [32]byte) (*types.Transaction, error) { - return _ValidatorRegistration.contract.Transact(opts, "deposit", _pubkey, _withdrawalShardID, _withdrawalAddressbytes32, _randaoCommitment) +// Solidity: function GWEI_PER_ETH() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCaller) GWEIPERETH(opts *bind.CallOpts) (*big.Int, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + err := _ValidatorRegistration.contract.Call(opts, out, "GWEI_PER_ETH") + return *ret0, err } -// Deposit is a paid mutator transaction binding the contract method 0x878df5ac. +// GWEIPERETH is a free data retrieval call binding the contract method 0x24eda209. // -// Solidity: function deposit(_pubkey bytes, _withdrawalShardID uint256, _withdrawalAddressbytes32 address, _randaoCommitment bytes32) returns() -func (_ValidatorRegistration *ValidatorRegistrationSession) Deposit(_pubkey []byte, _withdrawalShardID *big.Int, _withdrawalAddressbytes32 common.Address, _randaoCommitment [32]byte) (*types.Transaction, error) { - return _ValidatorRegistration.Contract.Deposit(&_ValidatorRegistration.TransactOpts, _pubkey, _withdrawalShardID, _withdrawalAddressbytes32, _randaoCommitment) +// Solidity: function GWEI_PER_ETH() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationSession) GWEIPERETH() (*big.Int, error) { + return _ValidatorRegistration.Contract.GWEIPERETH(&_ValidatorRegistration.CallOpts) } -// Deposit is a paid mutator transaction binding the contract method 0x878df5ac. +// GWEIPERETH is a free data retrieval call binding the contract method 0x24eda209. // -// Solidity: function deposit(_pubkey bytes, _withdrawalShardID uint256, _withdrawalAddressbytes32 address, _randaoCommitment bytes32) returns() -func (_ValidatorRegistration *ValidatorRegistrationTransactorSession) Deposit(_pubkey []byte, _withdrawalShardID *big.Int, _withdrawalAddressbytes32 common.Address, _randaoCommitment [32]byte) (*types.Transaction, error) { - return _ValidatorRegistration.Contract.Deposit(&_ValidatorRegistration.TransactOpts, _pubkey, _withdrawalShardID, _withdrawalAddressbytes32, _randaoCommitment) +// Solidity: function GWEI_PER_ETH() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCallerSession) GWEIPERETH() (*big.Int, error) { + return _ValidatorRegistration.Contract.GWEIPERETH(&_ValidatorRegistration.CallOpts) } -// ValidatorRegistrationValidatorRegisteredIterator is returned from FilterValidatorRegistered and is used to iterate over the raw logs and unpacked data for ValidatorRegistered events raised by the ValidatorRegistration contract. -type ValidatorRegistrationValidatorRegisteredIterator struct { - Event *ValidatorRegistrationValidatorRegistered // Event containing the contract specifics and raw log +// MERKLETREEDEPTH is a free data retrieval call binding the contract method 0x3568cda0. +// +// Solidity: function MERKLE_TREE_DEPTH() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCaller) MERKLETREEDEPTH(opts *bind.CallOpts) (*big.Int, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + err := _ValidatorRegistration.contract.Call(opts, out, "MERKLE_TREE_DEPTH") + return *ret0, err +} + +// MERKLETREEDEPTH is a free data retrieval call binding the contract method 0x3568cda0. +// +// Solidity: function MERKLE_TREE_DEPTH() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationSession) MERKLETREEDEPTH() (*big.Int, error) { + return _ValidatorRegistration.Contract.MERKLETREEDEPTH(&_ValidatorRegistration.CallOpts) +} + +// MERKLETREEDEPTH is a free data retrieval call binding the contract method 0x3568cda0. +// +// Solidity: function MERKLE_TREE_DEPTH() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCallerSession) MERKLETREEDEPTH() (*big.Int, error) { + return _ValidatorRegistration.Contract.MERKLETREEDEPTH(&_ValidatorRegistration.CallOpts) +} + +// MINTOPUPSIZE is a free data retrieval call binding the contract method 0x20d24cb7. +// +// Solidity: function MIN_TOPUP_SIZE() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCaller) MINTOPUPSIZE(opts *bind.CallOpts) (*big.Int, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + err := _ValidatorRegistration.contract.Call(opts, out, "MIN_TOPUP_SIZE") + return *ret0, err +} + +// MINTOPUPSIZE is a free data retrieval call binding the contract method 0x20d24cb7. +// +// Solidity: function MIN_TOPUP_SIZE() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationSession) MINTOPUPSIZE() (*big.Int, error) { + return _ValidatorRegistration.Contract.MINTOPUPSIZE(&_ValidatorRegistration.CallOpts) +} + +// MINTOPUPSIZE is a free data retrieval call binding the contract method 0x20d24cb7. +// +// Solidity: function MIN_TOPUP_SIZE() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCallerSession) MINTOPUPSIZE() (*big.Int, error) { + return _ValidatorRegistration.Contract.MINTOPUPSIZE(&_ValidatorRegistration.CallOpts) +} + +// SECONDSPERDAY is a free data retrieval call binding the contract method 0x74f0314f. +// +// Solidity: function SECONDS_PER_DAY() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCaller) SECONDSPERDAY(opts *bind.CallOpts) (*big.Int, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + err := _ValidatorRegistration.contract.Call(opts, out, "SECONDS_PER_DAY") + return *ret0, err +} + +// SECONDSPERDAY is a free data retrieval call binding the contract method 0x74f0314f. +// +// Solidity: function SECONDS_PER_DAY() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationSession) SECONDSPERDAY() (*big.Int, error) { + return _ValidatorRegistration.Contract.SECONDSPERDAY(&_ValidatorRegistration.CallOpts) +} + +// SECONDSPERDAY is a free data retrieval call binding the contract method 0x74f0314f. +// +// Solidity: function SECONDS_PER_DAY() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCallerSession) SECONDSPERDAY() (*big.Int, error) { + return _ValidatorRegistration.Contract.SECONDSPERDAY(&_ValidatorRegistration.CallOpts) +} + +// GetReceiptRoot is a free data retrieval call binding the contract method 0x4213155f. +// +// Solidity: function getReceiptRoot() constant returns(bytes) +func (_ValidatorRegistration *ValidatorRegistrationCaller) GetReceiptRoot(opts *bind.CallOpts) ([]byte, error) { + var ( + ret0 = new([]byte) + ) + out := ret0 + err := _ValidatorRegistration.contract.Call(opts, out, "getReceiptRoot") + return *ret0, err +} + +// GetReceiptRoot is a free data retrieval call binding the contract method 0x4213155f. +// +// Solidity: function getReceiptRoot() constant returns(bytes) +func (_ValidatorRegistration *ValidatorRegistrationSession) GetReceiptRoot() ([]byte, error) { + return _ValidatorRegistration.Contract.GetReceiptRoot(&_ValidatorRegistration.CallOpts) +} + +// GetReceiptRoot is a free data retrieval call binding the contract method 0x4213155f. +// +// Solidity: function getReceiptRoot() constant returns(bytes) +func (_ValidatorRegistration *ValidatorRegistrationCallerSession) GetReceiptRoot() ([]byte, error) { + return _ValidatorRegistration.Contract.GetReceiptRoot(&_ValidatorRegistration.CallOpts) +} + +// ReceiptTree is a free data retrieval call binding the contract method 0x701f8212. +// +// Solidity: function receiptTree( uint256) constant returns(bytes) +func (_ValidatorRegistration *ValidatorRegistrationCaller) ReceiptTree(opts *bind.CallOpts, arg0 *big.Int) ([]byte, error) { + var ( + ret0 = new([]byte) + ) + out := ret0 + err := _ValidatorRegistration.contract.Call(opts, out, "receiptTree", arg0) + return *ret0, err +} + +// ReceiptTree is a free data retrieval call binding the contract method 0x701f8212. +// +// Solidity: function receiptTree( uint256) constant returns(bytes) +func (_ValidatorRegistration *ValidatorRegistrationSession) ReceiptTree(arg0 *big.Int) ([]byte, error) { + return _ValidatorRegistration.Contract.ReceiptTree(&_ValidatorRegistration.CallOpts, arg0) +} + +// ReceiptTree is a free data retrieval call binding the contract method 0x701f8212. +// +// Solidity: function receiptTree( uint256) constant returns(bytes) +func (_ValidatorRegistration *ValidatorRegistrationCallerSession) ReceiptTree(arg0 *big.Int) ([]byte, error) { + return _ValidatorRegistration.Contract.ReceiptTree(&_ValidatorRegistration.CallOpts, arg0) +} + +// TotalDepositCount is a free data retrieval call binding the contract method 0xd6343867. +// +// Solidity: function totalDepositCount() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCaller) TotalDepositCount(opts *bind.CallOpts) (*big.Int, error) { + var ( + ret0 = new(*big.Int) + ) + out := ret0 + err := _ValidatorRegistration.contract.Call(opts, out, "totalDepositCount") + return *ret0, err +} + +// TotalDepositCount is a free data retrieval call binding the contract method 0xd6343867. +// +// Solidity: function totalDepositCount() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationSession) TotalDepositCount() (*big.Int, error) { + return _ValidatorRegistration.Contract.TotalDepositCount(&_ValidatorRegistration.CallOpts) +} + +// TotalDepositCount is a free data retrieval call binding the contract method 0xd6343867. +// +// Solidity: function totalDepositCount() constant returns(uint256) +func (_ValidatorRegistration *ValidatorRegistrationCallerSession) TotalDepositCount() (*big.Int, error) { + return _ValidatorRegistration.Contract.TotalDepositCount(&_ValidatorRegistration.CallOpts) +} + +// Deposit is a paid mutator transaction binding the contract method 0x98b1e06a. +// +// Solidity: function deposit(depositParams bytes) returns() +func (_ValidatorRegistration *ValidatorRegistrationTransactor) Deposit(opts *bind.TransactOpts, depositParams []byte) (*types.Transaction, error) { + return _ValidatorRegistration.contract.Transact(opts, "deposit", depositParams) +} + +// Deposit is a paid mutator transaction binding the contract method 0x98b1e06a. +// +// Solidity: function deposit(depositParams bytes) returns() +func (_ValidatorRegistration *ValidatorRegistrationSession) Deposit(depositParams []byte) (*types.Transaction, error) { + return _ValidatorRegistration.Contract.Deposit(&_ValidatorRegistration.TransactOpts, depositParams) +} + +// Deposit is a paid mutator transaction binding the contract method 0x98b1e06a. +// +// Solidity: function deposit(depositParams bytes) returns() +func (_ValidatorRegistration *ValidatorRegistrationTransactorSession) Deposit(depositParams []byte) (*types.Transaction, error) { + return _ValidatorRegistration.Contract.Deposit(&_ValidatorRegistration.TransactOpts, depositParams) +} + +// ValidatorRegistrationChainStartIterator is returned from FilterChainStart and is used to iterate over the raw logs and unpacked data for ChainStart events raised by the ValidatorRegistration contract. +type ValidatorRegistrationChainStartIterator struct { + Event *ValidatorRegistrationChainStart // Event containing the contract specifics and raw log contract *bind.BoundContract // Generic contract to use for unpacking event data event string // Event name to use for unpacking event data @@ -265,7 +447,7 @@ type ValidatorRegistrationValidatorRegisteredIterator struct { // Next advances the iterator to the subsequent event, returning whether there // are any more events found. In case of a retrieval or parsing error, false is // returned and Error() can be queried for the exact failure. -func (it *ValidatorRegistrationValidatorRegisteredIterator) Next() bool { +func (it *ValidatorRegistrationChainStartIterator) Next() bool { // If the iterator failed, stop iterating if it.fail != nil { return false @@ -274,7 +456,7 @@ func (it *ValidatorRegistrationValidatorRegisteredIterator) Next() bool { if it.done { select { case log := <-it.logs: - it.Event = new(ValidatorRegistrationValidatorRegistered) + it.Event = new(ValidatorRegistrationChainStart) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -289,7 +471,7 @@ func (it *ValidatorRegistrationValidatorRegisteredIterator) Next() bool { // Iterator still in progress, wait for either a data or an error event select { case log := <-it.logs: - it.Event = new(ValidatorRegistrationValidatorRegistered) + it.Event = new(ValidatorRegistrationChainStart) if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { it.fail = err return false @@ -305,72 +487,52 @@ func (it *ValidatorRegistrationValidatorRegisteredIterator) Next() bool { } // Error returns any retrieval or parsing error occurred during filtering. -func (it *ValidatorRegistrationValidatorRegisteredIterator) Error() error { +func (it *ValidatorRegistrationChainStartIterator) Error() error { return it.fail } // Close terminates the iteration process, releasing any pending underlying // resources. -func (it *ValidatorRegistrationValidatorRegisteredIterator) Close() error { +func (it *ValidatorRegistrationChainStartIterator) Close() error { it.sub.Unsubscribe() return nil } -// ValidatorRegistrationValidatorRegistered represents a ValidatorRegistered event raised by the ValidatorRegistration contract. -type ValidatorRegistrationValidatorRegistered struct { - HashedPubkey [32]byte - WithdrawalShardID *big.Int - WithdrawalAddressbytes32 common.Address - RandaoCommitment [32]byte - Raw types.Log // Blockchain specific contextual infos +// ValidatorRegistrationChainStart represents a ChainStart event raised by the ValidatorRegistration contract. +type ValidatorRegistrationChainStart struct { + ReceiptRoot common.Hash + Time []byte + Raw types.Log // Blockchain specific contextual infos } -// FilterValidatorRegistered is a free log retrieval operation binding the contract event 0x7b0678aab009b61a805f5004869728b53a444f9a3e6bb9e22b8537c89af51274. +// FilterChainStart is a free log retrieval operation binding the contract event 0xb24eb1954aa12b3433be2b11740f2f73c8673453064575f93f223a85f1215f3a. // -// Solidity: event ValidatorRegistered(hashedPubkey indexed bytes32, withdrawalShardID uint256, withdrawalAddressbytes32 indexed address, randaoCommitment indexed bytes32) -func (_ValidatorRegistration *ValidatorRegistrationFilterer) FilterValidatorRegistered(opts *bind.FilterOpts, hashedPubkey [][32]byte, withdrawalAddressbytes32 []common.Address, randaoCommitment [][32]byte) (*ValidatorRegistrationValidatorRegisteredIterator, error) { +// Solidity: event ChainStart(receiptRoot indexed bytes, time bytes) +func (_ValidatorRegistration *ValidatorRegistrationFilterer) FilterChainStart(opts *bind.FilterOpts, receiptRoot [][]byte) (*ValidatorRegistrationChainStartIterator, error) { - var hashedPubkeyRule []interface{} - for _, hashedPubkeyItem := range hashedPubkey { - hashedPubkeyRule = append(hashedPubkeyRule, hashedPubkeyItem) + var receiptRootRule []interface{} + for _, receiptRootItem := range receiptRoot { + receiptRootRule = append(receiptRootRule, receiptRootItem) } - var withdrawalAddressbytes32Rule []interface{} - for _, withdrawalAddressbytes32Item := range withdrawalAddressbytes32 { - withdrawalAddressbytes32Rule = append(withdrawalAddressbytes32Rule, withdrawalAddressbytes32Item) - } - var randaoCommitmentRule []interface{} - for _, randaoCommitmentItem := range randaoCommitment { - randaoCommitmentRule = append(randaoCommitmentRule, randaoCommitmentItem) - } - - logs, sub, err := _ValidatorRegistration.contract.FilterLogs(opts, "ValidatorRegistered", hashedPubkeyRule, withdrawalAddressbytes32Rule, randaoCommitmentRule) + logs, sub, err := _ValidatorRegistration.contract.FilterLogs(opts, "ChainStart", receiptRootRule) if err != nil { return nil, err } - return &ValidatorRegistrationValidatorRegisteredIterator{contract: _ValidatorRegistration.contract, event: "ValidatorRegistered", logs: logs, sub: sub}, nil + return &ValidatorRegistrationChainStartIterator{contract: _ValidatorRegistration.contract, event: "ChainStart", logs: logs, sub: sub}, nil } -// WatchValidatorRegistered is a free log subscription operation binding the contract event 0x7b0678aab009b61a805f5004869728b53a444f9a3e6bb9e22b8537c89af51274. +// WatchChainStart is a free log subscription operation binding the contract event 0xb24eb1954aa12b3433be2b11740f2f73c8673453064575f93f223a85f1215f3a. // -// Solidity: event ValidatorRegistered(hashedPubkey indexed bytes32, withdrawalShardID uint256, withdrawalAddressbytes32 indexed address, randaoCommitment indexed bytes32) -func (_ValidatorRegistration *ValidatorRegistrationFilterer) WatchValidatorRegistered(opts *bind.WatchOpts, sink chan<- *ValidatorRegistrationValidatorRegistered, hashedPubkey [][32]byte, withdrawalAddressbytes32 []common.Address, randaoCommitment [][32]byte) (event.Subscription, error) { +// Solidity: event ChainStart(receiptRoot indexed bytes, time bytes) +func (_ValidatorRegistration *ValidatorRegistrationFilterer) WatchChainStart(opts *bind.WatchOpts, sink chan<- *ValidatorRegistrationChainStart, receiptRoot [][]byte) (event.Subscription, error) { - var hashedPubkeyRule []interface{} - for _, hashedPubkeyItem := range hashedPubkey { - hashedPubkeyRule = append(hashedPubkeyRule, hashedPubkeyItem) + var receiptRootRule []interface{} + for _, receiptRootItem := range receiptRoot { + receiptRootRule = append(receiptRootRule, receiptRootItem) } - var withdrawalAddressbytes32Rule []interface{} - for _, withdrawalAddressbytes32Item := range withdrawalAddressbytes32 { - withdrawalAddressbytes32Rule = append(withdrawalAddressbytes32Rule, withdrawalAddressbytes32Item) - } - var randaoCommitmentRule []interface{} - for _, randaoCommitmentItem := range randaoCommitment { - randaoCommitmentRule = append(randaoCommitmentRule, randaoCommitmentItem) - } - - logs, sub, err := _ValidatorRegistration.contract.WatchLogs(opts, "ValidatorRegistered", hashedPubkeyRule, withdrawalAddressbytes32Rule, randaoCommitmentRule) + logs, sub, err := _ValidatorRegistration.contract.WatchLogs(opts, "ChainStart", receiptRootRule) if err != nil { return nil, err } @@ -380,8 +542,142 @@ func (_ValidatorRegistration *ValidatorRegistrationFilterer) WatchValidatorRegis select { case log := <-logs: // New log arrived, parse the event and forward to the user - event := new(ValidatorRegistrationValidatorRegistered) - if err := _ValidatorRegistration.contract.UnpackLog(event, "ValidatorRegistered", log); err != nil { + event := new(ValidatorRegistrationChainStart) + if err := _ValidatorRegistration.contract.UnpackLog(event, "ChainStart", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ValidatorRegistrationHashChainValueIterator is returned from FilterHashChainValue and is used to iterate over the raw logs and unpacked data for HashChainValue events raised by the ValidatorRegistration contract. +type ValidatorRegistrationHashChainValueIterator struct { + Event *ValidatorRegistrationHashChainValue // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *ValidatorRegistrationHashChainValueIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(ValidatorRegistrationHashChainValue) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(ValidatorRegistrationHashChainValue) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *ValidatorRegistrationHashChainValueIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *ValidatorRegistrationHashChainValueIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// ValidatorRegistrationHashChainValue represents a HashChainValue event raised by the ValidatorRegistration contract. +type ValidatorRegistrationHashChainValue struct { + PreviousReceiptRoot common.Hash + Data []byte + TotalDepositcount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterHashChainValue is a free log retrieval operation binding the contract event 0xd60002d8f2bba463e7d533a8fa9b84d9d7451dc0bb7e9da868104601a5d2b9ef. +// +// Solidity: event HashChainValue(previousReceiptRoot indexed bytes, data bytes, totalDepositcount uint256) +func (_ValidatorRegistration *ValidatorRegistrationFilterer) FilterHashChainValue(opts *bind.FilterOpts, previousReceiptRoot [][]byte) (*ValidatorRegistrationHashChainValueIterator, error) { + + var previousReceiptRootRule []interface{} + for _, previousReceiptRootItem := range previousReceiptRoot { + previousReceiptRootRule = append(previousReceiptRootRule, previousReceiptRootItem) + } + + logs, sub, err := _ValidatorRegistration.contract.FilterLogs(opts, "HashChainValue", previousReceiptRootRule) + if err != nil { + return nil, err + } + return &ValidatorRegistrationHashChainValueIterator{contract: _ValidatorRegistration.contract, event: "HashChainValue", logs: logs, sub: sub}, nil +} + +// WatchHashChainValue is a free log subscription operation binding the contract event 0xd60002d8f2bba463e7d533a8fa9b84d9d7451dc0bb7e9da868104601a5d2b9ef. +// +// Solidity: event HashChainValue(previousReceiptRoot indexed bytes, data bytes, totalDepositcount uint256) +func (_ValidatorRegistration *ValidatorRegistrationFilterer) WatchHashChainValue(opts *bind.WatchOpts, sink chan<- *ValidatorRegistrationHashChainValue, previousReceiptRoot [][]byte) (event.Subscription, error) { + + var previousReceiptRootRule []interface{} + for _, previousReceiptRootItem := range previousReceiptRoot { + previousReceiptRootRule = append(previousReceiptRootRule, previousReceiptRootItem) + } + + logs, sub, err := _ValidatorRegistration.contract.WatchLogs(opts, "HashChainValue", previousReceiptRootRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(ValidatorRegistrationHashChainValue) + if err := _ValidatorRegistration.contract.UnpackLog(event, "HashChainValue", log); err != nil { return err } event.Raw = log diff --git a/contracts/validator-registration-contract/validator_registration.sol b/contracts/validator-registration-contract/validator_registration.sol index fea31b709..358efa874 100644 --- a/contracts/validator-registration-contract/validator_registration.sol +++ b/contracts/validator-registration-contract/validator_registration.sol @@ -1,48 +1,111 @@ -pragma solidity 0.4.23; +pragma solidity ^0.4.23; +pragma experimental ABIEncoderV2; + contract ValidatorRegistration { - event ValidatorRegistered( - bytes32 indexed hashedPubkey, - uint256 withdrawalShardID, - address indexed withdrawalAddressbytes32, - bytes32 indexed randaoCommitment + + event HashChainValue( + bytes indexed previousReceiptRoot, + bytes data, + uint totalDepositcount ); - mapping (bytes32 => bool) public usedHashedPubkey; + event ChainStart( + bytes indexed receiptRoot, + bytes time + ); - uint public constant VALIDATOR_DEPOSIT = 32 ether; + uint public constant DEPOSIT_SIZE = 32 ether; + uint public constant DEPOSITS_FOR_CHAIN_START = 8; + uint public constant MIN_TOPUP_SIZE = 1 ether; + uint public constant GWEI_PER_ETH = 10 ** 9; + // Setting MERKLE_TREE_DEPTH to 16 instead of 32 due to gas limit + uint public constant MERKLE_TREE_DEPTH = 16; + uint public constant SECONDS_PER_DAY = 86400; - // Validator registers by sending a transaction of 32ETH to - // the following deposit function. The deposit function takes in - // validator's public key, withdrawal shard ID (which shard - // to send the deposit back to), withdrawal address (which address - // to send the deposit back to) and randao commitment. + mapping (uint => bytes) public receiptTree; + uint public totalDepositCount; + + // When users wish to become a validator by moving ETH from + // 1.0 chian to the 2.0 chain, they should call this function + // sending along DEPOSIT_SIZE ETH and providing depositParams + // as a simple serialize'd DepositParams object of the following + // form: + // { + // 'pubkey': 'int256', + // 'proof_of_possession': ['int256'], + // 'withdrawal_credentials`: 'hash32', + // 'randao_commitment`: 'hash32' + // } function deposit( - bytes _pubkey, - uint _withdrawalShardID, - address _withdrawalAddressbytes32, - bytes32 _randaoCommitment + bytes depositParams ) public payable { + uint index = totalDepositCount + 2 ** MERKLE_TREE_DEPTH; + bytes memory msgGweiInBytes = toBytes(msg.value); + bytes memory timeStampInBytes = toBytes(block.timestamp); + bytes memory depositData = mergeBytes(mergeBytes(msgGweiInBytes, timeStampInBytes), depositParams); + + emit HashChainValue(receiptTree[1], depositParams, totalDepositCount); + + receiptTree[index] = abi.encodePacked(keccak256(depositData)); + for (uint i = 0; i < MERKLE_TREE_DEPTH; i++) { + index = index / 2; + receiptTree[index] = abi.encodePacked(keccak256(mergeBytes(receiptTree[index * 2], receiptTree[index * 2 + 1]))); + } + require( - msg.value == VALIDATOR_DEPOSIT, - "Incorrect validator deposit" + msg.value <= DEPOSIT_SIZE, + "Deposit can't be greater than DEPOSIT_SIZE." ); require( - _pubkey.length == 48, - "Public key is not 48 bytes" + msg.value >= MIN_TOPUP_SIZE, + "Deposit can't be lesser than MIN_TOPUP_SIZE." ); + if (msg.value == DEPOSIT_SIZE) { + totalDepositCount++; + } - bytes32 hashedPubkey = keccak256(abi.encodePacked(_pubkey)); - require( - !usedHashedPubkey[hashedPubkey], - "Public key already used" - ); + // When ChainStart log publishes, beacon chain node initializes the chain and use timestampDayBoundry + // as genesis time. + if (totalDepositCount == DEPOSITS_FOR_CHAIN_START) { + uint timestampDayBoundry = block.timestamp - block.timestamp % SECONDS_PER_DAY + SECONDS_PER_DAY; + bytes memory timestampDayBoundryBytes = toBytes(timestampDayBoundry); + emit ChainStart(receiptTree[1], timestampDayBoundryBytes); + } + } - usedHashedPubkey[hashedPubkey] = true; + function getReceiptRoot() public constant returns (bytes) { + return receiptTree[1]; + } - emit ValidatorRegistered(hashedPubkey, _withdrawalShardID, _withdrawalAddressbytes32, _randaoCommitment); + function toBytes(uint x) private constant returns (bytes memory) { + bytes memory b = new bytes(32); + assembly { mstore(add(b, 32), x) } + return b; + } + + function mergeBytes(bytes memory a, bytes memory b) private returns (bytes memory c) { + // Store the length of the first array + uint alen = a.length; + // Store the length of BOTH arrays + uint totallen = alen + b.length; + // Count the loops required for array a (sets of 32 bytes) + uint loopsa = (a.length + 31) / 32; + // Count the loops required for array b (sets of 32 bytes) + uint loopsb = (b.length + 31) / 32; + assembly { + let m := mload(0x40) + // Load the length of both arrays to the head of the new bytes array + mstore(m, totallen) + // Add the contents of a to the array + for { let i := 0 } lt(i, loopsa) { i := add(1, i) } { mstore(add(m, mul(32, add(1, i))), mload(add(a, mul(32, add(1, i))))) } + // Add the contents of b to the array + for { let i := 0 } lt(i, loopsb) { i := add(1, i) } { mstore(add(m, add(mul(32, add(1, i)), alen)), mload(add(b, mul(32, add(1, i))))) } + mstore(0x40, add(m, add(32, totallen))) + c := m + } } } diff --git a/contracts/validator-registration-contract/validator_registration_test.go b/contracts/validator-registration-contract/validator_registration_test.go index 98daefb3d..eeaef31f6 100644 --- a/contracts/validator-registration-contract/validator_registration_test.go +++ b/contracts/validator-registration-contract/validator_registration_test.go @@ -1,6 +1,7 @@ package vrc import ( + "bytes" "crypto/ecdsa" "fmt" "log" @@ -15,19 +16,16 @@ import ( ) var ( - amount33Eth, _ = new(big.Int).SetString("33000000000000000000", 10) - amount32Eth, _ = new(big.Int).SetString("32000000000000000000", 10) - amount31Eth, _ = new(big.Int).SetString("31000000000000000000", 10) + amount33Eth, _ = new(big.Int).SetString("33000000000000000000", 10) + amount32Eth, _ = new(big.Int).SetString("32000000000000000000", 10) + amountLessThan1Eth, _ = new(big.Int).SetString("500000000000000000", 10) ) type testAccount struct { - addr common.Address - withdrawalAddress common.Address - randaoCommitment [32]byte - pubKey []byte - contract *ValidatorRegistration - backend *backends.SimulatedBackend - txOpts *bind.TransactOpts + addr common.Address + contract *ValidatorRegistration + backend *backends.SimulatedBackend + txOpts *bind.TransactOpts } func setup() (*testAccount, error) { @@ -45,7 +43,7 @@ func setup() (*testAccount, error) { addr := crypto.PubkeyToAddress(privKey.PublicKey) txOpts := bind.NewKeyedTransactor(privKey) - startingBalance, _ := new(big.Int).SetString("100000000000000000000", 10) + startingBalance, _ := new(big.Int).SetString("1000000000000000000000", 10) genesis[addr] = core.GenesisAccount{Balance: startingBalance} backend := backends.NewSimulatedBackend(genesis, 2100000) @@ -54,7 +52,7 @@ func setup() (*testAccount, error) { return nil, err } - return &testAccount{addr, common.Address{}, [32]byte{}, pubKey, contract, backend, txOpts}, nil + return &testAccount{addr, contract, backend, txOpts}, nil } func TestSetupAndContractRegistration(t *testing.T) { @@ -64,100 +62,58 @@ func TestSetupAndContractRegistration(t *testing.T) { } } -// negative test case, public key that is not 48 bytes. -func TestRegisterWithLessThan48BytesPubkey(t *testing.T) { +// negative test case, deposit with less than 1 ETH which is less than the top off amount. +func TestRegisterWithLessThan1Eth(t *testing.T) { testAccount, err := setup() if err != nil { t.Fatal(err) } - var pubKey = make([]byte, 32) - copy(pubKey, testAccount.pubKey[:]) - withdrawAddr := &common.Address{'A', 'D', 'D', 'R', 'E', 'S', 'S'} - randaoCommitment := &[32]byte{'S', 'H', 'H', 'H', 'H', 'I', 'T', 'S', 'A', 'S', 'E', 'C', 'R', 'E', 'T'} - testAccount.txOpts.Value = amount32Eth - _, err = testAccount.contract.Deposit(testAccount.txOpts, pubKey, big.NewInt(0), *withdrawAddr, *randaoCommitment) - if err == nil { - t.Error("Validator registration should have failed with a 32 bytes pubkey") - } -} - -// negative test case, deposit with less than 32 ETH. -func TestRegisterWithLessThan32Eth(t *testing.T) { - testAccount, err := setup() - if err != nil { - t.Fatal(err) - } - withdrawAddr := &common.Address{'A', 'D', 'D', 'R', 'E', 'S', 'S'} - randaoCommitment := &[32]byte{'S', 'H', 'H', 'H', 'H', 'I', 'T', 'S', 'A', 'S', 'E', 'C', 'R', 'E', 'T'} - - testAccount.txOpts.Value = amount31Eth - _, err = testAccount.contract.Deposit(testAccount.txOpts, testAccount.pubKey, big.NewInt(0), *withdrawAddr, *randaoCommitment) + testAccount.txOpts.Value = amountLessThan1Eth + _, err = testAccount.contract.Deposit(testAccount.txOpts, []byte{}) if err == nil { t.Error("Validator registration should have failed with insufficient deposit") } } -// negative test case, deposit more than 32 ETH. +// negative test case, deposit with more than 32 ETH which is more than the asked amount. func TestRegisterWithMoreThan32Eth(t *testing.T) { testAccount, err := setup() if err != nil { t.Fatal(err) } - withdrawAddr := &common.Address{'A', 'D', 'D', 'R', 'E', 'S', 'S'} - randaoCommitment := &[32]byte{'S', 'H', 'H', 'H', 'H', 'I', 'T', 'S', 'A', 'S', 'E', 'C', 'R', 'E', 'T'} testAccount.txOpts.Value = amount33Eth - _, err = testAccount.contract.Deposit(testAccount.txOpts, testAccount.pubKey, big.NewInt(0), *withdrawAddr, *randaoCommitment) + _, err = testAccount.contract.Deposit(testAccount.txOpts, []byte{}) if err == nil { - t.Error("Validator registration should have failed with more than deposit amount") + t.Error("Validator registration should have failed with more than asked deposit amount") } } -// negative test case, test registering with the same public key twice. -func TestRegisterTwice(t *testing.T) { +// normal test case, test depositing 32 ETH and verify HashChainValue event is correctly emitted. +func TestValidatorRegisters(t *testing.T) { testAccount, err := setup() if err != nil { t.Fatal(err) } - withdrawAddr := &common.Address{'A', 'D', 'D', 'R', 'E', 'S', 'S'} - randaoCommitment := &[32]byte{'S', 'H', 'H', 'H', 'H', 'I', 'T', 'S', 'A', 'S', 'E', 'C', 'R', 'E', 'T'} - testAccount.txOpts.Value = amount32Eth - _, err = testAccount.contract.Deposit(testAccount.txOpts, testAccount.pubKey, big.NewInt(0), *withdrawAddr, *randaoCommitment) + + _, err = testAccount.contract.Deposit(testAccount.txOpts, []byte{'A'}) testAccount.backend.Commit() if err != nil { t.Errorf("Validator registration failed: %v", err) } - - testAccount.txOpts.Value = amount32Eth - _, err = testAccount.contract.Deposit(testAccount.txOpts, testAccount.pubKey, big.NewInt(0), *withdrawAddr, *randaoCommitment) - testAccount.backend.Commit() - if err == nil { - t.Errorf("Registration should have failed with same public key twice") - } -} - -// normal test case, test depositing 32 ETH and verify validatorRegistered event is correctly emitted. -func TestRegister(t *testing.T) { - testAccount, err := setup() - if err != nil { - t.Fatal(err) - } - withdrawAddr := &common.Address{'A', 'D', 'D', 'R', 'E', 'S', 'S'} - randaoCommitment := &[32]byte{'S', 'H', 'H', 'H', 'H', 'I', 'T', 'S', 'A', 'S', 'E', 'C', 'R', 'E', 'T'} - shardID := big.NewInt(99) - testAccount.txOpts.Value = amount32Eth - - var hashedPub [32]byte - copy(hashedPub[:], crypto.Keccak256(testAccount.pubKey)) - - _, err = testAccount.contract.Deposit(testAccount.txOpts, testAccount.pubKey, shardID, *withdrawAddr, *randaoCommitment) + _, err = testAccount.contract.Deposit(testAccount.txOpts, []byte{'B'}) testAccount.backend.Commit() if err != nil { t.Errorf("Validator registration failed: %v", err) } - log, err := testAccount.contract.FilterValidatorRegistered(&bind.FilterOpts{}, [][32]byte{}, []common.Address{}, [][32]byte{}) + _, err = testAccount.contract.Deposit(testAccount.txOpts, []byte{'C'}) + testAccount.backend.Commit() + if err != nil { + t.Errorf("Validator registration failed: %v", err) + } + log, err := testAccount.contract.FilterHashChainValue(&bind.FilterOpts{}, [][]byte{}) defer func() { err = log.Close() @@ -173,16 +129,65 @@ func TestRegister(t *testing.T) { t.Fatal(log.Error()) } log.Next() - if log.Event.WithdrawalShardID.Cmp(shardID) != 0 { - t.Errorf("validatorRegistered event withdrawal shard ID miss matched. Want: %v, Got: %v", shardID, log.Event.WithdrawalShardID) + + if log.Event.TotalDepositcount.Cmp(big.NewInt(0)) != 0 { + t.Errorf("HashChainValue event total desposit count miss matched. Want: %v, Got: %v", big.NewInt(0), log.Event.TotalDepositcount) } - if log.Event.RandaoCommitment != *randaoCommitment { - t.Errorf("validatorRegistered event randao commitment miss matched. Want: %v, Got: %v", *randaoCommitment, log.Event.RandaoCommitment) + if !bytes.Equal(log.Event.Data, []byte{'A'}) { + t.Errorf("validatorRegistered event randao commitment miss matched. Want: %v, Got: %v", []byte{'A'}, log.Event.Data) } - if log.Event.HashedPubkey != hashedPub { - t.Errorf("validatorRegistered event public key miss matched. Want: %v, Got: %v", common.BytesToHash(testAccount.pubKey), log.Event.HashedPubkey) + + log.Next() + if log.Event.TotalDepositcount.Cmp(big.NewInt(1)) != 0 { + t.Errorf("HashChainValue event total desposit count miss matched. Want: %v, Got: %v", big.NewInt(1), log.Event.TotalDepositcount) } - if log.Event.WithdrawalAddressbytes32 != *withdrawAddr { - t.Errorf("validatorRegistered event withdrawal address miss matched. Want: %v, Got: %v", *withdrawAddr, log.Event.WithdrawalAddressbytes32) + if !bytes.Equal(log.Event.Data, []byte{'B'}) { + t.Errorf("validatorRegistered event randao commitment miss matched. Want: %v, Got: %v", []byte{'B'}, log.Event.Data) + } + + log.Next() + if log.Event.TotalDepositcount.Cmp(big.NewInt(2)) != 0 { + t.Errorf("HashChainValue event total desposit count miss matched. Want: %v, Got: %v", big.NewInt(1), log.Event.TotalDepositcount) + } + if !bytes.Equal(log.Event.Data, []byte{'C'}) { + t.Errorf("validatorRegistered event randao commitment miss matched. Want: %v, Got: %v", []byte{'B'}, log.Event.Data) + } +} + +// normal test case, test beacon chain start log event. +func TestChainStarts(t *testing.T) { + testAccount, err := setup() + if err != nil { + t.Fatal(err) + } + testAccount.txOpts.Value = amount32Eth + + for i := 0; i < 9; i++ { + _, err = testAccount.contract.Deposit(testAccount.txOpts, []byte{'A'}) + testAccount.backend.Commit() + if err != nil { + t.Errorf("Validator registration failed: %v", err) + } + } + + log, err := testAccount.contract.FilterChainStart(&bind.FilterOpts{}, [][]byte{}) + + defer func() { + err = log.Close() + if err != nil { + t.Fatal(err) + } + }() + + if err != nil { + t.Fatal(err) + } + if log.Error() != nil { + t.Fatal(log.Error()) + } + log.Next() + + if len(log.Event.Time) == 0 { + t.Error("Chain start even did not get emitted, The start time is empty") } }