funga

Signer and keystore daemon and library for cryptocurrency software development
Log | Files | Refs | README | LICENSE

commit fafef39943f896c5895327df1814d76f8f38a6a0
Author: nolash <dev@holbrook.no>
Date:   Tue,  4 Aug 2020 23:41:31 +0200

Initial commit

Diffstat:
Asrc/common.py | 5+++++
Asrc/signer/__init__.py | 1+
Asrc/signer/defaultsigner.py | 39+++++++++++++++++++++++++++++++++++++++
Asrc/transaction.py | 42++++++++++++++++++++++++++++++++++++++++++
Atest/sign.py | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 151 insertions(+), 0 deletions(-)

diff --git a/src/common.py b/src/common.py @@ -0,0 +1,5 @@ +def strip_hex_prefix(hx): + if hx[:2] == '0x': + return hx[2:] + return hx + diff --git a/src/signer/__init__.py b/src/signer/__init__.py @@ -0,0 +1 @@ +from signer.defaultsigner import ReferenceSigner, Signer diff --git a/src/signer/defaultsigner.py b/src/signer/defaultsigner.py @@ -0,0 +1,39 @@ +import logging +import sha3 + +from eth_keys import KeyAPI +from eth_keys.backends import NativeECCBackend + +keys = KeyAPI(NativeECCBackend) +logg = logging.getLogger(__name__) + + +class Signer: + + def __init__(self, keyGetter): + self.keyGetter = keyGetter + + def signTransaction(self, tx): + raise NotImplementedError + + +class ReferenceSigner(Signer): + + def __init__(self, keyGetter): + super(ReferenceSigner, self).__init__(keyGetter) + + + def signTransaction(self, tx): + s = tx.serialize() + h = sha3.keccak_256() + h.update(s) + g = h.digest() + k = keys.PrivateKey(self.keyGetter(tx.sender)) + z = keys.ecdsa_sign(message_hash=g, private_key=k) + tx.v = (tx.v * 2) + 35 + z[64] + tx.r = z[:32] + tx.s = z[32:64] + return z + + + diff --git a/src/transaction.py b/src/transaction.py @@ -0,0 +1,42 @@ +import logging +import binascii + +from rlp import encode as rlp_encode + +from common import strip_hex_prefix + +logg = logging.getLogger(__name__) + +class Transaction: + + def __init__(self, tx, nonce, chainId=1): + + to = binascii.unhexlify(strip_hex_prefix(tx['to'])) + data = binascii.unhexlify(strip_hex_prefix(tx['data'])) + + self.nonce = nonce + self.gas_price = int(tx['gasPrice']) + self.start_gas = int(tx['gas']) + self.to = to + self.value = int(tx['value']) + self.data = data + self.v = chainId + self.r = 0 + self.s = 0 + self.sender = tx['from'] + + + def serialize(self): + b = self.nonce.to_bytes(8, byteorder='little') + s = [ + self.nonce, + self.gas_price, + self.start_gas, + self.to, + self.value, + self.data, + self.v, + self.r, + self.s, + ] + return rlp_encode(s) diff --git a/test/sign.py b/test/sign.py @@ -0,0 +1,64 @@ +#!/usr/bin/python + +import unittest +import logging + +from rlp import encode as rlp_encode + +from signer import ReferenceSigner +from transaction import Transaction + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + + +tx = { + 'from': "0xEB014f8c8B418Db6b45774c326A0E64C78914dC0", + 'gasPrice': "20000000000", + 'gas': "22000", + 'to': '0x3535353535353535353535353535353535353535', + 'value': "1000", + 'data': "deadbeef", +} + +class TestSign(unittest.TestCase): + + pk = None + nonce = -1 + + + def getPk(self, address): + return self.pk + + + def getNonce(self): + self.nonce += 1 + return self.nonce + + + def setUp(self): + #self.pk = b'abcdefghijklmnopqrstuvwxyz012345' #random.sample(range(256), k=32) + self.pk = bytes.fromhex('5087503f0a9cc35b38665955eb830c63f778453dd11b8fa5bd04bc41fd2cc6d6') + + + def tearDown(self): + logg.info('teardown empty') + + + + def test_serialize_transaction(self): + t = Transaction(tx, 0) + self.assertRegex(t.__class__.__name__, "Transaction") + logg.debug('{}'.format(rlp_encode(t.serialize()))) + + + def test_sign_transaction(self): + t = Transaction(tx, 461, 8995) + s = ReferenceSigner(self.getPk) + z = s.signTransaction(t) + logg.debug('{}'.format(z.to_bytes())) + logg.debug('{}'.format(t.serialize().hex())) + + +if __name__ == '__main__': + unittest.main()