Coverage Summary for Class: Transaction (org.ethereum.core)
Class |
Method, %
|
Line, %
|
Transaction |
81.6%
(40/49)
|
75%
(159/212)
|
Transaction$MockitoMock$1327637120 |
Transaction$MockitoMock$1327637120$auxiliary$0IbzBe72 |
Transaction$MockitoMock$1327637120$auxiliary$2UvoH7Yj |
Transaction$MockitoMock$1327637120$auxiliary$aDBoJSCb |
Transaction$MockitoMock$1327637120$auxiliary$bFphU5dT |
Transaction$MockitoMock$1327637120$auxiliary$C9N0br7u |
Transaction$MockitoMock$1327637120$auxiliary$D40lkm8R |
Transaction$MockitoMock$1327637120$auxiliary$f6XTEap4 |
Transaction$MockitoMock$1327637120$auxiliary$fRFc2v0J |
Transaction$MockitoMock$1327637120$auxiliary$GoNGKV4b |
Transaction$MockitoMock$1327637120$auxiliary$hsJCpDBO |
Transaction$MockitoMock$1327637120$auxiliary$JCzHKxVt |
Transaction$MockitoMock$1327637120$auxiliary$JZnWKQwE |
Transaction$MockitoMock$1327637120$auxiliary$K5Q4e8zk |
Transaction$MockitoMock$1327637120$auxiliary$k5vTpW7a |
Transaction$MockitoMock$1327637120$auxiliary$KBd7rdnt |
Transaction$MockitoMock$1327637120$auxiliary$MCcVhMve |
Transaction$MockitoMock$1327637120$auxiliary$mw7fZV2k |
Transaction$MockitoMock$1327637120$auxiliary$P8m4ITfJ |
Transaction$MockitoMock$1327637120$auxiliary$PHrp7Tx6 |
Transaction$MockitoMock$1327637120$auxiliary$pM1tZ3Ul |
Transaction$MockitoMock$1327637120$auxiliary$qKUOE7eP |
Transaction$MockitoMock$1327637120$auxiliary$R7xS3iW3 |
Transaction$MockitoMock$1327637120$auxiliary$TK9Y7Ehs |
Transaction$MockitoMock$1327637120$auxiliary$TRac2qMH |
Transaction$MockitoMock$1327637120$auxiliary$uafDxUYt |
Transaction$MockitoMock$1327637120$auxiliary$uzEJvsZl |
Transaction$MockitoMock$1327637120$auxiliary$vCuvTDSl |
Transaction$MockitoMock$1327637120$auxiliary$vw4dr732 |
Transaction$MockitoMock$1327637120$auxiliary$X7C164WH |
Transaction$MockitoMock$1327637120$auxiliary$Y2bPmYa9 |
Transaction$MockitoMock$1327637120$auxiliary$yFvmLi79 |
Transaction$MockitoMock$1327637120$auxiliary$yP6yjEdA |
Total |
81.6%
(40/49)
|
75%
(159/212)
|
1 /*
2 * This file is part of RskJ
3 * Copyright (C) 2017 RSK Labs Ltd.
4 * (derived from ethereumJ library, Copyright (c) 2016 <ether.camp>)
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package org.ethereum.core;
21
22 import co.rsk.core.Coin;
23 import co.rsk.core.RskAddress;
24 import co.rsk.crypto.Keccak256;
25 import co.rsk.metrics.profilers.Metric;
26 import co.rsk.metrics.profilers.Profiler;
27 import co.rsk.metrics.profilers.ProfilerFactory;
28 import co.rsk.panic.PanicProcessor;
29 import co.rsk.peg.BridgeUtils;
30 import co.rsk.util.ListArrayUtil;
31 import org.bouncycastle.util.BigIntegers;
32 import org.ethereum.config.Constants;
33 import org.ethereum.config.blockchain.upgrades.ActivationConfig;
34 import org.ethereum.crypto.ECKey;
35 import org.ethereum.crypto.ECKey.MissingPrivateKeyException;
36 import org.ethereum.crypto.HashUtil;
37 import org.ethereum.crypto.signature.ECDSASignature;
38 import org.ethereum.crypto.signature.Secp256k1;
39 import org.ethereum.util.ByteUtil;
40 import org.ethereum.util.RLP;
41 import org.ethereum.util.RLPList;
42 import org.ethereum.vm.GasCost;
43 import org.ethereum.vm.PrecompiledContracts;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import javax.annotation.Nullable;
48 import java.math.BigInteger;
49 import java.security.SignatureException;
50 import java.util.Objects;
51
52 import static org.ethereum.util.ByteUtil.EMPTY_BYTE_ARRAY;
53
54 /**
55 * A transaction (formally, T) is a single cryptographically
56 * signed instruction sent by an actor external to Ethereum.
57 * An external actor can be a person (via a mobile device or desktop computer)
58 * or could be from a piece of automated software running on a server.
59 * There are two types of transactions: those which result in message calls
60 * and those which result in the creation of new contracts.
61 */
62 public class Transaction {
63 public static final int DATAWORD_LENGTH = 32;
64 private static final byte[] ZERO_BYTE_ARRAY = new byte[]{0};
65 private static final Logger logger = LoggerFactory.getLogger(Transaction.class);
66 private static final Profiler profiler = ProfilerFactory.getInstance();
67 private static final PanicProcessor panicProcessor = new PanicProcessor();
68 private static final BigInteger SECP256K1N_HALF = Constants.getSECP256K1N().divide(BigInteger.valueOf(2));
69 /**
70 * Since EIP-155, we could encode chainId in V
71 */
72 public static final byte CHAIN_ID_INC = 35;
73 public static final byte LOWER_REAL_V = 27;
74
75 protected RskAddress sender;
76 /* whether this is a local call transaction */
77 private boolean isLocalCall;
78 /* a counter used to make sure each transaction can only be processed once */
79 private final byte[] nonce;
80 private final Coin value;
81 /* the address of the destination account
82 * In creation transaction the receive address is - 0 */
83 private final RskAddress receiveAddress;
84 private final Coin gasPrice;
85 /* the amount of "gas" to allow for the computation.
86 * Gas is the fuel of the computational engine.
87 * Every computational step taken and every byte added
88 * to the state or transaction list consumes some gas. */
89 private final byte[] gasLimit;
90 /* An unlimited size byte array specifying
91 * input [data] of the message call or
92 * Initialization code for a new contract */
93 private final byte[] data;
94 private final byte chainId;
95 /* the elliptic curve signature
96 * (including public key recovery bits) */
97 private ECDSASignature signature;
98 private byte[] rlpEncoding;
99 private byte[] rawRlpEncoding;
100 private Keccak256 hash;
101 private Keccak256 rawHash;
102
103 protected Transaction(byte[] rawData) {
104 this(RLP.decodeList(rawData));
105 }
106
107 protected Transaction(RLPList transaction) {
108 if (transaction.size() != 9) {
109 throw new IllegalArgumentException("A transaction must have exactly 9 elements");
110 }
111 this.nonce = transaction.get(0).getRLPData();
112 this.gasPrice = RLP.parseCoinNonNullZero(transaction.get(1).getRLPData());
113 this.gasLimit = transaction.get(2).getRLPData();
114 this.receiveAddress = RLP.parseRskAddress(transaction.get(3).getRLPData());
115 this.value = RLP.parseCoinNullZero(transaction.get(4).getRLPData());
116 this.data = transaction.get(5).getRLPData();
117 // only parse signature in case tx is signed
118 byte[] vData = transaction.get(6).getRLPData();
119 if (vData != null) {
120 if (vData.length != 1) {
121 throw new TransactionException("Signature V is invalid");
122 }
123 byte v = vData[0];
124 this.chainId = extractChainIdFromV(v);
125 byte[] r = transaction.get(7).getRLPData();
126 byte[] s = transaction.get(8).getRLPData();
127 this.signature = ECDSASignature.fromComponents(r, s, getRealV(v));
128 } else {
129 this.chainId = 0;
130 logger.trace("RLP encoded tx is not signed!");
131 }
132 }
133
134 /* creation contract tx
135 * [ nonce, gasPrice, gasLimit, "", endowment, init, signature(v, r, s) ]
136 * or simple send tx
137 * [ nonce, gasPrice, gasLimit, receiveAddress, value, data, signature(v, r, s) ]
138 */
139 protected Transaction(byte[] nonce, byte[] gasPriceRaw, byte[] gasLimit, byte[] receiveAddress, byte[] value, byte[] data) {
140 this(nonce, gasPriceRaw, gasLimit, receiveAddress, value, data, (byte) 0);
141 }
142
143 protected Transaction(byte[] nonce, byte[] gasPriceRaw, byte[] gasLimit, byte[] receiveAddress, byte[] valueRaw, byte[] data,
144 byte chainId) {
145 this(
146 nonce,
147 RLP.parseCoinNonNullZero(ByteUtil.cloneBytes(gasPriceRaw)),
148 gasLimit,
149 RLP.parseRskAddress(ByteUtil.cloneBytes(receiveAddress)),
150 RLP.parseCoinNullZero(ByteUtil.cloneBytes(valueRaw)),
151 data,
152 chainId,
153 false
154 );
155 }
156
157 protected Transaction(byte[] nonce, Coin gasPriceRaw, byte[] gasLimit, RskAddress receiveAddress, Coin valueRaw, byte[] data,
158 byte chainId, final boolean localCall) {
159 this.nonce = ByteUtil.cloneBytes(nonce);
160 this.gasPrice = gasPriceRaw;
161 this.gasLimit = ByteUtil.cloneBytes(gasLimit);
162 this.receiveAddress = receiveAddress;
163 this.value = valueRaw;
164 this.data = ByteUtil.cloneBytes(data);
165 this.chainId = chainId;
166 this.isLocalCall = localCall;
167 }
168
169 public static TransactionBuilder builder() {
170 return new TransactionBuilder();
171 }
172
173 public Transaction toImmutableTransaction() {
174 return new ImmutableTransaction(this.getEncoded());
175 }
176
177 private byte extractChainIdFromV(byte v) {
178 if (v == LOWER_REAL_V || v == (LOWER_REAL_V + 1)) {
179 return 0;
180 }
181 return (byte) (((0x00FF & v) - CHAIN_ID_INC) / 2);
182 }
183
184 private byte getRealV(byte v) {
185 if (v == LOWER_REAL_V || v == (LOWER_REAL_V + 1)) {
186 return v;
187 }
188 byte realV = LOWER_REAL_V;
189 int inc = 0;
190 if ((int) v % 2 == 0) {
191 inc = 1;
192 }
193 return (byte) (realV + inc);
194 }
195
196 // There was a method called NEW_getTransactionCost that implemented this alternative solution:
197 // "return (this.isContractCreation() ? GasCost.TRANSACTION_CREATE_CONTRACT : GasCost.TRANSACTION)
198 // + zeroVals * GasCost.TX_ZERO_DATA + nonZeroes * GasCost.TX_NO_ZERO_DATA;"
199 public long transactionCost(Constants constants, ActivationConfig.ForBlock activations) {
200 // Federators txs to the bridge are free during system setup
201 if (BridgeUtils.isFreeBridgeTx(this, constants, activations)) {
202 return 0;
203 }
204
205 long nonZeroes = this.nonZeroDataBytes();
206 long zeroVals = ListArrayUtil.getLength(this.getData()) - nonZeroes;
207
208 return (this.isContractCreation() ? GasCost.TRANSACTION_CREATE_CONTRACT : GasCost.TRANSACTION) + zeroVals * GasCost.TX_ZERO_DATA + nonZeroes * GasCost.TX_NO_ZERO_DATA;
209 }
210
211 public void verify() {
212 validate();
213 }
214
215 private void validate() {
216 if (getNonce().length > DATAWORD_LENGTH) {
217 throw new RuntimeException("Nonce is not valid");
218 }
219 if (receiveAddress != null && receiveAddress.getBytes().length != 0 && receiveAddress.getBytes().length != Constants.getMaxAddressByteLength()) {
220 throw new RuntimeException("Receive address is not valid");
221 }
222 if (gasLimit.length > DATAWORD_LENGTH) {
223 throw new RuntimeException("Gas Limit is not valid");
224 }
225 if (gasPrice != null && gasPrice.getBytes().length > DATAWORD_LENGTH) {
226 throw new RuntimeException("Gas Price is not valid");
227 }
228 if (value.getBytes().length > DATAWORD_LENGTH) {
229 throw new RuntimeException("Value is not valid");
230 }
231 if (getSignature() != null) {
232 if (BigIntegers.asUnsignedByteArray(signature.getR()).length > DATAWORD_LENGTH) {
233 throw new RuntimeException("Signature R is not valid");
234 }
235 if (BigIntegers.asUnsignedByteArray(signature.getS()).length > DATAWORD_LENGTH) {
236 throw new RuntimeException("Signature S is not valid");
237 }
238 if (getSender().getBytes() != null && getSender().getBytes().length != Constants.getMaxAddressByteLength()) {
239 throw new RuntimeException("Sender is not valid");
240 }
241 }
242 }
243
244 public Keccak256 getHash() {
245 if (hash == null) {
246 byte[] plainMsg = this.getEncoded();
247 this.hash = new Keccak256(HashUtil.keccak256(plainMsg));
248 }
249
250 return this.hash;
251 }
252
253 public Keccak256 getRawHash() {
254 if (rawHash == null) {
255 byte[] plainMsg = this.getEncodedRaw();
256 this.rawHash = new Keccak256(HashUtil.keccak256(plainMsg));
257 }
258
259 return this.rawHash;
260 }
261
262 public byte[] getNonce() {
263 return nullToZeroArray(nonce);
264 }
265
266 public Coin getValue() {
267 return value;
268 }
269
270 public RskAddress getReceiveAddress() {
271 return receiveAddress;
272 }
273
274 public Coin getGasPrice() {
275 // some blocks have zero encoded as null, but if we altered the internal field then re-encoding the value would
276 // give a different value than the original.
277 if (gasPrice == null) {
278 return Coin.ZERO;
279 }
280
281 return gasPrice;
282 }
283
284 public byte[] getGasLimit() {
285 return gasLimit;
286 }
287
288 public byte[] getData() {
289 return data;
290 }
291
292 public ECDSASignature getSignature() {
293 return signature;
294 }
295
296 public boolean acceptTransactionSignature(byte currentChainId) {
297 ECDSASignature signature = getSignature();
298 if (signature == null || !signature.validateComponents() || signature.getS().compareTo(SECP256K1N_HALF) >= 0) {
299 return false;
300 }
301
302 return this.getChainId() == 0 || this.getChainId() == currentChainId;
303 }
304
305 public void sign(byte[] privKeyBytes) throws MissingPrivateKeyException {
306 byte[] raw = this.getRawHash().getBytes();
307 ECKey key = ECKey.fromPrivate(privKeyBytes).decompress();
308 this.signature = ECDSASignature.fromSignature(key.sign(raw));
309 this.rlpEncoding = null;
310 this.hash = null;
311 this.sender = null;
312 }
313
314 public void setSignature(ECDSASignature signature) {
315 this.signature = signature;
316 this.rlpEncoding = null;
317 this.hash = null;
318 this.sender = null;
319 }
320
321 /**
322 * Only for compatibility until we could finally remove old {@link org.ethereum.crypto.ECKey.ECDSASignature}.
323 *
324 * @param signature
325 * @return
326 */
327 public void setSignature(ECKey.ECDSASignature signature) {
328 this.signature = ECDSASignature.fromSignature(signature);
329 this.rlpEncoding = null;
330 this.hash = null;
331 this.sender = null;
332 }
333
334 @Nullable
335 public RskAddress getContractAddress() {
336 if (!isContractCreation()) {
337 return null;
338 }
339
340 return new RskAddress(HashUtil.calcNewAddr(this.getSender().getBytes(), this.getNonce()));
341 }
342
343 public boolean isContractCreation() {
344 return this.receiveAddress.equals(RskAddress.nullAddress());
345 }
346
347 private long nonZeroDataBytes() {
348 if (data == null) {
349 return 0;
350 }
351
352 int counter = 0;
353 for (final byte aData : data) {
354 if (aData != 0) {
355 ++counter;
356 }
357 }
358 return counter;
359 }
360
361 /*
362 * Crypto
363 */
364
365 public ECKey getKey() {
366 Metric metric = profiler.start(Profiler.PROFILING_TYPE.KEY_RECOV_FROM_SIG);
367 byte[] raw = getRawHash().getBytes();
368 //We clear the 4th bit, the compress bit, in case a signature is using compress in true
369 ECKey key = Secp256k1.getInstance().recoverFromSignature((signature.getV() - 27) & ~4, signature, raw, true);
370 profiler.stop(metric);
371 return key;
372 }
373
374 public synchronized RskAddress getSender() {
375 if (sender != null) {
376 return sender;
377 }
378
379
380 Metric metric = profiler.start(Profiler.PROFILING_TYPE.KEY_RECOV_FROM_SIG);
381 try {
382 ECKey key = Secp256k1.getInstance().signatureToKey(getRawHash().getBytes(), getSignature());
383 sender = new RskAddress(key.getAddress());
384 } catch (SignatureException e) {
385 logger.error(e.getMessage(), e);
386 panicProcessor.panic("transaction", e.getMessage());
387 sender = RskAddress.nullAddress();
388 } finally {
389 profiler.stop(metric);
390 }
391
392 return sender;
393 }
394
395 public synchronized RskAddress getSender(SignatureCache signatureCache) {
396 if (sender != null) {
397 return sender;
398 }
399
400 sender = signatureCache.getSender(this);
401
402 return sender;
403 }
404
405 public byte getChainId() {
406 return chainId;
407 }
408
409 public byte getEncodedV() {
410 return this.chainId == 0
411 ? this.signature.getV()
412 : (byte)(this.signature.getV() - LOWER_REAL_V + CHAIN_ID_INC + this.chainId * 2);
413 }
414
415 @Override
416 public String toString() {
417 return "TransactionData [" + "hash=" + ByteUtil.toHexStringOrEmpty(getHash().getBytes()) +
418 " nonce=" + ByteUtil.toHexStringOrEmpty(nonce) +
419 ", gasPrice=" + gasPrice +
420 ", gas=" + ByteUtil.toHexStringOrEmpty(gasLimit) +
421 ", receiveAddress=" + receiveAddress +
422 ", value=" + value +
423 ", data=" + ByteUtil.toHexStringOrEmpty(data) +
424 ", signatureV=" + (signature == null ? "" : signature.getV()) +
425 ", signatureR=" + (signature == null ? "" : ByteUtil.toHexStringOrEmpty(BigIntegers.asUnsignedByteArray(signature.getR()))) +
426 ", signatureS=" + (signature == null ? "" : ByteUtil.toHexStringOrEmpty(BigIntegers.asUnsignedByteArray(signature.getS()))) +
427 "]";
428
429 }
430
431 /**
432 * For signatures you have to keep also
433 * RLP of the transaction without any signature data
434 */
435 public byte[] getEncodedRaw() {
436 if (this.rawRlpEncoding == null) {
437 // Since EIP-155 use chainId for v
438 if (chainId == 0) {
439 this.rawRlpEncoding = encode(null, null, null);
440 } else {
441 byte[] v = RLP.encodeByte(chainId);
442 byte[] r = RLP.encodeElement(EMPTY_BYTE_ARRAY);
443 byte[] s = RLP.encodeElement(EMPTY_BYTE_ARRAY);
444
445 this.rawRlpEncoding = encode(v, r, s);
446 }
447 }
448
449 return ByteUtil.cloneBytes(this.rawRlpEncoding);
450 }
451
452 public byte[] getEncoded() {
453 if (this.rlpEncoding == null) {
454 byte[] v;
455 byte[] r;
456 byte[] s;
457
458 if (this.signature != null) {
459 v = RLP.encodeByte((byte) (chainId == 0 ? signature.getV() : (signature.getV() - LOWER_REAL_V) + (chainId * 2 + CHAIN_ID_INC)));
460 r = RLP.encodeElement(BigIntegers.asUnsignedByteArray(signature.getR()));
461 s = RLP.encodeElement(BigIntegers.asUnsignedByteArray(signature.getS()));
462 } else {
463 v = chainId == 0 ? RLP.encodeElement(EMPTY_BYTE_ARRAY) : RLP.encodeByte(chainId);
464 r = RLP.encodeElement(EMPTY_BYTE_ARRAY);
465 s = RLP.encodeElement(EMPTY_BYTE_ARRAY);
466 }
467
468 this.rlpEncoding = encode(v, r, s);
469 }
470
471 return ByteUtil.cloneBytes(this.rlpEncoding);
472 }
473
474 private byte[] encode(byte[] v, byte[] r, byte[] s) {
475 // parse null as 0 for nonce
476 byte[] toEncodeNonce;
477 if (this.nonce == null || this.nonce.length == 1 && this.nonce[0] == 0) {
478 toEncodeNonce = RLP.encodeElement(null);
479 } else {
480 toEncodeNonce = RLP.encodeElement(this.nonce);
481 }
482 byte[] toEncodeGasPrice = RLP.encodeCoinNonNullZero(this.gasPrice);
483 byte[] toEncodeGasLimit = RLP.encodeElement(this.gasLimit);
484 byte[] toEncodeReceiveAddress = RLP.encodeRskAddress(this.receiveAddress);
485 byte[] toEncodeValue = RLP.encodeCoinNullZero(this.value);
486 byte[] toEncodeData = RLP.encodeElement(this.data);
487
488 if (v == null && r == null && s == null) {
489 return RLP.encodeList(toEncodeNonce, toEncodeGasPrice, toEncodeGasLimit,
490 toEncodeReceiveAddress, toEncodeValue, toEncodeData);
491 }
492
493 return RLP.encodeList(toEncodeNonce, toEncodeGasPrice, toEncodeGasLimit,
494 toEncodeReceiveAddress, toEncodeValue, toEncodeData, v, r, s);
495 }
496
497 public BigInteger getGasLimitAsInteger() {
498 return (this.getGasLimit() == null) ? null : BigIntegers.fromUnsignedByteArray(this.getGasLimit());
499 }
500
501 public BigInteger getNonceAsInteger() {
502 return (this.getNonce() == null) ? null : BigIntegers.fromUnsignedByteArray(this.getNonce());
503 }
504
505 @Override
506 public int hashCode() {
507 return this.getHash().hashCode();
508 }
509
510 @Override
511 public boolean equals(Object obj) {
512
513 if (!(obj instanceof Transaction)) {
514 return false;
515 }
516
517 Transaction tx = (Transaction) obj;
518
519 return Objects.equals(this.getHash(), tx.getHash());
520 }
521
522
523 private byte[] nullToZeroArray(byte[] data) {
524 return data == null ? ZERO_BYTE_ARRAY : data;
525 }
526
527 public boolean isLocalCallTransaction() {
528 return isLocalCall;
529 }
530
531 public void setLocalCallTransaction(boolean isLocalCall) {
532 this.isLocalCall = isLocalCall;
533 }
534
535 public boolean isRemascTransaction(int txPosition, int txsSize) {
536 return isLastTx(txPosition, txsSize) && checkRemascAddress() && checkRemascTxZeroValues();
537 }
538
539 private boolean isLastTx(int txPosition, int txsSize) {
540 return txPosition == (txsSize - 1);
541 }
542
543 private boolean checkRemascAddress() {
544 return PrecompiledContracts.REMASC_ADDR.equals(getReceiveAddress());
545 }
546
547 private boolean checkRemascTxZeroValues() {
548 if (null != getData() || null != getSignature()) {
549 return false;
550 }
551
552 return Coin.ZERO.equals(getValue()) &&
553 BigInteger.ZERO.equals(new BigInteger(1, getGasLimit())) &&
554 Coin.ZERO.equals(getGasPrice());
555 }
556 }