Skip to content

Commit

Permalink
Draft implementation of DelegateAction (NEP-366)
Browse files Browse the repository at this point in the history
This is a draft implementation of NEP-366 (near/NEPs#366)

Not done yet:
* Need to implement DelegateAction for implicit accounts (nonce problem)
* Need new error codes for DelegateAction
* Implement Fees for DelegateAction
* Add tests
  • Loading branch information
Egor Ulesykiy committed Aug 29, 2022
1 parent 771bcff commit 77e24a8
Show file tree
Hide file tree
Showing 14 changed files with 325 additions and 36 deletions.
1 change: 1 addition & 0 deletions chain/chain/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,7 @@ impl RuntimeAdapter for KeyValueRuntime {
receipt_id: create_receipt_nonce(from.clone(), to.clone(), amount, nonce),
receipt: ReceiptEnum::Action(ActionReceipt {
signer_id: from.clone(),
publisher_id: None,
signer_public_key: PublicKey::empty(KeyType::ED25519),
gas_price,
output_data_receivers: vec![],
Expand Down
1 change: 1 addition & 0 deletions chain/rosetta-rpc/src/adapters/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ impl From<NearActions> for Vec<crate::models::Operation> {
);
operations.push(deploy_contract_operation);
}
near_primitives::transaction::Action::Delegate(_) => todo!(),
}
}
operations
Expand Down
30 changes: 29 additions & 1 deletion core/primitives/src/receipt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::borsh::maybestd::collections::HashMap;
use crate::hash::CryptoHash;
use crate::logging;
use crate::serialize::{option_base64_format, u128_dec_format_compatible};
use crate::transaction::{Action, TransferAction};
use crate::transaction::{Action, DelegateAction, TransferAction};
use crate::types::{AccountId, Balance, ShardId};

/// Receipts are used for a cross-shard communication.
Expand Down Expand Up @@ -52,6 +52,7 @@ impl Receipt {

receipt: ReceiptEnum::Action(ActionReceipt {
signer_id: "system".parse().unwrap(),
publisher_id: None,
signer_public_key: PublicKey::empty(KeyType::ED25519),
gas_price: 0,
output_data_receivers: vec![],
Expand Down Expand Up @@ -79,6 +80,7 @@ impl Receipt {

receipt: ReceiptEnum::Action(ActionReceipt {
signer_id: receiver_id.clone(),
publisher_id: None,
signer_public_key,
gas_price: 0,
output_data_receivers: vec![],
Expand All @@ -87,6 +89,30 @@ impl Receipt {
}),
}
}

pub fn new_delegate_actions(
publisher_id: &AccountId,
predecessor_id: &AccountId,
delegate_action: &DelegateAction,
public_key: &PublicKey,
gas_price: Balance,
) -> Self {
Receipt {
predecessor_id: predecessor_id.clone(),
receiver_id: delegate_action.reciever_id.clone(),
receipt_id: CryptoHash::default(),

receipt: ReceiptEnum::Action(ActionReceipt {
signer_id: predecessor_id.clone(),
publisher_id: Some(publisher_id.clone()),
signer_public_key: public_key.clone(),
gas_price: gas_price,
output_data_receivers: vec![],
input_data_ids: vec![],
actions: delegate_action.actions.clone(),
}),
}
}
}

/// Receipt could be either ActionReceipt or DataReceipt
Expand All @@ -103,6 +129,8 @@ pub enum ReceiptEnum {
pub struct ActionReceipt {
/// A signer of the original transaction
pub signer_id: AccountId,
/// A publisher's identifier in case of a delgated action
pub publisher_id: Option<AccountId>,
/// An access key which was used to sign the original transaction
pub signer_public_key: PublicKey,
/// A gas_price which has been used to buy gas in the original transaction
Expand Down
40 changes: 40 additions & 0 deletions core/primitives/src/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::borrow::Borrow;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::io::Error;

use borsh::{BorshDeserialize, BorshSerialize};
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -70,6 +71,7 @@ pub enum Action {
AddKey(AddKeyAction),
DeleteKey(DeleteKeyAction),
DeleteAccount(DeleteAccountAction),
Delegate(SignedDelegateAction),
}

impl Action {
Expand All @@ -83,6 +85,10 @@ impl Action {
match self {
Action::FunctionCall(a) => a.deposit,
Action::Transfer(a) => a.deposit,
Action::Delegate(a) => {
let delegate_action = a.get_delegate_action().unwrap();
delegate_action.deposit
}
_ => 0,
}
}
Expand Down Expand Up @@ -220,6 +226,40 @@ impl From<DeleteAccountAction> for Action {
}
}

#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))]
#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
pub struct DelegateAction {
pub reciever_id: AccountId,
pub deposit: Balance,
pub nonce: u64,
pub actions: Vec<Action>,
}
#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))]
#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, PartialEq, Eq, Clone, Debug)]
pub struct SignedDelegateAction {
// Borsh doesn't support recursive types. Therefore this field
// is deserialized to DelegateAction in runtime
pub delegate_action_serde: Vec<u8>,
pub public_key: PublicKey,
pub signature: Signature,
}

impl SignedDelegateAction {
pub fn get_delegate_action(&self) -> Result<DelegateAction, Error> {
DelegateAction::try_from_slice(&self.delegate_action_serde)
}

pub fn get_hash(&self) -> CryptoHash {
hash(&self.delegate_action_serde)
}
}

impl From<SignedDelegateAction> for Action {
fn from(delegate_action: SignedDelegateAction) -> Self {
Self::Delegate(delegate_action)
}
}

#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))]
#[derive(BorshSerialize, BorshDeserialize, Serialize, Deserialize, Eq, Debug, Clone)]
#[borsh_init(init)]
Expand Down
23 changes: 22 additions & 1 deletion core/primitives/src/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ use crate::sharding::{
use crate::transaction::{
Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction,
DeployContractAction, ExecutionMetadata, ExecutionOutcome, ExecutionOutcomeWithIdAndProof,
ExecutionStatus, FunctionCallAction, SignedTransaction, StakeAction, TransferAction,
ExecutionStatus, FunctionCallAction, SignedDelegateAction, SignedTransaction, StakeAction,
TransferAction,
};
use crate::types::{
AccountId, AccountWithPublicKey, Balance, BlockHeight, CompiledContractCache, EpochHeight,
Expand Down Expand Up @@ -870,6 +871,11 @@ pub enum ActionView {
DeleteAccount {
beneficiary_id: AccountId,
},
Delegate {
delegate_action_serde: Vec<u8>,
signature: Signature,
public_key: PublicKey,
},
}

impl From<Action> for ActionView {
Expand Down Expand Up @@ -897,6 +903,11 @@ impl From<Action> for ActionView {
Action::DeleteAccount(action) => {
ActionView::DeleteAccount { beneficiary_id: action.beneficiary_id }
}
Action::Delegate(action) => ActionView::Delegate {
delegate_action_serde: action.delegate_action_serde,
signature: action.signature,
public_key: action.public_key,
},
}
}
}
Expand Down Expand Up @@ -931,6 +942,15 @@ impl TryFrom<ActionView> for Action {
ActionView::DeleteAccount { beneficiary_id } => {
Action::DeleteAccount(DeleteAccountAction { beneficiary_id })
}
ActionView::Delegate {
delegate_action_serde: delegate_action,
signature,
public_key,
} => Action::Delegate(SignedDelegateAction {
delegate_action_serde: delegate_action,
signature,
public_key,
}),
})
}
}
Expand Down Expand Up @@ -1404,6 +1424,7 @@ impl TryFrom<ReceiptView> for Receipt {
actions,
} => ReceiptEnum::Action(ActionReceipt {
signer_id,
publisher_id: None,
signer_public_key,
gas_price,
output_data_receivers: output_data_receivers
Expand Down
1 change: 1 addition & 0 deletions genesis-tools/genesis-csv-to-json/src/csv_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ fn account_records(row: &Row, gas_price: Balance) -> Vec<StateRecord> {
receipt_id: hash(row.account_id.as_ref().as_bytes()),
receipt: ReceiptEnum::Action(ActionReceipt {
signer_id: row.account_id.clone(),
publisher_id: None,
// `signer_public_key` can be anything because the key checks are not applied when
// a transaction is already converted to a receipt.
signer_public_key: PublicKey::empty(KeyType::ED25519),
Expand Down
1 change: 1 addition & 0 deletions integration-tests/src/tests/standard_cases/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1418,6 +1418,7 @@ fn make_write_key_value_action(key: Vec<u64>, value: Vec<u64>) -> Action {
fn make_receipt(node: &impl Node, actions: Vec<Action>, receiver_id: AccountId) -> Receipt {
let receipt_enum = ReceiptEnum::Action(ActionReceipt {
signer_id: alice_account(),
publisher_id: None,
signer_public_key: node.signer().as_ref().public_key(),
gas_price: 0,
output_data_receivers: vec![],
Expand Down
50 changes: 48 additions & 2 deletions runtime/runtime/src/actions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use near_primitives::runtime::config::AccountCreationConfig;
use near_primitives::runtime::fees::RuntimeFeesConfig;
use near_primitives::transaction::{
Action, AddKeyAction, DeleteAccountAction, DeleteKeyAction, DeployContractAction,
FunctionCallAction, StakeAction, TransferAction,
FunctionCallAction, SignedDelegateAction, StakeAction, TransferAction,
};
use near_primitives::types::validator_stake::ValidatorStake;
use near_primitives::types::{AccountId, BlockHeight, EpochInfoProvider, TrieCacheMode};
Expand All @@ -30,6 +30,7 @@ use near_vm_errors::{
use near_vm_logic::types::PromiseResult;
use near_vm_logic::VMContext;

use crate::balance_checker::receipt_cost;
use crate::config::{safe_add_gas, RuntimeConfig};
use crate::ext::{ExternalError, RuntimeExt};
use crate::{ActionResult, ApplyState};
Expand Down Expand Up @@ -253,6 +254,7 @@ pub(crate) fn action_function_call(
receipt_id: CryptoHash::default(),
receipt: ReceiptEnum::Action(ActionReceipt {
signer_id: action_receipt.signer_id.clone(),
publisher_id: action_receipt.publisher_id.clone(),
signer_public_key: action_receipt.signer_public_key.clone(),
gas_price: action_receipt.gas_price,
output_data_receivers: receipt.output_data_receivers,
Expand Down Expand Up @@ -613,6 +615,46 @@ pub(crate) fn action_add_key(
Ok(())
}

pub(crate) fn action_delegate_action(
apply_state: &ApplyState,
action_receipt: &ActionReceipt,
predecessor_id: &AccountId,
signed_delegate_action: &SignedDelegateAction,
result: &mut ActionResult,
) -> Result<(), RuntimeError> {
match signed_delegate_action.get_delegate_action() {
Ok(delegate_action) => {
let new_receipt = Receipt::new_delegate_actions(
&action_receipt.signer_id,
predecessor_id,
&delegate_action,
&signed_delegate_action.public_key,
action_receipt.gas_price,
);

let transaction_costs = &apply_state.config.transaction_costs;
let current_protocol_version = apply_state.current_protocol_version;
let cost = receipt_cost(transaction_costs, &new_receipt, current_protocol_version)?;

if let Some(refund) = delegate_action.deposit.checked_sub(cost.clone()) {
let refund_receipt = Receipt::new_balance_refund(&action_receipt.signer_id, refund);

result.new_receipts.push(new_receipt);
result.new_receipts.push(refund_receipt);
} else {
result.result = Err(ActionErrorKind::LackBalanceForState {
account_id: action_receipt.signer_id.clone(),
amount: cost.clone(),
}
.into());
}
}
Err(_) => todo!(),
}

Ok(())
}

pub(crate) fn check_actor_permissions(
action: &Action,
account: &Option<Account>,
Expand Down Expand Up @@ -645,7 +687,10 @@ pub(crate) fn check_actor_permissions(
.into());
}
}
Action::CreateAccount(_) | Action::FunctionCall(_) | Action::Transfer(_) => (),
Action::CreateAccount(_)
| Action::FunctionCall(_)
| Action::Transfer(_)
| Action::Delegate(_) => (),
};
Ok(())
}
Expand Down Expand Up @@ -720,6 +765,7 @@ pub(crate) fn check_account_existence(
.into());
}
}
Action::Delegate(_) => (),
};
Ok(())
}
Expand Down
Loading

0 comments on commit 77e24a8

Please sign in to comment.