@nomicfoundation/ethereumjs-tx
Version:
Implementation of the various Ethereum Transaction Types
112 lines (92 loc) • 2.95 kB
text/typescript
import {
SECP256K1_ORDER_DIV_2,
bigIntToUnpaddedBytes,
ecrecover,
} from '@nomicfoundation/ethereumjs-util'
import { keccak256 as bufferKeccak256 } from 'ethereum-cryptography/keccak.js'
import { BaseTransaction } from '../baseTransaction.js'
import { Capability } from '../types.js'
import type { LegacyTxInterface } from '../types.js'
function keccak256(msg: Uint8Array): Uint8Array {
return new Uint8Array(bufferKeccak256(Buffer.from(msg)))
}
export function errorMsg(tx: LegacyTxInterface, msg: string) {
return `${msg} (${tx.errorStr()})`
}
export function isSigned(tx: LegacyTxInterface): boolean {
const { v, r, s } = tx
if (v === undefined || r === undefined || s === undefined) {
return false
} else {
return true
}
}
/**
* The amount of gas paid for the data in this tx
*/
export function getDataFee(tx: LegacyTxInterface, extraCost?: bigint): bigint {
if (tx.cache.dataFee && tx.cache.dataFee.hardfork === tx.common.hardfork()) {
return tx.cache.dataFee.value
}
const cost = BaseTransaction.prototype.getDataFee.bind(tx)() + (extraCost ?? 0n)
if (Object.isFrozen(tx)) {
tx.cache.dataFee = {
value: cost,
hardfork: tx.common.hardfork(),
}
}
return cost
}
export function hash(tx: LegacyTxInterface): Uint8Array {
if (!tx.isSigned()) {
const msg = errorMsg(tx, 'Cannot call hash method if transaction is not signed')
throw new Error(msg)
}
const keccakFunction = tx.common.customCrypto.keccak256 ?? keccak256
if (Object.isFrozen(tx)) {
if (!tx.cache.hash) {
tx.cache.hash = keccakFunction(tx.serialize())
}
return tx.cache.hash
}
return keccakFunction(tx.serialize())
}
/**
* EIP-2: All transaction signatures whose s-value is greater than secp256k1n/2are considered invalid.
* Reasoning: https://ethereum.stackexchange.com/a/55728
*/
export function validateHighS(tx: LegacyTxInterface): void {
const { s } = tx
if (tx.common.gteHardfork('homestead') && s !== undefined && s > SECP256K1_ORDER_DIV_2) {
const msg = errorMsg(
tx,
'Invalid Signature: s-values greater than secp256k1n/2 are considered invalid'
)
throw new Error(msg)
}
}
export function getSenderPublicKey(tx: LegacyTxInterface): Uint8Array {
if (tx.cache.senderPubKey !== undefined) {
return tx.cache.senderPubKey
}
const msgHash = tx.getMessageToVerifySignature()
const { v, r, s } = tx
validateHighS(tx)
try {
const ecrecoverFunction = tx.common.customCrypto.ecrecover ?? ecrecover
const sender = ecrecoverFunction(
msgHash,
v!,
bigIntToUnpaddedBytes(r!),
bigIntToUnpaddedBytes(s!),
tx.supports(Capability.EIP155ReplayProtection) ? tx.common.chainId() : undefined
)
if (Object.isFrozen(tx)) {
tx.cache.senderPubKey = sender
}
return sender
} catch (e: any) {
const msg = errorMsg(tx, 'Invalid Signature')
throw new Error(msg)
}
}