Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Epoch change to Light Client contract and prover service #2706

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.contracts.example
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,4 @@ export DEPLOYER_PRIVATE_KEY= #include the 0x prefix
export DEPLOYER_MNEMONIC=
export DEPLOYER_MNEMONIC_OFFSET=
export DEPLOYER_HARDWARE_WALLET_ADDRESS=
export USE_HARDWARE_WALLET=false # values {true, false}
export USE_HARDWARE_WALLET=false # values {true, false}
5 changes: 2 additions & 3 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
LightClientBench:testCorrectUpdateBench() (gas: 499764)
PlonkVerifier2_verify_Test:test_verify_succeeds() (gas: 381889)
PlonkVerifier_verify_Test:test_verify_succeeds() (gas: 388519)
LightClientBench:testCorrectUpdateBench() (gas: 516005)
PlonkVerifier_verify_Test:test_verify_succeeds() (gas: 389906)
1 change: 1 addition & 0 deletions Cargo.lock

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

1 change: 0 additions & 1 deletion contract-bindings-alloy/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,3 @@ pub mod lightclientarbitrum;
pub mod lightclientmock;
pub mod permissionedstaketable;
pub mod plonkverifier;
pub mod plonkverifier2;
1,475 changes: 1,314 additions & 161 deletions contract-bindings-alloy/src/lightclient.rs

Large diffs are not rendered by default.

1,476 changes: 1,316 additions & 160 deletions contract-bindings-alloy/src/lightclientarbitrum.rs

Large diffs are not rendered by default.

1,814 changes: 1,578 additions & 236 deletions contract-bindings-alloy/src/lightclientmock.rs

Large diffs are not rendered by default.

40 changes: 20 additions & 20 deletions contract-bindings-alloy/src/plonkverifier.rs

Large diffs are not rendered by default.

3,432 changes: 0 additions & 3,432 deletions contract-bindings-alloy/src/plonkverifier2.rs

This file was deleted.

1 change: 0 additions & 1 deletion contract-bindings-ethers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ pub mod light_client_arbitrum;
pub mod light_client_mock;
pub mod permissioned_stake_table;
pub mod plonk_verifier;
pub mod plonk_verifier_2;
pub mod shared_types;
531 changes: 523 additions & 8 deletions contract-bindings-ethers/src/light_client.rs

Large diffs are not rendered by default.

531 changes: 523 additions & 8 deletions contract-bindings-ethers/src/light_client_arbitrum.rs

Large diffs are not rendered by default.

593 changes: 585 additions & 8 deletions contract-bindings-ethers/src/light_client_mock.rs

Large diffs are not rendered by default.

22 changes: 11 additions & 11 deletions contract-bindings-ethers/src/plonk_verifier.rs

Large diffs are not rendered by default.

538 changes: 0 additions & 538 deletions contract-bindings-ethers/src/plonk_verifier_2.rs

This file was deleted.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion contract-bindings/artifacts/LightClient_bytecode.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions contracts/rust/adapter/src/bin/eval_domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use ark_ff::{Field, PrimeField};
use ark_poly::{EvaluationDomain, Radix2EvaluationDomain};

fn main() {
let domain = Radix2EvaluationDomain::<Fr>::new(2u32.pow(5) as usize).unwrap();
let domain = Radix2EvaluationDomain::<Fr>::new(2u32.pow(20) as usize).unwrap();

let size_inv = <<Fr as Field>::BasePrimeField as PrimeField>::into_bigint(domain.size_inv);
let size_inv_str = format!("0x{:X}", size_inv).to_lowercase();
Expand All @@ -26,8 +26,8 @@ fn main() {

let mut domain_elements_str = "".to_owned();

// Generates the domain elements for 8 inputs: 1, g, g^2,...,g^7
for i in 0..8 {
// Generates the domain elements: 1, g, g^2,...,g^10
for i in 0..=10 {
let mut element_fr = domain.group_gen;
element_fr = element_fr.pow([i]);

Expand Down
19 changes: 5 additions & 14 deletions contracts/rust/adapter/src/light_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ use ark_ff::PrimeField;
use ark_std::str::FromStr;
use diff_test_bn254::{field_to_u256, u256_to_field};
use ethers::{
abi::AbiDecode,
abi::Token,
abi::Tokenize,
abi::{AbiDecode, Token, Tokenize},
prelude::{AbiError, EthAbiCodec, EthAbiType},
types::U256,
};
use hotshot_types::light_client::{GenericLightClientState, GenericStakeTableState, PublicInput};
use hotshot_types::light_client::{GenericLightClientState, GenericStakeTableState};

/// Intermediate representations for `LightClientState` in Solidity
#[derive(Clone, Debug, EthAbiType, EthAbiCodec, PartialEq)]
Expand Down Expand Up @@ -73,16 +71,6 @@ impl<F: PrimeField> From<GenericLightClientState<F>> for ParsedLightClientState
}
}

impl From<PublicInput> for ParsedLightClientState {
fn from(pi: PublicInput) -> Self {
Self {
view_num: field_to_u256(pi.view_number()).as_u64(),
block_height: field_to_u256(pi.block_height()).as_u64(),
block_comm_root: field_to_u256(pi.block_comm_root()),
}
}
}

impl From<(u64, u64, U256)> for ParsedLightClientState {
fn from(s: (u64, u64, U256)) -> Self {
Self {
Expand Down Expand Up @@ -189,6 +177,7 @@ pub struct LightClientConstructorArgs {
pub light_client_state: ParsedLightClientState,
pub stake_table_state: ParsedStakeTableState,
pub max_history_seconds: u32,
pub blocks_per_epoch: u64,
}

impl LightClientConstructorArgs {
Expand All @@ -201,6 +190,7 @@ impl LightClientConstructorArgs {
light_client_state: ParsedLightClientState::dummy_genesis(),
stake_table_state: ParsedStakeTableState::dummy_genesis(),
max_history_seconds: 864000,
blocks_per_epoch: 14400, // 24hr assuming 6 sec block time
}
}
}
Expand All @@ -216,6 +206,7 @@ impl Tokenize for LightClientConstructorArgs {
ethers::abi::Token::Tuple(self.light_client_state.into_tokens()),
ethers::abi::Token::Tuple(self.stake_table_state.into_tokens()),
ethers::abi::Token::Uint(U256::from(self.max_history_seconds)),
ethers::abi::Token::Uint(U256::from(self.blocks_per_epoch)),
]
}
}
1 change: 1 addition & 0 deletions contracts/rust/diff-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ diff-test-bn254 = { git = "https://github.com/EspressoSystems/solidity-bn254.git
ethers = { version = "2.0.4" }
hotshot-contract-adapter = { path = "../adapter" }
hotshot-state-prover = { workspace = true }
hotshot-types = { workspace = true }
jf-pcs = { workspace = true }
jf-plonk = { workspace = true }
jf-signature = { workspace = true }
Expand Down
54 changes: 39 additions & 15 deletions contracts/rust/diff-test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ use ethers::{
abi::{AbiDecode, AbiEncode, Address},
types::{Bytes, U256},
};
use hotshot_contract_adapter::{jellyfish::*, light_client::ParsedLightClientState};
use hotshot_contract_adapter::{
jellyfish::*,
light_client::{ParsedLightClientState, ParsedStakeTableState},
};
use hotshot_state_prover::mock_ledger::{
gen_plonk_proof_for_test, MockLedger, MockSystemParam, STAKE_TABLE_CAPACITY,
gen_plonk_proof_for_test, MockLedger, MockSystemParam, STAKE_TABLE_CAPACITY_FOR_TEST,
};
use hotshot_types::utils::epoch_from_block_number;
use jf_pcs::prelude::Commitment;
use jf_plonk::proof_system::structs::{Proof, VerifyingKey};
use jf_plonk::proof_system::PlonkKzgSnark;
Expand Down Expand Up @@ -83,6 +87,8 @@ enum Action {
MockConsecutiveFinalizedStates,
/// Get a light client state that skipped a few blocks
MockSkipBlocks,
/// Compute the epoch number from block height
EpochCompute,
}

#[allow(clippy::type_complexity)]
Expand Down Expand Up @@ -125,7 +131,7 @@ fn main() {

let log_size = cli.args[0].parse::<u32>().unwrap();
let zeta = u256_to_field::<Fr>(cli.args[1].parse::<U256>().unwrap());
let pi_u256: [U256; 7] = AbiDecode::decode_hex(&cli.args[2]).unwrap();
let pi_u256: [U256; 11] = AbiDecode::decode_hex(&cli.args[2]).unwrap();
let pi: Vec<Fr> = pi_u256.into_iter().map(u256_to_field).collect();

let verifier = Verifier::<Bn254>::new(2u32.pow(log_size) as usize).unwrap();
Expand Down Expand Up @@ -257,7 +263,7 @@ fn main() {
}

let vk = cli.args[0].parse::<ParsedVerifyingKey>().unwrap().into();
let pi_u256: [U256; 7] = AbiDecode::decode_hex(&cli.args[1]).unwrap();
let pi_u256: [U256; 11] = AbiDecode::decode_hex(&cli.args[1]).unwrap();
let pi: Vec<Fr> = pi_u256.into_iter().map(u256_to_field).collect();
let proof: Proof<Bn254> = cli.args[2].parse::<ParsedPlonkProof>().unwrap().into();
let msg = {
Expand Down Expand Up @@ -295,8 +301,8 @@ fn main() {
.is_ok());

let vk_parsed: ParsedVerifyingKey = vk.into();
let mut pi_parsed = [U256::default(); 7];
assert_eq!(public_input.len(), 7);
let mut pi_parsed = [U256::default(); 11];
assert_eq!(public_input.len(), 11);
for (i, pi) in public_input.into_iter().enumerate() {
pi_parsed[i] = field_to_u256(pi);
}
Expand Down Expand Up @@ -380,7 +386,10 @@ fn main() {
let pp = MockSystemParam::init();
let ledger = MockLedger::init(pp, num_init_validators as usize);

let res = (ledger.get_state(), ledger.get_stake_table_state());
let res: (ParsedLightClientState, ParsedStakeTableState) = (
ledger.light_client_state().into(),
ledger.voting_stake_table_state().into(),
);
println!("{}", res.encode_hex());
}
Action::MockConsecutiveFinalizedStates => {
Expand All @@ -394,6 +403,7 @@ fn main() {

let mut new_states: Vec<ParsedLightClientState> = vec![];
let mut proofs: Vec<ParsedPlonkProof> = vec![];
let mut next_st_states: Vec<ParsedStakeTableState> = vec![];

for _ in 1..4 {
// random number of notarized but not finalized block
Expand All @@ -407,11 +417,12 @@ fn main() {
ledger.elapse_with_block();

let (pi, proof) = ledger.gen_state_proof();
new_states.push(pi.into());
next_st_states.push(pi.next_st_state.into());
new_states.push(pi.lc_state.into());
proofs.push(proof.into());
}

let res = (new_states, proofs);
let res = (new_states, next_st_states, proofs);
println!("{}", res.encode_hex());
}
Action::MockSkipBlocks => {
Expand All @@ -427,21 +438,24 @@ fn main() {
};

let pp = MockSystemParam::init();
let mut ledger = MockLedger::init(pp, STAKE_TABLE_CAPACITY / 2);
let mut ledger = MockLedger::init(pp, STAKE_TABLE_CAPACITY_FOR_TEST / 2);

for _ in 0..num_block_skipped {
ledger.elapse_with_block();
}

let res = if require_valid_proof {
let (state, proof) = ledger.gen_state_proof();
let state_parsed: ParsedLightClientState = state.into();
let (pi, proof) = ledger.gen_state_proof();
let state_parsed: ParsedLightClientState = pi.lc_state.into();
let proof_parsed: ParsedPlonkProof = proof.into();
(state_parsed, proof_parsed)
let next_stake_table: ParsedStakeTableState = pi.next_st_state.into();
(state_parsed, next_stake_table, proof_parsed)
} else {
let state_parsed = ledger.get_state();
let state_parsed = ledger.light_client_state().into();
let proof_parsed = ParsedPlonkProof::dummy(&mut ledger.rng);
(state_parsed, proof_parsed)
let next_stake_table: ParsedStakeTableState =
ledger.next_stake_table_state().into();
(state_parsed, next_stake_table, proof_parsed)
};
println!("{}", res.encode_hex());
}
Expand Down Expand Up @@ -487,5 +501,15 @@ fn main() {
let res = (vk_parsed, sig_parsed);
println!("{}", res.encode_hex());
}
Action::EpochCompute => {
if cli.args.len() != 2 {
panic!("Should provide arg1=blockNum, arg2=blocksPerEpoch");
}
let block_num = cli.args[0].parse::<u64>().unwrap();
let epoch_height = cli.args[1].parse::<u64>().unwrap();

let res = epoch_from_block_number(block_num, epoch_height);
println!("{}", (res,).encode_hex());
}
};
}
28 changes: 20 additions & 8 deletions contracts/script/LightClient.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ contract DeployLightClientScript is Script {
/// @param stateHistoryRetentionPeriod state history retention period in seconds
/// @param owner The address that will be set as the owner of the proxy (typically a multisig
/// wallet).

function run(uint32 numInitValidators, uint32 stateHistoryRetentionPeriod, address owner)
/// @param blocksPerEpoch Number of HotShot block per epoch (also per stake table snapshot)
function run(
uint32 numInitValidators,
uint32 stateHistoryRetentionPeriod,
address owner,
uint64 blocksPerEpoch
)
public
returns (
address proxyAddress,
Expand Down Expand Up @@ -57,7 +62,8 @@ contract DeployLightClientScript is Script {
proxyAddress = Upgrades.deployUUPSProxy(
contractName,
abi.encodeCall(
LC.initialize, (state, stakeState, stateHistoryRetentionPeriod, deployer)
LC.initialize,
(state, stakeState, stateHistoryRetentionPeriod, deployer, blocksPerEpoch)
)
);

Expand Down Expand Up @@ -209,7 +215,11 @@ contract UpgradeLightClientContractWithSameContractScript is Script {
/// the admin is not a multisig wallet but is the same as the associated mnemonic
/// used in staging deployments only
contract DeployLightClientContractScript is Script {
function run(uint32 numInitValidators, uint32 stateHistoryRetentionPeriod)
function run(
uint32 numInitValidators,
uint32 stateHistoryRetentionPeriod,
uint64 blocksPerEpoch
)
external
returns (
address payable proxyAddress,
Expand All @@ -229,7 +239,7 @@ contract DeployLightClientContractScript is Script {
(LC.LightClientState memory state, LC.StakeTableState memory stakeState) =
abi.decode(result, (LC.LightClientState, LC.StakeTableState));

return deployContract(state, stakeState, stateHistoryRetentionPeriod);
return deployContract(state, stakeState, stateHistoryRetentionPeriod, blocksPerEpoch);
}

/// @notice deploys the impl, proxy & initializes the impl
Expand All @@ -239,7 +249,8 @@ contract DeployLightClientContractScript is Script {
function deployContract(
LC.LightClientState memory state,
LC.StakeTableState memory stakeState,
uint32 stateHistoryRetentionPeriod
uint32 stateHistoryRetentionPeriod,
uint64 blocksPerEpoch
)
private
returns (
Expand All @@ -259,11 +270,12 @@ contract DeployLightClientContractScript is Script {

// Encode the initializer function call
bytes memory data = abi.encodeWithSignature(
"initialize((uint64,uint64,uint256),(uint256,uint256,uint256,uint256),uint32,address)",
"initialize((uint64,uint64,uint256),(uint256,uint256,uint256,uint256),uint32,address,uint64)",
state,
stakeState,
stateHistoryRetentionPeriod,
admin
admin,
blocksPerEpoch
);

// our proxy
Expand Down
10 changes: 8 additions & 2 deletions contracts/script/LightClientArb.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ contract DeployLightClientArbitrum is Script {
error RetentionPeriodIsNotSetCorrectly();
error InitialStateIsNotSetCorrectly();

function run(uint32 numInitValidators, uint32 stateHistoryRetentionPeriod, address owner)
function run(
uint32 numInitValidators,
uint32 stateHistoryRetentionPeriod,
address owner,
uint64 blocksPerEpoch
)
public
returns (
address proxyAddress,
Expand Down Expand Up @@ -47,7 +52,8 @@ contract DeployLightClientArbitrum is Script {
proxyAddress = Upgrades.deployUUPSProxy(
"LightClientArbitrum.sol:LightClientArbitrum",
abi.encodeCall(
LC.initialize, (state, stakeState, stateHistoryRetentionPeriod, deployer)
LC.initialize,
(state, stakeState, stateHistoryRetentionPeriod, deployer, blocksPerEpoch)
)
);

Expand Down
9 changes: 6 additions & 3 deletions contracts/script/LightClientCallNewFinalizedState.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ contract CallNewFinalizedState is Script {
cmds[2] = vm.toString(numInitValidators);

bytes memory result = vm.ffi(cmds);
(LC.LightClientState[] memory states, V.PlonkProof[] memory proofs,) =
abi.decode(result, (LC.LightClientState[], V.PlonkProof[], LC.StakeTableState[]));
(
LC.LightClientState[] memory states,
LC.StakeTableState[] memory nextStakeTables,
V.PlonkProof[] memory proofs
) = abi.decode(result, (LC.LightClientState[], LC.StakeTableState[], V.PlonkProof[]));

address admin;
string memory seedPhrase = vm.envString("MNEMONIC");
Expand All @@ -28,7 +31,7 @@ contract CallNewFinalizedState is Script {

LCMock lc = LCMock(lcContractAddress);

lc.newFinalizedState(states[0], proofs[0]);
lc.newFinalizedState(states[0], nextStakeTables[0], proofs[0]);

vm.stopBroadcast();
}
Expand Down
Loading
Loading