funga/crypto_dev_signer/eth/transaction.py

173 lines
4.6 KiB
Python
Raw Normal View History

2020-08-08 10:45:37 +02:00
# standard imports
2020-08-04 23:41:31 +02:00
import logging
import binascii
2021-03-27 15:32:05 +01:00
import re
2020-08-04 23:41:31 +02:00
# external imports
#from rlp import encode as rlp_encode
from hexathon import (
strip_0x,
add_0x,
2021-03-27 15:32:05 +01:00
int_to_minbytes,
)
2020-08-04 23:41:31 +02:00
2021-03-27 15:32:05 +01:00
# local imports
from crypto_dev_signer.eth.encoding import chain_id_to_v
2021-04-12 19:09:11 +02:00
#from crypto_dev_signer.eth.rlp import rlp_encode
import rlp
2021-03-27 15:32:05 +01:00
logg = logging.getLogger().getChild(__name__)
2020-08-04 23:41:31 +02:00
2021-04-12 19:09:11 +02:00
rlp_encode = rlp.encode
2020-08-08 10:45:37 +02:00
2020-08-04 23:41:31 +02:00
class Transaction:
2020-08-06 11:07:18 +02:00
def rlp_serialize(self):
raise NotImplementedError
def serialize(self):
raise NotImplementedError
class EIP155Transaction:
2020-08-04 23:41:31 +02:00
2021-03-27 15:32:05 +01:00
def __init__(self, tx, nonce_in, chainId_in=1):
to = b''
data = b''
if tx.get('to') != None:
to = bytes.fromhex(strip_0x(tx['to'], allow_empty=True))
2021-03-27 15:32:05 +01:00
if tx.get('data') != None:
data = bytes.fromhex(strip_0x(tx['data'], allow_empty=True))
2020-08-04 23:41:31 +02:00
2020-10-26 09:02:29 +01:00
gas_price = None
start_gas = None
value = None
2021-03-27 15:32:05 +01:00
nonce = None
chainId = None
2020-10-26 09:02:29 +01:00
2021-03-27 15:32:05 +01:00
# TODO: go directly from hex to bytes
2020-10-26 09:02:29 +01:00
try:
gas_price = int(tx['gasPrice'])
2021-03-27 15:32:05 +01:00
byts = ((gas_price.bit_length()-1)/8)+1
gas_price = gas_price.to_bytes(int(byts), 'big')
2020-10-26 09:02:29 +01:00
except ValueError:
2021-03-27 15:32:05 +01:00
gas_price = bytes.fromhex(strip_0x(tx['gasPrice'], allow_empty=True))
2020-10-26 09:02:29 +01:00
try:
start_gas = int(tx['gas'])
2021-03-27 15:32:05 +01:00
byts = ((start_gas.bit_length()-1)/8)+1
start_gas = start_gas.to_bytes(int(byts), 'big')
2020-10-26 09:02:29 +01:00
except ValueError:
2021-03-27 15:32:05 +01:00
start_gas = bytes.fromhex(strip_0x(tx['gas'], allow_empty=True))
2020-10-26 09:02:29 +01:00
try:
value = int(tx['value'])
2021-03-27 15:32:05 +01:00
byts = ((value.bit_length()-1)/8)+1
value = value.to_bytes(int(byts), 'big')
2020-10-26 09:02:29 +01:00
except ValueError:
2021-03-27 15:32:05 +01:00
value = bytes.fromhex(strip_0x(tx['value'], allow_empty=True))
2020-10-26 09:02:29 +01:00
try:
2021-03-27 15:32:05 +01:00
nonce = int(nonce_in)
byts = ((nonce.bit_length()-1)/8)+1
nonce = nonce.to_bytes(int(byts), 'big')
2020-10-26 09:02:29 +01:00
except ValueError:
2021-03-27 15:32:05 +01:00
nonce = bytes.fromhex(strip_0x(nonce_in, allow_empty=True))
2021-01-12 14:50:52 +01:00
try:
2021-03-27 15:32:05 +01:00
chainId = int(chainId_in)
byts = ((chainId.bit_length()-1)/8)+1
chainId = chainId.to_bytes(int(byts), 'big')
2021-01-12 14:50:52 +01:00
except ValueError:
2021-03-27 15:32:05 +01:00
chainId = bytes.fromhex(strip_0x(chainId_in, allow_empty=True))
2020-10-26 09:02:29 +01:00
2020-08-04 23:41:31 +02:00
self.nonce = nonce
2020-10-26 09:02:29 +01:00
self.gas_price = gas_price
self.start_gas = start_gas
2020-08-04 23:41:31 +02:00
self.to = to
2020-10-26 09:02:29 +01:00
self.value = value
2020-08-04 23:41:31 +02:00
self.data = data
self.v = chainId
2020-08-06 11:07:18 +02:00
self.r = b''
self.s = b''
self.sender = strip_0x(tx['from'])
2020-08-04 23:41:31 +02:00
2021-04-12 19:09:11 +02:00
def canonical_order(self):
2020-08-04 23:41:31 +02:00
s = [
self.nonce,
self.gas_price,
self.start_gas,
self.to,
self.value,
self.data,
self.v,
self.r,
self.s,
]
return s
def bytes_serialize(self):
2021-04-12 19:09:11 +02:00
s = self.canonical_order()
b = b''
for e in s:
b += e
return b
def rlp_serialize(self):
2021-04-12 19:09:11 +02:00
s = self.canonical_order()
2020-08-04 23:41:31 +02:00
return rlp_encode(s)
def serialize(self):
2021-01-12 14:50:52 +01:00
tx = {
'nonce': add_0x(self.nonce.hex(), allow_empty=True),
'gasPrice': add_0x(self.gas_price.hex()),
'gas': add_0x(self.start_gas.hex()),
'value': add_0x(self.value.hex(), allow_empty=True),
2021-03-27 15:32:05 +01:00
'data': add_0x(self.data.hex(), allow_empty=True),
'v': add_0x(self.v.hex(), allow_empty=True),
'r': add_0x(self.r.hex(), allow_empty=True),
's': add_0x(self.s.hex(), allow_empty=True),
}
if self.to == None or len(self.to) == 0:
tx['to'] = None
else:
tx['to'] = add_0x(self.to.hex())
2021-01-12 14:50:52 +01:00
if tx['data'] == '':
tx['data'] = '0x'
if tx['value'] == '':
tx['value'] = '0x00'
if tx['nonce'] == '':
tx['nonce'] = '0x00'
return tx
2021-03-27 15:32:05 +01:00
2021-06-25 13:08:26 +02:00
def apply_signature(self, chain_id, signature, v=None):
2021-03-27 15:32:05 +01:00
if len(self.r + self.s) > 0:
raise AttributeError('signature already set')
if len(signature) < 65:
raise ValueError('invalid signature length')
2021-06-25 13:08:26 +02:00
if v == None:
v = chain_id_to_v(chain_id, signature)
2021-03-27 15:32:05 +01:00
self.v = int_to_minbytes(v)
self.r = signature[:32]
self.s = signature[32:64]
for i in range(len(self.r)):
if self.r[i] > 0:
self.r = self.r[i:]
break
for i in range(len(self.s)):
if self.s[i] > 0:
self.s = self.s[i:]
break