3

Lets consider a simple Contract that is to track a variable. A client App needs to access this variable's history to show a graph.

I can create an Array inside the Contract, and push() the value anytime, like the following (untested code):

contract Price {
    uint[] public priceHistory;
    function logPrice(uint price) public {
        priceHistory.push(price);
    }
}

Perhaps a cheaper and better option is to emit Events, similar to the following:

contract Price {
    event PriceChanged(uint price);
    function logPrice(uint price) public {
       emit PriceChanged(price);
    }
}

As per (3) a cheaper form of storage, the second approach is low cost. My question is, can we assume, reliably, that the logs will be written in time order (so that we can reconstruct a history from them).

The next question is, if this Contract calls other Contracts, which follow the same design pattern (all storage is in logs), how can we weave together a history with proper time-ordering? ie, establishing ClientApp -> ContractA - ContractB -> ContractC from reading Event logs for all the three Contracts.

One thought could be to pass-on transaction-hash from the first user -> contract call, and pass it all the way to subsequent contract -> contract calls.

If this were a monolithic system, timestamp could have helped. But since its a distributed system, I wonder how do we establish the notion of a sequence here?

2 Answers 2

4

Yes, but with some nuances so the guarantees are well understood.

Event log entries are part of the transactions in blocks. As such, you can reason about event sequence assurances with the same principles you would use to reason about transaction order. They will arrive in the same order as the transactions mine.

The idea of using event logs as inexpensive storage is sometimes called a "stateless" pattern. The main caveat is that such entries are not (easily) accessible for contract logic.

I'm not 100% sure what this means:

ClientApp -> ContractA -> ContractB -> ContractC

  1. If the client app makes three calls to three contracts, no assurance of the order, because mining will decide.
  2. If the client calls ContractA which then calls ContractB which in turn calls ContractC then it is deterministic and you can have it in the order the functions were called, or in the reverse order depending on how you write the contract functions.

If you habitually place the event emitter first, then you get "ready(a), aim(b), fire(c). All three events arrive with the confirmed transaction, in that order.

emit LogReady(...
contractB.aim(...

If you put the contract call before the event, then you get fire(c), ready(b), aim(a), in that order when the mined transaction arrives.

contractB.aim(...
emit LogReady(...

The second way is more intuitive - first do stuff, then log what you did. It results in peculiar (reverse order) logs for long chains of functions. The first way is not so intuitive but it yields a nice sequence regardless of how many separate event LogName() logs are involved.

Hope it helps.

5
  • Thanks Rob. It verifies my understanding that EVM maintains Causation dependency of the events. Any thoughts about my second question here ... how do I establish a sequence of events for operations that span multiple contracts? Is passing txn hashes from ContA->ContB->ContC a viable solution?
    – Amarsh
    Commented Dec 7, 2018 at 5:31
  • @Rob - What do you mean by "It results in peculiar (reverse order) logs for long chains of functions." Can you elaborate? Commented Mar 2, 2021 at 16:09
  • 1
    It goes "fire!", "ready!", "aim!" if a chain of contracts emit events after they call each other. You get logs like "Received(1)", followed by "Sent(1)". Go, "emit, call" instead of "call, emit". Another way to remember is to treat the event as a state change and take of it before interacting with other contracts (checks, effects, interactions). Commented Mar 2, 2021 at 16:42
  • @RobHitchens Can the "peculiar" order (interact 1st, emit 2nd) lead to some security issues or invalid behaviour on dApps that interact with the contract? If a dApp reacts on these 3 events (Fire, Ready, Aim), how the reverse order would affect the logic of the dApp? Applying this "peculiar" order sounds more like it could make dApp code buggy or behave in unexpected ways and lead to web2 security issues. What are your insights on the potential security implications of this peculiar emitting order?
    – CJ42
    Commented Oct 13, 2022 at 8:17
  • It won't affect the contract's security, or other contracts, but it certainly won't improve matters for the client developers. Commented Oct 15, 2022 at 3:59
0

Events are guaranteed to be in the same sequence that of transactions. (there is some randomness in order of transactions due to the way transactions are selected for mining by nodes)

Transactions are ordered

  1. By the blocks these are part of
  2. Transaction sequence within a block

You you can

  1. Scan for all blocks and transactions within the blocks
  2. Find events emitted by each transaction

Code for this is provided in following thread : web3: How do I get past events of myContract.myEvent?

2
  • there is some randomness in order of transactions due to the way transactions are selected for mining by nodes - I wonder of this is really the case. I mean, a financial transaction is so sensitive to ordering that even one transaction being out of order would render the entire ledger inconsistent.
    – Amarsh
    Commented Dec 7, 2018 at 5:54
  • Once a transaction is part of ledger, order is guaranteed. Mining is the process which includes transactions in ledger. For comparison with real world, if a bank account has only $10 and 8 transactions of $2 each are submitted at the same time; there will be some amount of randomness about which of these 8 transactions will be declined. Commented Dec 7, 2018 at 10:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.