Skip to content

Commit

Permalink
Preferred sequencer in registry (#535)
Browse files Browse the repository at this point in the history
  • Loading branch information
citizen-stig authored and preston-evans98 committed Sep 14, 2023
1 parent 14cfe8e commit 850c8b1
Show file tree
Hide file tree
Showing 8 changed files with 185 additions and 3 deletions.
1 change: 1 addition & 0 deletions examples/demo-stf/src/genesis_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ pub fn create_demo_genesis_config<C: Context>(
amount: LOCKED_AMOUNT,
token_address,
},
preferred_sequencer: None,
};

let value_setter_config = ValueSetterConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::SequencerRegistry;
derive(schemars::JsonSchema)
)]
#[derive(borsh::BorshDeserialize, borsh::BorshSerialize, Debug, PartialEq, Clone)]
// TODO: Replace with DA address generic, when AddressTrait is split
pub enum CallMessage {
Register { da_address: Vec<u8> },
Exit { da_address: Vec<u8> },
Expand Down Expand Up @@ -47,11 +48,21 @@ impl<C: sov_modules_api::Context> SequencerRegistry<C> {
bail!("Unauthorized exit attempt");
}

self.allowed_sequencers.delete(&da_address, working_set);
self.delete(da_address, working_set);

self.bank
.transfer_from(locker, sequencer, coins, working_set)?;

Ok(CallResponse::default())
}

pub(crate) fn delete(&self, da_address: Vec<u8>, working_set: &mut WorkingSet<C::Storage>) {
self.allowed_sequencers.delete(&da_address, working_set);

if let Some(preferred_sequencer) = self.preferred_sequencer.get(working_set) {
if da_address == preferred_sequencer {
self.preferred_sequencer.delete(working_set);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ impl<C: sov_modules_api::Context> SequencerRegistry<C> {
&config.seq_rollup_address,
working_set,
)?;
if let Some(preferred_sequencer) = &config.preferred_sequencer {
if &config.seq_da_address != preferred_sequencer {
anyhow::bail!("Preferred sequencer is not in list of allowed sequencers");
}
self.preferred_sequencer
.set(preferred_sequencer, working_set);
}

Ok(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl<C: Context> ApplyBlobHooks for SequencerRegistry<C> {
match result {
SequencerOutcome::Completed => (),
SequencerOutcome::Slashed { sequencer } => {
self.allowed_sequencers.delete(&sequencer, working_set);
self.delete(sequencer, working_set);
}
}
Ok(())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ use sov_modules_macros::ModuleInfo;
use sov_state::{StateMap, StateValue, WorkingSet};

/// Initial configuration for the sov_sequencer_registry module.
/// TODO: Should we allow multiple sequencers in genesis?
pub struct SequencerConfig<C: sov_modules_api::Context> {
pub seq_rollup_address: C::Address,
// TODO: Replace with DA address generic, when AddressTrait is split
pub seq_da_address: Vec<u8>,
pub coins_to_lock: sov_bank::Coins<C>,
// TODO: Replace with DA address generic, when AddressTrait is split
pub preferred_sequencer: Option<Vec<u8>>,
}

#[cfg_attr(feature = "native", derive(sov_modules_macros::ModuleCallJsonSchema))]
Expand All @@ -35,6 +39,12 @@ pub struct SequencerRegistry<C: sov_modules_api::Context> {
#[state]
pub(crate) allowed_sequencers: StateMap<Vec<u8>, C::Address>,

/// Optional preferred sequencer
/// If set, batches from this sequencer will be processed first in block,
/// So this sequencer can guarantee soft confirmation time for transactions
#[state]
pub(crate) preferred_sequencer: StateValue<Vec<u8>>,

/// Coin's that will be slashed if the sequencer is malicious.
/// The coins will be transferred from `self.seq_rollup_address` to `self.address`
/// and locked forever, until sequencer decides to exit
Expand Down Expand Up @@ -112,4 +122,13 @@ impl<C: sov_modules_api::Context> SequencerRegistry<C> {

Ok(())
}

/// Return preferred sequencer if it was set
/// TODO: Replace with DA address generic, when AddressTrait is split
pub fn get_preferred_sequencer(
&self,
working_set: &mut WorkingSet<C::Storage>,
) -> Option<Vec<u8>> {
self.preferred_sequencer.get(working_set)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ pub fn create_sequencer_config(
amount: LOCKED_AMOUNT,
token_address,
},
preferred_sequencer: None,
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ mod helpers;
use helpers::*;
use sov_modules_api::Address;
use sov_rollup_interface::mocks::TestBlob;
use sov_sequencer_registry::SequencerOutcome;
use sov_sequencer_registry::{SequencerOutcome, SequencerRegistry};

#[test]
fn begin_blob_hook_known_sequencer() {
Expand Down Expand Up @@ -136,6 +136,69 @@ fn end_blob_hook_slash() {
assert!(resp.address.is_none());
}

#[test]
fn end_blob_hook_slash_preferred_sequencer() {
let bank = sov_bank::Bank::<C>::default();
let (bank_config, seq_rollup_address) = create_bank_config();

let token_address = sov_bank::get_genesis_token_address::<C>(
&bank_config.tokens[0].token_name,
bank_config.tokens[0].salt,
);

let registry = SequencerRegistry::<C>::default();
let mut sequencer_config = create_sequencer_config(seq_rollup_address, token_address);

sequencer_config.preferred_sequencer = Some(sequencer_config.seq_da_address.clone());

let mut test_sequencer = TestSequencer {
bank,
bank_config,
registry,
sequencer_config,
};

let tmpdir = tempfile::tempdir().unwrap();
let working_set = &mut WorkingSet::new(ProverStorage::with_path(tmpdir.path()).unwrap());
test_sequencer.genesis(working_set);
let balance_after_genesis = {
let resp = test_sequencer.query_balance_via_bank(working_set);
resp.amount.unwrap()
};
assert_eq!(INITIAL_BALANCE - LOCKED_AMOUNT, balance_after_genesis);

let mut test_blob = TestBlob::new(
Vec::new(),
Address::from(GENESIS_SEQUENCER_DA_ADDRESS),
[0_u8; 32],
);

test_sequencer
.registry
.begin_blob_hook(&mut test_blob, working_set)
.unwrap();

let result = SequencerOutcome::Slashed {
sequencer: GENESIS_SEQUENCER_DA_ADDRESS.to_vec(),
};
test_sequencer
.registry
.end_blob_hook(result, working_set)
.unwrap();

let resp = test_sequencer.query_balance_via_bank(working_set);
assert_eq!(balance_after_genesis, resp.amount.unwrap());
let resp = test_sequencer
.registry
.sequencer_address(GENESIS_SEQUENCER_DA_ADDRESS.to_vec(), working_set);
assert!(resp.address.is_none());

assert!(test_sequencer
.registry
.get_preferred_sequencer(working_set)
.is_none());
}

#[test]
fn end_blob_hook_slash_unknown_sequencer() {
let mut test_sequencer = create_test_sequencer();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use sov_state::{ProverStorage, WorkingSet};
mod helpers;

use helpers::*;
use sov_sequencer_registry::SequencerRegistry;

// Happy path for registration and exit
// This test checks:
Expand Down Expand Up @@ -214,3 +215,82 @@ fn test_allow_exit_last_sequencer() {
.call(exit_message, &sender_context, working_set)
.expect("Last sequencer exit has failed");
}

#[test]
fn test_preferred_sequencer_returned_and_removed() {
let bank = sov_bank::Bank::<C>::default();
let (bank_config, seq_rollup_address) = create_bank_config();

let token_address = sov_bank::get_genesis_token_address::<C>(
&bank_config.tokens[0].token_name,
bank_config.tokens[0].salt,
);

let registry = SequencerRegistry::<C>::default();
let mut sequencer_config = create_sequencer_config(seq_rollup_address, token_address);

sequencer_config.preferred_sequencer = Some(sequencer_config.seq_da_address.clone());

let mut test_sequencer = TestSequencer {
bank,
bank_config,
registry,
sequencer_config,
};

let tmpdir = tempfile::tempdir().unwrap();
let working_set = &mut WorkingSet::new(ProverStorage::with_path(tmpdir.path()).unwrap());
test_sequencer.genesis(working_set);

assert_eq!(
test_sequencer.sequencer_config.preferred_sequencer,
test_sequencer.registry.get_preferred_sequencer(working_set)
);

let sequencer_address = generate_address(GENESIS_SEQUENCER_KEY);
let sender_context = C::new(sequencer_address);
let exit_message = CallMessage::Exit {
da_address: GENESIS_SEQUENCER_DA_ADDRESS.to_vec(),
};
test_sequencer
.registry
.call(exit_message, &sender_context, working_set)
.expect("Last sequencer exit has failed");

// Preferred sequencer exited, so result is none
assert!(test_sequencer
.registry
.get_preferred_sequencer(working_set)
.is_none());
}

#[test]
fn test_preferred_sequencer_not_allowed_sequencers() {
let bank = sov_bank::Bank::<C>::default();
let (bank_config, seq_rollup_address) = create_bank_config();

let token_address = sov_bank::get_genesis_token_address::<C>(
&bank_config.tokens[0].token_name,
bank_config.tokens[0].salt,
);

let some_da_address = UNKNOWN_SEQUENCER_DA_ADDRESS.to_vec();

let registry = SequencerRegistry::<C>::default();
let mut sequencer_config = create_sequencer_config(seq_rollup_address, token_address);

sequencer_config.preferred_sequencer = Some(some_da_address);

let tmpdir = tempfile::tempdir().unwrap();
let working_set = &mut WorkingSet::new(ProverStorage::with_path(tmpdir.path()).unwrap());

bank.genesis(&bank_config, working_set).unwrap();
let genesis_result = registry.genesis(&sequencer_config, working_set);
assert!(genesis_result.is_err());

let message = genesis_result.err().unwrap().to_string();
assert_eq!(
"Preferred sequencer is not in list of allowed sequencers",
message
);
}

0 comments on commit 850c8b1

Please sign in to comment.