-
Notifications
You must be signed in to change notification settings - Fork 1k
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
Redefine opcode prices and make them adjustable #1875
Comments
Agree completely! Same should be true for syscalls, oracle calls... Maybe in a decade it will have reached a price equilibrium, but for now at least GAS cannot be treated like it's a stablecoin. It is volatile, and that volatility needs to be addressed. Coefficients and a modifiable base price seems like a good way to do it, definitely an improvement over a simple multiplier. |
I think there could be another advantage of this feature. If most smart contract users think the opcode price too high to continue with Neo smart contract, we may lower the fee to attract more people. If there are too many smart contracts, do the opposite. However there could be some problems: users might worry about the dynamic price as they are not certain about how much budget they will need even in terms of GAS, which is very inconvenient for stable users. Moreover, modifying opcode price by committee members with no concrete rule sounds like delegate cartels. |
This is a valid concern, but to me that just reminds that we need more clear governance rules in general, what questions are solved by the committee, what's the procedure, what are the limits, how conflicts are resolved, things like that. Like this particular question, it can be solved with a simple policy, for example "committee can change base opcode price no more often than once a year or if GAS price changes five-fold since the previous change relative to XDR". Maybe with some limit to the change step. And something like this IMO will give enough predictability for end users. |
The current model is like this. |
Was it really measured? Take INC, ADD, MUL and SHL as an example. Is there a three-fold difference between SHL and INC or 1.5 diff between ADD and MUL? Then compare it to my favorite pet peeve which is |
I used to make a tool to test the CPU time consumed by each instruction. But it is based on the early NEO3 code. I will rewrite it based on the latest NEO3 code in a while. |
Cannot agree. Limiting times that committee members can change opcodes price still doesn't solve the problem. There is still no rule how to change the price. Suppose you are a common user, how can u trust the network if the price can be changed by will at any second? Furthermore, there are many leaks in such rules. For example, you suppose committee members can change price at most some times a year. You know what? If committee members change the price to 0, or 100000000 per opcode and use up all possible times to change price, does it mean overall network must suffer such price for one year, even if committee members are shuffled? Yes, this is not likely to happen exactly because such price is very unreasonable, but what if committee members change the price to a seemingly reasonable standard but eventually ruin the network? What will you do at that time, hard folk? When you put forward such issues you should tink about the practicability of it. Opcode as well as some interops price should be redefined. However, changing opcodes price by committee is too radical. |
If we don't trust the committee then what's the point of its existence? And how to solve these tricky questions like opcode prices? The committee can ruin the network already in many ways, that's the power it has. However if we don't give this particular aspect of the power to the committee then what's the protocol for opcode price change? At the moment that would be "someone creates a patch and a bunch of core developers review and merge it", is it any better than the committee? I don't think so. I think if we're to have the committee it should run the network, it should have all the powers needed and at the same time it should have explicit transparent rules for every question this committee is in charge of (like #1867, for example). And committee decisions should be expressed as signed transactions on the network, again bringing more transparency to the process of any kind of policy change (changes in the node's code are exactly the opposite of transparent). This particular question, think of it as of central bank decision on base interest rate. There is some kind of committee there too. And it can ruin country's economy with its decision. Yet it somehow makes a decision and usually it works. |
Trust relays in how much rights granted. Too much rights weaken trust.
So you think dynamic price by committee is the only way? Why don't set up a reasonable price list at the start after careful calculation?
But previous members should not impact future network env all right? Plz note that this is pointing out why "committee can change base opcode price no more often than once a year or if GAS price changes five-fold since the previous change relative to XDR" is unreasonable. Before puting forward a funtional change you should think carefully about what will be the impact rather than at will.
When did I deny the existence of committee? What I doubt is HOW MUCH POWER SHOULD BE GRANTED EXACTLY TO THEM, to prevent abusing and destroying environment.
Then why do we work on NEO if a centralized authority works with no problem?? |
This particular question has been discussed already, that price is being expressed in GAS and I still can't buy Coca-Cola for GAS at the nearest store. The price of execution should be economically viable for dApps to use the blockchain. That price would be checked against not only blockchain of the day, but also things like AWS Lambda which is expressed in USD. Ask yourself --- would Neo 2 survive without |
From your reason, then this price should be bound to exchange price rather than set by committee. Ask yourself, would you trust a committee or direct bounding to exchange price for stabilization?
So what? It only shows that pricing itself is unreasonable. It doesn't show that we need a dynamic opcode price, or dynamic pricing by committee. |
Can't agree with this. It's not acceptable that one day an action costs $1, and the next week the same thing might cost $50 because GAS has gone up 50x. At some points it was possible to deploy a contract with storage on Neo2 for $500, at other times the price was $30,000. Ethereum too is currently killing itself over ludicrous fees. It is true that this is mostly a consequence of congestion rather than price volatility, but the result is the same, an unusable network.
It is fine to start with a reasonable price list, but that price list doesn't stay reasonable for long because it is denominated in GAS, which will change in price. We need a way to scale opcode fees in accordance with GAS price so that no matter what the market price of GAS is, the cost to use the network is the same reasonable sum as before. |
100% agreed. Again: GAS is volatile and traded freely in the markets (thus prone to speculation); it cannot under any circumstances be expected that GAS has a stable value. |
Hi @EdgeDLT and @diskooooo, From my perspective, we need to be careful even when speculating about GAS price. There are projects that provide a parcel of the block for holders of the token (thus, in every block, holders have slots for transactions). I believe that GAS can be, yes, more stable, it all depends on the blockchain use and a balance between demand and offer of those interested in using it. 1 GAS for 1 second does not looked bad to me when I first read @erikzhang talking about this. I believe that we need to attach it to some metric. Furthermore, it should be adjusted during years long execution.
|
Hey @vncoelho. I don't think it's really a case of speculating about GAS price. You can expect that the economic design will eventually lead GAS to stability, but it is not stable today, or tomorrow, and it won't be stable next year either. 1 GAS for 1 second sounds great in principle. Today that means $1.90 for one second, not a great deal even now. What about if 1 GAS is $50, then I should pay $50 for 1 second of compute? It does not make sense to me; we must adjust for volatility. How about something similar to Justin Drake's proposal on Ethereum for an enshrined price feed? Use the oracle service to get the current GAS value every x blocks and use it to adjust opcode pricing to the market rate for that epoch. |
I am not sure, @EdgeDLT. |
We've created a benchmark for this in neo-go, check out nspcc-dev/neo-go#1493, you can run it locally to check some of these results and there are results from my machine posted there too (100000 iterations for each test, that's the basis for the table below). The benchmark creates a VM instance to run just one opcode that we're targeting preparing any environment for it when needed. An instruction is run via Here is the table based on neo-go testing results and considering numbers outlined in #2004 for neo-vm. All of these are calculated against the golden standard of NOP for every case, absolute numbers mean nothing here, we're concentrating on relative performance for now. "Current" is current relation derived from opcode prices and "Optimal" is just a bit shorter word for "Recommended", that's what I'd propose to use based on data we have. For some opcodes we have very stable results for both implementations, for some it's not that easy, but we can discuss each opcode and tests for it additionaly if needed. What's more interesting is that NOP-based coefficients seem to work fine in that they provide enough granularity for this and allow to create some meaningful table. I should notice though that this subject is a bit more complex than these numbers because of implicit type conversions, reference counting and huge difference between regular expected usages and edge cases. All of these distort the picture considerably, but we have to make some choice anyway, so I've added some per-instruction comments. We also have patterns based on our instruction set that need to be accounted for. Lots of instructions have short and long (_L) versions, they differ in size, but their execution time tends to be about the same, so we don't want to differentiate their coefficients (contract size cost should motivate people here). Instructions operating on two arguments cost a bit more than operating on one argument. Using parameters can also cost a bit more.
|
In some cases you forgot considering Struct.Clone(). |
Do you have, or can you create, a cost difference for typical smart contract use-cases? From eyeballing the table it feels like with the recommended prices execution will be more expensive. |
I think we can replay preview3 testnet transactions to see how proposed coefficients affect GAS cost of real executions (probably next week). In general, yes, this makes some basic opcodes cost more, but at the same time making things like PICKITEM way cheaper, so the end result depends on usage patterns. But then we're talking fair coefficients here at the moment, while the base NOP price can be as low as 1 Satoshi. |
I'd be interested to see the results of that exercise. |
If we're to take preview3 testnet at the height of 331056 with just 186 transactions inside, we can get the following picture. Сalculating overall GAS consumption for all executions gives us a cost of 241.7818188 GAS, with proposed changes that would be 241.1211635 or 99.73%, pretty close, but still a little less. Overall there are 9893 instructions executed with the following distribution:
141 of 186 transactions did really burn a little more of GAS for their execution with the biggest relative increase for 173dcbc4a88995a0cf7bdd006923d148f787f76ca75621dc4c440ca6d9afbc73 that burned 0 GAS and now suddenly required 60×10⁻⁸. It's a very special transaction that does exactly this:
So I guess it's not very representative. As for other 140 transactions, the biggest relative increase happened for 1dd1dced6a2dace4346bfa0a8aeef380e7f3153a523d868253dad1a24d17f36d which paid 1008450×10⁻⁸ and after the proposed changes would have to pay 1017700×10⁻⁸ which is a 0.917% increase, it's a bit more useful with the following code inside:
So from 140 transactions we have 14 that increased their cost for more than 0.1% and others are even less than that. And all of these 140 are heavily dominated by some syscall in their cost, so I don't think they're really interesting, they just don't execute enough of VM code. What's interesting is that we have 45 transactions that actually cost less using the alternative pricing. The winners here are 2b5592173c587673e076337f6bc500f17362ee3f39a2d4a6b7c69f57162490d9, 89fa34dd37ebe06bb2c3cc19d014c3bc37e545e07bca1d5d8d8fe86ae61886c8, 46ccaabc95617de3adb2747419a45187cdef5342f30fdbdb1985a4e0d4884c35 and 5538fc911c3f4323bf55eeb47b44ca7320bdd6a0c14f6a6f01930b8468878c6f. All of these transactions use 13799010×10⁻⁸ GAS with current pricing and 7221070×10⁻⁸ with proposed scheme which means that they're actually 47.7% cheaper now. They're quite similar, doing things like this:
So there is some non-native NEP-5 contract behind this that runs some real opcodes in VM. Other than that, from 45 transactions 19 decreased their cost by less than 0.2%, from quick glance those are mostly contract-deploying transactions that benefited a little from cheaper BTW, speaking of real contracts with real opcodes, we have NeoFS contract there too (written in Go and compiled with neo-go, of course) and its invocations in 33ce82de7487063ca8cf278949268f5e50314e2d375ef8b3f479d5dd1e782041 and b0f53ef871b426a26ea4a80f9a47cc78bec64d7125ec899ef4de82701a37bba6 decreased in cost by 14.2% and 2.6% respectively. So, I think that:
We can use this table as the basis for the new pricing scheme, maybe adjusting some particular opcodes based on additional discussions (I can do some recalculations relatively easy now). |
How did u count time consuming in ur test? Did u count overall vm running time or time used by specified Opcode? |
You fellows may be concentrating on relative performance, for now, but it's unwise to neglect real time consuming, as it's important to know how much time each Opcode can cost at most. Even though time cost varies across different machines, a rough value is needed to see whether some opcodes offend the "at most 1 second per gas consumed" rule. And yes, if you are interested in relative performance, feel free to calculate by division by yourself:) |
I'm seeing a Fee Ratio change submitted in #2032. Surely a step in the right direction, but why not do this truly dynamically by using an oracle request to get the real time $ cost of GAS? It's the only way to ensure $ equivalents for OPcode fees are somewhat static in a highly volatile market, isn't it? When doing storage or computing on AWS, you also expect time spent or bytes used to have the same $ cost always... |
I think it's not a good idea that our protocol need the outside GAS/NEO price. If we want use the price, we'd better do it in contract level, not neo-core. |
This defines a new Policy contract setting, BaseExecFee which contains a price of basic VM execution unit --- NOP instruction. It then redefines all opcode prices as coefficients applied to that base fee. The following invariants hold for this: * transaction fees are expressed in GAS * BaseExecFee is expressed in GAS * opcode price is a coefficient now * syscall price is expressed in GAS * ApplicationEngine.AddGas() also accepts GAS values, so it's a caller's responsibility to calculate that in whatever fashion he likes. Caveats: * while coefficients are based on table from neo-project#1875 (which is built on instruction execution time relations for two complete VM implementations), some modifications were applied: - it's impossible for SYSCALL to have non-0 cost now (tests just hang) - all slot operations have the same price * block and consensus payloads are adjusted to use BaseExecFee, but probably ECDsaVerifyPrice is still a constant * it's not really tested other than unit tests In general, this allows to define any execution cost in NOPs and then use BaseExecFee to get the price. The same principle could be applied to storage pricing based on FeePerByte (StoragePrice could just be `100 * FeePerByte`), but that's a bit different topic. Closes neo-project#1875.
This defines a new Policy contract setting, BaseExecFee which contains a price of basic VM execution unit --- NOP instruction. It then redefines all opcode prices as coefficients applied to that base fee. The following invariants hold for this: * transaction fees are expressed in GAS * BaseExecFee is expressed in GAS * opcode price is a coefficient now * syscall price is expressed in GAS * ApplicationEngine.AddGas() also accepts GAS values, so it's a caller's responsibility to calculate that in whatever fashion he likes. Caveats: * while coefficients are based on table from neo-project#1875 (which is built on instruction execution time relations for two complete VM implementations), some modifications were applied: - it's impossible for SYSCALL to have non-0 cost now (tests just hang) - all slot operations have the same price * block and consensus payloads are adjusted to use BaseExecFee, but probably ECDsaVerifyPrice is still a constant * it's not really tested other than unit tests In general, this allows to define any execution cost in NOPs and then use BaseExecFee to get the price. The same principle could be applied to storage pricing based on FeePerByte (StoragePrice could just be `100 * FeePerByte`), but that's a bit different topic. Closes neo-project#1875.
Summary or problem description
We have a number of issues recently around our opcode pricing (#1862, #1863, and not so recently in #1433), so there seems to be a problem of defining some fair reasonable price for various opcodes.
Do you have any solution you want to propose?
It comes in two parts, one for fairness and another one for reasonableness.
The fairness part is that instead of defining opcode prices in GAS directly let's first define execution time relations between different opcodes. Let's call it a coefficient. We can do that using the simplest instruction possible as a reference --- NOP. So executing a NOP takes some time and one NOP is our basic execution unit which has a coefficient of one.
How to get a coefficient for PUSH1, for example? Execute it and compare its execution time with NOP execution time. Maybe it's about the same. Maybe it's 2, 3, 5 times more. But you can get a coefficient for it, say 2. Then do the same for PUSHINT8, then for INC, then ADD, etc. Obviously it wouldn't be easy as to ADD things we need to PUSH them first and we need to test EQUAL for various data lengths and some control flow instructions are even more involved, but it can be done, it just takes some time.
To really nail this fairness part we should use two full independent Neo 3 VM implementations, one neo-vm itself and another neo-go. They have a lot of similarities, but they also have a number of differences, so if we're to define a set of tests in VM code (like neo-vm unit tests) we can run them in both VMs and find some average values.
Of course these coefficients may change in the future as VM implementations evolve. But in practice I think they will be stable enough for a year of two. These coefficients can thus be hardcoded in a (versioned) VM. Now to the reasonableness.
The reasonableness part is actually almost the same as was proposed in #1433 (comment) (that even had an implementation in #1453), but with a little twist. As we have now opcode coefficients defined we only need to set the base NOP price in GAS and everything else would be calculated automatically.
That value IMO should be a part of Policy contract, because in practice everyone would be recalculating this price to some USD and comparing with other options. And GAS/USD ratio can jump in hardly predictable ways (that's why I think "1 GAS per second" policy is not enough, there are a lot of prices for 1 second of execution and they're way lower than dollar value of one GAS), so to be reasonable and to stay reasonable this base price needs to be adjustable.
Neo Version
Where in the software does this update applies to?
The text was updated successfully, but these errors were encountered: