@@ -4558,3 +4558,94 @@ def write_all(fd, bytestr):
45584558
45594559 with pytest .raises (RpcError , match = "maybe encrypted" ):
45604560 l1 .rpc .exposesecret (passphrase = password )
4561+
4562+
4563+ def test_peer_storage (node_factory , bitcoind ):
4564+ """Test that we offer and re-xmit peer storage for our peers if they have a channel or are explicitly enabled"""
4565+ l1 , l2 , l3 = node_factory .get_nodes (3 ,
4566+ opts = {'experimental-peer-storage' : None ,
4567+ 'may_reconnect' : True ,
4568+ 'dev-no-reconnect' : None })
4569+
4570+ # Connect them, no peer storage yet anyway.
4571+ l1 .rpc .connect (l2 .info ['id' ], 'localhost' , l2 .port )
4572+
4573+ assert not l1 .daemon .is_in_log (r'WIRE_PEER_STORAGE_RETRIEVAL' )
4574+ assert not l2 .daemon .is_in_log (r'WIRE_PEER_STORAGE_RETRIEVAL' )
4575+
4576+ # And they won't store.
4577+ assert l1 .rpc .listdatastore (['chanbackup' , 'peers' , l2 .info ['id' ]]) == {'datastore' : []}
4578+ assert l2 .rpc .listdatastore (['chanbackup' , 'peers' , l1 .info ['id' ]]) == {'datastore' : []}
4579+
4580+ # Reconnect, still no xmit.
4581+ l1 .rpc .disconnect (l2 .info ['id' ])
4582+ l1 .rpc .connect (l2 .info ['id' ], 'localhost' , l2 .port )
4583+
4584+ # Give it time to make sure we *would* catch it if it happened.
4585+ time .sleep (10 )
4586+ assert not l1 .daemon .is_in_log (r'WIRE_PEER_STORAGE_RETRIEVAL' )
4587+ assert not l2 .daemon .is_in_log (r'WIRE_PEER_STORAGE_RETRIEVAL' )
4588+
4589+ # But we can force it manually by creating an empty one.
4590+ l1 .rpc .datastore (['chanbackup' , 'peers' , l2 .info ['id' ]], hex = '' )
4591+ assert l1 .rpc .listdatastore (['chanbackup' , 'peers' , l2 .info ['id' ]])['datastore' ][0 ]['hex' ] == ''
4592+ l1 .restart ()
4593+ l1 .rpc .connect (l2 .info ['id' ], 'localhost' , l2 .port )
4594+
4595+ # Note: l1 may or may not send peer_storage_retrieval: if it
4596+ # receives storage fast enough, it will, otherwise not.
4597+ wait_for (lambda : l1 .rpc .listdatastore (['chanbackup' , 'peers' , l2 .info ['id' ]])['datastore' ][0 ]['hex' ] != '' )
4598+
4599+ # Next reconnect, l1 will send it back.
4600+ l1 .rpc .disconnect (l2 .info ['id' ])
4601+ l1 .rpc .connect (l2 .info ['id' ], 'localhost' , l2 .port )
4602+
4603+ l1 .daemon .wait_for_log (r'peer_out WIRE_PEER_STORAGE_RETRIEVAL' )
4604+ l2 .daemon .wait_for_log (r'peer_in WIRE_PEER_STORAGE_RETRIEVAL' )
4605+ assert not l1 .daemon .is_in_log (r'peer_in WIRE_PEER_STORAGE_RETRIEVAL' )
4606+ assert not l2 .daemon .is_in_log (r'peer_out WIRE_PEER_STORAGE_RETRIEVAL' )
4607+
4608+ # It must be valid.
4609+ l2 .daemon .wait_for_log (r'Received peer_storage from peer' )
4610+ assert not l2 .daemon .is_in_log (r'PeerStorageFailed' )
4611+
4612+ # l2 will only store if we have an ESTABLISHED channel.
4613+ l1 .openchannel (l2 , confirm = False , wait_for_announce = False )
4614+ assert l2 .rpc .listdatastore (['chanbackup' , 'peers' , l1 .info ['id' ]]) == {'datastore' : []}
4615+
4616+ # But it will create an entry once it hits CHANNELD_NORMAL.
4617+ bitcoind .generate_block (1 , wait_for_mempool = 1 )
4618+ wait_for (lambda : l2 .rpc .listdatastore (['chanbackup' , 'peers' , l1 .info ['id' ]]) != {'datastore' : []})
4619+
4620+ # Now it will store l1's backup when channels change.
4621+ l1 .openchannel (l3 , wait_for_announce = False )
4622+ wait_for (lambda : l2 .rpc .listdatastore (['chanbackup' , 'peers' , l1 .info ['id' ]])['datastore' ][0 ]['hex' ] != '' )
4623+
4624+ # Now every time we reconnect, both sides restore.
4625+ l1 .restart ()
4626+ l1 .rpc .connect (l2 .info ['id' ], 'localhost' , l2 .port )
4627+
4628+ l1 .daemon .wait_for_logs ([r'peer_out WIRE_PEER_STORAGE_RETRIEVAL' ,
4629+ r'peer_in WIRE_PEER_STORAGE_RETRIEVAL' ])
4630+ l2 .daemon .wait_for_logs ([r'peer_out WIRE_PEER_STORAGE_RETRIEVAL' ,
4631+ r'peer_in WIRE_PEER_STORAGE_RETRIEVAL' ])
4632+
4633+ # Even if we close channel, and it's long forgotten, we will store for
4634+ # them as a courtesy.
4635+ l1 .rpc .close (l2 .info ['id' ])
4636+ assert len (l1 .rpc .listpeerchannels ()['channels' ]) == 2
4637+ bitcoind .generate_block (100 , wait_for_mempool = 1 )
4638+ wait_for (lambda : len (l1 .rpc .listpeerchannels ()['channels' ]) == 1 )
4639+
4640+ # Now try restarting l2 and connecting that way instead.
4641+ l2 .restart ()
4642+ l2 .rpc .connect (l1 .info ['id' ], 'localhost' , l1 .port )
4643+
4644+ l1 .daemon .wait_for_logs ([r'peer_out WIRE_PEER_STORAGE_RETRIEVAL' ,
4645+ r'peer_in WIRE_PEER_STORAGE_RETRIEVAL' ])
4646+ l2 .daemon .wait_for_logs ([r'peer_out WIRE_PEER_STORAGE_RETRIEVAL' ,
4647+ r'peer_in WIRE_PEER_STORAGE_RETRIEVAL' ])
4648+
4649+ # This should never happen
4650+ assert not l1 .daemon .is_in_log (r'PeerStorageFailed' )
4651+ assert not l2 .daemon .is_in_log (r'PeerStorageFailed' )
0 commit comments