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/get pox addrs #3245

Merged
merged 65 commits into from
Aug 22, 2022
Merged
Show file tree
Hide file tree
Changes from 64 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
bf54779
feat: add PoxAddress type to represent a `pox-addr` tuple passed into…
jcnelson Aug 9, 2022
b63f764
chore: expose PoxAddress
jcnelson Aug 9, 2022
76ba303
feat: add docs for `get-burn-block-info? pox-addrs`
jcnelson Aug 9, 2022
6eefc51
feat: implement `get-burn-block-info? pox-addrs` (wraps the database …
jcnelson Aug 9, 2022
da14380
chore: expose PoxAddress as clarity::vm::PoxAddress
jcnelson Aug 9, 2022
a1000b5
chore: mock get_pox_payout_addrs()
jcnelson Aug 9, 2022
f2bd018
chore: add type signature for `get-burn-block-info? pox-addrs` to typ…
jcnelson Aug 9, 2022
dc0aaf1
chore: use PoxAddress instead of StacksAddress for representing burnc…
jcnelson Aug 9, 2022
8d973d1
chore: use PoxAddress instead of StacksAddress
jcnelson Aug 9, 2022
7914f7b
chore: use PoxAddress instead of StacksAddress
jcnelson Aug 9, 2022
e4d48e9
chore: add FromColumn<PoxAddress>
jcnelson Aug 9, 2022
8900410
chore: log parent block-commit coordinates when rejecting a block-commit
jcnelson Aug 9, 2022
da96474
feat: when updating the sortition DB MARF, store the PoX payout addre…
jcnelson Aug 9, 2022
3bae932
refactor: commit_outs are now PoxAddress, not StacksAddress
jcnelson Aug 9, 2022
8a9e45b
refactor: burnchain recipient is a PoxAddress now
jcnelson Aug 9, 2022
5fe5a8d
chore: StackStxOp and LeaderBlockCommitOp use PoxAddress instead of S…
jcnelson Aug 9, 2022
76b888d
chore: use PoxAddress instead of StacksAddress for recipient
jcnelson Aug 9, 2022
e80dcde
refactor: burnchain recipient is now a PoxAddress
jcnelson Aug 9, 2022
6db3c90
refactor: burnchain recipient is now a PoxAddress
jcnelson Aug 9, 2022
d28423f
chore: PoX reward set now uses PoxAddress, not StacksAddress
jcnelson Aug 9, 2022
dd214fb
refactor: use PoxAddress instead of StacksAddress
jcnelson Aug 9, 2022
27a85b5
feat: add PoxAddressExtensions trait for converting a PoxAddress to b…
jcnelson Aug 9, 2022
fdb4e9c
chore: add get_pox_payout_addrs()
jcnelson Aug 9, 2022
6f92edd
refactor: use PoxAddress instead of StacksAddress
jcnelson Aug 9, 2022
c7cf376
feat: add unit tests for `get-burn-block-info? pox-addrs` (and update…
jcnelson Aug 9, 2022
a824c26
refactor: now using PoxAddress instead of StacksAddress to recover in…
jcnelson Aug 9, 2022
78782d1
chore: mock get_pox_payout_addrs()
jcnelson Aug 9, 2022
3635d41
chore: TrieCache and TrieCacheState are now clone()-able
jcnelson Aug 9, 2022
85ea09a
refactor: distinguish between reopening a MARF as read-only with or w…
jcnelson Aug 9, 2022
b48f618
feat: add the ability to run a closure against the uncommitted MARF s…
jcnelson Aug 9, 2022
215df5d
refactor: API sync
jcnelson Aug 9, 2022
902ee00
chore: mock get_pox_payout_addrs()
jcnelson Aug 9, 2022
83f0e8f
feat: implement get_pox_payout_addrs()
jcnelson Aug 9, 2022
54e4466
refactor: using PoxAddress instead of StacksAddress
jcnelson Aug 9, 2022
0695eff
feat: get_indexed() and get_ancestor_block_hash() in an IndexDBTx now…
jcnelson Aug 9, 2022
0d14605
chore: derive all the usual things for AddressHashMode to make it ser…
jcnelson Aug 9, 2022
161478d
refactor: use PoxAddress (and its extensions) isntead of StacksAddress
jcnelson Aug 9, 2022
a87c2ea
refactor: now using PoxAddress instead of StacksAddress
jcnelson Aug 9, 2022
ad2c285
refactor: using PoxAddress instead of StacksAddress
jcnelson Aug 9, 2022
a5ec814
refactor: using PoxAddress instead of StacksAddress
jcnelson Aug 9, 2022
77850d1
refactor: using PoxAddress instead of StacksAddress
jcnelson Aug 9, 2022
9c48135
fix: config.burnchain.peer_host should default to 127.0.0.1 instead o…
jcnelson Aug 9, 2022
4ae87a3
feat: integration test for `get-burn-block-info? pox-addrs`
jcnelson Aug 9, 2022
f4d5f56
chore: make the new `transition_adds_get_pox_addr_recipients` test ru…
jcnelson Aug 9, 2022
6fb6529
Merge branch 'next' into feat/get-pox-addrs
jcnelson Aug 9, 2022
68310f2
fix: use PoxConstants to determine when we're in the prepare phase, n…
jcnelson Aug 9, 2022
65127c4
fix: test the correct payout value when we're in the prepare vs rewar…
jcnelson Aug 9, 2022
e34add8
fix: when mocking a block-commit, always put in the correct burn addr…
jcnelson Aug 9, 2022
2837418
fix: test that our `get-burn-block-info? pox-addrs` calls return the …
jcnelson Aug 9, 2022
1ae111d
fix: fix failing test -- we can't deduce the true hash mode from just…
jcnelson Aug 10, 2022
8b18fa1
fix: no longer comparing address version, but hash mode
jcnelson Aug 10, 2022
a327c0a
fix: no longer comparing address version, but hash mode
jcnelson Aug 10, 2022
a529500
fix: don't re-open a storage tx's underlying DB (since then uncommitt…
jcnelson Aug 10, 2022
5742e37
docs: note that uncommitted writes are visible in get_indexed()
jcnelson Aug 10, 2022
d3716f3
chore: log initial mining bonus
jcnelson Aug 10, 2022
49dea6e
refactor: move PoxAddress to src/chainstate/stacks/address.rs, and ha…
jcnelson Aug 11, 2022
eb898ea
chore: revert changes to the MARF's read-only views (because YAGNI)
jcnelson Aug 12, 2022
2bb1c1d
chore: return Clarity tuples not PoxAddresses
jcnelson Aug 12, 2022
c9a3d03
chore: store PoX payouts in the `snapshots` table instead of the MARF…
jcnelson Aug 12, 2022
699a3c4
feat: query pox payouts from snapshots table, not the MARF
jcnelson Aug 12, 2022
e0b342c
docs: indicate that burn addresses will be reported in `pox-addrs`
jcnelson Aug 16, 2022
09ad07a
fix: populate PoX addresses paid out with burn addresses, up to OUTPU…
jcnelson Aug 16, 2022
0a49e0b
fix: update test to verify that 1 or 2 burn addresses are reported, a…
jcnelson Aug 16, 2022
4d78bfc
fix: update integration test to verify that 1 or 2 burn addresses get…
jcnelson Aug 16, 2022
7f5af6b
chore: add/expand rust docs and remove dead code
jcnelson Aug 19, 2022
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
1 change: 1 addition & 0 deletions .github/workflows/bitcoin-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ jobs:
- tests::epoch_21::transition_adds_burn_block_height
- tests::epoch_21::transition_fixes_bitcoin_rigidity
- tests::epoch_21::transition_adds_pay_to_contract
- tests::epoch_21::transition_adds_get_pox_addr_recipients
steps:
- uses: actions/checkout@v2
- name: Download docker image
Expand Down
71 changes: 55 additions & 16 deletions clarity/src/vm/database/clarity_db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ use crate::vm::errors::{
use crate::vm::representations::ClarityName;
use crate::vm::types::{
serialization::NONE_SERIALIZATION_LEN, OptionalData, PrincipalData,
QualifiedContractIdentifier, StandardPrincipalData, TupleData, TupleTypeSignature,
TypeSignature, Value, NONE,
QualifiedContractIdentifier, SequenceData, StandardPrincipalData, TupleData,
TupleTypeSignature, TypeSignature, Value, NONE,
};
use stacks_common::util::hash::{to_hex, Hash160, Sha256Sum, Sha512Trunc256Sum};

Expand All @@ -53,11 +53,16 @@ use stacks_common::consts::{
BITCOIN_REGTEST_FIRST_BLOCK_HASH, BITCOIN_REGTEST_FIRST_BLOCK_HEIGHT,
BITCOIN_REGTEST_FIRST_BLOCK_TIMESTAMP, FIRST_BURNCHAIN_CONSENSUS_HASH, FIRST_STACKS_BLOCK_HASH,
};

use stacks_common::address::AddressHashMode;
use stacks_common::types::chainstate::ConsensusHash;
use stacks_common::types::Address;

use super::clarity_store::SpecialCaseHandler;
use super::key_value_wrapper::ValueResult;

use serde_json;

pub const STORE_CONTRACT_SRC_INTERFACE: bool = true;

pub type StacksEpoch = GenericStacksEpoch<ExecutionCost>;
Expand Down Expand Up @@ -139,6 +144,13 @@ pub trait BurnStateDB {
/// the epoch enclosing `height`.
fn get_stacks_epoch(&self, height: u32) -> Option<StacksEpoch>;
fn get_stacks_epoch_by_epoch_id(&self, epoch_id: &StacksEpochId) -> Option<StacksEpoch>;

/// Get the PoX payout addresses for a given burnchain block
fn get_pox_payout_addrs(
&self,
height: u32,
sortition_id: &SortitionId,
) -> Option<(Vec<TupleData>, u128)>;
}

impl HeadersDB for &dyn HeadersDB {
Expand Down Expand Up @@ -223,6 +235,13 @@ impl BurnStateDB for &dyn BurnStateDB {
fn get_stacks_epoch_by_epoch_id(&self, epoch_id: &StacksEpochId) -> Option<StacksEpoch> {
(*self).get_stacks_epoch_by_epoch_id(epoch_id)
}
fn get_pox_payout_addrs(
&self,
height: u32,
sortition_id: &SortitionId,
) -> Option<(Vec<TupleData>, u128)> {
(*self).get_pox_payout_addrs(height, sortition_id)
}
}

pub struct NullHeadersDB {}
Expand Down Expand Up @@ -351,6 +370,13 @@ impl BurnStateDB for NullBurnStateDB {
fn get_pox_rejection_fraction(&self) -> u64 {
panic!("NullBurnStateDB should not return PoX info");
}
fn get_pox_payout_addrs(
&self,
_height: u32,
_sortition_id: &SortitionId,
) -> Option<(Vec<TupleData>, u128)> {
None
}
}

impl<'a> ClarityDatabase<'a> {
Expand Down Expand Up @@ -756,25 +782,12 @@ impl<'a> ClarityDatabase<'a> {
.expect("Failed to get block data.")
}

/// Fetch the burnchain block header hash for a given burnchain height.
/// Because the burnchain can fork, we need to resolve the burnchain hash from the
/// currently-evaluated Stacks chain tip as follows:
///
/// 1. Get the current Stacks tip height (which is in the process of being evaluated)
/// 2. Get the parent block's StacksBlockId, which is SHA512-256(consensus_hash, block_hash).
/// This is the highest Stacks block in this fork whose consensus hash is known.
/// 3. Resolve the parent StacksBlockId to its consensus hash
/// 4. Resolve the consensus hash to the associated SortitionId
/// 5. Resolve the SortitionID at `burnchain_block_height` from the SortitionID obtained in
/// (4).
///
/// This way, the `BurnchainHeaderHash` returned is guaranteed to be on the burnchain fork
/// that holds the currently-evaluated Stacks fork (even if it's not the canonical burnchain
/// fork).
pub fn get_burnchain_block_header_hash_for_burnchain_height(
&mut self,
burnchain_block_height: u32,
) -> Option<BurnchainHeaderHash> {
fn get_sortition_id_for_stacks_tip(&mut self) -> Option<SortitionId> {
let current_stacks_height = self.get_current_block_height();

if current_stacks_height < 1 {
Expand Down Expand Up @@ -804,10 +817,36 @@ impl<'a> ClarityDatabase<'a> {
&consensus_hash
));

Some(sortition_id)
}

/// Fetch the burnchain block header hash for a given burnchain height.
/// Because the burnchain can fork, we need to resolve the burnchain hash from the
/// currently-evaluated Stacks chain tip.
///
/// This way, the `BurnchainHeaderHash` returned is guaranteed to be on the burnchain fork
/// that holds the currently-evaluated Stacks fork (even if it's not the canonical burnchain
/// fork).
pub fn get_burnchain_block_header_hash_for_burnchain_height(
&mut self,
burnchain_block_height: u32,
) -> Option<BurnchainHeaderHash> {
let sortition_id = self.get_sortition_id_for_stacks_tip()?;
self.burn_state_db
.get_burn_header_hash(burnchain_block_height, &sortition_id)
}

/// Get the PoX reward addresses and per-address payout for a given burnchain height. Because the burnchain can fork,
/// we need to resolve the PoX addresses from the currently-evaluated Stacks chain tip.
pub fn get_pox_payout_addrs_for_burnchain_height(
&mut self,
burnchain_block_height: u32,
) -> Option<(Vec<TupleData>, u128)> {
let sortition_id = self.get_sortition_id_for_stacks_tip()?;
self.burn_state_db
.get_pox_payout_addrs(burnchain_block_height, &sortition_id)
}

pub fn get_burnchain_block_height(&mut self, id_bhh: &StacksBlockId) -> Option<u32> {
self.headers_db.get_burn_block_height_for_block(id_bhh)
}
Expand Down
55 changes: 54 additions & 1 deletion clarity/src/vm/docs/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1499,7 +1499,7 @@ this value is less than or equal to the value for `miner-spend-total` at the sam

const GET_BURN_BLOCK_INFO_API: SpecialAPI = SpecialAPI {
input_type: "BurnBlockInfoPropertyName, uint",
output_type: "(optional buff)",
output_type: "(optional buff) | (optional (tuple (addrs (list 2 (tuple (hashbytes (buff 20)) (version (buff 1))))) (payout uint)))",
signature: "(get-burn-block-info? prop-name block-height)",
description: "The `get-burn-block-info?` function fetches data for a block of the given *burnchain* block height. The
value and type returned are determined by the specified `BlockInfoPropertyName`. Valid values for `block-height` only
Expand All @@ -1510,9 +1510,23 @@ The following `BlockInfoPropertyName` values are defined:

* The `header-hash` property returns a 32-byte buffer representing the header hash of the burnchain block at
burnchain height `block-height`.

* The `pox-addrs` property returns a tuple with two items: a list of up to two PoX addresses that received a PoX payout at that block height, and the amount of burnchain
tokens paid to each address (note that per the blockchain consensus rules, each PoX payout will be the same for each address in the block-commit transaction).
The list will include burn addresses -- that is, the unspendable addresses that miners pay to when there are no PoX addresses left to be paid. During the prepare phase,
there will be exactly one burn address reported. During the reward phase, up to two burn addresses may be reported in the event that some PoX reward slots are not claimed.

The `addrs` list contains the same PoX address values passed into the PoX smart contract:
* They each have type signature `(tuple (hashbytes (buff 20)) (version (buff 1)))`
* The `version` field can be any of the following:
* `0x00` means this is a p2pkh address, and `hashbytes` is the hash160 of a single public key
* `0x01` means this is a p2sh address, and `hashbytes` is the hash160 of a redeemScript script
* `0x02` means this is a p2wpkh-p2sh address, and `hashbytes` is the hash160 of a p2wpkh witness script
* `0x03` means this is a p2wsh-p2sh address, and `hashbytes` is the hash160 of a p2wsh witness script
",
example: "
(get-burn-block-info? header-hash u677050) ;; Returns (some 0xe67141016c88a7f1203eca0b4312f2ed141531f59303a1c267d7d83ab6b977d8)
(get-burn-block-info? pox-addrs u677050) ;; Returns (some (tuple (addrs ((tuple (hashbytes 0x395f3643cea07ec4eec73b4d9a973dcce56b9bf1) (version 0x00)) (tuple (hashbytes 0x7c6775e20e3e938d2d7e9d79ac310108ba501ddb) (version 0x01)))) (payout u123)))
"
};

Expand Down Expand Up @@ -2252,11 +2266,15 @@ mod test {
QualifiedContractIdentifier, Value,
};
use stacks_common::types::{StacksEpochId, PEER_VERSION_EPOCH_2_1};
use stacks_common::util::hash::hex_bytes;

use super::make_all_api_reference;
use super::make_json_api_reference;
use crate::address::AddressHashMode;
use crate::types::chainstate::{SortitionId, StacksAddress, StacksBlockId};
use crate::types::Address;
use crate::vm::analysis::type_check;
use crate::vm::types::TupleData;
use crate::{types::chainstate::VRFSeed, vm::StacksEpoch};
use crate::{
types::chainstate::{BlockHeaderHash, BurnchainHeaderHash, ConsensusHash},
Expand Down Expand Up @@ -2386,6 +2404,41 @@ mod test {
fn get_stacks_epoch_by_epoch_id(&self, epoch_id: &StacksEpochId) -> Option<StacksEpoch> {
self.get_stacks_epoch(0)
}
fn get_pox_payout_addrs(
&self,
height: u32,
sortition_id: &SortitionId,
) -> Option<(Vec<TupleData>, u128)> {
// (some (tuple (addrs ((tuple (hashbytes 0x395f3643cea07ec4eec73b4d9a973dcce56b9bf1) (version 0x00)) (tuple (hashbytes 0x7c6775e20e3e938d2d7e9d79ac310108ba501ddb) (version 0x01)))) (payout u123)))

Some((
vec![
TupleData::from_data(vec![
("version".into(), Value::buff_from(vec![0u8]).unwrap()),
(
"hashbytes".into(),
Value::buff_from(
hex_bytes("395f3643cea07ec4eec73b4d9a973dcce56b9bf1").unwrap(),
)
.unwrap(),
),
])
.unwrap(),
TupleData::from_data(vec![
("version".into(), Value::buff_from(vec![1u8]).unwrap()),
(
"hashbytes".into(),
Value::buff_from(
hex_bytes("7c6775e20e3e938d2d7e9d79ac310108ba501ddb").unwrap(),
)
.unwrap(),
),
])
.unwrap(),
],
123,
))
}
}

fn docs_execute(store: &mut MemoryBackingStore, program: &str) {
Expand Down
34 changes: 31 additions & 3 deletions clarity/src/vm/functions/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ use crate::vm::errors::{
use crate::vm::representations::{SymbolicExpression, SymbolicExpressionType};
use crate::vm::types::{
BlockInfoProperty, BuffData, BurnBlockInfoProperty, OptionalData, PrincipalData, SequenceData,
TypeSignature, Value, BUFF_32,
TupleData, TypeSignature, Value, BUFF_32,
};
use crate::vm::{eval, Environment, LocalContext};
use stacks_common::types::chainstate::StacksBlockId;
Expand Down Expand Up @@ -794,9 +794,10 @@ pub fn special_get_block_info(
Ok(Value::some(result)?)
}

/// Interprets `args` as variables `[property_name, burnblock_height]`, and returns
/// Interprets `args` as variables `[property_name, burn_block_height]`, and returns
/// a property value determined by `property_name`:
/// - `header_hash` returns the burnblock header hash at `burnblock_height`
/// - `header_hash` returns the burn block header hash at `burn_block_height`
/// - `pox_addrs` returns the list of PoX addresses paid out at `burn_block_height`
///
/// # Errors:
/// - CheckErrors::IncorrectArgumentCount if there aren't 2 arguments.
Expand Down Expand Up @@ -852,5 +853,32 @@ pub fn special_get_burn_block_info(
None => Ok(Value::none()),
}
}
BurnBlockInfoProperty::PoxAddrs => {
let pox_addrs_and_payout = env
.global_context
.database
.get_pox_payout_addrs_for_burnchain_height(height_value);

match pox_addrs_and_payout {
Some((addrs, payout)) => Ok(Value::some(Value::Tuple(
TupleData::from_data(vec![
(
"addrs".into(),
Value::list_from(
addrs
.into_iter()
.map(|addr_tuple| Value::Tuple(addr_tuple))
.collect(),
)
.expect("FATAL: could not convert address list to Value"),
),
("payout".into(), Value::UInt(payout)),
])
.expect("FATAL: failed to build pox addrs and payout tuple"),
))
.expect("FATAL: could not build Some(..)")),
None => Ok(Value::none()),
}
}
}
}
16 changes: 15 additions & 1 deletion clarity/src/vm/test_util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::vm::execute as vm_execute;
use crate::vm::execute_on_network as vm_execute_on_network;
use crate::vm::representations::SymbolicExpression;
use crate::vm::types::StandardPrincipalData;
use crate::vm::types::{PrincipalData, ResponseData, Value};
use crate::vm::types::{PrincipalData, ResponseData, TupleData, Value};
use crate::vm::StacksEpoch;
use stacks_common::address::{AddressHashMode, C32_ADDRESS_VERSION_TESTNET_SINGLESIG};
use stacks_common::consts::{
Expand Down Expand Up @@ -225,4 +225,18 @@ impl BurnStateDB for UnitTestBurnStateDB {
) -> Option<SortitionId> {
None
}
fn get_pox_payout_addrs(
&self,
_height: u32,
_sortition_id: &SortitionId,
) -> Option<(Vec<TupleData>, u128)> {
Some((
vec![TupleData::from_data(vec![
("version".into(), Value::buff_from(vec![0u8]).unwrap()),
("hashbytes".into(), Value::buff_from(vec![0u8; 20]).unwrap()),
])
.unwrap()],
123,
))
}
}
20 changes: 20 additions & 0 deletions clarity/src/vm/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,7 @@ define_versioned_named_enum!(BlockInfoProperty(ClarityVersion) {
// Properties for "get-burn-block-info".
define_named_enum!(BurnBlockInfoProperty {
HeaderHash("header-hash"),
PoxAddrs("pox-addrs"),
});

impl OptionalData {
Expand Down Expand Up @@ -692,6 +693,25 @@ impl BurnBlockInfoProperty {
use self::BurnBlockInfoProperty::*;
match self {
HeaderHash => BUFF_32.clone(),
PoxAddrs => TupleTypeSignature::try_from(vec![
(
"addrs".into(),
TypeSignature::list_of(
TypeSignature::TupleType(
TupleTypeSignature::try_from(vec![
("version".into(), BUFF_1.clone()),
("hashbytes".into(), BUFF_20.clone()),
])
.expect("FATAL: bad type signature for pox addr"),
),
2,
)
.expect("FATAL: bad list type signature"),
),
("payout".into(), TypeSignature::UIntType),
])
.expect("FATAL: bad type signature for pox addr")
.into(),
}
}
}
Expand Down
Loading