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

[Coinbase Go/No-Go] Bond balances in genesis block #2308

Merged
merged 48 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
74d29bf
Pass validator to bonded_balances
vicsn Jan 18, 2024
9bfa3cc
Enforce that bonded balances are consistent with the committee
d0cd Jan 19, 2024
bf03d25
Cleanup
d0cd Jan 19, 2024
d84e304
Add more checks
d0cd Jan 19, 2024
befcf83
Cleanup
d0cd Jan 19, 2024
8d1563f
More consistency checks
d0cd Jan 19, 2024
dc96d3d
Merge branch 'mainnet' into bond_balances_in_genesis
d0cd Jan 21, 2024
133c822
Update Cargo.lock
d0cd Jan 21, 2024
ed0abd0
Regen expectations
d0cd Jan 22, 2024
b72f6d6
Add equality test
d0cd Jan 24, 2024
e05c5af
Clippy
d0cd Jan 24, 2024
15999db
Fix test
d0cd Jan 26, 2024
e12600c
Add tests
d0cd Jan 26, 2024
285435b
Merge branch 'mainnet' into bond_balances_in_genesis
howardwu Jan 28, 2024
3fca065
Merge branch 'mainnet' into bond_balances_in_genesis
howardwu Jan 28, 2024
04fff0d
Update ledger/block/src/ratify/serialize.rs
d0cd Jan 28, 2024
33c4af3
Add test
d0cd Jan 28, 2024
50c0673
Better error message
d0cd Jan 28, 2024
9e83c91
Box the elements of Genesis::Ratify
d0cd Jan 28, 2024
7540ac7
Reduce test MAXIMUM_CONFIRMED_TRANSACTIONS
d0cd Jan 28, 2024
c2ef7fb
Merge branch 'mainnet' into bond_balances_in_genesis
d0cd Feb 10, 2024
d7e719b
Fix tests
d0cd Feb 10, 2024
19f93ce
Update metadata mapping for delegators; update tests
d0cd Feb 10, 2024
6e93760
Move test to ledger crate; this is because committee is not update un…
d0cd Feb 10, 2024
30baf60
Clippy
d0cd Feb 10, 2024
caf3e38
Regen expectations
d0cd Feb 10, 2024
39596bc
Merge branch 'mainnet' into bond_balances_in_genesis
d0cd Feb 11, 2024
9bbf4fb
Fix
d0cd Feb 11, 2024
45113b9
Remove unnecessary checks
raychu86 Feb 12, 2024
74cf60c
Add additional check for total stake
raychu86 Feb 12, 2024
1cb2e52
Add test checking closed validator on genesis
d0cd Feb 12, 2024
1f07554
Merge branch 'mainnet' into bond_balances_in_genesis
d0cd Feb 12, 2024
c0e90e6
Merge branch 'mainnet' of https://github.com/AleoHQ/snarkVM into bond…
raychu86 Feb 12, 2024
1b6163c
Regenerate genesis block
raychu86 Feb 12, 2024
63ff34b
Merge mainnet
raychu86 Feb 12, 2024
a4e6c0e
Regenerate genesis block
raychu86 Feb 12, 2024
a01e333
Use saturating_sub
raychu86 Feb 12, 2024
48d4fab
Add correctness test
d0cd Feb 12, 2024
cf32b3c
Update synthesizer/src/vm/finalize.rs
d0cd Feb 13, 2024
9f2ca27
Add comments
d0cd Feb 13, 2024
02fcf3c
Add comments
d0cd Feb 13, 2024
425db4d
Resample expectations
raychu86 Feb 13, 2024
3f42b53
Abstract sampling the validators
howardwu Feb 13, 2024
6d4773a
Abstract sampling the validators
howardwu Feb 13, 2024
6cf37c6
Merge branch 'bond_balances_in_genesis' of https://github.com/AleoHQ/…
howardwu Feb 13, 2024
532b37b
Abstract the committee_map logic
howardwu Feb 13, 2024
073c949
Abstract the bonded balances
howardwu Feb 13, 2024
d4aea86
Abstract construction of public balances
d0cd Feb 13, 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
14 changes: 7 additions & 7 deletions Cargo.lock

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

26 changes: 23 additions & 3 deletions ledger/block/src/ratify/bytes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,22 @@ impl<N: Network> FromBytes for Ratify<N> {
// Insert the public balance.
public_balances.insert(address, amount);
}
// Read the number of bonded balances.
let num_bonded_balances: u16 = FromBytes::read_le(&mut reader)?;
// Read the bonded balances.
let mut bonded_balances = BondedBalances::with_capacity(num_bonded_balances as usize);
for _ in 0..num_bonded_balances {
// Read the address.
let address: Address<N> = FromBytes::read_le(&mut reader)?;
// Read the validator address.
let validator_address: Address<N> = FromBytes::read_le(&mut reader)?;
// Read the amount.
let amount: u64 = FromBytes::read_le(&mut reader)?;
// Insert the bonded balance.
bonded_balances.insert(address, (validator_address, amount));
}
// Return the ratify object.
Self::Genesis(committee, public_balances)
Self::Genesis(Box::new(committee), Box::new(public_balances), Box::new(bonded_balances))
}
1 => {
// Read the amount.
Expand All @@ -69,12 +83,18 @@ impl<N: Network> ToBytes for Ratify<N> {
1u8.write_le(&mut writer)?;

match self {
Self::Genesis(committee, public_balances) => {
Self::Genesis(committee, public_balances, bonded_balances) => {
(0 as Variant).write_le(&mut writer)?;
committee.write_le(&mut writer)?;
u16::try_from(public_balances.len()).map_err(|e| error(e.to_string()))?.write_le(&mut writer)?;
for (address, amount) in public_balances {
for (address, amount) in public_balances.iter() {
address.write_le(&mut writer)?;
amount.write_le(&mut writer)?;
}
u16::try_from(bonded_balances.len()).map_err(|e| error(e.to_string()))?.write_le(&mut writer)?;
for (address, (validator_address, amount)) in bonded_balances.iter() {
address.write_le(&mut writer)?;
validator_address.write_le(&mut writer)?;
amount.write_le(&mut writer)?;
}
Ok(())
Expand Down
19 changes: 17 additions & 2 deletions ledger/block/src/ratify/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,14 @@ use indexmap::IndexMap;
type Variant = u8;
/// A helper type to represent the public balances.
type PublicBalances<N> = IndexMap<Address<N>, u64>;
/// A helper type to represent the bonded balances.
type BondedBalances<N> = IndexMap<Address<N>, (Address<N>, u64)>;

// Note: The size of the `Ratify` object is 32 bytes.
#[derive(Clone, PartialEq, Eq)]
pub enum Ratify<N: Network> {
/// The genesis.
Genesis(Committee<N>, PublicBalances<N>),
Genesis(Box<Committee<N>>, Box<PublicBalances<N>>, Box<BondedBalances<N>>),
/// The block reward.
BlockReward(u64),
/// The puzzle reward.
Expand All @@ -55,11 +58,23 @@ pub(crate) mod test_helpers {
for (address, _) in committee.members().iter() {
public_balances.insert(*address, rng.gen());
}
let bonded_balances =
committee.members().iter().map(|(address, (amount, _))| (*address, (*address, *amount))).collect();

vec![
Ratify::Genesis(committee, public_balances),
Ratify::Genesis(Box::new(committee), Box::new(public_balances), Box::new(bonded_balances)),
Ratify::BlockReward(rng.gen()),
Ratify::PuzzleReward(rng.gen()),
]
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn check_ratify_size() {
assert_eq!(std::mem::size_of::<Ratify<console::network::MainnetV0>>(), 32);
}
}
10 changes: 7 additions & 3 deletions ledger/block/src/ratify/serialize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ impl<N: Network> Serialize for Ratify<N> {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
match serializer.is_human_readable() {
true => match self {
Self::Genesis(committee, public_balances) => {
let mut input = serializer.serialize_struct("Ratify", 3)?;
Self::Genesis(committee, public_balances, bonded_balances) => {
let mut input = serializer.serialize_struct("Ratify", 4)?;
input.serialize_field("type", "genesis")?;
input.serialize_field("committee", &committee)?;
input.serialize_field("public_balances", &public_balances)?;
input.serialize_field("bonded_balances", &bonded_balances)?;
input.end()
}
Self::BlockReward(amount) => {
Expand Down Expand Up @@ -60,8 +61,11 @@ impl<'de, N: Network> Deserialize<'de> for Ratify<N> {
// Retrieve the public balances.
let public_balances: PublicBalances<N> =
DeserializeExt::take_from_value::<D>(&mut object, "public_balances")?;
// Retrieve the bonded balances.
let bonded_balances: BondedBalances<N> =
DeserializeExt::take_from_value::<D>(&mut object, "bonded_balances")?;
// Construct the ratify object.
Ratify::Genesis(committee, public_balances)
Ratify::Genesis(Box::new(committee), Box::new(public_balances), Box::new(bonded_balances))
}
Some("block_reward") => {
// Retrieve the amount.
Expand Down
2 changes: 2 additions & 0 deletions ledger/committee/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ use std::collections::HashSet;
pub const MIN_VALIDATOR_STAKE: u64 = 1_000_000_000_000u64; // microcredits
/// The minimum amount of stake required for a delegator to bond.
pub const MIN_DELEGATOR_STAKE: u64 = 10_000_000u64; // microcredits
/// The maximum number of delegators.
pub const MAX_DELEGATORS: u32 = 100_000u32;

#[derive(Clone, PartialEq, Eq)]
pub struct Committee<N: Network> {
Expand Down
196 changes: 196 additions & 0 deletions ledger/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
use crate::{
advance::split_candidate_solutions,
test_helpers::{CurrentLedger, CurrentNetwork},
Ledger,
RecordsFilter,
};
use aleo_std::StorageMode;
Expand All @@ -23,7 +24,9 @@ use console::{
network::prelude::*,
program::{Entry, Identifier, Literal, Plaintext, ProgramID, Value},
};
use indexmap::IndexMap;
use ledger_block::{ConfirmedTransaction, Rejected, Transaction};
use ledger_committee::{Committee, MIN_VALIDATOR_STAKE};
use ledger_store::{helpers::memory::ConsensusMemory, ConsensusStore};
use synthesizer::{program::Program, vm::VM};

Expand Down Expand Up @@ -544,6 +547,23 @@ fn test_bond_and_unbond_validator() {
let committee = ledger.latest_committee().unwrap();
assert!(committee.is_committee_member(new_member_address));

// Check that number of validators in the `metadata` mapping in `credtis.aleo` is updated.
let program_id = ProgramID::<CurrentNetwork>::from_str("credits.aleo").unwrap();
let metadata_mapping_name = Identifier::from_str("metadata").unwrap();
let key = Plaintext::<CurrentNetwork>::from_str("aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc")
.unwrap();
let num_validators = match ledger
.vm()
.finalize_store()
.get_value_confirmed(program_id, metadata_mapping_name, &key)
.unwrap()
.unwrap()
{
Value::Plaintext(Plaintext::Literal(Literal::U32(num_validators), _)) => *num_validators as usize,
_ => panic!("Unexpected value type"),
};
assert_eq!(num_validators, committee.num_members());

// Construct the bond public
let unbond_amount = committee.get_stake(new_member_address); // 1 million credits.
let inputs = [Value::from_str(&format!("{unbond_amount}u64")).unwrap()];
Expand All @@ -566,6 +586,23 @@ fn test_bond_and_unbond_validator() {
// Check that the committee does not include the new member.
let committee = ledger.latest_committee().unwrap();
assert!(!committee.is_committee_member(new_member_address));

// Check that number of validators in the `metadata` mapping in `credtis.aleo` is updated.
let program_id = ProgramID::<CurrentNetwork>::from_str("credits.aleo").unwrap();
let metadata_mapping_name = Identifier::from_str("metadata").unwrap();
let key = Plaintext::<CurrentNetwork>::from_str("aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc")
.unwrap();
let num_validators = match ledger
.vm()
.finalize_store()
.get_value_confirmed(program_id, metadata_mapping_name, &key)
.unwrap()
.unwrap()
{
Value::Plaintext(Plaintext::Literal(Literal::U32(num_validators), _)) => *num_validators as usize,
_ => panic!("Unexpected value type"),
};
assert_eq!(num_validators, committee.num_members());
}

#[test]
Expand Down Expand Up @@ -1301,3 +1338,162 @@ fn test_split_candidate_solutions() {
split_candidate_solutions(candidate_solutions, max_solutions, |candidate| candidate % 2 == 0);
}
}

#[test]
fn test_max_committee_limit_with_bonds() {
// Initialize an RNG.
let rng = &mut TestRng::default();

// Initialize the VM.
let vm = VM::from(ConsensusStore::<CurrentNetwork, ConsensusMemory<CurrentNetwork>>::open(None).unwrap()).unwrap();

// Construct the validators, one less than the maximum committee size.
let validators = (0..Committee::<CurrentNetwork>::MAX_COMMITTEE_SIZE - 1)
.map(|_| {
let private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let amount = MIN_VALIDATOR_STAKE;
let is_open = true;
(private_key, (amount, is_open))
})
.collect::<IndexMap<_, _>>();

// Track the allocated amount.
let mut allocated_amount = 0;

// Construct the committee.
let mut committee_map = IndexMap::new();
for (private_key, (amount, _)) in &validators {
let address = Address::try_from(private_key).unwrap();
committee_map.insert(address, (*amount, true));
allocated_amount += *amount;
}

// Initialize two new validators.
let first_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let first_address = Address::try_from(&first_private_key).unwrap();
let second_private_key = PrivateKey::<CurrentNetwork>::new(rng).unwrap();
let second_address = Address::try_from(&second_private_key).unwrap();

// Construct the public balances, allocating the remaining supply to the first validator and two new validators.
// The remaining validators will have a balance of 0.
let mut public_balances = IndexMap::new();
for (private_key, _) in &validators {
public_balances.insert(Address::try_from(private_key).unwrap(), 0);
}
let remaining_supply = <CurrentNetwork as Network>::STARTING_SUPPLY - allocated_amount;
let amount = remaining_supply / 3;
public_balances.insert(Address::try_from(validators.keys().next().unwrap()).unwrap(), amount);
public_balances.insert(first_address, amount);
public_balances.insert(second_address, remaining_supply - 2 * amount);

// Construct the bonded balances.
let bonded_balances = validators
.iter()
.map(|(private_key, (amount, _))| {
let address = Address::try_from(private_key).unwrap();
(address, (address, *amount))
})
.collect();

// Construct the genesis block, which should pass.
let genesis_block = vm
.genesis_quorum(
validators.keys().next().unwrap(),
Committee::new_genesis(committee_map).unwrap(),
public_balances,
bonded_balances,
rng,
)
.unwrap();

// Initialize a Ledger from the genesis block.
let ledger =
Ledger::<CurrentNetwork, ConsensusMemory<CurrentNetwork>>::load(genesis_block, StorageMode::Production)
.unwrap();

// Bond the first validator.
let bond_first_transaction = ledger
.vm()
.execute(
&first_private_key,
("credits.aleo", "bond_public"),
vec![
Value::<CurrentNetwork>::from_str(&first_address.to_string()).unwrap(),
Value::<CurrentNetwork>::from_str(&format!("{MIN_VALIDATOR_STAKE}u64")).unwrap(),
]
.iter(),
None,
0,
None,
rng,
)
.unwrap();

// Create a block.
let block = ledger
.prepare_advance_to_next_beacon_block(
validators.keys().next().unwrap(),
vec![],
vec![],
vec![bond_first_transaction],
rng,
)
.unwrap();

// Check that the next block is valid.
ledger.check_next_block(&block, rng).unwrap();

// Check that the first validator is not in the committee.
let committee = ledger.latest_committee().unwrap();
assert!(!committee.is_committee_member(first_address));

// Add the block to the ledger.
ledger.advance_to_next_block(&block).unwrap();

// Check that the first validator was added to the committee.
let committee = ledger.latest_committee().unwrap();
assert!(committee.is_committee_member(first_address));

// Attempt to bond the second validator.
let bond_second_transaction = ledger
.vm()
.execute(
&second_private_key,
("credits.aleo", "bond_public"),
vec![
Value::<CurrentNetwork>::from_str(&second_address.to_string()).unwrap(),
Value::<CurrentNetwork>::from_str(&format!("{MIN_VALIDATOR_STAKE}u64")).unwrap(),
]
.iter(),
None,
0,
None,
rng,
)
.unwrap();

// Create a block.
let block = ledger
.prepare_advance_to_next_beacon_block(
validators.keys().next().unwrap(),
vec![],
vec![],
vec![bond_second_transaction],
rng,
)
.unwrap();

// Check that the next block is valid.
ledger.check_next_block(&block, rng).unwrap();

// Check that the second validator is not in the committee.
let committee = ledger.latest_committee().unwrap();
assert!(!committee.is_committee_member(second_address));

// Add the block to the ledger.
ledger.advance_to_next_block(&block).unwrap();

// Check that the second validator was not added to the committee.
let committee = ledger.latest_committee().unwrap();
assert!(!committee.is_committee_member(second_address));
}
Loading