Skip to content

Commit b15ce55

Browse files
tx: implement tx containers and tx workers [WIP]
1 parent 41f158b commit b15ce55

File tree

6 files changed

+403
-0
lines changed

6 files changed

+403
-0
lines changed
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
import type {
2+
AccessList,
3+
AccessListBytes,
4+
AuthorizationList,
5+
AuthorizationListBytes,
6+
JSONTx,
7+
TransactionType,
8+
} from './types.js'
9+
import type {
10+
Address,
11+
AddressLike,
12+
BigIntLike,
13+
BytesLike,
14+
PrefixedHexString,
15+
} from '@ethereumjs/util'
16+
17+
// TODO
18+
// Make a very simple "Features" class which handles supports/activate/deactivate (?)
19+
20+
export enum Feature {
21+
ReplayProtection = 'ReplayProtection', // For EIP-155 replay protection
22+
ECDSASignable = 'ECDSASignable', // For unsigned/signed ECDSA containers
23+
ECDSASigned = 'ECDSASigned', // For signed ECDSA containers
24+
25+
LegacyGasMarket = 'LegacyGasMarket', // Txs with legacy gas market (pre-1559)
26+
FeeMarket = 'FeeMarket', // Txs with EIP1559 gas market
27+
28+
TypedTransaction = 'TypedTransaction',
29+
30+
AccessLists = 'AccessLists',
31+
EOACode = 'EOACode',
32+
}
33+
34+
export type NestedUint8Array = (Uint8Array | NestedUint8Array)[]
35+
36+
export interface TxDataContainer {
37+
supports(capability: Feature): boolean
38+
type: TransactionType
39+
40+
// Raw list of Uint8Arrays (can be nested)
41+
raw(): NestedUint8Array // TODO make more tx-specific
42+
43+
// The serialized version of the raw() one
44+
// (current: RLP.encode)
45+
serialize(): Uint8Array
46+
47+
// Utility to convert to a JSON object
48+
toJSON(): JSONTx
49+
50+
/** Signature related stuff, TODO */
51+
/*
52+
isSigned(): boolean
53+
isValid(): boolean
54+
verifySignature(): boolean
55+
getSenderAddress(): Address
56+
getSenderPublicKey(): Uint8Array
57+
sign(privateKey: Uint8Array): Transaction[T]
58+
errorStr(): string
59+
60+
addSignature(
61+
v: bigint,
62+
r: Uint8Array | bigint,
63+
s: Uint8Array | bigint,
64+
convertV?: boolean,
65+
): Transaction[T]
66+
67+
68+
// Get the non-hashed message to sign (this is input, but then hashed, is input to methods like ecsign)
69+
getMessageToSign(): Uint8Array | Uint8Array[]
70+
// Get the hashed message to sign (allows for flexibility over the hash method, now: keccak256)
71+
getHashedMessageToSign(): Uint8Array
72+
73+
// The hash of the transaction (note: hash currently has to do with signed txs but on L2 likely can also be of non-signed txs (?))
74+
hash(): Uint8Array
75+
76+
*/
77+
}
78+
79+
// Container "fields" and container "interface" below
80+
// Fields: used for the CONSTRUCTOR of the containers
81+
// Interface: used for the resulting constructor, so each param of the field is converted to that type before resulting in the container
82+
83+
export type DefaultContainerDataFields = {
84+
nonce?: BigIntLike
85+
gasLimit?: BigIntLike
86+
to?: AddressLike
87+
value?: BigIntLike
88+
data?: BytesLike | '' // Note: '' is for empty data (TODO look if we want to keep this)
89+
}
90+
91+
export interface DefaultContainerInterface {
92+
readonly gasPrice: bigint
93+
readonly nonce: bigint
94+
readonly gasLimit: bigint
95+
readonly value: bigint
96+
readonly data: Uint8Array
97+
readonly to: Address | null // TODO: figure out how to handle this on txs which do not allow to:null (7702/4844)
98+
}
99+
100+
export type ECDSASignedContainerFields = {
101+
v?: BigIntLike
102+
r?: BigIntLike
103+
s?: BigIntLike
104+
}
105+
106+
export interface ECDSAContainerInterface {
107+
readonly v?: bigint
108+
readonly r?: bigint
109+
readonly s?: bigint
110+
}
111+
112+
// The container / tx data fields if the tx can create contracts (to `null`)
113+
/*export type ContractCreationDataFields = {
114+
to?: DefaultContainerDataFields['to'] | null | ''
115+
}*/
116+
117+
/*
118+
export interface ContractCreationContainerInterface {
119+
to: DefaultContainerInterface['to'] | null
120+
}
121+
*/
122+
123+
export type LegacyGasMarketFields = {
124+
gasPrice: BigIntLike
125+
}
126+
127+
export interface LegacyGasMarketInterface {
128+
readonly gasPrice: bigint
129+
}
130+
131+
interface L1DefaultContainer
132+
extends TxDataContainer,
133+
DefaultContainerInterface,
134+
ECDSAContainerInterface {}
135+
136+
export interface LegacyContainerInterface extends L1DefaultContainer, LegacyGasMarketInterface {
137+
// to: DefaultContainerInterface['to'] | null
138+
}
139+
140+
export type ChainIdFields = {
141+
chainId?: BigIntLike
142+
}
143+
144+
export interface ChainIdInterface {
145+
chainId: bigint
146+
}
147+
148+
export type AccessListFields = {
149+
accessList?: AccessListBytes | AccessList | null
150+
}
151+
152+
export interface AccessListInterface {
153+
accessList: AccessListBytes
154+
}
155+
156+
interface L1_2930Interface extends L1DefaultContainer, ChainIdInterface, AccessListInterface {}
157+
158+
export interface AccessList2930ContainerInterface
159+
extends L1_2930Interface,
160+
LegacyGasMarketInterface {}
161+
162+
// interface AccessList2930Interface: L1DefaultFields, ContractCreationDataFields, LegacyGasMarket, ChainId, AccessList
163+
164+
// EIP1559 txs
165+
export type FeeMarketFields = {
166+
maxPriorityFeePerGas?: BigIntLike
167+
maxFeePerGas?: BigIntLike
168+
}
169+
170+
export interface FeeMarketInterface {
171+
readonly maxPriorityFeePerGas: bigint
172+
readonly maxFeePerGas: bigint
173+
}
174+
175+
export interface FeeMarket1559Interface extends L1_2930Interface, FeeMarketInterface {}
176+
177+
// EIP4844 txs
178+
export type BlobFields = {
179+
blobVersionedHashes?: BytesLike[]
180+
maxFeePerBlobGas?: BigIntLike
181+
blobs?: BytesLike[]
182+
kzgCommitments?: BytesLike[]
183+
kzgProofs?: BytesLike[]
184+
blobsData?: string[]
185+
}
186+
187+
export interface BlobInterface {
188+
readonly blobVersionedHashes: PrefixedHexString[] // TODO why is this a string and not uint8array?
189+
readonly blobs?: PrefixedHexString[]
190+
readonly kzgCommitments?: PrefixedHexString[]
191+
readonly kzgProofs?: PrefixedHexString[]
192+
readonly maxFeePerBlobGas: bigint
193+
}
194+
195+
export interface Blob4844Interface extends FeeMarket1559Interface, BlobInterface {}
196+
197+
// EIP7702 txs
198+
199+
export type AuthorizationListFields = {
200+
authorizationList?: AuthorizationListBytes | AuthorizationList | never
201+
}
202+
203+
export interface AuthorizationListInterface {
204+
readonly authorizationList: AuthorizationListBytes
205+
}
206+
207+
export interface EOA7702Interface extends FeeMarket1559Interface, AuthorizationListInterface {}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Address, bytesToBigInt, toBytes } from '@ethereumjs/util'
2+
3+
import { Feature } from '../dataContainerTypes.js'
4+
5+
import type { AccessListInterface } from '../dataContainerTypes.js'
6+
7+
const accessListFeatures = new Set<Feature>([
8+
Feature.ECDSASignable,
9+
Feature.LegacyGasMarket,
10+
Feature.AccessLists,
11+
])
12+
13+
export class AccessList2930Container implements AccessListInterface {}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import type { EOA7702Interface } from '../dataContainerTypes.js'
2+
3+
export class EOA7702DataContainer implements EOA7702Interface {}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
import { RLP } from '@ethereumjs/rlp'
2+
import { Address, bytesToBigInt, toBytes } from '@ethereumjs/util'
3+
4+
import { Feature } from '../dataContainerTypes.js'
5+
import { TransactionType } from '../types.js'
6+
7+
import type {
8+
ContractCreationContainerInterface,
9+
DefaultContainerInterface,
10+
ECDSAContainerInterface,
11+
LegacyContainerDataFields,
12+
LegacyGasMarketInterface,
13+
TxDataContainer,
14+
} from '../dataContainerTypes.js'
15+
import type { TxOptions } from '../types.js'
16+
17+
const legacyFeatures = new Set<Feature>([Feature.ECDSASignable, Feature.LegacyGasMarket])
18+
19+
export class LegacyDataContainer
20+
implements
21+
TxDataContainer,
22+
DefaultContainerInterface,
23+
ContractCreationContainerInterface,
24+
ECDSAContainerInterface,
25+
LegacyGasMarketInterface
26+
{
27+
public type: number = TransactionType.Legacy // Legacy tx type
28+
29+
// Tx data part (part of the RLP)
30+
public readonly gasPrice: bigint
31+
public readonly nonce: bigint
32+
public readonly gasLimit: bigint
33+
public readonly value: bigint
34+
public readonly data: Uint8Array
35+
// TODO fix type (how to do this? need to somehow override the interface)
36+
public readonly to: Address | null
37+
38+
// Props only for signed txs
39+
public readonly v?: bigint
40+
public readonly r?: bigint
41+
public readonly s?: bigint
42+
43+
// TODO: verify if txOptions is necessary
44+
// TODO (optimizing): for reach tx we auto-convert the input values to the target values (mostly bigints)
45+
// Is this necessary? What if we need the unconverted values? Convert it on the fly?
46+
constructor(txData: LegacyContainerDataFields, txOptions: TxOptions) {
47+
const { nonce, gasLimit, to, value, data, v, r, s } = txData
48+
49+
// Set the tx properties
50+
const toB = toBytes(to === '' ? '0x' : to)
51+
this.to = toB.length > 0 ? new Address(toB) : null
52+
53+
this.nonce = bytesToBigInt(toBytes(nonce))
54+
this.gasLimit = bytesToBigInt(toBytes(gasLimit))
55+
this.value = bytesToBigInt(toBytes(value))
56+
this.data = toBytes(data === '' ? '0x' : data)
57+
this.gasPrice = bytesToBigInt(toBytes(txData.gasPrice))
58+
59+
// Set signature values (if the tx is signed)
60+
61+
const vB = toBytes(v)
62+
const rB = toBytes(r)
63+
const sB = toBytes(s)
64+
this.v = vB.length > 0 ? bytesToBigInt(vB) : undefined
65+
this.r = rB.length > 0 ? bytesToBigInt(rB) : undefined
66+
this.s = sB.length > 0 ? bytesToBigInt(sB) : undefined
67+
}
68+
69+
raw() {
70+
// TODO
71+
return []
72+
}
73+
serialize() {
74+
return RLP.encode(this.raw())
75+
}
76+
77+
supports(feature: Feature) {
78+
return legacyFeatures.has(feature)
79+
}
80+
81+
toJSON() {
82+
return {}
83+
}
84+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { RLP } from '@ethereumjs/rlp'
2+
3+
import type { TxDataContainer } from '../dataContainerTypes.js'
4+
import type { NestedUint8Array } from '@ethereumjs/rlp'
5+
6+
export abstract class TemplateDataContainer implements TxDataContainer {
7+
type = -1
8+
9+
abstract raw(): NestedUint8Array
10+
serialize() {
11+
// Defaults to use RLP.encode
12+
return RLP.encode(this.raw())
13+
}
14+
15+
supports(/*feature: Feature*/) {
16+
return false
17+
}
18+
19+
toJSON() {
20+
return {}
21+
}
22+
}

0 commit comments

Comments
 (0)