use of org.hyperledger.besu.evm.worldstate.WorldUpdater in project besu by hyperledger.
the class StateDiffGenerator method generateStateDiff.
public Stream<Trace> generateStateDiff(final TransactionTrace transactionTrace) {
final List<TraceFrame> traceFrames = transactionTrace.getTraceFrames();
if (traceFrames.size() < 1) {
return Stream.empty();
}
// This corresponds to the world state after the TX executed
// It is two deep because of the way we addressed Spurious Dragon.
final WorldUpdater transactionUpdater = traceFrames.get(0).getWorldUpdater().parentUpdater().get().parentUpdater().get();
// This corresponds to the world state prior to the TX execution,
// Either the initial block state or the state of the prior TX
final WorldUpdater previousUpdater = transactionUpdater.parentUpdater().get();
final StateDiffTrace stateDiffResult = new StateDiffTrace();
for (final Account updatedAccount : transactionUpdater.getTouchedAccounts()) {
final Address accountAddress = updatedAccount.getAddress();
final Account rootAccount = previousUpdater.get(accountAddress);
// calculate storage diff
final Map<String, DiffNode> storageDiff = new TreeMap<>();
for (final Map.Entry<UInt256, UInt256> entry : ((UpdateTrackingAccount<?>) updatedAccount).getUpdatedStorage().entrySet()) {
// FIXME cast
final UInt256 newValue = entry.getValue();
if (rootAccount == null) {
if (!UInt256.ZERO.equals(newValue)) {
storageDiff.put(entry.getKey().toHexString(), new DiffNode(null, newValue.toHexString()));
}
} else {
final UInt256 originalValue = rootAccount.getStorageValue(entry.getKey());
if (!originalValue.equals(newValue)) {
storageDiff.put(entry.getKey().toHexString(), new DiffNode(originalValue.toHexString(), newValue.toHexString()));
}
}
}
// populate the diff object
final AccountDiff accountDiff = new AccountDiff(createDiffNode(rootAccount, updatedAccount, StateDiffGenerator::balanceAsHex), createDiffNode(rootAccount, updatedAccount, StateDiffGenerator::codeAsHex), createDiffNode(rootAccount, updatedAccount, StateDiffGenerator::nonceAsHex), storageDiff);
if (accountDiff.hasDifference()) {
stateDiffResult.put(accountAddress.toHexString(), accountDiff);
}
}
// Add deleted accounts
for (final Address accountAddress : transactionUpdater.getDeletedAccountAddresses()) {
final Account deletedAccount = previousUpdater.get(accountAddress);
if (deletedAccount == null) {
continue;
}
final AccountDiff accountDiff = new AccountDiff(createDiffNode(deletedAccount, null, StateDiffGenerator::balanceAsHex), createDiffNode(deletedAccount, null, StateDiffGenerator::codeAsHex), createDiffNode(deletedAccount, null, StateDiffGenerator::nonceAsHex), Collections.emptyMap());
stateDiffResult.put(accountAddress.toHexString(), accountDiff);
}
return Stream.of(stateDiffResult);
}
use of org.hyperledger.besu.evm.worldstate.WorldUpdater in project besu by hyperledger.
the class TransactionTracer method traceTransactionToFile.
public List<String> traceTransactionToFile(final Hash blockHash, final Optional<TransactionTraceParams> transactionTraceParams, final Path traceDir) {
final Optional<Hash> selectedHash = transactionTraceParams.map(TransactionTraceParams::getTransactionHash).map(Hash::fromHexString);
final boolean showMemory = transactionTraceParams.map(TransactionTraceParams::traceOptions).map(TraceOptions::isMemoryEnabled).orElse(true);
if (!Files.isDirectory(traceDir) && !traceDir.toFile().mkdirs()) {
throw new RuntimeException(String.format("Trace directory '%s' does not exist and could not be made.", traceDir));
}
return blockReplay.performActionWithBlock(blockHash, (body, header, blockchain, worldState, transactionProcessor) -> {
WorldUpdater stackedUpdater = worldState.updater().updater();
final List<String> traces = new ArrayList<>();
for (int i = 0; i < body.getTransactions().size(); i++) {
((StackedUpdater<?, ?>) stackedUpdater).markTransactionBoundary();
final Transaction transaction = body.getTransactions().get(i);
if (selectedHash.isEmpty() || selectedHash.filter(isEqual(transaction.getHash())).isPresent()) {
final File traceFile = generateTraceFile(traceDir, blockHash, i, transaction);
try (PrintStream out = new PrintStream(new FileOutputStream(traceFile))) {
final Stopwatch timer = Stopwatch.createStarted();
final TransactionProcessingResult result = processTransaction(header, blockchain, stackedUpdater, transaction, transactionProcessor, new StandardJsonTracer(out, showMemory));
out.println(summaryTrace(transaction, timer.stop().elapsed(TimeUnit.NANOSECONDS), result));
traces.add(traceFile.getPath());
} catch (FileNotFoundException e) {
throw new RuntimeException("Unable to create transaction trace : " + e.getMessage());
}
} else {
processTransaction(header, blockchain, stackedUpdater, transaction, transactionProcessor, OperationTracer.NO_TRACING);
}
}
return Optional.of(traces);
}).orElse(new ArrayList<>());
}
use of org.hyperledger.besu.evm.worldstate.WorldUpdater in project besu by hyperledger.
the class TraceTransactionIntegrationTest method shouldTraceSStoreOperation.
@Test
public void shouldTraceSStoreOperation() {
final KeyPair keyPair = SignatureAlgorithmFactory.getInstance().generateKeyPair();
final Transaction createTransaction = Transaction.builder().type(TransactionType.FRONTIER).gasLimit(300_000).gasPrice(Wei.ZERO).nonce(0).payload(Bytes.fromHexString(CONTRACT_CREATION_DATA)).value(Wei.ZERO).signAndBuild(keyPair);
final BlockHeader genesisBlockHeader = genesisBlock.getHeader();
final MutableWorldState worldState = worldStateArchive.getMutable(genesisBlockHeader.getStateRoot(), genesisBlockHeader.getHash()).get();
final WorldUpdater createTransactionUpdater = worldState.updater();
TransactionProcessingResult result = transactionProcessor.processTransaction(blockchain, createTransactionUpdater, genesisBlockHeader, createTransaction, genesisBlockHeader.getCoinbase(), blockHashLookup, false, TransactionValidationParams.blockReplay());
assertThat(result.isSuccessful()).isTrue();
final Account createdContract = createTransactionUpdater.getTouchedAccounts().stream().filter(account -> !account.getCode().isEmpty()).findAny().get();
createTransactionUpdater.commit();
// Now call the transaction to execute the SSTORE.
final DebugOperationTracer tracer = new DebugOperationTracer(new TraceOptions(true, true, true));
final Transaction executeTransaction = Transaction.builder().type(TransactionType.FRONTIER).gasLimit(300_000).gasPrice(Wei.ZERO).nonce(1).payload(Bytes.fromHexString(CALL_SET_OTHER)).to(createdContract.getAddress()).value(Wei.ZERO).signAndBuild(keyPair);
final WorldUpdater storeUpdater = worldState.updater();
result = transactionProcessor.processTransaction(blockchain, storeUpdater, genesisBlockHeader, executeTransaction, genesisBlockHeader.getCoinbase(), tracer, blockHashLookup, false);
assertThat(result.isSuccessful()).isTrue();
// No storage changes before the SSTORE call.
TraceFrame frame = tracer.getTraceFrames().get(170);
assertThat(frame.getOpcode()).isEqualTo("DUP6");
assertStorageContainsExactly(frame);
// Storage changes show up in the SSTORE frame.
frame = tracer.getTraceFrames().get(171);
assertThat(frame.getOpcode()).isEqualTo("SSTORE");
assertStorageContainsExactly(frame, entry("0x01", "0x6261720000000000000000000000000000000000000000000000000000000006"));
// And storage changes are still present in future frames.
frame = tracer.getTraceFrames().get(172);
assertThat(frame.getOpcode()).isEqualTo("PUSH2");
assertStorageContainsExactly(frame, entry("0x01", "0x6261720000000000000000000000000000000000000000000000000000000006"));
}
use of org.hyperledger.besu.evm.worldstate.WorldUpdater in project besu by hyperledger.
the class AbstractBlockCreator method rewardBeneficiary.
/* Copied from BlockProcessor (with modifications). */
boolean rewardBeneficiary(final MutableWorldState worldState, final ProcessableBlockHeader header, final List<BlockHeader> ommers, final Address miningBeneficiary, final Wei blockReward, final boolean skipZeroBlockRewards) {
// TODO(tmm): Added to make this work, should come from blockProcessor.
final int MAX_GENERATION = 6;
if (skipZeroBlockRewards && blockReward.isZero()) {
return true;
}
final Wei coinbaseReward = protocolSpec.getBlockProcessor().getCoinbaseReward(blockReward, header.getNumber(), ommers.size());
final WorldUpdater updater = worldState.updater();
final EvmAccount beneficiary = updater.getOrCreate(miningBeneficiary);
beneficiary.getMutable().incrementBalance(coinbaseReward);
for (final BlockHeader ommerHeader : ommers) {
if (ommerHeader.getNumber() - header.getNumber() > MAX_GENERATION) {
LOG.trace("Block processing error: ommer block number {} more than {} generations current block number {}", ommerHeader.getNumber(), MAX_GENERATION, header.getNumber());
return false;
}
final EvmAccount ommerCoinbase = updater.getOrCreate(ommerHeader.getCoinbase());
final Wei ommerReward = protocolSpec.getBlockProcessor().getOmmerReward(blockReward, header.getNumber(), ommerHeader.getNumber());
ommerCoinbase.getMutable().incrementBalance(ommerReward);
}
updater.commit();
return true;
}
use of org.hyperledger.besu.evm.worldstate.WorldUpdater in project besu by hyperledger.
the class BlockTransactionSelector method evaluateTransaction.
/*
* Passed into the PendingTransactions, and is called on each transaction until sufficient
* transactions are found which fill a block worth of gas.
*
* This function will continue to be called until the block under construction is suitably
* full (in terms of gasLimit) and the provided transaction's gasLimit does not fit within
* the space remaining in the block.
*
*/
private TransactionSelectionResult evaluateTransaction(final Transaction transaction) {
if (isCancelled.get()) {
throw new CancellationException("Cancelled during transaction selection.");
}
if (transactionTooLargeForBlock(transaction)) {
LOG.trace("{} too large to select for block creation", transaction);
if (blockOccupancyAboveThreshold()) {
return TransactionSelectionResult.COMPLETE_OPERATION;
} else {
return TransactionSelectionResult.CONTINUE;
}
}
// If the gas price specified by the transaction is less than this node is willing to accept,
// do not include it in the block.
final Wei actualMinTransactionGasPriceInBlock = feeMarket.getTransactionPriceCalculator().price(transaction, processableBlockHeader.getBaseFee());
if (minTransactionGasPrice.compareTo(actualMinTransactionGasPriceInBlock) > 0) {
LOG.warn("Gas fee of {} lower than configured minimum {}, deleting", transaction, minTransactionGasPrice);
return TransactionSelectionResult.DELETE_TRANSACTION_AND_CONTINUE;
}
final WorldUpdater worldStateUpdater = worldState.updater();
final BlockHashLookup blockHashLookup = new BlockHashLookup(processableBlockHeader, blockchain);
final boolean isGoQuorumPrivateTransaction = transaction.isGoQuorumPrivateTransaction(transactionProcessor.getTransactionValidator().getGoQuorumCompatibilityMode());
TransactionProcessingResult effectiveResult;
if (isGoQuorumPrivateTransaction) {
final ValidationResult<TransactionInvalidReason> validationResult = validateTransaction(processableBlockHeader, transaction, worldStateUpdater);
if (!validationResult.isValid()) {
LOG.warn("Invalid transaction: {}. Block {} Transaction {}", validationResult.getErrorMessage(), processableBlockHeader.getParentHash().toHexString(), transaction.getHash().toHexString());
return transactionSelectionResultForInvalidResult(validationResult);
} else {
// valid GoQuorum private tx, we need to hand craft the receipt and increment the nonce
effectiveResult = publicResultForWhenWeHaveAPrivateTransaction(transaction);
worldStateUpdater.getOrCreate(transaction.getSender()).getMutable().incrementNonce();
}
} else {
effectiveResult = transactionProcessor.processTransaction(blockchain, worldStateUpdater, processableBlockHeader, transaction, miningBeneficiary, blockHashLookup, false, TransactionValidationParams.mining());
}
if (!effectiveResult.isInvalid()) {
worldStateUpdater.commit();
LOG.trace("Selected {} for block creation", transaction);
updateTransactionResultTracking(transaction, effectiveResult);
} else {
return transactionSelectionResultForInvalidResult(effectiveResult.getValidationResult());
}
return TransactionSelectionResult.CONTINUE;
}
Aggregations