commit 1bbd2c8790722fdc4b0563e120b044a1b712ffe1
parent c669ed829fc4d78cca5a6fb73ab1e6d44abd5c56
Author: nolash <dev@holbrook.no>
Date: Sat, 27 Mar 2021 15:32:05 +0100
Add separate signer with rlp output
Diffstat:
5 files changed, 78 insertions(+), 55 deletions(-)
diff --git a/crypto_dev_signer/eth/signer/defaultsigner.py b/crypto_dev_signer/eth/signer/defaultsigner.py
@@ -4,6 +4,10 @@ import logging
# external imports
import sha3
import coincurve
+from hexathon import int_to_minbytes
+
+# local imports
+from crypto_dev_signer.eth.encoding import chain_id_to_v
logg = logging.getLogger().getChild(__name__)
@@ -31,26 +35,16 @@ class ReferenceSigner(Signer):
h = sha3.keccak_256()
h.update(s)
message_to_sign = h.digest()
- z = self.sign(tx.sender, message_to_sign, password)
-
- vnum = int.from_bytes(tx.v, 'big')
- v = (vnum * 2) + 35 + z[64]
- byts = ((v.bit_length()-1)/8)+1
- tx.v = v.to_bytes(int(byts), 'big')
- tx.r = z[:32]
- tx.s = z[32:64]
+ z = self.sign_pure(tx.sender, message_to_sign, password)
- for i in range(len(tx.r)):
- if tx.r[i] > 0:
- tx.r = tx.r[i:]
- break
+ return z
- for i in range(len(tx.s)):
- if tx.s[i] > 0:
- tx.s = tx.s[i:]
- break
- return z
+ def sign_transaction_to_rlp(self, tx, password=None):
+ chain_id = int.from_bytes(tx.v, byteorder='big')
+ sig = self.sign_transaction(tx, password)
+ tx.apply_signature(chain_id, sig)
+ return tx.rlp_serialize()
def sign_ethereum_message(self, address, message, password=None):
@@ -73,12 +67,12 @@ class ReferenceSigner(Signer):
h.update(ethereumed_message_header + message)
message_to_sign = h.digest()
- z = self.sign(address, message_to_sign, password)
+ z = self.sign_pure(address, message_to_sign, password)
return z
# TODO: generic sign should be moved to non-eth context
- def sign(self, address, message, password=None):
+ def sign_pure(self, address, message, password=None):
pk = coincurve.PrivateKey(secret=self.keyGetter.get(address, password))
z = pk.sign_recoverable(hasher=None, message=message)
return z
diff --git a/crypto_dev_signer/eth/transaction.py b/crypto_dev_signer/eth/transaction.py
@@ -1,15 +1,20 @@
# standard imports
import logging
import binascii
+import re
# external imports
from rlp import encode as rlp_encode
from hexathon import (
strip_0x,
add_0x,
+ int_to_minbytes,
)
-logg = logging.getLogger(__name__)
+# local imports
+from crypto_dev_signer.eth.encoding import chain_id_to_v
+
+logg = logging.getLogger().getChild(__name__)
class Transaction:
@@ -23,54 +28,55 @@ class Transaction:
class EIP155Transaction:
- def __init__(self, tx, nonce, chainId=1):
- to = None
- data = None
- if tx['to'] != None:
- #to = binascii.unhexlify(strip_0x(tx['to'], allow_empty=True))
+ 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))
- if tx['data'] != None:
- #data = binascii.unhexlify(strip_0x(tx['data'], allow_empty=True))
+ if tx.get('data') != None:
data = bytes.fromhex(strip_0x(tx['data'], allow_empty=True))
gas_price = None
start_gas = None
value = None
+ nonce = None
+ chainId = None
+ # TODO: go directly from hex to bytes
try:
gas_price = int(tx['gasPrice'])
+ byts = ((gas_price.bit_length()-1)/8)+1
+ gas_price = gas_price.to_bytes(int(byts), 'big')
except ValueError:
- gas_price = int(tx['gasPrice'], 16)
- byts = ((gas_price.bit_length()-1)/8)+1
- gas_price = gas_price.to_bytes(int(byts), 'big')
+ gas_price = bytes.fromhex(strip_0x(tx['gasPrice'], allow_empty=True))
try:
start_gas = int(tx['gas'])
+ byts = ((start_gas.bit_length()-1)/8)+1
+ start_gas = start_gas.to_bytes(int(byts), 'big')
except ValueError:
- start_gas = int(tx['gas'], 16)
- byts = ((start_gas.bit_length()-1)/8)+1
- start_gas = start_gas.to_bytes(int(byts), 'big')
+ start_gas = bytes.fromhex(strip_0x(tx['gas'], allow_empty=True))
try:
value = int(tx['value'])
+ byts = ((value.bit_length()-1)/8)+1
+ value = value.to_bytes(int(byts), 'big')
except ValueError:
- value = int(tx['value'], 16)
- byts = ((value.bit_length()-1)/8)+1
- value = value.to_bytes(int(byts), 'big')
+ value = bytes.fromhex(strip_0x(tx['value'], allow_empty=True))
try:
- nonce = int(nonce)
+ nonce = int(nonce_in)
+ byts = ((nonce.bit_length()-1)/8)+1
+ nonce = nonce.to_bytes(int(byts), 'big')
except ValueError:
- nonce = int(nonce, 16)
- byts = ((nonce.bit_length()-1)/8)+1
- nonce = nonce.to_bytes(int(byts), 'big')
+ nonce = bytes.fromhex(strip_0x(nonce_in, allow_empty=True))
try:
- chainId = int(chainId)
+ chainId = int(chainId_in)
+ byts = ((chainId.bit_length()-1)/8)+1
+ chainId = chainId.to_bytes(int(byts), 'big')
except ValueError:
- chainId = int(chainId, 16)
- byts = ((chainId.bit_length()-1)/8)+1
- chainId = chainId.to_bytes(int(byts), 'big')
+ chainId = bytes.fromhex(strip_0x(chainId_in, allow_empty=True))
self.nonce = nonce
self.gas_price = gas_price
@@ -120,7 +126,7 @@ class EIP155Transaction:
'gas': add_0x(self.start_gas.hex()),
'to': add_0x(self.to.hex()),
'value': add_0x(self.value.hex(), allow_empty=True),
- 'data': add_0x(self.data.hex()),
+ '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),
@@ -135,3 +141,24 @@ class EIP155Transaction:
tx['nonce'] = '0x00'
return tx
+
+
+ def apply_signature(self, chain_id, signature):
+ if len(self.r + self.s) > 0:
+ raise AttributeError('signature already set')
+ if len(signature) < 65:
+ raise ValueError('invalid signature length')
+ v = chain_id_to_v(chain_id, signature)
+ 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
diff --git a/crypto_dev_signer/runnable/signer.py b/crypto_dev_signer/runnable/signer.py
@@ -102,12 +102,13 @@ def personal_new_account(p):
return r
-def personal_sign_transaction(p):
+def personal_signTransaction(p):
logg.debug('got {} to sign'.format(p[0]))
#t = EIP155Transaction(p[0], p[0]['nonce'], 8995)
t = EIP155Transaction(p[0], p[0]['nonce'], int(p[0]['chainId']))
- z = signer.sign_transaction(t, p[1])
- raw_signed_tx = t.rlp_serialize()
+ # z = signer.sign_transaction(t, p[1])
+ # raw_signed_tx = t.rlp_serialize()
+ raw_signed_tx = signer.sign_transaction_to_rlp(t, p[1])
o = {
'raw': '0x' + raw_signed_tx.hex(),
'tx': t.serialize(),
@@ -116,9 +117,9 @@ def personal_sign_transaction(p):
return o
-# TODO: temporary workaround for platform, since personal_sign_transaction is missing from web3.py
-def eth_sign_transaction(tx):
- return personal_sign_transaction([tx[0], ''])
+# TODO: temporary workaround for platform, since personal_signTransaction is missing from web3.py
+def eth_signTransaction(tx):
+ return personal_signTransaction([tx[0], ''])
def eth_sign(p):
@@ -132,8 +133,8 @@ def eth_sign(p):
methods = {
'personal_newAccount': personal_new_account,
- 'personal_sign_transaction': personal_sign_transaction,
- 'eth_sign_transaction': eth_sign_transaction,
+ 'personal_signTransaction': personal_signTransaction,
+ 'eth_signTransaction': eth_signTransaction,
'eth_sign': eth_sign,
}
diff --git a/requirements.txt b/requirements.txt
@@ -3,9 +3,10 @@ cryptography==3.2.1
pysha3==1.0.2
#simple-rlp==0.1.2
rlp==2.0.1
+eth-utils==1.10.0
json-rpc==1.13.0
confini~=0.3.6a3
sqlalchemy==1.3.20
coincurve==15.0.0
pycrypto==2.6.1
-hexathon~=0.0.1a5
+hexathon~=0.0.1a6
diff --git a/setup.py b/setup.py
@@ -24,7 +24,7 @@ f.close()
setup(
name="crypto-dev-signer",
- version="0.4.14a10",
+ version="0.4.14a14",
description="A signer and keystore daemon and library for cryptocurrency software development",
author="Louis Holbrook",
author_email="dev@holbrook.no",