@@ -36,7 +36,7 @@ pub struct Joinstr<'a> {
3636 pub inner : Arc < Mutex < JoinstrInner < ' a > > > ,
3737}
3838
39- #[ derive( Debug , Default , Clone , Copy ) ]
39+ #[ derive( Debug , Default , Clone , Copy , Serialize , Deserialize ) ]
4040pub enum Role {
4141 #[ default]
4242 Unknown ,
@@ -95,9 +95,10 @@ pub struct JoinstrInner<'a> {
9595
9696#[ derive( Debug , Clone , Serialize , Deserialize ) ]
9797pub struct State {
98+ role : Role ,
9899 pool_secret_key : String , /* nostr::Keys*/
99100 relay : String ,
100- electrum : Option < String > ,
101+ electrum : Option < ( String , u16 ) > ,
101102 pool : Pool ,
102103 input : Option < Coin > ,
103104 output : Option < Address < NetworkUnchecked > > ,
@@ -827,6 +828,160 @@ impl Joinstr<'_> {
827828
828829 Ok ( ( ) )
829830 }
831+
832+ pub fn restart < S > ( state : State , name : & str , signer : S ) -> Result < Self , Error >
833+ where
834+ S : JoinstrSigner + Sync + Clone + Send + ' static ,
835+ {
836+ let State {
837+ role,
838+ pool_secret_key,
839+ relay,
840+ electrum,
841+ pool,
842+ input,
843+ output,
844+ network,
845+ peers,
846+ outputs,
847+ inputs,
848+ final_tx,
849+ } = state;
850+ let secret_key = nostr:: SecretKey :: from_hex ( pool_secret_key) . map_err ( |_| Error :: PoolKey ) ?;
851+ let keys = Keys :: new ( secret_key) ;
852+ let mut j = Joinstr :: new ( keys, relay, name) ?. network ( network) ;
853+ let mut inner = j. inner . lock ( ) . expect ( "poisoned" ) ;
854+ inner. role = role;
855+ inner. pool = Some ( pool) ;
856+ if let Some ( ( url, port) ) = electrum {
857+ inner. electrum_client = Some ( crate :: electrum:: Client :: new ( & url, port) ?)
858+ }
859+ inner. input = input;
860+ if let Some ( addr) = output {
861+ if addr. is_valid_for_network ( network) {
862+ inner. output = Some ( addr. assume_checked ( ) . clone ( ) ) ;
863+ } else {
864+ return Err ( Error :: WrongAddressNetwork ) ;
865+ }
866+ }
867+ inner. network = network;
868+ inner. peers = peers;
869+ let mut outs = vec ! [ ] ;
870+ for o in outputs {
871+ if !o. is_valid_for_network ( network) {
872+ return Err ( Error :: WrongAddressNetwork ) ;
873+ }
874+ outs. push ( o. assume_checked ( ) . clone ( ) ) ;
875+ }
876+ inner. outputs = outs;
877+ let mut inps = vec ! [ ] ;
878+ for i in inputs {
879+ inps. push ( serde_json:: from_value ( i) . map_err ( |_| Error :: InputParsing ) ?) ;
880+ }
881+ inner. inputs = inps;
882+
883+ // pool state is already finalized
884+ if let Some ( tx) = final_tx {
885+ inner. final_tx = Some ( tx) ;
886+ drop ( inner) ;
887+ return Ok ( j) ;
888+ }
889+
890+ let expected_peers = inner
891+ . pool
892+ . as_ref ( )
893+ . expect ( "always have a pool" )
894+ . payload
895+ . as_ref ( )
896+ . expect ( "have a payload" )
897+ . peers ;
898+
899+ let mut recv_peers = vec ! [ ] ;
900+ let mut recv_outputs = vec ! [ ] ;
901+ let mut recv_inputs = vec ! [ ] ;
902+
903+ // NOTE: here it's tricky to restart if we have a non finalized coinjoin:
904+ // - we cannot trust the timestamp of messages
905+ // - an external actor can have posted a join request w/ a fake timestamp
906+ // - a malicious peer can have posted a message w/ a fake timestamp
907+ // - a malicious peer can have posted a fake message
908+ // - a message can have been deleted
909+ //
910+ // FIXME: it's then easy to be DOS by a malicious actor, we should have a way to sign
911+ // with the pool key & post a backup state
912+
913+ // get all already received pool messages
914+ while let Ok ( Some ( msg) ) = inner. client . try_receive_pool_msg ( ) {
915+ match msg {
916+ PoolMessage :: Input ( input) => {
917+ recv_inputs. push ( input) ;
918+ }
919+ PoolMessage :: Output ( address) => {
920+ if address. is_valid_for_network ( network) {
921+ recv_outputs. push ( address. assume_checked ( ) ) ;
922+ }
923+ }
924+ PoolMessage :: Psbt ( psbt) => {
925+ if let Ok ( input) = psbt. try_into ( ) {
926+ recv_inputs. push ( input) ;
927+ }
928+ }
929+ PoolMessage :: Join ( Some ( public_key) ) => {
930+ recv_peers. push ( public_key) ;
931+ }
932+ _ => { }
933+ }
934+ }
935+
936+ let total_peers = inner. peers . len ( ) + recv_peers. len ( ) ;
937+ let total_outputs = inner. outputs . len ( ) + recv_outputs. len ( ) ;
938+ let total_inputs = inner. inputs . len ( ) + recv_inputs. len ( ) ;
939+
940+ if total_peers > expected_peers {
941+ return Err ( Error :: PoolCorrupted ) ;
942+ }
943+ if total_outputs > total_peers {
944+ return Err ( Error :: PoolCorrupted ) ;
945+ }
946+ if total_inputs > total_outputs {
947+ return Err ( Error :: PoolCorrupted ) ;
948+ }
949+ if total_inputs > total_peers {
950+ return Err ( Error :: PoolCorrupted ) ;
951+ }
952+
953+ inner. peers . append ( & mut recv_peers) ;
954+ inner. outputs . append ( & mut recv_outputs) ;
955+ inner. inputs . append ( & mut recv_inputs) ;
956+
957+ let joined = inner. peers . len ( ) >= expected_peers;
958+ let output_registered = inner. outputs . len ( ) >= expected_peers;
959+ let inputs_registered = inner. inputs . len ( ) >= expected_peers;
960+
961+ drop ( inner) ;
962+
963+ if !joined || !output_registered {
964+ j. register_outputs ( ) ?;
965+ }
966+
967+ if !inputs_registered {
968+ j. inner . lock ( ) . expect ( "poisoned" ) . generate_unsigned_tx ( ) ?;
969+
970+ rand_delay ( ) ;
971+
972+ let mut inner = j. inner . lock ( ) . expect ( "poisoned" ) ;
973+ if inner. input . is_some ( ) {
974+ inner. register_input ( & signer) ?;
975+ }
976+ drop ( inner) ;
977+
978+ j. register_inputs ( ) ?;
979+
980+ j. inner . lock ( ) . expect ( "poisoned" ) . broadcast_tx ( ) ?;
981+ }
982+
983+ Ok ( j)
984+ }
830985}
831986
832987impl < ' a > JoinstrInner < ' a > {
@@ -1278,13 +1433,14 @@ impl<'a> JoinstrInner<'a> {
12781433 } else {
12791434 return None ;
12801435 } ;
1281- let electrum = self . electrum_client . as_ref ( ) . map ( |c| c. url ( ) ) ;
1436+ let electrum = self . electrum_client . as_ref ( ) . map ( |c| ( c. url ( ) , c . port ( ) ) ) ;
12821437 let pool = if let Some ( pool) = & self . pool {
12831438 pool. clone ( )
12841439 } else {
12851440 return None ;
12861441 } ;
12871442 Some ( State {
1443+ role : self . role ,
12881444 pool_secret_key : keys. secret_key ( ) . to_secret_hex ( ) ,
12891445 relay,
12901446 electrum,
0 commit comments