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 6 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
24 changes: 22 additions & 2 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(committee, public_balances, bonded_balances)
}
1 => {
// Read the amount.
Expand All @@ -69,14 +83,20 @@ 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 {
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 {
address.write_le(&mut writer)?;
validator_address.write_le(&mut writer)?;
amount.write_le(&mut writer)?;
}
Ok(())
}
Self::BlockReward(amount) => {
Expand Down
10 changes: 8 additions & 2 deletions ledger/block/src/ratify/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ 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)>;

#[allow(clippy::large_enum_variant)]
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder if this has a non-trivial impact on object/storage size?

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 might have some impact, but the jump from (Committee<N>, PublicBalances<N>) to (Committee<N>, PublicBalances<N>, BondedBalances<N>) won't be that big.

Copy link
Contributor

Choose a reason for hiding this comment

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

Considering every block contains this enum, and we will always pay the worst-case memory footprint, I don't understand why one chose to ignore this clippy warning.

You should follow the advise of clippy to resolve it (I assume its to use Box). Otherwise, every enum variant will always pay the worst case bound.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

why one chose to ignore this clippy warning
By being too much in a get shit done mood, my bad.

@d0cd since you've been finishing off the PR, want to wrap the contents in Box? I also just recently noticed we're already using this pattern in snarkVM.

Copy link
Contributor

Choose a reason for hiding this comment

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

Done! By boxing the elements of Genesis::Ratify the size reduces from 240 to 32 bytes.

// Note: The size of the `Ratify` object is 240 bytes.
#[derive(Clone, PartialEq, Eq)]
pub enum Ratify<N: Network> {
/// The genesis.
Genesis(Committee<N>, PublicBalances<N>),
Genesis(Committee<N>, PublicBalances<N>, BondedBalances<N>),
/// The block reward.
BlockReward(u64),
/// The puzzle reward.
Expand All @@ -55,9 +59,11 @@ 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(committee, public_balances, bonded_balances),
Ratify::BlockReward(rng.gen()),
Ratify::PuzzleReward(rng.gen()),
]
Expand Down
8 changes: 6 additions & 2 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) => {
Self::Genesis(committee, public_balances, bonded_balances) => {
let mut input = serializer.serialize_struct("Ratify", 3)?;
d0cd marked this conversation as resolved.
Show resolved Hide resolved
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(committee, public_balances, bonded_balances)
}
Some("block_reward") => {
// Retrieve the amount.
Expand Down
67 changes: 55 additions & 12 deletions synthesizer/src/vm/finalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,12 +182,12 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {

// Initialize an iterator for ratifications before finalize.
let pre_ratifications = ratifications.iter().filter(|r| match r {
Ratify::Genesis(_, _) => true,
Ratify::Genesis(_, _, _) => true,
Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => false,
});
// Initialize an iterator for ratifications after finalize.
let post_ratifications = ratifications.iter().filter(|r| match r {
Ratify::Genesis(_, _) => false,
Ratify::Genesis(_, _, _) => false,
Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => true,
});

Expand Down Expand Up @@ -464,12 +464,12 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
atomic_finalize!(self.finalize_store(), FinalizeMode::RealRun, {
// Initialize an iterator for ratifications before finalize.
let pre_ratifications = ratifications.iter().filter(|r| match r {
Ratify::Genesis(_, _) => true,
Ratify::Genesis(_, _, _) => true,
Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => false,
});
// Initialize an iterator for ratifications after finalize.
let post_ratifications = ratifications.iter().filter(|r| match r {
Ratify::Genesis(_, _) => false,
Ratify::Genesis(_, _, _) => false,
Ratify::BlockReward(..) | Ratify::PuzzleReward(..) => true,
});

Expand Down Expand Up @@ -707,7 +707,7 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
// Iterate over the ratifications.
for ratify in pre_ratifications {
match ratify {
Ratify::Genesis(committee, public_balances) => {
Ratify::Genesis(committee, public_balances, bonded_balances) => {
// Ensure this is the genesis block.
ensure!(state.block_height() == 0, "Ratify::Genesis(..) expected a genesis block");
// Ensure the genesis committee round is 0.
Expand All @@ -730,17 +730,60 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
// }
// }

// Initialize the stakers.
let mut stakers = IndexMap::with_capacity(committee.members().len());
// Iterate over the committee members.
for (validator, (microcredits, _)) in committee.members() {
// Insert the validator into the stakers.
stakers.insert(*validator, (*validator, *microcredits));
// Calculate the stake per validator using `bonded_balances`.
let mut stake_per_validator = IndexMap::with_capacity(committee.members().len());
howardwu marked this conversation as resolved.
Show resolved Hide resolved
for (address, (validator_address, amount)) in bonded_balances {
// Check that the amount meets the minimum requirement, depending on whether the address is a validator.
if *address == *validator_address {
ensure!(
*amount >= 1_000_000_000_000_u64,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a way to ensure that these values match the credits.aleo minimum requirements? Might be good to write some comments and maybe add some tests for enforcement to catch any changes down the line.

"Ratify::Genesis(..) the validator {address} must stake at least 1_000_000_000_000",
);
} else {
ensure!(
*amount >= 10_000_000_u64,
"Ratify::Genesis(..) the delegator {address} must stake at least 10_000_000",
);
// If the address is a delegator, check that the corresponding validator is open.
ensure!(
committee.is_committee_member_open(*validator_address),
"Ratify::Genesis(..) the delegator {address} is delegating to a closed validator {validator_address}",
);
}
// Ensure that each staker has an entry in `public_balances`.
ensure!(
public_balances.contains_key(address),
"Ratify::Genesis(..) the staker {address} is missing from the public balances",
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is this check necessary? Unless you decrement from public_balances, remove this check, it is extraneous.

Copy link
Contributor

Choose a reason for hiding this comment

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

This check was introduced to be consistent with credits.aleo.
In the finalize block of bond_public, we check that a corresponding entry in the account mapping exists for both validators and delegators.

// Accumulate the staked amount per validator.
let total = stake_per_validator.entry(validator_address).or_insert(0u64);
*total = total.saturating_add(*amount);
raychu86 marked this conversation as resolved.
Show resolved Hide resolved
}
// Ensure the stake per validator matches the committee.
ensure!(
stake_per_validator.len() == committee.members().len(),
"Ratify::Genesis(..) the number of validators in the committee does not match the number of validators in the bonded balances",
);

// Check that `committee` is consistent with `stake_per_validator`.
for (validator_address, amount) in stake_per_validator {
// Retrieve the expected validator stake from the committee.
let expected_amount = match committee.members().get(validator_address) {
Some((amount, _)) => *amount,
None => bail!(
"Ratify::Genesis(..) found a validator in the bonded balances that is not in the committee"
),
};
howardwu marked this conversation as resolved.
Show resolved Hide resolved
// Ensure the staked amount matches the committee.
ensure!(
expected_amount == amount,
"Ratify::Genesis(..) inconsistent staked amount for validator {validator_address}",
);
}

// Construct the next committee map and next bonded map.
let (next_committee_map, next_bonded_map) =
to_next_commitee_map_and_bonded_map(committee, &stakers);
to_next_commitee_map_and_bonded_map(committee, bonded_balances);
raychu86 marked this conversation as resolved.
Show resolved Hide resolved

// Insert the next committee into storage.
store.committee_store().insert(state.block_height(), committee.clone())?;
Expand Down
8 changes: 6 additions & 2 deletions synthesizer/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,11 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
Address::try_from(private_keys[2])? => remaining_supply / 4,
Address::try_from(private_keys[3])? => remaining_supply / 4,
};
// Construct the bonded balances.
let bonded_balances =
committee.members().iter().map(|(address, (amount, _))| (*address, (*address, *amount))).collect();
// Return the genesis block.
self.genesis_quorum(private_key, committee, public_balances, rng)
self.genesis_quorum(private_key, committee, public_balances, bonded_balances, rng)
}

/// Returns a new genesis block for a quorum chain.
Expand All @@ -254,6 +257,7 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
private_key: &PrivateKey<N>,
committee: Committee<N>,
public_balances: IndexMap<Address<N>, u64>,
bonded_balances: IndexMap<Address<N>, (Address<N>, u64)>,
rng: &mut R,
) -> Result<Block<N>> {
// Retrieve the total stake.
Expand All @@ -275,7 +279,7 @@ impl<N: Network, C: ConsensusStorage<N>> VM<N, C> {
let inputs = [caller.to_string(), format!("{amount}_u64")];

// Prepare the ratifications.
let ratifications = vec![Ratify::Genesis(committee, public_balances)];
let ratifications = vec![Ratify::Genesis(committee, public_balances, bonded_balances)];
// Prepare the solutions.
let solutions = None; // The genesis block does not require solutions.
// Prepare the transactions.
Expand Down