From 87520a39b1da09ca866d649d8f8818492e55572d Mon Sep 17 00:00:00 2001 From: vub Date: Fri, 14 Apr 2017 15:35:35 -0400 Subject: [PATCH] Added validation code mechanism --- casper4/simple_casper.v.py | 53 +++++++++------------------------ casper4/simple_casper_tester.py | 31 +++++++++++-------- 2 files changed, 33 insertions(+), 51 deletions(-) diff --git a/casper4/simple_casper.v.py b/casper4/simple_casper.v.py index 512e7b6..a0b485a 100644 --- a/casper4/simple_casper.v.py +++ b/casper4/simple_casper.v.py @@ -92,6 +92,8 @@ total_destroyed: wei_value # Sighash calculator library address sighasher: address +# Purity checker library address +purity_checker: address # Reward for preparing or committing, as fraction of deposit size reward_factor: public(decimal) @@ -136,6 +138,8 @@ def initiate(): self.current_epoch = block.number / self.epoch_length # Set the sighash calculator address self.sighasher = 0x476c2cA9a7f3B16FeCa86512276271FAf63B6a24 + # Set the purity checker address + self.purity_checker = 0xD7a3BD6C9eA32efF147d067f907AE6b22d436F91 # Set an initial root of the epoch hash chain self.consensus_messages[0].ancestry_hash_justified[0x0000000000000000000000000000000000000000000000000000000000000000] = True # self.consensus_messages[0].committed = True @@ -182,6 +186,7 @@ def initialize_epoch(epoch: num): # Send a deposit to join the validator set def deposit(validation_addr: address, withdrawal_addr: address): assert self.current_epoch == block.number / self.epoch_length + assert extract32(raw_call(self.purity_checker, concat('\xa1\x90>\xab', as_bytes32(validation_addr)), gas=500000, outsize=32), 0) != as_bytes32(0) self.validators[self.nextValidatorIndex] = { deposit: msg.value, dynasty_start: self.dynasty + 2, @@ -210,11 +215,7 @@ def flick_status(validator_index: num, logout_msg: bytes <= 1024): sig = values[2] assert self.current_epoch == epoch # Signature check - assert len(sig) == 96 - assert ecrecover(sighash, - as_num256(extract32(sig, 0)), - as_num256(extract32(sig, 32)), - as_num256(extract32(sig, 64))) == self.validators[validator_index].addr + assert extract32(raw_call(self.validators[validator_index].addr, concat(sighash, sig), gas=500000, outsize=32), 0) == as_bytes32(1) # Logging in if login_flag: # Check that we are logged out @@ -323,11 +324,7 @@ def prepare(validator_index: num, prepare_msg: bytes <= 1024): sig = values[5] # For now, the sig is a simple ECDSA sig # Check the signature - assert len(sig) == 96 - assert ecrecover(sighash, - extract32(sig, 0, type=num256), - extract32(sig, 32, type=num256), - extract32(sig, 64, type=num256)) == self.validators[validator_index].addr + assert extract32(raw_call(self.validators[validator_index].addr, concat(sighash, sig), gas=500000, outsize=32), 0) == as_bytes32(1) # Check that we are in an epoch after we started validating assert self.current_epoch >= self.dynasty_start_epoch[self.validators[validator_index].dynasty_start] # Check that this prepare has not yet been made @@ -386,11 +383,7 @@ def commit(validator_index: num, commit_msg: bytes <= 1024): prev_commit_epoch = values[2] sig = values[3] # Check the signature - assert len(sig) == 96 - assert ecrecover(sighash, - extract32(sig, 0, type=num256), - extract32(sig, 32, type=num256), - extract32(sig, 64, type=num256)) == self.validators[validator_index].addr + assert extract32(raw_call(self.validators[validator_index].addr, concat(sighash, sig), gas=500000, outsize=32), 0) == as_bytes32(1) # Check that we are in the right epoch assert self.current_epoch == block.number / self.epoch_length assert self.current_epoch == epoch @@ -441,14 +434,8 @@ def double_prepare_slash(validator_index: num, prepare1: bytes <= 1000, prepare2 epoch2 = values2[0] sig2 = values2[5] # Check the signatures - assert ecrecover(sighash1, - as_num256(extract32(sig1, 0)), - as_num256(extract32(sig1, 32)), - as_num256(extract32(sig1, 64))) == self.validators[validator_index].addr - assert ecrecover(sighash2, - as_num256(extract32(sig2, 0)), - as_num256(extract32(sig2, 32)), - as_num256(extract32(sig2, 64))) == self.validators[validator_index].addr + assert extract32(raw_call(self.validators[validator_index].addr, concat(sighash1, sig1), gas=500000, outsize=32), 0) == as_bytes32(1) + assert extract32(raw_call(self.validators[validator_index].addr, concat(sighash2, sig2), gas=500000, outsize=32), 0) == as_bytes32(1) # Check that they're from the same epoch assert epoch1 == epoch2 # Check that they're not the same message @@ -474,14 +461,8 @@ def prepare_commit_inconsistency_slash(validator_index: num, prepare_msg: bytes commit_epoch = values2[0] sig2 = values2[3] # Check the signatures - assert ecrecover(sighash1, - as_num256(extract32(sig1, 0)), - as_num256(extract32(sig1, 32)), - as_num256(extract32(sig1, 64))) == self.validators[validator_index].addr - assert ecrecover(sighash2, - as_num256(extract32(sig2, 0)), - as_num256(extract32(sig2, 32)), - as_num256(extract32(sig2, 64))) == self.validators[validator_index].addr + assert extract32(raw_call(self.validators[validator_index].addr, concat(sighash1, sig1), gas=500000, outsize=32), 0) == as_bytes32(1) + assert extract32(raw_call(self.validators[validator_index].addr, concat(sighash2, sig2), gas=500000, outsize=32), 0) == as_bytes32(1) # Check that they're not the same message assert sighash1 != sighash2 # Check that the prepare refers to something older than the commit @@ -504,10 +485,7 @@ def commit_non_justification_slash(validator_index: num, commit_msg: bytes <= 10 sig = values[3] # Check the signature assert len(sig) == 96 - assert ecrecover(sighash, - extract32(sig, 0, type=num256), - extract32(sig, 32, type=num256), - extract32(sig, 64, type=num256)) == self.validators[validator_index].addr + assert extract32(raw_call(self.validators[validator_index].addr, concat(sighash, sig), gas=500000, outsize=32), 0) == as_bytes32(1) # Check that the commit is old enough assert self.current_epoch == block.number / self.epoch_length assert (self.current_epoch - epoch) * self.epoch_length * self.block_time > self.insufficiency_slash_delay @@ -543,10 +521,7 @@ def prepare_non_justification_slash(validator_index: num, prepare_msg: bytes <= source_ancestry_hash = values[4] sig = values[5] # Check the signature - assert ecrecover(sighash, - extract32(sig, 0, type=num256), - extract32(sig, 32, type=num256), - extract32(sig, 64, type=num256)) == self.validators[validator_index].addr + assert extract32(raw_call(self.validators[validator_index].addr, concat(sighash, sig), gas=500000, outsize=32), 0) == as_bytes32(1) # Check that the view change is old enough assert self.current_epoch == block.number / self.epoch_length assert (self.current_epoch - epoch) * self.block_time * self.epoch_length > self.insufficiency_slash_delay diff --git a/casper4/simple_casper_tester.py b/casper4/simple_casper_tester.py index ebc8f5e..e0ec243 100644 --- a/casper4/simple_casper_tester.py +++ b/casper4/simple_casper_tester.py @@ -1,6 +1,7 @@ from ethereum import tester as t from ethereum import utils, state_transition, transactions, abi from viper import compiler +import serpent from ethereum.slogging import LogRecorder, configure_logging, set_level config_string = ':info,eth.vm.log:trace,eth.vm.op:trace,eth.vm.stack:trace,eth.vm.exit:trace,eth.pb.msg:trace,eth.pb.tx:debug' #configure_logging(config_string=config_string) @@ -19,6 +20,15 @@ def inject_tx(txhex): assert s.state.get_code(contract_address) return contract_address +code_template = """ +~calldatacopy(0, 0, 128) +~call(3000, 1, 0, 0, 128, 0, 32) +return(~mload(0) == %s) +""" + +def mk_validation_code(address): + return serpent.compile(code_template % (utils.checksum_encode(address))) + # Install RLP decoder library rlp_decoder_address = inject_tx( '0xf90237808506fc23ac00830330888080b902246102128061000e60003961022056600060007f010000000000000000000000000000000000000000000000000000000000000060003504600060c082121515585760f882121561004d5760bf820336141558576001905061006e565b600181013560f783036020035260005160f6830301361415585760f6820390505b5b368112156101c2577f010000000000000000000000000000000000000000000000000000000000000081350483602086026040015260018501945060808112156100d55760018461044001526001828561046001376001820191506021840193506101bc565b60b881121561014357608081038461044001526080810360018301856104600137608181141561012e5760807f010000000000000000000000000000000000000000000000000000000000000060018401350412151558575b607f81038201915060608103840193506101bb565b60c08112156101b857600182013560b782036020035260005160388112157f010000000000000000000000000000000000000000000000000000000000000060018501350402155857808561044001528060b6838501038661046001378060b6830301830192506020810185019450506101ba565bfe5b5b5b5061006f565b601f841315155857602060208502016020810391505b6000821215156101fc578082604001510182826104400301526020820391506101d8565b808401610420528381018161044003f350505050505b6000f31b2d4f') @@ -37,10 +47,14 @@ ct = abi.ContractTranslator([{'name': 'check(address)', 'type': 'function', 'con assert utils.big_endian_to_int(s.send(t.k0, purity_checker_address, 0, ct.encode('submit', [rlp_decoder_address]))) == 1 assert utils.big_endian_to_int(s.send(t.k0, purity_checker_address, 0, ct.encode('submit', [sighasher_address]))) == 1 +k1_valcode_addr = s.send(t.k0, "", 0, mk_validation_code(t.a0)) +assert utils.big_endian_to_int(s.send(t.k0, purity_checker_address, 0, ct.encode('submit', [k1_valcode_addr]))) == 1 + # Install Casper -casper_code = open('simple_casper.v.py').read().replace('0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6', utils.checksum_encode(t.a0)) \ - .replace('0x476c2cA9a7f3B16FeCa86512276271FAf63B6a24', utils.checksum_encode(sighasher_address)) +casper_code = open('simple_casper.v.py').read().replace('0x1Db3439a222C519ab44bb1144fC28167b4Fa6EE6', utils.checksum_encode(k1_valcode_addr)) \ + .replace('0x476c2cA9a7f3B16FeCa86512276271FAf63B6a24', utils.checksum_encode(sighasher_address)) \ + .replace('0xD7a3BD6C9eA32efF147d067f907AE6b22d436F91', utils.checksum_encode(purity_checker_address)) print('Casper code length', len(compiler.compile(casper_code))) @@ -50,15 +64,6 @@ print('Gas consumed to launch Casper', s.state.receipts[-1].gas_used - s.state.r # Helper functions for making a prepare, commit, login and logout message -code_template = """ -~calldatacopy(0, 0, 128) -~call(3000, 1, 0, 0, 128, 0, 32) -return(~mload(0) == %s) -""" - -def mk_validation_code(address): - return serpent.compile(code_template % (utils.checksum_encode(address))) - def mk_prepare(epoch, hash, ancestry_hash, source_epoch, source_ancestry_hash, key): sighash = utils.sha3(rlp.encode([epoch, hash, ancestry_hash, source_epoch, source_ancestry_hash])) v, r, s = utils.ecdsa_raw_sign(sighash, key) @@ -209,7 +214,9 @@ assert casper.get_current_epoch() == 1 assert casper.get_consensus_messages__ancestry_hash_justified(0, b'\x00' * 32) print("Epoch 1 initialized") for k in (t.k1, t.k2, t.k3, t.k4, t.k5, t.k6): - casper.deposit(utils.privtoaddr(k), utils.privtoaddr(k), value=3 * 10**18) + valcode_addr = s.send(t.k0, '', 0, mk_validation_code(utils.privtoaddr(k))) + assert utils.big_endian_to_int(s.send(t.k0, purity_checker_address, 0, ct.encode('submit', [valcode_addr]))) == 1 + casper.deposit(valcode_addr, utils.privtoaddr(k), value=3 * 10**18) print("Processed 6 deposits") casper.prepare(0, mk_prepare(1, b'\x10' * 32, b'\x00' * 32, 0, b'\x00' * 32, t.k0)) casper.commit(0, mk_commit(1, b'\x10' * 32, 0, t.k0))