diff --git a/src/contract/mod.rs b/src/contract/mod.rs index a09e70c0..687b2279 100644 --- a/src/contract/mod.rs +++ b/src/contract/mod.rs @@ -61,6 +61,8 @@ pub use seal::{ }; pub use state::{ConfidentialState, ExposedState, StateCommitment, StateData, StateType}; +use crate::Layer1; + #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display)] #[display(lowercase)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] @@ -79,6 +81,14 @@ pub enum AltLayer1 { // Prime = 0x11, } +impl AltLayer1 { + pub fn layer1(&self) -> Layer1 { + match self { + AltLayer1::Liquid => Layer1::Liquid, + } + } +} + #[derive(Wrapper, Clone, PartialEq, Eq, Hash, Debug, From)] #[wrapper(Deref)] #[derive(StrictType, StrictDumb, StrictEncode, StrictDecode)] diff --git a/src/validation/status.rs b/src/validation/status.rs index e743a241..d63a2b2f 100644 --- a/src/validation/status.rs +++ b/src/validation/status.rs @@ -25,13 +25,15 @@ use core::ops::AddAssign; use std::fmt::{self, Display, Formatter}; use bp::dbc::anchor; +use bp::seals::txout::blind::ChainBlindSeal; use bp::{seals, Txid}; use strict_types::SemId; use crate::contract::Opout; use crate::schema::{self, SchemaId}; use crate::{ - AssignmentType, BundleId, Layer1, OccurrencesMismatch, OpFullType, OpId, SecretSeal, StateType, + AssignmentType, BundleId, Layer1, OccurrencesMismatch, OpFullType, OpId, SealDefinition, + SecretSeal, StateType, }; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Display)] @@ -318,6 +320,9 @@ pub enum Failure { SealNoWitnessTx(Txid), /// witness layer 1 {anchor} doesn't match seal definition {seal}. SealWitnessLayer1Mismatch { seal: Layer1, anchor: Layer1 }, + /// seal {1:?} is defined on {0} which is not in the set of layers allowed + /// by the contract genesis. + SealInvalidLayer1(Layer1, SealDefinition), /// transition {0} doesn't close seal with the witness transaction {1}. /// Details: {2} SealInvalid(OpId, Txid, seals::txout::VerifyError), diff --git a/src/validation/validator.rs b/src/validation/validator.rs index d033e828..440114a3 100644 --- a/src/validation/validator.rs +++ b/src/validation/validator.rs @@ -31,9 +31,9 @@ use super::status::{Failure, Warning}; use super::{ConsignmentApi, Status, Validity, VirtualMachine}; use crate::vm::AluRuntime; use crate::{ - Anchor, AnchoredBundle, BundleId, ContractId, GraphSeal, Layer1, OpId, OpRef, Operation, Opout, - Schema, SchemaId, SchemaRoot, Script, SealDefinition, SubSchema, Transition, TransitionBundle, - TypedAssigns, + AltLayer1, Anchor, AnchoredBundle, BundleId, ContractId, GraphSeal, Layer1, OpId, OpRef, + Operation, Opout, Schema, SchemaId, SchemaRoot, Script, SealDefinition, SubSchema, Transition, + TransitionBundle, TypedAssigns, }; #[derive(Clone, Debug, Display, Error, From)] @@ -57,6 +57,7 @@ pub struct Validator<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> { schema_id: SchemaId, genesis_id: OpId, contract_id: ContractId, + layers1: BTreeSet, anchor_index: BTreeMap, end_transitions: Vec<(&'consignment Transition, BundleId)>, validation_index: BTreeSet, @@ -75,9 +76,10 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> let mut status = Status::default(); // Frequently used computation-heavy data - let genesis_id = consignment.genesis().id(); - let contract_id = consignment.genesis().contract_id(); - let schema_id = consignment.genesis().schema_id; + let genesis = consignment.genesis(); + let genesis_id = genesis.id(); + let contract_id = genesis.contract_id(); + let schema_id = genesis.schema_id; // Create indexes let mut anchor_index = BTreeMap::::new(); @@ -132,6 +134,9 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> // Index used to avoid repeated validations of the same anchor+transition pairs let anchor_validation_index = BTreeSet::::new(); + let mut layers1 = bset! { Layer1::Bitcoin }; + layers1.extend(genesis.alt_layer1.iter().map(AltLayer1::layer1)); + let vm = match &consignment.schema().script { Script::AluVM(lib) => { Box::new(AluRuntime::new(lib)) as Box @@ -144,6 +149,7 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> schema_id, genesis_id, contract_id, + layers1, anchor_index, end_transitions, validation_index, @@ -424,56 +430,56 @@ impl<'consignment, 'resolver, C: ConsignmentApi, R: ResolveTx> continue; }; - let seal = match (seal, self.anchor_index.get(&op)) { + let Some(anchor) = self.anchor_index.get(&op) else { + panic!("anchor for the operation {op} was not indexed by the validator"); + }; + if seal.layer1() != anchor.layer1() { + self.status.add_failure(Failure::SealWitnessLayer1Mismatch { + seal: seal.layer1(), + anchor: anchor.layer1(), + }); + continue; + } + if !self.layers1.contains(&seal.layer1()) { + self.status + .add_failure(Failure::SealInvalidLayer1(seal.layer1(), seal)); + continue; + } + + let seal = match (seal, anchor) { ( SealDefinition::Bitcoin( seal @ GraphSeal { txid: TxPtr::WitnessTx, .. }, - ), - Some(Anchor::Bitcoin(anchor)), - ) | - ( + ) | SealDefinition::Liquid( seal @ GraphSeal { txid: TxPtr::WitnessTx, .. }, ), - Some(Anchor::Liquid(anchor)), + Anchor::Bitcoin(anchor) | Anchor::Liquid(anchor), ) => { let prev_witness_txid = anchor.txid; seal.resolve(prev_witness_txid) } - (SealDefinition::Bitcoin(_) | SealDefinition::Liquid(_), None) => { - panic!("anchor for the operation {op} was not indexed by the validator"); - } ( SealDefinition::Bitcoin( seal @ GraphSeal { txid: TxPtr::Txid(txid), .. }, - ), - Some(Anchor::Bitcoin(_)), - ) | - ( + ) | SealDefinition::Liquid( seal @ GraphSeal { txid: TxPtr::Txid(txid), .. }, ), - Some(Anchor::Liquid(_)), + Anchor::Bitcoin(_) | Anchor::Liquid(_), ) => seal.resolve(txid), - (SealDefinition::Bitcoin(_) | SealDefinition::Liquid(_), Some(anchor)) => { - self.status.add_failure(Failure::SealWitnessLayer1Mismatch { - seal: seal.layer1(), - anchor: anchor.layer1(), - }); - continue; - } }; seals.push(seal); }