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 }