-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
estimateGas Error: failed to meet quorum #1021
Comments
I need to make the quorum errors more concise. :) This will occur whenever after all backends have been queried there is no consensus on the correct result. This will usually happens if a block was just mined that changes the result and not all backends have seen it. For example, if you had a balance of 0 tokens, and a tx sending you 2 was just mined, if you asked to estimate the cost of sending 2 tokens, backends that saw that above tx would say one thing and the others would say insufficient balance. It is possible neither answer has a meaningful majority (in the case a timeout occurs too). Does this sound like your case? Can you give some background on what you are doing? I am also considering adding a “try again” like thing to the FallbackProvider. Often a quorum error is quite decoupled from the actual problem though, such as network link errors... It is something I’d love to iron out more though. :) |
Thanks for the quick response.
No, the function that I am trying to estimate gas for is quite straightforward, we are invoking function of a contract but not using interface since encoded data comes from another service, so I am estimating gas by forming tx object. But upon re-examining the function & keeping
I didn't quite get it, I am already
|
@ricmoo |
You mean, why does gasPrice need a quorum? The gasPrice is a bit special. It will take the median of the responding backends. It shouldn’t trigger a quorum error unless more than half the backends respond. The goal of quorum is to mitigate the impact out-of-sync and malicious nodes. For example, imagine a hacker compromised INFURA and had it return a very low gas price. Then anyone using INFURA would submit transactions with a gas price so low, it would never get mined. With a quorum, the hacker would need to compromise half of all the backends, so they would (for example) need to find an exploit in INFURA, Alchemy and Etherscan. I also have a gas price oracle I’ve designed, and plan to leverage that soon too. :) Does that make sense? |
oh, no I don't mean gas price. Though may be I misunderstand the call. I thought it is about estimate the gas needed, not the price. gas needed and price are two different thing to me. gas needed is how much gas limit assigned to the transaction and gas price is how much I want to pay for it(to have it mined faster or don't care if it is done in 3 hours time). |
Oh. That’s correct. I don’t quite understand your previous question then? Sorry. What do you mean by “ which should have nothing to do with gas price, why would it need quorum from different provider?”? |
the signature of the call makes me think it is about gasLimit(i.e. gas needed not price needed), why I said I was confused |
@garyng2000 did you mean that
So if a node is out-of-sync, it might return a different gas estimate in some cases. |
I just inspected the above error, thrown by Fallback provider containing one provider which failed and the actual response body is:
This looks like a valid json rpc response but it was marked as |
You can reproduce the error that @dev1644 got by doing this: p = ethers.getDefaultProvider('ropsten');
await p.estimateGas({"from":"0xB660b10a922815667F0303576750FDBC6943e44a","gasPrice":{"_hex":"0x113abe6400","_isBigNumber":true},"to":"0xA0940f0388601f96D8885292A711C25665e325c8","value":{"_hex":"0x00","_isBigNumber":true},"data":"0xa4240de669667648dbfd2cc0c1fb5f7fe69d462710198881d53e833a8f64e071c6eca88a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"})
// this gives quorum error p = ethers.providers.InfuraProvider('ropsten');
await p.estimateGas({"from":"0xB660b10a922815667F0303576750FDBC6943e44a","gasPrice":{"_hex":"0x113abe6400","_isBigNumber":true},"to":"0xA0940f0388601f96D8885292A711C25665e325c8","value":{"_hex":"0x00","_isBigNumber":true},"data":"0xa4240de669667648dbfd2cc0c1fb5f7fe69d462710198881d53e833a8f64e071c6eca88a00000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"})
// this gives processing response error, which is gas required exceeds allowance |
it is called estimateGas because it really is just an estimate. Even on my single node private network(no out of sync condition possible), estimateGas(geth) can return something that is not the actual gas needed ! For me estimateGas is just a way to limit it such that I don't say 'I need 8M gas' when I really need ~200K. Though I always add something say 2x of the estimate. The reason is the state of the contract can change between estimate and actual execution. A hypothetical example :
|
That's correct, I too allow a bit more gas ;)
I think I didn't quite understand your previous comment #1021 (comment). Was it solved? |
for me, I would only use one provider and do the estimate assuming it is in-sync(thus confused why a quorum is needed). If it failed in actual execution, let it be and handle that accordingly(completely async may be 2 days later). Because there is no way to rule out the scenario I mentioned so that has to be designed into the whole application anyway(i.e. the pre-check can succeed, no revert) but it may eventually fail when it is being picked up by the miner. |
or to put it the other way, estimateGas to me is just a rough pre-check before even submitting and nothing more, why I wonder why quorum is needed. |
So, one thing I noticed, which is maybe what you are asking, is that you recreate a new FallbackProvider every time you estimate in your code above. You don't need to do this, you can indeed have a single long-lived Provider. Is there a reason you create a new Provider every time? |
You don't need a FallbackProvider. You could also use a FallbackProvider, but override the quorum to The value of the quorum is two-fold; a) prevents a single node from lying to you (through accident or malice) and b) keeps you app alive even if a backend is down. If you only care about b), you can totally tune the quorum to a lower value. The default is half the backends, rounded up. So, with 3 backends, it would be 2. To override:
Does that help? |
oh, not me. I would always use single provider. I was just wondering why it need a multi-provider consensus to estimateGas, it would mean more calls too. It probably is ok(or needed) for real/near time front end oriented application, though I was more from the general perspective about how estimateGas should be used. |
Oh, so why specifically don't we just use one backend for estimateGas? A lie can still cost you. If I lie and say a value that is wildly too low, for example, you could issue failing (out-of-gas) transactions. In general though, I highly recommend people include the If you code in Vyper, the ABI already includes the |
how can you compute the 'worst case' given my hypothetical example, it can be unbound and the worst case is 8M and still not enough, surely it may be a design issue though :-) |
So Vyper is intentionally not Turing complete, which is why they can do it. :) But many things can have their worst case computed, such as a token transfer. Worst case beyond logic the storage, which if it was zero costs 20k gas, if non-zero, 5k; so if you assume 20k you're golden. But it's true though, many things don't have a worst case. But you can still come up with reasonable assumptions. Or rely on estimateGas for things that iterate over loops and such. But keep in mind things that are unbound also become an attack vector. So, generally it's best to try keeping real-time constraints in mind when writing a contract, or it is possible under normal use it may become unusable. :) I generally limit gas to out-going calls too (for things I don't have control over), to prevent them from kicking me in the butt. Just another idea. Gas is its own, and very important east in Ethereum. :) |
I think there is some sort of bug in the response processer from the outcomes in #1021 (comment). The actual JSON response seems valid but it gives |
I get a quorum error in both cases. I was planning to put some refactoring into the FallbackProvider, maybe it's a good time to do that. I want to make the errors more concise, and favour errors that are more meaningful than just pick the first error. Mainly I need to make quorum either a number of a more advanced callable, because Alchemy currently only allows a block range of 1000 for getLogs, so in the event the request is a getLogs beyond that range, I want to pull it from the quorum (for the default provider) since it will always throw an error... |
Good to know that this problem is figured out.
But I'm confused about how can a quorum error comes in an |
Oh! I see what you mean. I didn’t realize the provider lines were different. I only copied the long lines. Now I’m guessing they were the same. I can try again later. Yes, quorum only applies to FallbackProvider. :) |
I trimmed some additional code before pasting that snippet (to highlight provider related code), so I am checking whether the FallbackProvider is already created or not if it is created then returning it. So yeah I am not recreating Fallback provider each time, sorry for the misunderstanding 😅
Interesting, I was unaware of this, this might be the cause of the error :thinking_face: , I will try it & will observe & update here whether the error is repeating or not. |
Yes, the response is valid. Its been a few days since this error occurred & according to a condition in contract's function, tx will always fail thus the I am pasting much more recent error that I got and this isn't reproducible when I estimated gas with Error log -:
While trying to reproduce I got |
Configured my service for this, but still getting a
|
I’ll try getting a version out tomorrow that at least makes that error blob readable and somewhat concise. I think that’s step 1. :) |
Updated Ethers version to
Again we are getting |
I still need to look into the processing error, but making the error more readable was step 1. :) |
@dev1644 This error is probably due to assert or maybe an infinite loop. |
Do you mean at having assert or infinite loop at smart-contract function? |
I just looked at your error in your recent comment and tried to make sense of it #1021 (comment)
The above is the error message (eliminating other distractions like failed to meet quorum or processing response error) that I think could help you dig the error further. The purpose I commented on this thread is that I believe you're looking to get rid of this error (just like plenty of my colleagues after upgrading to v5). If @ricmoo makes the errors more readable, you're still going to see something like The only thing that's confusing most people is not-self-explanatory errors thrown through ethers.js and I'm happy to see this problem is gaining attention so that effort can be put to fix it. I had created #871 when I moved to v5, noticing changes in error messages between v4 and v5. Also, it's surprising that you are getting a concrete estimation with |
@dev1644, |
I have looked at the contract and I am 100% sure its not the contract fault at all. Moreover, my service doesn't give this error all the time it's coming up for only some transaction. so if I send 100 tx to my service, the chances are (based on past analysis of logs) this error pops in once or twice per 10 tx & I can't reproduce it through
I don't think so, I am passing the correct network (ropsten for now), but I don't then it should affect estimation at all because if I send a transaction with some
@garyng2000 Yes, I thought of it, I tried with different providers at my snippet not in my service till now, but I am planning to do it shortly. |
I’ve updated the JsonRpcProvider to better support detecting unpredictable gas errors and set a list of forwardable errors for the FallbackProvider. Since a quorum indicating unpredictable gas is actually a correct response. I’m just testing locally and will put it up soon. It shouldn’t remove any errors, but will make errors easier to understand and more importantly reflect the detectable standard blockchain errors ethers is designed to detect. :) |
Finally got the CI passing again (looks like GitHub removed lib-usb from their default linux distro?). Anyways, try out 5.0.13, which should bubble better errors up for Let me know how it works out for you. :) |
Thank you very much, errors are nice and readable now.
I again reviewed this service code, seems like the issue was the same that I pointed a few days ago here
I have updated the service accordingly and not getting error since past 24 hours. |
now should never be used for this kind of logic, it is effectively machine time and there is no guarantee that it is in sync among all the nodes. At the very least, give it some slack of say 30-60s(that is assuming no machine lack more than that). |
Gas estimates with FallBackProvider are problematic. I often get 4 different results from the 4 different back-ends I use (My own parity node, Infura, Cloudflare, Alchemy) and therefore can't reach a quorum of 2. I think the Fallback provider should return the median value among the responding back-ends. |
That probably does make sense. Can you provide an example address and call that returns the variety of gas estimates? |
Sure, this is what I was doing on mainnet:
This function relies on Solidity |
Might be duplicate of #841
I often receive this error when I try to estimate gas for my transaction.
Most of the times it works but often times it throws error shown below.
I'm using
ethers v5.0.8
The text was updated successfully, but these errors were encountered: