Skip to content
Merged
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
6 changes: 6 additions & 0 deletions dsm_client/deterministic_state_machine/dsm/src/ccb/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,12 @@ pub mod class {
/// root proves the creation, and a second creation of the same policy
/// commit on that lineage cannot insert it again.
pub const ECONOMIC_TOKEN_CREATION_STATE: u16 = 0x0060;
/// `0x0061` — a trader's balance of one token before a trade (SoFi
/// Amendment S12). `T°` states that balance only by the hash of its leaf,
/// so the exercise carries the value as this object, named in
/// `𝒞_E^pre` and accepted only because it hashes to the leaf the core
/// states.
pub const SOFI_TRADER_PRE_BALANCE: u16 = 0x0061;
}

/// Discriminants **allocated but not encodable** — see [`class`] for the ones
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ pub const TAG_DSM_SOFI_POLICY_FULFILLMENT_OBJECT: TaggedHashDomain<'static> =
/// The signed fulfillment envelope, indexed under `FulfillmentId`.
pub const TAG_DSM_SOFI_FULFILLMENT_OBJECT: TaggedHashDomain<'static> =
crate::tagged_domain!(b"DSM/sofi/fulfillment-object/v1");
/// Immutable-store namespace of a `TraderPreBalance` (SoFi Amendment S12):
/// `addr = immutable_addr(tag, CCB bytes)`, the address `𝒞_E^pre` names it
/// by. It has no locator: the closure names the address.
pub const TAG_DSM_SOFI_TRADER_PRE_BALANCE_OBJECT: TaggedHashDomain<'static> =
crate::tagged_domain!(b"DSM/sofi/trader-pre-balance-object/v1");

// ── The DLV tree's leaves, and the route digest (P15-4, P15-8) ─────────────

Expand Down Expand Up @@ -205,6 +210,7 @@ pub(crate) const SOFI_TAGS: &[TaggedHashDomain<'static>] = &[
TAG_DSM_SOFI_PREIMAGE_OBJECT,
TAG_DSM_SOFI_POLICY_FULFILLMENT_OBJECT,
TAG_DSM_SOFI_FULFILLMENT_OBJECT,
TAG_DSM_SOFI_TRADER_PRE_BALANCE_OBJECT,
TAG_DSM_SOFI_VAULT_CREATION_KEY,
TAG_DSM_SOFI_VAULT_STATE_KEY,
TAG_DSM_SOFI_VAULT_LEAF_STATE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ mod tests {
// it outside the registry).
// -1 with the client-edited storage node list: its placement seed was the
// network hash domain's only input.
const EXPECTED_TAG_COUNT: usize = 350;
// +1 with the TraderPreBalance object namespace (SoFi Amendment S12).
const EXPECTED_TAG_COUNT: usize = 351;

/// Scan the crate source for every declared domain-tag constant.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ use crate::economic::authority_evidence::{verify_authority_evidence, AuthorityEv
use crate::economic::claim::AdmissionSubstrate;
use crate::economic::decode::decode_admission_manifest;
use crate::economic::lineage::{
activate, advance_validated, AcceptedSubstrate, EconomicActivationSnapshot,
activate, advance_validated, AcceptedClaim, AcceptedSubstrate, EconomicActivationSnapshot,
EconomicValidationError, ValidatedEconomicRoot,
};
use crate::economic::provenance::{
Expand Down Expand Up @@ -389,6 +389,7 @@ fn walk_positions(
[u8; 32],
[u8; 32],
[u8; 32],
AcceptedClaim,
)> = None;
for position in first_position..=target_position {
if state.steps_remaining == 0 {
Expand Down Expand Up @@ -560,13 +561,21 @@ fn walk_positions(
verified.c_dsm_plus,
verified.embedded_parent,
manifest_addr,
advanced.claim,
));
}

let (witness, proven_ak, verified_operation, c_dsm_plus, embedded_parent, manifest_addr) = last
.ok_or_else(|| {
incomplete("walk had no steps — the start memo already covers the target")
})?;
let (
witness,
proven_ak,
verified_operation,
c_dsm_plus,
embedded_parent,
manifest_addr,
accepted_claim,
) = last.ok_or_else(|| {
incomplete("walk had no steps — the start memo already covers the target")
})?;
// SINGLE-ROOT BY CONSTRUCTION, not by label. Every position this walk
// traversed decoded as a single-root claim: a conditional `C_q` is refused
// above with `Unresolved`, resolved or not, because resolution is
Expand All @@ -582,6 +591,7 @@ fn walk_positions(
embedded_parent,
verified_operation,
manifest_addr,
accepted_claim,
))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
use crate::common::domain_tags::{TAG_DSM_ECON_SOURCE_VALIDATED_PEER_DEBIT};
use crate::crypto::blake3::dsm_domain_hasher;
use crate::economic::credit::CreditSource;
use crate::economic::lineage::ValidatedEconomicRoot;
use crate::economic::lineage::{AcceptedClaim, ValidatedEconomicRoot};
use crate::economic::mutation::EconomicLeafMutation;
use crate::economic::state::EconomicLeafState;
use crate::economic::witness::EconomicTransitionWitness;
Expand Down Expand Up @@ -142,6 +142,9 @@ pub struct PeerTransitionFacts {
/// as the walk verified it. A release that names another manifest for
/// the same root is refused against this, not against a re-read cell.
admission_manifest_addr: [u8; 32],
/// The claim `advance_validated` accepted at this position: what a
/// setup of this peer names by `claim_ref` (SoFi Amendment S9).
accepted_claim: AcceptedClaim,
}

impl ValidatedPeerTransition {
Expand All @@ -164,6 +167,7 @@ impl ValidatedPeerTransition {
embedded_parent: [u8; 32],
verified_operation: crate::types::operations::Operation,
admission_manifest_addr: [u8; 32],
accepted_claim: AcceptedClaim,
) -> Self {
Self::SingleRoot(PeerTransitionFacts {
peer_genesis,
Expand All @@ -175,6 +179,7 @@ impl ValidatedPeerTransition {
embedded_parent,
verified_operation,
admission_manifest_addr,
accepted_claim,
})
}

Expand Down Expand Up @@ -205,6 +210,12 @@ impl ValidatedPeerTransition {
self.facts().admission_manifest_addr
}

/// The claim the walk accepted at this position, as `advance_validated`
/// produced it.
pub fn accepted_claim(&self) -> &AcceptedClaim {
&self.facts().accepted_claim
}

pub fn witness(&self) -> &EconomicTransitionWitness {
&self.facts().witness
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ use super::wire::{
ParentClaimRef, next_position, DlvPolicyFulfillmentBody, SettlementPreimage,
SofiResolutionClaim, SofiWireError, TraderFulfillmentBody, TraderPrecommitBody, ValidationRef,
};
use crate::ccb::decode::policy_object_address;

type D32 = [u8; 32];

Expand Down Expand Up @@ -501,7 +500,7 @@ impl Items {
fn closure_object_verifies(reference: &ValidationRef, bytes: &[u8]) -> Option<bool> {
Some(match reference {
ValidationRef::ContentAddr { object_class, addr } => {
policy_object_address(*object_class, bytes)? == *addr
derive::closure_content_address(*object_class, bytes)? == *addr
}
ValidationRef::SingleRootClaim { claim_ref } => derive::claim_ref(bytes) == *claim_ref,
ValidationRef::ConditionalClaim {
Expand Down Expand Up @@ -971,10 +970,10 @@ mod tests {
let mut refs: Vec<ValidationRef> = closure.keys().copied().collect();
refs.sort_by_key(ValidationRef::encode);
let f = swap_fixture_with(2, PreEClosureIndex::new(refs).unwrap());
// The fixture adds the single-root parent P names; the closure holds
// its exact envelope under that reference.
// The fixture adds the single-root parent P names and the trader's
// balances before the trade; the closure holds their exact bytes.
let mut closure = closure;
closure.insert(f.precommit.parent_reference(), f.parent_claim.clone());
closure.extend(f.closure_objects());
let precommit = f.precommit;
let precommit_sig = sign_p(&precommit);
let e = derive::recompute_e(&f.preimage).unwrap();
Expand Down Expand Up @@ -1251,7 +1250,7 @@ mod tests {
refs.sort_by_key(ValidationRef::encode);
let f = swap_fixture_with(1, PreEClosureIndex::new(refs).unwrap());
let mut closure = extra;
closure.insert(f.precommit.parent_reference(), f.parent_claim.clone());
closure.extend(f.closure_objects());
(f, closure)
}

Expand Down
27 changes: 24 additions & 3 deletions dsm_client/deterministic_state_machine/dsm/src/sofi/derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@ use crate::common::domain_tags::{
TAG_DSM_SOFI_STORAGE_SEED_V4, TAG_DSM_SOFI_SUCC_ATTEMPT, TAG_DSM_SOFI_SUCC_CELL_V2,
TAG_DSM_SOFI_TRADER_CORE_V3, TAG_DSM_SOFI_TRADER_PRECOMMIT_ID, TAG_DSM_SOFI_ROUTE_DIGEST,
TAG_DSM_SOFI_TRADER_PRECOMMIT_SIGN, TAG_DSM_SOFI_VAULT_GENESIS_LOCATOR, TAG_DSM_SOFI_VAULT_ID,
TAG_DSM_SOFI_VAULT_LEAF_STATE, TAG_DSM_SOFI_VAULT_STATE_KEY,
TAG_DSM_SOFI_TRADER_PRE_BALANCE_OBJECT, TAG_DSM_SOFI_VAULT_LEAF_STATE,
TAG_DSM_SOFI_VAULT_STATE_KEY,
};
use crate::common::domain_tags::TAG_DSM_ECONOMIC_LEAF_STATE;
use crate::storage_object::immutable_addr;
use crate::crypto::blake3::dsm_domain_hasher;
use crate::crypto::domain::TaggedHashDomain;

use super::wire::{
DlvPolicyFulfillmentBody, RouteDigestPreimage, RouteLegSet, SettlementBody, SettlementPreimage,
RouteLegEntry, SofiResolutionClaim, SofiSetupBody, SofiWireError, TraderFulfillmentBody,
TraderPrecommitBody, TraderRelationshipLeaf, VaultRelationshipLeaf, VaultStateLeaf,
CANONICAL_MAX_LEGS, ROUTE_MIN_LEGS,
TraderPreBalance, TraderPrecommitBody, TraderRelationshipLeaf, VaultRelationshipLeaf,
VaultStateLeaf, CANONICAL_MAX_LEGS, ROUTE_MIN_LEGS,
};

type D32 = [u8; 32];
Expand Down Expand Up @@ -84,6 +86,25 @@ pub fn trader_relationship_leaf_value(leaf: &TraderRelationshipLeaf) -> D32 {
*hasher.finalize().as_bytes()
}

/// The address `𝒞_E^pre` names a `TraderPreBalance` by (SoFi Amendment
/// S12): `immutable_addr(DSM/sofi/trader-pre-balance-object/v1, CCB bytes)`.
pub fn trader_pre_balance_addr(balance: &TraderPreBalance) -> D32 {
immutable_addr(TAG_DSM_SOFI_TRADER_PRE_BALANCE_OBJECT, &balance.encode())
}

/// The address a closure `ContentAddr` naming `object_class` gives `bytes`,
/// under that class's own namespace, so the address binds the kind. `None`
/// for a class with no content-addressing rule, which no bytes satisfy.
pub fn closure_content_address(object_class: u16, bytes: &[u8]) -> Option<D32> {
match object_class {
crate::ccb::class::SOFI_TRADER_PRE_BALANCE => Some(immutable_addr(
TAG_DSM_SOFI_TRADER_PRE_BALANCE_OBJECT,
bytes,
)),
other => crate::ccb::decode::policy_object_address(other, bytes),
}
}

/// `X_route = H(route-digest/v1 ‖ CCB(RouteDigestPreimage))`.
pub fn route_digest(preimage: &RouteDigestPreimage) -> Result<D32, SofiWireError> {
Ok(h(TAG_DSM_SOFI_ROUTE_DIGEST, &[&preimage.encode()?]))
Expand Down
25 changes: 22 additions & 3 deletions dsm_client/deterministic_state_machine/dsm/src/sofi/exercise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ use super::publication::{
};
use super::wire::{
DlvPolicyFulfillmentBody, SettlementPreimage, SofiExercise, TraderFulfillmentBody,
TraderPrecommitBody,
TraderPrecommitBody, ValidationRef,
};

type D32 = [u8; 32];
Expand All @@ -47,6 +47,24 @@ pub struct RecognizedExercise {
pub external_commitment: D32,
}

impl RecognizedExercise {
/// The closure objects this exercise carries, each under the reference
/// in `𝒞_E^pre` it answers: the exercise carries them in reference order
/// (Section 17.5), and recognition requires one per reference. Nothing is
/// trusted because it is here: Core re-derives every reference from the
/// bytes it is handed.
pub fn closure_objects(&self) -> std::collections::BTreeMap<ValidationRef, Vec<u8>> {
self.preimage
.settlement()
.closure()
.refs()
.iter()
.copied()
.zip(self.closure.iter().cloned())
.collect()
}
}

/// Rebuild the objects an exercise carries and check their binding to one
/// another. `None` for bytes that are not one exercise of one operation.
pub fn recognize_exercise(bytes: &[u8]) -> Option<RecognizedExercise> {
Expand Down Expand Up @@ -375,8 +393,9 @@ pub(crate) mod fixtures {
.iter()
.map(DlvPolicyFulfillmentBody::encode)
.collect(),
// 𝒞_E^pre references the parent P names; the exercise carries it.
vec![f.parent_claim.clone()],
// 𝒞_E^pre references the parent P names and the trader's
// balances before the trade; the exercise carries them.
f.closure_in_order(),
)
.unwrap();
Built {
Expand Down
3 changes: 0 additions & 3 deletions dsm_client/deterministic_state_machine/dsm/src/sofi/facts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,6 @@ pub enum NotEstablished {
ConformanceEvidence(Vec<ConformanceMissing>),
/// Route evidence not in hand after the retry budget.
RouteEvidence(Vec<Missing>),
/// Route evidence with no source this verifier can acquire it from: the
/// leaf pre values of another trader's route.
RouteEvidenceHasNoSource(Vec<Missing>),
/// `P` names a conditional parent this verifier has not resolved.
ParentUnresolved { fulfillment_id: D32 },
/// A leg's attempt cell is not decided by its reads yet.
Expand Down
14 changes: 10 additions & 4 deletions dsm_client/deterministic_state_machine/dsm/src/sofi/publication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ use crate::common::domain_tags::{
TAG_DSM_SOFI_FULFILLMENT_OBJECT, TAG_DSM_SOFI_POLICY_FULFILLMENT_OBJECT,
TAG_DSM_SOFI_PRECOMMIT_OBJECT, TAG_DSM_SOFI_PREIMAGE_LOCATOR, TAG_DSM_SOFI_PREIMAGE_OBJECT,
TAG_DSM_SOFI_REL_INDEX, TAG_DSM_SOFI_SETUP_OBJECT, TAG_DSM_SOFI_SETUP_REF,
TAG_DSM_SOFI_TRADER_PRECOMMIT_ID, TAG_DSM_SOFI_VAULT_GENESIS_LOCATOR,
TAG_DSM_SOFI_VAULT_GENESIS_OBJECT,
TAG_DSM_SOFI_TRADER_PRECOMMIT_ID, TAG_DSM_SOFI_TRADER_PRE_BALANCE_OBJECT,
TAG_DSM_SOFI_VAULT_GENESIS_LOCATOR, TAG_DSM_SOFI_VAULT_GENESIS_OBJECT,
};
use crate::crypto::domain::TaggedHashDomain;
use crate::storage_object::immutable_addr;
Expand All @@ -41,7 +41,7 @@ use super::derive;
use super::signature::{verify_fulfillment, verify_precommit, verify_setup};
use super::wire::{
DlvPolicyFulfillmentBody, SettlementPreimage, SignedSofiObject, SofiSetupBody, SofiWireError,
TraderFulfillmentBody, TraderPrecommitBody, VaultGenesisPreimage,
TraderFulfillmentBody, TraderPreBalance, TraderPrecommitBody, VaultGenesisPreimage,
};

type D32 = [u8; 32];
Expand Down Expand Up @@ -112,6 +112,10 @@ pub enum Publication<'a> {
class: VaultPolicyClass,
bytes: &'a [u8],
},
/// A trader's balance of one token before a trade (Amendment S12), bare,
/// found by the address `𝒞_E^pre` names. The exercise carries it too;
/// publishing it lets a reader that holds only the closure fetch it.
TraderPreBalance(&'a TraderPreBalance),
}

fn envelope(
Expand Down Expand Up @@ -150,6 +154,7 @@ impl Publication<'_> {
),
Self::VaultGenesis(preimage) => preimage.encode(),
Self::VaultPolicy { bytes, .. } => Ok(bytes.to_vec()),
Self::TraderPreBalance(balance) => Ok(balance.encode()),
}
}

Expand All @@ -167,6 +172,7 @@ impl Publication<'_> {
VaultPolicyClass::Fee => TAG_DSM_FEE_POLICY_OBJECT,
VaultPolicyClass::Release => TAG_DSM_RELEASE_POLICY_OBJECT,
},
Self::TraderPreBalance(_) => TAG_DSM_SOFI_TRADER_PRE_BALANCE_OBJECT,
}
}

Expand Down Expand Up @@ -216,7 +222,7 @@ impl Publication<'_> {
index_namespace: TAG_DSM_SOFI_VAULT_GENESIS_LOCATOR.source_bytes(),
locator: derive::vault_genesis_locator(&preimage.vault_id()),
}],
Self::VaultPolicy { .. } => Vec::new(),
Self::VaultPolicy { .. } | Self::TraderPreBalance(_) => Vec::new(),
})
}
}
Expand Down
Loading
Loading