from ethereum import tester as t from ethereum import utils, state_transition, transactions from viper import compiler #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) import rlp s = t.state() t.languages['viper'] = compiler.Compiler() t.gas_limit = 9999999 EPOCH_LENGTH = 256 # Install RLP decoder library s.state.set_balance('0xfe2ec957647679d210034b65e9c7db2452910b0c', 9350880000000000) state_transition.apply_transaction(s.state, rlp.decode(utils.decode_hex('f903bd808506fc23ac008304c1908080b903aa6103988061000e6000396103a65660006101bf5361202059905901600090526101008152602081019050602052600060605261040036018060200159905901600090528181526020810190509050608052600060e0527f0100000000000000000000000000000000000000000000000000000000000000600035046101005260c061010051121561007e57fe5b60f86101005112156100a95760c061010051036001013614151561009e57fe5b6001610120526100ec565b60f761010051036020036101000a600161012051013504610140526101405160f7610100510360010101361415156100dd57fe5b60f76101005103600101610120525b5b366101205112156102ec577f01000000000000000000000000000000000000000000000000000000000000006101205135046101005260e0516060516020026020510152600160605101606052608061010051121561017a57600160e0516080510152600161012051602060e0516080510101376001610120510161012052602160e0510160e0526102da565b60b8610100511215610218576080610100510360e05160805101526080610100510360016101205101602060e05160805101013760816101005114156101ef5760807f010000000000000000000000000000000000000000000000000000000000000060016101205101350412156101ee57fe5b5b600160806101005103016101205101610120526020608061010051030160e0510160e0526102d9565b60c06101005112156102d65760b761010051036020036101000a6001610120510135046101405260007f0100000000000000000000000000000000000000000000000000000000000000600161012051013504141561027357fe5b603861014051121561028157fe5b6101405160e05160805101526101405160b761010051600161012051010103602060e05160805101013761014051600160b7610100510301016101205101610120526020610140510160e0510160e0526102d8565bfe5b5b5b602060605113156102e757fe5b6100ed565b60e051606051602002602051015261082059905901600090526108008152602081019050610160526000610120525b6060516101205113151561035c576020602060605102610120516020026020510151010161012051602002610160510152600161012051016101205261031b565b60e0518060206020606051026101605101018260805160006004600a8705601201f161038457fe5b50602060e051602060605102010161016051f35b6000f31b2d4f'), transactions.Transaction)) assert s.state.get_code('0x0b8178879f97f2ada01fb8d219ee3d0ad74e91e0') # Install sig hasher sighasher = s.contract(open('sighash.se.py').read(), language='serpent') casper_code = open('simple_casper.v.py').read().replace('0x1db3439a222c519ab44bb1144fc28167b4fa6ee6', '0x'+utils.encode_hex(t.a0)) \ .replace('0x38920146f10f3956fc09970beededcb2d9638712', '0x'+utils.encode_hex(sighasher)) print('Casper code length', len(compiler.compile(casper_code))) casper = s.abi_contract(casper_code, language='viper', startgas=5555555) print('Gas consumed', s.state.receipts[-1].gas_used - s.state.receipts[-2].gas_used) 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) sig = utils.encode_int32(v) + utils.encode_int32(r) + utils.encode_int32(s) return rlp.encode([epoch, hash, ancestry_hash, source_epoch, source_ancestry_hash, sig]) def mk_commit(epoch, hash, key): sighash = utils.sha3(rlp.encode([epoch, hash])) v, r, s = utils.ecdsa_raw_sign(sighash, key) sig = utils.encode_int32(v) + utils.encode_int32(r) + utils.encode_int32(s) return rlp.encode([epoch, hash, sig]) def mk_status_flicker(epoch, login, key): sighash = utils.sha3(rlp.encode([epoch, login])) v, r, s = utils.ecdsa_raw_sign(sighash, key) sig = utils.encode_int32(v) + utils.encode_int32(r) + utils.encode_int32(s) return rlp.encode([epoch, login, sig]) s.state.block_number = EPOCH_LENGTH # Initialize the first epoch casper.initialize_epoch(1) assert casper.get_nextValidatorIndex() == 1 start = s.snapshot() # Send a prepare message casper.prepare(0, mk_prepare(1, '\x35' * 32, '\x00' * 32, 0, '\x00' * 32, t.k0)) epoch_1_anchash = utils.sha3(b'\x35' * 32 + b'\x00' * 32) assert casper.get_consensus_messages__hash_justified(1, b'\x35' * 32) assert casper.get_consensus_messages__ancestry_hash_justified(1, epoch_1_anchash) # Send a commit message casper.commit(0, mk_commit(1, '\x35' * 32, t.k0)) # Check that we committed assert casper.get_consensus_messages__committed(1) # Initialize the second epoch s.state.block_number += EPOCH_LENGTH casper.initialize_epoch(2) # Check that the dynasty increased as expected assert casper.get_dynasty() == 1 assert casper.get_total_deposits(1) == casper.get_total_deposits(0) > 0 # Send a prepare message casper.prepare(0, mk_prepare(2, '\x45' * 32, epoch_1_anchash, 1, epoch_1_anchash, t.k0)) # Send a commit message epoch_2_commit = mk_commit(2, '\x45' * 32, t.k0) casper.commit(0, epoch_2_commit) epoch_2_anchash = utils.sha3(b'\x45' * 32 + epoch_1_anchash) assert casper.get_consensus_messages__ancestry_hash_justified(2, epoch_2_anchash) # Check that we committed assert casper.get_consensus_messages__committed(2) # Initialize the third epoch s.state.block_number += EPOCH_LENGTH casper.initialize_epoch(3) # Test the NO_DBL_PREPARE slashing condition p1 = mk_prepare(3, '\x56' * 32, epoch_2_anchash, 2, epoch_2_anchash, t.k0) p2 = mk_prepare(3, '\x57' * 32, epoch_2_anchash, 2, epoch_2_anchash, t.k0) snapshot = s.snapshot() casper.double_prepare_slash(0, p1, p2) s.revert(snapshot) # Test the PREPARE_COMMIT_CONSISTENCY slashing condition p3 = mk_prepare(3, '\x58' * 32, epoch_2_anchash, 0, b'\x00' * 32, t.k0) snapshot = s.snapshot() casper.prepare_commit_inconsistency_slash(0, p3, epoch_2_commit) s.revert(snapshot) # Finish the third epoch casper.prepare(0, p1) casper.commit(0, mk_commit(3, '\x56' * 32, t.k0)) epoch_3_anchash = utils.sha3(b'\x56' * 32 + epoch_2_anchash) assert casper.get_consensus_messages__ancestry_hash_justified(3, epoch_3_anchash) assert casper.get_consensus_messages__committed(3) # Initialize the fourth epoch. Not doing prepares or commits during this epoch. s.state.block_number += EPOCH_LENGTH casper.initialize_epoch(4) assert casper.get_dynasty() == 3 epoch_4_anchash = utils.sha3(b'\x67' * 32 + epoch_3_anchash) # Not publishing this prepare for the time being p4 = mk_prepare(4, '\x78' * 32, '\x12' * 32, 3, '\x24' * 32, t.k0) # Initialize the fifth epoch s.state.block_number += EPOCH_LENGTH casper.initialize_epoch(5) # Dynasty not incremented because no commits were made assert casper.get_dynasty() == 3 epoch_5_anchash = utils.sha3(b'\x78' * 32 + epoch_4_anchash) p5 = mk_prepare(5, '\x78' * 32, epoch_4_anchash, 3, epoch_3_anchash, t.k0) casper.prepare(0, p5) # Test the COMMIT_REQ slashing condition kommit = mk_commit(5, b'\x80' * 32, t.k0) s.state.block_number += EPOCH_LENGTH * 30 for i in range(6, 36): casper.initialize_epoch(i) snapshot = s.snapshot() casper.commit_non_justification_slash(0, kommit) s.revert(snapshot) try: casper.commit_non_justification_slash(0, epoch_2_commit) success = True except: success = False assert not success # Test the PREPARE_REQ slashing condition casper.derive_ancestry(epoch_3_anchash, epoch_2_anchash, epoch_1_anchash) snapshot = s.snapshot() casper.prepare_non_justification_slash(0, p4) s.revert(snapshot) try: casper.prepare_non_justification_slash(0, p5) success = True except: success = False assert not success # Restart the chain s.revert(start) assert casper.get_dynasty() == 0 assert casper.get_current_epoch() == 1 assert casper.get_consensus_messages__ancestry_hash_justified(0, b'\x00' * 32) 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**15) 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, t.k0)) epoch_1_anchash = utils.sha3(b'\x10' * 32 + b'\x00' * 32) assert casper.get_consensus_messages__committed(1) s.state.block_number += EPOCH_LENGTH casper.initialize_epoch(2) assert casper.get_dynasty() == 1 casper.prepare(0, mk_prepare(2, b'\x20' * 32, epoch_1_anchash, 1, epoch_1_anchash, t.k0)) casper.commit(0, mk_commit(2, b'\x20' * 32, t.k0)) epoch_2_anchash = utils.sha3(b'\x20' * 32 + epoch_1_anchash) assert casper.get_consensus_messages__committed(2) s.state.block_number += EPOCH_LENGTH casper.initialize_epoch(3) assert casper.get_dynasty() == 2 assert 3 * 10**15 <= casper.get_total_deposits(0) < 4 * 10**15 assert 3 * 10**15 <= casper.get_total_deposits(1) < 4 * 10**15 assert 21 * 10**15 <= casper.get_total_deposits(2) < 22 * 10**15 try: # Try to log out, but sign with the wrong key casper.flick_status(0, mk_status_flicker(3, 0, t.k1)) success = True except: success = False assert not success # Log out casper.flick_status(4, mk_status_flicker(3, 0, t.k4)) casper.flick_status(5, mk_status_flicker(3, 0, t.k5)) casper.flick_status(6, mk_status_flicker(3, 0, t.k6)) # Validators leave the fwd validator set in dynasty 4 assert casper.get_validators__dynasty_end(4) == 4 epoch_3_anchash = utils.sha3(b'\x30' * 32 + epoch_2_anchash) # Prepare from one validator casper.prepare(0, mk_prepare(3, b'\x30' * 32, epoch_2_anchash, 2, epoch_2_anchash, t.k0)) # Not prepared yet assert not casper.get_consensus_messages__hash_justified(3, b'\x30' * 32) # Prepare from 3 more validators for i, k in ((1, t.k1), (2, t.k2), (3, t.k3)): casper.prepare(i, mk_prepare(3, b'\x30' * 32, epoch_2_anchash, 2, epoch_2_anchash, k)) # Still not prepared assert not casper.get_consensus_messages__hash_justified(3, b'\x30' * 32) # Prepare from a firth validator casper.prepare(4, mk_prepare(3, b'\x30' * 32, epoch_2_anchash, 2, epoch_2_anchash, t.k4)) # NOW we're prepared! assert casper.get_consensus_messages__hash_justified(3, b'\x30' * 32) # Five commits for i, k in enumerate([t.k0, t.k1, t.k2, t.k3, t.k4]): casper.commit(i, mk_commit(3, b'\x30' * 32, k)) # And we committed! assert casper.get_consensus_messages__committed(3) # Start epoch 4 s.state.block_number += EPOCH_LENGTH casper.initialize_epoch(4) assert casper.get_dynasty() == 3 # Prepare and commit epoch_4_anchash = utils.sha3(b'\x40' * 32 + epoch_3_anchash) for i, k in enumerate([t.k0, t.k1, t.k2, t.k3, t.k4]): casper.prepare(i, mk_prepare(4, b'\x40' * 32, epoch_3_anchash, 3, epoch_3_anchash, k)) for i, k in enumerate([t.k0, t.k1, t.k2, t.k3, t.k4]): casper.commit(i, mk_commit(4, b'\x40' * 32, k)) assert casper.get_consensus_messages__committed(4) # Start epoch 5 / dynasty 4 s.state.block_number += EPOCH_LENGTH casper.initialize_epoch(5) assert casper.get_dynasty() == 4 assert 21 * 10**15 <= casper.get_total_deposits(3) <= 22 * 10**15 assert 12 * 10**15 <= casper.get_total_deposits(4) <= 13 * 10**15 epoch_5_anchash = utils.sha3(b'\x50' * 32 + epoch_4_anchash) # Do three prepares for i, k in enumerate([t.k0, t.k1, t.k2]): casper.prepare(i, mk_prepare(5, b'\x50' * 32, epoch_4_anchash, 4, epoch_4_anchash, k)) # Three prepares are insufficient because there are still five validators in the rear validator set assert not casper.get_consensus_messages__hash_justified(5, b'\x50' * 32) # Do two more prepares for i, k in [(3, t.k3), (4, t.k4)]: casper.prepare(i, mk_prepare(5, b'\x50' * 32, epoch_4_anchash, 4, epoch_4_anchash, k)) # Now we're good! assert casper.get_consensus_messages__hash_justified(5, b'\x50' * 32) for i, k in enumerate([t.k0, t.k1, t.k2, t.k3, t.k4]): casper.commit(i, mk_commit(5, b'\x50' * 32, k)) # Committed! assert casper.get_consensus_messages__committed(5) # Start epoch 6 / dynasty 5 s.state.block_number += EPOCH_LENGTH casper.initialize_epoch(6) assert casper.get_dynasty() == 5 # Log back in casper.flick_status(4, mk_status_flicker(6, 1, t.k4)) assert casper.get_validators__dynasty_start(4) == 7 # Here three prepares and three commits should be sufficient! epoch_6_anchash = utils.sha3(b'\x60' * 32 + epoch_5_anchash) for i, k in enumerate([t.k0, t.k1, t.k2]): casper.prepare(i, mk_prepare(6, b'\x60' * 32, epoch_5_anchash, 5, epoch_5_anchash, k)) for i, k in enumerate([t.k0, t.k1, t.k2]): casper.commit(i, mk_commit(6, b'\x60' * 32, k)) assert casper.get_consensus_messages__committed(6) # Start epoch 7 / dynasty 6 s.state.block_number += EPOCH_LENGTH casper.initialize_epoch(7) assert casper.get_dynasty() == 6 # Here three prepares and three commits should be sufficient! epoch_7_anchash = utils.sha3(b'\x70' * 32 + epoch_6_anchash) for i, k in enumerate([t.k0, t.k1, t.k2]): casper.prepare(i, mk_prepare(7, b'\x70' * 32, epoch_6_anchash, 6, epoch_6_anchash, k)) for i, k in enumerate([t.k0, t.k1, t.k2]): casper.commit(i, mk_commit(7, b'\x70' * 32, k)) assert casper.get_consensus_messages__committed(7) # Start epoch 8 / dynasty 7 s.state.block_number += EPOCH_LENGTH casper.initialize_epoch(8) assert casper.get_dynasty() == 7 assert 12 * 10**15 <= casper.get_total_deposits(6) <= 13 * 10**15 assert 15 * 10**15 <= casper.get_total_deposits(7) <= 16 * 10**15 epoch_8_anchash = utils.sha3(b'\x80' * 32 + epoch_7_anchash) # Do three prepares for i, k in enumerate([t.k0, t.k1, t.k2]): casper.prepare(i, mk_prepare(8, b'\x80' * 32, epoch_7_anchash, 7, epoch_7_anchash, k)) # Three prepares are insufficient because there are still five validators in the rear validator set assert not casper.get_consensus_messages__hash_justified(8, b'\x80' * 32) # Do one more prepare for i, k in [(3, t.k3)]: casper.prepare(i, mk_prepare(8, b'\x80' * 32, epoch_7_anchash, 7, epoch_7_anchash, k)) # Now we're good! assert casper.get_consensus_messages__hash_justified(8, b'\x80' * 32) for i, k in enumerate([t.k0, t.k1, t.k2, t.k3, t.k4]): casper.commit(i, mk_commit(8, b'\x80' * 32, k)) assert casper.get_consensus_messages__committed(8)