A new price function for HTTPS outcalls

The HTTPS outcalls feature has been live on the Internet Computer for almost a year now. This feature lets dapps on the Internet Computer call any HTTP server, over HTTPS, ingest data from web 2.0 sources and publish updates from the dapps to web 2.0.

When launching the feature, during the experimental phase, the pricing was set quite conservatively. The price per HTTPS outcall on a 13-nodes subnet was set to:

400M + 100K * (request_size + max_response_bytes) cycles

Where request_size is the size of the request in bytes and max_response_bytes is the value provided in the request for the max_response_bytes argument (which defaults to 2MB, but can be set to much lower based on the application requirements).

This formula above results in a relatively high price per HTTPS outcall, compared to other operations on the Internet Computer (but still, much cheaper than using external oracles).

Recently, we have re-evaluated this price formula by conducting a set of experiments and a thorough analysis to reduce the price of HTTPS outcalls, while still having it cover the actual cost of them to node providers.

We would like to propose a new formula for the price of HTTPS outcalls, which would be, for a 13-nodes subnet, as follows:

49.14M + 5200 * request_size + 10400 * max_response_bytes cycles

As you can see, the proposed new formula reduces the price per outcall by almost 10x factor. In the rest of this post, I will try to explain what stands behind these numbers.

Part 1: The Cost Factors of HTTPS Outcalls

To make an HTTPS outcall, each node on the subnet starts by sending out the HTTP request, then getting a response. Each node then applies the transform function on the response, if one is specified. Then, each node hashes the transformed response, signs the hash, and sends the hash with the signature to all its peers. A block maker may, at some point, validate the received hashes (and their corresponding signatures), and if enough hashes match, include the response in the block and hence return the response to the calling canister.

As you may see in the description above, the process of making one outcall is quite involved and includes several sources of overhead. For example, the fact that the outcall is made by all subnet nodes, and the fact that each node then has to send out (N-1) messages for each outcall, where N is the number of nodes in the subnet. Part of the cost is therefore growing quadratically with the size of the subnet, and looking forward into the future where bigger and bigger subnets will be available, the price formula should account for this quadratic growth in cost.

Part 2: Quantifying it in Cycles

Quantifying the resources such as CPU, network bandwidth, memory, etc., we upper-bound the cost for each node making an HTTPS outcall at:

3M + 60K * N + 400 * request_size + 800 * response_size cycles

(where N is the number of nodes in the subnet, request_size is the size of the request in bytes, and response_size is the size of the response in bytes, assuming no transformation is performed)

This formula has been obtained by experimenting with the feature on testnets of different sizes, with and without HTTPS outcalls being made, with different sizes of requests and responses, and by considering its theoretical properties of messages, e.g., number of messages sent and their sizes.

Plugging a subnet size of N=13 (and using max_response_bytes as an approximation for the actual response size) would yield the new formula shown above.

Part 3: What does that Mean?

The figure above shows the current and proposed new price of an HTTPS outcall on a 13-nodes subnet, with a request size of 1KB. Using the new price function, the price indeed drops significantly for all sizes of responses. The table below shows some sample prices in SDR cents:

Response size 1KB 100KB 1MB
Current price 0.6 10.5 100.5
Proposed price 0.06 1.09 10.45

We hope that the new price function would encourage more developers to use HTTPS outcalls in their canisters, to demonstrate the unparalleled power of the Internet Computer to the world.

We have not submitted a proposal for the suggested changes yet. We would be happy to have a discussion and the community’s feedback on this topic.

Best regards,
Yotam

29 Likes

This is a great first step! A 10x reduction does indeed make it more appealing. Has IPv4 been resolved? I think that is also a major blocker(even if it is theoretically more than practical).

2 Likes

IPv4 is not resolved yet. We have a path forward that would use NAT to provide IPv4 access to IC nodes, but (and this is my view only) it would take time until we get that working.

2 Likes

Oh yes I love this, I was developing my dapp for athe buidl hackaton but realized that it was too http outcall intensive to do it on the backend so I had to do it on the frontend, now i will rebuild it in motoko!

Great news

5 Likes

It seems to me like a standard machine learning exercise to find the best variables. How did you come up with your numbers and are they indeed the best? Do you see them change over time? Does it make sense to have it dynamically recalibrate itself without human intervention.

1 Like

@armsves was it too expensive because the responses you need are big? Are you aware of the role of the max_response_bytes argument?

1 Like

@integral_wizard It’s quite complex. We ran multiple experiments on testnets of different sizes to try and isolate the cost factors of HTTPS outcalls from other operations of a test canister. These are surely not “the best” numbers, but should be good enough. Dynamic pricing would be very hard to use, as developers would not be able to predict the cost of their code, but I am not ruling it out completely. The number may change over time, especially when we have bigger subnets. But we did try to account for that as well with the newly proposed formula.

1 Like

Not really big, around 10kb each, but for each try now i’d have minimum 5 calls (if all the data arrives correctly if not can be up to 50), but in the future each will be around 25 calls, so it wasn’t really an option, now it’s doable :slight_smile:

3 Likes

Please note: with the acceptance of proposal 123311, the new pricing for HTTPS outcalls is coming into force when the corresponding subnet is upgraded to the new elected replica version 7742d9. You can follow the upgrade proposals in order to know whether the new pricing is active on your subnet of interest (once the corresponding proposal is accepted and executed).

2 Likes

Just checking, is this live now?

Hi Jordan
On Governance - IC Dashboard you can see which version has been deployed on which subnet through executed proposals. I count 35 subnets which now have version 7742d96d, so all app subnets use the version with the updated prices now.

3 Likes

As of now, all subnets have been upgraded so the new price function is live on all subnets.

5 Likes

I ran some tests. One call to fetch prices from something like <api.coincap.io> costs around 0.02 XDR.
The cost of fetching prices for a year/ every minute will cost 10 368 XDR.
An oracle will probably need to fetch from at least 3 different locations, so multiply that by 3.
And once per minute isn’t really good for DeFi/high frequency trading, so perhaps 30 times a minute, would cost 933 120 XDR a year. I think you need to provide a discount for canisters making that many calls. For a lot of use cases, the current price will be pretty inexpensive, while for others relying on a lot of calls, too expensive.

You are planning to implement a feature where only one node will be making the request.
An aggregator would probably want to make ~20 of those to different APIs instead of 3 calls to 3 APIs. Perhaps the cost of each for a 13-node subnet will be 1/13 of the original cost?

Is it possible to have HTTP outcalls return the result from each node instead of trying to reach a consensus? Then the canister will receive something like [5.12, 5.13, 5.13, 5.13, 5.14…] and can run an algorithm on those to figure the ‘average’ result.

1 Like

Hi @infu !
For a single outcall to cost 0.02 XDR it means the max_response_bytes was about 200,000 bytes, which is relatively high, but of course depends on the content you are working with.
Unfortunately, HTTPS outcalls is a quite “expensive” feature in terms of the resources it requires. Especially if the responses are big. I am not sure high-frequency trading is the best use case for it. This is also due to the need for consensus, so you’d probably not want to fetch “current” prices but a bit older ones (e.g., a minute ago), so all nodes see the same timestamp.
Returning multiple results to the canister would mean even higher cost, since the payload that needs to go through consensus is larger eventually. We cannot “skip” consensus even in this case, as all these results need to end up in the replicated state.
If we implement a feature where only one node makes the request, it will indeed be much less expensive. For now, honestly, it is not clear when or whether we will actually implement such a feature. So far, it has not been requested much by the community.

BTW, are you aware of the Exchange Rate Canister?

4 Likes

I am trying to request https://api.kucoin.com/api/v1/market/orderbook/level1?symbol=BTC-USDT Doesn’t look like more than 2,000 bytes
and when trying with fewer cycles, I get this

Reject text: http_request request sent with 15_849_446_800 cycles, 
but 20_849_545_600 cycles are required.

Which is 0.02 XDR unless I am not calculating it properly. The subnet has 13 nodes https://dashboard.internetcomputer.org/subnet/4ecnw-byqwz-dtgss-ua2mh-pfvs7-c3lct-gtf4e-hnu75-j7eek-iifqm-sqe EDIT - these fine details seem to matter a lot here. I am not passing max_response_bytes and that’s where the high prices must come from.

I am aware of the Exchange Rate Canister and looked at its code to see how it works. Didn’t know it was in production tho, since I can’t get it to return a result. The requests take ~249sec and return an error. Tried ICP/USD, ICP/USDT, BTC/USDT with and without timestamps. Maybe I am entering the wrong inputs? EDIT - Looking closer at the page you sent, the problem is probably because I am not sending any cycles to the ERC canister and my calls get cut in a way that doesn’t provide an error, so it timeouts.

I am ok with not having the results from all nodes, but I think being able to use make outcalls only with one node will be a pretty great feature.

Edit - prices came down to 0.0004 XDR per call. Collecting the price from one API/every min will then cost 207 USD per year.

2 Likes

Indeed, despite being optional, max_response_bytes is super important. The price of the call must be set when it is executed, not after it completes. So we cannot make it charge based on the actual response size. We therefore charge based on the provided value for max_response_bytes, and if it’s not provided, the maximal size is assumed.

W.r.t the exchange rate canister, yes, cycles must be provided. I will look into the weird behavior you describe when cycles are not provided.

3 Likes

Hi @infu, w.r.t the problem you had when calling the XRC without cycles – did you use dfx for that? If so, you should probably upgrade it as it was a known bug in an older version.

I called it with https://dashboard.internetcomputer.org/ and also with our VScode extension Blast. Probably both using an anonymous principal

What is the current price function? The price function described in the OP seems very different than the one described in the documentation here: Paying for resources in cycles | Internet Computer

We are trying to calculate this automatically for users of Azle, but I’m having a hard time getting an accurate result.

Is this the current formula? Or from here: Paying for resources in cycles | Internet Computer

HTTPS outcalls: The cost for an HTTPS outcall is calculated using the formula (3_000_000 + 60_000 * n) * n for the base fee and 400 * n each request byte and 800 * n for each response byte, where n is the number of nodes in the subnet. These costs are included in the chart found below.