diff --git a/casper4/rlp_decoder.se b/casper4/rlp_decoder.se new file mode 100644 index 0000000..2f2c65d --- /dev/null +++ b/casper4/rlp_decoder.se @@ -0,0 +1,64 @@ +macro calldatachar($x): + div(calldataload($x), 2**248) + +macro calldatabytes_as_int($x, $b): + div(calldataload($x), 256**(32-$b)) + +def any(): + positions = array(32) + positionIndex = 0 + data = string(1024) + dataPos = 0 + # Can only parse lists + c = calldatachar(0) + if c < 192: + ~invalid() + if c < 248: + if ~calldatasize() != 1 + (c - 192): + ~invalid() + i = 1 + else: + L = calldatabytes_as_int(i + 1, c - 247) + if ~calldatasize() != 1 + (c - 247) + L: + ~invalid() + i = 1 + (c - 247) + while i < ~calldatasize(): + c = calldatachar(i) + positions[positionIndex] = dataPos + positionIndex += 1 + if c < 128: + calldatacopy(data + dataPos, i, 1) + i += 1 + dataPos += 1 + elif c < 184: + calldatacopy(data + dataPos, i + 1, c - 128) + # Output could have been in single-byte format + if c == 129: + if calldatachar(i + 1) < 128: + ~invalid() + i += c - 128 + 1 + dataPos += (c - 128) + elif c < 192: + L = calldatabytes_as_int(i + 1, c - 183) + # Forbid leading zero byte + if calldatachar(i + 1) == 0: + ~invalid() + # Forbid too short values + if L < 56: + ~invalid() + calldatacopy(data + dataPos, i + 1 + c - 183, L) + i += (c - 183) + 1 + L + dataPos += L + else: + # Not handling nested arrays + ~invalid() + if dataPos > 1024 or positionIndex > 32: + ~invalid() + positions[positionIndex] = dataPos + output = string(2048) + i = 0 + while i <= positionIndex: + output[i] = positions[i] + positionIndex * 32 + 32 + i += 1 + mcopy(output + positionIndex * 32 + 32, data, dataPos) + ~return(output, positionIndex * 32 + dataPos + 32) diff --git a/casper4/remove_rlp_last.se b/casper4/sighash.se similarity index 93% rename from casper4/remove_rlp_last.se rename to casper4/sighash.se index 8ff70e3..b0798a6 100644 --- a/casper4/remove_rlp_last.se +++ b/casper4/sighash.se @@ -45,7 +45,7 @@ newmemindex = 1000 if newlen < 56: ~mstore8(newmemindex, 192 + newlen) ~calldatacopy(newmemindex + 1, startpos, newlen) - ~return(newmemindex, 1 + newlen) + return(~sha3(newmemindex, 1 + newlen)) else: _log = 0 _newlen = newlen @@ -55,4 +55,4 @@ else: ~mstore8(newmemindex, 247 + _log) ~mstore(newmemindex + 1, newlen * (256 ** (32 - _log))) ~calldatacopy(newmemindex + 1 + _log, startpos, newlen) - ~return(newmemindex, 1 + _log + newlen) + return(~sha3(newmemindex, 1 + _log + newlen)) diff --git a/casper4/simple_casper.v.py b/casper4/simple_casper.v.py index bce6c1d..a8d3c7f 100644 --- a/casper4/simple_casper.v.py +++ b/casper4/simple_casper.v.py @@ -82,7 +82,8 @@ def __init__(): self.interest_rate = 0.000001 self.block_time = 7 self.epoch_length = 256 - self.withdrawal_delay = 2500000 + # Only ~11.5 days, for testing purposes + self.withdrawal_delay = 1000000 # Temporary backdoor for testing purposes (to allow recovering destroyed deposits) self.owner = 0x1db3439a222c519ab44bb1144fc28167b4fa6ee6 # Add an initial validator @@ -99,7 +100,7 @@ def __init__(): # Initialize the epoch counter self.current_epoch = block.number / self.epoch_length # Set the RLP decoder address - self.rlp_decoder = 0x2dadb850a35439906b82be11dfe3e00d5f944213 + self.rlp_decoder = 0x38920146f10f3956fc09970beededcb2d9638711 # Called at the start of any epoch def initialize_epoch(epoch: num): @@ -166,8 +167,20 @@ def withdraw(index: num): # Process a prepare message def prepare(index: num, epoch: num, hash: bytes32, ancestry_hash: bytes32, epoch_source: num, source_ancestry_hash: bytes32, sig: bytes <= 96): - # Signature check - sighash = sha3(concat("prepare", as_bytes32(epoch), hash, ancestry_hash, as_bytes32(epoch_source), source_ancestry_hash)) + # Get hash for signature, and implicitly assert that it is an RLP list + # consisting solely of RLP elements + sighash = extract32(raw_call(self.sighasher, self.prepare_msg, gas=50000, outsize=32), 0) + values = raw_call(self.rlp_decoder, self.prepare_msg, gas=50000, outsize=2048) + # Extract parameters + epoch = extract32(values, extract32(value, 0, type=num128), type=num128) + hash = extract32(values, extract32(value, 32, type=num128), type=bytes32) + ancestry_hash = extract32(values, extract32(value, 64, type=num128), type=bytes32) + source_epoch = extract32(values, extract32(value, 96, type=num128), type=num128) + source_ancestry_hash = extract32(values, extract32(value, 128, type=num128), type=bytes32) + sig = slice(values, start=extract32(value, 160, type=num128), end=extract32(value, 192, type=num128)) + # Assert that there are only 6 elements + assert as_num128(extract32(value, 0)) == 224 + # For now, the sig is a simple ECDSA sig assert len(sig) == 96 assert ecrecover(sighash, as_num256(extract32(sig, 0)),