4

In general, I assume events traverse from smaller to larger block number.

Example:

contractDeployedBlockNumber = 10000
fromBlock: contractDeployedBlockNumber, toBlock: 'latest'

[Q] Could event watch() traverse from 'latest' block number through 0th block number (or smaller block number)? this is basically reverse traverse operation in order to catch the latest event first and break, this will help to retrieve the latest emitted event as fast as possible.


Smart contract's function keeps a registered users' information and updates it using registerUser() function.

contract Example {
   mapping(address => uint) userLatestEmittedBlocNum;
   function registerUser(string memory userEmail,
                         string memory name,
                         string memory githubUserName) public
       returns (bool success)
   {
       userLatestEmittedBlocNum[msg.sender] = block.number;
       emit LogUser(msg.sender, userEmail, name, githubUserName);
       return true;
   }

   function getUserLatestEmittedBlocNum(address userAddress) public view
    returns(uint)
  {
      if (userLatestEmittedBlocNum[userAddress] != 0)
          return (userLatestEmittedBlocNum[userAddress]);
   }
   event LogUser(address userAddress, string userEmail, string name, string githubUserName);
 }

I can store the block number where the user's information is updated under userLatestEmittedBlocNum[msg.sender] and retrieve the emitted log directly from that block number, but this will cost additional storage and gas usage.

blockReadFrom = Example.functions. getUserLatestEmittedBlocNum(userAddress).call()
my_filter = Example.eventFilter('LogUser',{'fromBlock':int(blockReadFrom),'toBlock':int(blockReadFrom) + 1})

As a results we need to retrieve the latest event for each users for the up-to-date information.

It may be inefficient to iterate from smaller block number to latest block number (that will also get the previous updated events as well), and span range might be very large. Instead, if we could able to scan from latest block number to small block number and break the iteration when we catch the event, it might be faster to find the latest emitted log.

On a smart contract, some information is logged and over time it may updated, hence the latest event is valid. So I want to retrieve the latest event on my smart contract, without start scanning from the much smaller block number, which is time consuming and inefficient. So basically starting from 'latest' block number to smaller block, and break this iteration if I catch the event. Otherwise if I start from smaller block number, no matter what, I have to iterate till the latest block number.


Goal: fromBlock: latest, toBlock: 0

blockNum = 0;
var contract = myContract.LogJob({}, { fromBlock: blockNum, toBlock: 'latest' });
var i = 0;
contract.watch(function (error, result) {
    console.log(i++ + ": " + JSON.stringify(result));
});
2
  • You would know when your contract got mined. So, basically, you need to iterate from latest to that mindedBlockNumber. Hope I guess I was able to give you some idea. Please let me know if my understanding is incorrect.
    – Rajesh
    Mar 19, 2018 at 9:19
  • But I can not iterate from latest to smaller block number, it returns nothing. fromBlock: latest, toBlock: 0 @ Rajesh
    – alper
    Mar 19, 2018 at 9:32

1 Answer 1

2

I'll try, because bounties. :-)

Technically, no, but you could create a similar effect.

I say "no" because the blockchain is a well-ordered set of blocks each containing a well-ordered set of transactions. By extension, the blockchain is a well-ordered set of transactions starting with the first transaction in or after block 0.

Here's the kicker.

It's meant to be reviewed in order owing to the fact that a blocks after the genesis block can't be independently verified without reference to the preceeding block. In other words, to know that you are looking at the authentic 50th block (by consensus), you must have knowledge of block 49. If you start at the end you will, generally, either recursively apply logic all the way back to block 0, or place your trust in something other than your own independent assessment.

Also, you can never know you have the latest block. The most you can ever know is you have the latest block you know about (and there is probably an even newer one on the way). What, would be first on a list of events starting with the newest first? It wouldn't last very long.

To accomplish something similar to your use-case seems to imply listening to all known events and continuously inserting newer "most recent" blocks at the beginning of a list. Or (probably more efficient) append the newest to the end of your own list and then read it back in reverse when you need to. This would probably be in relation to an offchain concern (UI, cache, something else ...) - maybe "point in time" snapshots or live updates as new "most recent" log entries are revealed.

It's perfectly fair to maintain your own off-chain data sources for performance and other reasons. You can still get the benefits of blockchain when users/clients can verify the facts if they want to by confirming facts against their own copy of the chain. This is approximately what is happening when you use online block explorers and other apps that need more performance than a typical node would be capable of. You can sort that sort of thing any way you like.

Hope it helps.

UPDATE

There is a pattern to efficiently do this if you design the contract to support it. It uses very simple breadcrumbs:

pragma solidity 0.5.1;

contract ReverseOrderEvents {

    uint lastEventBlock;

    event LogEvent(address sender, uint previousEvent);

    function doSomething() public {
        emit LogEvent(msg.sender, lastEventBlock);
        lastEventBlock = block.number;
    }
}

On the client side, when lastEvent != 0 then jump to that block and "listen" for events of interest. This event will include a pointer to the block that contains the previous event. Rinse and repeat. When you hit 0 there are no previous events.

The technique is simple, and you could use separate pointers for separate logs, separate users, etc., as needed so clients can find the most important recent information quickly.

This allows a client to start with the most recent and crawl back as far as interested. The pattern can be used in stateless designs where the data is in the event log and possibly only the most recent entry is important.

Just in case it isn't clear, watch() doesn't go in reverse. To use this, you would watch() one block only (which would be fast), find what you need, and then watch() the next single block of interest, following the reverse order clues you laid down for yourself.

5
  • TY. I got your point but I am not trying to watch events. It must be a very simple algorithm. I just want to retrive events from latest block number to smaller one. I can log them on my local machine but I don’t want to do that. Why cannot we get them from latest to smaller, since we are able to get them from smaller to larger. I still believe this could be implemented on web3 side that is actually be a useful function. @Rob Hitchens
    – alper
    Mar 23, 2018 at 6:15
  • I think I explained why it isn't there. Did you know that you can extend the web3 library yourself? Mar 23, 2018 at 14:38
  • Could you guide me on the extension of web3, could I do the coding on their github? My best solution is to store the deployed functions blockNumber and update it if there is an update, which will point to the exact block number of the updated event. @Rob Hitchens
    – alper
    Mar 25, 2018 at 14:17
  • @RobHitchens I think a completely client side model would work, since you can get the most recent known block number on the client, then programmatically, using that block number check the existence of the event using get instead of watch, to poll, and if not get reduce the block number by one and repeat the process, this increases the time taken, but ensures no need to add anything to the smart contract. @alper You would need complication either on the client side or a intentionally structured contract to achieve this afaik
    – Adibas03
    Mar 14, 2019 at 8:49
  • Your updated answer is the the solution I explained on my question... uint lastEventBlock will cause additional memory storage. I am trying to find out a solution where there will be no additional storage usage on the contract. @RobHitchens
    – alper
    Mar 15, 2019 at 20:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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