-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Updated with more GIPs to expand test sample
- Loading branch information
1 parent
2f21f6e
commit 29b8021
Showing
8 changed files
with
853 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
--- | ||
GIP: "0017" | ||
Title: Allow batching calls in Staking contract | ||
Authors: Ariel Barmat <[email protected]> | ||
Created: 2021-09-25 | ||
Stage: Candidate | ||
Implementations: https://github.com/graphprotocol/contracts/pull/475 | ||
--- | ||
|
||
# Abstract | ||
|
||
Expose a way to batch multiple calls into a single transaction. It provides great flexibility for indexer agents to combine multiple functions in different ways. It also reduce the gas cost by saving the initial gas, and in some cases, accessing a "used" slot by the other bundled transactions. | ||
|
||
# Specification | ||
|
||
A new contract called MultiCall is introduced, inspired by the one used by Uniswap. The payable keyword was removed from the `multicall()` as the protocol does not deal with ETH. Additionally, it is insecure in some instances if the contract relies on msg.value. | ||
|
||
The Staking contract inherits from MultiCall that expose a public `multicall(bytes[] calldata data)` function that receives an array of payloads to send to the contract itself. This allows to batch ANY publicly callable contract function. | ||
|
||
By using a multicall a user can batch an arbitrary number of operations into a single transaction. The indexer agent can combine close, open, claim transactions in any way it sees more effective. | ||
|
||
# Implementation | ||
|
||
See [@graphprotocol/contracts#475](https://github.com/graphprotocol/contracts/pull/475) | ||
|
||
# Backwards Compatibility | ||
|
||
The proposal introduces some breaking changes to save bytecode. | ||
|
||
The following functions are removed as they can be constructed using a | ||
multicall: | ||
|
||
- closeAllocationMany() | ||
- closeAndAllocate() | ||
- claimMany() | ||
|
||
# Validation | ||
|
||
### Audits | ||
|
||
The implementation was audited by Consensys Diligence. | ||
|
||
### Testnet | ||
|
||
The implementation has not yet been deployed to Testnet. | ||
|
||
# Copyright Waiver | ||
|
||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
--- | ||
GIP: "0005" | ||
Title: Gas costing for handlers | ||
Authors: Leonardo Yvens<[email protected]> and Zac Burns <[email protected]> | ||
Created: 2021-05-04 | ||
Stage: Candidate | ||
Implementations: https://github.com/graphprotocol/graph-node/pull/2414 | ||
--- | ||
|
||
## Abstract | ||
|
||
This GIP proposes that the computational cost of a handler be measured in units of gas and limited to a maximum cost. If a handler attempts to consume more gas than the maximum, the subgraph will fail with a deterministic error. This is necessary so that it is possible to prove that a subgraph cannot be indexed past the block which triggers such a handler. | ||
|
||
## Motivation | ||
|
||
Subgraph handlers that run for too long are an issue for operators, since they excessively consume resources, and also a issue for protocol security since they make the subgraph impossible to sync in a reasonable time, if it can be synced at all. Graph Node currently implements a timeout for handlers, which is useful to prevent excessive resource consumption, however timeouts are not deterministic. For protocol security, it is necessary to deterministically measure the cost of a handler. This GIP proposes gas costing for handlers, similar to the gas costing for transactions that exists in blockchain protocols. | ||
|
||
## Detailed Specification | ||
|
||
Gas is defined as the unit of execution cost. As a reference, one unit of gas should correspond to 0.1ns of execution time, making for 10 billion gas in a second. Close correlation with execution time is not a primary goal of gas costing, at least not in this first iteration. Still gas needs to have some correlation to execution time in order to be useful. | ||
|
||
The maximum gas cost for a handler should be sufficient for any reasonable subgraph, and be limited by the cost a Fisherman or Arbitrator would be willing to incur in order to check an attestation. The proposed limit is 1000 seconds of execution time, which corresponds to 10 trillion gas. | ||
|
||
The costs incurred by executing a handler can be categorized as either WASM execution costs or host function execution costs. WASM execution costs must be measured by injecting a callback at the block, see the documentation for this technique in the [pwasm-utils crate](https://docs.rs/pwasm-utils/0.17.1/pwasm_utils/fn.inject_gas_counter.html). For the gas costing of individual WASM instructions, refer to the implementation in [this commit](https://github.com/graphprotocol/graph-node/blob/30720a8f1fb074b71fe76729d4e0dc4ba3c3e955/runtime/wasm/src/gas_rules.rs). | ||
|
||
The cost of most host functions is calculated as a base cost added to a cost per byte of input. To | ||
account for host functions that have non-linear complexity, the input bytes are first combined | ||
through a complexity function and then multiplied by the cost per byte. To calculate the size of the | ||
input bytes, a `gas_size_of` function exists for each data type that is an input to a host | ||
function, see implementations of `GasSizeOf` in the reference implementation for details. Note that | ||
`gas_size_of` is an approximation of the true in-memory size of the data, the actual size may vary | ||
between platforms and implementations but still this approximation should be sufficient for gas | ||
costing purposes. | ||
|
||
#### Constants | ||
|
||
|
||
##### Table 1: | ||
|
||
| Constant | Value (in gas) | Description | | ||
| ---------------------- | ------------------------------------ | ------------------------------------------------------------ | | ||
| GAS_PER_SECOND | 10_000_000_000 | Relates time and gas units. | | ||
| MAX_GAS_PER_HANDLER | 3600 * GAS_PER_SECOND | The limit is one hour worth of gas. | | ||
| HOST_EXPORT_GAS | 10_000 | Cost of the callback that measures gas. | | ||
| DEFAULT_BASE_COST | 100_000 | Base cost of most host functions. | | ||
| DEFAULT_GAS_PER_BYTE | 1_000 | Equivalent to 10MB/s, so 36GB in the 1 hour limit. | | ||
| BIG_MATH_GAS_PER_BYTE | 100 | Equivalent to 100MB/s. | | ||
| ETHEREUM_CALL | 25_000_000_000 | Gas cost of a contract call. Allows for 400 contract calls. | | ||
| CREATE_DATA_SOURCE | MAX_GAS_PER_HANDLER / 100_000 | Cost of `dataSource.create`. | | ||
| LOG_GAS | MAX_GAS_PER_HANDLER / 100_000 | Base cost of `log.log`. | | ||
| STORE_SET_BASE_COST | MAX_GAS_PER_HANDLER / 250_000 | Base cost of `store.set`. | | ||
| STORE_SET_GAS_PER_BYTE | MAX_GAS_PER_HANDLER / 1_000_000_000 | Allows 1GB of entity data for `store.set`. | | ||
| STORE_GET_BASE_COST | MAX_GAS_PER_HANDLER / 10_000_000 | Base cost of `store.get`. | | ||
| STORE_GET_GAS_PER_BYTE | MAX_GAS_PER_HANDLER / 1_000_000_000 | Allows 10GB of entity data for `store.get`. | | ||
|
||
#### Costs for host functions | ||
|
||
This section specifies the costs attributed to each host function. | ||
|
||
Most host functions have linear complexity on a single parameter `N` and are costed as: | ||
|
||
```` | ||
DEFAULT_BASE_COST + gas_size_of(N)*DEFAULT_GAS_PER_BYTE | ||
```` | ||
|
||
Host functions that differ from this are listed in the table below: | ||
|
||
| Host function | Cost | | ||
| --------------------------- | ------------------------------------------------------------ | | ||
| abort | DEFAULT_BASE_COST | | ||
| store.set(key, data) | STORE_SET_BASE_COST + STORE_SET_GAS_PER_BYTE * (gas_size_of(key) + gas_size_of(data)) | | ||
| store.remove(key) | STORE_SET_BASE_COST + STORE_SET_GAS_PER_BYTE * gas_size_of(key) | | ||
| store.get(key) -> data | STORE_GET_BASE_COST + STORE_GET_GAS_PER_BYTE * (gas_size_of(key) + gas_size_of(data)) | | ||
| ethereum.call | ETHEREUM_CALL | | ||
| dataSource.create | CREATE_DATA_SOURCE | | ||
| log.log(m) | LOG_GAS + DEFAULT_GAS_PER_BYTE * gas_size_of(m) | | ||
| bigInt.plus(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * max(gas_size_of(x), gas_size_of(y)) | | ||
| bigInt.minus(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * max(gas_size_of(x), gas_size_of(y)) | | ||
| bigInt.times(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * gas_size_of(x) * gas_size_of(y) | | ||
| bigInt.divided_by(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * gas_size_of(x) * gas_size_of(y) | | ||
| bigInt.mod(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * gas_size_of(x) * gas_size_of(y) | | ||
| bigInt.pow(x, exp) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * gas_size_of(x) ^ exp | | ||
| bigInt.bit_or(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * max(gas_size_of(x), gas_size_of(y)) | | ||
| bigInt.bit_and(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * min(gas_size_of(x), gas_size_of(y)) | | ||
| bigDecimal.plus(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * (gas_size_of(x) + gas_size_of(y)) | | ||
| bigDecimal.minus(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * (gas_size_of(x) + gas_size_of(y)) | | ||
| bigDecimal.times(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * gas_size_of(x) * gas_size_of(y) | | ||
| bigDecimal.divided_by(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * gas_size_of(x) * gas_size_of(y) | | ||
| bigDecimal.equals(x, y) | DEFAULT_BASE_COST + BIG_MATH_GAS_PER_BYTE * min(gas_size_of(x), gas_size_of(y)) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
--- | ||
GIP: "0006" | ||
Title: Withdraw helper contract to support collecting funds from Vector channels. | ||
Authors: Ariel Barmat <[email protected]> | ||
Created: 2021-05-21 | ||
Stage: Candidate | ||
Implementations: https://github.com/graphprotocol/contracts/pull/454 | ||
--- | ||
|
||
# Abstract | ||
|
||
Consumers in the Graph Network set up state channels for sending query fees to Indexers using Vector. [Vector](https://github.com/connext/vector) is an ultra-simple, flexible state channel protocol implementation. This proposal introduces a contract required to encode custom logic in the process of withdrawing query fees from Vector state channels. The **GRTWithdrawHelper** contract receives query fees from a Vector state channel, then it forwards them to the **Staking** contract where they are distributed according to the protocol economics. | ||
|
||
# Motivation | ||
|
||
Vector state channels are contracts that hold funds used as collateral for consumer-to-indexer payments. When an Indexer closes an allocation, both parties sign a withdrawal commitment to transfer the query fees accumulated for the allocation back to the network. Vector, by default, supports plain transfers from the channel to any destination defined in the withdrawal commitment. In the case of the Graph Network, all funds should be collected by calling the collect() function in the Staking contract. For the purpose of calling custom logic when transferring from channels, Vector supports using a **WithdrawHelper** contract. In this proposal, we describe the implementation of such a contract for connecting a Vector withdrawal transfer to The Graph smart contracts. | ||
|
||
# Detailed Specification | ||
|
||
We created a contract called **GRTWithdrawHelper** that extends the base **WithdrawHelper** provided by the Vector framework. This contract has a function `execute(WithdrawData _wd, uint256 _actualAmount) external` that is called in the same transaction when the indexer withdraw funds from the channel. The execute function evaluates the validity of the `WithdrawData` struct, and then approve and transfer the received funds to the **Staking** contract by calling `collect()`. | ||
|
||
### Transaction Flow | ||
|
||
```sequence | ||
Channel->GRTWithdrawHelper: transfer(_actualAmount) | ||
Channel->GRTWithdrawHelper: execute(WithdrawData _wd, uint256 _actualAmount) | ||
GRTWithdrawHelper->Staking: approve(_actualAmount) | ||
GRTWithdrawHelper->Staking: collect(_actualAmount, allocationID) | ||
Staking->GRTWithdrawHelper: transferFrom(_actualAmount) | ||
``` | ||
|
||
## Features | ||
|
||
- The **GRTWithdrawHelper** is deployed with the token address that will be used for transfers, in our case it is the GRT token address. It can't be changed at later stage but this can easily be solved deploying a new contract if needed. | ||
- The contract expects to have available tokens in balance when `execute(WithdrawData calldata wd, uint256 actualAmount)` is called by the Channel. For that to happen the Channel needs to transfer funds to the **GRTWithdrawHelper** before execute is called. | ||
- On execute, the **GRTWithdrawHelper** will approve the funds to send to the Staking contract and then execute `collect()` passing the proper allocationID and amount. The Staking contract will then pull the tokens. | ||
- If the call to `collect()` fails, the funds are returned to a return address. This is important for Vector accounting of funds. | ||
|
||
## Requirements | ||
|
||
- Set the **GRTWithdrawHelper** contract as **AssetHolder** in the **Staking** contract. This way we allow the network contracts to accept funds from this contract. | ||
- The cooperative withdrawal commitments must have the following data (indicated with arrows): | ||
|
||
``` | ||
struct WithdrawData { | ||
address channelAddress; | ||
address assetId; // -> GRT token address | ||
address payable recipient; // -> GRTWithdrawHelper contract address | ||
uint256 amount; | ||
uint256 nonce; | ||
address callTo; // -> GRTWithdrawHelper contract address | ||
bytes callData; // -> CollectData struct defined below | ||
} | ||
``` | ||
|
||
``` | ||
struct CollectData { | ||
address staking; // -> Staking contract address | ||
address allocationID; // -> AllocationID to send collected funds | ||
address returnAddress; // -> Address where to return funds in case of errors | ||
} | ||
``` | ||
|
||
The parties must agree and sign the WithdrawalCommitment with that information to achieve proper execution of the withdrawals. | ||
|
||
## Exceptions | ||
|
||
The following conditions will result in reverts of the execution: | ||
|
||
- `assetId` in `WithdrawData` not matching the token address configured in the **GRTWithdrawHelper**. | ||
- **GRTWithdrawHelper** not having enough balance to transfer the **Staking** contract as stated in `actualAmount`. | ||
- Setting an invalid staking contract address. | ||
|
||
Note: To avoid unexpected conditions in the Vector off-chain logic, it is encouraged that the parties verify the WithdrawData does not revert by doing a call/estimateGas. | ||
|
||
## Handled Exceptions | ||
|
||
These reverts are handled and will make the contract to return the funds to the return address. | ||
|
||
- Doing a withdrawal for a non-existing `allocationID` as defined in `CollectData` when calling `collect()`. | ||
|
||
## Assumptions | ||
|
||
- This contract does not know if a `WithdrawalCommitment` is correct, it just accepts a transfer and `WithdrawData` that defines a proper destination. | ||
|
||
- A properly formatted `WithdrawData` can be crafted to send outside funds to the **Staking** contract. In that case, the person will get those funds distributed with Curators + Delegators + taxed a protocol percentage. There is no rational economic incentive to do so. | ||
|
||
# Backwards Compatibility | ||
|
||
The contract in this proposal is new and peripheral to the rest of the network contracts. No breaking change is introduced. The Graph Council will need to set the **GRTWithdrawHelper** as an **AssetHolder** in the Staking contract. | ||
|
||
# Validation | ||
|
||
## Audits | ||
|
||
An audit of the changes described in this document was performed by ChainSafe (see [`assets/gip-0006/TheGraph-GRTWithdrawHelper-Audit.pdf`](../assets/gip-0006/TheGraph-GRTWithdrawHelper-Audit.pdf)). | ||
|
||
## Testnet | ||
|
||
The implementation was deployed and tested on Rinkeby testnet. https://rinkeby.etherscan.io/address/0xe5fa88135c992a385aaa1c65a0c1b8ff3fde1fd4 | ||
|
||
# Copyright Waiver | ||
|
||
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). |
Oops, something went wrong.