so what is your expectation that we should deliver?
I don’t see any point why this indexing service has to be part of the core node, also this will not happen.
So why don’t you build a indexing middleware for your usecase? or make a PR with functionality specifically for this to our middleware?
Well, maybe we can’t understand each other, but this is something that will definitely be needed by almost every dApp (dAepp?). The thing I want is very simple and without it the UX of the dApps will not be at the level that it is supposed to be. So to summarize what we currently need:
A way to subscribe for an events by given topic/event name
If 1) is not possible, then a way to read past events fired by a given contract (maybe by given contract’s pub key) and given block height (for example all events between block A and B)
If 2) and 1) are not possible then a way to read all transactions to a given contract and given block height
If you still don’t understand what I want and that actually everyone will need that eventually, then maybe @nikitafuchs.chain and @jsnewby can also elaborate on that matter.
P.S. Yes, I will build our own middleware for our case, but can’t do it in a proper way without any of the points above.
This is not so hard to do, my middleware is in mongodb and nodejs, the db has events and you can hook on them, this is not part of the protocol nor is it exposed on the middleware, but it is a perk of having a middleware that sends the events to the database. - actually a perk of indexing the blockchain in a database
With the right index in the mongodb it can be done in a few milliseconds
Hey guys, what @kraykov is asking here is a way to subscribe/listen for events fired in a Sophia smart contract and also a way to query them in the UI similar to what web3 or other eth SDKs are offering.
The idea is simple:
contract EventFirer =
record state = { number: int }
public stateful function init() = { number = 0 }
datatype event = LogNumberChanged(indexed address, indexed int)
public stateful function change_number(new_number: int) : int =
put(state{number = new_number})
Chain.event(LogNumberChanged(Call.caller, new_number))
state.number
We have here a simple smart contract which is storing some info as an event/log in the blockchain using Chain.event and an event datatype.
This on protocol layer should be using log opcodes and store info for the fired event there and it is working now as the suggested method by @philipp.chain - querying node’s API endpoint directly and getting the info for certain transaction works.
But this is not what the discussion here is about. From what I see as a smart contract / dapp developer coming from the ethereum space @kraykov is asking for a way to request all of the instances of this event ever fired in the blockchain or fixing a certain frame of block_height numbers.
Lets assume we have the same contract above in Solidity. This is what we are getting in the logs after calling the changeNumber function and this is what gets stored in the logs on-chain.
So as an example using web3 in ethereum a developer has a simple and easy way to get all of the smart contract’s fired events instances in the UI with a simple fn call using the web3.js library like so:
...
var numberChangedEventAll = myContract.NumberChanged({_sender: userAddress}, {fromBlock: 0, toBlock: 'latest'});
numberChangedEventAll.watch(function(err, result) {
if (err) {
console.log(err)
return;
}
// append details of result.args to UI
})
...
Which returns an array with the event log data as in the picture above.
Additionally the {fromBlock: 0, toBlock: 'latest'} gives you the ability to filter the results however you need them in the frontend of your application.
Also, this doesn’t require for the developer to know or query the exact tx number in order to get the event data.
Why this features is needed?
e.g. I want to get all the times the number was changed. In eth we can do the above, and the length of the array returned from the example would be the actual result.
Or if I want to get every number changes by a certain account pubkey (for example the current user).
And more UI/business logic based on what actions and interactions we have stored via events. You get the point…
How can we do that in aeternity?
My attempt of following along with the issue:
This is the tx data from the transaction calling the change_number fn in the Sophia smart contract:
So in the array of log objects in the result.log property we have data similar to what we have in solidity.
This is how it looks like:
So the event fired from the smart contract is being stored on-chain. The name of the event I’m not sure though.
Part of the code in the aeternity node which is used to structure the data for the event log entry.
Topics are basically what we’ve provided as event parameters whether indexed or not.
Not sure if and how the event name is stored. (Which in fact is the only reason preventing me from making a PR to implement this feature in the js-sdk). I assume it is encoded in the data field - tried base58 decode but with no luck. I haven’t had the time to dive deeper into this yet.
Suggestion
If in the decoded log data we have the name of the event or at least something linking it to the contract ABI the requested feature should be easily implementable in aeternity’s js(and not only) SDK, as the events fired in a smart contracts are already stored on chain and can be accessed through the node’s API endpoints.
Moreover, the middleware is used to cache and store the blockchain data in an easy-to-query format - so this task should be almost trivial to implement using the last two giving the point at this point one should have already provided the contract address, the event name
This is one of the functionalities that should be proveded and easily accessible for the developers. First because this is the way it should be working, second because this is how developers from other smart contract platforms are used to interact with and handle smart contract events.
@kraykov I do really well understand what you want, I am just saying it is not there yet and I don’t know of any immediate plans that we will build this.
I also tried to explain that this should not be really hard to build for yourself, without loosing any trust or decreasing security of your platform. Although it is not trivial to for us to build this in a very generic way for any dev to be useful and maintain and host this, which is probably why this has not been built yet.
At aeternity we try not to be a centralized company to build tools for others to use. We want to establish an open source community around the blockchain protocol. In the first step a lot of tools came from aeternity directly or were funded in some way by the company.
We want to empower people that have the need for some tools, fixes, additions or protocol changes to build them themselves and participate in building a great platform around the blockchain protocol.
Everything we build is open source for this reason, not happy with a tool, make a PR to fix it. Missing feature in the middleware, contribute to create it. Need for another dev tool, build it. There is no need to ask for permission to do any of this.
This is how all great open source tools are built, if everyone would wait for the central maintainer to ship features they wouldn’t be in a state where they are now. People contribute, participate or even fork projects in order to make them great.
Thanks @philipp.chain, but I’m not sure if this is correct, as the topics array contains 3 members in this case and from the example you could see that the last one is a plain int (not hashed).
Also not sure why they are 3 instead of 2 (which is the number of arguments I’ve implemented my event to have: LogNumberChanged(indexed address, indexed int).
Another thing unclear to me is the contents of the the data field, which I’m more likely to believe is the hashed vm word.
This might be poorly documented (blame me…), but you get an extra (implicit) indexed field. It is the Blake2b-hash of the event constructor (LogNumberChanged in you case) - this is similar to Solidity/Ethereum I think?! This implicit argument is the first in the array.
in case of type addressint the param always needs to be declared with indexed right? otherwise I get a compile error. is there a specific reason for that?
No, at some point we were thinking about having non-indexed words (ints), but that doesn’t really make sense so indexed is kind of superfluous… We might remove that in the future.
Thanks for taking the time to create this useful document @hanssv.chain! I have few questions though:
1875564187002476023854543820981509249331367502514562901623081521315128929137 in the second event in the example corresponds to <<"ct_2pvLLjPfjq...">> . A boolean argument would come out as 0 (= false) or 1 (= true).
What does this mean? Isn’t this parameter supposed to be evaluating to the Contract.address as specified in the example (maybe it is but as an integer?)? And how one is supposed to get the address in base58 format ct_2pvLL.... from this 1875564187002476023854543820981509249331367502514562901623081521315128929137 integer if the above is correct?
Also, I didn’t quite get this:
A boolean argument would come out as 0 (= false) or 1 (= true)
1875564... is a 256-bit unsigned integer, if you put the 256-bits into a byte array (length 32 = 256/8) you have [222,152, ...] which if you Base58Check encode it and stick the ct_ prefix onto it become ct_2pvLL....
If you have datatype event = ... | AnEvent(..., indexed bool, ...) the corresponding topic will be either 0 or 1 .
In eth’s contract, if we set an event in a function, then we can use web3.js to get the event. e.g. once someone changed the state of the contract’ data, using web3.js(just binding the contract address) the front-end can change the data or UI automatically. But I don’t know how to implement it in AE. I think we are thinking about the same problem.