Skip to content

Instantly share code, notes, and snippets.

@ralexstokes
Created June 9, 2018 18:13
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ralexstokes/555b4ab3a99ba943e423eba578b9db81 to your computer and use it in GitHub Desktop.
Save ralexstokes/555b4ab3a99ba943e423eba578b9db81 to your computer and use it in GitHub Desktop.
Port `msg_hash.se.py` to Python code.
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