Replies: 4 comments 8 replies
-
Surely we don't need all the host functions to just have cross contract calls? As a side note, from a minimality perspective, implementing cross-contract calls is sufficient to have all host functions because any host functions not required for cross-contract calls could then be contracts on NEAR that could be called from Aurora. |
Beta Was this translation helpful? Give feedback.
-
Just out of pure curiosity, are there any contracts out there right now that are async on NEAR that you are aware of? |
Beta Was this translation helpful? Give feedback.
-
I'd like to add some more details to this proposal. I believe the following API (presented as Rust functions for clarity, but implemented as precompiles in the Engine) is sufficient to have Aurora -> Near interaction. It is based on the corresponding NEAR API, but slightly modified for our use case. /// Data required to make a call to a smart contract on NEAR
struct CallNearArgs {
account_id: String,
method: String,
/// It is up to the caller to know the ABI of the contract (e.g. borsh, json)
args: Bytes,
/// It is an open question as to where this NEAR should come from. I think the best thing
/// would be if we could use bridged wrapped NEAR.
near_amount: u128,
/// This is the amount of NEAR gas to attach to the promise.
gas_limit: u64,
}
/// Make an async call to a NEAR contract. As with the current exit
/// precompiles, the promise is actually scheduled after the EVM
/// transaction is completed, so the return value is a "dummy" value
/// used for internal bookkeeping (for referencing in callbacks/compositions).
/// The cost of this call in terms of EVM gas should depend on `call_data.gas_limit`
/// to prevent abusing this to run expensive NEAR computations for free via the relayer.
/// This is effectively "send and forget" communication with NEAR since the
/// outcome of the call has no impact on Aurora. See `callback_aurora` to see
/// how to make use of the return value of the call.
call_near(call_data: CallNearArgs) -> PromiseId
/// Chain together multiple NEAR calls. Note this is semantically different from
/// `call_near + callback_aurora + call_near` even though these might look
/// equivalent. The reason they are different is because `callback_aurora` creates
/// a dependency of the EVM transaction on the outcome of the NEAR contract
/// call, while `callback_near` does not.
callback_near(base: PromiseId, call_data: CallNearArgs) -> PromiseId
/// This is what is needed for the error handling of exit precompiles. This function allows
/// handling the result of a NEAR call from within the EVM (by making a contract call). More details below.
/// The `gas_limit` here is denominated in EVM gas; the cost of this call in terms of EVM
/// gas can be equal to this parameter (effectively you consume the gas now to have it
/// available to spend in the callback later).
callback_aurora(base: PromiseId, contract: Address, args: Bytes, eth_amount: U256, gas_limit: u64) -> PromiseId
/// Compose multiple promises into a single promise. This allows consuming
/// results from multiple async calls in a single callback. Example:
/// `let a = call_near(A); let b = call_near(B); let ab = promise_and(&[a, b]); callback_aurora(ab, ...);`
promise_and(ids: &[PromiseId]) -> PromiseId
/// Returns the number of results available from previous promises.
/// 0 in the case the execution is not in a callback; 1 if it is a callback from
/// a simple `call_near`; or more if it is a callback from a promise created by `promise_and`.
promise_results_count() -> usize
/// Get the ith promise result. Returns an error if the call failed, or if the index
/// is out of range.
promise_result(result_idx: usize) -> Result<Bytes, Error> The most semantically complex function is To enable the atomicity we will need to make changes to the way the Engine consumes transactions. If we do not make any change, then it would be possible for another transaction to change the state out from under another transaction waiting for a response from NEAR. To prevent this, I propose that a transaction which invokes There is a question here about what the user experience should be like when the contract is in this locked state. One option is that the contract simply returns an We must also enforce the condition that a transaction can only call It would be allowed to chain together multiple aync calls and atomic callbacks (i.e. the callback makes a new @mfornet @joshuajbouw @sept-en please review this and let me know what you think. This introduces a lot of complexity into the engine, but I think this proposal is minimal to meet the requirements of #269 while enabling generic NEAR calls from aurora. Once we approve a high level design then we can schedule implementation work. |
Beta Was this translation helpful? Give feedback.
-
There is a vulnerability if we allow arbitrary cross-contract calls. It will allow everyone to make cross contract call where predecessor_id is One potential solution for this particular problem, is going through a proxy contract (like The only automatic cross-contract call are made directly to Some fundamental issues with this approach are:
|
Beta Was this translation helpful? Give feedback.
-
Proposal to make cross contract calls from Aurora to any NEAR contract
tl;dr: Enable the same API that is currently available on NEAR blockchain, giving exactly the same guarantees (spoiler alert: async model).
Enable NEAR blockchain API
For each host function available on NEAR create a pre-compile with the same interface that allows calling the host function from the solidity code / EVM. This will allow the possibility of accessing the NEAR ecosystem from contracts deployed on Aurora.
Guarantees
NEAR uses an async model, while Aurora strive to have a sync environment. Because of this reason, the result (either success or failures) of this pre-compiles won't necessarily affect the result of the underlying Aurora transaction that initiated this call. This means that even if the cross contract call fails the transaction that executed this pre-compile might not fail.
Whenever possible, if the result of a host function call is perceived immediately after it was invoked (in a synchronous way), this can be handled in the Aurora Runtime. If the effect or result is only perceived in a separate block the result won't affect the execution of the underlying aurora transaction.
Security implications
Each host function needs to be analysed independently, since allowing access to some of the host functions may create some security vulnerabilities for
aurora
contract or are not required at all. One example of this ispromise_batch_action_create_account
which can create sub-accounts for the caller account.All pre-compiles are available at:
nearcore/runtime/near-vm-logic/src/logic.rs
Motivation
Having this available on Aurora will allow better integration between contracts that lives inside Aurora, and native NEAR contracts. Having this features, will remove the need of hard coding some integrations between Aurora and NEAR. Some are already implemented, like moving tokens from Aurora to NEAR ecosystem.
Main motivation for this features is to have cross-contract call available as a pre-compile.
Notice any use of these methods may break the sync nature of EVM contracts on Aurora, and this should be explicitly stated for users and developers making use of this feature.
promise_create
In the case of promise_create it is required to attach some gas, and in some cases some NEAR balance. These are concepts not available in the context of EVM contracts, however, it is responsibility of the developer that will make use of this feature to attach enough resources for making the contract call.
The same way Aurora provides an interface to pay the NEAR gas fees paying with ETH, a similar interface should be used to attach some NEAR tokens to the call. A promise_create that attaches a positive amount of NEAR tokens, will be using it from the attached deposit of the NEAR call and not from Aurora balance.
Async model from sync model
For promises that are scheduled from Aurora transactions, they are only scheduled after the end of the transaction, and in case they were not reverted first from inside the EVM runtime. See more about how withdrawing ERC20 from Aurora to NEAR is implemented.
NEAR API in Aurora
The API for host functions is very low level. Instead we can provide a higher level API, similar to the API existing in near-sdk-rs. I see two potential paths:
Priorities
While the proposal encourages to enable all (or several) host functions from aurora runtime, they don't need to be implemented at once. We can implement them on demand, encourage and mentor open source community members to contribute, and give bounties for some of them.
Beta Was this translation helpful? Give feedback.
All reactions