blob: b79f9be2502f256dffc474b52437a32a8853a0c6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE
* file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
* to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.apache.tuweni.eth;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.rlp.RLP;
import org.apache.tuweni.rlp.RLPReader;
import org.apache.tuweni.rlp.RLPWriter;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nullable;
import com.google.common.base.MoreObjects;
/**
* A transaction receipt, containing information pertaining a transaction execution.
*
* <p>
* Transaction receipts have two different formats: state root-encoded and status-encoded. The difference between these
* two formats is that the state root-encoded transaction receipt contains the state root for world state after the
* transaction has been processed (e.g. not invalid) and the status-encoded transaction receipt instead has contains the
* status of the transaction (e.g. 1 for success and 0 for failure). The other transaction receipt fields are the same
* for both formats: logs, logs bloom, and cumulative gas used in the block. The TransactionReceiptType attribute is the
* best way to check which format has been used.
*/
public final class TransactionReceipt {
/**
* Read a transaction receipt from its RLP serialized representation
*
* @param bytes the bytes of the serialized transaction receipt
* @return a transaction receipt
*/
public static TransactionReceipt fromBytes(Bytes bytes) {
return RLP.decode(bytes, TransactionReceipt::readFrom);
}
/**
* Creates a transaction receipt for the given RLP
*
* @param reader the RLP-encoded transaction receipt
* @return the transaction receipt
*/
public static TransactionReceipt readFrom(final RLPReader reader) {
return reader.readList(input -> {
Bytes statusOrRootState = input.readValue();
long cumulativeGas = input.readLong();
LogsBloomFilter bloomFilter = new LogsBloomFilter(input.readValue());
List<Log> logs = input.readListContents(Log::readFrom);
if (statusOrRootState.size() == 32) {
return new TransactionReceipt(Bytes32.wrap(statusOrRootState), cumulativeGas, bloomFilter, logs);
} else {
int status = statusOrRootState.toInt();
return new TransactionReceipt(status, cumulativeGas, bloomFilter, logs);
}
});
}
private final Bytes32 stateRoot;
private final long cumulativeGasUsed;
private final List<Log> logs;
private final LogsBloomFilter bloomFilter;
private final Integer status;
/**
* Creates an instance of a state root-encoded transaction receipt.
*
* @param stateRoot the state root for the world state after the transaction has been processed
* @param cumulativeGasUsed the total amount of gas consumed in the block after this transaction
* @param bloomFilter the bloom filter of the logs
* @param logs the logs generated within the transaction
*/
public TransactionReceipt(Bytes32 stateRoot, long cumulativeGasUsed, LogsBloomFilter bloomFilter, List<Log> logs) {
this(stateRoot, null, cumulativeGasUsed, bloomFilter, logs);
}
/**
* Creates an instance of a status-encoded transaction receipt.
*
* @param status the status code for the transaction (1 for success and 0 for failure)
* @param cumulativeGasUsed the total amount of gas consumed in the block after this transaction
* @param bloomFilter the bloom filter of the logs
* @param logs the logs generated within the transaction
*/
public TransactionReceipt(int status, long cumulativeGasUsed, LogsBloomFilter bloomFilter, List<Log> logs) {
this(null, status, cumulativeGasUsed, bloomFilter, logs);
}
private TransactionReceipt(
@Nullable Bytes32 stateRoot,
@Nullable Integer status,
long cumulativeGasUsed,
LogsBloomFilter bloomFilter,
List<Log> logs) {
this.stateRoot = stateRoot;
this.cumulativeGasUsed = cumulativeGasUsed;
this.status = status;
this.logs = logs;
this.bloomFilter = bloomFilter;
}
/**
* Writes the transaction receipt into a serialized RLP form.
*
* @return the transaction receipt encoded as a set of bytes using the RLP serialization
*/
public Bytes toBytes() {
return RLP.encode(this::writeTo);
}
/**
* Write an RLP representation.
*
* @param writer The RLP output to write to
*/
public void writeTo(final RLPWriter writer) {
writer.writeList(out -> {
// Determine whether it's a state root-encoded transaction receipt
// or is a status code-encoded transaction receipt.
if (stateRoot != null) {
out.writeValue(stateRoot);
} else {
out.writeLong(status);
}
out.writeLong(cumulativeGasUsed);
out.writeValue(bloomFilter.toBytes());
out.writeList(logs, (logWriter, log) -> log.writeTo(logWriter));
});
}
/**
* Returns the state root for a state root-encoded transaction receipt
*
* @return the state root if the transaction receipt is state root-encoded; otherwise {@code null}
*/
public Bytes32 stateRoot() {
return stateRoot;
}
/**
* Returns the total amount of gas consumed in the block after the transaction has been processed.
*
* @return the total amount of gas consumed in the block after the transaction has been processed
*/
public long cumulativeGasUsed() {
return cumulativeGasUsed;
}
/**
* Returns the logs generated by the transaction.
*
* @return the logs generated by the transaction
*/
public List<Log> logs() {
return logs;
}
/**
* Returns the logs bloom filter for the logs generated by the transaction
*
* @return the logs bloom filter for the logs generated by the transaction
*/
public LogsBloomFilter bloomFilter() {
return bloomFilter;
}
/**
* Computes the logs bloom filters of the current receipt and compares it to the bloom filter stored.
*
* @return true if the computed bloom filter matches the bloom filter stored
*/
public boolean isValid() {
return LogsBloomFilter.compute(logs).equals(bloomFilter);
}
/**
* Returns the status code for the status-encoded transaction receipt
*
* @return the status code if the transaction receipt is status-encoded; otherwise {@code null}
*/
public Integer status() {
return status;
}
@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof TransactionReceipt)) {
return false;
}
final TransactionReceipt other = (TransactionReceipt) obj;
return logs.equals(other.logs)
&& Objects.equals(stateRoot, other.stateRoot)
&& cumulativeGasUsed == other.cumulativeGasUsed
&& Objects.equals(status, other.status);
}
@Override
public int hashCode() {
return Objects.hash(logs, stateRoot, cumulativeGasUsed);
}
@Override
public String toString() {
return MoreObjects
.toStringHelper(this)
.add("stateRoot", stateRoot)
.add("cumulativeGasUsed", cumulativeGasUsed)
.add("logs", logs)
.add("bloomFilter", bloomFilter)
.add("status", status)
.toString();
}
}