Created
June 9, 2018 18:13
-
-
Save ralexstokes/555b4ab3a99ba943e423eba578b9db81 to your computer and use it in GitHub Desktop.
Port `msg_hash.se.py` to Python code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from web3 import Web3 | |
import rlp | |
from eth_tester import ( | |
EthereumTester, | |
PyEVMBackend | |
) | |
from web3.providers.eth_tester import ( | |
EthereumTesterProvider, | |
) | |
base_tester = EthereumTester(PyEVMBackend()) | |
w3 = Web3(EthereumTesterProvider(base_tester)) | |
validation_key = w3.eth.account.create(str(base_tester.get_accounts()[0])).privateKey | |
# from utils | |
def encode_int32(val): | |
return Web3.toBytes(val).rjust(32, b'\x00') | |
class Memory(list): | |
# https://stackoverflow.com/a/869901 | |
def __setitem__(self, index, value): | |
size = len(self) | |
if index >= size: | |
self.extend(0 for _ in range(size, index + 1)) | |
list.__setitem__(self, index, value)# input byte array | |
def casper_vote_with_hash(): | |
msg = [1, w3.sha3(text='some block hash'), 11, 10] | |
msg_hash = w3.sha3(rlp.encode(msg)) | |
signed = w3.eth.account.signHash(msg_hash, validation_key) | |
sig = encode_int32(signed.v) + encode_int32(signed.r) + encode_int32(signed.s) | |
msg.append(sig) | |
return [rlp.encode(msg), msg_hash] | |
message, expected_msg_hash = casper_vote_with_hash() | |
calldata = message | |
# memory | |
memory = Memory([]) | |
# Fetches the char from calldata at position index | |
def calldatachar(index): | |
return calldata[index] | |
# Fetches the next b bytes from calldata starting at position x | |
def calldata_bytes_as_int(x, b): | |
return sum(calldata[x:x+b]) | |
# Save some space for the msg_hash later | |
msg_hash = None | |
# Position in calldata | |
pos = 0 | |
# First char in calldata | |
c0 = calldatachar(0) | |
# The start of the array must be in 192...255 because it represents | |
# a list length | |
# Length ++ body case | |
if c0 < 248: | |
pos = 1 | |
else: | |
# Length of length ++ length ++ body case | |
pos = (c0 - 246) | |
# Start position of the list (save it) | |
startpos = pos | |
# Start position of the previous element | |
lastpos = 0 | |
# Keep looping until we hit the end of the input | |
while pos < len(calldata): | |
# Next char in calldata | |
c = calldatachar(pos) | |
lastpos = pos | |
# Single byte 0x00...0x7f body case | |
if c < 128: | |
pos += 1 | |
# Length ++ body case | |
elif c < 184: | |
pos += c - 127 | |
# Length of length ++ length ++ body case | |
elif c < 192: | |
pos += calldata_bytes_as_int(pos + 1, c - 183) + (c - 182) | |
# Length of new RLP list | |
newlen = lastpos - startpos | |
# Length ++ body case | |
if newlen < 56: | |
# Store length in the first byte | |
memory[0] = 192 + newlen | |
# Copy calldata right after length | |
for i in range(newlen): | |
memory[1+i] = calldata[startpos+i] | |
msg_hash = w3.sha3(bytes(memory[0:1+newlen])) | |
else: | |
# The log256 of the length (ie. length of length) | |
# Can't go higher than 16777216 bytes due to gas limits | |
_log = None | |
if newlen < 256: | |
_log = 1 | |
elif newlen < 65536: | |
_log = 2 | |
else: | |
_log = 3 | |
# Store the length | |
memory[0] = newlen | |
# Store the length of the length right before the length | |
index_to_list_length = 31 - _log | |
memory[index_to_list_length] = 247 + _log | |
# Store the rest of the data | |
for i in range(newlen): | |
memory[32+i] = calldata[startpos+i] | |
# Return the hash | |
msg_hash = w3.sha3(bytes(memory[index_to_list_length:1+_log+newlen])) | |
assert expected_msg_hash == msg_hash |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment