autopilot: add OnChannelPendingOpen state update
This commit is contained in:
parent
956acdfa14
commit
aa7c2e6d47
@ -186,13 +186,18 @@ type balanceUpdate struct {
|
|||||||
balanceDelta btcutil.Amount
|
balanceDelta btcutil.Amount
|
||||||
}
|
}
|
||||||
|
|
||||||
// chanOpenUpdate is a type of external state update the indicates a new
|
// chanOpenUpdate is a type of external state update that indicates a new
|
||||||
// channel has been opened, either by the Agent itself (within the main
|
// channel has been opened, either by the Agent itself (within the main
|
||||||
// controller loop), or by an external user to the system.
|
// controller loop), or by an external user to the system.
|
||||||
type chanOpenUpdate struct {
|
type chanOpenUpdate struct {
|
||||||
newChan Channel
|
newChan Channel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// chanPendingOpenUpdate is a type of external state update that indicates a new
|
||||||
|
// channel has been opened, either by the agent itself or an external subsystem,
|
||||||
|
// but is still pending.
|
||||||
|
type chanPendingOpenUpdate struct{}
|
||||||
|
|
||||||
// chanOpenFailureUpdate is a type of external state update that indicates
|
// chanOpenFailureUpdate is a type of external state update that indicates
|
||||||
// a previous channel open failed, and that it might be possible to try again.
|
// a previous channel open failed, and that it might be possible to try again.
|
||||||
type chanOpenFailureUpdate struct{}
|
type chanOpenFailureUpdate struct{}
|
||||||
@ -231,6 +236,18 @@ func (a *Agent) OnChannelOpen(c Channel) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnChannelPendingOpen is a callback that should be executed each time a new
|
||||||
|
// channel is opened, either by the agent or an external subsystems, but is
|
||||||
|
// still pending.
|
||||||
|
func (a *Agent) OnChannelPendingOpen() {
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case a.stateUpdates <- &chanPendingOpenUpdate{}:
|
||||||
|
case <-a.quit:
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
// OnChannelOpenFailure is a callback that should be executed when the
|
// OnChannelOpenFailure is a callback that should be executed when the
|
||||||
// autopilot has attempted to open a channel, but failed. In this case we can
|
// autopilot has attempted to open a channel, but failed. In this case we can
|
||||||
// retry channel creation with a different node.
|
// retry channel creation with a different node.
|
||||||
@ -382,6 +399,12 @@ func (a *Agent) controller(startingBalance btcutil.Amount) {
|
|||||||
|
|
||||||
updateBalance()
|
updateBalance()
|
||||||
|
|
||||||
|
// A new channel has been opened by the agent or an
|
||||||
|
// external subsystem, but is still pending
|
||||||
|
// confirmation.
|
||||||
|
case *chanPendingOpenUpdate:
|
||||||
|
updateBalance()
|
||||||
|
|
||||||
// A channel has been closed, this may free up an
|
// A channel has been closed, this may free up an
|
||||||
// available slot, triggering a new channel update.
|
// available slot, triggering a new channel update.
|
||||||
case *chanCloseUpdate:
|
case *chanCloseUpdate:
|
||||||
@ -598,6 +621,12 @@ func (a *Agent) controller(startingBalance btcutil.Amount) {
|
|||||||
err)
|
err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Since the channel open was successful
|
||||||
|
// and is currently pending, we'll
|
||||||
|
// trigger the autopilot agent to query
|
||||||
|
// for more peers.
|
||||||
|
a.OnChannelPendingOpen()
|
||||||
}(chanCandidate)
|
}(chanCandidate)
|
||||||
}
|
}
|
||||||
pendingMtx.Unlock()
|
pendingMtx.Unlock()
|
||||||
|
@ -1089,3 +1089,97 @@ func TestAgentPendingChannelState(t *testing.T) {
|
|||||||
t.Fatalf("select wasn't queried in time")
|
t.Fatalf("select wasn't queried in time")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestAgentPendingOpenChannel ensures that the agent queries its heuristic once
|
||||||
|
// it detects a channel is pending open. This allows the agent to use its own
|
||||||
|
// change outputs that have yet to confirm for funding transactions.
|
||||||
|
func TestAgentPendingOpenChannel(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
// First, we'll create all the dependencies that we'll need in order to
|
||||||
|
// create the autopilot agent.
|
||||||
|
self, err := randKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to generate key: %v", err)
|
||||||
|
}
|
||||||
|
heuristic := &mockHeuristic{
|
||||||
|
moreChansResps: make(chan moreChansResp),
|
||||||
|
directiveResps: make(chan []AttachmentDirective),
|
||||||
|
}
|
||||||
|
chanController := &mockChanController{
|
||||||
|
openChanSignals: make(chan openChanIntent),
|
||||||
|
}
|
||||||
|
memGraph, _, _ := newMemChanGraph()
|
||||||
|
|
||||||
|
// The wallet will start with 6 BTC available.
|
||||||
|
const walletBalance = btcutil.SatoshiPerBitcoin * 6
|
||||||
|
|
||||||
|
// With the dependencies we created, we can now create the initial
|
||||||
|
// agent itself.
|
||||||
|
cfg := Config{
|
||||||
|
Self: self,
|
||||||
|
Heuristic: heuristic,
|
||||||
|
ChanController: chanController,
|
||||||
|
WalletBalance: func() (btcutil.Amount, error) {
|
||||||
|
return walletBalance, nil
|
||||||
|
},
|
||||||
|
Graph: memGraph,
|
||||||
|
MaxPendingOpens: 10,
|
||||||
|
}
|
||||||
|
agent, err := New(cfg, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create agent: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// To ensure the heuristic doesn't block on quitting the agent, we'll
|
||||||
|
// use the agent's quit chan to signal when it should also stop.
|
||||||
|
heuristic.quit = agent.quit
|
||||||
|
|
||||||
|
// With the autopilot agent and all its dependencies we'll start the
|
||||||
|
// primary controller goroutine.
|
||||||
|
if err := agent.Start(); err != nil {
|
||||||
|
t.Fatalf("unable to start agent: %v", err)
|
||||||
|
}
|
||||||
|
defer agent.Stop()
|
||||||
|
|
||||||
|
// We'll send an initial "no" response to advance the agent past its
|
||||||
|
// initial check.
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
select {
|
||||||
|
case heuristic.moreChansResps <- moreChansResp{false, 0, 0}:
|
||||||
|
case <-time.After(time.Second * 10):
|
||||||
|
t.Fatalf("heuristic wasn't queried in time")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Next, we'll signal that a new channel has been opened, but it is
|
||||||
|
// still pending.
|
||||||
|
agent.OnChannelPendingOpen()
|
||||||
|
|
||||||
|
// The agent should now query the heuristic in order to determine its
|
||||||
|
// next action as its local state has now been modified.
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
select {
|
||||||
|
case heuristic.moreChansResps <- moreChansResp{false, 0, 0}:
|
||||||
|
case <-time.After(time.Second * 10):
|
||||||
|
t.Fatalf("heuristic wasn't queried in time")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// We'll wait here for either the agent to query the heuristic to be
|
||||||
|
// queried, or for the timeout above to tick.
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
// There shouldn't be a call to the Select method as we've returned
|
||||||
|
// "false" for NeedMoreChans above.
|
||||||
|
select {
|
||||||
|
case heuristic.directiveResps <- []AttachmentDirective{}:
|
||||||
|
t.Fatalf("Select was called but shouldn't have been")
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user