Skip to content
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

Feat: Exit and XCC promises are sequential #868

Merged
merged 2 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 134 additions & 110 deletions engine-sdk/src/near_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,118 @@ impl Runtime {
);
}
}

#[allow(clippy::too_many_lines)]
unsafe fn append_batch_actions(id: u64, args: &PromiseBatchAction) {
for action in &args.actions {
match action {
PromiseAction::CreateAccount => {
exports::promise_batch_action_create_account(id);
}
PromiseAction::Transfer { amount } => {
let amount = amount.as_u128();
let amount_addr = core::ptr::addr_of!(amount);
exports::promise_batch_action_transfer(id, amount_addr as _);
}
PromiseAction::DeployContract { code } => {
let code = code.as_slice();
exports::promise_batch_action_deploy_contract(
id,
code.len() as _,
code.as_ptr() as _,
);
}
PromiseAction::FunctionCall {
name,
gas,
attached_yocto,
args,
} => {
let method_name = name.as_bytes();
let arguments = args.as_slice();
let amount = attached_yocto.as_u128();
let amount_addr = core::ptr::addr_of!(amount);
exports::promise_batch_action_function_call(
id,
method_name.len() as _,
method_name.as_ptr() as _,
arguments.len() as _,
arguments.as_ptr() as _,
amount_addr as _,
gas.as_u64(),
);
}
PromiseAction::Stake { amount, public_key } => {
feature_gated!("all-promise-actions", {
let amount = amount.as_u128();
let amount_addr = core::ptr::addr_of!(amount);
let pk: RawPublicKey = public_key.into();
let pk_bytes = pk.as_bytes();
exports::promise_batch_action_stake(
id,
amount_addr as _,
pk_bytes.len() as _,
pk_bytes.as_ptr() as _,
);
});
}
PromiseAction::AddFullAccessKey { public_key, nonce } => {
let pk: RawPublicKey = public_key.into();
let pk_bytes = pk.as_bytes();
exports::promise_batch_action_add_key_with_full_access(
id,
pk_bytes.len() as _,
pk_bytes.as_ptr() as _,
*nonce,
);
}
PromiseAction::AddFunctionCallKey {
public_key,
nonce,
allowance,
receiver_id,
function_names,
} => {
let pk: RawPublicKey = public_key.into();
let pk_bytes = pk.as_bytes();
let allowance = allowance.as_u128();
let allowance_addr = core::ptr::addr_of!(allowance);
let receiver_id = receiver_id.as_bytes();
let function_names = function_names.as_bytes();
exports::promise_batch_action_add_key_with_function_call(
id,
pk_bytes.len() as _,
pk_bytes.as_ptr() as _,
*nonce,
allowance_addr as _,
receiver_id.len() as _,
receiver_id.as_ptr() as _,
function_names.len() as _,
function_names.as_ptr() as _,
);
}
PromiseAction::DeleteKey { public_key } => {
let pk: RawPublicKey = public_key.into();
let pk_bytes = pk.as_bytes();
exports::promise_batch_action_delete_key(
id,
pk_bytes.len() as _,
pk_bytes.as_ptr() as _,
);
}
PromiseAction::DeleteAccount { beneficiary_id } => {
feature_gated!("all-promise-actions", {
let beneficiary_id = beneficiary_id.as_bytes();
exports::promise_batch_action_delete_key(
id,
beneficiary_id.len() as _,
beneficiary_id.as_ptr() as _,
);
});
}
}
}
}
}

impl StorageIntermediate for RegisterIndex {
Expand Down Expand Up @@ -350,120 +462,28 @@ impl crate::promise::PromiseHandler for Runtime {
PromiseId::new(id)
}

#[allow(clippy::too_many_lines)]
unsafe fn promise_create_batch(&mut self, args: &PromiseBatchAction) -> PromiseId {
let account_id = args.target_account_id.as_bytes();

let id = { exports::promise_batch_create(account_id.len() as _, account_id.as_ptr() as _) };

for action in &args.actions {
match action {
PromiseAction::CreateAccount => {
exports::promise_batch_action_create_account(id);
}
PromiseAction::Transfer { amount } => {
let amount = amount.as_u128();
let amount_addr = core::ptr::addr_of!(amount);
exports::promise_batch_action_transfer(id, amount_addr as _);
}
PromiseAction::DeployContract { code } => {
let code = code.as_slice();
exports::promise_batch_action_deploy_contract(
id,
code.len() as _,
code.as_ptr() as _,
);
}
PromiseAction::FunctionCall {
name,
gas,
attached_yocto,
args,
} => {
let method_name = name.as_bytes();
let arguments = args.as_slice();
let amount = attached_yocto.as_u128();
let amount_addr = core::ptr::addr_of!(amount);
exports::promise_batch_action_function_call(
id,
method_name.len() as _,
method_name.as_ptr() as _,
arguments.len() as _,
arguments.as_ptr() as _,
amount_addr as _,
gas.as_u64(),
);
}
PromiseAction::Stake { amount, public_key } => {
feature_gated!("all-promise-actions", {
let amount = amount.as_u128();
let amount_addr = core::ptr::addr_of!(amount);
let pk: RawPublicKey = public_key.into();
let pk_bytes = pk.as_bytes();
exports::promise_batch_action_stake(
id,
amount_addr as _,
pk_bytes.len() as _,
pk_bytes.as_ptr() as _,
);
});
}
PromiseAction::AddFullAccessKey { public_key, nonce } => {
let pk: RawPublicKey = public_key.into();
let pk_bytes = pk.as_bytes();
exports::promise_batch_action_add_key_with_full_access(
id,
pk_bytes.len() as _,
pk_bytes.as_ptr() as _,
*nonce,
);
}
PromiseAction::AddFunctionCallKey {
public_key,
nonce,
allowance,
receiver_id,
function_names,
} => {
let pk: RawPublicKey = public_key.into();
let pk_bytes = pk.as_bytes();
let allowance = allowance.as_u128();
let allowance_addr = core::ptr::addr_of!(allowance);
let receiver_id = receiver_id.as_bytes();
let function_names = function_names.as_bytes();
exports::promise_batch_action_add_key_with_function_call(
id,
pk_bytes.len() as _,
pk_bytes.as_ptr() as _,
*nonce,
allowance_addr as _,
receiver_id.len() as _,
receiver_id.as_ptr() as _,
function_names.len() as _,
function_names.as_ptr() as _,
);
}
PromiseAction::DeleteKey { public_key } => {
let pk: RawPublicKey = public_key.into();
let pk_bytes = pk.as_bytes();
exports::promise_batch_action_delete_key(
id,
pk_bytes.len() as _,
pk_bytes.as_ptr() as _,
);
}
PromiseAction::DeleteAccount { beneficiary_id } => {
feature_gated!("all-promise-actions", {
let beneficiary_id = beneficiary_id.as_bytes();
exports::promise_batch_action_delete_key(
id,
beneficiary_id.len() as _,
beneficiary_id.as_ptr() as _,
);
});
}
}
}
Self::append_batch_actions(id, args);

PromiseId::new(id)
}

unsafe fn promise_attach_batch_callback(
&mut self,
base: PromiseId,
args: &PromiseBatchAction,
) -> PromiseId {
let account_id = args.target_account_id.as_bytes();

let id = {
exports::promise_batch_then(base.raw(), account_id.len() as _, account_id.as_ptr() as _)
};

Self::append_batch_actions(id, args);

PromiseId::new(id)
}
Expand Down Expand Up @@ -658,7 +678,11 @@ pub(crate) mod exports {
) -> u64;
pub(crate) fn promise_and(promise_idx_ptr: u64, promise_idx_count: u64) -> u64;
pub(crate) fn promise_batch_create(account_id_len: u64, account_id_ptr: u64) -> u64;
fn promise_batch_then(promise_index: u64, account_id_len: u64, account_id_ptr: u64) -> u64;
pub(crate) fn promise_batch_then(
promise_index: u64,
account_id_len: u64,
account_id_ptr: u64,
) -> u64;
// #######################
// # Promise API actions #
// #######################
Expand Down
18 changes: 18 additions & 0 deletions engine-sdk/src/promise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ pub trait PromiseHandler {
/// code or adding/removing access keys.
unsafe fn promise_create_batch(&mut self, args: &PromiseBatchAction) -> PromiseId;

/// # Safety
/// See note on `promise_create_call`. Promise batches in particular must be used very
/// carefully because they can take destructive actions such as deploying new contract
/// code or adding/removing access keys.
unsafe fn promise_attach_batch_callback(
&mut self,
base: PromiseId,
args: &PromiseBatchAction,
) -> PromiseId;

fn promise_return(&mut self, promise: PromiseId);

/// # Safety
Expand Down Expand Up @@ -132,6 +142,14 @@ impl PromiseHandler for Noop {
PromiseId::new(0)
}

unsafe fn promise_attach_batch_callback(
&mut self,
_base: PromiseId,
_args: &PromiseBatchAction,
) -> PromiseId {
PromiseId::new(0)
}

fn promise_return(&mut self, _promise: PromiseId) {}

fn read_only(&self) -> Self::ReadOnly {
Expand Down
8 changes: 8 additions & 0 deletions engine-standalone-storage/src/promise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,14 @@ impl<'a> PromiseHandler for NoScheduler<'a> {
PromiseId::new(0)
}

unsafe fn promise_attach_batch_callback(
&mut self,
_base: PromiseId,
_args: &PromiseBatchAction,
) -> PromiseId {
PromiseId::new(0)
}

fn promise_return(&mut self, _promise: PromiseId) {}

fn read_only(&self) -> Self::ReadOnly {
Expand Down
33 changes: 33 additions & 0 deletions engine-test-doubles/src/promise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,22 @@ impl PromiseTracker {
self.internal_index += 1;
id
}

fn remove_as_near_promise(&mut self, id: u64) -> Option<NearPromise> {
let result = match self.scheduled_promises.remove(&id)? {
PromiseArgs::Batch(x) => NearPromise::Simple(SimpleNearPromise::Batch(x)),
PromiseArgs::Create(x) => NearPromise::Simple(SimpleNearPromise::Create(x)),
PromiseArgs::Recursive(x) => x,
PromiseArgs::Callback { base, callback } => {
let base_promise = self.remove_as_near_promise(base.raw())?;
NearPromise::Then {
base: Box::new(base_promise),
callback: SimpleNearPromise::Create(callback),
}
}
};
Some(result)
}
}

impl PromiseHandler for PromiseTracker {
Expand Down Expand Up @@ -91,6 +107,23 @@ impl PromiseHandler for PromiseTracker {
PromiseId::new(id)
}

unsafe fn promise_attach_batch_callback(
&mut self,
base: PromiseId,
args: &PromiseBatchAction,
) -> PromiseId {
let id = self.take_id();
let base_promise = self
.remove_as_near_promise(base.raw())
.expect("Base promise id must be known");
let new_promise = PromiseArgs::Recursive(NearPromise::Then {
base: Box::new(base_promise),
callback: SimpleNearPromise::Batch(args.clone()),
});
self.scheduled_promises.insert(id, new_promise);
PromiseId::new(id)
}

fn promise_return(&mut self, promise: PromiseId) {
self.returned_promise = Some(promise);
}
Expand Down
Loading
Loading