diff --git a/console/network/src/canary_v0.rs b/console/network/src/canary_v0.rs index c6dfd55a65..a45c4ab290 100644 --- a/console/network/src/canary_v0.rs +++ b/console/network/src/canary_v0.rs @@ -87,6 +87,17 @@ lazy_static! { map }; + pub static ref CANARY_CREDITS_V1_PROVING_KEYS: IndexMap>> = { + let mut map = IndexMap::new(); + snarkvm_parameters::insert_canary_credit_v1_keys!(map, VarunaProvingKey, Prover); + map + }; + pub static ref CANARY_CREDITS_V1_VERIFYING_KEYS: IndexMap>> = { + let mut map = IndexMap::new(); + snarkvm_parameters::insert_canary_credit_v1_keys!(map, VarunaVerifyingKey, Verifier); + map + }; + pub static ref CANARY_CREDITS_PROVING_KEYS: IndexMap>> = { let mut map = IndexMap::new(); snarkvm_parameters::insert_canary_credit_keys!(map, VarunaProvingKey, Prover); @@ -225,6 +236,20 @@ impl Network for CanaryV0 { .ok_or_else(|| anyhow!("Verifying key (v0) for credits_v0.aleo/{function_name}' not found")) } + /// Returns the proving key for the given function name in the v1 version of `credits.aleo`. + fn get_credits_v1_proving_key(function_name: String) -> Result<&'static Arc>> { + CANARY_CREDITS_V1_PROVING_KEYS + .get(&function_name) + .ok_or_else(|| anyhow!("Proving key (v0) for credits.aleo/{function_name}' not found")) + } + + /// Returns the verifying key for the given function name in the v1 version of `credits.aleo`. + fn get_credits_v1_verifying_key(function_name: String) -> Result<&'static Arc>> { + CANARY_CREDITS_V1_VERIFYING_KEYS + .get(&function_name) + .ok_or_else(|| anyhow!("Verifying key (v0) for credits_v0.aleo/{function_name}' not found")) + } + /// Returns the proving key for the given function name in `credits.aleo`. fn get_credits_proving_key(function_name: String) -> Result<&'static Arc>> { CANARY_CREDITS_PROVING_KEYS diff --git a/console/network/src/consensus_heights.rs b/console/network/src/consensus_heights.rs index 0f246882fc..9c551bee1c 100644 --- a/console/network/src/consensus_heights.rs +++ b/console/network/src/consensus_heights.rs @@ -54,8 +54,9 @@ pub enum ConsensusVersion { /// Introduces `aleo::GENERATOR`, `aleo::GENERATOR_POWERS`, `snark.verify` opcodes, /// and dynamic dispatch, and identifier literal types. V14 = 14, - /// V15: Introduces the record-existence check and `commit.*.raw` instruction variants. - /// Increase the anchor time to 35. + /// V15: Introduces the record-existence check, `commit.*.raw` instruction variants, + /// and `redelegate` to `credits.aleo` + /// Increase the anchor time to 35 and updates the unbonding period. V15 = 15, } diff --git a/console/network/src/lib.rs b/console/network/src/lib.rs index 9ef59c579b..51439ab69e 100644 --- a/console/network/src/lib.rs +++ b/console/network/src/lib.rs @@ -36,7 +36,6 @@ mod mainnet_v0; pub use mainnet_v0::*; mod testnet_v0; - pub use testnet_v0::*; pub mod prelude { @@ -371,6 +370,12 @@ pub trait Network: /// Returns the verifying key for the given function name in the v0 version of `credits.aleo`. fn get_credits_v0_verifying_key(function_name: String) -> Result<&'static Arc>>; + /// Returns the proving key for the given function name in the V1 version of `credits.aleo`. + fn get_credits_v1_proving_key(function_name: String) -> Result<&'static Arc>>; + + /// Returns the verifying key for the given function name in the V1 version of `credits.aleo`. + fn get_credits_v1_verifying_key(function_name: String) -> Result<&'static Arc>>; + /// Returns the proving key for the given function name in `credits.aleo`. fn get_credits_proving_key(function_name: String) -> Result<&'static Arc>>; diff --git a/console/network/src/mainnet_v0.rs b/console/network/src/mainnet_v0.rs index a2b3ded30b..525f031ba9 100644 --- a/console/network/src/mainnet_v0.rs +++ b/console/network/src/mainnet_v0.rs @@ -75,6 +75,7 @@ lazy_static! { /// The Poseidon path hasher for dynamic records, using a rate of 2. pub static ref DYNAMIC_RECORD_PATH_HASHER: Poseidon2 = Poseidon2::::setup("DynamicRecordPathHasher").expect("Failed to setup DynamicRecordPathHasher"); + // TODO (raychu86): Determine if we can optimize by clearing from lazy_static after they are no longer necessary. pub static ref CREDITS_V0_PROVING_KEYS: IndexMap>> = { let mut map = IndexMap::new(); snarkvm_parameters::insert_credit_v0_keys!(map, VarunaProvingKey, Prover); @@ -86,6 +87,17 @@ lazy_static! { map }; + pub static ref CREDITS_V1_PROVING_KEYS: IndexMap>> = { + let mut map = IndexMap::new(); + snarkvm_parameters::insert_credit_v1_keys!(map, VarunaProvingKey, Prover); + map + }; + pub static ref CREDITS_V1_VERIFYING_KEYS: IndexMap>> = { + let mut map = IndexMap::new(); + snarkvm_parameters::insert_credit_v1_keys!(map, VarunaVerifyingKey, Verifier); + map + }; + pub static ref CREDITS_PROVING_KEYS: IndexMap>> = { let mut map = IndexMap::new(); snarkvm_parameters::insert_credit_keys!(map, VarunaProvingKey, Prover); @@ -230,6 +242,20 @@ impl Network for MainnetV0 { .ok_or_else(|| anyhow!("Verifying key (v0) for credits_v0.aleo/{function_name}' not found")) } + /// Returns the proving key for the given function name in the v1 version of `credits.aleo`. + fn get_credits_v1_proving_key(function_name: String) -> Result<&'static Arc>> { + CREDITS_V1_PROVING_KEYS + .get(&function_name) + .ok_or_else(|| anyhow!("Proving key for credits.aleo/{function_name}' not found")) + } + + /// Returns the verifying key for the given function name in the v1 version of `credits.aleo`. + fn get_credits_v1_verifying_key(function_name: String) -> Result<&'static Arc>> { + CREDITS_V1_VERIFYING_KEYS + .get(&function_name) + .ok_or_else(|| anyhow!("Verifying key for credits.aleo/{function_name}' not found")) + } + /// Returns the proving key for the given function name in `credits.aleo`. fn get_credits_proving_key(function_name: String) -> Result<&'static Arc>> { CREDITS_PROVING_KEYS diff --git a/console/network/src/testnet_v0.rs b/console/network/src/testnet_v0.rs index d45bb38459..3d5c42a77e 100644 --- a/console/network/src/testnet_v0.rs +++ b/console/network/src/testnet_v0.rs @@ -87,6 +87,17 @@ lazy_static! { map }; + pub static ref TESTNET_CREDITS_V1_PROVING_KEYS: IndexMap>> = { + let mut map = IndexMap::new(); + snarkvm_parameters::insert_testnet_credit_v1_keys!(map, VarunaProvingKey, Prover); + map + }; + pub static ref TESTNET_CREDITS_V1_VERIFYING_KEYS: IndexMap>> = { + let mut map = IndexMap::new(); + snarkvm_parameters::insert_testnet_credit_v1_keys!(map, VarunaVerifyingKey, Verifier); + map + }; + pub static ref TESTNET_CREDITS_PROVING_KEYS: IndexMap>> = { let mut map = IndexMap::new(); snarkvm_parameters::insert_testnet_credit_keys!(map, VarunaProvingKey, Prover); @@ -225,6 +236,20 @@ impl Network for TestnetV0 { .ok_or_else(|| anyhow!("Verifying key (v0) for credits_v0.aleo/{function_name}' not found")) } + /// Returns the proving key for the given function name in the v1 version of `credits.aleo`. + fn get_credits_v1_proving_key(function_name: String) -> Result<&'static Arc>> { + TESTNET_CREDITS_V1_PROVING_KEYS + .get(&function_name) + .ok_or_else(|| anyhow!("Proving key (v0) for credits.aleo/{function_name}' not found")) + } + + /// Returns the verifying key for the given function name in the v1 version of `credits.aleo`. + fn get_credits_v1_verifying_key(function_name: String) -> Result<&'static Arc>> { + TESTNET_CREDITS_V1_VERIFYING_KEYS + .get(&function_name) + .ok_or_else(|| anyhow!("Verifying key (v0) for credits_v0.aleo/{function_name}' not found")) + } + /// Returns the proving key for the given function name in `credits.aleo`. fn get_credits_proving_key(function_name: String) -> Result<&'static Arc>> { TESTNET_CREDITS_PROVING_KEYS diff --git a/ledger/block/src/transition/mod.rs b/ledger/block/src/transition/mod.rs index ee083b1e0f..0ba1adbb2e 100644 --- a/ledger/block/src/transition/mod.rs +++ b/ledger/block/src/transition/mod.rs @@ -559,6 +559,15 @@ impl Transition { && self.program_id.to_string() == "credits.aleo" && self.function_name.to_string() == "upgrade" } + + /// Returns `true` if this is a `redelegate` transition. + #[inline] + pub fn is_redelegate(&self) -> bool { + self.inputs.len() == 1 + && self.outputs.len() == 1 + && self.program_id.to_string() == "credits.aleo" + && self.function_name.to_string() == "redelegate" + } } impl Transition { diff --git a/ledger/store/src/transaction/deployment.rs b/ledger/store/src/transaction/deployment.rs index a5a1c31e49..ab0d4f852d 100644 --- a/ledger/store/src/transaction/deployment.rs +++ b/ledger/store/src/transaction/deployment.rs @@ -1419,7 +1419,11 @@ impl> DeploymentStore { // Insert `credits.aleo`, which is the default program. let credits_id = ProgramID::from_str("credits.aleo")?; storage.edition_map().insert(credits_id, 0)?; - storage.program_map().insert((credits_id, 0), Program::credits()?)?; + storage.program_map().insert((credits_id, 0), Program::credits_v0()?)?; + storage.edition_map().insert(credits_id, 1)?; + storage.program_map().insert((credits_id, 1), Program::credits_v1()?)?; + storage.edition_map().insert(credits_id, 2)?; + storage.program_map().insert((credits_id, 2), Program::credits()?)?; // Return the deployment store. Ok(Self { storage, _phantom: PhantomData }) diff --git a/parameters/src/canary/mod.rs b/parameters/src/canary/mod.rs index f36fd587d7..547539275a 100644 --- a/parameters/src/canary/mod.rs +++ b/parameters/src/canary/mod.rs @@ -21,51 +21,59 @@ pub const RESTRICTIONS_LIST: &str = include_str!("./resources/restrictions.json" const REMOTE_URLS: [&str; 2] = ["https://parameters.provable.com/canary", "https://s3.us-west-1.amazonaws.com/canary.parameters"]; +// V2 Credits Keys + +// Redelegate +impl_remote!(RedelegateProver, REMOTE_URLS, "resources/", "redelegate", "prover", "credits"); +impl_local!(RedelegateVerifier, "resources/", "redelegate", "verifier", "credits"); + +// V1 and V2 Credits Keys + // BondPublic -impl_remote!(BondPublicProver, REMOTE_URLS, "resources/", "bond_public", "prover", "credits"); -impl_local!(BondPublicVerifier, "resources/", "bond_public", "verifier", "credits"); +impl_remote!(BondPublicProver, REMOTE_URLS, "resources/", "bond_public", "prover", "credits_v1"); +impl_local!(BondPublicVerifier, "resources/", "bond_public", "verifier", "credits_v1"); // BondValidator -impl_remote!(BondValidatorProver, REMOTE_URLS, "resources/", "bond_validator", "prover", "credits"); -impl_local!(BondValidatorVerifier, "resources/", "bond_validator", "verifier", "credits"); +impl_remote!(BondValidatorProver, REMOTE_URLS, "resources/", "bond_validator", "prover", "credits_v1"); +impl_local!(BondValidatorVerifier, "resources/", "bond_validator", "verifier", "credits_v1"); // UnbondPublic -impl_remote!(UnbondPublicProver, REMOTE_URLS, "resources/", "unbond_public", "prover", "credits"); -impl_local!(UnbondPublicVerifier, "resources/", "unbond_public", "verifier", "credits"); +impl_remote!(UnbondPublicProver, REMOTE_URLS, "resources/", "unbond_public", "prover", "credits_v1"); +impl_local!(UnbondPublicVerifier, "resources/", "unbond_public", "verifier", "credits_v1"); // ClaimUnbondPublic -impl_remote!(ClaimUnbondPublicProver, REMOTE_URLS, "resources/", "claim_unbond_public", "prover", "credits"); -impl_local!(ClaimUnbondPublicVerifier, "resources/", "claim_unbond_public", "verifier", "credits"); +impl_remote!(ClaimUnbondPublicProver, REMOTE_URLS, "resources/", "claim_unbond_public", "prover", "credits_v1"); +impl_local!(ClaimUnbondPublicVerifier, "resources/", "claim_unbond_public", "verifier", "credits_v1"); // SetValidatorState -impl_remote!(SetValidatorStateProver, REMOTE_URLS, "resources/", "set_validator_state", "prover", "credits"); -impl_local!(SetValidatorStateVerifier, "resources/", "set_validator_state", "verifier", "credits"); +impl_remote!(SetValidatorStateProver, REMOTE_URLS, "resources/", "set_validator_state", "prover", "credits_v1"); +impl_local!(SetValidatorStateVerifier, "resources/", "set_validator_state", "verifier", "credits_v1"); // TransferPrivate -impl_remote!(TransferPrivateProver, REMOTE_URLS, "resources/", "transfer_private", "prover", "credits"); -impl_local!(TransferPrivateVerifier, "resources/", "transfer_private", "verifier", "credits"); +impl_remote!(TransferPrivateProver, REMOTE_URLS, "resources/", "transfer_private", "prover", "credits_v1"); +impl_local!(TransferPrivateVerifier, "resources/", "transfer_private", "verifier", "credits_v1"); // TransferPublic -impl_remote!(TransferPublicProver, REMOTE_URLS, "resources/", "transfer_public", "prover", "credits"); -impl_local!(TransferPublicVerifier, "resources/", "transfer_public", "verifier", "credits"); +impl_remote!(TransferPublicProver, REMOTE_URLS, "resources/", "transfer_public", "prover", "credits_v1"); +impl_local!(TransferPublicVerifier, "resources/", "transfer_public", "verifier", "credits_v1"); // TransferPublicAsSigner -impl_remote!(TransferPublicAsSignerProver, REMOTE_URLS, "resources/", "transfer_public_as_signer", "prover", "credits"); -impl_local!(TransferPublicAsSignerVerifier, "resources/", "transfer_public_as_signer", "verifier", "credits"); +impl_remote!(TransferPublicAsSignerProver, REMOTE_URLS, "resources/", "transfer_public_as_signer", "prover", "credits_v1"); +impl_local!(TransferPublicAsSignerVerifier, "resources/", "transfer_public_as_signer", "verifier", "credits_v1"); // TransferPrivateToPublic -impl_remote!(TransferPrivateToPublicProver, REMOTE_URLS, "resources/", "transfer_private_to_public", "prover", "credits"); -impl_local!(TransferPrivateToPublicVerifier, "resources/", "transfer_private_to_public", "verifier", "credits"); +impl_remote!(TransferPrivateToPublicProver, REMOTE_URLS, "resources/", "transfer_private_to_public", "prover", "credits_v1"); +impl_local!(TransferPrivateToPublicVerifier, "resources/", "transfer_private_to_public", "verifier", "credits_v1"); // TransferPublicToPrivate -impl_remote!(TransferPublicToPrivateProver, REMOTE_URLS, "resources/", "transfer_public_to_private", "prover", "credits"); -impl_local!(TransferPublicToPrivateVerifier, "resources/", "transfer_public_to_private", "verifier", "credits"); +impl_remote!(TransferPublicToPrivateProver, REMOTE_URLS, "resources/", "transfer_public_to_private", "prover", "credits_v1"); +impl_local!(TransferPublicToPrivateVerifier, "resources/", "transfer_public_to_private", "verifier", "credits_v1"); // Join -impl_remote!(JoinProver, REMOTE_URLS, "resources/", "join", "prover", "credits"); -impl_local!(JoinVerifier, "resources/", "join", "verifier", "credits"); +impl_remote!(JoinProver, REMOTE_URLS, "resources/", "join", "prover", "credits_v1"); +impl_local!(JoinVerifier, "resources/", "join", "verifier", "credits_v1"); // Split -impl_remote!(SplitProver, REMOTE_URLS, "resources/", "split", "prover", "credits"); -impl_local!(SplitVerifier, "resources/", "split", "verifier", "credits"); +impl_remote!(SplitProver, REMOTE_URLS, "resources/", "split", "prover", "credits_v1"); +impl_local!(SplitVerifier, "resources/", "split", "verifier", "credits_v1"); // FeePrivate -impl_remote!(FeePrivateProver, REMOTE_URLS, "resources/", "fee_private", "prover", "credits"); -impl_local!(FeePrivateVerifier, "resources/", "fee_private", "verifier", "credits"); +impl_remote!(FeePrivateProver, REMOTE_URLS, "resources/", "fee_private", "prover", "credits_v1"); +impl_local!(FeePrivateVerifier, "resources/", "fee_private", "verifier", "credits_v1"); // FeePublic -impl_remote!(FeePublicProver, REMOTE_URLS, "resources/", "fee_public", "prover", "credits"); -impl_local!(FeePublicVerifier, "resources/", "fee_public", "verifier", "credits"); +impl_remote!(FeePublicProver, REMOTE_URLS, "resources/", "fee_public", "prover", "credits_v1"); +impl_local!(FeePublicVerifier, "resources/", "fee_public", "verifier", "credits_v1"); // Upgrade -impl_remote!(UpgradeProver, REMOTE_URLS, "resources/", "upgrade", "prover", "credits"); -impl_local!(UpgradeVerifier, "resources/", "upgrade", "verifier", "credits"); +impl_remote!(UpgradeProver, REMOTE_URLS, "resources/", "upgrade", "prover", "credits_v1"); +impl_local!(UpgradeVerifier, "resources/", "upgrade", "verifier", "credits_v1"); // V0 Credits Keys @@ -117,6 +125,17 @@ impl_local!(UpgradeV0Verifier, "resources/", "upgrade", "verifier", "credits_v0" #[macro_export] macro_rules! insert_canary_credit_keys { + ($map:ident, $type:ident<$network:ident>, $variant:ident) => {{ + paste::paste! { + let string = stringify!([<$variant:lower>]); + $crate::insert_canary_credit_v1_keys!($map, $type<$network>, $variant); + $crate::insert_canary_key!($map, string, $type<$network>, ("redelegate", $crate::canary::[]::load_bytes())); + } + }}; +} + +#[macro_export] +macro_rules! insert_canary_credit_v1_keys { ($map:ident, $type:ident<$network:ident>, $variant:ident) => {{ paste::paste! { let string = stringify!([<$variant:lower>]); @@ -178,8 +197,8 @@ macro_rules! insert_canary_key { // Inclusion impl_remote!(InclusionV0Prover, REMOTE_URLS, "resources/", "inclusion", "prover", "credits_v0"); impl_local!(InclusionV0Verifier, "resources/", "inclusion", "verifier", "credits_v0"); -impl_remote!(InclusionProver, REMOTE_URLS, "resources/", "inclusion", "prover", "credits"); -impl_local!(InclusionVerifier, "resources/", "inclusion", "verifier", "credits"); +impl_remote!(InclusionProver, REMOTE_URLS, "resources/", "inclusion", "prover", "credits_v1"); +impl_local!(InclusionVerifier, "resources/", "inclusion", "verifier", "credits_v1"); /// The function name for the inclusion circuit. pub const NETWORK_INCLUSION_FUNCTION_NAME: &str = "inclusion"; @@ -230,6 +249,8 @@ mod tests { FeePublicVerifier::load_bytes().expect("Failed to load fee_public verifier"); UpgradeProver::load_bytes().expect("Failed to load upgrade prover"); UpgradeVerifier::load_bytes().expect("Failed to load upgrade verifier"); + RedelegateProver::load_bytes().expect("Failed to load redelegate prover"); + RedelegateVerifier::load_bytes().expect("Failed to load redelegate verifier"); InclusionProver::load_bytes().expect("Failed to load inclusion prover"); InclusionVerifier::load_bytes().expect("Failed to load inclusion verifier"); } diff --git a/parameters/src/canary/resources/credits/redelegate.metadata b/parameters/src/canary/resources/credits/redelegate.metadata new file mode 100644 index 0000000000..358aaef38e --- /dev/null +++ b/parameters/src/canary/resources/credits/redelegate.metadata @@ -0,0 +1,6 @@ +{ + "prover_checksum": "7a21a24a098f9e7da0152dcf3ae6b1ad927567a03709ea000042664b6f95835e", + "prover_size": 27065194, + "verifier_checksum": "0cc82122d771b717ac33e31af2d86f56169d87c6e8a69ffe3d588de41b80bc8b", + "verifier_size": 673 +} \ No newline at end of file diff --git a/parameters/src/canary/resources/credits/redelegate.verifier b/parameters/src/canary/resources/credits/redelegate.verifier new file mode 100644 index 0000000000..da4691414d Binary files /dev/null and b/parameters/src/canary/resources/credits/redelegate.verifier differ diff --git a/parameters/src/canary/resources/credits/bond_public.metadata b/parameters/src/canary/resources/credits_v1/bond_public.metadata similarity index 100% rename from parameters/src/canary/resources/credits/bond_public.metadata rename to parameters/src/canary/resources/credits_v1/bond_public.metadata diff --git a/parameters/src/canary/resources/credits/bond_public.verifier b/parameters/src/canary/resources/credits_v1/bond_public.verifier similarity index 100% rename from parameters/src/canary/resources/credits/bond_public.verifier rename to parameters/src/canary/resources/credits_v1/bond_public.verifier diff --git a/parameters/src/canary/resources/credits/bond_validator.metadata b/parameters/src/canary/resources/credits_v1/bond_validator.metadata similarity index 100% rename from parameters/src/canary/resources/credits/bond_validator.metadata rename to parameters/src/canary/resources/credits_v1/bond_validator.metadata diff --git a/parameters/src/canary/resources/credits/bond_validator.verifier b/parameters/src/canary/resources/credits_v1/bond_validator.verifier similarity index 100% rename from parameters/src/canary/resources/credits/bond_validator.verifier rename to parameters/src/canary/resources/credits_v1/bond_validator.verifier diff --git a/parameters/src/canary/resources/credits/claim_unbond_public.metadata b/parameters/src/canary/resources/credits_v1/claim_unbond_public.metadata similarity index 100% rename from parameters/src/canary/resources/credits/claim_unbond_public.metadata rename to parameters/src/canary/resources/credits_v1/claim_unbond_public.metadata diff --git a/parameters/src/canary/resources/credits/claim_unbond_public.verifier b/parameters/src/canary/resources/credits_v1/claim_unbond_public.verifier similarity index 100% rename from parameters/src/canary/resources/credits/claim_unbond_public.verifier rename to parameters/src/canary/resources/credits_v1/claim_unbond_public.verifier diff --git a/parameters/src/canary/resources/credits/fee_private.metadata b/parameters/src/canary/resources/credits_v1/fee_private.metadata similarity index 100% rename from parameters/src/canary/resources/credits/fee_private.metadata rename to parameters/src/canary/resources/credits_v1/fee_private.metadata diff --git a/parameters/src/canary/resources/credits/fee_private.verifier b/parameters/src/canary/resources/credits_v1/fee_private.verifier similarity index 100% rename from parameters/src/canary/resources/credits/fee_private.verifier rename to parameters/src/canary/resources/credits_v1/fee_private.verifier diff --git a/parameters/src/canary/resources/credits/fee_public.metadata b/parameters/src/canary/resources/credits_v1/fee_public.metadata similarity index 100% rename from parameters/src/canary/resources/credits/fee_public.metadata rename to parameters/src/canary/resources/credits_v1/fee_public.metadata diff --git a/parameters/src/canary/resources/credits/fee_public.verifier b/parameters/src/canary/resources/credits_v1/fee_public.verifier similarity index 100% rename from parameters/src/canary/resources/credits/fee_public.verifier rename to parameters/src/canary/resources/credits_v1/fee_public.verifier diff --git a/parameters/src/canary/resources/credits/inclusion.metadata b/parameters/src/canary/resources/credits_v1/inclusion.metadata similarity index 100% rename from parameters/src/canary/resources/credits/inclusion.metadata rename to parameters/src/canary/resources/credits_v1/inclusion.metadata diff --git a/parameters/src/canary/resources/credits/inclusion.verifier b/parameters/src/canary/resources/credits_v1/inclusion.verifier similarity index 100% rename from parameters/src/canary/resources/credits/inclusion.verifier rename to parameters/src/canary/resources/credits_v1/inclusion.verifier diff --git a/parameters/src/canary/resources/credits/join.metadata b/parameters/src/canary/resources/credits_v1/join.metadata similarity index 100% rename from parameters/src/canary/resources/credits/join.metadata rename to parameters/src/canary/resources/credits_v1/join.metadata diff --git a/parameters/src/canary/resources/credits/join.verifier b/parameters/src/canary/resources/credits_v1/join.verifier similarity index 100% rename from parameters/src/canary/resources/credits/join.verifier rename to parameters/src/canary/resources/credits_v1/join.verifier diff --git a/parameters/src/canary/resources/credits/set_validator_state.metadata b/parameters/src/canary/resources/credits_v1/set_validator_state.metadata similarity index 100% rename from parameters/src/canary/resources/credits/set_validator_state.metadata rename to parameters/src/canary/resources/credits_v1/set_validator_state.metadata diff --git a/parameters/src/canary/resources/credits/set_validator_state.verifier b/parameters/src/canary/resources/credits_v1/set_validator_state.verifier similarity index 100% rename from parameters/src/canary/resources/credits/set_validator_state.verifier rename to parameters/src/canary/resources/credits_v1/set_validator_state.verifier diff --git a/parameters/src/canary/resources/credits/split.metadata b/parameters/src/canary/resources/credits_v1/split.metadata similarity index 100% rename from parameters/src/canary/resources/credits/split.metadata rename to parameters/src/canary/resources/credits_v1/split.metadata diff --git a/parameters/src/canary/resources/credits/split.verifier b/parameters/src/canary/resources/credits_v1/split.verifier similarity index 100% rename from parameters/src/canary/resources/credits/split.verifier rename to parameters/src/canary/resources/credits_v1/split.verifier diff --git a/parameters/src/canary/resources/credits/transfer_private.metadata b/parameters/src/canary/resources/credits_v1/transfer_private.metadata similarity index 100% rename from parameters/src/canary/resources/credits/transfer_private.metadata rename to parameters/src/canary/resources/credits_v1/transfer_private.metadata diff --git a/parameters/src/canary/resources/credits/transfer_private.verifier b/parameters/src/canary/resources/credits_v1/transfer_private.verifier similarity index 100% rename from parameters/src/canary/resources/credits/transfer_private.verifier rename to parameters/src/canary/resources/credits_v1/transfer_private.verifier diff --git a/parameters/src/canary/resources/credits/transfer_private_to_public.metadata b/parameters/src/canary/resources/credits_v1/transfer_private_to_public.metadata similarity index 100% rename from parameters/src/canary/resources/credits/transfer_private_to_public.metadata rename to parameters/src/canary/resources/credits_v1/transfer_private_to_public.metadata diff --git a/parameters/src/canary/resources/credits/transfer_private_to_public.verifier b/parameters/src/canary/resources/credits_v1/transfer_private_to_public.verifier similarity index 100% rename from parameters/src/canary/resources/credits/transfer_private_to_public.verifier rename to parameters/src/canary/resources/credits_v1/transfer_private_to_public.verifier diff --git a/parameters/src/canary/resources/credits/transfer_public.metadata b/parameters/src/canary/resources/credits_v1/transfer_public.metadata similarity index 100% rename from parameters/src/canary/resources/credits/transfer_public.metadata rename to parameters/src/canary/resources/credits_v1/transfer_public.metadata diff --git a/parameters/src/canary/resources/credits/transfer_public.verifier b/parameters/src/canary/resources/credits_v1/transfer_public.verifier similarity index 100% rename from parameters/src/canary/resources/credits/transfer_public.verifier rename to parameters/src/canary/resources/credits_v1/transfer_public.verifier diff --git a/parameters/src/canary/resources/credits/transfer_public_as_signer.metadata b/parameters/src/canary/resources/credits_v1/transfer_public_as_signer.metadata similarity index 100% rename from parameters/src/canary/resources/credits/transfer_public_as_signer.metadata rename to parameters/src/canary/resources/credits_v1/transfer_public_as_signer.metadata diff --git a/parameters/src/canary/resources/credits/transfer_public_as_signer.verifier b/parameters/src/canary/resources/credits_v1/transfer_public_as_signer.verifier similarity index 100% rename from parameters/src/canary/resources/credits/transfer_public_as_signer.verifier rename to parameters/src/canary/resources/credits_v1/transfer_public_as_signer.verifier diff --git a/parameters/src/canary/resources/credits/transfer_public_to_private.metadata b/parameters/src/canary/resources/credits_v1/transfer_public_to_private.metadata similarity index 100% rename from parameters/src/canary/resources/credits/transfer_public_to_private.metadata rename to parameters/src/canary/resources/credits_v1/transfer_public_to_private.metadata diff --git a/parameters/src/canary/resources/credits/transfer_public_to_private.verifier b/parameters/src/canary/resources/credits_v1/transfer_public_to_private.verifier similarity index 100% rename from parameters/src/canary/resources/credits/transfer_public_to_private.verifier rename to parameters/src/canary/resources/credits_v1/transfer_public_to_private.verifier diff --git a/parameters/src/canary/resources/credits/unbond_public.metadata b/parameters/src/canary/resources/credits_v1/unbond_public.metadata similarity index 100% rename from parameters/src/canary/resources/credits/unbond_public.metadata rename to parameters/src/canary/resources/credits_v1/unbond_public.metadata diff --git a/parameters/src/canary/resources/credits/unbond_public.verifier b/parameters/src/canary/resources/credits_v1/unbond_public.verifier similarity index 100% rename from parameters/src/canary/resources/credits/unbond_public.verifier rename to parameters/src/canary/resources/credits_v1/unbond_public.verifier diff --git a/parameters/src/canary/resources/credits/upgrade.metadata b/parameters/src/canary/resources/credits_v1/upgrade.metadata similarity index 100% rename from parameters/src/canary/resources/credits/upgrade.metadata rename to parameters/src/canary/resources/credits_v1/upgrade.metadata diff --git a/parameters/src/canary/resources/credits/upgrade.verifier b/parameters/src/canary/resources/credits_v1/upgrade.verifier similarity index 100% rename from parameters/src/canary/resources/credits/upgrade.verifier rename to parameters/src/canary/resources/credits_v1/upgrade.verifier diff --git a/parameters/src/mainnet/mod.rs b/parameters/src/mainnet/mod.rs index b9f5591c5f..50f046bec3 100644 --- a/parameters/src/mainnet/mod.rs +++ b/parameters/src/mainnet/mod.rs @@ -75,51 +75,59 @@ impl_local!(NegBeta, "resources/", "neg-powers-of-beta", "usrs"); // Negative Powers of Beta in G2 impl_local!(BetaH, "resources/", "beta-h", "usrs"); +// V2 Credits Keys + +// Redelegate +impl_remote!(RedelegateProver, REMOTE_URLS, "resources/", "redelegate", "prover", "credits"); +impl_local!(RedelegateVerifier, "resources/", "redelegate", "verifier", "credits"); + +// V1 and V2 Credits Keys + // BondPublic -impl_remote!(BondPublicProver, REMOTE_URLS, "resources/", "bond_public", "prover", "credits"); -impl_local!(BondPublicVerifier, "resources/", "bond_public", "verifier", "credits"); +impl_remote!(BondPublicProver, REMOTE_URLS, "resources/", "bond_public", "prover", "credits_v1"); +impl_local!(BondPublicVerifier, "resources/", "bond_public", "verifier", "credits_v1"); // BondValidator -impl_remote!(BondValidatorProver, REMOTE_URLS, "resources/", "bond_validator", "prover", "credits"); -impl_local!(BondValidatorVerifier, "resources/", "bond_validator", "verifier", "credits"); +impl_remote!(BondValidatorProver, REMOTE_URLS, "resources/", "bond_validator", "prover", "credits_v1"); +impl_local!(BondValidatorVerifier, "resources/", "bond_validator", "verifier", "credits_v1"); // UnbondPublic -impl_remote!(UnbondPublicProver, REMOTE_URLS, "resources/", "unbond_public", "prover", "credits"); -impl_local!(UnbondPublicVerifier, "resources/", "unbond_public", "verifier", "credits"); +impl_remote!(UnbondPublicProver, REMOTE_URLS, "resources/", "unbond_public", "prover", "credits_v1"); +impl_local!(UnbondPublicVerifier, "resources/", "unbond_public", "verifier", "credits_v1"); // ClaimUnbondPublic -impl_remote!(ClaimUnbondPublicProver, REMOTE_URLS, "resources/", "claim_unbond_public", "prover", "credits"); -impl_local!(ClaimUnbondPublicVerifier, "resources/", "claim_unbond_public", "verifier", "credits"); +impl_remote!(ClaimUnbondPublicProver, REMOTE_URLS, "resources/", "claim_unbond_public", "prover", "credits_v1"); +impl_local!(ClaimUnbondPublicVerifier, "resources/", "claim_unbond_public", "verifier", "credits_v1"); // SetValidatorState -impl_remote!(SetValidatorStateProver, REMOTE_URLS, "resources/", "set_validator_state", "prover", "credits"); -impl_local!(SetValidatorStateVerifier, "resources/", "set_validator_state", "verifier", "credits"); +impl_remote!(SetValidatorStateProver, REMOTE_URLS, "resources/", "set_validator_state", "prover", "credits_v1"); +impl_local!(SetValidatorStateVerifier, "resources/", "set_validator_state", "verifier", "credits_v1"); // TransferPrivate -impl_remote!(TransferPrivateProver, REMOTE_URLS, "resources/", "transfer_private", "prover", "credits"); -impl_local!(TransferPrivateVerifier, "resources/", "transfer_private", "verifier", "credits"); +impl_remote!(TransferPrivateProver, REMOTE_URLS, "resources/", "transfer_private", "prover", "credits_v1"); +impl_local!(TransferPrivateVerifier, "resources/", "transfer_private", "verifier", "credits_v1"); // TransferPublic -impl_remote!(TransferPublicProver, REMOTE_URLS, "resources/", "transfer_public", "prover", "credits"); -impl_local!(TransferPublicVerifier, "resources/", "transfer_public", "verifier", "credits"); +impl_remote!(TransferPublicProver, REMOTE_URLS, "resources/", "transfer_public", "prover", "credits_v1"); +impl_local!(TransferPublicVerifier, "resources/", "transfer_public", "verifier", "credits_v1"); // TransferPublicAsSigner -impl_remote!(TransferPublicAsSignerProver, REMOTE_URLS, "resources/", "transfer_public_as_signer", "prover", "credits"); -impl_local!(TransferPublicAsSignerVerifier, "resources/", "transfer_public_as_signer", "verifier", "credits"); +impl_remote!(TransferPublicAsSignerProver, REMOTE_URLS, "resources/", "transfer_public_as_signer", "prover", "credits_v1"); +impl_local!(TransferPublicAsSignerVerifier, "resources/", "transfer_public_as_signer", "verifier", "credits_v1"); // TransferPrivateToPublic -impl_remote!(TransferPrivateToPublicProver, REMOTE_URLS, "resources/", "transfer_private_to_public", "prover", "credits"); -impl_local!(TransferPrivateToPublicVerifier, "resources/", "transfer_private_to_public", "verifier", "credits"); +impl_remote!(TransferPrivateToPublicProver, REMOTE_URLS, "resources/", "transfer_private_to_public", "prover", "credits_v1"); +impl_local!(TransferPrivateToPublicVerifier, "resources/", "transfer_private_to_public", "verifier", "credits_v1"); // TransferPublicToPrivate -impl_remote!(TransferPublicToPrivateProver, REMOTE_URLS, "resources/", "transfer_public_to_private", "prover", "credits"); -impl_local!(TransferPublicToPrivateVerifier, "resources/", "transfer_public_to_private", "verifier", "credits"); +impl_remote!(TransferPublicToPrivateProver, REMOTE_URLS, "resources/", "transfer_public_to_private", "prover", "credits_v1"); +impl_local!(TransferPublicToPrivateVerifier, "resources/", "transfer_public_to_private", "verifier", "credits_v1"); // Join -impl_remote!(JoinProver, REMOTE_URLS, "resources/", "join", "prover", "credits"); -impl_local!(JoinVerifier, "resources/", "join", "verifier", "credits"); +impl_remote!(JoinProver, REMOTE_URLS, "resources/", "join", "prover", "credits_v1"); +impl_local!(JoinVerifier, "resources/", "join", "verifier", "credits_v1"); // Split -impl_remote!(SplitProver, REMOTE_URLS, "resources/", "split", "prover", "credits"); -impl_local!(SplitVerifier, "resources/", "split", "verifier", "credits"); +impl_remote!(SplitProver, REMOTE_URLS, "resources/", "split", "prover", "credits_v1"); +impl_local!(SplitVerifier, "resources/", "split", "verifier", "credits_v1"); // FeePrivate -impl_remote!(FeePrivateProver, REMOTE_URLS, "resources/", "fee_private", "prover", "credits"); -impl_local!(FeePrivateVerifier, "resources/", "fee_private", "verifier", "credits"); +impl_remote!(FeePrivateProver, REMOTE_URLS, "resources/", "fee_private", "prover", "credits_v1"); +impl_local!(FeePrivateVerifier, "resources/", "fee_private", "verifier", "credits_v1"); // FeePublic -impl_remote!(FeePublicProver, REMOTE_URLS, "resources/", "fee_public", "prover", "credits"); -impl_local!(FeePublicVerifier, "resources/", "fee_public", "verifier", "credits"); +impl_remote!(FeePublicProver, REMOTE_URLS, "resources/", "fee_public", "prover", "credits_v1"); +impl_local!(FeePublicVerifier, "resources/", "fee_public", "verifier", "credits_v1"); // Upgrade -impl_remote!(UpgradeProver, REMOTE_URLS, "resources/", "upgrade", "prover", "credits"); -impl_local!(UpgradeVerifier, "resources/", "upgrade", "verifier", "credits"); +impl_remote!(UpgradeProver, REMOTE_URLS, "resources/", "upgrade", "prover", "credits_v1"); +impl_local!(UpgradeVerifier, "resources/", "upgrade", "verifier", "credits_v1"); // V0 Credits Keys @@ -171,6 +179,17 @@ impl_local!(UpgradeV0Verifier, "resources/", "upgrade", "verifier", "credits_v0" #[macro_export] macro_rules! insert_credit_keys { + ($map:ident, $type:ident<$network:ident>, $variant:ident) => {{ + paste::paste! { + let string = stringify!([<$variant:lower>]); + $crate::insert_credit_v1_keys!($map, $type<$network>, $variant); + $crate::insert_key!($map, string, $type<$network>, ("redelegate", $crate::mainnet::[]::load_bytes())); + } + }}; +} + +#[macro_export] +macro_rules! insert_credit_v1_keys { ($map:ident, $type:ident<$network:ident>, $variant:ident) => {{ paste::paste! { let string = stringify!([<$variant:lower>]); @@ -232,8 +251,8 @@ macro_rules! insert_key { // Inclusion impl_remote!(InclusionV0Prover, REMOTE_URLS, "resources/", "inclusion", "prover", "credits_v0"); impl_local!(InclusionV0Verifier, "resources/", "inclusion", "verifier", "credits_v0"); -impl_remote!(InclusionProver, REMOTE_URLS, "resources/", "inclusion", "prover", "credits"); -impl_local!(InclusionVerifier, "resources/", "inclusion", "verifier", "credits"); +impl_remote!(InclusionProver, REMOTE_URLS, "resources/", "inclusion", "prover", "credits_v1"); +impl_local!(InclusionVerifier, "resources/", "inclusion", "verifier", "credits_v1"); /// The function name for the inclusion circuit. pub const NETWORK_INCLUSION_FUNCTION_NAME: &str = "inclusion"; @@ -300,6 +319,8 @@ mod tests { FeePublicVerifier::load_bytes().expect("Failed to load fee_public verifier"); UpgradeProver::load_bytes().expect("Failed to load upgrade prover"); UpgradeVerifier::load_bytes().expect("Failed to load upgrade verifier"); + RedelegateProver::load_bytes().expect("Failed to load redelegate prover"); + RedelegateVerifier::load_bytes().expect("Failed to load redelegate verifier"); InclusionProver::load_bytes().expect("Failed to load inclusion prover"); InclusionVerifier::load_bytes().expect("Failed to load inclusion verifier"); } diff --git a/parameters/src/mainnet/resources/credits/redelegate.metadata b/parameters/src/mainnet/resources/credits/redelegate.metadata new file mode 100644 index 0000000000..2d4f72e9ca --- /dev/null +++ b/parameters/src/mainnet/resources/credits/redelegate.metadata @@ -0,0 +1,6 @@ +{ + "prover_checksum": "175088ea4696187d80f8a82461833fe9c6667441ee9780fcbfb0fc8a63d4c68e", + "prover_size": 27065194, + "verifier_checksum": "09d7259f6d58a0b6e31a1ed157f58c5958b02850ec3866cd5e6bfdf82dd2ca5d", + "verifier_size": 673 +} \ No newline at end of file diff --git a/parameters/src/mainnet/resources/credits/redelegate.verifier b/parameters/src/mainnet/resources/credits/redelegate.verifier new file mode 100644 index 0000000000..08be8c9992 Binary files /dev/null and b/parameters/src/mainnet/resources/credits/redelegate.verifier differ diff --git a/parameters/src/mainnet/resources/credits/bond_public.metadata b/parameters/src/mainnet/resources/credits_v1/bond_public.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/bond_public.metadata rename to parameters/src/mainnet/resources/credits_v1/bond_public.metadata diff --git a/parameters/src/mainnet/resources/credits/bond_public.verifier b/parameters/src/mainnet/resources/credits_v1/bond_public.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/bond_public.verifier rename to parameters/src/mainnet/resources/credits_v1/bond_public.verifier diff --git a/parameters/src/mainnet/resources/credits/bond_validator.metadata b/parameters/src/mainnet/resources/credits_v1/bond_validator.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/bond_validator.metadata rename to parameters/src/mainnet/resources/credits_v1/bond_validator.metadata diff --git a/parameters/src/mainnet/resources/credits/bond_validator.verifier b/parameters/src/mainnet/resources/credits_v1/bond_validator.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/bond_validator.verifier rename to parameters/src/mainnet/resources/credits_v1/bond_validator.verifier diff --git a/parameters/src/mainnet/resources/credits/claim_unbond_public.metadata b/parameters/src/mainnet/resources/credits_v1/claim_unbond_public.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/claim_unbond_public.metadata rename to parameters/src/mainnet/resources/credits_v1/claim_unbond_public.metadata diff --git a/parameters/src/mainnet/resources/credits/claim_unbond_public.verifier b/parameters/src/mainnet/resources/credits_v1/claim_unbond_public.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/claim_unbond_public.verifier rename to parameters/src/mainnet/resources/credits_v1/claim_unbond_public.verifier diff --git a/parameters/src/mainnet/resources/credits/fee_private.metadata b/parameters/src/mainnet/resources/credits_v1/fee_private.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/fee_private.metadata rename to parameters/src/mainnet/resources/credits_v1/fee_private.metadata diff --git a/parameters/src/mainnet/resources/credits/fee_private.verifier b/parameters/src/mainnet/resources/credits_v1/fee_private.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/fee_private.verifier rename to parameters/src/mainnet/resources/credits_v1/fee_private.verifier diff --git a/parameters/src/mainnet/resources/credits/fee_public.metadata b/parameters/src/mainnet/resources/credits_v1/fee_public.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/fee_public.metadata rename to parameters/src/mainnet/resources/credits_v1/fee_public.metadata diff --git a/parameters/src/mainnet/resources/credits/fee_public.verifier b/parameters/src/mainnet/resources/credits_v1/fee_public.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/fee_public.verifier rename to parameters/src/mainnet/resources/credits_v1/fee_public.verifier diff --git a/parameters/src/mainnet/resources/credits/inclusion.metadata b/parameters/src/mainnet/resources/credits_v1/inclusion.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/inclusion.metadata rename to parameters/src/mainnet/resources/credits_v1/inclusion.metadata diff --git a/parameters/src/mainnet/resources/credits/inclusion.verifier b/parameters/src/mainnet/resources/credits_v1/inclusion.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/inclusion.verifier rename to parameters/src/mainnet/resources/credits_v1/inclusion.verifier diff --git a/parameters/src/mainnet/resources/credits/join.metadata b/parameters/src/mainnet/resources/credits_v1/join.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/join.metadata rename to parameters/src/mainnet/resources/credits_v1/join.metadata diff --git a/parameters/src/mainnet/resources/credits/join.verifier b/parameters/src/mainnet/resources/credits_v1/join.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/join.verifier rename to parameters/src/mainnet/resources/credits_v1/join.verifier diff --git a/parameters/src/mainnet/resources/credits/set_validator_state.metadata b/parameters/src/mainnet/resources/credits_v1/set_validator_state.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/set_validator_state.metadata rename to parameters/src/mainnet/resources/credits_v1/set_validator_state.metadata diff --git a/parameters/src/mainnet/resources/credits/set_validator_state.verifier b/parameters/src/mainnet/resources/credits_v1/set_validator_state.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/set_validator_state.verifier rename to parameters/src/mainnet/resources/credits_v1/set_validator_state.verifier diff --git a/parameters/src/mainnet/resources/credits/split.metadata b/parameters/src/mainnet/resources/credits_v1/split.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/split.metadata rename to parameters/src/mainnet/resources/credits_v1/split.metadata diff --git a/parameters/src/mainnet/resources/credits/split.verifier b/parameters/src/mainnet/resources/credits_v1/split.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/split.verifier rename to parameters/src/mainnet/resources/credits_v1/split.verifier diff --git a/parameters/src/mainnet/resources/credits/transfer_private.metadata b/parameters/src/mainnet/resources/credits_v1/transfer_private.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/transfer_private.metadata rename to parameters/src/mainnet/resources/credits_v1/transfer_private.metadata diff --git a/parameters/src/mainnet/resources/credits/transfer_private.verifier b/parameters/src/mainnet/resources/credits_v1/transfer_private.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/transfer_private.verifier rename to parameters/src/mainnet/resources/credits_v1/transfer_private.verifier diff --git a/parameters/src/mainnet/resources/credits/transfer_private_to_public.metadata b/parameters/src/mainnet/resources/credits_v1/transfer_private_to_public.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/transfer_private_to_public.metadata rename to parameters/src/mainnet/resources/credits_v1/transfer_private_to_public.metadata diff --git a/parameters/src/mainnet/resources/credits/transfer_private_to_public.verifier b/parameters/src/mainnet/resources/credits_v1/transfer_private_to_public.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/transfer_private_to_public.verifier rename to parameters/src/mainnet/resources/credits_v1/transfer_private_to_public.verifier diff --git a/parameters/src/mainnet/resources/credits/transfer_public.metadata b/parameters/src/mainnet/resources/credits_v1/transfer_public.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/transfer_public.metadata rename to parameters/src/mainnet/resources/credits_v1/transfer_public.metadata diff --git a/parameters/src/mainnet/resources/credits/transfer_public.verifier b/parameters/src/mainnet/resources/credits_v1/transfer_public.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/transfer_public.verifier rename to parameters/src/mainnet/resources/credits_v1/transfer_public.verifier diff --git a/parameters/src/mainnet/resources/credits/transfer_public_as_signer.metadata b/parameters/src/mainnet/resources/credits_v1/transfer_public_as_signer.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/transfer_public_as_signer.metadata rename to parameters/src/mainnet/resources/credits_v1/transfer_public_as_signer.metadata diff --git a/parameters/src/mainnet/resources/credits/transfer_public_as_signer.verifier b/parameters/src/mainnet/resources/credits_v1/transfer_public_as_signer.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/transfer_public_as_signer.verifier rename to parameters/src/mainnet/resources/credits_v1/transfer_public_as_signer.verifier diff --git a/parameters/src/mainnet/resources/credits/transfer_public_to_private.metadata b/parameters/src/mainnet/resources/credits_v1/transfer_public_to_private.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/transfer_public_to_private.metadata rename to parameters/src/mainnet/resources/credits_v1/transfer_public_to_private.metadata diff --git a/parameters/src/mainnet/resources/credits/transfer_public_to_private.verifier b/parameters/src/mainnet/resources/credits_v1/transfer_public_to_private.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/transfer_public_to_private.verifier rename to parameters/src/mainnet/resources/credits_v1/transfer_public_to_private.verifier diff --git a/parameters/src/mainnet/resources/credits/unbond_public.metadata b/parameters/src/mainnet/resources/credits_v1/unbond_public.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/unbond_public.metadata rename to parameters/src/mainnet/resources/credits_v1/unbond_public.metadata diff --git a/parameters/src/mainnet/resources/credits/unbond_public.verifier b/parameters/src/mainnet/resources/credits_v1/unbond_public.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/unbond_public.verifier rename to parameters/src/mainnet/resources/credits_v1/unbond_public.verifier diff --git a/parameters/src/mainnet/resources/credits/upgrade.metadata b/parameters/src/mainnet/resources/credits_v1/upgrade.metadata similarity index 100% rename from parameters/src/mainnet/resources/credits/upgrade.metadata rename to parameters/src/mainnet/resources/credits_v1/upgrade.metadata diff --git a/parameters/src/mainnet/resources/credits/upgrade.verifier b/parameters/src/mainnet/resources/credits_v1/upgrade.verifier similarity index 100% rename from parameters/src/mainnet/resources/credits/upgrade.verifier rename to parameters/src/mainnet/resources/credits_v1/upgrade.verifier diff --git a/parameters/src/testnet/mod.rs b/parameters/src/testnet/mod.rs index fdfe8942f3..54cb4baa38 100644 --- a/parameters/src/testnet/mod.rs +++ b/parameters/src/testnet/mod.rs @@ -22,51 +22,59 @@ pub const RESTRICTIONS_LIST: &str = include_str!("./resources/restrictions.json" const REMOTE_URLS: [&str; 2] = ["https://parameters.provable.com/testnet", "https://s3.us-west-1.amazonaws.com/testnet.parameters"]; +// V2 Credits Keys + +// Redelegate +impl_remote!(RedelegateProver, REMOTE_URLS, "resources/", "redelegate", "prover", "credits"); +impl_local!(RedelegateVerifier, "resources/", "redelegate", "verifier", "credits"); + +// V1 and V2 Credits Keys + // BondPublic -impl_remote!(BondPublicProver, REMOTE_URLS, "resources/", "bond_public", "prover", "credits"); -impl_local!(BondPublicVerifier, "resources/", "bond_public", "verifier", "credits"); +impl_remote!(BondPublicProver, REMOTE_URLS, "resources/", "bond_public", "prover", "credits_v1"); +impl_local!(BondPublicVerifier, "resources/", "bond_public", "verifier", "credits_v1"); // BondValidator -impl_remote!(BondValidatorProver, REMOTE_URLS, "resources/", "bond_validator", "prover", "credits"); -impl_local!(BondValidatorVerifier, "resources/", "bond_validator", "verifier", "credits"); +impl_remote!(BondValidatorProver, REMOTE_URLS, "resources/", "bond_validator", "prover", "credits_v1"); +impl_local!(BondValidatorVerifier, "resources/", "bond_validator", "verifier", "credits_v1"); // UnbondPublic -impl_remote!(UnbondPublicProver, REMOTE_URLS, "resources/", "unbond_public", "prover", "credits"); -impl_local!(UnbondPublicVerifier, "resources/", "unbond_public", "verifier", "credits"); +impl_remote!(UnbondPublicProver, REMOTE_URLS, "resources/", "unbond_public", "prover", "credits_v1"); +impl_local!(UnbondPublicVerifier, "resources/", "unbond_public", "verifier", "credits_v1"); // ClaimUnbondPublic -impl_remote!(ClaimUnbondPublicProver, REMOTE_URLS, "resources/", "claim_unbond_public", "prover", "credits"); -impl_local!(ClaimUnbondPublicVerifier, "resources/", "claim_unbond_public", "verifier", "credits"); +impl_remote!(ClaimUnbondPublicProver, REMOTE_URLS, "resources/", "claim_unbond_public", "prover", "credits_v1"); +impl_local!(ClaimUnbondPublicVerifier, "resources/", "claim_unbond_public", "verifier", "credits_v1"); // SetValidatorState -impl_remote!(SetValidatorStateProver, REMOTE_URLS, "resources/", "set_validator_state", "prover", "credits"); -impl_local!(SetValidatorStateVerifier, "resources/", "set_validator_state", "verifier", "credits"); +impl_remote!(SetValidatorStateProver, REMOTE_URLS, "resources/", "set_validator_state", "prover", "credits_v1"); +impl_local!(SetValidatorStateVerifier, "resources/", "set_validator_state", "verifier", "credits_v1"); // TransferPrivate -impl_remote!(TransferPrivateProver, REMOTE_URLS, "resources/", "transfer_private", "prover", "credits"); -impl_local!(TransferPrivateVerifier, "resources/", "transfer_private", "verifier", "credits"); +impl_remote!(TransferPrivateProver, REMOTE_URLS, "resources/", "transfer_private", "prover", "credits_v1"); +impl_local!(TransferPrivateVerifier, "resources/", "transfer_private", "verifier", "credits_v1"); // TransferPublic -impl_remote!(TransferPublicProver, REMOTE_URLS, "resources/", "transfer_public", "prover", "credits"); -impl_local!(TransferPublicVerifier, "resources/", "transfer_public", "verifier", "credits"); +impl_remote!(TransferPublicProver, REMOTE_URLS, "resources/", "transfer_public", "prover", "credits_v1"); +impl_local!(TransferPublicVerifier, "resources/", "transfer_public", "verifier", "credits_v1"); // TransferPublicAsSigner -impl_remote!(TransferPublicAsSignerProver, REMOTE_URLS, "resources/", "transfer_public_as_signer", "prover", "credits"); -impl_local!(TransferPublicAsSignerVerifier, "resources/", "transfer_public_as_signer", "verifier", "credits"); +impl_remote!(TransferPublicAsSignerProver, REMOTE_URLS, "resources/", "transfer_public_as_signer", "prover", "credits_v1"); +impl_local!(TransferPublicAsSignerVerifier, "resources/", "transfer_public_as_signer", "verifier", "credits_v1"); // TransferPrivateToPublic -impl_remote!(TransferPrivateToPublicProver, REMOTE_URLS, "resources/", "transfer_private_to_public", "prover", "credits"); -impl_local!(TransferPrivateToPublicVerifier, "resources/", "transfer_private_to_public", "verifier", "credits"); +impl_remote!(TransferPrivateToPublicProver, REMOTE_URLS, "resources/", "transfer_private_to_public", "prover", "credits_v1"); +impl_local!(TransferPrivateToPublicVerifier, "resources/", "transfer_private_to_public", "verifier", "credits_v1"); // TransferPublicToPrivate -impl_remote!(TransferPublicToPrivateProver, REMOTE_URLS, "resources/", "transfer_public_to_private", "prover", "credits"); -impl_local!(TransferPublicToPrivateVerifier, "resources/", "transfer_public_to_private", "verifier", "credits"); +impl_remote!(TransferPublicToPrivateProver, REMOTE_URLS, "resources/", "transfer_public_to_private", "prover", "credits_v1"); +impl_local!(TransferPublicToPrivateVerifier, "resources/", "transfer_public_to_private", "verifier", "credits_v1"); // Join -impl_remote!(JoinProver, REMOTE_URLS, "resources/", "join", "prover", "credits"); -impl_local!(JoinVerifier, "resources/", "join", "verifier", "credits"); +impl_remote!(JoinProver, REMOTE_URLS, "resources/", "join", "prover", "credits_v1"); +impl_local!(JoinVerifier, "resources/", "join", "verifier", "credits_v1"); // Split -impl_remote!(SplitProver, REMOTE_URLS, "resources/", "split", "prover", "credits"); -impl_local!(SplitVerifier, "resources/", "split", "verifier", "credits"); +impl_remote!(SplitProver, REMOTE_URLS, "resources/", "split", "prover", "credits_v1"); +impl_local!(SplitVerifier, "resources/", "split", "verifier", "credits_v1"); // FeePrivate -impl_remote!(FeePrivateProver, REMOTE_URLS, "resources/", "fee_private", "prover", "credits"); -impl_local!(FeePrivateVerifier, "resources/", "fee_private", "verifier", "credits"); +impl_remote!(FeePrivateProver, REMOTE_URLS, "resources/", "fee_private", "prover", "credits_v1"); +impl_local!(FeePrivateVerifier, "resources/", "fee_private", "verifier", "credits_v1"); // FeePublic -impl_remote!(FeePublicProver, REMOTE_URLS, "resources/", "fee_public", "prover", "credits"); -impl_local!(FeePublicVerifier, "resources/", "fee_public", "verifier", "credits"); +impl_remote!(FeePublicProver, REMOTE_URLS, "resources/", "fee_public", "prover", "credits_v1"); +impl_local!(FeePublicVerifier, "resources/", "fee_public", "verifier", "credits_v1"); // Upgrade -impl_remote!(UpgradeProver, REMOTE_URLS, "resources/", "upgrade", "prover", "credits"); -impl_local!(UpgradeVerifier, "resources/", "upgrade", "verifier", "credits"); +impl_remote!(UpgradeProver, REMOTE_URLS, "resources/", "upgrade", "prover", "credits_v1"); +impl_local!(UpgradeVerifier, "resources/", "upgrade", "verifier", "credits_v1"); // V0 Credits Keys @@ -118,6 +126,17 @@ impl_local!(UpgradeV0Verifier, "resources/", "upgrade", "verifier", "credits_v0" #[macro_export] macro_rules! insert_testnet_credit_keys { + ($map:ident, $type:ident<$network:ident>, $variant:ident) => {{ + paste::paste! { + let string = stringify!([<$variant:lower>]); + $crate::insert_testnet_credit_v1_keys!($map, $type<$network>, $variant); + $crate::insert_testnet_key!($map, string, $type<$network>, ("redelegate", $crate::testnet::[]::load_bytes())); + } + }}; +} + +#[macro_export] +macro_rules! insert_testnet_credit_v1_keys { ($map:ident, $type:ident<$network:ident>, $variant:ident) => {{ paste::paste! { let string = stringify!([<$variant:lower>]); @@ -179,8 +198,8 @@ macro_rules! insert_testnet_key { // Inclusion impl_remote!(InclusionV0Prover, REMOTE_URLS, "resources/", "inclusion", "prover", "credits_v0"); impl_local!(InclusionV0Verifier, "resources/", "inclusion", "verifier", "credits_v0"); -impl_remote!(InclusionProver, REMOTE_URLS, "resources/", "inclusion", "prover", "credits"); -impl_local!(InclusionVerifier, "resources/", "inclusion", "verifier", "credits"); +impl_remote!(InclusionProver, REMOTE_URLS, "resources/", "inclusion", "prover", "credits_v1"); +impl_local!(InclusionVerifier, "resources/", "inclusion", "verifier", "credits_v1"); /// The function name for the inclusion circuit. pub const NETWORK_INCLUSION_FUNCTION_NAME: &str = "inclusion"; @@ -231,6 +250,8 @@ mod tests { FeePublicVerifier::load_bytes().expect("Failed to load fee_public verifier"); UpgradeProver::load_bytes().expect("Failed to load upgrade prover"); UpgradeVerifier::load_bytes().expect("Failed to load upgrade verifier"); + RedelegateProver::load_bytes().expect("Failed to load redelegate prover"); + RedelegateVerifier::load_bytes().expect("Failed to load redelegate verifier"); InclusionProver::load_bytes().expect("Failed to load inclusion prover"); InclusionVerifier::load_bytes().expect("Failed to load inclusion verifier"); } diff --git a/parameters/src/testnet/resources/credits/redelegate.metadata b/parameters/src/testnet/resources/credits/redelegate.metadata new file mode 100644 index 0000000000..916de0e2fa --- /dev/null +++ b/parameters/src/testnet/resources/credits/redelegate.metadata @@ -0,0 +1,6 @@ +{ + "prover_checksum": "bbd94b49712ca062afe76d764efed620306142dd98ea01707bebb3c6bc5f6639", + "prover_size": 27065194, + "verifier_checksum": "c281f4439c28d14b9ad1e097bcd83121550123ab5527da7df1f0ab598a814654", + "verifier_size": 673 +} \ No newline at end of file diff --git a/parameters/src/testnet/resources/credits/redelegate.verifier b/parameters/src/testnet/resources/credits/redelegate.verifier new file mode 100644 index 0000000000..8bb7c485af Binary files /dev/null and b/parameters/src/testnet/resources/credits/redelegate.verifier differ diff --git a/parameters/src/testnet/resources/credits/bond_public.metadata b/parameters/src/testnet/resources/credits_v1/bond_public.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/bond_public.metadata rename to parameters/src/testnet/resources/credits_v1/bond_public.metadata diff --git a/parameters/src/testnet/resources/credits/bond_public.verifier b/parameters/src/testnet/resources/credits_v1/bond_public.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/bond_public.verifier rename to parameters/src/testnet/resources/credits_v1/bond_public.verifier diff --git a/parameters/src/testnet/resources/credits/bond_validator.metadata b/parameters/src/testnet/resources/credits_v1/bond_validator.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/bond_validator.metadata rename to parameters/src/testnet/resources/credits_v1/bond_validator.metadata diff --git a/parameters/src/testnet/resources/credits/bond_validator.verifier b/parameters/src/testnet/resources/credits_v1/bond_validator.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/bond_validator.verifier rename to parameters/src/testnet/resources/credits_v1/bond_validator.verifier diff --git a/parameters/src/testnet/resources/credits/claim_unbond_public.metadata b/parameters/src/testnet/resources/credits_v1/claim_unbond_public.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/claim_unbond_public.metadata rename to parameters/src/testnet/resources/credits_v1/claim_unbond_public.metadata diff --git a/parameters/src/testnet/resources/credits/claim_unbond_public.verifier b/parameters/src/testnet/resources/credits_v1/claim_unbond_public.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/claim_unbond_public.verifier rename to parameters/src/testnet/resources/credits_v1/claim_unbond_public.verifier diff --git a/parameters/src/testnet/resources/credits/fee_private.metadata b/parameters/src/testnet/resources/credits_v1/fee_private.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/fee_private.metadata rename to parameters/src/testnet/resources/credits_v1/fee_private.metadata diff --git a/parameters/src/testnet/resources/credits/fee_private.verifier b/parameters/src/testnet/resources/credits_v1/fee_private.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/fee_private.verifier rename to parameters/src/testnet/resources/credits_v1/fee_private.verifier diff --git a/parameters/src/testnet/resources/credits/fee_public.metadata b/parameters/src/testnet/resources/credits_v1/fee_public.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/fee_public.metadata rename to parameters/src/testnet/resources/credits_v1/fee_public.metadata diff --git a/parameters/src/testnet/resources/credits/fee_public.verifier b/parameters/src/testnet/resources/credits_v1/fee_public.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/fee_public.verifier rename to parameters/src/testnet/resources/credits_v1/fee_public.verifier diff --git a/parameters/src/testnet/resources/credits/inclusion.metadata b/parameters/src/testnet/resources/credits_v1/inclusion.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/inclusion.metadata rename to parameters/src/testnet/resources/credits_v1/inclusion.metadata diff --git a/parameters/src/testnet/resources/credits/inclusion.verifier b/parameters/src/testnet/resources/credits_v1/inclusion.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/inclusion.verifier rename to parameters/src/testnet/resources/credits_v1/inclusion.verifier diff --git a/parameters/src/testnet/resources/credits/inclusion_v0.metadata b/parameters/src/testnet/resources/credits_v1/inclusion_v0.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/inclusion_v0.metadata rename to parameters/src/testnet/resources/credits_v1/inclusion_v0.metadata diff --git a/parameters/src/testnet/resources/credits/inclusion_v0.verifier b/parameters/src/testnet/resources/credits_v1/inclusion_v0.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/inclusion_v0.verifier rename to parameters/src/testnet/resources/credits_v1/inclusion_v0.verifier diff --git a/parameters/src/testnet/resources/credits/join.metadata b/parameters/src/testnet/resources/credits_v1/join.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/join.metadata rename to parameters/src/testnet/resources/credits_v1/join.metadata diff --git a/parameters/src/testnet/resources/credits/join.verifier b/parameters/src/testnet/resources/credits_v1/join.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/join.verifier rename to parameters/src/testnet/resources/credits_v1/join.verifier diff --git a/parameters/src/testnet/resources/credits/set_validator_state.metadata b/parameters/src/testnet/resources/credits_v1/set_validator_state.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/set_validator_state.metadata rename to parameters/src/testnet/resources/credits_v1/set_validator_state.metadata diff --git a/parameters/src/testnet/resources/credits/set_validator_state.verifier b/parameters/src/testnet/resources/credits_v1/set_validator_state.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/set_validator_state.verifier rename to parameters/src/testnet/resources/credits_v1/set_validator_state.verifier diff --git a/parameters/src/testnet/resources/credits/split.metadata b/parameters/src/testnet/resources/credits_v1/split.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/split.metadata rename to parameters/src/testnet/resources/credits_v1/split.metadata diff --git a/parameters/src/testnet/resources/credits/split.verifier b/parameters/src/testnet/resources/credits_v1/split.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/split.verifier rename to parameters/src/testnet/resources/credits_v1/split.verifier diff --git a/parameters/src/testnet/resources/credits/transfer_private.metadata b/parameters/src/testnet/resources/credits_v1/transfer_private.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/transfer_private.metadata rename to parameters/src/testnet/resources/credits_v1/transfer_private.metadata diff --git a/parameters/src/testnet/resources/credits/transfer_private.verifier b/parameters/src/testnet/resources/credits_v1/transfer_private.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/transfer_private.verifier rename to parameters/src/testnet/resources/credits_v1/transfer_private.verifier diff --git a/parameters/src/testnet/resources/credits/transfer_private_to_public.metadata b/parameters/src/testnet/resources/credits_v1/transfer_private_to_public.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/transfer_private_to_public.metadata rename to parameters/src/testnet/resources/credits_v1/transfer_private_to_public.metadata diff --git a/parameters/src/testnet/resources/credits/transfer_private_to_public.verifier b/parameters/src/testnet/resources/credits_v1/transfer_private_to_public.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/transfer_private_to_public.verifier rename to parameters/src/testnet/resources/credits_v1/transfer_private_to_public.verifier diff --git a/parameters/src/testnet/resources/credits/transfer_public.metadata b/parameters/src/testnet/resources/credits_v1/transfer_public.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/transfer_public.metadata rename to parameters/src/testnet/resources/credits_v1/transfer_public.metadata diff --git a/parameters/src/testnet/resources/credits/transfer_public.verifier b/parameters/src/testnet/resources/credits_v1/transfer_public.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/transfer_public.verifier rename to parameters/src/testnet/resources/credits_v1/transfer_public.verifier diff --git a/parameters/src/testnet/resources/credits/transfer_public_as_signer.metadata b/parameters/src/testnet/resources/credits_v1/transfer_public_as_signer.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/transfer_public_as_signer.metadata rename to parameters/src/testnet/resources/credits_v1/transfer_public_as_signer.metadata diff --git a/parameters/src/testnet/resources/credits/transfer_public_as_signer.verifier b/parameters/src/testnet/resources/credits_v1/transfer_public_as_signer.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/transfer_public_as_signer.verifier rename to parameters/src/testnet/resources/credits_v1/transfer_public_as_signer.verifier diff --git a/parameters/src/testnet/resources/credits/transfer_public_to_private.metadata b/parameters/src/testnet/resources/credits_v1/transfer_public_to_private.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/transfer_public_to_private.metadata rename to parameters/src/testnet/resources/credits_v1/transfer_public_to_private.metadata diff --git a/parameters/src/testnet/resources/credits/transfer_public_to_private.verifier b/parameters/src/testnet/resources/credits_v1/transfer_public_to_private.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/transfer_public_to_private.verifier rename to parameters/src/testnet/resources/credits_v1/transfer_public_to_private.verifier diff --git a/parameters/src/testnet/resources/credits/unbond_public.metadata b/parameters/src/testnet/resources/credits_v1/unbond_public.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/unbond_public.metadata rename to parameters/src/testnet/resources/credits_v1/unbond_public.metadata diff --git a/parameters/src/testnet/resources/credits/unbond_public.verifier b/parameters/src/testnet/resources/credits_v1/unbond_public.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/unbond_public.verifier rename to parameters/src/testnet/resources/credits_v1/unbond_public.verifier diff --git a/parameters/src/testnet/resources/credits/upgrade.metadata b/parameters/src/testnet/resources/credits_v1/upgrade.metadata similarity index 100% rename from parameters/src/testnet/resources/credits/upgrade.metadata rename to parameters/src/testnet/resources/credits_v1/upgrade.metadata diff --git a/parameters/src/testnet/resources/credits/upgrade.verifier b/parameters/src/testnet/resources/credits_v1/upgrade.verifier similarity index 100% rename from parameters/src/testnet/resources/credits/upgrade.verifier rename to parameters/src/testnet/resources/credits_v1/upgrade.verifier diff --git a/synthesizer/process/src/lib.rs b/synthesizer/process/src/lib.rs index b6df5842ca..979bc1f965 100644 --- a/synthesizer/process/src/lib.rs +++ b/synthesizer/process/src/lib.rs @@ -241,78 +241,62 @@ impl Process { #[inline] pub fn load() -> Result { let timer = timer!("Process::load"); - - // Initialize the process. - let mut process = - Self { universal_srs: UniversalSRS::load()?, stacks: Default::default(), old_stacks: Default::default() }; - lap!(timer, "Initialize process"); - - // Initialize the 'credits.aleo' program. - let program = Program::credits()?; - lap!(timer, "Load credits program"); - - // Compute the 'credits.aleo' program stack. - let stack = Stack::new(&process, &program)?; - lap!(timer, "Initialize stack"); - - // Synthesize the 'credits.aleo' verifying keys. - for function_name in program.functions().keys() { - // Load the verifying key. - let verifying_key = N::get_credits_verifying_key(function_name.to_string())?; - // Retrieve the number of public and private variables. - // Note: This number does *NOT* include the number of constants. This is safe because - // this program is never deployed, as it is a first-class citizen of the protocol. - let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64; - // Insert the verifying key. - stack.insert_verifying_key(function_name, VerifyingKey::new(verifying_key.clone(), num_variables))?; - lap!(timer, "Load verifying key for {function_name}"); - } - lap!(timer, "Load circuit keys"); - - // Add the stack to the process. - process.add_stack(stack); - + // Specify the version of credits.aleo to load. + let credits_version = CreditsVersion::V2; + // Load the process with the specified version of credits.aleo. + let process = Self::load_with_credits(credits_version)?; finish!(timer, "Process::load"); // Return the process. Ok(process) } - /// Initializes a new process with the V0 credits.aleo verifiying keys. + /// Initializes a new process with the V1 version of credits.aleo. + #[inline] + pub fn load_v1() -> Result { + let timer = timer!("Process::load_v1"); + // Specify the version of credits.aleo to load. + let credits_version = CreditsVersion::V1; + // Load the process with the specified version of credits.aleo. + let process = Self::load_with_credits(credits_version)?; + finish!(timer, "Process::load_v1"); + // Return the process. + Ok(process) + } + + /// Initializes a new process with the V0 version of credits.aleo. #[inline] pub fn load_v0() -> Result { let timer = timer!("Process::load_v0"); + // Specify the version of credits.aleo to load. + let credits_version = CreditsVersion::V0; + // Load the process with the specified version of credits.aleo. + let process = Self::load_with_credits(credits_version)?; + finish!(timer, "Process::load_v0"); + // Return the process. + Ok(process) + } + + /// Helper function to load `credits.aleo` based on the provided version. + fn load_with_credits(credits_version: CreditsVersion) -> Result { + let timer = timer!("Process::load_with_credits"); // Initialize the process. let mut process = Self { universal_srs: UniversalSRS::load()?, stacks: Default::default(), old_stacks: Default::default() }; lap!(timer, "Initialize process"); - // Initialize the 'credits.aleo' program. - let program = Program::credits()?; - lap!(timer, "Load credits program"); - // Compute the 'credits.aleo' program stack. - let stack = Stack::new(&process, &program)?; + let stack = Stack::new_credits(&process, credits_version)?; lap!(timer, "Initialize stack"); // Synthesize the 'credits.aleo' verifying keys. - for function_name in program.functions().keys() { - // Load the verifying key. - let verifying_key = N::get_credits_v0_verifying_key(function_name.to_string())?; - // Retrieve the number of public and private variables. - // Note: This number does *NOT* include the number of constants. This is safe because - // this program is never deployed, as it is a first-class citizen of the protocol. - let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64; - // Insert the verifying key. - stack.insert_verifying_key(function_name, VerifyingKey::new(verifying_key.clone(), num_variables))?; - lap!(timer, "Load verifying key for {function_name}"); - } + stack.insert_credits_verifying_keys(credits_version)?; lap!(timer, "Load circuit keys"); // Add the stack to the process. process.add_stack(stack); - finish!(timer, "Process::load_v0"); + finish!(timer, "Process::load_with_credits"); // Return the process. Ok(process) } diff --git a/synthesizer/process/src/stack/mod.rs b/synthesizer/process/src/stack/mod.rs index fde5182c97..47ed84f9f7 100644 --- a/synthesizer/process/src/stack/mod.rs +++ b/synthesizer/process/src/stack/mod.rs @@ -100,6 +100,14 @@ use std::{ #[cfg(not(feature = "serial"))] use rayon::prelude::*; +/// The `CreditsVersion` is used to track the version of the `credits.aleo` program. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum CreditsVersion { + V0 = 0, + V1 = 1, + V2 = 2, +} + pub type Assignments = Arc::Field>, CallMetrics)>>>; /// A stack of translations for the transitions in the execution. Each function execution level pushes a new group, /// and translations for dynamic calls made at that level are collected into the top group. When the transition is @@ -421,6 +429,65 @@ impl Stack { } } + /// Initializes a new stack given the process and the program. + pub fn new_credits(process: &Process, credits_version: CreditsVersion) -> Result { + // Load the appropriate version of the `credits.aleo` program. + let (program, edition) = match credits_version { + CreditsVersion::V0 => (Program::::credits_v0()?, 0), + CreditsVersion::V1 => (Program::::credits_v1()?, 1), + CreditsVersion::V2 => (Program::::credits()?, 2), + }; + + // Check that the program is well-formed. + check_program_is_well_formed(&program)?; + + // Construct a new stack. + let stack = Self::create_raw(process, &program, edition)?; + // Initialize and check the stack's validity. + stack.initialize_and_check(process)?; + // Return the stack. + Ok(stack) + } + + /// Inserts the verifying keys (based on the version) if the program ID is 'credits.aleo'. + pub fn insert_credits_verifying_keys(&self, credits_version: CreditsVersion) -> Result<()> { + // Retrieve the program ID. + let program_id = ProgramID::from_str("credits.aleo")?; + ensure!( + self.program_id() == &program_id, + "Cannot insert credits verifying keys for program '{}' since the program ID is not '{program_id}'.", + self.program_id(), + ); + + // Ensure that the provided credits verion matches the stack's program edition. + let program_edition = *self.program_edition; + let credits_version_as_edition = credits_version as u16; + ensure!( + program_edition == credits_version_as_edition, + "The provided credits version ({program_edition}) should match the stack's program edition ({credits_version_as_edition})." + ); + + // Retrieve the program. + let program = self.program(); + + for function_name in program.functions().keys() { + // Load the verifying key. + let verifying_key = match credits_version { + CreditsVersion::V0 => N::get_credits_v0_verifying_key(function_name.to_string()), + CreditsVersion::V1 => N::get_credits_v1_verifying_key(function_name.to_string()), + CreditsVersion::V2 => N::get_credits_verifying_key(function_name.to_string()), + }?; + // Retrieve the number of public and private variables. + // Note: This number does *NOT* include the number of constants. This is safe because + // this program is never deployed, as it is a first-class citizen of the protocol. + let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64; + // Insert the verifying key. + self.insert_verifying_key(function_name, VerifyingKey::new(verifying_key.clone(), num_variables))?; + } + + Ok(()) + } + /// Inserts the proving key if the program ID is 'credits.aleo'. fn try_insert_credits_function_proving_key(&self, function_name: &Identifier) -> Result<()> { // If the program is 'credits.aleo' and it does not exist yet, load the proving key directly. diff --git a/synthesizer/process/src/tests/test_credits.rs b/synthesizer/process/src/tests/test_credits.rs index fd2202ab92..cdac576c17 100644 --- a/synthesizer/process/src/tests/test_credits.rs +++ b/synthesizer/process/src/tests/test_credits.rs @@ -42,6 +42,7 @@ type CurrentAleo = AleoV0; const NUM_BLOCKS_TO_UNLOCK: u32 = 360; const TEST_COMMISSION: u8 = 5; +const REDELEGATE_COOLDOWN: u32 = 403_200; /// Samples a new finalize store. macro_rules! sample_finalize_store { @@ -225,6 +226,36 @@ fn withdraw_state>( Ok(Some(withdrawal_address)) } +/// Get the current redelegate state from the `redelegated` mapping for the given delegator address. +/// Returns the `redelegate_state` as `(previous_validator, new_validator, cooldown_height)`. +fn redelegate_state>( + store: &FinalizeStore, + address: &Address, +) -> Result, Address, u32)>> { + let state = match get_mapping_value(store, "credits.aleo", "redelegated", Literal::Address(*address))? { + Some(Value::Plaintext(Plaintext::Struct(state, _))) => state, + None => return Ok(None), + _ => bail!("Malformed redelegate state for {address}"), + }; + + let previous_validator = match state.get(&Identifier::from_str("previous_validator")?) { + Some(Plaintext::Literal(Literal::Address(addr), _)) => *addr, + _ => bail!("`previous_validator` not found for: {address}"), + }; + + let new_validator = match state.get(&Identifier::from_str("new_validator")?) { + Some(Plaintext::Literal(Literal::Address(addr), _)) => *addr, + _ => bail!("`new_validator` not found for: {address}"), + }; + + let height = match state.get(&Identifier::from_str("height")?) { + Some(Plaintext::Literal(Literal::U32(height), _)) => **height, + _ => bail!("`height` not found for: {address}"), + }; + + Ok(Some((previous_validator, new_validator, height))) +} + /// Initialize an account with a given balance fn initialize_account>( finalize_store: &FinalizeStore, @@ -407,6 +438,26 @@ fn unbond_public>( ) } +/// Perform a `redelegate`. +fn redelegate>( + process: &Process, + finalize_store: &FinalizeStore, + caller_private_key: &PrivateKey, + new_validator_address: &Address, + block_height: u32, + rng: &mut TestRng, +) -> Result<()> { + execute_function( + process, + finalize_store, + caller_private_key, + "redelegate", + &[new_validator_address.to_string()], + Some(block_height), + rng, + ) +} + /// Perform a `set_validator_state`. fn set_validator_state>( process: &Process, @@ -2805,6 +2856,218 @@ fn test_bond_validator_with_different_commission_fails() { ); } +/// Ensure `redelegate` stores the correct bond, delegated, and redelegation state, and sets the +/// cooldown height to `block_height + REDELEGATE_COOLDOWN`. +#[test] +fn test_redelegate_stores_correct_state() { + let rng = &mut TestRng::default(); + + let process = Process::::load().unwrap(); + let store = sample_finalize_store!(); + + // Initialize two validators and one delegator. + let (validators, delegators) = initialize_stakers(&store, 2, 1, rng).unwrap(); + let mut validators_iter = validators.iter(); + let (v1_key, (v1_addr, _, _, v1_withdraw)) = validators_iter.next().unwrap(); + let (v2_key, (v2_addr, _, _, v2_withdraw)) = validators_iter.next().unwrap(); + let (delegator_key, (delegator_addr, _)) = delegators.first().unwrap(); + + // Bond both validators. + let validator_amount = MIN_VALIDATOR_STAKE; + bond_validator(&process, &store, v1_key, v1_withdraw, validator_amount, TEST_COMMISSION, rng).unwrap(); + bond_validator(&process, &store, v2_key, v2_withdraw, validator_amount, TEST_COMMISSION, rng).unwrap(); + + // Bond the delegator to validator 1. + let delegator_amount = MIN_DELEGATOR_STAKE; + bond_public(&process, &store, delegator_key, v1_addr, delegator_addr, delegator_amount, rng).unwrap(); + + // Sanity check: no redelegate state exists yet. + assert_eq!(redelegate_state(&store, delegator_addr).unwrap(), None); + + // Redelegate from validator 1 to validator 2 at block height 1. + let block_height = 1u32; + redelegate(&process, &store, delegator_key, v2_addr, block_height, rng).unwrap(); + + // The bond state must point to validator 2. + assert_eq!(bond_state(&store, delegator_addr).unwrap(), Some((*v2_addr, delegator_amount))); + + // Delegated totals must be updated: v1 loses the delegator's stake, v2 gains it. + assert_eq!(delegated_state(&store, v1_addr).unwrap(), Some(validator_amount)); + assert_eq!(delegated_state(&store, v2_addr).unwrap(), Some(validator_amount + delegator_amount)); + + // The redelegate state must record the old validator, the new validator, and the cooldown height. + let (prev, next, height) = redelegate_state(&store, delegator_addr).unwrap().unwrap(); + assert_eq!(prev, *v1_addr); + assert_eq!(next, *v2_addr); + assert_eq!(height, block_height + REDELEGATE_COOLDOWN); +} + +/// Ensure that redelegating to the same validator that is currently bonded fails. +#[test] +fn test_redelegate_to_same_validator_fails() { + let rng = &mut TestRng::default(); + + let process = Process::::load().unwrap(); + let store = sample_finalize_store!(); + + let (validators, delegators) = initialize_stakers(&store, 1, 1, rng).unwrap(); + let (v_key, (v_addr, _, _, v_withdraw)) = validators.first().unwrap(); + let (delegator_key, (delegator_addr, _)) = delegators.first().unwrap(); + + bond_validator(&process, &store, v_key, v_withdraw, MIN_VALIDATOR_STAKE, TEST_COMMISSION, rng).unwrap(); + bond_public(&process, &store, delegator_key, v_addr, delegator_addr, MIN_DELEGATOR_STAKE, rng).unwrap(); + + // Redelegating to the current validator must fail. + assert!(redelegate(&process, &store, delegator_key, v_addr, 1, rng).is_err()); + + // The bond state must be unchanged. + assert_eq!(bond_state(&store, delegator_addr).unwrap(), Some((*v_addr, MIN_DELEGATOR_STAKE))); +} + +/// Ensure that only the staker (not an unrelated address) can call `redelegate`. +/// Since `self.caller` is used as the delegator key in finalize, any caller without a bond state is rejected. +#[test] +fn test_redelegate_only_staker_can_call() { + let rng = &mut TestRng::default(); + + let process = Process::::load().unwrap(); + let store = sample_finalize_store!(); + + let (validators, delegators) = initialize_stakers(&store, 2, 1, rng).unwrap(); + let mut validators_iter = validators.iter(); + let (v1_key, (v1_addr, _, _, v1_withdraw)) = validators_iter.next().unwrap(); + let (v2_key, (v2_addr, _, _, v2_withdraw)) = validators_iter.next().unwrap(); + let (delegator_key, (delegator_addr, _)) = delegators.first().unwrap(); + + bond_validator(&process, &store, v1_key, v1_withdraw, MIN_VALIDATOR_STAKE, TEST_COMMISSION, rng).unwrap(); + bond_validator(&process, &store, v2_key, v2_withdraw, MIN_VALIDATOR_STAKE, TEST_COMMISSION, rng).unwrap(); + bond_public(&process, &store, delegator_key, v1_addr, delegator_addr, MIN_DELEGATOR_STAKE, rng).unwrap(); + + // A third party with no bond state cannot redelegate on the delegator's behalf. + let stranger_key = PrivateKey::::new(rng).unwrap(); + assert!(redelegate(&process, &store, &stranger_key, v2_addr, 1, rng).is_err()); + + // The delegator's bond state must be unchanged. + assert_eq!(bond_state(&store, delegator_addr).unwrap(), Some((*v1_addr, MIN_DELEGATOR_STAKE))); +} + +/// Ensure that a validator (who has an entry in the `committee` mapping) cannot call `redelegate`. +#[test] +fn test_validator_cannot_redelegate() { + let rng = &mut TestRng::default(); + + let process = Process::::load().unwrap(); + let store = sample_finalize_store!(); + + let (validators, _) = initialize_stakers(&store, 2, 0, rng).unwrap(); + let mut validators_iter = validators.iter(); + let (v1_key, (_, _, _, v1_withdraw)) = validators_iter.next().unwrap(); + let (v2_key, (v2_addr, _, _, v2_withdraw)) = validators_iter.next().unwrap(); + + bond_validator(&process, &store, v1_key, v1_withdraw, MIN_VALIDATOR_STAKE, TEST_COMMISSION, rng).unwrap(); + bond_validator(&process, &store, v2_key, v2_withdraw, MIN_VALIDATOR_STAKE, TEST_COMMISSION, rng).unwrap(); + + // A validator (present in the committee mapping) must be rejected by the `contains committee` check. + assert!(redelegate(&process, &store, v1_key, v2_addr, 1, rng).is_err()); +} + +/// Ensure that a second redelegation is rejected before the cooldown expires, and accepted at +/// exactly the cooldown height. +#[test] +fn test_redelegate_cooldown_not_expired_fails() { + let rng = &mut TestRng::default(); + + let process = Process::::load().unwrap(); + let store = sample_finalize_store!(); + + let (validators, delegators) = initialize_stakers(&store, 3, 1, rng).unwrap(); + let mut validators_iter = validators.iter(); + let (v1_key, (v1_addr, _, _, v1_withdraw)) = validators_iter.next().unwrap(); + let (v2_key, (v2_addr, _, _, v2_withdraw)) = validators_iter.next().unwrap(); + let (v3_key, (v3_addr, _, _, v3_withdraw)) = validators_iter.next().unwrap(); + let (delegator_key, (delegator_addr, _)) = delegators.first().unwrap(); + + bond_validator(&process, &store, v1_key, v1_withdraw, MIN_VALIDATOR_STAKE, TEST_COMMISSION, rng).unwrap(); + bond_validator(&process, &store, v2_key, v2_withdraw, MIN_VALIDATOR_STAKE, TEST_COMMISSION, rng).unwrap(); + bond_validator(&process, &store, v3_key, v3_withdraw, MIN_VALIDATOR_STAKE, TEST_COMMISSION, rng).unwrap(); + bond_public(&process, &store, delegator_key, v1_addr, delegator_addr, MIN_DELEGATOR_STAKE, rng).unwrap(); + + // First redelegate: validator 1 → validator 2 at block 1. + redelegate(&process, &store, delegator_key, v2_addr, 1, rng).unwrap(); + let (_, _, cooldown_height) = redelegate_state(&store, delegator_addr).unwrap().unwrap(); + assert_eq!(cooldown_height, 1 + REDELEGATE_COOLDOWN); + + // A second redelegate one block before the cooldown expires must fail. + assert!(redelegate(&process, &store, delegator_key, v3_addr, cooldown_height - 1, rng).is_err()); + assert_eq!(bond_state(&store, delegator_addr).unwrap(), Some((*v2_addr, MIN_DELEGATOR_STAKE))); + + // A second redelegate at exactly the cooldown height must succeed. + redelegate(&process, &store, delegator_key, v3_addr, cooldown_height, rng).unwrap(); + assert_eq!(bond_state(&store, delegator_addr).unwrap(), Some((*v3_addr, MIN_DELEGATOR_STAKE))); +} + +/// Ensure that redelegation to a validator address not yet in the committee succeeds. +/// The finalize uses `get.or_use committee[new_validator] default` where the default is open, +/// so any address that has not explicitly closed itself is a valid target. +#[test] +fn test_redelegate_to_validator_not_in_committee_succeeds() { + let rng = &mut TestRng::default(); + + let process = Process::::load().unwrap(); + let store = sample_finalize_store!(); + + let (validators, delegators) = initialize_stakers(&store, 1, 1, rng).unwrap(); + let (v_key, (v_addr, _, _, v_withdraw)) = validators.first().unwrap(); + let (delegator_key, (delegator_addr, _)) = delegators.first().unwrap(); + + bond_validator(&process, &store, v_key, v_withdraw, MIN_VALIDATOR_STAKE, TEST_COMMISSION, rng).unwrap(); + bond_public(&process, &store, delegator_key, v_addr, delegator_addr, MIN_DELEGATOR_STAKE, rng).unwrap(); + + // A fresh address with no committee entry defaults to `is_open = true` via `get.or_use`. + let future_validator_key = PrivateKey::::new(rng).unwrap(); + let future_validator_addr = Address::try_from(&future_validator_key).unwrap(); + + redelegate(&process, &store, delegator_key, &future_validator_addr, 1, rng).unwrap(); + + // The bond state must point to the new (non-committee) address. + assert_eq!(bond_state(&store, delegator_addr).unwrap(), Some((future_validator_addr, MIN_DELEGATOR_STAKE))); +} + +/// Ensure that fully unbonding a delegator (and claiming the unbond) removes the `redelegated` entry. +#[test] +fn test_full_unbond_removes_redelegate_state() { + let rng = &mut TestRng::default(); + + let process = Process::::load().unwrap(); + let store = sample_finalize_store!(); + + let (validators, delegators) = initialize_stakers(&store, 2, 1, rng).unwrap(); + let mut validators_iter = validators.iter(); + let (v1_key, (v1_addr, _, _, v1_withdraw)) = validators_iter.next().unwrap(); + let (v2_key, (v2_addr, _, _, v2_withdraw)) = validators_iter.next().unwrap(); + let (delegator_key, (delegator_addr, _)) = delegators.first().unwrap(); + + bond_validator(&process, &store, v1_key, v1_withdraw, MIN_VALIDATOR_STAKE, TEST_COMMISSION, rng).unwrap(); + bond_validator(&process, &store, v2_key, v2_withdraw, MIN_VALIDATOR_STAKE, TEST_COMMISSION, rng).unwrap(); + bond_public(&process, &store, delegator_key, v1_addr, delegator_addr, MIN_DELEGATOR_STAKE, rng).unwrap(); + + // Redelegate to validator 2 so a `redelegated` entry exists. + redelegate(&process, &store, delegator_key, v2_addr, 1, rng).unwrap(); + assert!(redelegate_state(&store, delegator_addr).unwrap().is_some()); + + // Fully unbond the delegator from validator 2. + unbond_public(&process, &store, delegator_key, delegator_addr, MIN_DELEGATOR_STAKE, 2, rng).unwrap(); + let unbond_height = unbond_state(&store, delegator_addr).unwrap().unwrap().1; + + // Claim the unbond — this triggers `remove redelegated[staker]` in the finalize. + claim_unbond_public(&process, &store, delegator_key, delegator_addr, unbond_height, rng).unwrap(); + + // All delegator state must be cleared. + assert_eq!(redelegate_state(&store, delegator_addr).unwrap(), None); + assert_eq!(bond_state(&store, delegator_addr).unwrap(), None); + assert_eq!(unbond_state(&store, delegator_addr).unwrap(), None); +} + // Test cases: // set_validator_state: diff --git a/synthesizer/program/src/lib.rs b/synthesizer/program/src/lib.rs index fef1bc5985..d763587534 100644 --- a/synthesizer/program/src/lib.rs +++ b/synthesizer/program/src/lib.rs @@ -304,6 +304,18 @@ impl ProgramCore { }) } + /// Initializes the v0 version of the credits program. + #[inline] + pub fn credits_v0() -> Result { + Self::from_str(include_str!("./resources/credits_v0.aleo")) + } + + /// Initializes the v1 version of the credits program. + #[inline] + pub fn credits_v1() -> Result { + Self::from_str(include_str!("./resources/credits_v1.aleo")) + } + /// Initializes the credits program. #[inline] pub fn credits() -> Result { diff --git a/synthesizer/program/src/resources/credits.aleo b/synthesizer/program/src/resources/credits.aleo index 8367485a53..97286df895 100644 --- a/synthesizer/program/src/resources/credits.aleo +++ b/synthesizer/program/src/resources/credits.aleo @@ -151,6 +151,24 @@ mapping pool: /**********************************************************************************************************************/ +// The `redelegated` mapping contains the delegator with their redelegation information. +mapping redelegated: + // The key represents the address of the delegator. + key as address.public; + // The value represents the redelegate state. + value as redelegate_state.public; + +// The `redelegate_state` struct tracks the redelegation information for a delegator, including the previous validator, new validator, and the unlock height. +struct redelegate_state: + // The previous validator address from which the microcredits are being redelegated. + previous_validator as address; + // The new validator address to which the microcredits are being redelegated. + new_validator as address; + // The block height at which the delegator can redelegate to another validator. + height as u32; + +/**********************************************************************************************************************/ + // The `credits` record is used to store credits privately. record credits: // The address of the owner. @@ -448,7 +466,7 @@ finalize unbond_public: input r2 as u64.public; // Set the default unbonding state. - add block.height 360u32 into r3; + add block.height 403200u32 into r3; // Construct the default unbonding state. cast 0u64 r3 into r4 as unbond_state; @@ -545,6 +563,11 @@ finalize unbond_public: // Remove the bonded state. remove bonded[r1]; + /* Redelegated */ + + // Remove the redelegated state. + remove redelegated[r1]; + /* Metadata */ // Retrieve the current number of delegators. @@ -1088,3 +1111,96 @@ finalize upgrade: set r2 into pool[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc]; /**********************************************************************************************************************/ + +/// The `redelegate` function allows a delegator to redelegate from their current validator to a new validator without unbonding first. +/// Once a delegator redelegates, they must wait for a cooldown period of 403,200 blocks (approximately 28 days) before they can redelegate +/// again to another validator. This function is callable only by the delegator's address and the delegator cannot redelegate to themselves. +/// The new validator must be different from the current validator, must be open to new stakers, and must not be unbonding. +function redelegate: + // Input the new validator's address. + input r0 as address.public; + + // Ensure that the delegator is not redelegating to itself. + assert.neq self.caller r0; + + // Redelegate to the new validator. + async redelegate self.caller r0 into r1; + + // Output the finalize future. + output r1 as credits.aleo/redelegate.future; + +finalize redelegate: + // Input the delegator's address. + input r0 as address.public; + + // Input the new validator's address. + input r1 as address.public; + + /* Check Validator Requirements */ + + // Retrieve the delegator's bond state. + // Note: If the bonded state does not exist, reject the transition. + get bonded[r0] into r2; + + // Ensure that the new validator is not the same as the current validator. + assert.neq r1 r2.validator; + + // Ensure that the delegator is not a validator. + contains committee[r0] into r3; + assert.eq r3 false; + assert.neq r0 r2.validator; + + // Ensure that the validator is open to new stakers. By default, `is_open` is set to `true`. + // Similar to `bond_public`, we do not check if the validator is in the committee. + cast true 0u8 into r4 as committee_state; + get.or_use committee[r1] r4 into r5; + assert.eq r5.is_open true; + + // Ensure that the validator is not unbonding + contains unbonding[r1] into r6; + assert.eq r6 false; + + /* Bonded */ + + // Update the bond state for the delegator + cast r1 r2.microcredits into r7 as bond_state; + + /* Redelegated */ + + // Check that the delegator has not redelegated within the cooldown period. + // Set the default redelegating state. + cast r2.validator r1 0u32 into r8 as redelegate_state; + // Get the redelegated state for the delegator, or default to the initial redelegation state. + get.or_use redelegated[r0] r8 into r9; + // Ensure that the redelegation cooldown period has passed since the last redelegation. + gte block.height r9.height into r10; + assert.eq r10 true; + + // Set the new redelegation height. + add block.height 403200u32 into r11; + // Construct the new redelegate state. + cast r2.validator r1 r11 into r12 as redelegate_state; + + /* Delegated */ + + // Update the delegated mappings for the old and the new validators. + get delegated[r2.validator] into r13; + // Subtract the delegator's microcredits from the old validator's total delegation. + sub r13 r2.microcredits into r14; + + // Get the current total delegation for the new validator. + get.or_use delegated[r1] 0u64 into r15; + // Add the delegator's microcredits to the new validator's total delegation. + add r15 r2.microcredits into r16; + + /* Writes */ + + // Set the new bond state for the delegator. + set r7 into bonded[r0]; + + // Set the new delegated totals for the old and the new validators. + set r14 into delegated[r2.validator]; + set r16 into delegated[r1]; + + // Set the new redelegation state for the delegator. + set r12 into redelegated[r0]; diff --git a/synthesizer/program/src/resources/credits_v0.aleo b/synthesizer/program/src/resources/credits_v0.aleo new file mode 100644 index 0000000000..8f964e8f57 --- /dev/null +++ b/synthesizer/program/src/resources/credits_v0.aleo @@ -0,0 +1,1038 @@ +// Copyright (c) 2019-2026 Provable Inc. +// This file is part of the snarkVM library. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/********************************************** INVARIANTS ************************************************************/ + +// 1. contains committee[r0] => +// a. contains delegated[r0] +// - The set of delegated addresses is superset of active validators. +// 2. delegated[*] >= 10,000 +// a. contain bonded[*].microcredits >= 10,000 +// - Delegators can bond to any address with the 10,000 credit minimum +// 3. delegated[r0] < 10,000,000 => +// a. !contain committee[r0] +// - If the delegated total (which includes the self bond) is less than 10,000,000 credits, it won't be in the committee +// 4. bond[r0].validator == r0 => +// a. contains committee[r0] +// - Self bonded addresses are always active validators. Delegators **can** force validators to unbond by removing sufficient delegation +// 5. committee[r0] => +// a. bonded[r0].microcredits >= 100 credits +// - Self bonded addresses are always active validators with at least 100 credits self bonded +// b. delegated[r0] >= 10,000,000 credits +// - The total delegated amount for an active validator is at least 10,000,000 credits +// 6. delegated[r0] == sum bond[*].microcredits by bond[].validator == r0 +// 7. bond[r0].microcredits < 10,000 => +// a. bond[r0].validator == r0 +// b. contains committee[r0] +// - A bond value < 10,000 implies self-bonded active validator. +// 8. bond[r0].validator != r0 => +// a. bond[r0].microcredits >= 10,000 +// 9. count bonded[] - count committee[] == metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0] +// - Delegator count consistent with metadata. +// 10. count committee[] == metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc] +// 11. 100,000 + count committee[] >= count bonded[] >= count delegated[] >= count committee[] +// 12. bond[*].microcredits > 100 + + +/**********************************************************************************************************************/ + +program credits.aleo; + +/**********************************************************************************************************************/ + +/// The `committee` mapping contains the active validator set and their corresponding stake. +mapping committee: + // The key represents the address of the validator. + key as address.public; + // The value represents the committee state of the validator. + value as committee_state.public; + +// The `committee_state` struct tracks the total stake of the validator, and whether they are open to new stakers. +struct committee_state: + // The boolean flag indicating if the validator is open to new stakers. + is_open as boolean; + // The percentage amount (from 0 to 100, inclusive) of rewards that retained by the validator. + commission as u8; + +/**********************************************************************************************************************/ + +// The `delegated` mapping tracks the total amount of microcredits that are prebonded and bonded to validator addresses. +// Note: The mapping includes both prebonded and bonded microcredits. However, it does not contain unbonding microcredits. +mapping delegated: + // The key represents the address of the validator. + key as address.public; + // The value represents the amount of microcredits bonded to the validator, by the validator and its delegators. + value as u64.public; + +/**********************************************************************************************************************/ + +/// The `metadata` mapping stores: +/// - The number of members in the committee. +/// - The number of delegators. +mapping metadata: + // The key represents the index at which the count is stored. + // - This address (aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc) stores the number of **members** in the committee. + // - This address (aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0) stores the number of **delegators**. + key as address.public; + // The value represents the count. + value as u32.public; + +/**********************************************************************************************************************/ + +// The `bonded` mapping represents the amount of microcredits that are currently bonded. +mapping bonded: + // The key represents the address of the staker, which includes the validators and their delegators. + key as address.public; + // The value represents the bond state. + value as bond_state.public; + +// The `bond_state` struct tracks the amount of microcredits that are currently bonded to the specified validator. +struct bond_state: + // The address of the validator. + validator as address; + // The amount of microcredits that are currently bonded to the specified validator. + microcredits as u64; + +/**********************************************************************************************************************/ + +// The `unbonding` mapping contains a set of stakers with their unbonding microcredits and unlock height. +mapping unbonding: + // The key represents the address of the staker, which includes the validators and their delegators. + key as address.public; + // The value represents the unbond state. + value as unbond_state.public; + +// The `unbond_state` struct tracks the microcredits that are currently unbonding, along with the unlock height. +struct unbond_state: + // The amount of microcredits that are currently unbonding. + microcredits as u64; + // The block height at which the unbonding will be complete, and can be claimed. + height as u32; + +/**********************************************************************************************************************/ + +// The `account` mapping is used to store credits publicly. +mapping account: + // The key represents the address of the owner. + key as address.public; + // The value represents the amount of public microcredits that belong to the specified owner. + value as u64.public; + +/**********************************************************************************************************************/ + +// The `withdraw` mapping contains the staking address and their corresponding withdrawal address. +mapping withdraw: + // The key represents the staking address of the owner. + key as address.public; + // The value represents the withdrawal address of the owner. + value as address.public; + +/**********************************************************************************************************************/ + +// The `credits` record is used to store credits privately. +record credits: + // The address of the owner. + owner as address.private; + // The amount of private microcredits that belong to the specified owner. + microcredits as u64.private; + +/**********************************************************************************************************************/ + +// This function allows a validator to bond their microcredits to themselves and specify a withdrawal address +// and commission percentage. +// +// The commission percentage is the portion of rewards that the validator may claim for itself. +// Note: Setting the commission percentage to 100 results in all rewards being retained for the validator. +// +// The corresponding functions for 'bond_validator' are 'unbond_public' and 'claim_unbond_public'. +function bond_validator: + // Input the withdrawal address. + input r0 as address.public; + // Input the amount of microcredits to bond. + input r1 as u64.public; + // Input the commission percentage. + input r2 as u8.public; + + // Ensure the withdrawal address is not the validator address. + assert.neq self.signer r0; + + // Determine if the amount is at least 1 credit. + gte r1 1_000_000u64 into r3; + // Enforce the amount is at least 1 credit. + assert.eq r3 true; + + // Ensure the commission percentage does not exceed 100%. + gt r2 100u8 into r4; + assert.neq r4 true; + + // Bond the specified amount of microcredits to the specified validator. + async bond_validator self.signer r0 r1 r2 into r5; + // Output the finalize future. + output r5 as credits.aleo/bond_validator.future; + +finalize bond_validator: + // Input the validator's address. + input r0 as address.public; + // Input the withdrawal address. + input r1 as address.public; + // Input the amount of microcredits to bond. + input r2 as u64.public; + // Input the commission percentage. + input r3 as u8.public; + + // Retrieve the withdrawal address for the validator. + get.or_use withdraw[r0] r1 into r4; + // Ensure that the withdrawal address is consistent. + assert.eq r1 r4; + + // Construct the initial committee state. + // Note: We set the initial 'is_open' state to 'true'. + cast true r3 into r5 as committee_state; + // Retrieve the committee state of the specified validator. + get.or_use committee[r0] r5 into r6; + + // Ensure the commission percentage remains unchanged. + // Note: The commission percentage can only be set when the validator bonds for the first time. + assert.eq r3 r6.commission; + + /* Bonded */ + + // Construct the initial bond state. + cast r0 0u64 into r7 as bond_state; + // Get the bond state for the signer, or default to the initial bond state. + get.or_use bonded[r0] r7 into r8; + // Enforce the validator matches in the bond state. + assert.eq r8.validator r0; + + // Increment the amount of microcredits for the bond state. + add r8.microcredits r2 into r9; + // Construct the updated bond state. + cast r0 r9 into r10 as bond_state; + + /* Delegated */ + + // Include total pre-delegated amount when deciding if validator has enough credits to join committee. + // First get the current delegated amount, or start with 0 if none delegated. + get.or_use delegated[r0] 0u64 into r11; + // Self-bond amount + existing delegated amount = total bond. + add r2 r11 into r12; + // Determine if the amount is at least 10 million credits. + gte r12 10_000_000_000_000u64 into r13; + // Enforce the amount is at least 10 million credits. + assert.eq r13 true; + + /* Account */ + + // Get the balance of the signer. + // If the account does not exist, this finalize scope will fail. + get account[r0] into r14; + // Decrement the balance of the signer. + sub r14 r2 into r15; + + /* Writes */ + + // if (new_validator) + contains committee[r0] into r16; + branch.eq r16 true to validator_in_committee; + // { + // Set the withdrawal address. + // Note: This operation is only necessary on the first time for a validator entry, in order to initialize the value. + set r4 into withdraw[r0]; + + // Check if the initial bond amount is at least 100 credits. + gte r2 100_000_000u64 into r17; + // Ensure that the initial bond is at least 100 credits. + assert.eq r17 true; + + // Get the committee size. + get.or_use metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc] 0u32 into r18; + // Increment the committee size by one. + add r18 1u32 into r19; + // Set the new committee size. + set r19 into metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc]; + + // Check if the validator exists in the unbonding state. + contains unbonding[r0] into r20; + // Ensure the validator currently is not unbonding. + assert.eq r20 false; + // } + + position validator_in_committee; + + // Update the committee state of the specified validator. + set r6 into committee[r0]; + // Update the delegated amount. + set r12 into delegated[r0]; + // Update the bond state for the signer. + set r10 into bonded[r0]; + // Update the balance of the signer. + set r15 into account[r0]; + +/**********************************************************************************************************************/ + +// This function allows any delegator to bond their microcredits to a validator and specify a withdrawal address. +// +// The declared validator is **not** required to be in the committee yet, however it may **not** be unbonding. +// This function enforces a minimum of 10,000 credits for each delegator. +// The maximum number of delegators allowed in the committee is 100,000 delegator addresses. +// +// The corresponding functions for 'bond_public' are 'bond_validator', 'unbond_public', and 'claim_unbond_public'. +function bond_public: + // Input the validator's address. + input r0 as address.public; + // Input the withdrawal address. + input r1 as address.public; + // Input the amount of microcredits to bond. + input r2 as u64.public; + + // Determine if the amount is at least one credit. + gte r2 1_000_000u64 into r3; + // Enforce the amount is at least one credit. + assert.eq r3 true; + + // Do not allow self-bonding. Validators start with bond_validator. + assert.neq self.caller r0; + + // Bond the specified amount of microcredits to the specified validator. + async bond_public self.caller r0 r1 r2 into r4; + // Output the finalize future. + output r4 as credits.aleo/bond_public.future; + +finalize bond_public: + // Input the staker's address. + input r0 as address.public; + // Input the validator's address. + input r1 as address.public; + // Input the withdrawal address. + input r2 as address.public; + // Input the amount of microcredits to bond. + input r3 as u64.public; + + // Retrieve the withdrawal address for the staker. + get.or_use withdraw[r0] r2 into r4; + // Ensure that the withdrawal address is consistent. + assert.eq r2 r4; + + // Check if the delegator is already bonded to the validator. + contains bonded[r0] into r5; + branch.eq r5 true to continue_bond_delegator; + // { + // Set the withdrawal address. + // Note: This operation is only necessary on the first time for a staker entry, in order to initialize the value. + set r2 into withdraw[r0]; + + // Ensure that the validator is open to new stakers. By default, `is_open` is set to `true`. + cast true 0u8 into r6 as committee_state; + get.or_use committee[r1] r6 into r7; + assert.eq r7.is_open true; + + // Get the number of delegators. + get.or_use metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0] 0u32 into r8; + // Increment the number of bonded delegators by one. + add r8 1u32 into r9; + // Determine if the number of delegators is less than or equal to 100_000. + lte r9 100_000u32 into r10; + // Enforce that the number of delegators is less than or equal to 100_000. + assert.eq r10 true; + // Set the new number of delegators. + set r9 into metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0]; + // } + + position continue_bond_delegator; + + /* Bonded */ + + // Construct the initial bond state. + cast r1 0u64 into r11 as bond_state; + // Get the bond state for the caller, or default to the initial bond state. + get.or_use bonded[r0] r11 into r12; + // Enforce the validator matches in the bond state. + assert.eq r12.validator r1; + + // Increment the microcredits in the bond state. + add r12.microcredits r3 into r13; + + // Determine if the amount is at least 10 thousand credits. + gte r13 10_000_000_000u64 into r14; + // Enforce the amount is at least 10 thousand credits. + assert.eq r14 true; + + // Construct the updated bond state. + cast r1 r13 into r15 as bond_state; + + /* Account */ + + // Get the balance of the caller. + // If the account does not exist, this finalize scope will fail. + get account[r0] into r16; + // Decrement the balance of the caller. + sub r16 r3 into r17; + + /* Delegated */ + + // Get current total delegated amount. + get.or_use delegated[r1] 0u64 into r18; + // Add new bond amount to current delegation. + add r3 r18 into r19; + + /* Unbonding */ + + // Check if the validator exists in the unbonding state. + contains unbonding[r1] into r20; + // Ensure the validator currently is not unbonding. + assert.eq r20 false; + + /* Writes */ + + // Update the bond state for the caller. + set r15 into bonded[r0]; + // Update the balance of the caller. + set r17 into account[r0]; + // Update delegated amount. + set r19 into delegated[r1]; + +/**********************************************************************************************************************/ + +// This function allows any staker to unbond their microcredits from a validator, +// callable only by using the withdrawal address of the staker or the validator. +// +// **Validators**: It will remove the validator if the self bonded balance falls below 100 credits +// or the total delegated balance falls below 10M credits. +// **Delegators**: It will remove the validator if the total delegated balance falls below 10M credits. +// It will remove the entire bond_state of the delegator if it falls below 10,000 credits. +// +// Validators are permitted to fully unbond any of their delegators. When a validator unbonds a delegator, +// the entire bonded balance is unbonded, regardless of the amount of microcredits and may end up removing the validator +// from the committee if the total delegated balance falls below 10M credits. +// +// The corresponding function for 'unbond_public' is 'claim_unbond_public'. +function unbond_public: + // Input the staker's address. + input r0 as address.public; + // Input the amount of microcredits to unbond. + input r1 as u64.public; + + // Unbond the specified amount of microcredits for the specified validator. + async unbond_public self.caller r0 r1 into r2; + // Output the finalize future. + output r2 as credits.aleo/unbond_public.future; + +finalize unbond_public: + // Input the caller's address. + input r0 as address.public; + // Input the staker's address. + input r1 as address.public; + // Input the amount of microcredits to unbond. + input r2 as u64.public; + + // Set the default unbonding state. + add block.height 360u32 into r3; + // Construct the default unbonding state. + cast 0u64 r3 into r4 as unbond_state; + + // Retrieve the staker's bond state. + // Note: If the bonded state does not exist, reject the transition. + get bonded[r1] into r5; + + /* Check Caller's Permission */ + + // Get the staker's withdrawal address. + get withdraw[r1] into r6; + // Check if the caller's address equals the staker's withdrawal address. + is.eq r0 r6 into r7; + + // Check if the validator's withdrawal address has been set. + // Note: This contains check must use `withdraw` and **not** `committee` to ensure the validator + // can unbond delegators even when the validator is not in the committee. Of course, if the validator is + // in the committee, then the validator must be able to unbond its delegators. + contains withdraw[r5.validator] into r8; + // Get the validator's withdrawal address from the bond state, using the zero address as the default. + get.or_use withdraw[r5.validator] aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc into r9; + // Check if the validator's withdrawal address matches the caller. + is.eq r0 r9 into r10; + // AND the withdraw address has been set (to prevent an edge case where a validator uses the zero address as a withdrawal address). + and r8 r10 into r11; + + // Either the caller is the staker's withdrawal address OR the validator's withdrawal address is set to the caller. + or r7 r11 into r12; + // Enforce the permission as `true`. + assert.eq r12 true; + + // Check if bonded to self (validator) or to a different address (delegator). + is.eq r5.validator r1 into r13; + branch.eq r13 true to unbond_validator; + + /* Unbond the Delegator */ + // { + // Retrieve or initialize the unbonding state. + get.or_use unbonding[r1] r4 into r14; + // Retrieve the delegated amount in microcredits for the validator. + get delegated[r5.validator] into r15; + + // Calculate new bonded microcredits. + // Note: If the subtraction underflows, reject the transition. + sub r5.microcredits r2 into r16; + + // Check if the delegator will fall below 10,000 bonded credits. + lt r16 10_000_000_000u64 into r17; + + // If the validator is forcing the delegator to unbond OR the delegator will fall below 10,000 bonded credits. + or r11 r17 into r18; + + // Determine the amount to unbond: requested amount if >= 10,000 credits, otherwise the full bonded amount. + ternary r18 r5.microcredits r2 into r19; + + /* Unbonding */ + + // Increment existing unbond amount by the new unbond amount. + // Note: If the addition overflows, reject the transition. + add r14.microcredits r19 into r20; + // Construct the updated unbond state. + cast r20 r3 into r21 as unbond_state; + // Store the new unbonding state. + set r21 into unbonding[r1]; + + /* Delegated */ + + // Calculate the new delegated total for the validator. + // Note: If the subtraction underflows, reject the transition. + sub r15 r19 into r22; + // Store the new delegated total. + set r22 into delegated[r5.validator]; + + // Check if the new bonded state is falling below 10,000 credits, or if the validator is forcing an unbond on the delegator. + branch.eq r18 true to remove_delegator; + + /* Decrement the Delegator */ + // { + /* Bonded */ + + // Construct the new bond state. + cast r5.validator r16 into r23 as bond_state; + // Set the new bond state. + set r23 into bonded[r1]; + + // Jump to the end of the unbond delegator process. + branch.eq true true to end_unbond_delegator; + // } + + position remove_delegator; + // { + /* Bonded */ + + // Remove the bonded state. + remove bonded[r1]; + + /* Metadata */ + + // Retrieve the current number of delegators. + get metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0] into r24; + // Decrement the number of delegators by one. + sub r24 1u32 into r25; + // Store the new number of delegators. + set r25 into metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0]; + // } Intentionally fall through to end_unbond_delegator + + position end_unbond_delegator; + // { + // Check if the new delegated total is at least 10M credits. + gte r22 10_000_000_000_000u64 into r26; + // Jump to end if the delegated total is at least 10M credits. + branch.eq r26 true to end; + // } + // If the delegated total is below 10M credits, continue to unbonding the validator. + //} + + /* Unbond the Validator */ + position unbond_validator; + // { + // Check if the committee contains the validator. + contains committee[r5.validator] into r27; + // Determine if the delegator unbonding from a validator is not in the committee. + nor r13 r27 into r28; + // Jump to the end, if the delegator unbonding from a validator is not in the committee. + branch.eq r28 true to end; + + // Retrieve the committee state. + // Note: If the committee state does not exist, reject the transition. + get committee[r5.validator] into r29; + // Retrieve the bond state of the validator. + // Note: If the bonded state does not exist, reject the transition. + get bonded[r5.validator] into r30; + // Retrieve the total delegated balance for the validator. + // Note: If the delegated state does not exist, reject the transition. + get delegated[r5.validator] into r31; + + // Check if the current delegated amount is below 10M credits. + // Note: This will only be `true` if a delegator unbond caused the validator to fall below 10M credits. + lt r31 10_000_000_000_000u64 into r32; + branch.eq r32 true to remove_validator; + + // Calculate the updated delegated total after unbonding. + // Note: If the subtraction underflows, reject the transition. + sub r31 r2 into r33; + + // Calculate the updated amount of microcredits after unbonding. + sub r30.microcredits r2 into r34; + + // Check if the new bond state is below the required minimums. + gte r34 100_000_000u64 into r35; // Minimum self bond requirement: 100 credits. + gte r33 10_000_000_000_000u64 into r36; // Minimum total delegated requirement: 10M credits. + + // If either bond state is below the thresholds, remove the validator from the committee. + and r35 r36 into r37; + branch.eq r37 false to remove_validator; + + /* Unbonding */ + + // Retrieve or initialize the unbonding state. + get.or_use unbonding[r5.validator] r4 into r38; + // Increment the unbond amount. + // Note: If the addition overflows, reject the transition. + add r38.microcredits r2 into r39; + // Set the updated unbond state. + cast r39 r3 into r40 as unbond_state; + // Store the new unbonding state. + set r40 into unbonding[r5.validator]; + + /* Delegated */ + + // Store the new delegated total. + set r33 into delegated[r5.validator]; + + /* Bonded */ + + // Construct and store the new bond state. + cast r5.validator r34 into r41 as bond_state; + set r41 into bonded[r5.validator]; + + // Jump to end. + branch.eq true true to end; + //} + + /* Remove the Validator from the Committee */ + position remove_validator; + // { + /* Committee */ + + // Remove the validator from the committee. + remove committee[r5.validator]; + + /* Metadata */ + + // Retrieve the current committee size. + get metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc] into r42; + // Decrement the committee size by one. + sub r42 1u32 into r43; + // Store the new committee size. + set r43 into metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc]; + + /* Delegated */ + + // Decrease the delegated total by the bonded amount. + sub r31 r30.microcredits into r44; + // Store the new delegated total. + set r44 into delegated[r5.validator]; + + /* Bonded */ + + // Remove the bonded state. + remove bonded[r5.validator]; + + /* Unbonding */ + + // Retrieve or initialize the unbonding state. + get.or_use unbonding[r5.validator] r4 into r45; + // Increment the unbond amount by the full bonded amount. + // Note: If the addition overflows, reject the transition. + add r30.microcredits r45.microcredits into r46; + // Construct the updated unbond state. + cast r46 r3 into r47 as unbond_state; + // Store the new unbonding state. + set r47 into unbonding[r5.validator]; + //} + + position end; + +/**********************************************************************************************************************/ + +// The `claim_unbond_public` function allows any staker to claim their microcredits +// to their withdrawal address after the unbonding period. +// +// Note: Please be advised that this function is callable by any user for any staker. +// +// This function also removes the staker's withdrawal address if the staker no longer has any bonded balance. +function claim_unbond_public: + // Input the staker's address. + input r0 as address.public; + + // Claim the unbonded microcredits. + async claim_unbond_public r0 into r1; + // Output the finalize future. + output r1 as credits.aleo/claim_unbond_public.future; + +finalize claim_unbond_public: + // Input the staker's address. + input r0 as address.public; + + // Get the unbond state for the address, or fail if it does not exist. + get unbonding[r0] into r1; + // Determine if unbonding is complete. + gte block.height r1.height into r2; + // Enforce the unbonding is complete. + assert.eq r2 true; + + /* Withdraw */ + + // Get the withdrawal address for the address. + get withdraw[r0] into r3; + + /* Account */ + + // Add the unbonded amount to the withdrawal address public balance. + // Increments `account[r3]` by `r1.microcredits`. + // If `account[r3]` does not exist, 0u64 is used. + // If `account[r3] + r1.microcredits` overflows, `claim_unbond_public` is reverted. + get.or_use account[r3] 0u64 into r4; + add r1.microcredits r4 into r5; + set r5 into account[r3]; + + /* Unbonding */ + + // Remove the unbond state for the staker. + remove unbonding[r0]; + + // Check if the staker is still bonded. + contains bonded[r0] into r6; + // Ends the `claim_unbond_public` logic. + branch.eq r6 true to end; + + /* Withdraw */ + + // If the caller is no longer bonded, remove the withdrawal address. + remove withdraw[r0]; + + // The terminus. + position end; + +/**********************************************************************************************************************/ + +// This function allows a validator to set their state to be either opened or closed to new stakers. +// When the validator is open to new stakers, any staker (including the validator) can bond or unbond from the validator. +// When the validator is closed to new stakers, existing stakers can still bond or unbond from the validator, but new stakers cannot bond. +// +// This function serves two primary purposes: +// 1. Allow a validator to leave the committee, by closing themselves to stakers and then unbonding all of their stakers. +// 2. Allow a validator to maintain their % of stake, by closing themselves to allowing more stakers to bond to them. +function set_validator_state: + // Input the 'is_open' state. + input r0 as boolean.public; + // Set the validator to be either open or closed to new stakers. + async set_validator_state self.caller r0 into r1; + // Output the finalize future. + output r1 as credits.aleo/set_validator_state.future; + +finalize set_validator_state: + // Input the validator's address. + input r0 as address.public; + // Input the 'is_open' state. + input r1 as boolean.public; + + // Get the committee state for the specified validator. + // If the validator does not exist, this finalize scope will fail. + get committee[r0] into r2; + + // Construct the updated committee state. + cast r1 r2.commission into r3 as committee_state; + // Update the committee state for the specified validator. + set r3 into committee[r0]; + +/**********************************************************************************************************************/ + +// The `transfer_public` function sends the specified amount +// from the caller's `account` to the receiver's `account`. +function transfer_public: + // Input the receiver. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Transfer the credits publicly. + async transfer_public self.caller r0 r1 into r2; + // Output the finalize future. + output r2 as credits.aleo/transfer_public.future; + +finalize transfer_public: + // Input the caller. + input r0 as address.public; + // Input the receiver. + input r1 as address.public; + // Input the amount. + input r2 as u64.public; + // Decrements `account[r0]` by `r2`. + // If `account[r0] - r2` underflows, `transfer_public` is reverted. + get account[r0] into r3; + sub r3 r2 into r4; + set r4 into account[r0]; + // Increments `account[r1]` by `r2`. + // If `account[r1]` does not exist, 0u64 is used. + // If `account[r1] + r2` overflows, `transfer_public` is reverted. + get.or_use account[r1] 0u64 into r5; + add r5 r2 into r6; + set r6 into account[r1]; + +/**********************************************************************************************************************/ + +// The `transfer_public_as_signer` function sends the specified amount +// from the signer's `account` to the receiver's `account`. +function transfer_public_as_signer: + // Input the receiver. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Transfer the credits publicly. + async transfer_public_as_signer self.signer r0 r1 into r2; + // Output the finalize future. + output r2 as credits.aleo/transfer_public_as_signer.future; + +finalize transfer_public_as_signer: + // Input the signer. + input r0 as address.public; + // Input the receiver. + input r1 as address.public; + // Input the amount. + input r2 as u64.public; + // Decrements `account[r0]` by `r2`. + // If `account[r0] - r2` underflows, `transfer_public_as_signer` is reverted. + get account[r0] into r3; + sub r3 r2 into r4; + set r4 into account[r0]; + // Increments `account[r1]` by `r2`. + // If `account[r1]` does not exist, 0u64 is used. + // If `account[r1] + r2` overflows, `transfer_public_as_signer` is reverted. + get.or_use account[r1] 0u64 into r5; + add r5 r2 into r6; + set r6 into account[r1]; + +/**********************************************************************************************************************/ + +// The `transfer_private` function sends the specified amount +// from the sender's record to the receiver in a record. +function transfer_private: + // Input the sender's record. + input r0 as credits.record; + // Input the receiver. + input r1 as address.private; + // Input the amount. + input r2 as u64.private; + // Checks the given record has a sufficient amount. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. The destination register `r3` holds + // the change amount to be returned to the sender. + sub r0.microcredits r2 into r3; + // Construct a record for the specified receiver. + cast r1 r2 into r4 as credits.record; + // Construct a record with the change amount for the sender. + cast r0.owner r3 into r5 as credits.record; + // Output the receiver's record. + output r4 as credits.record; + // Output the sender's change record. + output r5 as credits.record; + +/**********************************************************************************************************************/ + +// The `transfer_private_to_public` function turns a specified amount +// from a record into public credits for the specified receiver. +// +// This function preserves privacy for the sender's record, however +// it publicly reveals the receiver and the amount. +function transfer_private_to_public: + // Input the sender's record. + input r0 as credits.record; + // Input the receiver. + input r1 as address.public; + // Input the amount. + input r2 as u64.public; + // Checks the given record has a sufficient amount. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. The destination register `r3` holds + // the change amount for the sender. + sub r0.microcredits r2 into r3; + // Construct a record with the change amount for the sender. + cast r0.owner r3 into r4 as credits.record; + // Increment the amount publicly for the receiver. + async transfer_private_to_public r1 r2 into r5; + // Output the sender's change record. + output r4 as credits.record; + // Output the finalize future. + output r5 as credits.aleo/transfer_private_to_public.future; + +finalize transfer_private_to_public: + // Input the receiver. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Retrieve the balance of the receiver. + // If `account[r0]` does not exist, 0u64 is used. + get.or_use account[r0] 0u64 into r2; + // Increments `account[r0]` by `r1`. + // If `r1 + r2` overflows, `transfer_private_to_public` is reverted. + add r1 r2 into r3; + // Updates the balance of the sender. + set r3 into account[r0]; + +/**********************************************************************************************************************/ + +// The `transfer_public_to_private` function turns a specified amount +// from the mapping `account` into a record for the specified receiver. +// +// This function publicly reveals the sender, the receiver, and the specified amount. +// However, subsequent methods using the receiver's record can preserve the receiver's privacy. +function transfer_public_to_private: + // Input the receiver. + input r0 as address.private; + // Input the amount. + input r1 as u64.public; + // Construct a record for the receiver. + cast r0 r1 into r2 as credits.record; + // Decrement the balance of the sender publicly. + async transfer_public_to_private self.caller r1 into r3; + // Output the record of the receiver. + output r2 as credits.record; + // Output the finalize future. + output r3 as credits.aleo/transfer_public_to_private.future; + +finalize transfer_public_to_private: + // Input the sender. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Retrieve the balance of the sender. + get account[r0] into r2; + // Decrements `account[r0]` by `r1`. + // If `r2 - r1` underflows, `transfer_public_to_private` is reverted. + sub r2 r1 into r3; + // Updates the balance of the sender. + set r3 into account[r0]; + +/**********************************************************************************************************************/ + +// The `join` function combines two records into one. +function join: + // Input the first record. + input r0 as credits.record; + // Input the second record. + input r1 as credits.record; + // Combines the amount of the first record and the second record. + // This `add` operation is safe, and the proof will fail + // if an overflow occurs. + add r0.microcredits r1.microcredits into r2; + // Construct a record with the combined amount. + cast r0.owner r2 into r3 as credits.record; + // Output the record. + output r3 as credits.record; + +/**********************************************************************************************************************/ + +// The `split` function splits a record into two records. The given input amount will be stored in the first record, +// and the remaining amount will be stored in the second record, with the fee deducted from the remaining amount. +// If the caller executes a transaction that contains only a call to this function, then the transaction does not +// require a fee, unless the caller wishes to provide an additional fee. Transactions that contain multiple transitions +// (that include one or more calls to this function) will require a fee as per standard consensus rules. +function split: + // Input the record. + input r0 as credits.record; + // Input the amount to split. + input r1 as u64.private; + // Checks the given record has a sufficient amount to split. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. + sub r0.microcredits r1 into r2; + // Checks the given record has a sufficient fee to remove. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. + sub r2 10_000u64 into r3; + // Construct the first record. + cast r0.owner r1 into r4 as credits.record; + // Construct the second record. + cast r0.owner r3 into r5 as credits.record; + // Output the first record. + output r4 as credits.record; + // Output the second record. + output r5 as credits.record; + +/**********************************************************************************************************************/ + +// The `fee_private` function charges the specified amount from the sender's record. +function fee_private: + // Input the sender's record. + input r0 as credits.record; + // Input the amount. + input r1 as u64.public; + // Input the priority fee amount. + input r2 as u64.public; + // Input the deployment or execution ID. + input r3 as field.public; + // Ensure the amount is nonzero. + assert.neq r1 0u64; + // Ensure the deployment or execution ID is nonzero. + assert.neq r3 0field; + // Add the fee and priority fee amounts. + add r1 r2 into r4; + // Checks the given record has a sufficient amount. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. The destination register `r3` holds + // the change amount for the sender. + sub r0.microcredits r4 into r5; + // Construct a record with the change amount for the sender. + cast r0.owner r5 into r6 as credits.record; + // Output the sender's change record. + output r6 as credits.record; + +/**********************************************************************************************************************/ + +// The `fee_public` function charges the specified amount from the sender's account. +function fee_public: + // Input the amount. + input r0 as u64.public; + // Input the priority fee amount. + input r1 as u64.public; + // Input the deployment or execution ID. + input r2 as field.public; + // Ensure the amount is nonzero. + assert.neq r0 0u64; + // Ensure the deployment or execution ID is nonzero. + assert.neq r2 0field; + // Add the fee and priority fee amounts. + add r0 r1 into r3; + // Decrement the balance of the sender publicly. + async fee_public self.signer r3 into r4; + // Output the finalize future. + output r4 as credits.aleo/fee_public.future; + +finalize fee_public: + // Input the sender's address. + input r0 as address.public; + // Input the total fee amount. + input r1 as u64.public; + // Retrieve the balance of the sender. + // If `account[r0]` does not exist, `fee_public` is reverted. + get account[r0] into r2; + // Decrements `account[r0]` by `r1`. + // If `r2 - r1` underflows, `fee_public` is reverted. + sub r2 r1 into r3; + // Updates the balance of the sender. + set r3 into account[r0]; + +/**********************************************************************************************************************/ diff --git a/synthesizer/program/src/resources/credits_v1.aleo b/synthesizer/program/src/resources/credits_v1.aleo new file mode 100644 index 0000000000..8367485a53 --- /dev/null +++ b/synthesizer/program/src/resources/credits_v1.aleo @@ -0,0 +1,1090 @@ +// Copyright (c) 2019-2026 Provable Inc. +// This file is part of the snarkVM library. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/********************************************** INVARIANTS ************************************************************/ + +// 1. contains committee[r0] => +// a. contains delegated[r0] +// - The set of delegated addresses is superset of active validators. +// 2. delegated[*] >= 10,000 +// a. contain bonded[*].microcredits >= 10,000 +// - Delegators can bond to any address with the 10,000 credit minimum +// 3. delegated[r0] < 10,000,000 => +// a. !contain committee[r0] +// - If the delegated total (which includes the self bond) is less than 10,000,000 credits, it won't be in the committee +// 4. bond[r0].validator == r0 => +// a. contains committee[r0] +// - Self bonded addresses are always active validators. Delegators **can** force validators to unbond by removing sufficient delegation +// 5. committee[r0] => +// a. bonded[r0].microcredits >= 100 credits +// - Self bonded addresses are always active validators with at least 100 credits self bonded +// b. delegated[r0] >= 10,000,000 credits +// - The total delegated amount for an active validator is at least 10,000,000 credits +// 6. delegated[r0] == sum bond[*].microcredits by bond[].validator == r0 +// 7. bond[r0].microcredits < 10,000 => +// a. bond[r0].validator == r0 +// b. contains committee[r0] +// - A bond value < 10,000 implies self-bonded active validator. +// 8. bond[r0].validator != r0 => +// a. bond[r0].microcredits >= 10,000 +// 9. count bonded[] - count committee[] == metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0] +// - Delegator count consistent with metadata. +// 10. count committee[] == metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc] +// 11. 100,000 + count committee[] >= count bonded[] >= count delegated[] >= count committee[] +// 12. bond[*].microcredits > 100 + + +/**********************************************************************************************************************/ + +program credits.aleo; + +/**********************************************************************************************************************/ + +/// The `committee` mapping contains the active validator set and their corresponding stake. +mapping committee: + // The key represents the address of the validator. + key as address.public; + // The value represents the committee state of the validator. + value as committee_state.public; + +// The `committee_state` struct tracks the total stake of the validator, and whether they are open to new stakers. +struct committee_state: + // The boolean flag indicating if the validator is open to new stakers. + is_open as boolean; + // The percentage amount (from 0 to 100, inclusive) of rewards that retained by the validator. + commission as u8; + +/**********************************************************************************************************************/ + +// The `delegated` mapping tracks the total amount of microcredits that are prebonded and bonded to validator addresses. +// Note: The mapping includes both prebonded and bonded microcredits. However, it does not contain unbonding microcredits. +mapping delegated: + // The key represents the address of the validator. + key as address.public; + // The value represents the amount of microcredits bonded to the validator, by the validator and its delegators. + value as u64.public; + +/**********************************************************************************************************************/ + +/// The `metadata` mapping stores: +/// - The number of members in the committee. +/// - The number of delegators. +mapping metadata: + // The key represents the index at which the count is stored. + // - This address (aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc) stores the number of **members** in the committee. + // - This address (aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0) stores the number of **delegators**. + key as address.public; + // The value represents the count. + value as u32.public; + +/**********************************************************************************************************************/ + +// The `bonded` mapping represents the amount of microcredits that are currently bonded. +mapping bonded: + // The key represents the address of the staker, which includes the validators and their delegators. + key as address.public; + // The value represents the bond state. + value as bond_state.public; + +// The `bond_state` struct tracks the amount of microcredits that are currently bonded to the specified validator. +struct bond_state: + // The address of the validator. + validator as address; + // The amount of microcredits that are currently bonded to the specified validator. + microcredits as u64; + +/**********************************************************************************************************************/ + +// The `unbonding` mapping contains a set of stakers with their unbonding microcredits and unlock height. +mapping unbonding: + // The key represents the address of the staker, which includes the validators and their delegators. + key as address.public; + // The value represents the unbond state. + value as unbond_state.public; + +// The `unbond_state` struct tracks the microcredits that are currently unbonding, along with the unlock height. +struct unbond_state: + // The amount of microcredits that are currently unbonding. + microcredits as u64; + // The block height at which the unbonding will be complete, and can be claimed. + height as u32; + +/**********************************************************************************************************************/ + +// The `account` mapping is used to store credits publicly. +mapping account: + // The key represents the address of the owner. + key as address.public; + // The value represents the amount of public microcredits that belong to the specified owner. + value as u64.public; + +/**********************************************************************************************************************/ + +// The `withdraw` mapping contains the staking address and their corresponding withdrawal address. +mapping withdraw: + // The key represents the staking address of the owner. + key as address.public; + // The value represents the withdrawal address of the owner. + value as address.public; + +/**********************************************************************************************************************/ + +mapping pool: + // The key represents the index at which the count is stored. + // - This address (aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc) stores the number of microcredits + // that have been migrated into the pool for records after the inclusion upgrade. + key as address.public; + // The value represents the amount of microcredits in the pool. + value as u64.public; + +/**********************************************************************************************************************/ + +// The `credits` record is used to store credits privately. +record credits: + // The address of the owner. + owner as address.private; + // The amount of private microcredits that belong to the specified owner. + microcredits as u64.private; + +/**********************************************************************************************************************/ + +// This function allows a validator to bond their microcredits to themselves and specify a withdrawal address +// and commission percentage. +// +// The commission percentage is the portion of rewards that the validator may claim for itself. +// Note: Setting the commission percentage to 100 results in all rewards being retained for the validator. +// +// The corresponding functions for 'bond_validator' are 'unbond_public' and 'claim_unbond_public'. +function bond_validator: + // Input the withdrawal address. + input r0 as address.public; + // Input the amount of microcredits to bond. + input r1 as u64.public; + // Input the commission percentage. + input r2 as u8.public; + + // Ensure the withdrawal address is not the validator address. + assert.neq self.signer r0; + + // Determine if the amount is at least 1 credit. + gte r1 1_000_000u64 into r3; + // Enforce the amount is at least 1 credit. + assert.eq r3 true; + + // Ensure the commission percentage does not exceed 100%. + gt r2 100u8 into r4; + assert.neq r4 true; + + // Bond the specified amount of microcredits to the specified validator. + async bond_validator self.signer r0 r1 r2 into r5; + // Output the finalize future. + output r5 as credits.aleo/bond_validator.future; + +finalize bond_validator: + // Input the validator's address. + input r0 as address.public; + // Input the withdrawal address. + input r1 as address.public; + // Input the amount of microcredits to bond. + input r2 as u64.public; + // Input the commission percentage. + input r3 as u8.public; + + // Retrieve the withdrawal address for the validator. + get.or_use withdraw[r0] r1 into r4; + // Ensure that the withdrawal address is consistent. + assert.eq r1 r4; + + // Construct the initial committee state. + // Note: We set the initial 'is_open' state to 'true'. + cast true r3 into r5 as committee_state; + // Retrieve the committee state of the specified validator. + get.or_use committee[r0] r5 into r6; + + // Ensure the commission percentage remains unchanged. + // Note: The commission percentage can only be set when the validator bonds for the first time. + assert.eq r3 r6.commission; + + /* Bonded */ + + // Construct the initial bond state. + cast r0 0u64 into r7 as bond_state; + // Get the bond state for the signer, or default to the initial bond state. + get.or_use bonded[r0] r7 into r8; + // Enforce the validator matches in the bond state. + assert.eq r8.validator r0; + + // Increment the amount of microcredits for the bond state. + add r8.microcredits r2 into r9; + // Construct the updated bond state. + cast r0 r9 into r10 as bond_state; + + /* Delegated */ + + // Include total pre-delegated amount when deciding if validator has enough credits to join committee. + // First get the current delegated amount, or start with 0 if none delegated. + get.or_use delegated[r0] 0u64 into r11; + // Self-bond amount + existing delegated amount = total bond. + add r2 r11 into r12; + // Determine if the amount is at least 10 million credits. + gte r12 10_000_000_000_000u64 into r13; + // Enforce the amount is at least 10 million credits. + assert.eq r13 true; + + /* Account */ + + // Get the balance of the signer. + // If the account does not exist, this finalize scope will fail. + get account[r0] into r14; + // Decrement the balance of the signer. + sub r14 r2 into r15; + + /* Writes */ + + // if (new_validator) + contains committee[r0] into r16; + branch.eq r16 true to validator_in_committee; + // { + // Set the withdrawal address. + // Note: This operation is only necessary on the first time for a validator entry, in order to initialize the value. + set r4 into withdraw[r0]; + + // Check if the initial bond amount is at least 100 credits. + gte r2 100_000_000u64 into r17; + // Ensure that the initial bond is at least 100 credits. + assert.eq r17 true; + + // Get the committee size. + get.or_use metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc] 0u32 into r18; + // Increment the committee size by one. + add r18 1u32 into r19; + // Set the new committee size. + set r19 into metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc]; + + // Check if the validator exists in the unbonding state. + contains unbonding[r0] into r20; + // Ensure the validator currently is not unbonding. + assert.eq r20 false; + // } + + position validator_in_committee; + + // Update the committee state of the specified validator. + set r6 into committee[r0]; + // Update the delegated amount. + set r12 into delegated[r0]; + // Update the bond state for the signer. + set r10 into bonded[r0]; + // Update the balance of the signer. + set r15 into account[r0]; + +/**********************************************************************************************************************/ + +// This function allows any delegator to bond their microcredits to a validator and specify a withdrawal address. +// +// The declared validator is **not** required to be in the committee yet, however it may **not** be unbonding. +// This function enforces a minimum of 10,000 credits for each delegator. +// The maximum number of delegators allowed in the committee is 100,000 delegator addresses. +// +// The corresponding functions for 'bond_public' are 'bond_validator', 'unbond_public', and 'claim_unbond_public'. +function bond_public: + // Input the validator's address. + input r0 as address.public; + // Input the withdrawal address. + input r1 as address.public; + // Input the amount of microcredits to bond. + input r2 as u64.public; + + // Determine if the amount is at least one credit. + gte r2 1_000_000u64 into r3; + // Enforce the amount is at least one credit. + assert.eq r3 true; + + // Do not allow self-bonding. Validators start with bond_validator. + assert.neq self.caller r0; + + // Bond the specified amount of microcredits to the specified validator. + async bond_public self.caller r0 r1 r2 into r4; + // Output the finalize future. + output r4 as credits.aleo/bond_public.future; + +finalize bond_public: + // Input the staker's address. + input r0 as address.public; + // Input the validator's address. + input r1 as address.public; + // Input the withdrawal address. + input r2 as address.public; + // Input the amount of microcredits to bond. + input r3 as u64.public; + + // Retrieve the withdrawal address for the staker. + get.or_use withdraw[r0] r2 into r4; + // Ensure that the withdrawal address is consistent. + assert.eq r2 r4; + + // Check if the delegator is already bonded to the validator. + contains bonded[r0] into r5; + branch.eq r5 true to continue_bond_delegator; + // { + // Set the withdrawal address. + // Note: This operation is only necessary on the first time for a staker entry, in order to initialize the value. + set r2 into withdraw[r0]; + + // Ensure that the validator is open to new stakers. By default, `is_open` is set to `true`. + cast true 0u8 into r6 as committee_state; + get.or_use committee[r1] r6 into r7; + assert.eq r7.is_open true; + + // Get the number of delegators. + get.or_use metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0] 0u32 into r8; + // Increment the number of bonded delegators by one. + add r8 1u32 into r9; + // Determine if the number of delegators is less than or equal to 100_000. + lte r9 100_000u32 into r10; + // Enforce that the number of delegators is less than or equal to 100_000. + assert.eq r10 true; + // Set the new number of delegators. + set r9 into metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0]; + // } + + position continue_bond_delegator; + + /* Bonded */ + + // Construct the initial bond state. + cast r1 0u64 into r11 as bond_state; + // Get the bond state for the caller, or default to the initial bond state. + get.or_use bonded[r0] r11 into r12; + // Enforce the validator matches in the bond state. + assert.eq r12.validator r1; + + // Increment the microcredits in the bond state. + add r12.microcredits r3 into r13; + + // Determine if the amount is at least 10 thousand credits. + gte r13 10_000_000_000u64 into r14; + // Enforce the amount is at least 10 thousand credits. + assert.eq r14 true; + + // Construct the updated bond state. + cast r1 r13 into r15 as bond_state; + + /* Account */ + + // Get the balance of the caller. + // If the account does not exist, this finalize scope will fail. + get account[r0] into r16; + // Decrement the balance of the caller. + sub r16 r3 into r17; + + /* Delegated */ + + // Get current total delegated amount. + get.or_use delegated[r1] 0u64 into r18; + // Add new bond amount to current delegation. + add r3 r18 into r19; + + /* Unbonding */ + + // Check if the validator exists in the unbonding state. + contains unbonding[r1] into r20; + // Ensure the validator currently is not unbonding. + assert.eq r20 false; + + /* Writes */ + + // Update the bond state for the caller. + set r15 into bonded[r0]; + // Update the balance of the caller. + set r17 into account[r0]; + // Update delegated amount. + set r19 into delegated[r1]; + +/**********************************************************************************************************************/ + +// This function allows any staker to unbond their microcredits from a validator, +// callable only by using the withdrawal address of the staker or the validator. +// +// **Validators**: It will remove the validator if the self bonded balance falls below 100 credits +// or the total delegated balance falls below 10M credits. +// **Delegators**: It will remove the validator if the total delegated balance falls below 10M credits. +// It will remove the entire bond_state of the delegator if it falls below 10,000 credits. +// +// Validators are permitted to fully unbond any of their delegators. When a validator unbonds a delegator, +// the entire bonded balance is unbonded, regardless of the amount of microcredits and may end up removing the validator +// from the committee if the total delegated balance falls below 10M credits. +// +// The corresponding function for 'unbond_public' is 'claim_unbond_public'. +function unbond_public: + // Input the staker's address. + input r0 as address.public; + // Input the amount of microcredits to unbond. + input r1 as u64.public; + + // Unbond the specified amount of microcredits for the specified validator. + async unbond_public self.caller r0 r1 into r2; + // Output the finalize future. + output r2 as credits.aleo/unbond_public.future; + +finalize unbond_public: + // Input the caller's address. + input r0 as address.public; + // Input the staker's address. + input r1 as address.public; + // Input the amount of microcredits to unbond. + input r2 as u64.public; + + // Set the default unbonding state. + add block.height 360u32 into r3; + // Construct the default unbonding state. + cast 0u64 r3 into r4 as unbond_state; + + // Retrieve the staker's bond state. + // Note: If the bonded state does not exist, reject the transition. + get bonded[r1] into r5; + + /* Check Caller's Permission */ + + // Get the staker's withdrawal address. + get withdraw[r1] into r6; + // Check if the caller's address equals the staker's withdrawal address. + is.eq r0 r6 into r7; + + // Check if the validator's withdrawal address has been set. + // Note: This contains check must use `withdraw` and **not** `committee` to ensure the validator + // can unbond delegators even when the validator is not in the committee. Of course, if the validator is + // in the committee, then the validator must be able to unbond its delegators. + contains withdraw[r5.validator] into r8; + // Get the validator's withdrawal address from the bond state, using the zero address as the default. + get.or_use withdraw[r5.validator] aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc into r9; + // Check if the validator's withdrawal address matches the caller. + is.eq r0 r9 into r10; + // AND the withdraw address has been set (to prevent an edge case where a validator uses the zero address as a withdrawal address). + and r8 r10 into r11; + + // Either the caller is the staker's withdrawal address OR the validator's withdrawal address is set to the caller. + or r7 r11 into r12; + // Enforce the permission as `true`. + assert.eq r12 true; + + // Check if bonded to self (validator) or to a different address (delegator). + is.eq r5.validator r1 into r13; + branch.eq r13 true to unbond_validator; + + /* Unbond the Delegator */ + // { + // Retrieve or initialize the unbonding state. + get.or_use unbonding[r1] r4 into r14; + // Retrieve the delegated amount in microcredits for the validator. + get delegated[r5.validator] into r15; + + // Calculate new bonded microcredits. + // Note: If the subtraction underflows, reject the transition. + sub r5.microcredits r2 into r16; + + // Check if the delegator will fall below 10,000 bonded credits. + lt r16 10_000_000_000u64 into r17; + + // If the validator is forcing the delegator to unbond OR the delegator will fall below 10,000 bonded credits. + or r11 r17 into r18; + + // Determine the amount to unbond: requested amount if >= 10,000 credits, otherwise the full bonded amount. + ternary r18 r5.microcredits r2 into r19; + + /* Unbonding */ + + // Increment existing unbond amount by the new unbond amount. + // Note: If the addition overflows, reject the transition. + add r14.microcredits r19 into r20; + // Construct the updated unbond state. + cast r20 r3 into r21 as unbond_state; + // Store the new unbonding state. + set r21 into unbonding[r1]; + + /* Delegated */ + + // Calculate the new delegated total for the validator. + // Note: If the subtraction underflows, reject the transition. + sub r15 r19 into r22; + // Store the new delegated total. + set r22 into delegated[r5.validator]; + + // Check if the new bonded state is falling below 10,000 credits, or if the validator is forcing an unbond on the delegator. + branch.eq r18 true to remove_delegator; + + /* Decrement the Delegator */ + // { + /* Bonded */ + + // Construct the new bond state. + cast r5.validator r16 into r23 as bond_state; + // Set the new bond state. + set r23 into bonded[r1]; + + // Jump to the end of the unbond delegator process. + branch.eq true true to end_unbond_delegator; + // } + + position remove_delegator; + // { + /* Bonded */ + + // Remove the bonded state. + remove bonded[r1]; + + /* Metadata */ + + // Retrieve the current number of delegators. + get metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0] into r24; + // Decrement the number of delegators by one. + sub r24 1u32 into r25; + // Store the new number of delegators. + set r25 into metadata[aleo1qgqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqanmpl0]; + // } Intentionally fall through to end_unbond_delegator + + position end_unbond_delegator; + // { + // Check if the new delegated total is at least 10M credits. + gte r22 10_000_000_000_000u64 into r26; + // Jump to end if the delegated total is at least 10M credits. + branch.eq r26 true to end; + // } + // If the delegated total is below 10M credits, continue to unbonding the validator. + //} + + /* Unbond the Validator */ + position unbond_validator; + // { + // Check if the committee contains the validator. + contains committee[r5.validator] into r27; + // Determine if the delegator unbonding from a validator is not in the committee. + nor r13 r27 into r28; + // Jump to the end, if the delegator unbonding from a validator is not in the committee. + branch.eq r28 true to end; + + // Retrieve the committee state. + // Note: If the committee state does not exist, reject the transition. + get committee[r5.validator] into r29; + // Retrieve the bond state of the validator. + // Note: If the bonded state does not exist, reject the transition. + get bonded[r5.validator] into r30; + // Retrieve the total delegated balance for the validator. + // Note: If the delegated state does not exist, reject the transition. + get delegated[r5.validator] into r31; + + // Check if the current delegated amount is below 10M credits. + // Note: This will only be `true` if a delegator unbond caused the validator to fall below 10M credits. + lt r31 10_000_000_000_000u64 into r32; + branch.eq r32 true to remove_validator; + + // Calculate the updated delegated total after unbonding. + // Note: If the subtraction underflows, reject the transition. + sub r31 r2 into r33; + + // Calculate the updated amount of microcredits after unbonding. + sub r30.microcredits r2 into r34; + + // Check if the new bond state is below the required minimums. + gte r34 100_000_000u64 into r35; // Minimum self bond requirement: 100 credits. + gte r33 10_000_000_000_000u64 into r36; // Minimum total delegated requirement: 10M credits. + + // If either bond state is below the thresholds, remove the validator from the committee. + and r35 r36 into r37; + branch.eq r37 false to remove_validator; + + /* Unbonding */ + + // Retrieve or initialize the unbonding state. + get.or_use unbonding[r5.validator] r4 into r38; + // Increment the unbond amount. + // Note: If the addition overflows, reject the transition. + add r38.microcredits r2 into r39; + // Set the updated unbond state. + cast r39 r3 into r40 as unbond_state; + // Store the new unbonding state. + set r40 into unbonding[r5.validator]; + + /* Delegated */ + + // Store the new delegated total. + set r33 into delegated[r5.validator]; + + /* Bonded */ + + // Construct and store the new bond state. + cast r5.validator r34 into r41 as bond_state; + set r41 into bonded[r5.validator]; + + // Jump to end. + branch.eq true true to end; + //} + + /* Remove the Validator from the Committee */ + position remove_validator; + // { + /* Committee */ + + // Remove the validator from the committee. + remove committee[r5.validator]; + + /* Metadata */ + + // Retrieve the current committee size. + get metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc] into r42; + // Decrement the committee size by one. + sub r42 1u32 into r43; + // Store the new committee size. + set r43 into metadata[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc]; + + /* Delegated */ + + // Decrease the delegated total by the bonded amount. + sub r31 r30.microcredits into r44; + // Store the new delegated total. + set r44 into delegated[r5.validator]; + + /* Bonded */ + + // Remove the bonded state. + remove bonded[r5.validator]; + + /* Unbonding */ + + // Retrieve or initialize the unbonding state. + get.or_use unbonding[r5.validator] r4 into r45; + // Increment the unbond amount by the full bonded amount. + // Note: If the addition overflows, reject the transition. + add r30.microcredits r45.microcredits into r46; + // Construct the updated unbond state. + cast r46 r3 into r47 as unbond_state; + // Store the new unbonding state. + set r47 into unbonding[r5.validator]; + //} + + position end; + +/**********************************************************************************************************************/ + +// The `claim_unbond_public` function allows any staker to claim their microcredits +// to their withdrawal address after the unbonding period. +// +// Note: Please be advised that this function is callable by any user for any staker. +// +// This function also removes the staker's withdrawal address if the staker no longer has any bonded balance. +function claim_unbond_public: + // Input the staker's address. + input r0 as address.public; + + // Claim the unbonded microcredits. + async claim_unbond_public r0 into r1; + // Output the finalize future. + output r1 as credits.aleo/claim_unbond_public.future; + +finalize claim_unbond_public: + // Input the staker's address. + input r0 as address.public; + + // Get the unbond state for the address, or fail if it does not exist. + get unbonding[r0] into r1; + // Determine if unbonding is complete. + gte block.height r1.height into r2; + // Enforce the unbonding is complete. + assert.eq r2 true; + + /* Withdraw */ + + // Get the withdrawal address for the address. + get withdraw[r0] into r3; + + /* Account */ + + // Add the unbonded amount to the withdrawal address public balance. + // Increments `account[r3]` by `r1.microcredits`. + // If `account[r3]` does not exist, 0u64 is used. + // If `account[r3] + r1.microcredits` overflows, `claim_unbond_public` is reverted. + get.or_use account[r3] 0u64 into r4; + add r1.microcredits r4 into r5; + set r5 into account[r3]; + + /* Unbonding */ + + // Remove the unbond state for the staker. + remove unbonding[r0]; + + // Check if the staker is still bonded. + contains bonded[r0] into r6; + // Ends the `claim_unbond_public` logic. + branch.eq r6 true to end; + + /* Withdraw */ + + // If the caller is no longer bonded, remove the withdrawal address. + remove withdraw[r0]; + + // The terminus. + position end; + +/**********************************************************************************************************************/ + +// This function allows a validator to set their state to be either opened or closed to new stakers. +// When the validator is open to new stakers, any staker (including the validator) can bond or unbond from the validator. +// When the validator is closed to new stakers, existing stakers can still bond or unbond from the validator, but new stakers cannot bond. +// +// This function serves two primary purposes: +// 1. Allow a validator to leave the committee, by closing themselves to stakers and then unbonding all of their stakers. +// 2. Allow a validator to maintain their % of stake, by closing themselves to allowing more stakers to bond to them. +function set_validator_state: + // Input the 'is_open' state. + input r0 as boolean.public; + // Set the validator to be either open or closed to new stakers. + async set_validator_state self.caller r0 into r1; + // Output the finalize future. + output r1 as credits.aleo/set_validator_state.future; + +finalize set_validator_state: + // Input the validator's address. + input r0 as address.public; + // Input the 'is_open' state. + input r1 as boolean.public; + + // Get the committee state for the specified validator. + // If the validator does not exist, this finalize scope will fail. + get committee[r0] into r2; + + // Construct the updated committee state. + cast r1 r2.commission into r3 as committee_state; + // Update the committee state for the specified validator. + set r3 into committee[r0]; + +/**********************************************************************************************************************/ + +// The `transfer_public` function sends the specified amount +// from the caller's `account` to the receiver's `account`. +function transfer_public: + // Input the receiver. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Transfer the credits publicly. + async transfer_public self.caller r0 r1 into r2; + // Output the finalize future. + output r2 as credits.aleo/transfer_public.future; + +finalize transfer_public: + // Input the caller. + input r0 as address.public; + // Input the receiver. + input r1 as address.public; + // Input the amount. + input r2 as u64.public; + // Decrements `account[r0]` by `r2`. + // If `account[r0] - r2` underflows, `transfer_public` is reverted. + get account[r0] into r3; + sub r3 r2 into r4; + set r4 into account[r0]; + // Increments `account[r1]` by `r2`. + // If `account[r1]` does not exist, 0u64 is used. + // If `account[r1] + r2` overflows, `transfer_public` is reverted. + get.or_use account[r1] 0u64 into r5; + add r5 r2 into r6; + set r6 into account[r1]; + +/**********************************************************************************************************************/ + +// The `transfer_public_as_signer` function sends the specified amount +// from the signer's `account` to the receiver's `account`. +function transfer_public_as_signer: + // Input the receiver. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Transfer the credits publicly. + async transfer_public_as_signer self.signer r0 r1 into r2; + // Output the finalize future. + output r2 as credits.aleo/transfer_public_as_signer.future; + +finalize transfer_public_as_signer: + // Input the signer. + input r0 as address.public; + // Input the receiver. + input r1 as address.public; + // Input the amount. + input r2 as u64.public; + // Decrements `account[r0]` by `r2`. + // If `account[r0] - r2` underflows, `transfer_public_as_signer` is reverted. + get account[r0] into r3; + sub r3 r2 into r4; + set r4 into account[r0]; + // Increments `account[r1]` by `r2`. + // If `account[r1]` does not exist, 0u64 is used. + // If `account[r1] + r2` overflows, `transfer_public_as_signer` is reverted. + get.or_use account[r1] 0u64 into r5; + add r5 r2 into r6; + set r6 into account[r1]; + +/**********************************************************************************************************************/ + +// The `transfer_private` function sends the specified amount +// from the sender's record to the receiver in a record. +function transfer_private: + // Input the sender's record. + input r0 as credits.record; + // Input the receiver. + input r1 as address.private; + // Input the amount. + input r2 as u64.private; + // Checks the given record has a sufficient amount. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. The destination register `r3` holds + // the change amount to be returned to the sender. + sub r0.microcredits r2 into r3; + // Construct a record for the specified receiver. + cast r1 r2 into r4 as credits.record; + // Construct a record with the change amount for the sender. + cast r0.owner r3 into r5 as credits.record; + // Output the receiver's record. + output r4 as credits.record; + // Output the sender's change record. + output r5 as credits.record; + +/**********************************************************************************************************************/ + +// The `transfer_private_to_public` function turns a specified amount +// from a record into public credits for the specified receiver. +// +// This function preserves privacy for the sender's record, however +// it publicly reveals the receiver and the amount. +function transfer_private_to_public: + // Input the sender's record. + input r0 as credits.record; + // Input the receiver. + input r1 as address.public; + // Input the amount. + input r2 as u64.public; + // Checks the given record has a sufficient amount. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. The destination register `r3` holds + // the change amount for the sender. + sub r0.microcredits r2 into r3; + // Construct a record with the change amount for the sender. + cast r0.owner r3 into r4 as credits.record; + // Increment the amount publicly for the receiver. + async transfer_private_to_public r1 r2 into r5; + // Output the sender's change record. + output r4 as credits.record; + // Output the finalize future. + output r5 as credits.aleo/transfer_private_to_public.future; + +finalize transfer_private_to_public: + // Input the receiver. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Retrieve the balance of the receiver. + // If `account[r0]` does not exist, 0u64 is used. + get.or_use account[r0] 0u64 into r2; + // Increments `account[r0]` by `r1`. + // If `r1 + r2` overflows, `transfer_private_to_public` is reverted. + add r1 r2 into r3; + // Updates the balance of the sender. + set r3 into account[r0]; + +/**********************************************************************************************************************/ + +// The `transfer_public_to_private` function turns a specified amount +// from the mapping `account` into a record for the specified receiver. +// +// This function publicly reveals the sender, the receiver, and the specified amount. +// However, subsequent methods using the receiver's record can preserve the receiver's privacy. +function transfer_public_to_private: + // Input the receiver. + input r0 as address.private; + // Input the amount. + input r1 as u64.public; + // Construct a record for the receiver. + cast r0 r1 into r2 as credits.record; + // Decrement the balance of the sender publicly. + async transfer_public_to_private self.caller r1 into r3; + // Output the record of the receiver. + output r2 as credits.record; + // Output the finalize future. + output r3 as credits.aleo/transfer_public_to_private.future; + +finalize transfer_public_to_private: + // Input the sender. + input r0 as address.public; + // Input the amount. + input r1 as u64.public; + // Retrieve the balance of the sender. + get account[r0] into r2; + // Decrements `account[r0]` by `r1`. + // If `r2 - r1` underflows, `transfer_public_to_private` is reverted. + sub r2 r1 into r3; + // Updates the balance of the sender. + set r3 into account[r0]; + +/**********************************************************************************************************************/ + +// The `join` function combines two records into one. +function join: + // Input the first record. + input r0 as credits.record; + // Input the second record. + input r1 as credits.record; + // Combines the amount of the first record and the second record. + // This `add` operation is safe, and the proof will fail + // if an overflow occurs. + add r0.microcredits r1.microcredits into r2; + // Construct a record with the combined amount. + cast r0.owner r2 into r3 as credits.record; + // Output the record. + output r3 as credits.record; + +/**********************************************************************************************************************/ + +// The `split` function splits a record into two records. The given input amount will be stored in the first record, +// and the remaining amount will be stored in the second record, with the fee deducted from the remaining amount. +// If the caller executes a transaction that contains only a call to this function, then the transaction does not +// require a fee, unless the caller wishes to provide an additional fee. Transactions that contain multiple transitions +// (that include one or more calls to this function) will require a fee as per standard consensus rules. +function split: + // Input the record. + input r0 as credits.record; + // Input the amount to split. + input r1 as u64.private; + // Checks the given record has a sufficient amount to split. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. + sub r0.microcredits r1 into r2; + // Checks the given record has a sufficient fee to remove. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. + sub r2 10_000u64 into r3; + // Construct the first record. + cast r0.owner r1 into r4 as credits.record; + // Construct the second record. + cast r0.owner r3 into r5 as credits.record; + // Output the first record. + output r4 as credits.record; + // Output the second record. + output r5 as credits.record; + +/**********************************************************************************************************************/ + +// The `fee_private` function charges the specified amount from the sender's record. +function fee_private: + // Input the sender's record. + input r0 as credits.record; + // Input the amount. + input r1 as u64.public; + // Input the priority fee amount. + input r2 as u64.public; + // Input the deployment or execution ID. + input r3 as field.public; + // Ensure the amount is nonzero. + assert.neq r1 0u64; + // Ensure the deployment or execution ID is nonzero. + assert.neq r3 0field; + // Add the fee and priority fee amounts. + add r1 r2 into r4; + // Checks the given record has a sufficient amount. + // This `sub` operation is safe, and the proof will fail + // if an underflow occurs. The destination register `r3` holds + // the change amount for the sender. + sub r0.microcredits r4 into r5; + // Construct a record with the change amount for the sender. + cast r0.owner r5 into r6 as credits.record; + // Output the sender's change record. + output r6 as credits.record; + +/**********************************************************************************************************************/ + +// The `fee_public` function charges the specified amount from the sender's account. +function fee_public: + // Input the amount. + input r0 as u64.public; + // Input the priority fee amount. + input r1 as u64.public; + // Input the deployment or execution ID. + input r2 as field.public; + // Ensure the amount is nonzero. + assert.neq r0 0u64; + // Ensure the deployment or execution ID is nonzero. + assert.neq r2 0field; + // Add the fee and priority fee amounts. + add r0 r1 into r3; + // Decrement the balance of the sender publicly. + async fee_public self.signer r3 into r4; + // Output the finalize future. + output r4 as credits.aleo/fee_public.future; + +finalize fee_public: + // Input the sender's address. + input r0 as address.public; + // Input the total fee amount. + input r1 as u64.public; + // Retrieve the balance of the sender. + // If `account[r0]` does not exist, `fee_public` is reverted. + get account[r0] into r2; + // Decrements `account[r0]` by `r1`. + // If `r2 - r1` underflows, `fee_public` is reverted. + sub r2 r1 into r3; + // Updates the balance of the sender. + set r3 into account[r0]; + +/**********************************************************************************************************************/ + +// The `upgrade` function migrates the input record to the pool of records after the inclusion upgrade. +// The original record will be spent and the new record will be created with the same amount of credits. +// If the caller executes a transaction that contains only a call to this function, then the transaction does not +// require a fee, unless the caller wishes to provide an additional fee. Transactions that contain multiple transitions +// (that include one or more calls to this function) will require a fee as per standard consensus rules. +function upgrade: + // Input the record. + input r0 as credits.record; + + // Check if the record amount is at most 500,000 credits. + lte r0.microcredits 1_000_000_000_000u64 into r1; + // Ensure that the record amount is at most 500,000 credits. + assert.eq r1 true; + + // Construct a record for the specified receiver. + cast r0.owner r0.microcredits into r2 as credits.record; + + // Decrement the private pool of records before the inclusion upgrade. + async upgrade r0.microcredits into r3; + // Output the upgraded record. + output r2 as credits.record; + // Output the finalize future. + output r3 as credits.aleo/upgrade.future; + +finalize upgrade: + // Input the record amount. + input r0 as u64.public; + + // Get the remaining `credits.record` pool size. + get.or_use pool[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc] 0u64 into r1; + // Increment the pool by the record amount. + add r1 r0 into r2; + // Determine if the total number of migrated credits is less than or equal to 4_000_000 credits. + lte r2 4_000_000_000_000u64 into r3; + // Enforce that the total number of migrated credits is less than or equal to 4_000_000 credits. + assert.eq r3 true; + + // Set the new pool size. + set r2 into pool[aleo1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3ljyzc]; + +/**********************************************************************************************************************/ diff --git a/synthesizer/src/vm/mod.rs b/synthesizer/src/vm/mod.rs index 1e8324b85c..7fe909c8c3 100644 --- a/synthesizer/src/vm/mod.rs +++ b/synthesizer/src/vm/mod.rs @@ -77,6 +77,7 @@ use snarkvm_ledger_store::{ }; use snarkvm_synthesizer_process::{ Authorization, + CreditsVersion, InclusionVersion, Process, Trace, @@ -93,7 +94,6 @@ use snarkvm_synthesizer_program::{ Program, StackTrait as _, }; -use snarkvm_synthesizer_snark::VerifyingKey; use snarkvm_utilities::try_vm_runtime; use aleo_std::prelude::{finish, lap, timer}; @@ -163,17 +163,32 @@ impl> VM { // Determine the latest block height. let latest_block_height = block_store.current_block_height(); // Determine the consensus version. - let consensus_version = N::CONSENSUS_VERSION(latest_block_height)?; // TODO (raychu86): Record Commitment - Select the proper consensus version. + let consensus_version = N::CONSENSUS_VERSION(latest_block_height)?; // Initialize a new process based on the consensus version. if (ConsensusVersion::V1..=ConsensusVersion::V7).contains(&consensus_version) { Process::load_v0()? + } else if (ConsensusVersion::V8..=ConsensusVersion::V14).contains(&consensus_version) { + Process::load_v1()? } else { Process::load()? } }; #[cfg(any(test, feature = "test"))] // Initialize a new process. - let mut process = Process::load()?; + // We load from Credits V1 for tests because V2 is a superset of V1 and adds new functionality + // whose activation via ConsensusVersion needs to be tested. + let mut process = { + // Determine the latest block height. + let latest_block_height = block_store.current_block_height(); + // Determine the consensus version. + let consensus_version = N::CONSENSUS_VERSION(latest_block_height)?; + // Initialize a new process based on the consensus version. + if (ConsensusVersion::V1..=ConsensusVersion::V14).contains(&consensus_version) { + Process::load_v1()? + } else { + Process::load()? + } + }; // Retrieve the list of deployment transaction IDs and their associated block heights. let deployment_ids = transaction_store.deployment_transaction_ids().collect::>(); @@ -524,9 +539,13 @@ impl> VM { // Next, finalize the transactions. match self.finalize(state, block.ratifications(), block.solutions(), block.transactions()) { Ok(_ratified_finalize_operations) => { - // If the block advances to `ConsensusVersion::V8`, updated the VKs used for the credits program. + // If the block advances to `ConsensusVersion::V8`, updated the credits program to V1. if N::CONSENSUS_HEIGHT(ConsensusVersion::V8).unwrap_or_default() == block.height() { - self.update_credits_verifying_keys()?; + self.update_credits_program(CreditsVersion::V1)?; + } + // If the block advances to `ConsensusVersion::v15`, update the credits program to V2. + if N::CONSENSUS_HEIGHT(ConsensusVersion::V15).unwrap_or_default() == block.height() { + self.update_credits_program(CreditsVersion::V2)?; } // Unpause the atomic writes, executing the ones queued from block insertion and finalization. #[cfg(feature = "rocks")] @@ -573,30 +592,18 @@ impl> VM { impl> VM { /// Update the `credits.aleo` program in the VM with the latest verifying keys. - fn update_credits_verifying_keys(&self) -> Result<()> { - // Initialize the store for 'credits.aleo'. - let credits = Program::::credits()?; - + fn update_credits_program(&self, credits_version: CreditsVersion) -> Result<()> { // Acquire the process lock. - let process = self.process.write(); + let mut process = self.process.write(); + + // Generate the new stack for 'credits.aleo' with the latest verifying keys. + let stack = Stack::new_credits(&process, credits_version)?; // Synthesize the 'credits.aleo' verifying keys. - for function_name in credits.functions().keys() { - // Remove the proving key. - process.remove_proving_key(credits.id(), function_name)?; - // Load the verifying key. - let verifying_key = N::get_credits_verifying_key(function_name.to_string())?; - // Retrieve the number of public and private variables. - // Note: This number does *NOT* include the number of constants. This is safe because - // this program is never deployed, as it is a first-class citizen of the protocol. - let num_variables = verifying_key.circuit_info.num_public_and_private_variables as u64; - // Insert the verifying key. - process.insert_verifying_key( - credits.id(), - function_name, - VerifyingKey::new(verifying_key.clone(), num_variables), - )?; - } + stack.insert_credits_verifying_keys(credits_version)?; + + // Add the stack to the process. + process.add_stack(stack); Ok(()) } diff --git a/synthesizer/src/vm/tests/test_v14/snark_verify.rs b/synthesizer/src/vm/tests/test_v14/snark_verify.rs index 47602a354e..8fe7636a8b 100644 --- a/synthesizer/src/vm/tests/test_v14/snark_verify.rs +++ b/synthesizer/src/vm/tests/test_v14/snark_verify.rs @@ -15,9 +15,13 @@ use super::*; -use circuit::{Circuit, Environment}; -use console::algorithms::U8; -use snarkvm_synthesizer_snark::{ProvingKey, UniversalSRS}; +use crate::vm::test_helpers::*; + +use circuit::{Circuit, Environment, Inject, Mode}; +use console::{algorithms::U8, network::ConsensusVersion, program::Value}; +use snarkvm_synthesizer_program::Program; +use snarkvm_synthesizer_snark::{ProvingKey, UniversalSRS, VerifyingKey}; +use snarkvm_utilities::TestRng; use std::sync::OnceLock; diff --git a/synthesizer/src/vm/tests/test_v15/mod.rs b/synthesizer/src/vm/tests/test_v15/mod.rs index 1d68cef049..50aaedfaed 100644 --- a/synthesizer/src/vm/tests/test_v15/mod.rs +++ b/synthesizer/src/vm/tests/test_v15/mod.rs @@ -24,16 +24,18 @@ mod commit_raw; // Additional test for cost estimation without a private key. mod cost_for_call; +// Test for upgrading to credits.aleo V2. +mod update_credits; + use super::*; use crate::vm::test_helpers::{sample_vm_at_height, *}; use console::{ - account::ViewKey, + account::{Address, PrivateKey, ViewKey}, network::ConsensusVersion, - program::{Identifier, Value}, + program::{Identifier, Literal, Plaintext, ProgramID, Value}, }; - use snarkvm_synthesizer_program::Program; use snarkvm_utilities::TestRng; diff --git a/synthesizer/src/vm/tests/test_v15/update_credits.rs b/synthesizer/src/vm/tests/test_v15/update_credits.rs new file mode 100644 index 0000000000..4ea60716e0 --- /dev/null +++ b/synthesizer/src/vm/tests/test_v15/update_credits.rs @@ -0,0 +1,252 @@ +// Copyright (c) 2019-2026 Provable Inc. +// This file is part of the snarkVM library. + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: + +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; + +// The number of blocks that must pass before an unbond can be claimed before V15. +const UNBOND_BLOCKS_BEFORE_V15: u32 = 360; +// The number of blocks that must pass before an unbond can be claimed at and after V15. +const UNBOND_BLOCKS_AFTER_V15: u32 = 403_200; +// The minimum delegator stake required to bond to a validator, in microcredits. +const MIN_DELEGATOR_STAKE: u64 = 10_000_000_000u64; + +// Returns `true` if the credits.aleo stack contains the `redelegate` function. +fn credits_has_redelegate(vm: &VM) -> bool { + vm.process() + .read() + .get_stack("credits.aleo") + .unwrap() + .program() + .contains_function(&Identifier::from_str("redelegate").unwrap()) +} + +// Returns the unlock height from the unbonding state of the given staker. +fn unbond_height(vm: &VM, staker: &Address) -> u32 { + let Some(Value::Plaintext(Plaintext::Struct(state, _))) = vm + .finalize_store() + .get_value_confirmed( + ProgramID::from_str("credits.aleo").unwrap(), + Identifier::from_str("unbonding").unwrap(), + &Plaintext::from(Literal::Address(*staker)), + ) + .unwrap() + else { + panic!("Expected an unbond state for {staker}"); + }; + match state.get(&Identifier::from_str("height").unwrap()) { + Some(Plaintext::Literal(Literal::U32(h), _)) => **h, + _ => panic!("Expected a height in the unbond state for {staker}"), + } +} + +// This test verifies that `redelegate` cannot be executed before V15, as the function does not +// exist in credits.aleo prior to V15. +#[test] +fn test_redelegate_before_v15_fails() { + let rng = &mut TestRng::default(); + + let caller_private_key = sample_genesis_private_key(rng); + let v15_height = CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V15).unwrap(); + + // Initialize the VM at one block before V15. Credits.aleo is V1 at this height. + let vm = sample_vm_at_height(v15_height - 1, rng); + assert!(!credits_has_redelegate(&vm), "Expected credits.aleo not to have `redelegate` before V15"); + + // Generate a fresh address to use as the intended new validator. + let new_validator_key = PrivateKey::::new(rng).unwrap(); + let new_validator_address = Address::try_from(&new_validator_key).unwrap(); + + // Attempt to call `redelegate` before V15. This should fail because the function does not + // exist in credits.aleo V1. + let result = vm.execute( + &caller_private_key, + ("credits.aleo", "redelegate"), + [Value::from_str(&new_validator_address.to_string()).unwrap()].iter(), + None, + 0, + None, + rng, + ); + assert!(result.is_err(), "Expected `redelegate` to fail before V15, as the function does not exist"); +} + +// This test verifies that `redelegate` can be executed after V15. +#[test] +fn test_redelegate_after_v15_succeeds() { + let rng = &mut TestRng::default(); + + let caller_private_key = sample_genesis_private_key(rng); + let caller_address = Address::try_from(&caller_private_key).unwrap(); + let v15_height = CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V15).unwrap(); + + // Initialize the VM at V15. Credits.aleo is V2 at this height. + let vm = sample_vm_at_height(v15_height, rng); + assert!(credits_has_redelegate(&vm), "Expected credits.aleo to have `redelegate` at V15"); + + // Create a fresh delegator account and a fresh address to redelegate to. + let delegator_key = PrivateKey::::new(rng).unwrap(); + let delegator_address = Address::try_from(&delegator_key).unwrap(); + let new_validator_key = PrivateKey::::new(rng).unwrap(); + let new_validator_address = Address::try_from(&new_validator_key).unwrap(); + + // Fund the delegator with enough credits to bond and pay fees. + let transfer = vm + .execute( + &caller_private_key, + ("credits.aleo", "transfer_public"), + [ + Value::from_str(&delegator_address.to_string()).unwrap(), + Value::from_str(&format!("{}u64", MIN_DELEGATOR_STAKE * 2)).unwrap(), + ] + .iter(), + None, + 0, + None, + rng, + ) + .unwrap(); + let block = sample_next_block(&vm, &caller_private_key, &[transfer], rng).unwrap(); + assert_eq!(block.transactions().num_accepted(), 1); + vm.add_next_block(&block).unwrap(); + + // Bond the delegator to the genesis validator (which is open by default). + let bond = vm + .execute( + &delegator_key, + ("credits.aleo", "bond_public"), + [ + Value::from_str(&caller_address.to_string()).unwrap(), + Value::from_str(&delegator_address.to_string()).unwrap(), + Value::from_str(&format!("{MIN_DELEGATOR_STAKE}u64")).unwrap(), + ] + .iter(), + None, + 0, + None, + rng, + ) + .unwrap(); + let block = sample_next_block(&vm, &caller_private_key, &[bond], rng).unwrap(); + assert_eq!(block.transactions().num_accepted(), 1); + vm.add_next_block(&block).unwrap(); + + // Execute `redelegate` from the delegator to the new validator address. Since the new validator + // is not in the committee, it defaults to open, so the redelegate should succeed. + let redelegate = vm + .execute( + &delegator_key, + ("credits.aleo", "redelegate"), + [Value::from_str(&new_validator_address.to_string()).unwrap()].iter(), + None, + 0, + None, + rng, + ) + .unwrap(); + let block = sample_next_block(&vm, &caller_private_key, &[redelegate], rng).unwrap(); + assert_eq!(block.transactions().num_accepted(), 1); + assert_eq!(block.transactions().num_rejected(), 0); + assert_eq!(block.aborted_transaction_ids().len(), 0); + vm.add_next_block(&block).unwrap(); +} + +// This test verifies that unbonding before V15 produces an unlock height of `block_height + 360`. +#[test] +fn test_unbond_height_before_v15() { + let rng = &mut TestRng::default(); + + let caller_private_key = sample_genesis_private_key(rng); + let caller_address = Address::try_from(&caller_private_key).unwrap(); + let v15_height = CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V15).unwrap(); + + // Initialize the VM at one block before V15. Credits.aleo is V1 at this height, which uses + // a 360-block unbonding period. + let vm = sample_vm_at_height(v15_height - 1, rng); + + // Unbond the genesis validator. Block 18 (the V15 block) finalizes with V1 credits before + // the credits program is updated to V2, so the unbond height should reflect V1 semantics. + let unbond = vm + .execute( + &caller_private_key, + ("credits.aleo", "unbond_public"), + [Value::from_str(&caller_address.to_string()).unwrap(), Value::from_str("1u64").unwrap()].iter(), + None, + 0, + None, + rng, + ) + .unwrap(); + let block = sample_next_block(&vm, &caller_private_key, &[unbond], rng).unwrap(); + assert_eq!(block.transactions().num_accepted(), 1); + vm.add_next_block(&block).unwrap(); + + // The unbond was finalized at the current block height. Verify the unlock height reflects + // the 360-block unbonding period from credits.aleo V1. + let expected_height = vm.block_store().current_block_height() + UNBOND_BLOCKS_BEFORE_V15; + assert_eq!(unbond_height(&vm, &caller_address), expected_height); +} + +// This test verifies that unbonding after V15 produces an unlock height of `block_height + 403,200`. +#[test] +fn test_unbond_height_after_v15() { + let rng = &mut TestRng::default(); + + let caller_private_key = sample_genesis_private_key(rng); + let caller_address = Address::try_from(&caller_private_key).unwrap(); + let v15_height = CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V15).unwrap(); + + // Initialize the VM at V15. Credits.aleo is V2 at this height, which uses a 403,200-block + // unbonding period. + let vm = sample_vm_at_height(v15_height, rng); + + // Unbond the genesis validator. The next block finalizes with V2 credits, so the unbond + // height should reflect V2 semantics. + let unbond = vm + .execute( + &caller_private_key, + ("credits.aleo", "unbond_public"), + [Value::from_str(&caller_address.to_string()).unwrap(), Value::from_str("1u64").unwrap()].iter(), + None, + 0, + None, + rng, + ) + .unwrap(); + let block = sample_next_block(&vm, &caller_private_key, &[unbond], rng).unwrap(); + assert_eq!(block.transactions().num_accepted(), 1); + vm.add_next_block(&block).unwrap(); + + // The unbond was finalized at the current block height. Verify the unlock height reflects + // the 403,200-block unbonding period from credits.aleo V2. + let expected_height = vm.block_store().current_block_height() + UNBOND_BLOCKS_AFTER_V15; + assert_eq!(unbond_height(&vm, &caller_address), expected_height); +} + +// This test verifies that the credits.aleo stack is updated with the `redelegate` function at V15. +#[test] +fn test_credits_stack_updated_at_v15() { + let rng = &mut TestRng::default(); + + let v15_height = CurrentNetwork::CONSENSUS_HEIGHT(ConsensusVersion::V15).unwrap(); + + // Initialize the VM at one block before V15 and verify that credits.aleo does not yet contain + // the `redelegate` function. + let vm_before = sample_vm_at_height(v15_height - 1, rng); + assert!(!credits_has_redelegate(&vm_before), "Expected credits.aleo not to have `redelegate` before V15"); + + // Initialize the VM at V15 and verify that credits.aleo now contains the `redelegate` function. + let vm_after = sample_vm_at_height(v15_height, rng); + assert!(credits_has_redelegate(&vm_after), "Expected credits.aleo to have `redelegate` at V15"); +} diff --git a/synthesizer/src/vm/verify.rs b/synthesizer/src/vm/verify.rs index 11be654446..93dc82b2a0 100644 --- a/synthesizer/src/vm/verify.rs +++ b/synthesizer/src/vm/verify.rs @@ -826,6 +826,14 @@ impl> VM { } } + // Perform checks if the execution contains `credits.aleo/redelegate`. + if execution.transitions().any(|t| t.is_redelegate()) { + // Do not allow `credits.aleo/upgrade` calls on the previous inclusion version or until after the migration block has passed. + if consensus_version < ConsensusVersion::V15 { + bail!("Execution verification failed - `credits.aleo/redelegate` cannot be called yet"); + } + } + // Verify the execution proof, if it has not been partially-verified before. let verification = match is_partially_verified { true => Ok(()),