Skip to content

Commit eb39f47

Browse files
authored
Merge pull request #6261 from rdeioris/feat/sip_031_boot_contract
Feat/sip 031 boot contract
2 parents 644c1c1 + f79b505 commit eb39f47

File tree

4 files changed

+255
-5
lines changed

4 files changed

+255
-5
lines changed

stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 156 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ use crate::{tests, BitcoinRegtestController, BurnchainController, Config, Config
129129
pub static POX_4_DEFAULT_STACKER_BALANCE: u64 = 100_000_000_000_000;
130130
pub static POX_4_DEFAULT_STACKER_STX_AMT: u128 = 99_000_000_000_000;
131131

132+
use clarity::vm::database::STXBalance;
133+
use stacks::chainstate::stacks::boot::SIP_031_NAME;
134+
use stacks::clarity_vm::clarity::SIP_031_INITIAL_MINT;
135+
132136
use crate::clarity::vm::clarity::ClarityConnection;
133137

134138
lazy_static! {
@@ -12635,7 +12639,7 @@ fn write_signer_update(
1263512639
/// Test SIP-031 activation
1263612640
///
1263712641
/// - check epoch 3.2 is active
12638-
/// - TODO: check sip031 boot contract has a balance of 200_000_000 STX (TODO)
12642+
/// - check sip031 boot contract has a balance of 200_000_000 STX
1263912643
#[test]
1264012644
#[ignore]
1264112645
fn test_sip_031_activation() {
@@ -12717,7 +12721,29 @@ fn test_sip_031_activation() {
1271712721

1271812722
wait_for_first_naka_block_commit(60, &commits_submitted);
1271912723

12720-
// mine until epooch 3.2 height
12724+
// retrieve current liquidity
12725+
let last_block_id = StacksBlockId::from_hex(
12726+
&test_observer::get_blocks()
12727+
.last()
12728+
.unwrap()
12729+
.get("index_block_hash")
12730+
.unwrap()
12731+
.as_str()
12732+
.unwrap()[2..],
12733+
)
12734+
.unwrap();
12735+
12736+
let sip_031_initial_total_liquid_ustx = chainstate
12737+
.with_read_only_clarity_tx(
12738+
&sortdb
12739+
.index_handle_at_block(&chainstate, &last_block_id)
12740+
.unwrap(),
12741+
&last_block_id,
12742+
|conn| conn.with_clarity_db_readonly(|db| db.get_total_liquid_ustx().unwrap()),
12743+
)
12744+
.unwrap();
12745+
12746+
// mine until epoch 3.2 height
1272112747
loop {
1272212748
let commits_before = commits_submitted.load(Ordering::SeqCst);
1272312749
next_block_and_process_new_stacks_block(&mut btc_regtest_controller, 60, &coord_channel)
@@ -12756,6 +12782,134 @@ fn test_sip_031_activation() {
1275612782

1275712783
assert_eq!(epoch_version, Some(StacksEpochId::Epoch32));
1275812784

12785+
// check if sip-031 boot contract has been deployed
12786+
let sip_031_boot_contract_exists = chainstate.with_read_only_clarity_tx(
12787+
&sortdb
12788+
.index_handle_at_block(&chainstate, &latest_stacks_block_id)
12789+
.unwrap(),
12790+
&latest_stacks_block_id,
12791+
|conn| {
12792+
conn.with_clarity_db_readonly(|db| {
12793+
db.has_contract(&boot_code_id(SIP_031_NAME, naka_conf.is_mainnet()))
12794+
})
12795+
},
12796+
);
12797+
12798+
assert_eq!(sip_031_boot_contract_exists, Some(true));
12799+
12800+
// check if sip-031 boot contract has a balance of 200_000_000 STX
12801+
let sip_031_boot_contract_balance = chainstate.with_read_only_clarity_tx(
12802+
&sortdb
12803+
.index_handle_at_block(&chainstate, &latest_stacks_block_id)
12804+
.unwrap(),
12805+
&latest_stacks_block_id,
12806+
|conn| {
12807+
conn.with_clarity_db_readonly(|db| {
12808+
db.get_account_stx_balance(&PrincipalData::Contract(boot_code_id(
12809+
SIP_031_NAME,
12810+
naka_conf.is_mainnet(),
12811+
)))
12812+
})
12813+
},
12814+
);
12815+
12816+
assert_eq!(
12817+
sip_031_boot_contract_balance,
12818+
Some(Ok(STXBalance::Unlocked {
12819+
amount: SIP_031_INITIAL_MINT
12820+
}))
12821+
);
12822+
12823+
// check liquidity has been updated accordingly
12824+
let sip_031_total_liquid_ustx = chainstate
12825+
.with_read_only_clarity_tx(
12826+
&sortdb
12827+
.index_handle_at_block(&chainstate, &latest_stacks_block_id)
12828+
.unwrap(),
12829+
&latest_stacks_block_id,
12830+
|conn| conn.with_clarity_db_readonly(|db| db.get_total_liquid_ustx().unwrap()),
12831+
)
12832+
.unwrap();
12833+
12834+
assert!(sip_031_total_liquid_ustx - sip_031_initial_total_liquid_ustx >= SIP_031_INITIAL_MINT);
12835+
12836+
// check if the coinbase activation block receipt has the mint event
12837+
let mut mint_event_found: Option<serde_json::Value> = None;
12838+
let mut coinbase_txid: Option<String> = None;
12839+
for block in test_observer::get_blocks().iter().rev() {
12840+
let burn_block_height = block.get("burn_block_height").unwrap().as_u64().unwrap();
12841+
if burn_block_height
12842+
== naka_conf.burnchain.epochs.clone().unwrap()[StacksEpochId::Epoch32].start_height
12843+
{
12844+
// the first transaction is the coinbase
12845+
coinbase_txid = Some(
12846+
block
12847+
.get("transactions")
12848+
.unwrap()
12849+
.as_array()
12850+
.unwrap()
12851+
.first()
12852+
.unwrap()
12853+
.get("txid")
12854+
.unwrap()
12855+
.as_str()
12856+
.unwrap()
12857+
.into(),
12858+
);
12859+
let events = block.get("events").unwrap().as_array().unwrap();
12860+
for event in events {
12861+
if let Some(_) = event.get("stx_mint_event") {
12862+
mint_event_found = Some(event.clone());
12863+
break;
12864+
}
12865+
}
12866+
break;
12867+
}
12868+
}
12869+
12870+
assert!(coinbase_txid.is_some());
12871+
assert!(mint_event_found.is_some());
12872+
12873+
// check the amount
12874+
assert_eq!(
12875+
mint_event_found
12876+
.clone()
12877+
.unwrap()
12878+
.get("stx_mint_event")
12879+
.unwrap()
12880+
.get("amount")
12881+
.unwrap()
12882+
.as_str()
12883+
.unwrap(),
12884+
SIP_031_INITIAL_MINT.to_string()
12885+
);
12886+
12887+
// check the recipient
12888+
assert_eq!(
12889+
mint_event_found
12890+
.clone()
12891+
.unwrap()
12892+
.get("stx_mint_event")
12893+
.unwrap()
12894+
.get("recipient")
12895+
.unwrap()
12896+
.as_str()
12897+
.unwrap(),
12898+
boot_code_id(SIP_031_NAME, naka_conf.is_mainnet()).to_string()
12899+
);
12900+
12901+
// check the txid
12902+
assert_eq!(
12903+
mint_event_found
12904+
.clone()
12905+
.unwrap()
12906+
.get("txid")
12907+
.unwrap()
12908+
.as_str()
12909+
.unwrap(),
12910+
coinbase_txid.unwrap()
12911+
);
12912+
1275912913
coord_channel
1276012914
.lock()
1276112915
.expect("Mutex poisoned")

stackslib/src/chainstate/stacks/boot/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ pub const POX_4_NAME: &str = "pox-4";
6363
pub const SIGNERS_NAME: &str = "signers";
6464
pub const SIGNERS_VOTING_NAME: &str = "signers-voting";
6565
pub const SIGNERS_VOTING_FUNCTION_NAME: &str = "vote-for-aggregate-public-key";
66+
pub const SIP_031_NAME: &str = "sip-031";
6667
/// This is the name of a variable in the `.signers` contract which tracks the most recently updated
6768
/// reward cycle number.
6869
pub const SIGNERS_UPDATE_STATE: &str = "last-set-cycle";
@@ -76,6 +77,7 @@ pub const SIGNERS_BODY: &str = std::include_str!("signers.clar");
7677
pub const SIGNERS_DB_0_BODY: &str = std::include_str!("signers-0-xxx.clar");
7778
pub const SIGNERS_DB_1_BODY: &str = std::include_str!("signers-1-xxx.clar");
7879
pub const SIGNERS_VOTING_BODY: &str = std::include_str!("signers-voting.clar");
80+
pub const SIP_031_BODY: &str = std::include_str!("sip-031.clar");
7981

8082
pub const COSTS_1_NAME: &str = "costs";
8183
pub const COSTS_2_NAME: &str = "costs-2";
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
(define-data-var recipient principal 'ST000000000000000000002AMW42H)
2+
3+
(define-public (update-recipient (new-recipient principal)) (begin
4+
(asserts! (is-eq tx-sender (var-get recipient)) (err u101))
5+
(var-set recipient new-recipient)
6+
(ok true)
7+
)) ;; Returns (response bool uint)
8+
9+
(define-read-only (get-recipient) (ok (var-get recipient))) ;; Returns (response principal uint)
10+
11+
(define-public (claim) (ok true) ) ;; Returns (response uint uint)

stackslib/src/clarity_vm/clarity.rs

Lines changed: 86 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use clarity::vm::database::{
2929
RollbackWrapperPersistedLog, STXBalance, NULL_BURN_STATE_DB, NULL_HEADER_DB,
3030
};
3131
use clarity::vm::errors::Error as InterpreterError;
32+
use clarity::vm::events::{STXEventType, STXMintEventData};
3233
use clarity::vm::representations::SymbolicExpression;
3334
use clarity::vm::types::{PrincipalData, QualifiedContractIdentifier, Value};
3435
use clarity::vm::{ClarityVersion, ContractName};
@@ -42,7 +43,8 @@ use crate::chainstate::stacks::boot::{
4243
BOOT_CODE_COST_VOTING_TESTNET as BOOT_CODE_COST_VOTING, BOOT_CODE_POX_TESTNET, COSTS_2_NAME,
4344
COSTS_3_NAME, POX_2_MAINNET_CODE, POX_2_NAME, POX_2_TESTNET_CODE, POX_3_MAINNET_CODE,
4445
POX_3_NAME, POX_3_TESTNET_CODE, POX_4_CODE, POX_4_NAME, SIGNERS_BODY, SIGNERS_DB_0_BODY,
45-
SIGNERS_DB_1_BODY, SIGNERS_NAME, SIGNERS_VOTING_BODY, SIGNERS_VOTING_NAME,
46+
SIGNERS_DB_1_BODY, SIGNERS_NAME, SIGNERS_VOTING_BODY, SIGNERS_VOTING_NAME, SIP_031_BODY,
47+
SIP_031_NAME,
4648
};
4749
use crate::chainstate::stacks::db::{StacksAccount, StacksChainState};
4850
use crate::chainstate::stacks::events::{StacksTransactionEvent, StacksTransactionReceipt};
@@ -57,6 +59,8 @@ use crate::util_lib::boot::{boot_code_acc, boot_code_addr, boot_code_id, boot_co
5759
use crate::util_lib::db::Error as DatabaseError;
5860
use crate::util_lib::strings::StacksString;
5961

62+
pub const SIP_031_INITIAL_MINT: u128 = 200_000_000_000_000;
63+
6064
///
6165
/// A high-level interface for interacting with the Clarity VM.
6266
///
@@ -1617,10 +1621,89 @@ impl<'a> ClarityBlockConnection<'a, '_> {
16171621
tx_conn.epoch = StacksEpochId::Epoch32;
16181622
});
16191623

1620-
// TODO: SIP-031 setup (minting and transfer to the boot contract)
1624+
let mut receipts = vec![];
1625+
1626+
let boot_code_account = self
1627+
.get_boot_code_account()
1628+
.expect("FATAL: did not get boot account");
1629+
1630+
let mainnet = self.mainnet;
1631+
let tx_version = if mainnet {
1632+
TransactionVersion::Mainnet
1633+
} else {
1634+
TransactionVersion::Testnet
1635+
};
1636+
1637+
let boot_code_address = boot_code_addr(mainnet);
1638+
let boot_code_auth = boot_code_tx_auth(boot_code_address);
1639+
1640+
// SIP-031 setup (deploy of the boot contract, minting and transfer to the boot contract)
1641+
let sip_031_contract_id = boot_code_id(SIP_031_NAME, mainnet);
1642+
let payload = TransactionPayload::SmartContract(
1643+
TransactionSmartContract {
1644+
name: ContractName::try_from(SIP_031_NAME)
1645+
.expect("FATAL: invalid boot-code contract name"),
1646+
code_body: StacksString::from_str(SIP_031_BODY)
1647+
.expect("FATAL: invalid boot code body"),
1648+
},
1649+
Some(ClarityVersion::Clarity3),
1650+
);
1651+
1652+
let sip_031_contract_tx =
1653+
StacksTransaction::new(tx_version.clone(), boot_code_auth, payload);
1654+
1655+
let mut sip_031_initialization_receipt = self.as_transaction(|tx_conn| {
1656+
// initialize with a synthetic transaction
1657+
info!("Instantiate {} contract", &sip_031_contract_id);
1658+
let receipt = StacksChainState::process_transaction_payload(
1659+
tx_conn,
1660+
&sip_031_contract_tx,
1661+
&boot_code_account,
1662+
ASTRules::PrecheckSize,
1663+
None,
1664+
)
1665+
.expect("FATAL: Failed to process .sip-031 contract initialization");
1666+
receipt
1667+
});
1668+
1669+
if sip_031_initialization_receipt.result != Value::okay_true()
1670+
|| sip_031_initialization_receipt.post_condition_aborted
1671+
{
1672+
panic!(
1673+
"FATAL: Failure processing sip-031 contract initialization: {:#?}",
1674+
&sip_031_initialization_receipt
1675+
);
1676+
}
1677+
1678+
let recipient = PrincipalData::Contract(sip_031_contract_id);
1679+
1680+
self.as_transaction(|tx_conn| {
1681+
tx_conn
1682+
.with_clarity_db(|db| {
1683+
db.increment_ustx_liquid_supply(SIP_031_INITIAL_MINT)
1684+
.map_err(|e| e.into())
1685+
})
1686+
.expect("FATAL: `SIP-031 initial mint` overflowed");
1687+
StacksChainState::account_credit(
1688+
tx_conn,
1689+
&recipient,
1690+
u64::try_from(SIP_031_INITIAL_MINT)
1691+
.expect("FATAL: transferred more STX than exist"),
1692+
);
1693+
});
1694+
1695+
let event = STXEventType::STXMintEvent(STXMintEventData {
1696+
recipient,
1697+
amount: SIP_031_INITIAL_MINT,
1698+
});
1699+
sip_031_initialization_receipt
1700+
.events
1701+
.push(StacksTransactionEvent::STXEvent(event));
1702+
1703+
receipts.push(sip_031_initialization_receipt);
16211704

16221705
debug!("Epoch 3.2 initialized");
1623-
(old_cost_tracker, Ok(vec![]))
1706+
(old_cost_tracker, Ok(receipts))
16241707
})
16251708
}
16261709

0 commit comments

Comments
 (0)