8
8
ecsign ,
9
9
publicToAddress ,
10
10
} from 'ethereumjs-util'
11
- import { BaseTransactionData , BaseTxOptions , DEFAULT_COMMON , JsonTx } from './types'
11
+ import { TxData , TxOptions , JsonTx } from './types'
12
12
13
13
export abstract class BaseTransaction < TransactionObject > {
14
14
public readonly nonce : BN
@@ -19,8 +19,12 @@ export abstract class BaseTransaction<TransactionObject> {
19
19
public readonly data : Buffer
20
20
public readonly common : Common
21
21
22
- constructor ( txData : BaseTransactionData , txOptions : BaseTxOptions = { } ) {
23
- const { nonce, gasLimit, gasPrice, to, value, data } = txData
22
+ public readonly v ?: BN
23
+ public readonly r ?: BN
24
+ public readonly s ?: BN
25
+
26
+ constructor ( txData : TxData , txOptions : TxOptions = { } ) {
27
+ const { nonce, gasLimit, gasPrice, to, value, data, v, r, s } = txData
24
28
25
29
this . nonce = new BN ( toBuffer ( nonce ) )
26
30
this . gasPrice = new BN ( toBuffer ( gasPrice ) )
@@ -29,50 +33,62 @@ export abstract class BaseTransaction<TransactionObject> {
29
33
this . value = new BN ( toBuffer ( value ) )
30
34
this . data = toBuffer ( data )
31
35
36
+ this . v = v ? new BN ( toBuffer ( v ) ) : undefined
37
+ this . r = r ? new BN ( toBuffer ( r ) ) : undefined
38
+ this . s = s ? new BN ( toBuffer ( s ) ) : undefined
39
+
32
40
const validateCannotExceedMaxInteger = {
33
41
nonce : this . nonce ,
34
42
gasPrice : this . gasPrice ,
35
43
gasLimit : this . gasLimit ,
36
44
value : this . value ,
37
45
}
38
46
39
- this . validateExceedsMaxInteger ( validateCannotExceedMaxInteger )
47
+ this . _validateExceedsMaxInteger ( validateCannotExceedMaxInteger )
40
48
41
49
this . common =
42
50
( txOptions . common &&
43
51
Object . assign ( Object . create ( Object . getPrototypeOf ( txOptions . common ) ) , txOptions . common ) ) ??
44
- DEFAULT_COMMON
45
- }
46
-
47
- protected validateExceedsMaxInteger ( validateCannotExceedMaxInteger : { [ key : string ] : BN } ) {
48
- for ( const [ key , value ] of Object . entries ( validateCannotExceedMaxInteger ) ) {
49
- if ( value && value . gt ( MAX_INTEGER ) ) {
50
- throw new Error ( `${ key } cannot exceed MAX_INTEGER, given ${ value } ` )
51
- }
52
- }
52
+ new Common ( { chain : 'mainnet' } )
53
53
}
54
54
55
55
/**
56
- * If the tx's `to` is to the creation address
56
+ * Checks if the transaction has the minimum amount of gas required
57
+ * (DataFee + TxFee + Creation Fee).
57
58
*/
58
- toCreationAddress ( ) : boolean {
59
- return this . to === undefined || this . to . buf . length === 0
60
- }
61
-
62
59
/**
63
- * Computes a sha3-256 hash of the serialized unsigned tx, which is used to sign the transaction.
60
+ * Checks if the transaction has the minimum amount of gas required
61
+ * (DataFee + TxFee + Creation Fee).
64
62
*/
65
- rawTxHash ( ) : Buffer {
66
- return this . getMessageToSign ( )
67
- }
63
+ validate ( ) : boolean
64
+ /* eslint-disable-next-line no-dupe-class-members */
65
+ validate ( stringError : false ) : boolean
66
+ /* eslint-disable-next-line no-dupe-class-members */
67
+ validate ( stringError : true ) : string [ ]
68
+ /* eslint-disable-next-line no-dupe-class-members */
69
+ validate ( stringError : boolean = false ) : boolean | string [ ] {
70
+ const errors = [ ]
68
71
69
- abstract getMessageToSign ( ) : Buffer
72
+ if ( this . getBaseFee ( ) . gt ( this . gasLimit ) ) {
73
+ errors . push ( `gasLimit is too low. given ${ this . gasLimit } , need at least ${ this . getBaseFee ( ) } ` )
74
+ }
75
+
76
+ if ( this . isSigned ( ) && ! this . verifySignature ( ) ) {
77
+ errors . push ( 'Invalid Signature' )
78
+ }
79
+
80
+ return stringError ? errors : errors . length === 0
81
+ }
70
82
71
83
/**
72
- * Returns chain ID
84
+ * The minimum amount of gas the tx must have (DataFee + TxFee + Creation Fee)
73
85
*/
74
- getChainId ( ) : number {
75
- return this . common . chainId ( )
86
+ getBaseFee ( ) : BN {
87
+ const fee = this . getDataFee ( ) . addn ( this . common . param ( 'gasPrices' , 'tx' ) )
88
+ if ( this . common . gteHardfork ( 'homestead' ) && this . toCreationAddress ( ) ) {
89
+ fee . iaddn ( this . common . param ( 'gasPrices' , 'txCreation' ) )
90
+ }
91
+ return fee
76
92
}
77
93
78
94
/**
@@ -89,17 +105,6 @@ export abstract class BaseTransaction<TransactionObject> {
89
105
return new BN ( cost )
90
106
}
91
107
92
- /**
93
- * The minimum amount of gas the tx must have (DataFee + TxFee + Creation Fee)
94
- */
95
- getBaseFee ( ) : BN {
96
- const fee = this . getDataFee ( ) . addn ( this . common . param ( 'gasPrices' , 'tx' ) )
97
- if ( this . common . gteHardfork ( 'homestead' ) && this . toCreationAddress ( ) ) {
98
- fee . iaddn ( this . common . param ( 'gasPrices' , 'txCreation' ) )
99
- }
100
- return fee
101
- }
102
-
103
108
/**
104
109
* The up front amount that an account must have for this transaction to be valid
105
110
*/
@@ -108,44 +113,45 @@ export abstract class BaseTransaction<TransactionObject> {
108
113
}
109
114
110
115
/**
111
- * Checks if the transaction has the minimum amount of gas required
112
- * (DataFee + TxFee + Creation Fee).
116
+ * If the tx's `to` is to the creation address
113
117
*/
118
+ toCreationAddress ( ) : boolean {
119
+ return this . to === undefined || this . to . buf . length === 0
120
+ }
121
+
114
122
/**
115
- * Checks if the transaction has the minimum amount of gas required
116
- * (DataFee + TxFee + Creation Fee).
123
+ * Returns the raw `Buffer[]` (Transaction) or `Buffer` (typed transaction).
124
+ * This is the data which is found in the transactions of the block body.
125
+ *
126
+ * Note that if you want to use this function in a tx type independent way
127
+ * to then use the raw data output for tx instantiation with
128
+ * `Tx.fromValuesArray()` you should set the `asList` parameter to `true` -
129
+ * which is ignored on a legacy tx but provides the correct format on
130
+ * a typed tx.
131
+ *
132
+ * To prepare a tx to be added as block data with `Block.fromValuesArray()`
133
+ * just use the plain `raw()` method.
117
134
*/
118
- validate ( ) : boolean
119
- /* eslint-disable-next-line no-dupe-class-members */
120
- validate ( stringError : false ) : boolean
121
- /* eslint-disable-next-line no-dupe-class-members */
122
- validate ( stringError : true ) : string [ ]
123
- /* eslint-disable-next-line no-dupe-class-members */
124
- validate ( stringError : boolean = false ) : boolean | string [ ] {
125
- const errors = [ ]
126
-
127
- if ( this . getBaseFee ( ) . gt ( this . gasLimit ) ) {
128
- errors . push ( `gasLimit is too low. given ${ this . gasLimit } , need at least ${ this . getBaseFee ( ) } ` )
129
- }
130
-
131
- if ( this . isSigned ( ) && ! this . verifySignature ( ) ) {
132
- errors . push ( 'Invalid Signature' )
133
- }
134
-
135
- return stringError ? errors : errors . length === 0
136
- }
135
+ abstract raw ( asList : boolean ) : Buffer [ ] | Buffer
137
136
138
137
/**
139
138
* Returns the encoding of the transaction.
140
139
*/
141
140
abstract serialize ( ) : Buffer
142
141
143
142
/**
144
- * Returns an object with the JSON representation of the transaction
143
+ * Computes a sha3-256 hash of the serialized unsigned tx, which is used to sign the transaction.
145
144
*/
146
- abstract toJSON ( ) : JsonTx
145
+ abstract getMessageToSign ( ) : Buffer
146
+
147
+ abstract hash ( ) : Buffer
148
+
149
+ abstract getMessageToVerifySignature ( ) : Buffer
147
150
148
- abstract isSigned ( ) : boolean
151
+ public isSigned ( ) : boolean {
152
+ const { v, r, s } = this
153
+ return ! ! v && ! ! r && ! ! s
154
+ }
149
155
150
156
/**
151
157
* Determines if the signature is valid
@@ -160,22 +166,21 @@ export abstract class BaseTransaction<TransactionObject> {
160
166
}
161
167
}
162
168
163
- /**
164
- * Returns the raw `Buffer[]` (Transaction) or `Buffer` (typed transaction).
165
- * This is the data which is found in the transactions of the block body.
166
- */
167
- abstract raw ( ) : Buffer [ ] | Buffer
168
- abstract hash ( ) : Buffer
169
-
170
- abstract getMessageToVerifySignature ( ) : Buffer
171
169
/**
172
170
* Returns the sender's address
173
171
*/
174
172
getSenderAddress ( ) : Address {
175
173
return new Address ( publicToAddress ( this . getSenderPublicKey ( ) ) )
176
174
}
175
+
176
+ /**
177
+ * Returns the public key of the sender
178
+ */
177
179
abstract getSenderPublicKey ( ) : Buffer
178
180
181
+ /**
182
+ * Signs a tx and returns a new signed tx object
183
+ */
179
184
sign ( privateKey : Buffer ) : TransactionObject {
180
185
if ( privateKey . length !== 32 ) {
181
186
throw new Error ( 'Private key must be 32 bytes in length.' )
@@ -187,9 +192,22 @@ export abstract class BaseTransaction<TransactionObject> {
187
192
/* eslint-disable-next-line prefer-const */
188
193
let { v, r, s } = ecsign ( msgHash , privateKey )
189
194
190
- return this . processSignature ( v , r , s )
195
+ return this . _processSignature ( v , r , s )
191
196
}
192
197
198
+ /**
199
+ * Returns an object with the JSON representation of the transaction
200
+ */
201
+ abstract toJSON ( ) : JsonTx
202
+
193
203
// Accept the v,r,s values from the `sign` method, and convert this into a TransactionObject
194
- protected abstract processSignature ( v : number , r : Buffer , s : Buffer ) : TransactionObject
204
+ protected abstract _processSignature ( v : number , r : Buffer , s : Buffer ) : TransactionObject
205
+
206
+ protected _validateExceedsMaxInteger ( validateCannotExceedMaxInteger : { [ key : string ] : BN } ) {
207
+ for ( const [ key , value ] of Object . entries ( validateCannotExceedMaxInteger ) ) {
208
+ if ( value && value . gt ( MAX_INTEGER ) ) {
209
+ throw new Error ( `${ key } cannot exceed MAX_INTEGER, given ${ value } ` )
210
+ }
211
+ }
212
+ }
195
213
}
0 commit comments