1use alloc::collections::BTreeSet;
25use core::borrow::Borrow;
26use std::io;
27
28use amplify::MultiError;
29use commit_verify::{ReservedBytes, StrictHash};
30use indexmap::IndexSet;
31use sonic_callreq::MethodName;
32use sonicapi::{Api, NamedState, OpBuilder, SemanticError, Semantics, SigBlob};
33use strict_encoding::{
34 DecodeError, ReadRaw, SerializeError, StrictDecode, StrictEncode, StrictReader, StrictWriter, TypedRead, WriteRaw,
35};
36use ultrasonic::{AuthToken, CallError, CellAddr, ContractId, Identity, Issue, Operation, Opid, VerifiedOperation};
37
38use crate::deed::{CallParams, DeedBuilder};
39use crate::{Articles, EffectiveState, IssueError, ProcessedState, Stock, Transition};
40
41pub const DEEDS_VERSION: u16 = 0;
42
43#[derive(Clone, Debug)]
48pub struct Ledger<S: Stock>(S, ContractId);
49
50impl<S: Stock> Ledger<S> {
51 pub fn new(articles: Articles, conf: S::Conf) -> Result<Self, MultiError<IssueError, S::Error>> {
62 let contract_id = articles.contract_id();
63 let state = EffectiveState::with_articles(&articles)
64 .map_err(|e| IssueError::Genesis(articles.issue().meta.name.clone(), e))
65 .map_err(MultiError::A)?;
66 let mut stock = S::new(articles, state, conf).map_err(MultiError::B)?;
67 let genesis_opid = stock.articles().genesis_opid();
68 stock.mark_valid(genesis_opid);
69 stock.commit_transaction();
70 Ok(Self(stock, contract_id))
71 }
72
73 pub fn load(conf: S::Conf) -> Result<Self, S::Error> {
83 S::load(conf).map(|stock| {
84 let contract_id = stock.articles().contract_id();
85 Self(stock, contract_id)
86 })
87 }
88
89 pub fn config(&self) -> S::Conf { self.0.config() }
90
91 pub fn stock(&self) -> &S { &self.0 }
92
93 #[inline]
101 pub fn contract_id(&self) -> ContractId { self.1 }
102
103 #[inline]
109 pub fn articles(&self) -> &Articles { self.0.articles() }
110
111 #[inline]
117 pub fn state(&self) -> &EffectiveState { self.0.state() }
118
119 pub fn is_valid(&self, opid: Opid) -> bool { self.0.is_valid(opid) }
121
122 #[inline]
139 pub fn has_operation(&self, opid: Opid) -> bool { self.0.has_operation(opid) }
140
141 #[inline]
166 pub fn operation(&self, opid: Opid) -> Operation { self.0.operation(opid) }
167
168 #[inline]
191 pub fn operations(&self) -> impl Iterator<Item = (Opid, Operation)> + use<'_, S> { self.0.operations() }
192
193 #[inline]
215 pub fn trace(&self) -> impl Iterator<Item = (Opid, Transition)> + use<'_, S> { self.0.trace() }
216
217 #[inline]
218 pub fn read_by(&self, addr: CellAddr) -> impl Iterator<Item = Opid> + use<'_, S> { self.0.read_by(addr) }
219 #[inline]
220 pub fn spent_by(&self, addr: CellAddr) -> Option<Opid> { self.0.spent_by(addr) }
221
222 pub fn ancestors(&self, opids: impl IntoIterator<Item = Opid>) -> impl DoubleEndedIterator<Item = Opid> {
226 let mut chain = opids.into_iter().collect::<IndexSet<_>>();
227 let mut index = 0usize;
229 let genesis_opid = self.articles().genesis_opid();
230 while let Some(opid) = chain.get_index(index).copied() {
231 if opid != genesis_opid {
232 let op = self.0.operation(opid);
233 for inp in op.immutable_in {
234 let parent = inp.opid;
235 if !chain.contains(&parent) {
236 chain.insert(parent);
237 }
238 }
239 for inp in op.destructible_in {
240 let parent = inp.addr.opid;
241 if !chain.contains(&parent) {
242 chain.insert(parent);
243 }
244 }
245 }
246 index += 1;
247 }
248 chain.into_iter()
249 }
250
251 pub fn descendants(&self, opids: impl IntoIterator<Item = Opid>) -> impl DoubleEndedIterator<Item = Opid> {
255 let mut chain = opids.into_iter().collect::<IndexSet<_>>();
256 let mut index = 0usize;
258 while let Some(opid) = chain.get_index(index).copied() {
259 let op = self.0.operation(opid);
260 for no in 0..op.immutable_out.len_u16() {
261 let addr = CellAddr::new(opid, no);
262 for read in self.0.read_by(addr) {
263 if !chain.contains(&read) {
264 chain.insert(read);
265 }
266 }
267 }
268 for no in 0..op.destructible_out.len_u16() {
269 let addr = CellAddr::new(opid, no);
270 let Some(spent) = self.0.spent_by(addr) else { continue };
271 if !chain.contains(&spent) {
272 chain.insert(spent);
273 }
274 }
275 index += 1;
276 }
277 chain.into_iter()
278 }
279
280 pub fn export_all(&self, writer: StrictWriter<impl WriteRaw>) -> io::Result<()> {
282 self.export_internal(self.0.operation_count() as u32, writer, |_| true, |_, _, w| Ok(w))
283 }
284
285 pub fn export_all_aux<W: WriteRaw>(
288 &self,
289 writer: StrictWriter<W>,
290 aux: impl FnMut(Opid, &Operation, StrictWriter<W>) -> io::Result<StrictWriter<W>>,
291 ) -> io::Result<()> {
292 self.export_internal(self.0.operation_count() as u32, writer, |_| true, aux)
293 }
294
295 pub fn export(
297 &self,
298 terminals: impl IntoIterator<Item = impl Borrow<AuthToken>>,
299 writer: StrictWriter<impl WriteRaw>,
300 ) -> io::Result<()> {
301 self.export_aux(terminals, writer, |_, _, w| Ok(w))
302 }
303
304 pub fn export_aux<W: WriteRaw>(
307 &self,
308 terminals: impl IntoIterator<Item = impl Borrow<AuthToken>>,
309 writer: StrictWriter<W>,
310 aux: impl FnMut(Opid, &Operation, StrictWriter<W>) -> io::Result<StrictWriter<W>>,
311 ) -> io::Result<()> {
312 let mut queue = terminals
313 .into_iter()
314 .map(|terminal| self.0.state().addr(*terminal.borrow()).opid)
315 .collect::<BTreeSet<_>>();
316 let articles = self.articles();
317 let genesis_opid = articles.genesis_opid();
318 queue.remove(&genesis_opid);
319 let mut opids = queue.clone();
320 while let Some(opid) = queue.pop_first() {
321 let st = self.0.transition(opid);
322 for prev in st.destroyed.into_keys().map(|a| a.opid) {
323 if !opids.contains(&prev) && prev != genesis_opid {
324 opids.insert(prev);
325 queue.insert(prev);
326 }
327 }
328 }
329
330 let state = self.state();
332 let mut collect = |api: &Api, state: &ProcessedState| {
333 for (state_name, owned) in &api.global {
334 if owned.published {
335 let Some(cells) = state.global.get(state_name) else {
336 continue;
337 };
338 opids.extend(cells.keys().map(|addr| addr.opid));
339 }
340 }
341 };
342 collect(&articles.semantics().default, &state.main);
343 for (api_name, api) in &articles.semantics().custom {
344 let Some(state) = state.aux.get(api_name) else {
345 continue;
346 };
347 collect(api, state);
348 }
349 opids.remove(&genesis_opid);
350
351 self.export_internal(opids.len() as u32, writer, |opid| opids.remove(opid), aux)?;
352
353 debug_assert!(
354 opids.is_empty(),
355 "Missing operations: {}",
356 opids
357 .into_iter()
358 .map(|opid| opid.to_string())
359 .collect::<Vec<_>>()
360 .join("\n -")
361 );
362
363 Ok(())
364 }
365
366 pub fn export_internal<W: WriteRaw>(
372 &self,
373 count: u32,
374 mut writer: StrictWriter<W>,
375 mut should_include: impl FnMut(&Opid) -> bool,
376 mut aux: impl FnMut(Opid, &Operation, StrictWriter<W>) -> io::Result<StrictWriter<W>>,
377 ) -> io::Result<()> {
378 let articles = self.articles();
379 let genesis_opid = articles.genesis_opid();
380
381 writer = (DEEDS_VERSION as u8).strict_encode(writer)?;
383 let contract_id = self.contract_id();
385 writer = self.contract_id().strict_encode(writer)?;
386 writer = 0u8.strict_encode(writer)?;
388 writer = articles.strict_encode(writer)?;
390 writer = aux(genesis_opid, &articles.genesis().to_operation(contract_id), writer)?;
391 writer = count.strict_encode(writer)?;
393 for (opid, op) in self.0.operations() {
395 if !should_include(&opid) {
396 continue;
397 }
398 writer = op.strict_encode(writer)?;
399 writer = aux(opid, &op, writer)?;
400 }
401 Ok(())
402 }
403
404 pub fn upgrade_apis(&mut self, new_articles: Articles) -> Result<bool, MultiError<SemanticError, S::Error>> {
405 self.0
406 .update_articles(|articles| articles.upgrade_apis(new_articles))
407 }
408
409 pub fn accept<E>(
410 &mut self,
411 reader: &mut StrictReader<impl ReadRaw>,
412 sig_validator: impl FnOnce(StrictHash, &Identity, &SigBlob) -> Result<(), E>,
413 ) -> Result<(), MultiError<AcceptError, S::Error>> {
414 let count = (|| -> Result<u32, AcceptError> {
416 let _ = ReservedBytes::<1, { DEEDS_VERSION as u8 }>::strict_decode(reader)?;
418
419 let contract_id = ContractId::strict_decode(reader)?;
420
421 let ext_blocks = u8::strict_decode(reader)?;
423 for _ in 0..ext_blocks {
424 let len = u16::strict_decode(reader)?;
425 let r = unsafe { reader.raw_reader() };
426 let _ = r.read_raw::<{ u16::MAX as usize }>(len as usize)?;
427 }
428
429 let semantics = Semantics::strict_decode(reader)?;
431 let sig = Option::<SigBlob>::strict_decode(reader)?;
432 let issue = Issue::strict_decode(reader)?;
433 let articles = Articles::with(semantics, issue, sig, sig_validator)?;
434 if articles.contract_id() != contract_id {
435 return Err(AcceptError::Articles(SemanticError::ContractMismatch));
436 }
437
438 self.upgrade_apis(articles)
439 .map_err(|e| AcceptError::Persistence(e.to_string()))?;
440
441 let count = u32::strict_decode(reader)?;
442 Ok(count)
443 })()
444 .map_err(MultiError::A)?;
445
446 for _ in 0..=count {
448 let op = match Operation::strict_decode(reader) {
449 Ok(operation) => operation,
450 Err(DecodeError::Io(e)) if e.kind() == io::ErrorKind::UnexpectedEof => break,
451 Err(e) => return Err(MultiError::A(e.into())),
452 };
453 self.apply_verify(op, false)?;
454 }
455 self.commit_transaction();
459 Ok(())
460 }
461
462 pub fn rollback(&mut self, opids: impl IntoIterator<Item = Opid>) -> Result<(), S::Error> {
463 for opid in self.descendants(opids).rev() {
464 let mut transition = self.0.transition(opid);
465 let inputs = transition
467 .destroyed
468 .keys()
469 .copied()
470 .collect::<IndexSet<_>>();
471 for addr in inputs {
472 if !self.is_valid(addr.opid) {
473 let _ = transition.destroyed.remove(&addr);
475 }
476 }
477 self.0.update_state(|state, articles| {
478 state.rollback(transition, articles.semantics());
479 })?;
480 self.0.mark_invalid(opid);
481 }
482 self.commit_transaction();
483 Ok(())
484 }
485
486 pub fn forward(&mut self, opids: impl IntoIterator<Item = Opid>) -> Result<(), MultiError<AcceptError, S::Error>> {
487 for opid in self.descendants(opids) {
488 debug_assert!(!self.is_valid(opid));
489 if self
490 .ancestors([opid])
491 .filter(|id| *id != opid)
492 .all(|id| self.is_valid(id))
493 {
494 let op = self.0.operation(opid);
495 self.apply_verify(op, true)?;
496 debug_assert!(self.is_valid(opid));
497 }
498 }
499 self.commit_transaction();
500 Ok(())
501 }
502
503 pub fn start_deed(&mut self, method: impl Into<MethodName>) -> DeedBuilder<'_, S> {
504 let builder = OpBuilder::new(self.contract_id(), self.0.articles().call_id(method));
505 DeedBuilder { builder, ledger: self }
506 }
507
508 pub fn call(&mut self, params: CallParams) -> Result<Opid, MultiError<AcceptError, S::Error>> {
509 let mut builder = self.start_deed(params.core.method);
510
511 for NamedState { name, state } in params.core.global {
512 builder = builder.append(name, state.verified, state.unverified);
513 }
514 for NamedState { name, state } in params.core.owned {
515 builder = builder.assign(name, state.auth, state.data, state.lock);
516 }
517 for addr in params.reading {
518 builder = builder.reading(addr);
519 }
520 for (addr, satisfaction) in params.using {
521 if let Some(satisfaction) = satisfaction {
522 builder = builder.satisfying(addr, satisfaction.name, satisfaction.witness);
523 } else {
524 builder = builder.using(addr);
525 }
526 }
527
528 builder.commit()
529 }
530
531 pub fn apply_verify(
545 &mut self,
546 operation: Operation,
547 force: bool,
548 ) -> Result<bool, MultiError<AcceptError, S::Error>> {
549 if operation.contract_id != self.contract_id() {
550 return Err(MultiError::A(AcceptError::Articles(SemanticError::ContractMismatch)));
551 }
552
553 let opid = operation.opid();
554
555 let present = self.0.is_valid(opid);
556 let articles = self.0.articles();
557 if !present || force {
558 let verified = articles
559 .codex()
560 .verify(self.contract_id(), operation, &self.0.state().raw, articles)
561 .map_err(AcceptError::from)
562 .map_err(MultiError::A)?;
563 self.apply_internal(opid, verified, present && !force)
564 .map_err(MultiError::B)?;
565 }
566
567 Ok(present)
568 }
569
570 pub fn apply(&mut self, operation: VerifiedOperation) -> Result<Transition, S::Error> {
583 let opid = operation.opid();
584 let present = self.0.is_valid(opid);
585 self.apply_internal(opid, operation, present)
586 }
587
588 fn apply_internal(
589 &mut self,
590 opid: Opid,
591 operation: VerifiedOperation,
592 present: bool,
593 ) -> Result<Transition, S::Error> {
594 if !present {
595 self.0.add_operation(opid, operation.as_operation());
596 }
597
598 let op = operation.as_operation();
599 for read in &op.immutable_in {
600 self.0.add_reading(*read, opid);
601 }
602 for prevout in &op.destructible_in {
603 self.0.add_spending(prevout.addr, opid);
604 }
605
606 let transition = self
607 .0
608 .update_state(|state, articles| state.apply(operation, articles.semantics()))?;
609
610 self.0.add_transition(opid, &transition);
611 self.0.mark_valid(opid);
612 Ok(transition)
613 }
614
615 pub fn commit_transaction(&mut self) { self.0.commit_transaction(); }
616}
617
618#[derive(Debug, Display, Error, From)]
619#[display(inner)]
620pub enum AcceptError {
621 #[from]
622 Io(io::Error),
623
624 #[from]
625 Articles(SemanticError),
626
627 #[from]
628 Verify(CallError),
629
630 #[from]
631 Decode(DecodeError),
632
633 #[from]
634 Serialize(SerializeError),
635
636 Persistence(String),
637
638 #[cfg(feature = "binfile")]
639 #[display("Invalid file format")]
640 InvalidFileFormat,
641}
642
643#[cfg(feature = "binfile")]
644mod _fs {
645 use std::path::Path;
646
647 use binfile::BinFile;
648 use strict_encoding::{StreamReader, StreamWriter};
649
650 use super::*;
651
652 pub const DEEDS_MAGIC_NUMBER: u64 = u64::from_be_bytes(*b"DEEDLDGR");
653
654 impl<S: Stock> Ledger<S> {
655 pub fn export_all_to_file(&self, output: impl AsRef<Path>) -> io::Result<()> {
656 let file = BinFile::<DEEDS_MAGIC_NUMBER, DEEDS_VERSION>::create_new(output)?;
657 let writer = StrictWriter::with(StreamWriter::new::<{ usize::MAX }>(file));
658 self.export_all(writer)
659 }
660
661 pub fn export_to_file(
662 &self,
663 terminals: impl IntoIterator<Item = impl Borrow<AuthToken>>,
664 output: impl AsRef<Path>,
665 ) -> io::Result<()> {
666 let file = BinFile::<DEEDS_MAGIC_NUMBER, DEEDS_VERSION>::create_new(output)?;
667 let writer = StrictWriter::with(StreamWriter::new::<{ usize::MAX }>(file));
668 self.export(terminals, writer)
669 }
670
671 pub fn accept_from_file<E>(
672 &mut self,
673 input: impl AsRef<Path>,
674 sig_validator: impl FnOnce(StrictHash, &Identity, &SigBlob) -> Result<(), E>,
675 ) -> Result<(), MultiError<AcceptError, S::Error>> {
676 let file = BinFile::<DEEDS_MAGIC_NUMBER, DEEDS_VERSION>::open(input)
677 .map_err(|_| AcceptError::InvalidFileFormat)
678 .map_err(MultiError::from_a)?;
679 let mut reader = StrictReader::with(StreamReader::new::<{ usize::MAX }>(file));
680 self.accept(&mut reader, sig_validator)
681 }
682 }
683}
684#[cfg(feature = "binfile")]
685pub use _fs::*;