UNPKG

13.3 kBJavaScriptView Raw
1"use strict";
2var __assign = (this && this.__assign) || function () {
3 __assign = Object.assign || function(t) {
4 for (var s, i = 1, n = arguments.length; i < n; i++) {
5 s = arguments[i];
6 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7 t[p] = s[p];
8 }
9 return t;
10 };
11 return __assign.apply(this, arguments);
12};
13Object.defineProperty(exports, "__esModule", { value: true });
14var ethereumjs_util_1 = require("ethereumjs-util");
15var ethereumjs_common_1 = require("ethereumjs-common");
16var buffer_1 = require("buffer");
17// secp256k1n/2
18var N_DIV_2 = new ethereumjs_util_1.BN('7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0', 16);
19/**
20 * An Ethereum transaction.
21 */
22var Transaction = /** @class */ (function () {
23 /**
24 * Creates a new transaction from an object with its fields' values.
25 *
26 * @param data - A transaction can be initialized with its rlp representation, an array containing
27 * the value of its fields in order, or an object containing them by name.
28 *
29 * @param opts - The transaction's options, used to indicate the chain and hardfork the
30 * transactions belongs to.
31 *
32 * @note Transaction objects implement EIP155 by default. To disable it, use the constructor's
33 * second parameter to set a chain and hardfork before EIP155 activation (i.e. before Spurious
34 * Dragon.)
35 *
36 * @example
37 * ```js
38 * const txData = {
39 * nonce: '0x00',
40 * gasPrice: '0x09184e72a000',
41 * gasLimit: '0x2710',
42 * to: '0x0000000000000000000000000000000000000000',
43 * value: '0x00',
44 * data: '0x7f7465737432000000000000000000000000000000000000000000000000000000600057',
45 * v: '0x1c',
46 * r: '0x5e1d3a76fbf824220eafc8c79ad578ad2b67d01b0c2425eb1f1347e8f50882ab',
47 * s: '0x5bd428537f05f9830e93792f90ea6a3e2d1ee84952dd96edbae9f658f831ab13'
48 * };
49 * const tx = new Transaction(txData);
50 * ```
51 */
52 function Transaction(data, opts) {
53 if (data === void 0) { data = {}; }
54 if (opts === void 0) { opts = {}; }
55 // instantiate Common class instance based on passed options
56 if (opts.common) {
57 if (opts.chain || opts.hardfork) {
58 throw new Error('Instantiation with both opts.common, and opts.chain and opts.hardfork parameter not allowed!');
59 }
60 this._common = opts.common;
61 }
62 else {
63 var chain = opts.chain ? opts.chain : 'mainnet';
64 var hardfork = opts.hardfork ? opts.hardfork : 'petersburg';
65 this._common = new ethereumjs_common_1.default(chain, hardfork);
66 }
67 // Define Properties
68 var fields = [
69 {
70 name: 'nonce',
71 length: 32,
72 allowLess: true,
73 default: new buffer_1.Buffer([]),
74 },
75 {
76 name: 'gasPrice',
77 length: 32,
78 allowLess: true,
79 default: new buffer_1.Buffer([]),
80 },
81 {
82 name: 'gasLimit',
83 alias: 'gas',
84 length: 32,
85 allowLess: true,
86 default: new buffer_1.Buffer([]),
87 },
88 {
89 name: 'to',
90 allowZero: true,
91 length: 20,
92 default: new buffer_1.Buffer([]),
93 },
94 {
95 name: 'value',
96 length: 32,
97 allowLess: true,
98 default: new buffer_1.Buffer([]),
99 },
100 {
101 name: 'data',
102 alias: 'input',
103 allowZero: true,
104 default: new buffer_1.Buffer([]),
105 },
106 {
107 name: 'v',
108 allowZero: true,
109 default: new buffer_1.Buffer([]),
110 },
111 {
112 name: 'r',
113 length: 32,
114 allowZero: true,
115 allowLess: true,
116 default: new buffer_1.Buffer([]),
117 },
118 {
119 name: 's',
120 length: 32,
121 allowZero: true,
122 allowLess: true,
123 default: new buffer_1.Buffer([]),
124 },
125 ];
126 // attached serialize
127 ethereumjs_util_1.defineProperties(this, fields, data);
128 /**
129 * @property {Buffer} from (read only) sender address of this transaction, mathematically derived from other parameters.
130 * @name from
131 * @memberof Transaction
132 */
133 Object.defineProperty(this, 'from', {
134 enumerable: true,
135 configurable: true,
136 get: this.getSenderAddress.bind(this),
137 });
138 this._validateV(this.v);
139 this._overrideVSetterWithValidation();
140 }
141 /**
142 * If the tx's `to` is to the creation address
143 */
144 Transaction.prototype.toCreationAddress = function () {
145 return this.to.toString('hex') === '';
146 };
147 /**
148 * Computes a sha3-256 hash of the serialized tx
149 * @param includeSignature - Whether or not to include the signature
150 */
151 Transaction.prototype.hash = function (includeSignature) {
152 if (includeSignature === void 0) { includeSignature = true; }
153 var items;
154 if (includeSignature) {
155 items = this.raw;
156 }
157 else {
158 if (this._implementsEIP155()) {
159 items = this.raw.slice(0, 6).concat([
160 ethereumjs_util_1.toBuffer(this.getChainId()),
161 // TODO: stripping zeros should probably be a responsibility of the rlp module
162 ethereumjs_util_1.stripZeros(ethereumjs_util_1.toBuffer(0)),
163 ethereumjs_util_1.stripZeros(ethereumjs_util_1.toBuffer(0)),
164 ]);
165 }
166 else {
167 items = this.raw.slice(0, 6);
168 }
169 }
170 // create hash
171 return ethereumjs_util_1.rlphash(items);
172 };
173 /**
174 * returns chain ID
175 */
176 Transaction.prototype.getChainId = function () {
177 return this._common.chainId();
178 };
179 /**
180 * returns the sender's address
181 */
182 Transaction.prototype.getSenderAddress = function () {
183 if (this._from) {
184 return this._from;
185 }
186 var pubkey = this.getSenderPublicKey();
187 this._from = ethereumjs_util_1.publicToAddress(pubkey);
188 return this._from;
189 };
190 /**
191 * returns the public key of the sender
192 */
193 Transaction.prototype.getSenderPublicKey = function () {
194 if (!this.verifySignature()) {
195 throw new Error('Invalid Signature');
196 }
197 // If the signature was verified successfully the _senderPubKey field is defined
198 return this._senderPubKey;
199 };
200 /**
201 * Determines if the signature is valid
202 */
203 Transaction.prototype.verifySignature = function () {
204 var msgHash = this.hash(false);
205 // All transaction signatures whose s-value is greater than secp256k1n/2 are considered invalid.
206 if (this._common.gteHardfork('homestead') && new ethereumjs_util_1.BN(this.s).cmp(N_DIV_2) === 1) {
207 return false;
208 }
209 try {
210 var v = ethereumjs_util_1.bufferToInt(this.v);
211 var useChainIdWhileRecoveringPubKey = v >= this.getChainId() * 2 + 35 && this._common.gteHardfork('spuriousDragon');
212 this._senderPubKey = ethereumjs_util_1.ecrecover(msgHash, v, this.r, this.s, useChainIdWhileRecoveringPubKey ? this.getChainId() : undefined);
213 }
214 catch (e) {
215 return false;
216 }
217 return !!this._senderPubKey;
218 };
219 /**
220 * sign a transaction with a given private key
221 * @param privateKey - Must be 32 bytes in length
222 */
223 Transaction.prototype.sign = function (privateKey) {
224 // We clear any previous signature before signing it. Otherwise, _implementsEIP155's can give
225 // different results if this tx was already signed.
226 this.v = new buffer_1.Buffer([]);
227 this.s = new buffer_1.Buffer([]);
228 this.r = new buffer_1.Buffer([]);
229 var msgHash = this.hash(false);
230 var sig = ethereumjs_util_1.ecsign(msgHash, privateKey);
231 if (this._implementsEIP155()) {
232 sig.v += this.getChainId() * 2 + 8;
233 }
234 Object.assign(this, sig);
235 };
236 /**
237 * The amount of gas paid for the data in this tx
238 */
239 Transaction.prototype.getDataFee = function () {
240 var data = this.raw[5];
241 var cost = new ethereumjs_util_1.BN(0);
242 for (var i = 0; i < data.length; i++) {
243 data[i] === 0
244 ? cost.iaddn(this._common.param('gasPrices', 'txDataZero'))
245 : cost.iaddn(this._common.param('gasPrices', 'txDataNonZero'));
246 }
247 return cost;
248 };
249 /**
250 * the minimum amount of gas the tx must have (DataFee + TxFee + Creation Fee)
251 */
252 Transaction.prototype.getBaseFee = function () {
253 var fee = this.getDataFee().iaddn(this._common.param('gasPrices', 'tx'));
254 if (this._common.gteHardfork('homestead') && this.toCreationAddress()) {
255 fee.iaddn(this._common.param('gasPrices', 'txCreation'));
256 }
257 return fee;
258 };
259 /**
260 * the up front amount that an account must have for this transaction to be valid
261 */
262 Transaction.prototype.getUpfrontCost = function () {
263 return new ethereumjs_util_1.BN(this.gasLimit).imul(new ethereumjs_util_1.BN(this.gasPrice)).iadd(new ethereumjs_util_1.BN(this.value));
264 };
265 Transaction.prototype.validate = function (stringError) {
266 if (stringError === void 0) { stringError = false; }
267 var errors = [];
268 if (!this.verifySignature()) {
269 errors.push('Invalid Signature');
270 }
271 if (this.getBaseFee().cmp(new ethereumjs_util_1.BN(this.gasLimit)) > 0) {
272 errors.push(["gas limit is too low. Need at least " + this.getBaseFee()]);
273 }
274 if (stringError === false) {
275 return errors.length === 0;
276 }
277 else {
278 return errors.join(' ');
279 }
280 };
281 /**
282 * Returns the rlp encoding of the transaction
283 */
284 Transaction.prototype.serialize = function () {
285 // Note: This never gets executed, defineProperties overwrites it.
286 return ethereumjs_util_1.rlp.encode(this.raw);
287 };
288 /**
289 * Returns the transaction in JSON format
290 * @see {@link https://github.com/ethereumjs/ethereumjs-util/blob/master/docs/index.md#defineproperties|ethereumjs-util}
291 */
292 Transaction.prototype.toJSON = function (labels) {
293 if (labels === void 0) { labels = false; }
294 // Note: This never gets executed, defineProperties overwrites it.
295 return {};
296 };
297 Transaction.prototype._validateV = function (v) {
298 if (v === undefined || v.length === 0) {
299 return;
300 }
301 if (!this._common.gteHardfork('spuriousDragon')) {
302 return;
303 }
304 var vInt = ethereumjs_util_1.bufferToInt(v);
305 if (vInt === 27 || vInt === 28) {
306 return;
307 }
308 var isValidEIP155V = vInt === this.getChainId() * 2 + 35 || vInt === this.getChainId() * 2 + 36;
309 if (!isValidEIP155V) {
310 throw new Error("Incompatible EIP155-based V " + vInt + " and chain id " + this.getChainId() + ". See the second parameter of the Transaction constructor to set the chain id.");
311 }
312 };
313 Transaction.prototype._isSigned = function () {
314 return this.v.length > 0 && this.r.length > 0 && this.s.length > 0;
315 };
316 Transaction.prototype._overrideVSetterWithValidation = function () {
317 var _this = this;
318 var vDescriptor = Object.getOwnPropertyDescriptor(this, 'v');
319 Object.defineProperty(this, 'v', __assign({}, vDescriptor, { set: function (v) {
320 if (v !== undefined) {
321 _this._validateV(ethereumjs_util_1.toBuffer(v));
322 }
323 vDescriptor.set(v);
324 } }));
325 };
326 Transaction.prototype._implementsEIP155 = function () {
327 var onEIP155BlockOrLater = this._common.gteHardfork('spuriousDragon');
328 if (!this._isSigned()) {
329 // We sign with EIP155 all unsigned transactions after spuriousDragon
330 return onEIP155BlockOrLater;
331 }
332 // EIP155 spec:
333 // If block.number >= 2,675,000 and v = CHAIN_ID * 2 + 35 or v = CHAIN_ID * 2 + 36, then when computing
334 // the hash of a transaction for purposes of signing or recovering, instead of hashing only the first six
335 // elements (i.e. nonce, gasprice, startgas, to, value, data), hash nine elements, with v replaced by
336 // CHAIN_ID, r = 0 and s = 0.
337 var v = ethereumjs_util_1.bufferToInt(this.v);
338 var vAndChainIdMeetEIP155Conditions = v === this.getChainId() * 2 + 35 || v === this.getChainId() * 2 + 36;
339 return vAndChainIdMeetEIP155Conditions && onEIP155BlockOrLater;
340 };
341 return Transaction;
342}());
343exports.default = Transaction;
344//# sourceMappingURL=transaction.js.map
\No newline at end of file