Skip to content

Commit

Permalink
Merge pull request #239 from digit-ink/master
Browse files Browse the repository at this point in the history
Enable EIP-1559 ETH txs and update deprecated web3 methods/packages
  • Loading branch information
lemoustachiste authored May 3, 2023
2 parents a34a9ac + 107cd83 commit 48420fa
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 34 deletions.
20 changes: 15 additions & 5 deletions cert_issuer/blockchain_handlers/ethereum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@
from cert_issuer.models import MockTransactionHandler
from cert_issuer.signer import FileSecretManager

ONE_BILLION = 1000000000


class EthereumTransactionCostConstants(object):
def __init__(self, recommended_gas_price=20000000000, recommended_gas_limit=25000):
def __init__(self, max_priority_fee_per_gas, recommended_gas_price, recommended_gas_limit):
self.max_priority_fee_per_gas = max_priority_fee_per_gas
self.recommended_gas_price = recommended_gas_price
self.recommended_gas_limit = recommended_gas_limit
logging.info('Set cost constants to recommended_gas_price=%f, recommended_gas_limit=%f',
self.recommended_gas_price, self.recommended_gas_limit)
logging.info('Set cost constants to recommended_gas_price=%f Gwei, recommended_gas_limit=%d gas',
self.recommended_gas_price / ONE_BILLION, self.recommended_gas_limit)
if self.max_priority_fee_per_gas:
logging.info('and max_priority_fee_per_gas=%f Gwei', self.max_priority_fee_per_gas / ONE_BILLION)

"""
The below methods currently only use the supplied gasprice/limit.
Expand All @@ -27,6 +32,9 @@ def __init__(self, recommended_gas_price=20000000000, recommended_gas_limit=2500
def get_recommended_max_cost(self):
return self.recommended_gas_price * self.recommended_gas_limit

def get_max_priority_fee_per_gas(self):
return self.max_priority_fee_per_gas

def get_gas_price(self):
return self.recommended_gas_price

Expand Down Expand Up @@ -60,9 +68,11 @@ def instantiate_blockchain_handlers(app_config):
transaction_handler = MockTransactionHandler()
# ethereum chains
elif chain.is_ethereum_type():
cost_constants = EthereumTransactionCostConstants(app_config.gas_price, app_config.gas_limit)
nonce = app_config.nonce
cost_constants = EthereumTransactionCostConstants(app_config.max_priority_fee_per_gas,
app_config.gas_price, app_config.gas_limit)
connector = EthereumServiceProviderConnector(chain, app_config)
transaction_handler = EthereumTransactionHandler(connector, cost_constants, secret_manager,
transaction_handler = EthereumTransactionHandler(connector, nonce, cost_constants, secret_manager,
issuing_address=issuing_address)

return certificate_batch_handler, transaction_handler, connector
2 changes: 1 addition & 1 deletion cert_issuer/blockchain_handlers/ethereum/connectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ def __init__(self, ethereum_url):

def broadcast_tx(self, tx):
logging.info('Broadcasting transaction with EthereumRPCProvider')
response = self.w3.eth.sendRawTransaction("0x" + tx).hex()
response = self.w3.eth.sendRawTransaction(tx).hex()
return response

def get_balance(self, address):
Expand Down
14 changes: 7 additions & 7 deletions cert_issuer/blockchain_handlers/ethereum/signer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import rlp
from ethereum import transactions
from ethereum.utils import encode_hex
import web3
from eth_utils import to_hex

from cert_issuer.errors import UnableToSignTxError
from cert_issuer.models import Signer
Expand Down Expand Up @@ -28,12 +27,13 @@ def sign_message(self, wif, message_to_sign):
def sign_transaction(self, wif, transaction_to_sign):
##try to sign the transaction.

if isinstance(transaction_to_sign, transactions.Transaction):
if isinstance(transaction_to_sign, dict):
try:
raw_tx = rlp.encode(transaction_to_sign.sign(wif, self.netcode))
raw_tx_hex = encode_hex(raw_tx)
transaction_to_sign['chainId'] = self.netcode
raw_tx = web3.Account.sign_transaction(transaction_to_sign, wif)['rawTransaction']
raw_tx_hex = to_hex(raw_tx)
return raw_tx_hex
except Exception as msg:
return {'error': True, 'message': msg}
else:
raise UnableToSignTxError('You are trying to sign a non transaction type')
raise UnableToSignTxError('"sign_transaction()" expects a dict representing an unsigned transaction with fields such as "gas", "to", "data", etc. run "$ python cert_issuer -h" for more information on transaction configuration.')
23 changes: 13 additions & 10 deletions cert_issuer/blockchain_handlers/ethereum/transaction_handlers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging

from pycoin.serialize import b2h
from web3 import Web3
from eth_utils import to_hex, remove_0x_prefix

from cert_issuer.errors import InsufficientFundsError
from cert_issuer.blockchain_handlers.ethereum import tx_utils
Expand All @@ -14,27 +15,29 @@ def estimate_cost_for_certificate_batch(self):
pass

def create_transaction(self, tx_cost_constants, issuing_address, nonce, to_address, blockchain_bytes):
max_priority_fee_per_gas = tx_cost_constants.get_max_priority_fee_per_gas()
gasprice = tx_cost_constants.get_gas_price()
gaslimit = tx_cost_constants.get_gas_limit()

transaction = tx_utils.create_ethereum_trx(
issuing_address,
nonce,
to_address,
blockchain_bytes,
max_priority_fee_per_gas,
gasprice,
gaslimit)

return transaction


class EthereumTransactionHandler(TransactionHandler):
def __init__(self, connector, tx_cost_constants, secret_manager, issuing_address, prepared_inputs=None,
def __init__(self, connector, nonce, tx_cost_constants, secret_manager, issuing_address, prepared_inputs=None,
transaction_creator=EthereumTransactionCreator()):
self.connector = connector
self.nonce = nonce
self.tx_cost_constants = tx_cost_constants
self.secret_manager = secret_manager
self.issuing_address = issuing_address
self.issuing_address = Web3.toChecksumAddress(issuing_address)
# input transactions are not needed for Ether
self.prepared_inputs = prepared_inputs
self.transaction_creator = transaction_creator
Expand All @@ -46,7 +49,7 @@ def ensure_balance(self):
# for now transaction cost will be a constant: (25000 gas estimate times 20Gwei gasprice) from tx_utils
# can later be calculated inside EthereumTransaction_creator
transaction_cost = self.tx_cost_constants.get_recommended_max_cost()
logging.info('Total cost will be %d wei', transaction_cost)
logging.info('Total cost will be no more than %d wei', transaction_cost)

if transaction_cost > self.balance:
error_message = 'Please add {} wei to the address {}'.format(
Expand All @@ -55,7 +58,7 @@ def ensure_balance(self):
raise InsufficientFundsError(error_message)

def issue_transaction(self, blockchain_bytes):
eth_data_field = b2h(blockchain_bytes)
eth_data_field = remove_0x_prefix(to_hex(blockchain_bytes))
prepared_tx = self.create_transaction(blockchain_bytes)
signed_tx = self.sign_transaction(prepared_tx)
self.verify_transaction(signed_tx, eth_data_field)
Expand All @@ -65,13 +68,13 @@ def issue_transaction(self, blockchain_bytes):
def create_transaction(self, blockchain_bytes):
if self.balance:
# it is assumed here that the address has sufficient funds, as the ensure_balance has just been checked
nonce = self.connector.get_address_nonce(self.issuing_address)
nonce = self.nonce or self.connector.get_address_nonce(self.issuing_address)
logging.info("NONCE IS %d", nonce)
# Transactions in the first iteration will be send to burn address
toaddress = '0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead'
tx = self.transaction_creator.create_transaction(self.tx_cost_constants, self.issuing_address, nonce,
toaddress = Web3.toChecksumAddress('0xdeaddeaddeaddeaddeaddeaddeaddeaddeaddead')
prepared_tx = self.transaction_creator.create_transaction(self.tx_cost_constants, self.issuing_address, nonce,
toaddress, blockchain_bytes)

prepared_tx = tx
return prepared_tx
else:
raise InsufficientFundsError('Not sufficient ether to spend at: %s', self.issuing_address)
Expand Down
16 changes: 12 additions & 4 deletions cert_issuer/blockchain_handlers/ethereum/tx_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,20 @@
from cert_issuer.errors import UnverifiedTransactionError


def create_ethereum_trx(issuing_address, nonce, to_address, blockchain_bytes, gasprice, gaslimit):
def create_ethereum_trx(nonce, to_address, blockchain_bytes, max_priority_fee_per_gas, gasprice, gaslimit):
# the actual value transfer is 0 in the Ethereum implementation
from ethereum.transactions import Transaction
value = 0
tx = Transaction(nonce=nonce, gasprice=gasprice, startgas=gaslimit, to=to_address, value=value,
data=blockchain_bytes)
tx = dict(
nonce=nonce,
gas=gaslimit,
to=to_address,
value=value,
data=blockchain_bytes)
if max_priority_fee_per_gas:
tx['maxFeePerGas'] = gasprice
tx['maxPriorityFeePerGas'] = max_priority_fee_per_gas
else:
tx['gasPrice'] = gasprice
return tx


Expand Down
6 changes: 5 additions & 1 deletion cert_issuer/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,12 @@ def add_arguments(p):
p.add_argument('--no_bitcoind', dest='bitcoind', default=True, action='store_false',
help='Default; do not use bitcoind connectors; use APIs instead', env_var='NO_BITCOIND')
# ethereum arguments
p.add_argument('--nonce', default=0, type=int,
help='sets nonce of ETH transaction. useful if you run your own transaction management system.', env_var='NONCE')
p.add_argument('--max_priority_fee_per_gas', default=0, type=int,
help='decide the priority fee per gas spent for EIP-1559-compliant transactions (in wei, the smallest ETH unit)', env_var='MAX_PRIORITY_FEE_PER_GAS')
p.add_argument('--gas_price', default=20000000000, type=int,
help='decide the price per gas spent (in wei (smallest ETH unit))', env_var='GAS_PRICE')
help='decide the price per gas spent. sets max_fee_per_gas for EIP-1559-compliant transactions.', env_var='GAS_PRICE')
p.add_argument('--gas_limit', default=25000, type=int,
help='decide on the maximum spendable gas. gas_limit < 25000 might not be sufficient', env_var='GAS_LIMIT')
p.add_argument('--etherscan_api_token', default=None, type=str,
Expand Down
7 changes: 2 additions & 5 deletions ethereum_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
web3<=4.4.1
coincurve==7.1.0
ethereum==2.3.1
rlp<1
eth-account<=0.3.0
web3>=5.28.0
eth-utils<2
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cert-core>=3.0.0
cert-schema>=3.2.1
merkletools==1.0.3
configargparse==0.12.0
configargparse==0.13.0
glob2==0.6
mock==2.0.0
requests[security]>=2.18.4
Expand Down

0 comments on commit 48420fa

Please sign in to comment.