I have a contract with functions that modify contract state. Those functions are not supposed to be marked constant. When those functions are invoked, they generate a transaction to the blockchain. Is it possible for those functions to return a result, and if so, how do I get it?
thanks
0 ·
Comments
Public variables are not an option for my use case.
Is there an example of how to use events? The only examples I see are tied to javascript and web apps, but that's not my use case.
thanks
Events are described in the tutorials and are for communicating from the contract externally by logging into the blockchain. They're not for contracts talking to other contracts. AFAIK a contract can see an event.
What's the problem with public vars?
So a function called as a constant that calls a non-constant function is running locally in the calling node, has access to the state both functions needs, but won't remember any changes made. (The constant modifier on solidity is purely advisory. You can force a call of either type on any exposed contract method.)
In practice, when I want to see my transaction calls' return values, I end up calling them twice, once as a constant call, once as a transaction call (that order matters). It is possible that the return value from the constant call won't match what the return value of the transaction call would have been, particularly if you use block number values, and probably in other conditions as well, so it's not exactly the most reliable way to go about it.
In the long run, I'm hoping the web3 layer will acquire the ability to get return values from transaction calls somehow.
Events are really quite neat, and not as costly as you'd expect gas wise (about an order of magnitude less than storing a value, if I remember right.)
To throw another work-around at you, if events aren't your cup of tea, you could stash your intended return data somewhere private, then have a constant call to access it with the appropriate checks in front of it.
That won't make it any more secret to nodes, but contracts wouldn't be able to trivially grab it at least.
Interesting post Metal - I didn't realise you could do that.
The constant vs. non-constant discussion was helpful and clarified a few things for me. For anyone used to writing transactional (i.e. 2PC ACID) applications, constant functions calling non-constant functions is a common and acceptable pattern as long as you realize that the constant function might have to do some compensation if you get errors after the non-constant function returns. But what I've learned is that this pattern is problematic with ethereum, and it's a pattern that should probably be avoided.
WRT events, I finally figured it out and yes they are a nice tool to have. Where I was hung up was in the way that the doc talks about events, it discusses them in relation to logging. What I didn't realize is that the doc meant transactional logging (this is the first secret) not application or system logging. Events in ethereum are really just a view into the blockchain of the transactional happenings of a contract. Further, in the RPC API there is no concept of an event listener, which is what I was looking for. The second secret is to know that you have to create a filter in order to receive any events. Further, you never receive events, you have to poll for them. That's completely counter-intuitive for an eventing system, usually with eventing systems you create a listener and then filter the event stream to only what you want to see. By common definition, a filter eliminates unwanted things, it doesn't enable new things. Anyway, I discovered the secrets and was able to get it to work. Your posts gave me enough motivation to look at events a second time.
Which is a shame because if that worked, there would be some interesting use cases where you could offload heavy computational loads into constant calls that cost no gas, then fire up transactions from there to do the state-keeping bits needed. On the blockchain, it would look like only the latter transaction happened, with all the right precomputed parameters fed as input.
I'm guessing the problem to solve for that to happen is that the contract would effectively start a transaction on the caller's behalf, which could lead to unpleasant surprises. There'd probably be a need for a 3rd type of contract calling, "constant with option to transact", which would set ether and gas to use should the contract decide to start a transaction on the caller's behalf.
regarding RPC filters, I agree the polling API feels a bit icky. Or maybe just out of fashion. Maybe it could evolve some websocket support, but it's not clear how badly that's needed. New blocks appear every ~12 seconds, as do receipts and the logs in them, so having web3.js poll every few second over a local link isn't the worst thing.
It does however mean that playing Quake 3 on the blockchain will remain unbearably laggy for the time being.
"I'm guessing the problem to solve for that to happen is that the contract would effectively start a transaction on the caller's behalf, which could lead to unpleasant surprises."
Right, or provide an API to enable the application to control the begin and end of transactions. Just about every programming model that can do transactions has been able to do this going back at least 30 years, so yes, it's disappointing that this aspect is missing.
See https://github.com/fivedogit/solidity-baby-steps/tree/master/contracts "Ping" and "Pong".
Here's ping and pong slightly rewritten to leverage builtin contracts and public values, and to call methods on other contracts directly.
Note that although they call each other, they don't have to be compiled together as long as the "hasPongval" contract is included with each one (which defines the interface ping can call on pong, roughly.) To test this with minimal fuss, I suggest throwing it into https://chriseth.github.io/browser-solidity/ , then create Pong, setPongval to something, then create Ping using the address of your Pong instance (has to be passed as "0x..." rather than the raw number returned), then call getPongvalRemote() on it, then pongVal() to see if it made it across.
> ping.getPongval();
-1
> pong.getPongvalConstant();
-32
> ping.getPongvalRemote.sendTransaction({from:eth.coinbase,gas:1000000});
"0x02af62f1b3e690e6c2f0c865383cbac960ce9bc6dd1fa8e3a761f1b3de2aa105"
> ping.getPongval();
0
> pong.getPongvalConstant();
-32
So
uint32 public test;
will generate afunction test() constant returns (uint32) { return test; }
While
mapping (address=>uint32[]) public numbers;
will generate afunction numbers(address key, uint arrayIndex) constant returns (uint32) { return numbers[key][arrayIndex]; }
Solidity will also collapse duplicate declarations in derived contracts, so while it may not be the best style to define pongval twice, it should produce exactly the same code as if it was defined once.
I can't easily spot the root cause of what you're seeing. I did try my code in the solidity browser page and saw it working.