Refactor wallet signing

This commit is contained in:
Mark Spanbroek 2023-07-04 11:38:48 +02:00 committed by markspanbroek
parent f1a1221d14
commit 842bf4d0a2
2 changed files with 73 additions and 61 deletions

View File

@ -1,11 +1,9 @@
import eth/keys import eth/keys
import eth/rlp
import eth/common
import eth/common/transaction as ct
import ./provider import ./provider
import ./transaction as tx import ./transaction
import ./signer import ./signer
import ./wallet/error import ./wallet/error
import ./wallet/signing
export keys export keys
export WalletError export WalletError
@ -17,8 +15,6 @@ proc getRng: ref HmacDrbgContext =
rng = newRng() rng = newRng()
rng rng
type SignableTransaction = common.Transaction
type Wallet* = ref object of Signer type Wallet* = ref object of Signer
privateKey*: PrivateKey privateKey*: PrivateKey
publicKey*: PublicKey publicKey*: PublicKey
@ -58,61 +54,13 @@ method provider*(wallet: Wallet): Provider =
method getAddress(wallet: Wallet): Future[Address] {.async.} = method getAddress(wallet: Wallet): Future[Address] {.async.} =
return wallet.address return wallet.address
proc signTransaction(tr: var SignableTransaction, pk: PrivateKey) = proc signTransaction*(wallet: Wallet,
# Temporary V value, used to signal to the hashing function the transaction: Transaction): Future[seq[byte]] {.async.} =
# chain id that we'd like to use for an EIP-155 signature if sender =? transaction.sender and sender != wallet.address:
tr.V = int64(uint64(tr.chainId)) * 2 + 35
let h = tr.txHashNoSignature
let s = sign(pk, SkMessage(h.data))
let r = toRaw(s)
let v = r[64]
tr.R = fromBytesBE(UInt256, r.toOpenArray(0, 31))
tr.S = fromBytesBE(UInt256, r.toOpenArray(32, 63))
case tr.txType:
of TxLegacy:
tr.V = int64(v) + int64(uint64(tr.chainId))*2 + 35
of TxEip1559:
tr.V = int64(v)
else:
raiseWalletError "Transaction type not supported"
proc signTransaction*(wallet: Wallet, tx: tx.Transaction): Future[seq[byte]] {.async.} =
if sender =? tx.sender and sender != wallet.address:
raiseWalletError "from address mismatch" raiseWalletError "from address mismatch"
without nonce =? tx.nonce and chainId =? tx.chainId and gasLimit =? tx.gasLimit: return wallet.privateKey.sign(transaction)
raiseWalletError "Transaction is not properly populated"
var s: SignableTransaction method sendTransaction*(wallet: Wallet, transaction: Transaction): Future[TransactionResponse] {.async.} =
let signed = await signTransaction(wallet, transaction)
if maxFee =? tx.maxFee and maxPriorityFee =? tx.maxPriorityFee: return await provider(wallet).sendTransaction(signed)
s.txType = TxEip1559
s.maxFee = GasInt(maxFee.truncate(uint64))
s.maxPriorityFee = GasInt(maxPriorityFee.truncate(uint64))
elif gasPrice =? tx.gasPrice:
s.txType = TxLegacy
s.gasPrice = GasInt(gasPrice.truncate(uint64))
else:
raiseWalletError "Transaction is not properly populated"
s.chainId = ChainId(chainId.truncate(uint64))
s.gasLimit = GasInt(gasLimit.truncate(uint64))
s.value = tx.value
s.nonce = nonce.truncate(uint64)
s.to = some EthAddress(tx.to)
s.payload = tx.data
signTransaction(s, wallet.privateKey)
return rlp.encode(s)
method sendTransaction*(wallet: Wallet, tx: tx.Transaction): Future[TransactionResponse] {.async.} =
let rawTX = await signTransaction(wallet, tx)
return await provider(wallet).sendTransaction(rawTX)
#TODO add functionality to sign messages
#TODO add functionality to create wallets from Mnemoniks or Keystores

64
ethers/wallet/signing.nim Normal file
View File

@ -0,0 +1,64 @@
import pkg/eth/keys
import pkg/eth/rlp
import pkg/eth/common/transaction as eth
import ../basics
import ../transaction as ethers
import ./error
type
Transaction = ethers.Transaction
SignableTransaction = eth.Transaction
func toSignableTransaction(transaction: Transaction): SignableTransaction =
var signable: SignableTransaction
without nonce =? transaction.nonce:
raiseWalletError "missing nonce"
without chainId =? transaction.chainId:
raiseWalletError "missing chain id"
without gasLimit =? transaction.gasLimit:
raiseWalletError "missing gas limit"
signable.nonce = nonce.truncate(uint64)
signable.chainId = ChainId(chainId.truncate(uint64))
signable.gasLimit = GasInt(gasLimit.truncate(uint64))
signable.to = some EthAddress(transaction.to)
signable.value = transaction.value
signable.payload = transaction.data
if maxFee =? transaction.maxFee and
maxPriorityFee =? transaction.maxPriorityFee:
signable.txType = TxEip1559
signable.maxFee = GasInt(maxFee.truncate(uint64))
signable.maxPriorityFee = GasInt(maxPriorityFee.truncate(uint64))
elif gasPrice =? transaction.gasPrice:
signable.txType = TxLegacy
signable.gasPrice = GasInt(gasPrice.truncate(uint64))
else:
raiseWalletError "missing gas price"
signable
func sign(key: PrivateKey, transaction: SignableTransaction): seq[byte] =
var transaction = transaction
# Temporary V value, used to signal to the hashing function
# that we'd like to use an EIP-155 signature
transaction.V = int64(uint64(transaction.chainId)) * 2 + 35
let hash = transaction.txHashNoSignature().data
let signature = key.sign(SkMessage(hash)).toRaw()
transaction.R = UInt256.fromBytesBE(signature[0..<32])
transaction.S = UInt256.fromBytesBE(signature[32..<64])
transaction.V = int64(signature[64])
if transaction.txType == TxLegacy:
transaction.V += int64(uint64(transaction.chainId)) * 2 + 35
rlp.encode(transaction)
func sign*(key: PrivateKey, transaction: Transaction): seq[byte] =
key.sign(transaction.toSignableTransaction())