Skip to content

Commit

Permalink
feat: Implicit account creation (#3251)
Browse files Browse the repository at this point in the history
This PR implements near/NEPs#71

It contains a bunch of boilerplate code to pass the protocol version to VM and tests.

Logical changes:
- Bumps protocol version to 35
- Post protocol version, when a transfer action is executed on the 64-length hex like account ID as a single action in a transaction, it creates a new account if the account doesn't exist and add the ED25519 public key from the account ID hex representation.
- If a CreateAccount action attempts to create 64-length hex like account, it fails.
- Refunds don't automatically create accounts, because refunds are free and we don't want some type of abuse. NOTE: Account deletion with beneficiary creates a refund, so it'll not create a new account.
- `TransferAction` fee cost on such 64-len hex accounts include `CreateAction` and `AddFullAccessKey` costs
- The fee is also updated when calling a promise.
- `VMLogic::new` change is backward compatible until we release a new `near-sdk` version. So it shouldn't break master after pushing it.
- Bump `near-vm-*` versions to `2.2.0`.

TODO:

- [x] Add integration test for transfer from promise
- [x] Add migration test for transfer to 64len
- [x] Uncomment `VMLogic::new` for `near-sdk` `MockedBlockchain`

## Test plan:
- CI
- Added integration test and migration test
  • Loading branch information
Evgeny Kuzyakov authored and chefsale committed Sep 7, 2020
1 parent a60999f commit 0f96804
Show file tree
Hide file tree
Showing 51 changed files with 922 additions and 151 deletions.
24 changes: 16 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions chain/chain/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,7 @@ impl RuntimeAdapter for KeyValueRuntime {
_state_update: Option<StateRoot>,
_transaction: &SignedTransaction,
_verify_signature: bool,
_current_protocol_version: ProtocolVersion,
) -> Result<Option<InvalidTxError>, Error> {
Ok(None)
}
Expand All @@ -474,6 +475,7 @@ impl RuntimeAdapter for KeyValueRuntime {
_state_root: StateRoot,
transactions: &mut dyn PoolIterator,
_chain_validate: &mut dyn FnMut(&SignedTransaction) -> bool,
_current_protocol_version: ProtocolVersion,
) -> Result<Vec<SignedTransaction>, Error> {
let mut res = vec![];
while let Some(iter) = transactions.next() {
Expand Down
2 changes: 2 additions & 0 deletions chain/chain/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ pub trait RuntimeAdapter: Send + Sync {
state_root: Option<StateRoot>,
transaction: &SignedTransaction,
verify_signature: bool,
current_protocol_version: ProtocolVersion,
) -> Result<Option<InvalidTxError>, Error>;

/// Returns an ordered list of valid transactions from the pool up the given limits.
Expand All @@ -272,6 +273,7 @@ pub trait RuntimeAdapter: Send + Sync {
state_root: StateRoot,
pool_iterator: &mut dyn PoolIterator,
chain_validate: &mut dyn FnMut(&SignedTransaction) -> bool,
current_protocol_version: ProtocolVersion,
) -> Result<Vec<SignedTransaction>, Error>;

/// Verify validator signature for the given epoch.
Expand Down
58 changes: 33 additions & 25 deletions chain/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ impl Client {
.clone();

let prev_block_header = self.chain.get_block_header(&prev_block_hash)?.clone();
let transactions = self.prepare_transactions(shard_id, &chunk_extra, &prev_block_header);
let transactions = self.prepare_transactions(shard_id, &chunk_extra, &prev_block_header)?;
let num_filtered_transactions = transactions.len();
let (tx_root, _) = merklize(&transactions);
let ReceiptResponse(_, outgoing_receipts) = self.chain.get_outgoing_receipts_for_shard(
Expand Down Expand Up @@ -556,36 +556,40 @@ impl Client {
shard_id: ShardId,
chunk_extra: &ChunkExtra,
prev_block_header: &BlockHeader,
) -> Vec<SignedTransaction> {
) -> Result<Vec<SignedTransaction>, Error> {
let Self { chain, shards_mgr, runtime_adapter, .. } = self;

let next_epoch_id =
runtime_adapter.get_epoch_id_from_prev_block(&prev_block_header.hash())?;
let protocol_version = runtime_adapter.get_epoch_protocol_version(&next_epoch_id)?;

let transactions = if let Some(mut iter) = shards_mgr.get_pool_iterator(shard_id) {
let transaction_validity_period = chain.transaction_validity_period;
runtime_adapter
.prepare_transactions(
prev_block_header.gas_price(),
chunk_extra.gas_limit,
shard_id,
chunk_extra.state_root.clone(),
&mut iter,
&mut |tx: &SignedTransaction| -> bool {
chain
.mut_store()
.check_transaction_validity_period(
&prev_block_header,
&tx.transaction.block_hash,
transaction_validity_period,
)
.is_ok()
},
)
.expect("no StorageError please")
runtime_adapter.prepare_transactions(
prev_block_header.gas_price(),
chunk_extra.gas_limit,
shard_id,
chunk_extra.state_root.clone(),
&mut iter,
&mut |tx: &SignedTransaction| -> bool {
chain
.mut_store()
.check_transaction_validity_period(
&prev_block_header,
&tx.transaction.block_hash,
transaction_validity_period,
)
.is_ok()
},
protocol_version,
)?
} else {
vec![]
};
// Reintroduce valid transactions back to the pool. They will be removed when the chunk is
// included into the block.
shards_mgr.reintroduce_transactions(shard_id, &transactions);
transactions
Ok(transactions)
}

pub fn send_challenges(&mut self, challenges: Arc<RwLock<Vec<ChallengeBody>>>) {
Expand Down Expand Up @@ -1225,8 +1229,12 @@ impl Client {
let gas_price = cur_block_header.gas_price();
let epoch_id = self.runtime_adapter.get_epoch_id_from_prev_block(&head.last_block_hash)?;

if let Some(err) =
self.runtime_adapter.validate_tx(gas_price, None, &tx, true).expect("no storage errors")
let protocol_version = self.runtime_adapter.get_epoch_protocol_version(&epoch_id)?;

if let Some(err) = self
.runtime_adapter
.validate_tx(gas_price, None, &tx, true, protocol_version)
.expect("no storage errors")
{
debug!(target: "client", "Invalid tx during basic validation: {:?}", err);
return Ok(NetworkClientResponses::InvalidTx(err));
Expand All @@ -1252,7 +1260,7 @@ impl Client {
};
if let Some(err) = self
.runtime_adapter
.validate_tx(gas_price, Some(state_root), &tx, false)
.validate_tx(gas_price, Some(state_root), &tx, false, protocol_version)
.expect("no storage errors")
{
debug!(target: "client", "Invalid tx: {:?}", err);
Expand Down
10 changes: 9 additions & 1 deletion chain/jsonrpc/res/rpc_errors_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,8 @@
"TriesToStake",
"InsufficientStake",
"FunctionCallError",
"NewReceiptValidationError"
"NewReceiptValidationError",
"OnlyImplicitAccountCreationAllowed"
],
"props": {
"index": ""
Expand Down Expand Up @@ -718,6 +719,13 @@
"signer_id": ""
}
},
"OnlyImplicitAccountCreationAllowed": {
"name": "OnlyImplicitAccountCreationAllowed",
"subtypes": [],
"props": {
"account_id": ""
}
},
"ReceiptValidationError": {
"name": "ReceiptValidationError",
"subtypes": [
Expand Down
4 changes: 2 additions & 2 deletions core/crypto/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{InMemorySigner, Signature};

fn ed25519_key_pair_from_seed(seed: &str) -> ed25519_dalek::Keypair {
let seed_bytes = seed.as_bytes();
let len = seed_bytes.len();
let len = std::cmp::min(ed25519_dalek::SECRET_KEY_LENGTH, seed_bytes.len());
let mut seed: [u8; ed25519_dalek::SECRET_KEY_LENGTH] = [b' '; ed25519_dalek::SECRET_KEY_LENGTH];
seed[..len].copy_from_slice(&seed_bytes[..len]);
let secret = ed25519_dalek::SecretKey::from_bytes(&seed).unwrap();
Expand All @@ -17,7 +17,7 @@ fn ed25519_key_pair_from_seed(seed: &str) -> ed25519_dalek::Keypair {

fn secp256k1_secret_key_from_seed(seed: &str) -> secp256k1::key::SecretKey {
let seed_bytes = seed.as_bytes();
let len = seed_bytes.len();
let len = std::cmp::min(32, seed_bytes.len());
let mut seed: [u8; 32] = [b' '; 32];
seed[..len].copy_from_slice(&seed_bytes[..len]);
let mut rng: StdRng = rand::SeedableRng::from_seed(seed);
Expand Down
6 changes: 5 additions & 1 deletion core/primitives/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,9 @@ pub enum ActionErrorKind {
/// Error occurs when a new `ActionReceipt` created by the `FunctionCall` action fails
/// receipt validation.
NewReceiptValidationError(ReceiptValidationError),
/// Error occurs when a `CreateAccount` action is called on hex-characters account of length 64.
/// See implicit account creation NEP: https://github.com/nearprotocol/NEPs/pull/71
OnlyImplicitAccountCreationAllowed { account_id: AccountId },
}

impl From<ActionErrorKind> for ActionError {
Expand Down Expand Up @@ -670,7 +673,8 @@ impl Display for ActionErrorKind {
ActionErrorKind::NewReceiptValidationError(e) => {
write!(f, "An new action receipt created during a FunctionCall is not valid: {}", e)
}
ActionErrorKind::InsufficientStake { account_id, stake, minimum_stake } => write!(f, "Account {} tries to stake {} but minimum required stake is {}", account_id, stake, minimum_stake)
ActionErrorKind::InsufficientStake { account_id, stake, minimum_stake } => write!(f, "Account {} tries to stake {} but minimum required stake is {}", account_id, stake, minimum_stake),
ActionErrorKind::OnlyImplicitAccountCreationAllowed { account_id } => write!(f, "CreateAccount action is called on hex-characters account of length 64 {}", account_id)
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion core/primitives/src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub const DB_VERSION: DbVersion = 8;
pub type ProtocolVersion = u32;

/// Current latest version of the protocol.
pub const PROTOCOL_VERSION: ProtocolVersion = 34;
pub const PROTOCOL_VERSION: ProtocolVersion = 35;
/// Oldest supported version by this client.
pub const OLDEST_BACKWARD_COMPATIBLE_PROTOCOL_VERSION: ProtocolVersion = 29;

Expand All @@ -31,3 +31,6 @@ pub const MIN_GAS_PRICE_NEP_92_FIX: Balance = 100_000_000;
pub const MIN_PROTOCOL_VERSION_NEP_92_FIX: ProtocolVersion = 32;

pub const CORRECT_RANDOM_VALUE_PROTOCOL_VERSION: ProtocolVersion = 33;

/// See [NEP 71](https://github.com/nearprotocol/NEPs/pull/71)
pub const IMPLICIT_ACCOUNT_CREATION_PROTOCOL_VERSION: ProtocolVersion = 35;
4 changes: 2 additions & 2 deletions neard/res/genesis_config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"protocol_version": 34,
"protocol_version": 35,
"genesis_time": "1970-01-01T00:00:00.000000000Z",
"chain_id": "sample",
"genesis_height": 0,
Expand Down Expand Up @@ -236,4 +236,4 @@
"fishermen_threshold": "10000000000000000000000000",
"minimum_stake_divisor": 10,
"records": []
}
}
Loading

0 comments on commit 0f96804

Please sign in to comment.