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

Process successful .signers-voting results #4360

Merged
merged 81 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
6b87b77
updated error message format to match pox-4 messages
setzeus Feb 8, 2024
b5e765c
updated funcs name to reflect weight instead of slots
setzeus Feb 8, 2024
fa27b97
added vote func comments
setzeus Feb 8, 2024
b600a33
fix: accept votes for out of round
friedger Feb 1, 2024
4053af9
chore: better public keys, fix typos
friedger Feb 2, 2024
8f42eba
fix: use Point for aggregate public key
friedger Feb 6, 2024
8f7238b
chore: improve naming and comments
friedger Feb 8, 2024
74b40f5
feat: set aggregate public key once threshold is reached
obycode Feb 9, 2024
00bc983
chore: use uints for errors in signers-voting contract
obycode Feb 9, 2024
b2413c1
test: check for aggregate key approval event
obycode Feb 9, 2024
85b0ea2
fix: use weight in signer set (not number of slots)
obycode Feb 9, 2024
d98b6a7
fixed prepare-phase check & added cycle parameter to voting func
setzeus Feb 9, 2024
270bb36
updated broken tests
setzeus Feb 10, 2024
59bb4aa
feat: setup signers correctly when booting into Nakamoto
obycode Feb 11, 2024
970399f
new get-candidate-info getter
setzeus Feb 11, 2024
d8c8cfa
chore: fix style suggestions from PR review
obycode Feb 12, 2024
fe928d7
fix: fix issues in `get-candidate-info`
obycode Feb 12, 2024
afb43de
fix: properly retrieve signers in `advance_to_nakamoto`
obycode Feb 13, 2024
28f6333
fix: add test signers into peer config
obycode Feb 13, 2024
ccc20fc
refactor: improve testing functions related to signing
obycode Feb 13, 2024
2970970
chore: increase debug logging
obycode Feb 13, 2024
60f0e07
test: only vote in first nakamoto block of tenure
obycode Feb 13, 2024
407f3be
Add generate_aggregate_key to TestSigners
jferrant Feb 14, 2024
02bd070
test: use `generate_aggregate_key` between cycles
obycode Feb 14, 2024
00c5eba
fix: update `poly_commitments` in `generate_aggregate_key`
obycode Feb 14, 2024
1592b53
test: vote for aggregate key of next cycle in prepare phase
obycode Feb 14, 2024
c4be109
chore: update comment to match code
obycode Feb 14, 2024
eed2368
wip: try to merge `TestSigners` and `SelfSigner`
obycode Feb 14, 2024
699a1da
chore: update ed25519-dalek and rand libraries, use workspace versioning
kantai Feb 15, 2024
30c4c52
test: update signers key in tests
obycode Feb 16, 2024
d52b67e
fix: call `generate_aggregate_key` in `make_nakamoto_tenure`
obycode Feb 16, 2024
43acc8c
chore: remove `SelfSigner` which has been replaced with `TestSigners`
obycode Feb 16, 2024
69013f4
test: update `test_nakamoto_coordinator_10_tenures_and_extensions_10_…
obycode Feb 16, 2024
9cc1fd8
fix: Aaron's fix to the replay peer test failures
obycode Feb 16, 2024
d808c0b
Merge branch 'next' into feat/4354-aggregate-key
obycode Feb 16, 2024
4f7fa91
refactor: update uses of `TestSigners`
obycode Feb 16, 2024
8af5f9c
Merge branch 'next' into feat/4354-aggregate-key
obycode Feb 16, 2024
82df1f2
fix: fix TestSigners imports
obycode Feb 16, 2024
4e0180e
fix: allow duplicate keys in different voting round for the same cycle
obycode Feb 16, 2024
175d37e
fix: fix signers-voting tests
obycode Feb 16, 2024
e4c1e9c
chore: switch back to using weight instead of amount stacked
obycode Feb 16, 2024
a7c384c
fix: update for `slots` -> `weight` field naming change
obycode Feb 16, 2024
ffef7ee
fix: WIP fix the net tests after signer changes
obycode Feb 16, 2024
7c09d09
fix: TestPeer submits aggregate key votes automatically, inv tests **…
kantai Feb 17, 2024
52c7a59
test: alter NakamotoBootPlan to issue signer votes whenever possible …
kantai Feb 18, 2024
e220033
Merge remote-tracking branch 'origin/next' into feat/4354-aggregate-key
kantai Feb 18, 2024
746ea8b
test: fix merge of tests with other_peers
kantai Feb 18, 2024
6015cab
chore: fix warnings
obycode Feb 18, 2024
a8514f2
fix: attempt to add voting to the mockamoto tests
obycode Feb 18, 2024
2569934
use initial aggregate key boot contract for mockamoto, fix mockamoto …
kantai Feb 18, 2024
018ea78
fix: handle signer voting in nakamoto integration tests (WIP)
obycode Feb 19, 2024
024cd71
fix: update `correct_burn_outs` for new signer voting support
obycode Feb 19, 2024
8bbffeb
chore: remove hard-coded block height in `boot_to_epoch_3`
obycode Feb 19, 2024
ea4ecf9
Merge branch 'next' into feat/4354-aggregate-key
obycode Feb 19, 2024
6288b29
feat: add check for new round numbers
obycode Feb 19, 2024
192fdd5
first error message check
setzeus Feb 17, 2024
2230776
refactored helper make_signers_vote_for_aggregate_public_key & effect…
setzeus Feb 18, 2024
ff95043
removed unused err
setzeus Feb 18, 2024
ddad05b
catching all tenure-agnostic errs
setzeus Feb 18, 2024
d8cde7f
removed print statements & comment
setzeus Feb 18, 2024
ad1f014
formatted correctly
setzeus Feb 18, 2024
862e347
minor update
setzeus Feb 18, 2024
1ca5d86
test: separate out simple success test
obycode Feb 19, 2024
cd4a91d
test: add test for `ERR_INVALID_ROUND`
obycode Feb 19, 2024
955ac33
chore: update error codes to avoid overlap with .signers
obycode Feb 19, 2024
42a4efa
Merge branch 'next' into feat/4354-aggregate-key
obycode Feb 20, 2024
a92dbdb
refactor: delete `aggregate-public-keys` from pox-4
obycode Feb 20, 2024
f596c0b
fix: use `reward-cycle` parameter to get signer weight
obycode Feb 20, 2024
c8654f9
fix: update expected error after cycle fix in previous commit
obycode Feb 20, 2024
51f0c91
chore: assert that signers are not repeated in tests
obycode Feb 20, 2024
0fdaf62
refactor: refactoring suggestions from review
obycode Feb 20, 2024
252a98c
refactor: various refactoring suggestions from review
obycode Feb 21, 2024
e793b73
test: add test for out of window error
obycode Feb 20, 2024
70eb622
test: check for duplicate aggregate public key error
obycode Feb 20, 2024
3c0f858
test: test voting over multiple rounds
obycode Feb 20, 2024
911e5cd
test: try to vote early, before the prepare phase
obycode Feb 20, 2024
712c77e
test: verify that an old round can succeed after a new round has started
obycode Feb 21, 2024
ac80a88
feat: add round to `approved-aggregate-public-key` event
obycode Feb 21, 2024
91a1fd9
feat: add check for existing key in `signer_vote_if_needed`
obycode Feb 21, 2024
055fad3
Merge branch 'next' into feat/4354-aggregate-key
obycode Feb 21, 2024
d2e6ad8
Merge branch 'next' into feat/4354-aggregate-key
obycode Feb 21, 2024
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
22 changes: 4 additions & 18 deletions contrib/core-contract-tests/tests/pox-4/signers-voting.test.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,20 @@
import { Cl } from "@stacks/transactions";
import { beforeEach, describe, expect, it } from "vitest";
import { describe, expect, it } from "vitest";

const accounts = simnet.getAccounts();
const alice = accounts.get("wallet_1")!;
const bob = accounts.get("wallet_2")!;
const charlie = accounts.get("wallet_3")!;

const ERR_SIGNER_INDEX_MISMATCH = 10000;
const ERR_INVALID_SIGNER_INDEX = 10001;
const ERR_OUT_OF_VOTING_WINDOW = 10002
const ERR_OLD_ROUND = 10003;
const ERR_ILL_FORMED_AGGREGATE_PUBLIC_KEY = 10004;
const ERR_DUPLICATE_AGGREGATE_PUBLIC_KEY = 10005;
const ERR_DUPLICATE_VOTE = 10006;
const ERR_INVALID_BURN_BLOCK_HEIGHT = 10007

const KEY_1 = "123456789a123456789a123456789a123456789a123456789a123456789a010203";
const KEY_2 = "123456789a123456789a123456789a123456789a123456789a123456789ab0b1b2";
const SIGNERS_VOTING = "signers-voting";

describe("test signers-voting contract voting rounds", () => {
describe("test pox-info", () => {
it("should return correct burn-height", () => {
const { result:result1 } = simnet.callReadOnlyFn(SIGNERS_VOTING,
const { result: result1 } = simnet.callReadOnlyFn(SIGNERS_VOTING,
"reward-cycle-to-burn-height",
[Cl.uint(1)],
alice)
expect(result1).toEqual(Cl.uint(1050))

const { result:result2 } = simnet.callReadOnlyFn(SIGNERS_VOTING,
const { result: result2 } = simnet.callReadOnlyFn(SIGNERS_VOTING,
"reward-cycle-to-burn-height",
[Cl.uint(2)],
alice)
Expand All @@ -50,7 +36,7 @@ describe("test signers-voting contract voting rounds", () => {
})

it("should return true if in prepare phase", () => {
const { result:result999 } = simnet.callReadOnlyFn(SIGNERS_VOTING,
const { result: result999 } = simnet.callReadOnlyFn(SIGNERS_VOTING,
"is-in-prepare-phase",
[Cl.uint(999)],
alice)
Expand Down
35 changes: 33 additions & 2 deletions stackslib/src/chainstate/nakamoto/coordinator/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ use crate::chainstate::nakamoto::tests::node::{TestSigners, TestStacker};
use crate::chainstate::nakamoto::{NakamotoBlock, NakamotoChainState};
use crate::chainstate::stacks::address::PoxAddress;
use crate::chainstate::stacks::boot::test::{
key_to_stacks_addr, make_pox_4_aggregate_key, make_pox_4_lockup, make_signer_key_signature,
key_to_stacks_addr, make_pox_4_lockup, make_signer_key_signature,
make_signers_vote_for_aggregate_public_key,
};
use crate::chainstate::stacks::boot::MINERS_NAME;
use crate::chainstate::stacks::db::{MinerPaymentTxFees, StacksAccount, StacksChainState};
Expand Down Expand Up @@ -101,6 +102,23 @@ fn advance_to_nakamoto(
)
})
.collect()
} else if sortition_height == 7 {
// Vote for the aggregate key
test_stackers
.iter()
.enumerate()
.map(|(index, test_stacker)| {
info!("Vote for aggregate key: {}", index);
make_signers_vote_for_aggregate_public_key(
&test_stacker.signer_private_key,
0,
index as u128,
&test_signers.aggregate_public_key,
0,
7,
)
})
.collect()
} else {
vec![]
};
Expand All @@ -111,7 +129,8 @@ fn advance_to_nakamoto(
}

/// Make a peer and transition it into the Nakamoto epoch.
/// The node needs to be stacking; otherwise, Nakamoto won't activate.
/// The node needs to be stacking and it needs to vote for an aggregate key;
/// otherwise, Nakamoto can't activate.
pub fn boot_nakamoto<'a>(
test_name: &str,
mut initial_balances: Vec<(PrincipalData, u64)>,
Expand Down Expand Up @@ -152,7 +171,19 @@ pub fn boot_nakamoto<'a>(
})
.collect();

// Create some balances for test Signers
let mut signer_balances = test_stackers
.iter()
.map(|stacker| {
(
PrincipalData::from(p2pkh_from(&stacker.signer_private_key)),
1000,
)
})
.collect();

peer_config.initial_balances.append(&mut stacker_balances);
peer_config.initial_balances.append(&mut signer_balances);
peer_config.initial_balances.append(&mut initial_balances);
peer_config.burnchain.pox_constants.v2_unlock_height = 21;
peer_config.burnchain.pox_constants.pox_3_activation_height = 26;
Expand Down
112 changes: 5 additions & 107 deletions stackslib/src/chainstate/nakamoto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1796,7 +1796,7 @@ impl NakamotoChainState {
Ok(true)
}

/// Get the aggregate public key for the given block from the pox-4 contract
/// Get the aggregate public key for the given block from the signers-voting contract
fn load_aggregate_public_key<SH: SortitionHandle>(
sortdb: &SortitionDB,
sort_handle: &SH,
Expand All @@ -1818,7 +1818,10 @@ impl NakamotoChainState {
return Err(ChainstateError::InvalidStacksBlock(msg));
};

debug!("get-aggregate-public-key {} {}", at_block_id, rc);
debug!(
"get-approved-aggregate-key at block {}, cycle {}",
at_block_id, rc
);
match chainstate.get_aggregate_public_key_pox_4(sortdb, at_block_id, rc)? {
Some(key) => Ok(key),
None => {
Expand Down Expand Up @@ -2602,15 +2605,6 @@ impl NakamotoChainState {
);
}

if !clarity_tx.config.mainnet {
Self::set_aggregate_public_key(
&mut clarity_tx,
first_block_height,
pox_constants,
burn_header_height.into(),
);
}

// Handle signer stackerdb updates
let signer_set_calc;
if evaluated_epoch >= StacksEpochId::Epoch25 {
Expand Down Expand Up @@ -2675,102 +2669,6 @@ impl NakamotoChainState {
Ok(lockup_events)
}

/// Set the aggregate public key for verifying stacker signatures.
/// TODO: rely on signer voting instead
/// DO NOT USE IN MAINNET
pub(crate) fn set_aggregate_public_key(
clarity_tx: &mut ClarityTx,
first_block_height: u64,
pox_constants: &PoxConstants,
burn_header_height: u64,
) {
let mainnet = clarity_tx.config.mainnet;
let chain_id = clarity_tx.config.chain_id;
assert!(!mainnet);

let my_reward_cycle = pox_constants
.block_height_to_reward_cycle(
first_block_height,
burn_header_height
.try_into()
.expect("Burn block height exceeded u32"),
)
.expect("FATAL: block height occurs before first block height");

let parent_reward_cycle = my_reward_cycle.saturating_sub(1);
debug!(
"Try setting aggregate public key in reward cycle {}, parent {}",
my_reward_cycle, parent_reward_cycle
);

// execute `set-aggregate-public-key` using `clarity-tx`
let Some(aggregate_public_key) = clarity_tx
.connection()
.with_readonly_clarity_env(
mainnet,
chain_id,
ClarityVersion::Clarity2,
StacksAddress::burn_address(mainnet).into(),
None,
LimitedCostTracker::Free,
|vm_env| {
vm_env.execute_contract_allow_private(
&boot_code_id(POX_4_NAME, mainnet),
"get-aggregate-public-key",
&vec![SymbolicExpression::atom_value(Value::UInt(u128::from(
parent_reward_cycle,
)))],
true,
)
},
)
.ok()
.map(|agg_key_value| {
let agg_key_opt = agg_key_value
.expect_optional()
.expect("FATAL: not an optional")
.map(|agg_key_buff| {
Value::buff_from(agg_key_buff.expect_buff(33).expect("FATAL: not a buff"))
.expect("failed to reconstruct buffer")
});
agg_key_opt
})
.flatten()
else {
panic!(
"No aggregate public key in parent cycle {}",
parent_reward_cycle
);
};

clarity_tx.connection().as_transaction(|tx| {
tx.with_abort_callback(
|vm_env| {
vm_env.execute_in_env(
StacksAddress::burn_address(mainnet).into(),
None,
None,
|vm_env| {
vm_env.execute_contract_allow_private(
&boot_code_id(POX_4_NAME, mainnet),
"set-aggregate-public-key",
&vec![
SymbolicExpression::atom_value(Value::UInt(u128::from(
my_reward_cycle,
))),
SymbolicExpression::atom_value(aggregate_public_key),
],
false,
)
},
)
},
|_, _| false,
)
.expect("FATAL: failed to set aggregate public key")
});
}

/// Append a Nakamoto Stacks block to the Stacks chain state.
pub fn append_block<'a>(
chainstate_tx: &mut ChainstateTx,
Expand Down
2 changes: 1 addition & 1 deletion stackslib/src/chainstate/nakamoto/signer_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ impl NakamotoSigners {
"signer".into(),
Value::Principal(PrincipalData::from(signing_address)),
),
("weight".into(), Value::UInt(signer.slots.into())),
("weight".into(), Value::UInt(signer.stacked_amt.into())),
Copy link
Collaborator

@jferrant jferrant Feb 14, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think stacked_amt needs to be normalized no? Or is this already?

I.e. shoudl be some integer from 0 to 4000? Just surprised it is a u128 if it is normalized.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was not aware that this is supposed to be normalized. I thought it was just supposed to be the amount of STX?

Copy link
Collaborator

@jferrant jferrant Feb 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This "weight" is retrieved from the reward set entry which is a number of slots value and there are only 4000 of them. If signer A has 10 slots, they get key ids 1 through 10. The next signer in the list, Signer B, has 12 slots, they get key ids 11 through 22., etc. the max key id we can have is 4000. I do believe simply putting the stacked amt is incorrect.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so I'm just going to revert this change and use the slots field, which seems to be the number of reward slots.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but I'm also going to rename it to reward-slots to avoid confusion with stacker DB slots, which is a term also used in this file.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure though weight is also fine to keep (its what is used inside reward slot entry inside the rust codebase. Would prob try to keep it consistent)

Copy link
Contributor Author

@obycode obycode Feb 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, then I'll rename the field in the struct to be weight to match also.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understood voting power is by stacked amount, not by slots that is less or equal than stacked amount.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems there were some misunderstandings and back and forth there, but what we landed on is that this should be the number of reward slots.

])
.expect(
"BUG: Failed to construct `{ signer: principal, num-slots: u64 }` tuple",
Expand Down
36 changes: 12 additions & 24 deletions stackslib/src/chainstate/stacks/boot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1291,16 +1291,21 @@ impl StacksChainState {
.eval_boot_code_read_only(
sortdb,
block_id,
POX_4_NAME,
&format!("(get-aggregate-public-key u{})", reward_cycle),
SIGNERS_VOTING_NAME,
&format!("(get-approved-aggregate-key u{})", reward_cycle),
)?
.expect_optional()?;
debug!(
"Aggregate public key for reward cycle {} is {:?}",
reward_cycle, aggregate_public_key_opt
);

let aggregate_public_key = match aggregate_public_key_opt {
Some(value) => {
// A point should have 33 bytes exactly.
let data = value.expect_buff(33)?;
let msg = "Pox-4 get-aggregate-public-key returned a corrupted value.";
let msg =
"Pox-4 signers-voting get-approved-aggregate-key returned a corrupted value.";
let compressed_data = Compressed::try_from(data.as_slice()).expect(msg);
Some(Point::try_from(&compressed_data).expect(msg))
}
Expand Down Expand Up @@ -1333,9 +1338,8 @@ pub mod test {
use clarity::vm::contracts::Contract;
use clarity::vm::tests::symbols_from_values;
use clarity::vm::types::*;
use stacks_common::types::PrivateKey;
use stacks_common::util::hash::Sha256Sum;
use stacks_common::util::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey};
use stacks_common::util::hash::to_hex;
use stacks_common::util::secp256k1::Secp256k1PublicKey;
use stacks_common::util::*;

use super::*;
Expand Down Expand Up @@ -1900,30 +1904,13 @@ pub mod test {
make_tx(key, nonce, 0, payload)
}

pub fn make_pox_4_aggregate_key(
key: &StacksPrivateKey,
nonce: u64,
reward_cycle: u64,
aggregate_public_key: &Point,
) -> StacksTransaction {
let aggregate_public_key = Value::buff_from(aggregate_public_key.compress().data.to_vec())
.expect("Failed to serialize aggregate public key");
let payload = TransactionPayload::new_contract_call(
boot_code_test_addr(),
POX_4_NAME,
"set-aggregate-public-key",
vec![Value::UInt(reward_cycle as u128), aggregate_public_key],
)
.unwrap();
make_tx(key, nonce, 0, payload)
}

pub fn make_signers_vote_for_aggregate_public_key(
key: &StacksPrivateKey,
nonce: u64,
signer_index: u128,
aggregate_public_key: &Point,
round: u128,
cycle: u128,
) -> StacksTransaction {
let aggregate_public_key = Value::buff_from(aggregate_public_key.compress().data.to_vec())
.expect("Failed to serialize aggregate public key");
Expand All @@ -1935,6 +1922,7 @@ pub mod test {
Value::UInt(signer_index),
aggregate_public_key,
Value::UInt(round),
Value::UInt(cycle),
],
)
.unwrap();
Expand Down
14 changes: 0 additions & 14 deletions stackslib/src/chainstate/stacks/boot/pox-4.clar
Original file line number Diff line number Diff line change
Expand Up @@ -1331,17 +1331,3 @@
(define-read-only (get-partial-stacked-by-cycle (pox-addr { version: (buff 1), hashbytes: (buff 32) }) (reward-cycle uint) (sender principal))
(map-get? partial-stacked-by-cycle { pox-addr: pox-addr, reward-cycle: reward-cycle, sender: sender })
)

;; What is the given reward cycle's stackers' aggregate public key?
;; *New in Stacks 3.0*
(define-read-only (get-aggregate-public-key (reward-cycle uint))
(map-get? aggregate-public-keys reward-cycle)
)

;; Set the aggregate public key to the provided value
;; *New in Stacks 3.0*
(define-private (set-aggregate-public-key (reward-cycle uint) (aggregate-public-key (buff 33)))
(begin
(ok (map-set aggregate-public-keys reward-cycle aggregate-public-key))
)
)
Loading