mirror of
https://github.com/status-im/nimbus-eth1.git
synced 2025-02-24 17:58:30 +00:00
implement eth_signTransaction
This commit is contained in:
parent
d200a98a68
commit
7819dae7ce
@ -258,6 +258,23 @@ proc setupEthRpc*(node: EthereumNode, chain: BaseChainDB , server: RpcServer) =
|
||||
if not acc.unlocked:
|
||||
raise newException(ValueError, "Account locked, please unlock it first")
|
||||
result = ("0x" & sign(acc.privateKey, cast[string](msg))).HexDataStr
|
||||
|
||||
server.rpc("eth_signTransaction") do(data: TxSend) -> HexDataStr:
|
||||
let
|
||||
address = data.source.toAddress
|
||||
conf = getConfiguration()
|
||||
acc = conf.getAccount(address).tryGet()
|
||||
|
||||
if not acc.unlocked:
|
||||
raise newException(ValueError, "Account locked, please unlock it first")
|
||||
|
||||
let
|
||||
accDB = accountDbFromTag("latest")
|
||||
tx = unsignedTx(data, chain, accDB.getNonce(address) + 1)
|
||||
signedTx = signTransaction(tx, chain, acc.privateKey)
|
||||
rlpTx = rlp.encode(signedTx)
|
||||
|
||||
result = hexDataStr(rlpTx)
|
||||
#[
|
||||
# proc setupTransaction(send: EthSend): Transaction =
|
||||
# let
|
||||
|
@ -20,15 +20,15 @@ type
|
||||
currentBlock* : HexQuantityStr # BlockNumber
|
||||
highestBlock* : HexQuantityStr # BlockNumber
|
||||
|
||||
EthSend* = object
|
||||
TxSend* = object
|
||||
# Parameter from user
|
||||
source*: EthAddressStr # the address the transaction is send from.
|
||||
to*: EthAddressStr # (optional when creating new contract) the address the transaction is directed to.
|
||||
gas*: GasInt # (optional, default: 90000) integer of the gas provided for the transaction execution. It will return unused gas.
|
||||
gasPrice*: GasInt # (optional, default: To-Be-Determined) integer of the gasPrice used for each paid gas.
|
||||
value*: UInt256 # (optional) integer of the value sent with this transaction.
|
||||
data*: EthHashStr # TODO: Support more data. The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see Ethereum Contract ABI.
|
||||
nonce*: AccountNonce # (optional) integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce
|
||||
source*: EthAddressStr # 20 bytes, the address the transaction is send from.
|
||||
to*: Option[EthAddressStr] # (optional when creating new contract) 20 bytes, the address the transaction is directed to.
|
||||
gas*: Option[HexQuantityStr] # (optional, default: 90000) integer of the gas provided for the transaction execution. It will return unused gas.
|
||||
gasPrice*: Option[HexQuantityStr] # (optional, default: To-Be-Determined) integer of the gasPrice used for each paid gas.
|
||||
value*: Option[HexQuantityStr] # (optional) integer of the value sent with this transaction.
|
||||
data*: HexDataStr # TODO: Support more data. The compiled code of a contract OR the hash of the invoked method signature and encoded parameters. For details see Ethereum Contract ABI.
|
||||
nonce*: Option[HexQuantityStr] # (optional) integer of a nonce. This allows to overwrite your own pending transactions that use the same nonce
|
||||
|
||||
EthCall* = object
|
||||
# Parameter from user
|
||||
|
@ -7,9 +7,31 @@
|
||||
# This file may not be copied, modified, or distributed except according to
|
||||
# those terms.
|
||||
|
||||
import hexstrings, eth/[common, rlp], stew/byteutils,
|
||||
../db/[db_chain], strutils, algorithm,
|
||||
../constants, stint
|
||||
import hexstrings, eth/[common, rlp, keys], stew/byteutils, nimcrypto,
|
||||
../db/[db_chain], strutils, algorithm, options,
|
||||
../constants, stint, hexstrings, rpc_types
|
||||
|
||||
type
|
||||
UnsignedTx* = object
|
||||
nonce : AccountNonce
|
||||
gasPrice: GasInt
|
||||
gasLimit: GasInt
|
||||
to {.rlpCustomSerialization.}: EthAddress
|
||||
value : UInt256
|
||||
payload : Blob
|
||||
contractCreation {.rlpIgnore.}: bool
|
||||
|
||||
proc read(rlp: var Rlp, t: var UnsignedTx, _: type EthAddress): EthAddress {.inline.} =
|
||||
if rlp.blobLen != 0:
|
||||
result = rlp.read(EthAddress)
|
||||
else:
|
||||
t.contractCreation = true
|
||||
|
||||
proc append(rlpWriter: var RlpWriter, t: UnsignedTx, a: EthAddress) {.inline.} =
|
||||
if t.contractCreation:
|
||||
rlpWriter.append("")
|
||||
else:
|
||||
rlpWriter.append(a)
|
||||
|
||||
func toAddress*(value: EthAddressStr): EthAddress = hexToPaddedByteArray[20](value.string)
|
||||
|
||||
@ -19,6 +41,15 @@ func toHash*(value: array[32, byte]): Hash256 {.inline.} =
|
||||
func toHash*(value: EthHashStr): Hash256 {.inline.} =
|
||||
result = hexToPaddedByteArray[32](value.string).toHash
|
||||
|
||||
func hexToInt*(s: string, T: typedesc[SomeInteger]): T =
|
||||
var i = 0
|
||||
if s[i] == '0' and (s[i+1] in {'x', 'X'}): inc(i, 2)
|
||||
if s.len - i > sizeof(T) * 2:
|
||||
raise newException(ValueError, "input hex too big for destination int")
|
||||
while i < s.len:
|
||||
result = result shl 4 or readHexChar(s[i]).T
|
||||
inc(i)
|
||||
|
||||
proc headerFromTag*(chain: BaseChainDB, blockTag: string): BlockHeader =
|
||||
let tag = blockTag.toLowerAscii
|
||||
case tag
|
||||
@ -49,3 +80,72 @@ proc calculateMedianGasPrice*(chain: BaseChainDB): GasInt =
|
||||
result = (price div 2).GasInt
|
||||
else:
|
||||
result = prices[middle]
|
||||
|
||||
proc unsignedTx*(tx: TxSend, chain: BaseChainDB, defaultNonce: AccountNonce): UnsignedTx =
|
||||
if tx.to.isSome:
|
||||
result.to = toAddress(tx.to.get())
|
||||
result.contractCreation = false
|
||||
else:
|
||||
result.contractCreation = true
|
||||
|
||||
if tx.gas.isSome:
|
||||
result.gasLimit = hexToInt(tx.gas.get().string, GasInt)
|
||||
else:
|
||||
result.gasLimit = 90000.GasInt
|
||||
|
||||
if tx.gasPrice.isSome:
|
||||
result.gasPrice = hexToInt(tx.gasPrice.get().string, GasInt)
|
||||
else:
|
||||
result.gasPrice = calculateMedianGasPrice(chain)
|
||||
|
||||
if tx.value.isSome:
|
||||
result.value = UInt256.fromHex(tx.value.get().string)
|
||||
else:
|
||||
result.value = 0.u256
|
||||
|
||||
if tx.nonce.isSome:
|
||||
result.nonce = hexToInt(tx.nonce.get().string, AccountNonce)
|
||||
else:
|
||||
result.nonce = defaultNonce
|
||||
|
||||
result.payload = hexToSeqByte(tx.data.string)
|
||||
|
||||
func rlpEncode(tx: UnsignedTx, chainId: uint): auto =
|
||||
rlp.encode(Transaction(
|
||||
accountNonce: tx.nonce,
|
||||
gasPrice: tx.gasPrice,
|
||||
gasLimit: tx.gasLimit,
|
||||
to: tx.to,
|
||||
value: tx.value,
|
||||
payload: tx.payload,
|
||||
isContractCreation: tx.contractCreation,
|
||||
V: chainId.byte,
|
||||
R: 0.u256,
|
||||
S: 0.u256
|
||||
))
|
||||
|
||||
proc signTransaction*(tx: UnsignedTx, chain: BaseChainDB, privateKey: PrivateKey): Transaction =
|
||||
let eip155 = chain.currentBlock >= chain.config.eip155Block
|
||||
let rlpTx = if eip155:
|
||||
rlpEncode(tx, chain.config.chainId)
|
||||
else:
|
||||
rlp.encode(tx)
|
||||
|
||||
let sig = sign(privateKey, rlpTx).toRaw
|
||||
let v = if eip155:
|
||||
byte(sig[64].uint + chain.config.chainId * 2'u + 35'u)
|
||||
else:
|
||||
sig[64] + 27.byte
|
||||
|
||||
result = Transaction(
|
||||
accountNonce: tx.nonce,
|
||||
gasPrice: tx.gasPrice,
|
||||
gasLimit: tx.gasLimit,
|
||||
to: tx.to,
|
||||
value: tx.value,
|
||||
payload: tx.payload,
|
||||
isContractCreation: tx.contractCreation,
|
||||
V: v,
|
||||
R: Uint256.fromBytesBE(sig[0..31]),
|
||||
S: Uint256.fromBytesBE(sig[32..63])
|
||||
)
|
||||
|
@ -35,6 +35,7 @@ proc eth_getUncleCountByBlockHash(data: Hash256): HexQuantityStr
|
||||
proc eth_getUncleCountByBlockNumber(quantityTag: string): HexQuantityStr
|
||||
proc eth_getCode(data: EthAddressStr, quantityTag: string): HexDataStr
|
||||
proc eth_sign(data: EthAddressStr, message: HexDataStr): HexDataStr
|
||||
proc eth_signTransaction(data: TxSend): HexDataStr
|
||||
#proc eth_sendRawTransaction(data: string, quantityTag: int): UInt256
|
||||
proc eth_call(call: EthCall, quantityTag: string): string
|
||||
proc eth_estimateGas(call: EthCall, quantityTag: string): GasInt
|
||||
|
@ -11,7 +11,7 @@ import
|
||||
json_rpc/[rpcserver, rpcclient], eth/common as eth_common,
|
||||
eth/[rlp, keys], eth/trie/db, eth/p2p/rlpx_protocols/eth_protocol,
|
||||
../nimbus/rpc/[common, p2p, hexstrings, rpc_types],
|
||||
../nimbus/[constants, vm_state, config, genesis, utils],
|
||||
../nimbus/[constants, vm_state, config, genesis, utils, transaction],
|
||||
../nimbus/db/[accounts_cache, db_chain, storage_types],
|
||||
../nimbus/p2p/chain,
|
||||
./rpcclient/test_hexstrings, ./test_helpers
|
||||
@ -215,6 +215,21 @@ proc doTests {.async.} =
|
||||
let recoveredAddr = pubkey.toCanonicalAddress()
|
||||
check recoveredAddr == signer # verified
|
||||
|
||||
test "eth_signTransaction":
|
||||
var unsignedTx = TxSend(
|
||||
source: ethAddressStr(signer),
|
||||
to: ethAddressStr(ks2).some,
|
||||
gas: encodeQuantity(100000'u).some,
|
||||
gasPrice: none(HexQuantityStr),
|
||||
value: encodeQuantity(100'u).some,
|
||||
data: HexDataStr("0x"),
|
||||
nonce: none(HexQuantityStr)
|
||||
)
|
||||
|
||||
let res = await client.eth_signTransaction(unsignedTx)
|
||||
let signedTx = rlp.decode(hexToSeqByte(res.string), Transaction)
|
||||
check signer == signedTx.getSender() # verified
|
||||
|
||||
#test "eth_call":
|
||||
# let
|
||||
# blockNum = state.blockheader.blockNumber
|
||||
|
Loading…
x
Reference in New Issue
Block a user