diff --git a/Cargo.lock b/Cargo.lock index 4a6e8761e..6d1f7decc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -954,6 +954,27 @@ dependencies = [ "serde", ] +[[package]] +name = "fil_actor_sealer" +version = "16.0.1" +dependencies = [ + "anyhow", + "cid", + "fil_actors_runtime", + "frc42_dispatch", + "fvm_ipld_bitfield", + "fvm_ipld_blockstore", + "fvm_ipld_encoding", + "fvm_shared", + "lazy_static", + "log", + "multihash-codetable", + "num", + "num-derive", + "num-traits", + "serde", +] + [[package]] name = "fil_actor_system" version = "16.0.1" @@ -1123,6 +1144,7 @@ dependencies = [ "fil_actor_placeholder", "fil_actor_power", "fil_actor_reward", + "fil_actor_sealer", "fil_actor_system", "fil_actor_verifreg", "fil_actors_runtime", diff --git a/Cargo.toml b/Cargo.toml index 949fc932d..403f505fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,6 +46,7 @@ fil_actor_power = { workspace = true, features = ["fil-actor"] } fil_actor_reward = { workspace = true, features = ["fil-actor"] } fil_actor_system = { workspace = true, features = ["fil-actor"] } fil_actor_verifreg = { workspace = true, features = ["fil-actor"] } +fil_actor_sealer = { workspace = true, features = ["fil-actor"] } [build-dependencies] fil_actor_bundler = "8.0.0" @@ -156,6 +157,7 @@ fil_actor_power = { path = "actors/power" } fil_actor_reward = { path = "actors/reward" } fil_actor_system = { path = "actors/system" } fil_actor_verifreg = { path = "actors/verifreg" } +fil_actor_sealer = { path = "actors/sealer" } fil_actors_evm_shared = { path = "actors/evm/shared" } fil_actors_runtime = { path = "runtime" } fil_builtin_actors_state = { path = "state" } diff --git a/README.md b/README.md index 19af554a6..21e7f8cff 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ type ManifestPayload struct { evm &ActorBytecode eam &ActorBytecode ethaccount &ActorBytecode + sealer &ActorBytecode } representation listpairs # RAW block diff --git a/actors/miner/src/ext.rs b/actors/miner/src/ext.rs index f91276d66..33a4b0673 100644 --- a/actors/miner/src/ext.rs +++ b/actors/miner/src/ext.rs @@ -199,3 +199,17 @@ pub mod verifreg { pub sector_claims: Vec, } } + +pub mod sealer { + use super::*; + use fvm_ipld_bitfield::BitField; + + pub const ACTIVATE_SECTORS_METHOD: u64 = + frc42_dispatch::method_hash!("ActivateSectors"); + + #[derive(Debug, Serialize_tuple, Deserialize_tuple)] + pub struct ActivateSectorParams { + pub sector_numbers: BitField, + pub verifier_signature: Vec, + } +} diff --git a/actors/miner/src/lib.rs b/actors/miner/src/lib.rs index 7a5587b7a..6da0db9b8 100644 --- a/actors/miner/src/lib.rs +++ b/actors/miner/src/lib.rs @@ -2033,11 +2033,12 @@ impl Actor { return Err(actor_error!(illegal_argument, "aggregate proof type must be SnarkPackV2")); } - let (validation_batch, proof_inputs, sector_numbers) = validate_ni_sectors( + let (validation_batch, proof_inputs, sector_numbers, sealer_numbers) = validate_ni_sectors( rt, ¶ms.sectors, params.seal_proof_type, params.require_activation_success, + params.sealer_id_actor, )?; if validation_batch.success_count == 0 { @@ -2056,6 +2057,17 @@ impl Actor { ¶ms.aggregate_proof, )?; + if let Some(sealer_id_actor) = params.sealer_id_actor { + let Some(sig) = params.sealer_id_verifier_signature else { + return Err(actor_error!(illegal_argument, "sealer id verifier signature is required when using Sealer ID")); + }; + let Some(sealer_numbers) = sealer_numbers else { + return Err(actor_error!(illegal_argument, "sealer numbers are required when using Sealer ID")); + }; + + validate_sealer_id_numbers(rt, sealer_id_actor, sig, &sealer_numbers)?; + } + // With no data, QA power = raw power let qa_sector_power = raw_power_for_sector(info.sector_size); @@ -4952,22 +4964,29 @@ fn validate_ni_sectors( sectors: &[SectorNIActivationInfo], seal_proof_type: RegisteredSealProof, all_or_nothing: bool, -) -> Result<(BatchReturn, Vec, BitField), ActorError> { + sealer_id_actor: Option, +) -> Result<(BatchReturn, Vec, BitField, Option), ActorError> { let receiver = rt.message().receiver(); - let miner_id = receiver.id().unwrap(); let curr_epoch = rt.curr_epoch(); let activation_epoch = curr_epoch; let challenge_earliest = curr_epoch - rt.policy().max_prove_commit_ni_randomness_lookback; let unsealed_cid = CompactCommD::empty().get_cid(seal_proof_type).unwrap(); let entropy = serialize(&receiver, "address for get verify info")?; + let expected_sealer_id = match sealer_id_actor { + Some(sealer_id) => sealer_id, + None => receiver.id().unwrap(), + }; + if sectors.is_empty() { - return Ok((BatchReturn::empty(), vec![], BitField::new())); + return Ok((BatchReturn::empty(), vec![], BitField::new(), None)); } let mut batch = BatchReturnGen::new(sectors.len()); let mut verify_infos = vec![]; let mut sector_numbers = BitField::new(); + let mut sealing_numbers = BitField::new(); + for (i, sector) in sectors.iter().enumerate() { let mut fail_validation = false; @@ -4986,6 +5005,28 @@ fn validate_ni_sectors( sector_numbers.set(sector.sector_number); + if sealer_id_actor.is_none() && sector.sector_number != sector.sealing_number { + warn!("sealing number must be same as sector number for all sectors"); + fail_validation = true; + } + + if sealer_id_actor.is_some() { + if sealing_numbers.get(sector.sealing_number) { + return Err(actor_error!( + illegal_argument, + "duplicate sealing number {}", + sector.sealing_number + )); + } + + if sector.sealing_number > MAX_SECTOR_NUMBER { + warn!("sealing number {} out of range 0..(2^63-1)", sector.sealing_number); + fail_validation = true; + } + + sealing_numbers.set(sector.sealing_number); + } + if let Err(err) = validate_expiration( rt.policy(), curr_epoch, @@ -4997,13 +5038,8 @@ fn validate_ni_sectors( fail_validation = true; } - if sector.sealer_id != miner_id { - warn!("sealer must be the same as the receiver actor for all sectors"); - fail_validation = true; - } - - if sector.sector_number != sector.sealing_number { - warn!("sealing number must be same as sector number for all sectors"); + if sector.sealer_id != expected_sealer_id { + warn!("sealer must be the same as the expected sealer ID for all sectors"); fail_validation = true; } @@ -5061,7 +5097,12 @@ fn validate_ni_sectors( } } - Ok((batch.generate(), verify_infos, sector_numbers)) + let out_sealing_numbers = match sealer_id_actor { + Some(_) => Some(sealing_numbers), + None => None, + }; + + Ok((batch.generate(), verify_infos, sector_numbers, out_sealing_numbers)) } // Validates a batch of sector sealing proofs. @@ -5126,7 +5167,7 @@ fn validate_seal_aggregate_proof( fn verify_aggregate_seal( rt: &impl Runtime, proof_inputs: &[SectorSealProofInput], - miner_actor_id: ActorID, + sealer_id: ActorID, seal_proof: RegisteredSealProof, aggregate_proof: RegisteredAggregateProof, proof_bytes: &RawBytes, @@ -5135,7 +5176,7 @@ fn verify_aggregate_seal( proof_inputs.iter().map(|pi| pi.to_aggregate_seal_verify_info()).collect(); rt.verify_aggregate_seals(&AggregateSealVerifyProofAndInfos { - miner: miner_actor_id, + miner: sealer_id, seal_proof, aggregate_proof, proof: proof_bytes.clone().into(), @@ -5144,6 +5185,37 @@ fn verify_aggregate_seal( .context_code(ExitCode::USR_ILLEGAL_ARGUMENT, "aggregate seal verify failed") } +fn validate_sealer_id_numbers( + rt: &impl Runtime, + sealer_id_actor: ActorID, + sealer_id_verifier_signature: Vec, + sealer_numbers: &BitField, +) -> Result<(), ActorError> { + let params = ext::sealer::ActivateSectorParams { + sector_numbers: sealer_numbers.clone(), + verifier_signature: sealer_id_verifier_signature, + }; + + assert_eq(rt.get_code_cid_for_type(Type::Sealer), rt.get_actor_code_cid(sealer_id_actor)). + + let result = rt.send_simple( + &Address::new_id(sealer_id_actor), + ext::sealer::ACTIVATE_SECTORS_METHOD, + IpldBlock::serialize_cbor(¶ms)?, + TokenAmount::zero(), + )?; + + if !result.exit_code.is_success() { + return Err(ActorError::checked( + result.exit_code, + "failed to verify Sealer ID sector numbers".to_string(), + None, + )); + } + + Ok(()) +} + fn verify_deals( rt: &impl Runtime, sectors: &[ext::market::SectorDeals], diff --git a/actors/miner/src/types.rs b/actors/miner/src/types.rs index e8e2d24f8..b7b8d4ba5 100644 --- a/actors/miner/src/types.rs +++ b/actors/miner/src/types.rs @@ -155,6 +155,9 @@ pub struct ProveCommitSectorsNIParams { pub aggregate_proof_type: RegisteredAggregateProof, // Proof type for aggregation pub proving_deadline: u64, // The Window PoST deadline index at which to schedule the new sectors pub require_activation_success: bool, // Whether to abort if any sector activation fails + + pub sealer_id_actor: Option, // Optional sealer ID actor from which the sector numbers are derived from + pub sealer_id_verifier_signature: Option>, // Verifier signature } #[derive(Clone, Debug, Eq, PartialEq, Serialize_tuple, Deserialize_tuple)] diff --git a/actors/miner/tests/types_test.rs b/actors/miner/tests/types_test.rs index 221a1a2bb..055a19104 100644 --- a/actors/miner/tests/types_test.rs +++ b/actors/miner/tests/types_test.rs @@ -30,9 +30,13 @@ mod serialization { aggregate_proof_type: RegisteredAggregateProof::SnarkPackV2, proving_deadline: 2, require_activation_success: false, + + sealer_id_actor: None, + sealer_id_verifier_signature: None, + final_sector_numbers: None, }, - // [[],byte[],8,1,2,false] - &hex!("868040080102f4")[..], + // [[],byte[],8,1,2,false,null,null,null] + &hex!("898040080102f4f6f6f6")[..], ), ( ProveCommitSectorsNIParams { @@ -49,9 +53,13 @@ mod serialization { aggregate_proof_type: RegisteredAggregateProof::SnarkPackV2, proving_deadline: 6, require_activation_success: true, + + sealer_id_actor: None, + sealer_id_verifier_signature: None, + final_sector_numbers: None, }, - // [[[1,2,bagboea4seaaqa,3,4,5]],byte[deadbeef],18,1,6,true] - &hex!("8681860102d82a49000182e2039220010003040544deadbeef120106f5"), + // [[[1,2,bagboea4seaaqa,3,4,5]],byte[deadbeef],18,1,6,true,null,null,null] + &hex!("8981860102d82a49000182e2039220010003040544deadbeef120106f5f6f6f6"), ), ( ProveCommitSectorsNIParams { @@ -78,10 +86,14 @@ mod serialization { aggregate_proof_type: RegisteredAggregateProof::SnarkPackV2, proving_deadline: 11, require_activation_success: false, + + sealer_id_actor: None, + sealer_id_verifier_signature: None, + final_sector_numbers: None, }, - // [[[1,2,bagboea4seaaqa,3,4,5],[6,7,bagboea4seaaqc,8,9,10]],byte[deadbeef],18,1,11,false] + // [[[1,2,bagboea4seaaqa,3,4,5],[6,7,bagboea4seaaqc,8,9,10]],byte[deadbeef],18,1,11,false,null,null,null] &hex!( - "8682860102d82a49000182e20392200100030405860607d82a49000182e2039220010108090a44deadbeef12010bf4" + "8982860102d82a49000182e20392200100030405860607d82a49000182e2039220010108090a44deadbeef12010bf4f6f6f6" ), ), ]; diff --git a/actors/miner/tests/util.rs b/actors/miner/tests/util.rs index 842dde8bb..90a9b9b5a 100644 --- a/actors/miner/tests/util.rs +++ b/actors/miner/tests/util.rs @@ -566,6 +566,10 @@ impl ActorHarness { aggregate_proof_type: RegisteredAggregateProof::SnarkPackV2, proving_deadline, require_activation_success: true, + + sealer_id_actor: None, + sealer_id_verifier_signature: None, + final_sector_numbers: None, } } diff --git a/actors/sealer/Cargo.toml b/actors/sealer/Cargo.toml new file mode 100644 index 000000000..8d708d793 --- /dev/null +++ b/actors/sealer/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "fil_actor_sealer" +description = "Builtin sealer actor for Filecoin" +version.workspace = true +license.workspace = true +edition.workspace = true +repository.workspace = true +authors = ["Curio Storage Inc. "] +keywords = ["filecoin", "web3", "wasm"] + +[lib] +## lib is necessary for integration tests +## cdylib is necessary for Wasm build +crate-type = ["cdylib", "lib"] + +[dependencies] +fil_actors_runtime = { workspace = true } +fvm_shared = { workspace = true } +num-traits = { workspace = true } +num-derive = { workspace = true } +log = { workspace = true } +lazy_static = { workspace = true } +serde = { workspace = true } +fvm_ipld_blockstore = { workspace = true } +fvm_ipld_encoding = { workspace = true } +cid = { workspace = true } +fvm_ipld_bitfield = { workspace = true } +multihash-codetable = { workspace = true } +anyhow = { workspace = true } +frc42_dispatch = { workspace = true } + + +[dev-dependencies] +fil_actors_runtime = { workspace = true, features = ["test_utils", "sector-default"] } +num = { workspace = true } + +[features] +fil-actor = ["fil_actors_runtime/fil-actor"] diff --git a/actors/sealer/src/ext.rs b/actors/sealer/src/ext.rs new file mode 100644 index 000000000..9f2a14e67 --- /dev/null +++ b/actors/sealer/src/ext.rs @@ -0,0 +1,17 @@ +use fvm_ipld_encoding::strict_bytes; +use fvm_ipld_encoding::tuple::*; + +pub mod account { + use super::*; + + pub const AUTHENTICATE_MESSAGE_METHOD: u64 = + frc42_dispatch::method_hash!("AuthenticateMessage"); + + #[derive(Serialize_tuple, Deserialize_tuple)] + pub struct AuthenticateMessageParams { + #[serde(with = "strict_bytes")] + pub signature: Vec, + #[serde(with = "strict_bytes")] + pub message: Vec, + } +} diff --git a/actors/sealer/src/lib.rs b/actors/sealer/src/lib.rs new file mode 100644 index 000000000..08a484fd8 --- /dev/null +++ b/actors/sealer/src/lib.rs @@ -0,0 +1,174 @@ +// Copyright 2025 Curio Storage Inc. +// SPDX-License-Identifier: Apache-2.0, MIT + +use fvm_ipld_encoding::ipld_block::IpldBlock; +use fvm_shared::{METHOD_CONSTRUCTOR, MethodNum}; + +use num_derive::FromPrimitive; + +use fil_actors_runtime::builtin::singletons::INIT_ACTOR_ADDR; +use fil_actors_runtime::runtime::{ActorCode, Runtime}; +use fil_actors_runtime::{FIRST_EXPORTED_METHOD_NUMBER, actor_dispatch}; +use fil_actors_runtime::{ActorError, ActorDowncast, actor_error}; +use fvm_ipld_bitfield::{BitField, Validate}; +use fvm_ipld_encoding::{CborStore}; +use multihash_codetable::Code; +use fvm_shared::error::ExitCode; +use fil_actors_runtime::runtime::builtins::Type; +use fvm_shared::sector::SectorNumber; +use fil_actors_runtime::runtime::policy_constants::MAX_SECTOR_NUMBER; +use fvm_shared::econ::TokenAmount; +use num_traits::Zero; +use fvm_shared::sys::SendFlags; + +use crate::types::{ConstructorParams, ActivateSectorParams, CompactSectorNumbersParams, ActivateSectorReturn}; +use crate::ext::account; +pub use self::state::{State, CollisionPolicy}; + +pub mod ext; +mod state; +pub mod types; +pub mod testing; + +#[cfg(feature = "fil-actor")] +fil_actors_runtime::wasm_trampoline!(Actor); + +/// Sealer actor methods available +#[derive(FromPrimitive)] +#[repr(u64)] +pub enum Method { + Constructor = METHOD_CONSTRUCTOR, + + ActivateSectors = frc42_dispatch::method_hash!("ActivateSectors"), + CompactSectorNumbers = frc42_dispatch::method_hash!("CompactSectorNumbers"), +} + +/// Sealer Actor +pub struct Actor; + +impl Actor { + /// Constructor for Sealer actor + pub fn constructor(rt: &impl Runtime, _params: ConstructorParams) -> Result<(), ActorError> { + rt.validate_immediate_caller_is(std::iter::once(&INIT_ACTOR_ADDR))?; + + let empty_bitfield = rt.store().put_cbor(&BitField::new(), Code::Blake2b256).map_err(|e| { + e.downcast_default(ExitCode::USR_ILLEGAL_STATE, "failed to construct empty bitfield") + })?; + + let validator = _params.validator; + let state = State { + validator, + allocated_sectors: empty_bitfield, + }; + rt.create(&state)?; + Ok(()) + } + + pub fn activate_sectors(rt: &impl Runtime, params: ActivateSectorParams) -> Result { + rt.validate_immediate_caller_type(std::iter::once(&Type::Miner))?; + + rt.transaction(|state: &mut State, rt| { + + // Call the validator with the sector numbers + let payload = types::VerifierSignaturePayload::new( + params.sector_numbers.clone(), + rt.message().receiver(), + rt.message().caller(), + ); + + let serialized_payload = payload.serialize() + .map_err(|e| actor_error!(illegal_state, "failed to serialize payload: {}", e))?; + + // We're not actually signing anything here, just passing the payload to the validator + // The validator will verify the sector numbers are valid + let auth_params = account::AuthenticateMessageParams { + signature: params.verifier_signature, + message: serialized_payload, + }; + + // Call the validator actor to authenticate the sector numbers + let send_flags = SendFlags::default(); + + rt.send( + &state.validator, + account::AUTHENTICATE_MESSAGE_METHOD, + IpldBlock::serialize_cbor(&auth_params)?, + TokenAmount::zero(), + None, + send_flags, + )?; + + // Allocate the sector numbers after validation + state.allocate_sector_numbers( + rt.store(), + ¶ms.sector_numbers, + CollisionPolicy::DenyCollisions, + ) + })?; + + Ok(ActivateSectorReturn { + sector_numbers: params.sector_numbers, + }) + } + + pub fn compact_sector_numbers(rt: &impl Runtime, params: CompactSectorNumbersParams) -> Result<(), ActorError> { + let mask_sector_numbers = params + .mask_sector_numbers + .validate() + .map_err(|e| actor_error!(illegal_argument, "invalid mask bitfield: {}", e))?; + + let last_sector_number = mask_sector_numbers + .last() + .ok_or_else(|| actor_error!(illegal_argument, "invalid mask bitfield"))? + as SectorNumber; + + if last_sector_number > MAX_SECTOR_NUMBER { + return Err(actor_error!( + illegal_argument, + "masked sector number {} exceeded max sector number", + last_sector_number + )); + } + + rt.transaction(|state: &mut State, rt| { + rt.validate_immediate_caller_is([state.validator].iter())?; + + state.allocate_sector_numbers( + rt.store(), + mask_sector_numbers, + CollisionPolicy::AllowCollisions, + ) + })?; + + Ok(()) + } + + /// Fallback method for unimplemented method numbers. + pub fn fallback( + rt: &impl Runtime, + method: MethodNum, + _: Option, + ) -> Result, ActorError> { + rt.validate_immediate_caller_accept_any()?; + if method >= FIRST_EXPORTED_METHOD_NUMBER { + Ok(None) + } else { + Err(actor_error!(unhandled_message; "invalid method: {}", method)) + } + } +} + +impl ActorCode for Actor { + type Methods = Method; + + fn name() -> &'static str { + "Sealer" + } + + actor_dispatch! { + Constructor => constructor, + ActivateSectors => activate_sectors, + CompactSectorNumbers => compact_sector_numbers, + _ => fallback, + } +} diff --git a/actors/sealer/src/state.rs b/actors/sealer/src/state.rs new file mode 100644 index 000000000..72c9cf116 --- /dev/null +++ b/actors/sealer/src/state.rs @@ -0,0 +1,73 @@ +// Copyright 2024 Curio Storage Inc. +// SPDX-License-Identifier: Apache-2.0, MIT + +use fil_actors_runtime::{ActorError, ActorDowncast, actor_error}; +use fvm_ipld_encoding::tuple::*; +use fvm_shared::address::Address; +use cid::Cid; +use fvm_ipld_bitfield::BitField; +use fvm_ipld_blockstore::Blockstore; +use fvm_shared::error::ExitCode; +use multihash_codetable::Code; +use fvm_ipld_encoding::CborStore; + +/// State for the Sealer actor +#[derive(Serialize_tuple, Deserialize_tuple, Debug, Clone)] +pub struct State { + pub allocated_sectors: Cid, // BitField + + // Address of a validator actor which learns about all consumed sector numbers with the ability to veto a transaction + pub validator: Address, +} + +#[derive(PartialEq, Eq)] +pub enum CollisionPolicy { + AllowCollisions, + DenyCollisions, +} + +impl State { + /// Marks a set of sector numbers as having been allocated. + /// If policy is `DenyCollisions`, fails if the set intersects with the sector numbers already allocated. + pub fn allocate_sector_numbers( + &mut self, + store: &BS, + sector_numbers: &BitField, + policy: CollisionPolicy, + ) -> Result<(), ActorError> { + let prior_allocation = store + .get_cbor(&self.allocated_sectors) + .map_err(|e| { + e.downcast_default( + ExitCode::USR_ILLEGAL_STATE, + "failed to load allocated sectors bitfield", + ) + })? + .ok_or_else(|| actor_error!(illegal_state, "allocated sectors bitfield not found"))?; + + if policy != CollisionPolicy::AllowCollisions { + // NOTE: A fancy merge algorithm could extract this intersection while merging, below, saving + // one iteration of the runs + let collisions = &prior_allocation & sector_numbers; + if !collisions.is_empty() { + return Err(actor_error!( + illegal_argument, + "sector numbers {:?} already allocated", + collisions + )); + } + } + let new_allocation = &prior_allocation | sector_numbers; + self.allocated_sectors = + store.put_cbor(&new_allocation, Code::Blake2b256).map_err(|e| { + e.downcast_default( + ExitCode::USR_ILLEGAL_ARGUMENT, + format!( + "failed to store allocated sectors bitfield after adding {:?}", + sector_numbers, + ), + ) + })?; + Ok(()) + } +} \ No newline at end of file diff --git a/actors/sealer/src/testing.rs b/actors/sealer/src/testing.rs new file mode 100644 index 000000000..96db2ba6c --- /dev/null +++ b/actors/sealer/src/testing.rs @@ -0,0 +1,21 @@ +use fil_actors_runtime::MessageAccumulator; +use fvm_shared::address::Address; + +use crate::State; + +pub struct StateSummary { + pub validator: Address, +} + +pub fn check_state_invariants( + state: &State, + _id_address: &Address, +) -> (StateSummary, MessageAccumulator) { + let acc = MessageAccumulator::default(); + + // TODO: Add invariants + // check allocated sectors bitfield + // check verifier actor exists + + (StateSummary { validator: state.validator }, acc) +} \ No newline at end of file diff --git a/actors/sealer/src/types.rs b/actors/sealer/src/types.rs new file mode 100644 index 000000000..9c937a2bc --- /dev/null +++ b/actors/sealer/src/types.rs @@ -0,0 +1,53 @@ +use fvm_ipld_encoding::tuple::*; +use fvm_shared::address::Address; +use fvm_ipld_bitfield::BitField; +use fvm_ipld_encoding::strict_bytes; +use fvm_ipld_encoding::Error; + +#[derive(Debug, Serialize_tuple, Deserialize_tuple)] +pub struct ConstructorParams { + pub validator: Address, +} + +#[derive(Debug, Serialize_tuple, Deserialize_tuple)] +pub struct ActivateSectorParams { + pub sector_numbers: BitField, + pub verifier_signature: Vec, +} + +#[derive(Debug, Serialize_tuple, Deserialize_tuple)] +pub struct ActivateSectorReturn { + pub sector_numbers: BitField, +} + +#[derive(Debug, Serialize_tuple, Deserialize_tuple)] +pub struct CompactSectorNumbersParams { + pub mask_sector_numbers: BitField, +} + +pub const SIGNATURE_DOMAIN_SEPARATION_SEALER_NUMBERS: &[u8] = b"fil_sealernumbers:"; + +#[derive(Debug, Serialize_tuple, Deserialize_tuple)] +pub struct VerifierSignaturePayload { + #[serde(with = "strict_bytes")] + pub domain: Vec, + pub sector_numbers: BitField, + pub sealer_id_actor: Address, + pub miner_actor: Address, +} + +impl VerifierSignaturePayload { + pub fn new(sector_numbers: BitField, sealer_id_actor: Address, miner_actor: Address) -> Self { + Self { + domain: SIGNATURE_DOMAIN_SEPARATION_SEALER_NUMBERS.to_vec(), + sector_numbers, + sealer_id_actor, + miner_actor, + } + } + + pub fn serialize(&self) -> Result, Error> { + fvm_ipld_encoding::to_vec(self) + } +} + diff --git a/build.rs b/build.rs index 7dadee3c6..e4109c72e 100644 --- a/build.rs +++ b/build.rs @@ -30,6 +30,7 @@ const ACTORS: &[(&Package, &ID)] = &[ ("evm", "evm"), ("eam", "eam"), ("ethaccount", "ethaccount"), + ("sealer", "sealer"), ]; const NETWORK_ENV: &str = "BUILD_FIL_NETWORK"; diff --git a/runtime/src/test_utils.rs b/runtime/src/test_utils.rs index ca6cf046f..35c5f6a93 100644 --- a/runtime/src/test_utils.rs +++ b/runtime/src/test_utils.rs @@ -67,6 +67,7 @@ lazy_static::lazy_static! { pub static ref EVM_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/evm"); pub static ref EAM_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/eam"); pub static ref ETHACCOUNT_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/ethaccount"); + pub static ref SEALER_ACTOR_CODE_ID: Cid = make_identity_cid(b"fil/test/sealer"); pub static ref ACTOR_TYPES: BTreeMap = { let mut map = BTreeMap::new(); @@ -86,6 +87,7 @@ lazy_static::lazy_static! { map.insert(*EVM_ACTOR_CODE_ID, Type::EVM); map.insert(*EAM_ACTOR_CODE_ID, Type::EAM); map.insert(*ETHACCOUNT_ACTOR_CODE_ID, Type::EthAccount); + map.insert(*SEALER_ACTOR_CODE_ID, Type::Sealer); map }; pub static ref ACTOR_CODES: BTreeMap = [ @@ -105,6 +107,7 @@ lazy_static::lazy_static! { (Type::EVM, *EVM_ACTOR_CODE_ID), (Type::EAM, *EAM_ACTOR_CODE_ID), (Type::EthAccount, *ETHACCOUNT_ACTOR_CODE_ID), + (Type::Sealer, *SEALER_ACTOR_CODE_ID), ] .into_iter() .collect(); @@ -117,6 +120,7 @@ lazy_static::lazy_static! { map.insert(*PLACEHOLDER_ACTOR_CODE_ID, ()); map.insert(*EVM_ACTOR_CODE_ID, ()); map.insert(*ETHACCOUNT_ACTOR_CODE_ID, ()); + map.insert(*SEALER_ACTOR_CODE_ID, ()); map }; } diff --git a/vm_api/src/builtin.rs b/vm_api/src/builtin.rs index 0279349e6..b7371c52c 100644 --- a/vm_api/src/builtin.rs +++ b/vm_api/src/builtin.rs @@ -24,6 +24,7 @@ pub enum Type { EVM = 14, EAM = 15, EthAccount = 16, + Sealer = 17, } impl Type { @@ -45,6 +46,7 @@ impl Type { Type::EVM => "evm", Type::EAM => "eam", Type::EthAccount => "ethaccount", + Type::Sealer => "sealer", } } }