@@ -60,7 +60,9 @@ pub mod messages;
6060/// The structure that communicates with the Bitcoin P2P network and collects data.
6161pub mod node;
6262
63+ use bitcoin:: OutPoint ;
6364use chain:: Filter ;
65+ use package_type:: { OneParentOneChild , Single } ;
6466
6567use std:: net:: { IpAddr , Ipv4Addr , SocketAddr } ;
6668use std:: path:: PathBuf ;
@@ -431,6 +433,190 @@ impl Dialog {
431433 }
432434}
433435
436+ /// Types of packages valid for package relay.
437+ pub mod package_type {
438+ /// Single transaction.
439+ pub struct Single ;
440+ /// Exactly two transactions, one spending from the other.
441+ pub struct OneParentOneChild ;
442+ }
443+
444+ mod sealed {
445+ pub trait Sealed { }
446+ }
447+
448+ impl sealed:: Sealed for package_type:: Single { }
449+ impl sealed:: Sealed for package_type:: OneParentOneChild { }
450+
451+ /// Types of packages valid for package relay.
452+ pub trait PackageType : sealed:: Sealed { }
453+
454+ impl PackageType for package_type:: Single { }
455+ impl PackageType for package_type:: OneParentOneChild { }
456+
457+ /// Build transaction packages to submit to the network.
458+ #[ derive( Debug ) ]
459+ pub struct PackageBuilder < P : PackageType > {
460+ package : Vec < Transaction > ,
461+ _marker : core:: marker:: PhantomData < P > ,
462+ }
463+
464+ impl < P : PackageType > PackageBuilder < P > {
465+ /// Create a package from a single transaction.
466+ pub fn new_single ( parent : Transaction ) -> PackageBuilder < Single > {
467+ PackageBuilder {
468+ package : vec ! [ parent] ,
469+ _marker : core:: marker:: PhantomData ,
470+ }
471+ }
472+
473+ /// Create a package from a single parent and child relationship.
474+ ///
475+ /// # Errors
476+ ///
477+ /// If the package is invalid for mempool submission.
478+ pub fn new_one_parent_one_child (
479+ parent : Transaction ,
480+ child : Transaction ,
481+ ) -> Result < PackageBuilder < OneParentOneChild > , error:: InvalidPackageError > {
482+ let outpoints = {
483+ let txid = parent. compute_txid ( ) ;
484+ let mut outpoints = Vec :: with_capacity ( parent. output . len ( ) ) ;
485+ for vout in 0 ..parent. output . len ( ) {
486+ outpoints. push ( OutPoint {
487+ txid,
488+ vout : vout as u32 ,
489+ } ) ;
490+ }
491+ outpoints
492+ } ;
493+ if !child
494+ . input
495+ . iter ( )
496+ . any ( |input| outpoints. contains ( & input. previous_output ) )
497+ {
498+ return Err ( error:: InvalidPackageError :: UnrelatedTransactions ) ;
499+ }
500+ Ok ( PackageBuilder {
501+ package : vec ! [ parent, child] ,
502+ _marker : core:: marker:: PhantomData ,
503+ } )
504+ }
505+
506+ /// Create a package from an unchecked list of transactions.
507+ ///
508+ /// # Errors
509+ ///
510+ /// If the package is invalid for mempool submission.
511+ pub fn new_unchecked ( package : Vec < Transaction > ) -> Result < Package , error:: InvalidPackageError > {
512+ if package. len ( ) > 2 {
513+ return Err ( error:: InvalidPackageError :: InvalidPackageLength (
514+ package. len ( ) ,
515+ ) ) ;
516+ }
517+ if package. is_empty ( ) {
518+ return Err ( error:: InvalidPackageError :: InvalidPackageLength ( 0 ) ) ;
519+ }
520+ let mut tx_iter = package. into_iter ( ) ;
521+ let parent = tx_iter. next ( ) . expect ( "at least one transaction exists." ) ;
522+ let outpoints = {
523+ let txid = parent. compute_txid ( ) ;
524+ let mut outpoints = Vec :: with_capacity ( parent. output . len ( ) ) ;
525+ for vout in 0 ..parent. output . len ( ) {
526+ outpoints. push ( OutPoint {
527+ txid,
528+ vout : vout as u32 ,
529+ } ) ;
530+ }
531+ outpoints
532+ } ;
533+ let child = tx_iter. next ( ) ;
534+ // Replace with `let` chain in Rust version 2024
535+ if let Some ( child) = child {
536+ if !child
537+ . input
538+ . iter ( )
539+ . any ( |input| outpoints. contains ( & input. previous_output ) )
540+ {
541+ return Err ( error:: InvalidPackageError :: UnrelatedTransactions ) ;
542+ }
543+ }
544+ Ok ( Package {
545+ parent,
546+ child : tx_iter. next ( ) ,
547+ } )
548+ }
549+ }
550+
551+ impl PackageBuilder < package_type:: Single > {
552+ /// Build a package from a single transaction.
553+ pub fn build ( self ) -> Package {
554+ Package {
555+ parent : self
556+ . package
557+ . into_iter ( )
558+ . next ( )
559+ . expect ( "at least one transaction exists." ) ,
560+ child : None ,
561+ }
562+ }
563+
564+ /// Add a dependent transaction to the package.
565+ pub fn add_child ( mut self , child : Transaction ) -> PackageBuilder < OneParentOneChild > {
566+ self . package . push ( child) ;
567+ PackageBuilder {
568+ package : self . package ,
569+ _marker : core:: marker:: PhantomData ,
570+ }
571+ }
572+ }
573+
574+ impl PackageBuilder < package_type:: OneParentOneChild > {
575+ /// Build a package from a one-parent-one-child topology.
576+ pub fn build ( self ) -> Package {
577+ let mut tx_iter = self . package . into_iter ( ) ;
578+ let parent = tx_iter. next ( ) . expect ( "at least two transactions exist" ) ;
579+ let child = tx_iter. next ( ) . expect ( "at least two transactions exit" ) ;
580+ Package {
581+ parent,
582+ child : Some ( child) ,
583+ }
584+ }
585+ }
586+
587+ /// A package is a set of dependent transactions to submit to the mempool.
588+ #[ derive( Debug , Clone ) ]
589+ pub struct Package {
590+ parent : Transaction ,
591+ child : Option < Transaction > ,
592+ }
593+
594+ impl Package {
595+ fn advertise_package ( & self ) -> Wtxid {
596+ match & self . child {
597+ Some ( child) => child. compute_wtxid ( ) ,
598+ None => self . parent . compute_wtxid ( ) ,
599+ }
600+ }
601+
602+ fn parent ( & self ) -> Transaction {
603+ self . parent . clone ( )
604+ }
605+
606+ fn child ( & self ) -> Option < Transaction > {
607+ self . child . clone ( )
608+ }
609+ }
610+
611+ impl From < Transaction > for Package {
612+ fn from ( value : Transaction ) -> Self {
613+ Package {
614+ parent : value,
615+ child : None ,
616+ }
617+ }
618+ }
619+
434620macro_rules! impl_sourceless_error {
435621 ( $e: ident) => {
436622 impl std:: error:: Error for $e {
0 commit comments