| /* |
| * 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(); |
| } |
| } |