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

[Test] contract staking test contract #4976

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
17 changes: 17 additions & 0 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ CARGO := $(CARGO) $(CARGO_OPTS)
DISABLE_LOGGING = RUST_LOG=MatchesNothing

# Rust Contracts
ALL_CONTRACTS = $(shell find ./smart_contracts/contracts/[!.]* -not -path "./smart_contracts/contracts/vm2*" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
ALL_CONTRACTS = $(shell find ./smart_contracts/contracts/[!.]* -mindepth 1 -maxdepth 1 -not -path "./smart_contracts/contracts/vm2*" -type d -exec basename {} \;)
CLIENT_CONTRACTS = $(shell find ./smart_contracts/contracts/client -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
CARGO_HOME_REMAP = $(if $(CARGO_HOME),$(CARGO_HOME),$(HOME)/.cargo)
RUSTC_FLAGS = "--remap-path-prefix=$(CARGO_HOME_REMAP)=/home/cargo --remap-path-prefix=$$PWD=/dir"
Expand Down
20 changes: 17 additions & 3 deletions execution_engine/src/runtime/auction_internal.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::collections::BTreeSet;
use tracing::error;
use tracing::{debug, error};

use casper_storage::{
global_state::{error::Error as GlobalStateError, state::StateReader},
Expand Down Expand Up @@ -513,11 +513,25 @@ where
fn get_main_purse(&self) -> Result<URef, Error> {
// NOTE: this is used by the system and is not (and should not be made to be) accessible
// from userland.
Runtime::context(self)
match Runtime::context(self)
.runtime_footprint()
.borrow()
.main_purse()
.ok_or(Error::InvalidContext)
{
None => {
debug!("runtime attempt to access non-existent main purse");
Err(Error::InvalidContext)
}
Some(purse) => Ok(purse),
}
}

/// Set main purse.
fn set_main_purse(&mut self, purse: URef) {
Runtime::context(self)
.runtime_footprint()
.borrow_mut()
.set_main_purse(purse);
}
}

Expand Down
46 changes: 34 additions & 12 deletions execution_engine/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use std::{

use casper_wasm::elements::Module;
use casper_wasmi::{MemoryRef, Trap, TrapCode};
use tracing::error;
use tracing::{debug, error};

#[cfg(feature = "test-support")]
use casper_wasmi::RuntimeValue;
Expand Down Expand Up @@ -61,7 +61,7 @@ use casper_types::{
EntityKind, EntityVersion, EntityVersionKey, EntityVersions, Gas, GrantedAccess, Group, Groups,
HashAddr, HostFunction, HostFunctionCost, InitiatorAddr, Key, NamedArg, Package, PackageHash,
PackageStatus, Phase, PublicKey, RuntimeArgs, RuntimeFootprint, StoredValue,
TransactionRuntime, Transfer, TransferResult, TransferV2, TransferredTo, URef, URefAddr,
TransactionRuntime, Transfer, TransferResult, TransferV2, TransferredTo, URef,
DICTIONARY_ITEM_KEY_MAX_LENGTH, U512,
};

Expand Down Expand Up @@ -1082,11 +1082,16 @@ where
match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
Ok(pk) => DelegatorKind::PublicKey(pk),
Err(_) => {
let purse: URefAddr = Self::get_named_argument(
match Self::get_named_argument(
runtime_args,
auction::ARG_DELEGATOR_PURSE,
)?;
DelegatorKind::Purse(purse)
) {
Ok(addr) => DelegatorKind::Purse(addr),
Err(err) => {
debug!(%err, "failed to get delegator purse argument");
return Err(err);
}
}
}
}
};
Expand All @@ -1110,11 +1115,16 @@ where
match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
Ok(pk) => DelegatorKind::PublicKey(pk),
Err(_) => {
let purse: URefAddr = Self::get_named_argument(
match Self::get_named_argument(
runtime_args,
auction::ARG_DELEGATOR_PURSE,
)?;
DelegatorKind::Purse(purse)
) {
Ok(addr) => DelegatorKind::Purse(addr),
Err(err) => {
debug!(%err, "failed to get delegator purse argument");
return Err(err);
}
}
}
}
};
Expand All @@ -1135,11 +1145,16 @@ where
match Self::get_named_argument(runtime_args, auction::ARG_DELEGATOR) {
Ok(pk) => DelegatorKind::PublicKey(pk),
Err(_) => {
let purse: URefAddr = Self::get_named_argument(
match Self::get_named_argument(
runtime_args,
auction::ARG_DELEGATOR_PURSE,
)?;
DelegatorKind::Purse(purse)
) {
Ok(addr) => DelegatorKind::Purse(addr),
Err(err) => {
debug!(%err, "failed to get delegator purse argument");
return Err(err);
}
}
}
}
};
Expand Down Expand Up @@ -1852,10 +1867,17 @@ where
);
}
SystemEntityType::Auction => {
let mut combined_access_rights = self
.context
.runtime_footprint()
.borrow()
.extract_access_rights(context_entity_hash);
combined_access_rights.extend_access_rights(access_rights.take_access_rights());

return self.call_host_auction(
entry_point_name,
&runtime_args,
access_rights,
combined_access_rights,
stack,
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
use casper_engine_test_support::LmdbWasmTestBuilder;
use casper_engine_test_support::{
ExecuteRequestBuilder, LmdbWasmTestBuilder, DEFAULT_ACCOUNT_ADDR,
DEFAULT_GENESIS_TIMESTAMP_MILLIS, DEFAULT_PROPOSER_PUBLIC_KEY, LOCAL_GENESIS_REQUEST,
};
use casper_types::{
system::auction::{BidsExt, DelegatorKind, EraInfo, ValidatorBid},
Key, PublicKey, U512,
runtime_args,
system::auction::{
BidAddr, BidKind, BidsExt, DelegationRate, DelegatorKind, EraInfo, ValidatorBid,
ARG_AMOUNT, ARG_VALIDATOR,
},
GenesisValidator, Key, Motes, PublicKey, StoredValue, U512,
};
use num_traits::Zero;

const STORED_STAKING_CONTRACT_NAME: &str = "staking_stored.wasm";

mod bids;
mod distribute;
Expand Down Expand Up @@ -40,3 +50,192 @@ pub fn get_era_info(builder: &mut LmdbWasmTestBuilder) -> EraInfo {
.cloned()
.expect("should be era info")
}

#[ignore]
#[test]
fn should_support_contract_staking() {
let timestamp_millis = DEFAULT_GENESIS_TIMESTAMP_MILLIS;
let purse_name = "staking_purse".to_string();
let contract_name = "staking".to_string();
let stake = "stake".to_string();
let unstake = "unstake".to_string();
let account = *DEFAULT_ACCOUNT_ADDR;
let seed_amount = U512::from(10_000_000_000_000_000_u64);
let delegate_amount = U512::from(5_000_000_000_000_000_u64);
let validator_pk = &*DEFAULT_PROPOSER_PUBLIC_KEY;

let mut builder = LmdbWasmTestBuilder::default();
let mut genesis_request = LOCAL_GENESIS_REQUEST.clone();
genesis_request.set_enable_entity(false);

genesis_request.push_genesis_validator(
validator_pk,
GenesisValidator::new(
Motes::new(10_000_000_000_000_000_u64),
DelegationRate::zero(),
),
);
builder.run_genesis(genesis_request);

for _ in 0..=builder.get_auction_delay() {
// crank era
builder.run_auction(timestamp_millis, vec![]);
}

let account_main_purse = builder
.get_entity_with_named_keys_by_account_hash(account)
.expect("should have account")
.main_purse();
let starting_account_balance = builder.get_purse_balance(account_main_purse);

builder
.exec(
ExecuteRequestBuilder::standard(
account,
STORED_STAKING_CONTRACT_NAME,
runtime_args! {ARG_AMOUNT => seed_amount},
)
.build(),
)
.commit()
.expect_success();

let default_account = builder
.get_entity_with_named_keys_by_account_hash(account)
.expect("should have account");
let named_keys = default_account.named_keys();

let contract_purse = named_keys
.get(&purse_name)
.expect("purse_name key should exist")
.into_uref()
.expect("should be a uref");

let post_install_account_balance = builder.get_purse_balance(account_main_purse);
assert_eq!(
post_install_account_balance,
starting_account_balance.saturating_sub(seed_amount),
"post install should be reduced due to seeding contract purse"
);

let pre_delegation_balance = builder.get_purse_balance(contract_purse);
assert_eq!(pre_delegation_balance, seed_amount);

// stake from contract
builder
.exec(
ExecuteRequestBuilder::contract_call_by_name(
account,
&contract_name,
&stake,
runtime_args! {
ARG_AMOUNT => delegate_amount,
ARG_VALIDATOR => validator_pk.clone(),
},
)
.build(),
)
.commit()
.expect_success();

let post_delegation_balance = builder.get_purse_balance(contract_purse);
assert_eq!(
post_delegation_balance,
pre_delegation_balance.saturating_sub(delegate_amount),
"contract purse balance should be reduced by staked amount"
);

let delegation_key = Key::BidAddr(BidAddr::DelegatedPurse {
validator: validator_pk.to_account_hash(),
delegator: contract_purse.addr(),
});

if let StoredValue::BidKind(BidKind::Delegator(delegator)) = builder
.query(None, delegation_key, &[])
.expect("should have delegation bid")
{
assert_eq!(
delegator.staked_amount(),
delegate_amount,
"staked amount should match delegation amount"
);
}

for _ in 0..=10 {
// crank era
builder.run_auction(timestamp_millis, vec![]);
}

let increased_delegate_amount = if let StoredValue::BidKind(BidKind::Delegator(delegator)) =
builder
.query(None, delegation_key, &[])
.expect("should have delegation bid")
{
// assert_ne!(
// delegator.staked_amount(),
// delegate_amount,
// "staked amount should execeed delegation amount due to rewards"
// );
delegator.staked_amount()
} else {
U512::zero()
};

// unstake from contract
builder
.exec(
ExecuteRequestBuilder::contract_call_by_name(
account,
&contract_name,
&unstake,
runtime_args! {
ARG_AMOUNT => increased_delegate_amount,
ARG_VALIDATOR => validator_pk.clone(),
},
)
.build(),
)
.commit()
.expect_success();

assert!(
builder.query(None, delegation_key, &[]).is_err(),
"delegation record should be removed"
);

assert_eq!(
post_delegation_balance,
builder.get_purse_balance(contract_purse),
"at this point, unstaked token has not been returned"
);

let unbond_key = Key::BidAddr(BidAddr::UnbondPurse {
validator: validator_pk.to_account_hash(),
unbonder: contract_purse.addr(),
});
let unbonded_amount = if let StoredValue::BidKind(BidKind::Unbond(unbond)) = builder
.query(None, unbond_key, &[])
.expect("should have unbond")
{
let unbond_era = unbond.eras().first().expect("should have an era entry");
assert_eq!(
*unbond_era.amount(),
increased_delegate_amount,
"unbonded amount should match expectations"
);
*unbond_era.amount()
} else {
U512::zero()
};

for _ in 0..=10 {
// crank era
builder.run_auction(timestamp_millis, vec![]);
}

assert_eq!(
delegate_amount.saturating_add(unbonded_amount),
builder.get_purse_balance(contract_purse),
"unbonded amount should be available to contract staking purse"
);
}
17 changes: 17 additions & 0 deletions smart_contracts/contracts/test/staking-stored/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[package]
name = "staking-stored"
version = "0.1.0"
authors = ["Ed Hastings <ed@casper.network>"]
edition = "2021"

[[bin]]
name = "staking_stored"
path = "src/main.rs"
bench = false
doctest = false
test = false

[dependencies]
casper-contract = { path = "../../../contract" }
staking = { path = "../staking" }
casper-types = { path = "../../../../types" }
Loading
Loading