Merge pull request #2740 from wpaulino/gossip-sync-manager

discovery: introduce gossiper syncManager subsystem
This commit is contained in:
Olaoluwa Osuntokun 2019-04-03 15:46:12 -07:00 committed by GitHub
commit 30f2b1ca01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 2834 additions and 1000 deletions

View File

@ -248,7 +248,7 @@ type config struct {
Color string `long:"color" description:"The color of the node in hex format (i.e. '#3399FF'). Used to customize node appearance in intelligence services"`
MinChanSize int64 `long:"minchansize" description:"The smallest channel size (in satoshis) that we should accept. Incoming channels smaller than this will be rejected"`
NoChanUpdates bool `long:"nochanupdates" description:"If specified, lnd will not request real-time channel updates from connected peers. This option should be used by routing nodes to save bandwidth."`
NumGraphSyncPeers int `long:"numgraphsyncpeers" description:"The number of peers that we should receive new graph updates from. This option can be tuned to save bandwidth for light clients or routing nodes."`
RejectPush bool `long:"rejectpush" description:"If true, lnd will not accept channel opening requests with non-zero push amounts. This should prevent accidental pushes to merchant nodes."`
@ -335,6 +335,7 @@ func loadConfig() (*config, error) {
Alias: defaultAlias,
Color: defaultColor,
MinChanSize: int64(minChanFundingSize),
NumGraphSyncPeers: defaultMinPeers,
Tor: &torConfig{
SOCKS: defaultTorSOCKS,
DNS: defaultTorDNS,

View File

@ -20,6 +20,7 @@ import (
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/multimutex"
"github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/ticker"
)
var (
@ -75,7 +76,7 @@ type Config struct {
Router routing.ChannelGraphSource
// ChanSeries is an interfaces that provides access to a time series
// view of the current known channel graph. Each gossipSyncer enabled
// view of the current known channel graph. Each GossipSyncer enabled
// peer will utilize this in order to create and respond to channel
// graph time series queries.
ChanSeries ChannelGraphTimeSeries
@ -143,6 +144,28 @@ type Config struct {
// TODO(roasbeef): extract ann crafting + sign from fundingMgr into
// here?
AnnSigner lnwallet.MessageSigner
// NumActiveSyncers is the number of peers for which we should have
// active syncers with. After reaching NumActiveSyncers, any future
// gossip syncers will be passive.
NumActiveSyncers int
// RotateTicker is a ticker responsible for notifying the SyncManager
// when it should rotate its active syncers. A single active syncer with
// a chansSynced state will be exchanged for a passive syncer in order
// to ensure we don't keep syncing with the same peers.
RotateTicker ticker.Ticker
// HistoricalSyncTicker is a ticker responsible for notifying the
// syncManager when it should attempt a historical sync with a gossip
// sync peer.
HistoricalSyncTicker ticker.Ticker
// ActiveSyncerTimeoutTicker is a ticker responsible for notifying the
// syncManager when it should attempt to start the next pending
// activeSyncer due to the current one not completing its state machine
// within the timeout.
ActiveSyncerTimeoutTicker ticker.Ticker
}
// AuthenticatedGossiper is a subsystem which is responsible for receiving
@ -212,13 +235,14 @@ type AuthenticatedGossiper struct {
rejectMtx sync.RWMutex
recentRejects map[uint64]struct{}
// peerSyncers keeps track of all the gossip syncers we're maintain for
// peers that understand this mode of operation. When we go to send out
// new updates, for all peers in the map, we'll send the messages
// directly to their gossiper, rather than broadcasting them. With this
// change, we ensure we filter out all updates properly.
syncerMtx sync.RWMutex
peerSyncers map[routing.Vertex]*gossipSyncer
// syncMgr is a subsystem responsible for managing the gossip syncers
// for peers currently connected. When a new peer is connected, the
// manager will create its accompanying gossip syncer and determine
// whether it should have an activeSync or passiveSync sync type based
// on how many other gossip syncers are currently active. Any activeSync
// gossip syncers are started in a round-robin manner to ensure we're
// not syncing with multiple peers at the same time.
syncMgr *SyncManager
// reliableSender is a subsystem responsible for handling reliable
// message send requests to peers. This should only be used for channels
@ -243,7 +267,14 @@ func New(cfg Config, selfKey *btcec.PublicKey) *AuthenticatedGossiper {
prematureChannelUpdates: make(map[uint64][]*networkMsg),
channelMtx: multimutex.NewMutex(),
recentRejects: make(map[uint64]struct{}),
peerSyncers: make(map[routing.Vertex]*gossipSyncer),
syncMgr: newSyncManager(&SyncManagerCfg{
ChainHash: cfg.ChainHash,
ChanSeries: cfg.ChanSeries,
RotateTicker: cfg.RotateTicker,
HistoricalSyncTicker: cfg.HistoricalSyncTicker,
ActiveSyncerTimeoutTicker: cfg.ActiveSyncerTimeoutTicker,
NumActiveSyncers: cfg.NumActiveSyncers,
}),
}
gossiper.reliableSender = newReliableSender(&reliableSenderCfg{
@ -419,6 +450,8 @@ func (d *AuthenticatedGossiper) Start() error {
return err
}
d.syncMgr.Start()
d.wg.Add(1)
go d.networkHandler()
@ -435,11 +468,7 @@ func (d *AuthenticatedGossiper) Stop() {
d.blockEpochs.Cancel()
d.syncerMtx.RLock()
for _, syncer := range d.peerSyncers {
syncer.Stop()
}
d.syncerMtx.RUnlock()
d.syncMgr.Stop()
close(d.quit)
d.wg.Wait()
@ -463,7 +492,7 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(msg lnwire.Message,
errChan := make(chan error, 1)
// For messages in the known set of channel series queries, we'll
// dispatch the message directly to the gossipSyncer, and skip the main
// dispatch the message directly to the GossipSyncer, and skip the main
// processing loop.
switch m := msg.(type) {
case *lnwire.QueryShortChanIDs,
@ -471,12 +500,12 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(msg lnwire.Message,
*lnwire.ReplyChannelRange,
*lnwire.ReplyShortChanIDsEnd:
syncer, err := d.findGossipSyncer(peer.IdentityKey())
if err != nil {
log.Warnf("Unable to find gossip syncer for "+
"peer=%x: %v", peer.PubKey(), err)
syncer, ok := d.syncMgr.GossipSyncer(peer.PubKey())
if !ok {
log.Warnf("Gossip syncer for peer=%x not found",
peer.PubKey())
errChan <- err
errChan <- ErrGossipSyncerNotFound
return errChan
}
@ -488,24 +517,22 @@ func (d *AuthenticatedGossiper) ProcessRemoteAnnouncement(msg lnwire.Message,
return errChan
// If a peer is updating its current update horizon, then we'll dispatch
// that directly to the proper gossipSyncer.
// that directly to the proper GossipSyncer.
case *lnwire.GossipTimestampRange:
syncer, err := d.findGossipSyncer(peer.IdentityKey())
if err != nil {
log.Warnf("Unable to find gossip syncer for "+
"peer=%x: %v", peer.PubKey(), err)
syncer, ok := d.syncMgr.GossipSyncer(peer.PubKey())
if !ok {
log.Warnf("Gossip syncer for peer=%x not found",
peer.PubKey())
errChan <- err
errChan <- ErrGossipSyncerNotFound
return errChan
}
// If we've found the message target, then we'll dispatch the
// message directly to it.
err = syncer.ApplyGossipFilter(m)
if err != nil {
log.Warnf("unable to apply gossip "+
"filter for peer=%x: %v",
peer.PubKey(), err)
if err := syncer.ApplyGossipFilter(m); err != nil {
log.Warnf("Unable to apply gossip filter for peer=%x: "+
"%v", peer.PubKey(), err)
errChan <- err
return errChan
@ -590,10 +617,10 @@ type msgWithSenders struct {
}
// mergeSyncerMap is used to merge the set of senders of a particular message
// with peers that we have an active gossipSyncer with. We do this to ensure
// with peers that we have an active GossipSyncer with. We do this to ensure
// that we don't broadcast messages to any peers that we have active gossip
// syncers for.
func (m *msgWithSenders) mergeSyncerMap(syncers map[routing.Vertex]*gossipSyncer) {
func (m *msgWithSenders) mergeSyncerMap(syncers map[routing.Vertex]*GossipSyncer) {
for peerPub := range syncers {
m.senders[peerPub] = struct{}{}
}
@ -812,28 +839,6 @@ func (d *deDupedAnnouncements) Emit() []msgWithSenders {
return msgs
}
// findGossipSyncer is a utility method used by the gossiper to locate the
// gossip syncer for an inbound message so we can properly dispatch the
// incoming message. If a gossip syncer isn't found, then one will be created
// for the target peer.
func (d *AuthenticatedGossiper) findGossipSyncer(pub *btcec.PublicKey) (
*gossipSyncer, error) {
target := routing.NewVertex(pub)
// First, we'll try to find an existing gossiper for this peer.
d.syncerMtx.RLock()
syncer, ok := d.peerSyncers[target]
d.syncerMtx.RUnlock()
// If one exists, then we'll return it directly.
if ok {
return syncer, nil
}
return nil, ErrGossipSyncerNotFound
}
// networkHandler is the primary goroutine that drives this service. The roles
// of this goroutine includes answering queries related to the state of the
// network, syncing up newly connected peers, and also periodically
@ -1028,12 +1033,7 @@ func (d *AuthenticatedGossiper) networkHandler() {
// For the set of peers that have an active gossip
// syncers, we'll collect their pubkeys so we can avoid
// sending them the full message blast below.
d.syncerMtx.RLock()
syncerPeers := make(map[routing.Vertex]*gossipSyncer)
for peerPub, syncer := range d.peerSyncers {
syncerPeers[peerPub] = syncer
}
d.syncerMtx.RUnlock()
syncerPeers := d.syncMgr.GossipSyncers()
log.Infof("Broadcasting batch of %v new announcements",
len(announcementBatch))
@ -1088,62 +1088,16 @@ func (d *AuthenticatedGossiper) networkHandler() {
// InitSyncState is called by outside sub-systems when a connection is
// established to a new peer that understands how to perform channel range
// queries. We'll allocate a new gossip syncer for it, and start any goroutines
// needed to handle new queries. The recvUpdates bool indicates if we should
// continue to receive real-time updates from the remote peer once we've synced
// channel state.
func (d *AuthenticatedGossiper) InitSyncState(syncPeer lnpeer.Peer,
recvUpdates bool) {
d.syncerMtx.Lock()
defer d.syncerMtx.Unlock()
// If we already have a syncer, then we'll exit early as we don't want
// to override it.
nodeID := routing.Vertex(syncPeer.PubKey())
if _, ok := d.peerSyncers[nodeID]; ok {
return
}
log.Infof("Creating new gossipSyncer for peer=%x", nodeID[:])
encoding := lnwire.EncodingSortedPlain
syncer := newGossiperSyncer(gossipSyncerCfg{
chainHash: d.cfg.ChainHash,
syncChanUpdates: recvUpdates,
channelSeries: d.cfg.ChanSeries,
encodingType: encoding,
chunkSize: encodingTypeToChunkSize[encoding],
sendToPeer: func(msgs ...lnwire.Message) error {
return syncPeer.SendMessageLazy(false, msgs...)
},
})
copy(syncer.peerPub[:], nodeID[:])
d.peerSyncers[nodeID] = syncer
syncer.Start()
// needed to handle new queries.
func (d *AuthenticatedGossiper) InitSyncState(syncPeer lnpeer.Peer) {
d.syncMgr.InitSyncState(syncPeer)
}
// PruneSyncState is called by outside sub-systems once a peer that we were
// previously connected to has been disconnected. In this case we can stop the
// existing gossipSyncer assigned to the peer and free up resources.
func (d *AuthenticatedGossiper) PruneSyncState(peer *btcec.PublicKey) {
d.syncerMtx.Lock()
defer d.syncerMtx.Unlock()
log.Infof("Removing gossipSyncer for peer=%x",
peer.SerializeCompressed())
vertex := routing.NewVertex(peer)
syncer, ok := d.peerSyncers[vertex]
if !ok {
return
}
syncer.Stop()
delete(d.peerSyncers, vertex)
return
// existing GossipSyncer assigned to the peer and free up resources.
func (d *AuthenticatedGossiper) PruneSyncState(peer routing.Vertex) {
d.syncMgr.PruneSyncState(peer)
}
// isRecentlyRejectedMsg returns true if we recently rejected a message, and
@ -2514,3 +2468,8 @@ func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo,
return chanAnn, chanUpdate, err
}
// SyncManager returns the gossiper's SyncManager instance.
func (d *AuthenticatedGossiper) SyncManager() *SyncManager {
return d.syncMgr
}

View File

@ -27,6 +27,7 @@ import (
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/ticker"
)
var (
@ -713,12 +714,16 @@ func createTestCtx(startHeight uint32) (*testCtx, func(), error) {
c := make(chan struct{})
return c
},
Router: router,
TrickleDelay: trickleDelay,
RetransmitDelay: retransmitDelay,
ProofMatureDelta: proofMatureDelta,
WaitingProofStore: waitingProofStore,
MessageStore: newMockMessageStore(),
Router: router,
TrickleDelay: trickleDelay,
RetransmitDelay: retransmitDelay,
ProofMatureDelta: proofMatureDelta,
WaitingProofStore: waitingProofStore,
MessageStore: newMockMessageStore(),
RotateTicker: ticker.NewForce(DefaultSyncerRotationInterval),
HistoricalSyncTicker: ticker.NewForce(DefaultHistoricalSyncInterval),
ActiveSyncerTimeoutTicker: ticker.NewForce(DefaultActiveSyncerTimeout),
NumActiveSyncers: 3,
}, nodeKeyPub1)
if err := gossiper.Start(); err != nil {
@ -1447,16 +1452,20 @@ func TestSignatureAnnouncementRetryAtStartup(t *testing.T) {
// the message to the peer.
ctx.gossiper.Stop()
gossiper := New(Config{
Notifier: ctx.gossiper.cfg.Notifier,
Broadcast: ctx.gossiper.cfg.Broadcast,
NotifyWhenOnline: ctx.gossiper.reliableSender.cfg.NotifyWhenOnline,
NotifyWhenOffline: ctx.gossiper.reliableSender.cfg.NotifyWhenOffline,
Router: ctx.gossiper.cfg.Router,
TrickleDelay: trickleDelay,
RetransmitDelay: retransmitDelay,
ProofMatureDelta: proofMatureDelta,
WaitingProofStore: ctx.gossiper.cfg.WaitingProofStore,
MessageStore: ctx.gossiper.cfg.MessageStore,
Notifier: ctx.gossiper.cfg.Notifier,
Broadcast: ctx.gossiper.cfg.Broadcast,
NotifyWhenOnline: ctx.gossiper.reliableSender.cfg.NotifyWhenOnline,
NotifyWhenOffline: ctx.gossiper.reliableSender.cfg.NotifyWhenOffline,
Router: ctx.gossiper.cfg.Router,
TrickleDelay: trickleDelay,
RetransmitDelay: retransmitDelay,
ProofMatureDelta: proofMatureDelta,
WaitingProofStore: ctx.gossiper.cfg.WaitingProofStore,
MessageStore: ctx.gossiper.cfg.MessageStore,
RotateTicker: ticker.NewForce(DefaultSyncerRotationInterval),
HistoricalSyncTicker: ticker.NewForce(DefaultHistoricalSyncInterval),
ActiveSyncerTimeoutTicker: ticker.NewForce(DefaultActiveSyncerTimeout),
NumActiveSyncers: 3,
}, ctx.gossiper.selfKey)
if err != nil {
t.Fatalf("unable to recreate gossiper: %v", err)

View File

@ -8,6 +8,7 @@ import (
"github.com/btcsuite/btcd/btcec"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lnwire"
)
@ -59,29 +60,6 @@ func assertMsgsSent(t *testing.T, msgChan chan lnwire.Message,
}
}
// waitPredicate is a helper test function that will wait for a timeout period
// of time until the passed predicate returns true.
func waitPredicate(t *testing.T, timeout time.Duration, pred func() bool) {
t.Helper()
const pollInterval = 20 * time.Millisecond
exitTimer := time.After(timeout)
for {
<-time.After(pollInterval)
select {
case <-exitTimer:
t.Fatalf("predicate not satisfied after timeout")
default:
}
if pred() {
return
}
}
}
// TestReliableSenderFlow ensures that the flow for sending messages reliably to
// a peer while taking into account its connection lifecycle works as expected.
func TestReliableSenderFlow(t *testing.T) {
@ -262,27 +240,23 @@ func TestReliableSenderStaleMessages(t *testing.T) {
// message store since it is seen as stale and has been sent at least
// once. Once the message is removed, the peerHandler should be torn
// down as there are no longer any pending messages within the store.
var predErr error
waitPredicate(t, time.Second, func() bool {
err := lntest.WaitNoError(func() error {
msgs, err := reliableSender.cfg.MessageStore.MessagesForPeer(
peerPubKey,
)
if err != nil {
predErr = fmt.Errorf("unable to retrieve messages for "+
return fmt.Errorf("unable to retrieve messages for "+
"peer: %v", err)
return false
}
if len(msgs) != 0 {
predErr = fmt.Errorf("expected to not find any "+
return fmt.Errorf("expected to not find any "+
"messages for peer, found %d", len(msgs))
return false
}
predErr = nil
return true
})
if predErr != nil {
t.Fatal(predErr)
return nil
}, time.Second)
if err != nil {
t.Fatal(err)
}
// Override IsMsgStale to no longer mark messages as stale.

735
discovery/sync_manager.go Normal file
View File

@ -0,0 +1,735 @@
package discovery
import (
"container/list"
"errors"
"sync"
"time"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/lightningnetwork/lnd/lnpeer"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/ticker"
)
const (
// DefaultSyncerRotationInterval is the default interval in which we'll
// rotate a single active syncer.
DefaultSyncerRotationInterval = 20 * time.Minute
// DefaultHistoricalSyncInterval is the default interval in which we'll
// force a historical sync to ensure we have as much of the public
// network as possible.
DefaultHistoricalSyncInterval = 20 * time.Minute
// DefaultActiveSyncerTimeout is the default timeout interval in which
// we'll wait until an active syncer has completed its state machine and
// reached its final chansSynced state.
DefaultActiveSyncerTimeout = 5 * time.Minute
)
var (
// ErrSyncManagerExiting is an error returned when we attempt to
// start/stop a gossip syncer for a connected/disconnected peer, but the
// SyncManager has already been stopped.
ErrSyncManagerExiting = errors.New("sync manager exiting")
)
// staleActiveSyncer is an internal message the SyncManager will use in order to
// handle a peer corresponding to an active syncer being disconnected.
type staleActiveSyncer struct {
// syncer is the active syncer to be removed.
syncer *GossipSyncer
// transitioned, if true, signals that the active GossipSyncer is stale
// due to being transitioned to a PassiveSync state.
transitioned bool
// done serves as a signal to the caller that the SyncManager's internal
// state correctly reflects the stale active syncer. This is needed to
// ensure we always create a new syncer for a flappy peer after they
// disconnect if they happened to be an active syncer.
done chan struct{}
}
// SyncManagerCfg contains all of the dependencies required for the SyncManager
// to carry out its duties.
type SyncManagerCfg struct {
// ChainHash is a hash that indicates the specific network of the active
// chain.
ChainHash chainhash.Hash
// ChanSeries is an interface that provides access to a time series view
// of the current known channel graph. Each GossipSyncer enabled peer
// will utilize this in order to create and respond to channel graph
// time series queries.
ChanSeries ChannelGraphTimeSeries
// NumActiveSyncers is the number of peers for which we should have
// active syncers with. After reaching NumActiveSyncers, any future
// gossip syncers will be passive.
NumActiveSyncers int
// RotateTicker is a ticker responsible for notifying the SyncManager
// when it should rotate its active syncers. A single active syncer with
// a chansSynced state will be exchanged for a passive syncer in order
// to ensure we don't keep syncing with the same peers.
RotateTicker ticker.Ticker
// HistoricalSyncTicker is a ticker responsible for notifying the
// SyncManager when it should attempt a historical sync with a gossip
// sync peer.
HistoricalSyncTicker ticker.Ticker
// ActiveSyncerTimeoutTicker is a ticker responsible for notifying the
// SyncManager when it should attempt to start the next pending
// activeSyncer due to the current one not completing its state machine
// within the timeout.
ActiveSyncerTimeoutTicker ticker.Ticker
}
// SyncManager is a subsystem of the gossiper that manages the gossip syncers
// for peers currently connected. When a new peer is connected, the manager will
// create its accompanying gossip syncer and determine whether it should have an
// ActiveSync or PassiveSync sync type based on how many other gossip syncers
// are currently active. Any ActiveSync gossip syncers are started in a
// round-robin manner to ensure we're not syncing with multiple peers at the
// same time. The first GossipSyncer registered with the SyncManager will
// attempt a historical sync to ensure we have as much of the public channel
// graph as possible.
type SyncManager struct {
start sync.Once
stop sync.Once
cfg SyncManagerCfg
// historicalSync allows us to perform an initial historical sync only
// _once_ with a peer during the SyncManager's startup.
historicalSync sync.Once
// activeSyncers is the set of all syncers for which we are currently
// receiving graph updates from. The number of possible active syncers
// is bounded by NumActiveSyncers.
activeSyncers map[routing.Vertex]*GossipSyncer
// inactiveSyncers is the set of all syncers for which we are not
// currently receiving new graph updates from.
inactiveSyncers map[routing.Vertex]*GossipSyncer
// pendingActiveSyncers is a map that tracks our set of pending active
// syncers. This map will be queried when choosing the next pending
// active syncer in the queue to ensure it is not stale.
pendingActiveSyncers map[routing.Vertex]*GossipSyncer
// pendingActiveSyncerQueue is the list of active syncers which are
// pending to be started. Syncers will be added to this list through the
// newActiveSyncers and staleActiveSyncers channels.
pendingActiveSyncerQueue *list.List
// newActiveSyncers is a channel that will serve as a signal to the
// roundRobinHandler to allow it to transition the next pending active
// syncer in the queue.
newActiveSyncers chan struct{}
// staleActiveSyncers is a channel through which we'll send any stale
// active syncers that should be removed from the round-robin.
staleActiveSyncers chan *staleActiveSyncer
sync.Mutex
wg sync.WaitGroup
quit chan struct{}
}
// newSyncManager constructs a new SyncManager backed by the given config.
func newSyncManager(cfg *SyncManagerCfg) *SyncManager {
return &SyncManager{
cfg: *cfg,
activeSyncers: make(
map[routing.Vertex]*GossipSyncer, cfg.NumActiveSyncers,
),
inactiveSyncers: make(map[routing.Vertex]*GossipSyncer),
pendingActiveSyncers: make(map[routing.Vertex]*GossipSyncer),
pendingActiveSyncerQueue: list.New(),
newActiveSyncers: make(chan struct{}),
staleActiveSyncers: make(chan *staleActiveSyncer),
quit: make(chan struct{}),
}
}
// Start starts the SyncManager in order to properly carry out its duties.
func (m *SyncManager) Start() {
m.start.Do(func() {
m.wg.Add(2)
go m.syncerHandler()
go m.roundRobinHandler()
})
}
// Stop stops the SyncManager from performing its duties.
func (m *SyncManager) Stop() {
m.stop.Do(func() {
close(m.quit)
m.wg.Wait()
m.Lock()
defer m.Unlock()
for _, syncer := range m.inactiveSyncers {
syncer.Stop()
}
for _, syncer := range m.pendingActiveSyncers {
syncer.Stop()
}
for _, syncer := range m.activeSyncers {
syncer.Stop()
}
})
}
// syncerHandler is the SyncManager's main event loop responsible for:
//
// 1. Finding new peers to receive graph updates from to ensure we don't only
// receive them from the same set of peers.
//
// 2. Finding new peers to force a historical sync with to ensure we have as
// much of the public network as possible.
//
// NOTE: This must be run as a goroutine.
func (m *SyncManager) syncerHandler() {
defer m.wg.Done()
m.cfg.RotateTicker.Resume()
defer m.cfg.RotateTicker.Stop()
m.cfg.HistoricalSyncTicker.Resume()
defer m.cfg.HistoricalSyncTicker.Stop()
for {
select {
// Our RotateTicker has ticked, so we'll attempt to rotate a
// single active syncer with a passive one.
case <-m.cfg.RotateTicker.Ticks():
m.rotateActiveSyncerCandidate()
// Our HistoricalSyncTicker has ticked, so we'll randomly select
// a peer and force a historical sync with them.
case <-m.cfg.HistoricalSyncTicker.Ticks():
m.forceHistoricalSync()
case <-m.quit:
return
}
}
}
// signalNewActiveSyncer sends a signal to the roundRobinHandler to ensure it
// transitions any pending active syncers.
func (m *SyncManager) signalNewActiveSyncer() {
select {
case m.newActiveSyncers <- struct{}{}:
case <-m.quit:
}
}
// signalStaleActiveSyncer removes the syncer for the given peer from the
// round-robin queue.
func (m *SyncManager) signalStaleActiveSyncer(s *GossipSyncer, transitioned bool) {
done := make(chan struct{})
select {
case m.staleActiveSyncers <- &staleActiveSyncer{
syncer: s,
transitioned: transitioned,
done: done,
}:
case <-m.quit:
}
// Before returning to the caller, we'll wait for the roundRobinHandler
// to signal us that the SyncManager has correctly updated its internal
// state after handling the stale active syncer.
select {
case <-done:
case <-m.quit:
}
}
// roundRobinHandler is the SyncManager's event loop responsible for managing
// the round-robin queue of our active syncers to ensure they don't overlap and
// request the same set of channels, which significantly reduces bandwidth
// usage.
//
// NOTE: This must be run as a goroutine.
func (m *SyncManager) roundRobinHandler() {
defer m.wg.Done()
defer m.cfg.ActiveSyncerTimeoutTicker.Stop()
var (
// current will hold the current active syncer we're waiting for
// to complete its state machine.
current *GossipSyncer
// transitionNext will be responsible for containing the signal
// of when the current active syncer has completed its state
// machine. This signal allows us to transition the next pending
// active syncer, if any.
transitionNext chan struct{}
)
// transitionNextSyncer is a helper closure that we'll use to transition
// the next syncer queued up. If there aren't any, this will act as a
// NOP.
transitionNextSyncer := func() {
m.Lock()
current = m.nextPendingActiveSyncer()
m.Unlock()
for current != nil {
// We'll avoid performing the transition with the lock
// as it can potentially stall the SyncManager due to
// the syncTransitionTimeout.
err := m.transitionPassiveSyncer(current)
// If we timed out attempting to transition the syncer,
// we'll re-queue it to retry at a later time and move
// on to the next.
if err == ErrSyncTransitionTimeout {
log.Debugf("Timed out attempting to "+
"transition pending active "+
"GossipSyncer(%x)", current.cfg.peerPub)
m.Lock()
m.queueActiveSyncer(current)
current = m.nextPendingActiveSyncer()
m.Unlock()
continue
}
if err != nil {
log.Errorf("Unable to transition pending "+
"active GossipSyncer(%x): %v",
current.cfg.peerPub, err)
m.Lock()
current = m.nextPendingActiveSyncer()
m.Unlock()
continue
}
// The transition succeeded, so we'll set our signal to
// know when we should attempt to transition the next
// pending active syncer in our queue.
transitionNext = current.ResetSyncedSignal()
m.cfg.ActiveSyncerTimeoutTicker.Resume()
return
}
transitionNext = nil
m.cfg.ActiveSyncerTimeoutTicker.Pause()
}
for {
select {
// A new active syncer signal has been received, which indicates
// a new pending active syncer has been added to our queue.
// We'll only attempt to transition it now if we're not already
// in the middle of transitioning another one. We do this to
// ensure we don't overlap when requesting channels from
// different peers.
case <-m.newActiveSyncers:
if current == nil {
transitionNextSyncer()
}
// A stale active syncer has been received, so we'll need to
// remove them from our queue. If we are currently waiting for
// its state machine to complete, we'll move on to the next
// active syncer in the queue.
case staleActiveSyncer := <-m.staleActiveSyncers:
s := staleActiveSyncer.syncer
m.Lock()
// If the syncer has transitioned from an ActiveSync
// type, rather than disconnecting, we'll include it in
// the set of inactive syncers.
if staleActiveSyncer.transitioned {
m.inactiveSyncers[s.cfg.peerPub] = s
}
// Remove the internal active syncer references for this
// peer.
delete(m.pendingActiveSyncers, s.cfg.peerPub)
delete(m.activeSyncers, s.cfg.peerPub)
// We'll then attempt to find a passive syncer that can
// replace the stale active syncer.
newActiveSyncer := m.chooseRandomSyncer(nil, false)
if newActiveSyncer != nil {
m.queueActiveSyncer(newActiveSyncer)
}
m.Unlock()
// Signal to the caller that they can now proceed since
// the SyncManager's state correctly reflects the
// stale active syncer.
close(staleActiveSyncer.done)
// If we're not currently waiting for an active syncer
// to reach its terminal state, or if we are but we are
// currently waiting for the peer being
// disconnected/transitioned, then we'll move on to the
// next active syncer in our queue.
if current == nil || (current != nil &&
current.cfg.peerPub == s.cfg.peerPub) {
transitionNextSyncer()
}
// Our current active syncer has reached its terminal
// chansSynced state, so we'll proceed to transitioning the next
// pending active syncer if there is one.
case <-transitionNext:
transitionNextSyncer()
// We've timed out waiting for the current active syncer to
// reach its terminal chansSynced state, so we'll just
// move on to the next and avoid retrying as its already been
// transitioned.
case <-m.cfg.ActiveSyncerTimeoutTicker.Ticks():
log.Warnf("Timed out waiting for GossipSyncer(%x) to "+
"be fully synced", current.cfg.peerPub)
transitionNextSyncer()
case <-m.quit:
return
}
}
}
// queueActiveSyncer queues the given pending active gossip syncer to the end of
// the round-robin queue.
func (m *SyncManager) queueActiveSyncer(s *GossipSyncer) {
log.Debugf("Queueing next pending active GossipSyncer(%x)",
s.cfg.peerPub)
delete(m.inactiveSyncers, s.cfg.peerPub)
m.pendingActiveSyncers[s.cfg.peerPub] = s
m.pendingActiveSyncerQueue.PushBack(s)
}
// nextPendingActiveSyncer returns the next active syncer pending to be
// transitioned. If there aren't any, then `nil` is returned.
func (m *SyncManager) nextPendingActiveSyncer() *GossipSyncer {
next := m.pendingActiveSyncerQueue.Front()
for next != nil {
s := m.pendingActiveSyncerQueue.Remove(next).(*GossipSyncer)
// If the next pending active syncer is no longer in our lookup
// map, then the corresponding peer has disconnected, so we'll
// skip them.
if _, ok := m.pendingActiveSyncers[s.cfg.peerPub]; !ok {
next = m.pendingActiveSyncerQueue.Front()
continue
}
return s
}
return nil
}
// rotateActiveSyncerCandidate rotates a single active syncer. In order to
// achieve this, the active syncer must be in a chansSynced state in order to
// process the sync transition.
func (m *SyncManager) rotateActiveSyncerCandidate() {
// If we don't have a candidate to rotate with, we can return early.
m.Lock()
candidate := m.chooseRandomSyncer(nil, false)
if candidate == nil {
m.Unlock()
log.Debug("No eligible candidate to rotate active syncer")
return
}
// We'll choose an active syncer at random that's within a chansSynced
// state to rotate.
var activeSyncer *GossipSyncer
for _, s := range m.activeSyncers {
// The active syncer must be in a chansSynced state in order to
// process sync transitions.
if s.syncState() != chansSynced {
continue
}
activeSyncer = s
break
}
m.Unlock()
// If we couldn't find an eligible one, we can return early.
if activeSyncer == nil {
log.Debug("No eligible active syncer to rotate")
return
}
// Otherwise, we'll attempt to transition each syncer to their
// respective new sync type. We'll avoid performing the transition with
// the lock as it can potentially stall the SyncManager due to the
// syncTransitionTimeout.
if err := m.transitionActiveSyncer(activeSyncer); err != nil {
log.Errorf("Unable to transition active "+
"GossipSyncer(%x): %v", activeSyncer.cfg.peerPub, err)
return
}
m.Lock()
m.queueActiveSyncer(candidate)
m.Unlock()
m.signalNewActiveSyncer()
}
// transitionActiveSyncer transitions an active syncer to a passive one.
func (m *SyncManager) transitionActiveSyncer(s *GossipSyncer) error {
log.Debugf("Transitioning active GossipSyncer(%x) to passive",
s.cfg.peerPub)
if err := s.ProcessSyncTransition(PassiveSync); err != nil {
return err
}
m.signalStaleActiveSyncer(s, true)
return nil
}
// transitionPassiveSyncer transitions a passive syncer to an active one.
func (m *SyncManager) transitionPassiveSyncer(s *GossipSyncer) error {
log.Debugf("Transitioning passive GossipSyncer(%x) to active",
s.cfg.peerPub)
if err := s.ProcessSyncTransition(ActiveSync); err != nil {
return err
}
m.Lock()
m.activeSyncers[s.cfg.peerPub] = s
delete(m.pendingActiveSyncers, s.cfg.peerPub)
m.Unlock()
return nil
}
// forceHistoricalSync chooses a syncer with a remote peer at random and forces
// a historical sync with it.
func (m *SyncManager) forceHistoricalSync() {
m.Lock()
defer m.Unlock()
// We'll choose a random peer with whom we can perform a historical sync
// with. We'll set useActive to true to make sure we can still do one if
// we don't happen to have any non-active syncers.
candidatesChosen := make(map[routing.Vertex]struct{})
s := m.chooseRandomSyncer(candidatesChosen, true)
for s != nil {
// Blacklist the candidate to ensure it's not chosen again.
candidatesChosen[s.cfg.peerPub] = struct{}{}
err := s.historicalSync()
if err == nil {
return
}
log.Errorf("Unable to perform historical sync with "+
"GossipSyncer(%x): %v", s.cfg.peerPub, err)
s = m.chooseRandomSyncer(candidatesChosen, true)
}
}
// chooseRandomSyncer returns a random non-active syncer that's eligible for a
// sync transition. A blacklist can be used to skip any previously chosen
// candidates. The useActive boolean can be used to also filter active syncers.
//
// NOTE: It's possible for a nil value to be returned if there are no eligible
// candidate syncers.
//
// NOTE: This method must be called with the syncersMtx lock held.
func (m *SyncManager) chooseRandomSyncer(blacklist map[routing.Vertex]struct{},
useActive bool) *GossipSyncer {
eligible := func(s *GossipSyncer) bool {
// Skip any syncers that exist within the blacklist.
if blacklist != nil {
if _, ok := blacklist[s.cfg.peerPub]; ok {
return false
}
}
// Only syncers in a chansSynced state are viable for sync
// transitions, so skip any that aren't.
return s.syncState() == chansSynced
}
for _, s := range m.inactiveSyncers {
if !eligible(s) {
continue
}
return s
}
if useActive {
for _, s := range m.activeSyncers {
if !eligible(s) {
continue
}
return s
}
}
return nil
}
// InitSyncState is called by outside sub-systems when a connection is
// established to a new peer that understands how to perform channel range
// queries. We'll allocate a new GossipSyncer for it, and start any goroutines
// needed to handle new queries. The first GossipSyncer registered with the
// SyncManager will attempt a historical sync to ensure we have as much of the
// public channel graph as possible.
//
// TODO(wilmer): Only mark as ActiveSync if this isn't a channel peer.
func (m *SyncManager) InitSyncState(peer lnpeer.Peer) {
// If we already have a syncer, then we'll exit early as we don't want
// to override it.
nodeID := routing.Vertex(peer.PubKey())
if _, ok := m.GossipSyncer(nodeID); ok {
return
}
log.Infof("Creating new GossipSyncer for peer=%x", nodeID[:])
encoding := lnwire.EncodingSortedPlain
s := newGossipSyncer(gossipSyncerCfg{
chainHash: m.cfg.ChainHash,
peerPub: nodeID,
channelSeries: m.cfg.ChanSeries,
encodingType: encoding,
chunkSize: encodingTypeToChunkSize[encoding],
sendToPeer: func(msgs ...lnwire.Message) error {
return peer.SendMessage(false, msgs...)
},
})
// Gossip syncers are initialized by default as passive and in a
// chansSynced state so that they can reply to any peer queries or
// handle any sync transitions.
s.setSyncType(PassiveSync)
s.setSyncState(chansSynced)
s.Start()
m.Lock()
m.inactiveSyncers[nodeID] = s
// We'll force a historical sync with the first peer we connect to
// ensure we get as much of the graph as possible.
var err error
m.historicalSync.Do(func() {
log.Infof("Attempting historical sync with GossipSyncer(%x)",
s.cfg.peerPub)
err = s.historicalSync()
})
if err != nil {
log.Errorf("Unable to perform historical sync with "+
"GossipSyncer(%x): %v", s.cfg.peerPub, err)
// Reset historicalSync to ensure it is tried again with a
// different peer.
m.historicalSync = sync.Once{}
}
// If we've yet to reach our desired number of active syncers, then
// we'll use this one.
numActiveSyncers := len(m.activeSyncers) + len(m.pendingActiveSyncers)
if numActiveSyncers < m.cfg.NumActiveSyncers {
m.queueActiveSyncer(s)
m.Unlock()
m.signalNewActiveSyncer()
return
}
m.Unlock()
}
// PruneSyncState is called by outside sub-systems once a peer that we were
// previously connected to has been disconnected. In this case we can stop the
// existing GossipSyncer assigned to the peer and free up resources.
func (m *SyncManager) PruneSyncState(peer routing.Vertex) {
s, ok := m.GossipSyncer(peer)
if !ok {
return
}
log.Infof("Removing GossipSyncer for peer=%v", peer)
// We'll start by stopping the GossipSyncer for the disconnected peer.
s.Stop()
// If it's a non-active syncer, then we can just exit now.
m.Lock()
if _, ok := m.inactiveSyncers[s.cfg.peerPub]; ok {
delete(m.inactiveSyncers, s.cfg.peerPub)
m.Unlock()
return
}
m.Unlock()
// Otherwise, we'll need to dequeue it from our pending active syncers
// queue and find a new one to replace it, if any.
m.signalStaleActiveSyncer(s, false)
}
// GossipSyncer returns the associated gossip syncer of a peer. The boolean
// returned signals whether there exists a gossip syncer for the peer.
func (m *SyncManager) GossipSyncer(peer routing.Vertex) (*GossipSyncer, bool) {
m.Lock()
defer m.Unlock()
return m.gossipSyncer(peer)
}
// gossipSyncer returns the associated gossip syncer of a peer. The boolean
// returned signals whether there exists a gossip syncer for the peer.
func (m *SyncManager) gossipSyncer(peer routing.Vertex) (*GossipSyncer, bool) {
syncer, ok := m.inactiveSyncers[peer]
if ok {
return syncer, true
}
syncer, ok = m.pendingActiveSyncers[peer]
if ok {
return syncer, true
}
syncer, ok = m.activeSyncers[peer]
if ok {
return syncer, true
}
return nil, false
}
// GossipSyncers returns all of the currently initialized gossip syncers.
func (m *SyncManager) GossipSyncers() map[routing.Vertex]*GossipSyncer {
m.Lock()
defer m.Unlock()
numSyncers := len(m.inactiveSyncers) + len(m.activeSyncers) +
len(m.inactiveSyncers)
syncers := make(map[routing.Vertex]*GossipSyncer, numSyncers)
for _, syncer := range m.inactiveSyncers {
syncers[syncer.cfg.peerPub] = syncer
}
for _, syncer := range m.pendingActiveSyncers {
syncers[syncer.cfg.peerPub] = syncer
}
for _, syncer := range m.activeSyncers {
syncers[syncer.cfg.peerPub] = syncer
}
return syncers
}

View File

@ -0,0 +1,549 @@
package discovery
import (
"fmt"
"math"
"reflect"
"sync/atomic"
"testing"
"time"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/lntest"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/ticker"
)
// randPeer creates a random peer.
func randPeer(t *testing.T, quit chan struct{}) *mockPeer {
t.Helper()
return &mockPeer{
pk: randPubKey(t),
sentMsgs: make(chan lnwire.Message),
quit: quit,
}
}
// newTestSyncManager creates a new test SyncManager using mock implementations
// of its dependencies.
func newTestSyncManager(numActiveSyncers int) *SyncManager {
hID := lnwire.ShortChannelID{BlockHeight: latestKnownHeight}
return newSyncManager(&SyncManagerCfg{
ChanSeries: newMockChannelGraphTimeSeries(hID),
RotateTicker: ticker.NewForce(DefaultSyncerRotationInterval),
HistoricalSyncTicker: ticker.NewForce(DefaultHistoricalSyncInterval),
ActiveSyncerTimeoutTicker: ticker.NewForce(DefaultActiveSyncerTimeout),
NumActiveSyncers: numActiveSyncers,
})
}
// TestSyncManagerNumActiveSyncers ensures that we are unable to have more than
// NumActiveSyncers active syncers.
func TestSyncManagerNumActiveSyncers(t *testing.T) {
t.Parallel()
// We'll start by creating our test sync manager which will hold up to
// 3 active syncers.
const numActiveSyncers = 3
const numSyncers = numActiveSyncers + 1
syncMgr := newTestSyncManager(numActiveSyncers)
syncMgr.Start()
defer syncMgr.Stop()
// We'll go ahead and create our syncers. We'll gather the ones which
// should be active and passive to check them later on.
for i := 0; i < numActiveSyncers; i++ {
peer := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(peer)
// The first syncer registered always attempts a historical
// sync.
if i == 0 {
assertTransitionToChansSynced(t, syncMgr, peer, true)
}
assertPassiveSyncerTransition(t, syncMgr, peer)
assertSyncerStatus(t, syncMgr, peer, chansSynced, ActiveSync)
}
for i := 0; i < numSyncers-numActiveSyncers; i++ {
peer := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(peer)
assertSyncerStatus(t, syncMgr, peer, chansSynced, PassiveSync)
}
}
// TestSyncManagerNewActiveSyncerAfterDisconnect ensures that we can regain an
// active syncer after losing one due to the peer disconnecting.
func TestSyncManagerNewActiveSyncerAfterDisconnect(t *testing.T) {
t.Parallel()
// We'll create our test sync manager to only have one active syncer.
syncMgr := newTestSyncManager(1)
syncMgr.Start()
defer syncMgr.Stop()
// peer1 will represent an active syncer that performs a historical
// sync since it is the first registered peer with the SyncManager.
peer1 := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(peer1)
assertTransitionToChansSynced(t, syncMgr, peer1, true)
assertPassiveSyncerTransition(t, syncMgr, peer1)
// It will then be torn down to simulate a disconnection. Since there
// are no other candidate syncers available, the active syncer won't be
// replaced.
syncMgr.PruneSyncState(peer1.PubKey())
// Then, we'll start our active syncer again, but this time we'll also
// have a passive syncer available to replace the active syncer after
// the peer disconnects.
syncMgr.InitSyncState(peer1)
assertPassiveSyncerTransition(t, syncMgr, peer1)
// Create our second peer, which should be initialized as a passive
// syncer.
peer2 := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(peer2)
assertSyncerStatus(t, syncMgr, peer2, chansSynced, PassiveSync)
// Disconnect our active syncer, which should trigger the SyncManager to
// replace it with our passive syncer.
syncMgr.PruneSyncState(peer1.PubKey())
assertPassiveSyncerTransition(t, syncMgr, peer2)
}
// TestSyncManagerRotateActiveSyncerCandidate tests that we can successfully
// rotate our active syncers after a certain interval.
func TestSyncManagerRotateActiveSyncerCandidate(t *testing.T) {
t.Parallel()
// We'll create our sync manager with three active syncers.
syncMgr := newTestSyncManager(1)
syncMgr.Start()
defer syncMgr.Stop()
// The first syncer registered always performs a historical sync.
activeSyncPeer := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(activeSyncPeer)
assertTransitionToChansSynced(t, syncMgr, activeSyncPeer, true)
assertPassiveSyncerTransition(t, syncMgr, activeSyncPeer)
// We'll send a tick to force a rotation. Since there aren't any
// candidates, none of the active syncers will be rotated.
syncMgr.cfg.RotateTicker.(*ticker.Force).Force <- time.Time{}
assertNoMsgSent(t, activeSyncPeer)
assertSyncerStatus(t, syncMgr, activeSyncPeer, chansSynced, ActiveSync)
// We'll then go ahead and add a passive syncer.
passiveSyncPeer := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(passiveSyncPeer)
assertSyncerStatus(t, syncMgr, passiveSyncPeer, chansSynced, PassiveSync)
// We'll force another rotation - this time, since we have a passive
// syncer available, they should be rotated.
syncMgr.cfg.RotateTicker.(*ticker.Force).Force <- time.Time{}
// The transition from an active syncer to a passive syncer causes the
// peer to send out a new GossipTimestampRange in the past so that they
// don't receive new graph updates.
assertActiveSyncerTransition(t, syncMgr, activeSyncPeer)
// The transition from a passive syncer to an active syncer causes the
// peer to send a new GossipTimestampRange with the current timestamp to
// signal that they would like to receive new graph updates from their
// peers. This will also cause the gossip syncer to redo its state
// machine, starting from its initial syncingChans state. We'll then
// need to transition it to its final chansSynced state to ensure the
// next syncer is properly started in the round-robin.
assertPassiveSyncerTransition(t, syncMgr, passiveSyncPeer)
}
// TestSyncManagerHistoricalSync ensures that we only attempt a single
// historical sync during the SyncManager's startup, and that we can routinely
// force historical syncs whenever the HistoricalSyncTicker fires.
func TestSyncManagerHistoricalSync(t *testing.T) {
t.Parallel()
syncMgr := newTestSyncManager(0)
syncMgr.Start()
defer syncMgr.Stop()
// We should expect to see a QueryChannelRange message with a
// FirstBlockHeight of the genesis block, signaling that a historical
// sync is being attempted.
peer := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(peer)
assertMsgSent(t, peer, &lnwire.QueryChannelRange{
FirstBlockHeight: 0,
NumBlocks: math.MaxUint32,
})
// If an additional peer connects, then a historical sync should not be
// attempted again.
extraPeer := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(extraPeer)
assertNoMsgSent(t, extraPeer)
// Then, we'll send a tick to force a historical sync. This should
// trigger the extra peer to also perform a historical sync since the
// first peer is not eligible due to not being in a chansSynced state.
syncMgr.cfg.HistoricalSyncTicker.(*ticker.Force).Force <- time.Time{}
assertMsgSent(t, extraPeer, &lnwire.QueryChannelRange{
FirstBlockHeight: 0,
NumBlocks: math.MaxUint32,
})
}
// TestSyncManagerRoundRobinQueue ensures that any subsequent active syncers can
// only be started after the previous one has completed its state machine.
func TestSyncManagerRoundRobinQueue(t *testing.T) {
t.Parallel()
const numActiveSyncers = 3
// We'll start by creating our sync manager with support for three
// active syncers.
syncMgr := newTestSyncManager(numActiveSyncers)
syncMgr.Start()
defer syncMgr.Stop()
peers := make([]*mockPeer, 0, numActiveSyncers)
// The first syncer registered always attempts a historical sync.
firstPeer := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(firstPeer)
peers = append(peers, firstPeer)
assertTransitionToChansSynced(t, syncMgr, firstPeer, true)
// After completing the historical sync, a sync transition to ActiveSync
// should happen. It should transition immediately since it has no
// dependents.
assertActiveGossipTimestampRange(t, firstPeer)
// We'll create the remaining numActiveSyncers. These will be queued in
// the round robin since the first syncer has yet to reach chansSynced.
queuedPeers := make([]*mockPeer, 0, numActiveSyncers-1)
for i := 0; i < numActiveSyncers-1; i++ {
peer := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(peer)
peers = append(peers, peer)
queuedPeers = append(queuedPeers, peer)
}
// Ensure they cannot transition without sending a GossipTimestampRange
// message first.
for _, peer := range queuedPeers {
assertNoMsgSent(t, peer)
}
// Transition the first syncer to chansSynced, which should allow the
// second to transition next.
assertTransitionToChansSynced(t, syncMgr, firstPeer, false)
// assertSyncerTransitioned ensures the target peer's syncer is the only
// that has transitioned.
assertSyncerTransitioned := func(target *mockPeer) {
t.Helper()
for _, peer := range peers {
if peer.PubKey() != target.PubKey() {
assertNoMsgSent(t, peer)
continue
}
assertActiveGossipTimestampRange(t, target)
}
}
// For each queued syncer, we'll ensure they have transitioned to an
// ActiveSync type and reached their final chansSynced state to allow
// the next one to transition.
for _, peer := range queuedPeers {
assertSyncerTransitioned(peer)
assertTransitionToChansSynced(t, syncMgr, peer, false)
}
}
// TestSyncManagerRoundRobinTimeout ensures that if we timeout while waiting for
// an active syncer to reach its final chansSynced state, then we will go on to
// start the next.
func TestSyncManagerRoundRobinTimeout(t *testing.T) {
t.Parallel()
// Create our sync manager with support for two active syncers.
syncMgr := newTestSyncManager(2)
syncMgr.Start()
defer syncMgr.Stop()
// peer1 will be the first peer we start, which will time out and cause
// peer2 to start.
peer1 := randPeer(t, syncMgr.quit)
peer2 := randPeer(t, syncMgr.quit)
// The first syncer registered always attempts a historical sync.
syncMgr.InitSyncState(peer1)
assertTransitionToChansSynced(t, syncMgr, peer1, true)
// We assume the syncer for peer1 has transitioned once we see it send a
// lnwire.GossipTimestampRange message.
assertActiveGossipTimestampRange(t, peer1)
// We'll then create the syncer for peer2. This should cause it to be
// queued so that it starts once the syncer for peer1 is done.
syncMgr.InitSyncState(peer2)
assertNoMsgSent(t, peer2)
// Send a force tick to pretend the sync manager has timed out waiting
// for peer1's syncer to reach chansSynced.
syncMgr.cfg.ActiveSyncerTimeoutTicker.(*ticker.Force).Force <- time.Time{}
// Finally, ensure that the syncer for peer2 has transitioned.
assertActiveGossipTimestampRange(t, peer2)
}
// TestSyncManagerRoundRobinStaleSyncer ensures that any stale active syncers we
// are currently waiting for or are queued up to start are properly removed and
// stopped.
func TestSyncManagerRoundRobinStaleSyncer(t *testing.T) {
t.Parallel()
const numActiveSyncers = 4
// We'll create and start our sync manager with some active syncers.
syncMgr := newTestSyncManager(numActiveSyncers)
syncMgr.Start()
defer syncMgr.Stop()
peers := make([]*mockPeer, 0, numActiveSyncers)
// The first syncer registered always attempts a historical sync.
firstPeer := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(firstPeer)
peers = append(peers, firstPeer)
assertTransitionToChansSynced(t, syncMgr, firstPeer, true)
// After completing the historical sync, a sync transition to ActiveSync
// should happen. It should transition immediately since it has no
// dependents.
assertActiveGossipTimestampRange(t, firstPeer)
assertMsgSent(t, firstPeer, &lnwire.QueryChannelRange{
FirstBlockHeight: startHeight,
NumBlocks: math.MaxUint32 - startHeight,
})
// We'll create the remaining numActiveSyncers. These will be queued in
// the round robin since the first syncer has yet to reach chansSynced.
queuedPeers := make([]*mockPeer, 0, numActiveSyncers-1)
for i := 0; i < numActiveSyncers-1; i++ {
peer := randPeer(t, syncMgr.quit)
syncMgr.InitSyncState(peer)
peers = append(peers, peer)
queuedPeers = append(queuedPeers, peer)
}
// Ensure they cannot transition without sending a GossipTimestampRange
// message first.
for _, peer := range queuedPeers {
assertNoMsgSent(t, peer)
}
// assertSyncerTransitioned ensures the target peer's syncer is the only
// that has transitioned.
assertSyncerTransitioned := func(target *mockPeer) {
t.Helper()
for _, peer := range peers {
if peer.PubKey() != target.PubKey() {
assertNoMsgSent(t, peer)
continue
}
assertPassiveSyncerTransition(t, syncMgr, target)
}
}
// We'll then remove the syncers in the middle to cover the case where
// they are queued up in the sync manager's pending list.
for i, peer := range peers {
if i == 0 || i == len(peers)-1 {
continue
}
syncMgr.PruneSyncState(peer.PubKey())
}
// We'll then remove the syncer we are currently waiting for. This
// should prompt the last syncer to start since it is the only one left
// pending. We'll do this in a goroutine since the peer behind the new
// active syncer will need to send out its new GossipTimestampRange.
go syncMgr.PruneSyncState(peers[0].PubKey())
assertSyncerTransitioned(peers[len(peers)-1])
}
// assertNoMsgSent is a helper function that ensures a peer hasn't sent any
// messages.
func assertNoMsgSent(t *testing.T, peer *mockPeer) {
t.Helper()
select {
case msg := <-peer.sentMsgs:
t.Fatalf("peer %x sent unexpected message %v", peer.PubKey(),
spew.Sdump(msg))
case <-time.After(time.Second):
}
}
// assertMsgSent asserts that the peer has sent the given message.
func assertMsgSent(t *testing.T, peer *mockPeer, msg lnwire.Message) {
t.Helper()
var msgSent lnwire.Message
select {
case msgSent = <-peer.sentMsgs:
case <-time.After(time.Second):
t.Fatalf("expected peer %x to send %T message", peer.PubKey(),
msg)
}
if !reflect.DeepEqual(msgSent, msg) {
t.Fatalf("expected peer %x to send message: %v\ngot: %v",
peer.PubKey(), spew.Sdump(msg), spew.Sdump(msgSent))
}
}
// assertActiveGossipTimestampRange is a helper function that ensures a peer has
// sent a lnwire.GossipTimestampRange message indicating that it would like to
// receive new graph updates.
func assertActiveGossipTimestampRange(t *testing.T, peer *mockPeer) {
t.Helper()
var msgSent lnwire.Message
select {
case msgSent = <-peer.sentMsgs:
case <-time.After(time.Second):
t.Fatalf("expected peer %x to send lnwire.GossipTimestampRange "+
"message", peer.PubKey())
}
msg, ok := msgSent.(*lnwire.GossipTimestampRange)
if !ok {
t.Fatalf("expected peer %x to send %T message", peer.PubKey(),
msg)
}
if msg.FirstTimestamp == 0 {
t.Fatalf("expected *lnwire.GossipTimestampRange message with " +
"non-zero FirstTimestamp")
}
if msg.TimestampRange == 0 {
t.Fatalf("expected *lnwire.GossipTimestampRange message with " +
"non-zero TimestampRange")
}
}
// assertSyncerStatus asserts that the gossip syncer for the given peer matches
// the expected sync state and type.
func assertSyncerStatus(t *testing.T, syncMgr *SyncManager, peer *mockPeer,
syncState syncerState, syncType SyncerType) {
t.Helper()
s, ok := syncMgr.GossipSyncer(peer.PubKey())
if !ok {
t.Fatalf("gossip syncer for peer %x not found", peer.PubKey())
}
// We'll check the status of our syncer within a WaitPredicate as some
// sync transitions might cause this to be racy.
err := lntest.WaitNoError(func() error {
state := s.syncState()
if s.syncState() != syncState {
return fmt.Errorf("expected syncState %v for peer "+
"%x, got %v", syncState, peer.PubKey(), state)
}
typ := s.SyncType()
if s.SyncType() != syncType {
return fmt.Errorf("expected syncType %v for peer "+
"%x, got %v", syncType, peer.PubKey(), typ)
}
return nil
}, time.Second)
if err != nil {
t.Fatal(err)
}
}
// assertTransitionToChansSynced asserts the transition of an ActiveSync
// GossipSyncer to its final chansSynced state.
func assertTransitionToChansSynced(t *testing.T, syncMgr *SyncManager,
peer *mockPeer, historicalSync bool) {
t.Helper()
s, ok := syncMgr.GossipSyncer(peer.PubKey())
if !ok {
t.Fatalf("gossip syncer for peer %x not found", peer.PubKey())
}
firstBlockHeight := uint32(startHeight)
if historicalSync {
firstBlockHeight = 0
}
assertMsgSent(t, peer, &lnwire.QueryChannelRange{
FirstBlockHeight: firstBlockHeight,
NumBlocks: math.MaxUint32 - firstBlockHeight,
})
s.ProcessQueryMsg(&lnwire.ReplyChannelRange{Complete: 1}, nil)
chanSeries := syncMgr.cfg.ChanSeries.(*mockChannelGraphTimeSeries)
select {
case <-chanSeries.filterReq:
chanSeries.filterResp <- nil
case <-time.After(2 * time.Second):
t.Fatal("expected to receive FilterKnownChanIDs request")
}
err := lntest.WaitNoError(func() error {
state := syncerState(atomic.LoadUint32(&s.state))
if state != chansSynced {
return fmt.Errorf("expected syncerState %v, got %v",
chansSynced, state)
}
return nil
}, time.Second)
if err != nil {
t.Fatal(err)
}
}
// assertPassiveSyncerTransition asserts that a gossip syncer goes through all
// of its expected steps when transitioning from passive to active.
func assertPassiveSyncerTransition(t *testing.T, syncMgr *SyncManager,
peer *mockPeer) {
t.Helper()
assertActiveGossipTimestampRange(t, peer)
assertTransitionToChansSynced(t, syncMgr, peer, false)
}
// assertActiveSyncerTransition asserts that a gossip syncer goes through all of
// its expected steps when transitioning from active to passive.
func assertActiveSyncerTransition(t *testing.T, syncMgr *SyncManager,
peer *mockPeer) {
t.Helper()
assertMsgSent(t, peer, &lnwire.GossipTimestampRange{
FirstTimestamp: uint32(zeroTimestamp.Unix()),
TimestampRange: 0,
})
assertSyncerStatus(t, syncMgr, peer, chansSynced, PassiveSync)
}

View File

@ -13,18 +13,51 @@ import (
"golang.org/x/time/rate"
)
// syncerState is an enum that represents the current state of the
// gossipSyncer. As the syncer is a state machine, we'll gate our actions
// based off of the current state and the next incoming message.
// SyncerType encapsulates the different types of syncing mechanisms for a
// gossip syncer.
type SyncerType uint8
const (
// ActiveSync denotes that a gossip syncer should exercise its default
// behavior. This includes reconciling the set of missing graph updates
// with the remote peer _and_ receiving new updates from them.
ActiveSync SyncerType = iota
// PassiveSync denotes that a gossip syncer:
//
// 1. Should not attempt to query the remote peer for graph updates.
// 2. Should respond to queries from the remote peer.
// 3. Should not receive new updates from the remote peer.
//
// They are started in a chansSynced state in order to accomplish their
// responsibilities above.
PassiveSync
)
// String returns a human readable string describing the target SyncerType.
func (t SyncerType) String() string {
switch t {
case ActiveSync:
return "ActiveSync"
case PassiveSync:
return "PassiveSync"
default:
return fmt.Sprintf("unknown sync type %d", t)
}
}
// syncerState is an enum that represents the current state of the GossipSyncer.
// As the syncer is a state machine, we'll gate our actions based off of the
// current state and the next incoming message.
type syncerState uint32
const (
// syncingChans is the default state of the gossipSyncer. We start in
// syncingChans is the default state of the GossipSyncer. We start in
// this state when a new peer first connects and we don't yet know if
// we're fully synchronized.
syncingChans syncerState = iota
// waitingQueryRangeReply is the second main phase of the gossipSyncer.
// waitingQueryRangeReply is the second main phase of the GossipSyncer.
// We enter this state after we send out our first QueryChannelRange
// reply. We'll stay in this state until the remote party sends us a
// ReplyShortChanIDsEnd message that indicates they've responded to our
@ -33,19 +66,19 @@ const (
// chan ID's to us.
waitingQueryRangeReply
// queryNewChannels is the third main phase of the gossipSyncer. In
// queryNewChannels is the third main phase of the GossipSyncer. In
// this phase we'll send out all of our QueryShortChanIDs messages in
// response to the new channels that we don't yet know about.
queryNewChannels
// waitingQueryChanReply is the fourth main phase of the gossipSyncer.
// waitingQueryChanReply is the fourth main phase of the GossipSyncer.
// We enter this phase once we've sent off a query chink to the remote
// peer. We'll stay in this phase until we receive a
// ReplyShortChanIDsEnd message which indicates that the remote party
// has responded to all of our requests.
waitingQueryChanReply
// chansSynced is the terminal stage of the gossipSyncer. Once we enter
// chansSynced is the terminal stage of the GossipSyncer. Once we enter
// this phase, we'll send out our update horizon, which filters out the
// set of channel updates that we're interested in. In this state,
// we'll be able to accept any outgoing messages from the
@ -54,17 +87,6 @@ const (
chansSynced
)
const (
// DefaultMaxUndelayedQueryReplies specifies how many gossip queries we
// will respond to immediately before starting to delay responses.
DefaultMaxUndelayedQueryReplies = 10
// DefaultDelayedQueryReplyInterval is the length of time we will wait
// before responding to gossip queries after replying to
// maxUndelayedQueryReplies queries.
DefaultDelayedQueryReplyInterval = 5 * time.Second
)
// String returns a human readable string describing the target syncerState.
func (s syncerState) String() string {
switch s {
@ -88,6 +110,26 @@ func (s syncerState) String() string {
}
}
const (
// DefaultMaxUndelayedQueryReplies specifies how many gossip queries we
// will respond to immediately before starting to delay responses.
DefaultMaxUndelayedQueryReplies = 10
// DefaultDelayedQueryReplyInterval is the length of time we will wait
// before responding to gossip queries after replying to
// maxUndelayedQueryReplies queries.
DefaultDelayedQueryReplyInterval = 5 * time.Second
// chanRangeQueryBuffer is the number of blocks back that we'll go when
// asking the remote peer for their any channels they know of beyond
// our highest known channel ID.
chanRangeQueryBuffer = 144
// syncTransitionTimeout is the default timeout in which we'll wait up
// to when attempting to perform a sync transition.
syncTransitionTimeout = 5 * time.Second
)
var (
// encodingTypeToChunkSize maps an encoding type, to the max number of
// short chan ID's using the encoding type that we can fit into a
@ -98,21 +140,33 @@ var (
// ErrGossipSyncerExiting signals that the syncer has been killed.
ErrGossipSyncerExiting = errors.New("gossip syncer exiting")
// ErrSyncTransitionTimeout is an error returned when we've timed out
// attempting to perform a sync transition.
ErrSyncTransitionTimeout = errors.New("timed out attempting to " +
"transition sync type")
// zeroTimestamp is the timestamp we'll use when we want to indicate to
// peers that we do not want to receive any new graph updates.
zeroTimestamp time.Time
)
const (
// chanRangeQueryBuffer is the number of blocks back that we'll go when
// asking the remote peer for their any channels they know of beyond
// our highest known channel ID.
chanRangeQueryBuffer = 144
)
// syncTransitionReq encapsulates a request for a gossip syncer sync transition.
type syncTransitionReq struct {
newSyncType SyncerType
errChan chan error
}
// gossipSyncerCfg is a struct that packages all the information a gossipSyncer
// gossipSyncerCfg is a struct that packages all the information a GossipSyncer
// needs to carry out its duties.
type gossipSyncerCfg struct {
// chainHash is the chain that this syncer is responsible for.
chainHash chainhash.Hash
// peerPub is the public key of the peer we're syncing with, serialized
// in compressed format.
peerPub [33]byte
// syncChanUpdates is a bool that indicates if we should request a
// continual channel update stream or not.
syncChanUpdates bool
@ -144,18 +198,27 @@ type gossipSyncerCfg struct {
delayedQueryReplyInterval time.Duration
}
// gossipSyncer is a struct that handles synchronizing the channel graph state
// with a remote peer. The gossipSyncer implements a state machine that will
// GossipSyncer is a struct that handles synchronizing the channel graph state
// with a remote peer. The GossipSyncer implements a state machine that will
// progressively ensure we're synchronized with the channel state of the remote
// node. Once both nodes have been synchronized, we'll use an update filter to
// filter out which messages should be sent to a remote peer based on their
// update horizon. If the update horizon isn't specified, then we won't send
// them any channel updates at all.
//
// TODO(roasbeef): modify to only sync from one peer at a time?
type gossipSyncer struct {
started uint32
stopped uint32
type GossipSyncer struct {
started sync.Once
stopped sync.Once
// state is the current state of the GossipSyncer.
//
// NOTE: This variable MUST be used atomically.
state uint32
// syncType denotes the SyncerType the gossip syncer is currently
// exercising.
//
// NOTE: This variable MUST be used atomically.
syncType uint32
// remoteUpdateHorizon is the update horizon of the remote peer. We'll
// use this to properly filter out any messages.
@ -165,10 +228,25 @@ type gossipSyncer struct {
// determine if we've already sent out our update.
localUpdateHorizon *lnwire.GossipTimestampRange
// state is the current state of the gossipSyncer.
//
// NOTE: This variable MUST be used atomically.
state uint32
// syncTransitions is a channel through which new sync type transition
// requests will be sent through. These requests should only be handled
// when the gossip syncer is in a chansSynced state to ensure its state
// machine behaves as expected.
syncTransitionReqs chan *syncTransitionReq
// historicalSyncReqs is a channel that serves as a signal for the
// gossip syncer to perform a historical sync. Theese can only be done
// once the gossip syncer is in a chansSynced state to ensure its state
// machine behaves as expected.
historicalSyncReqs chan struct{}
// genHistoricalChanRangeQuery when true signals to the gossip syncer
// that it should request the remote peer for all of its known channel
// IDs starting from the genesis block of the chain. This can only
// happen if the gossip syncer receives a request to attempt a
// historical sync. It can be unset if the syncer ever transitions from
// PassiveSync to ActiveSync.
genHistoricalChanRangeQuery bool
// gossipMsgs is a channel that all messages from the target peer will
// be sent over.
@ -183,10 +261,6 @@ type gossipSyncer struct {
// state.
newChansToQuery []lnwire.ShortChannelID
// peerPub is the public key of the peer we're syncing with, serialized
// in compressed format.
peerPub [33]byte
cfg gossipSyncerCfg
// rateLimiter dictates the frequency with which we will reply to gossip
@ -195,15 +269,19 @@ type gossipSyncer struct {
// number of queries.
rateLimiter *rate.Limiter
// syncedSignal is a channel that, if set, will be closed when the
// GossipSyncer reaches its terminal chansSynced state.
syncedSignal chan struct{}
sync.Mutex
quit chan struct{}
wg sync.WaitGroup
}
// newGossiperSyncer returns a new instance of the gossipSyncer populated using
// newGossipSyncer returns a new instance of the GossipSyncer populated using
// the passed config.
func newGossiperSyncer(cfg gossipSyncerCfg) *gossipSyncer {
func newGossipSyncer(cfg gossipSyncerCfg) *GossipSyncer {
// If no parameter was specified for max undelayed query replies, set it
// to the default of 5 queries.
if cfg.maxUndelayedQueryReplies <= 0 {
@ -225,57 +303,48 @@ func newGossiperSyncer(cfg gossipSyncerCfg) *gossipSyncer {
interval, cfg.maxUndelayedQueryReplies,
)
return &gossipSyncer{
cfg: cfg,
rateLimiter: rateLimiter,
gossipMsgs: make(chan lnwire.Message, 100),
quit: make(chan struct{}),
return &GossipSyncer{
cfg: cfg,
rateLimiter: rateLimiter,
syncTransitionReqs: make(chan *syncTransitionReq),
historicalSyncReqs: make(chan struct{}),
gossipMsgs: make(chan lnwire.Message, 100),
quit: make(chan struct{}),
}
}
// Start starts the gossipSyncer and any goroutines that it needs to carry out
// Start starts the GossipSyncer and any goroutines that it needs to carry out
// its duties.
func (g *gossipSyncer) Start() error {
if !atomic.CompareAndSwapUint32(&g.started, 0, 1) {
return nil
}
func (g *GossipSyncer) Start() {
g.started.Do(func() {
log.Debugf("Starting GossipSyncer(%x)", g.cfg.peerPub[:])
log.Debugf("Starting gossipSyncer(%x)", g.peerPub[:])
g.wg.Add(1)
go g.channelGraphSyncer()
return nil
g.wg.Add(1)
go g.channelGraphSyncer()
})
}
// Stop signals the gossipSyncer for a graceful exit, then waits until it has
// Stop signals the GossipSyncer for a graceful exit, then waits until it has
// exited.
func (g *gossipSyncer) Stop() error {
if !atomic.CompareAndSwapUint32(&g.stopped, 0, 1) {
return nil
}
close(g.quit)
g.wg.Wait()
return nil
func (g *GossipSyncer) Stop() {
g.stopped.Do(func() {
close(g.quit)
g.wg.Wait()
})
}
// channelGraphSyncer is the main goroutine responsible for ensuring that we
// properly channel graph state with the remote peer, and also that we only
// send them messages which actually pass their defined update horizon.
func (g *gossipSyncer) channelGraphSyncer() {
func (g *GossipSyncer) channelGraphSyncer() {
defer g.wg.Done()
// TODO(roasbeef): also add ability to force transition back to syncing
// chans
// * needed if we want to sync chan state very few blocks?
for {
state := atomic.LoadUint32(&g.state)
log.Debugf("gossipSyncer(%x): state=%v", g.peerPub[:],
syncerState(state))
state := g.syncState()
syncType := g.SyncType()
log.Debugf("GossipSyncer(%x): state=%v, type=%v",
g.cfg.peerPub[:], state, syncType)
switch syncerState(state) {
// When we're in this state, we're trying to synchronize our
@ -286,7 +355,9 @@ func (g *gossipSyncer) channelGraphSyncer() {
case syncingChans:
// If we're in this state, then we'll send the remote
// peer our opening QueryChannelRange message.
queryRangeMsg, err := g.genChanRangeQuery()
queryRangeMsg, err := g.genChanRangeQuery(
g.genHistoricalChanRangeQuery,
)
if err != nil {
log.Errorf("unable to gen chan range "+
"query: %v", err)
@ -302,7 +373,7 @@ func (g *gossipSyncer) channelGraphSyncer() {
// With the message sent successfully, we'll transition
// into the next state where we wait for their reply.
atomic.StoreUint32(&g.state, uint32(waitingQueryRangeReply))
g.setSyncState(waitingQueryRangeReply)
// In this state, we've sent out our initial channel range
// query and are waiting for the final response from the remote
@ -359,13 +430,13 @@ func (g *gossipSyncer) channelGraphSyncer() {
// If this wasn't our last query, then we'll need to
// transition to our waiting state.
if !done {
atomic.StoreUint32(&g.state, uint32(waitingQueryChanReply))
g.setSyncState(waitingQueryChanReply)
continue
}
// If we're fully synchronized, then we can transition
// to our terminal state.
atomic.StoreUint32(&g.state, uint32(chansSynced))
g.setSyncState(chansSynced)
// In this state, we've just sent off a new query for channels
// that we don't yet know of. We'll remain in this state until
@ -382,7 +453,7 @@ func (g *gossipSyncer) channelGraphSyncer() {
// state to send of the remaining query chunks.
_, ok := msg.(*lnwire.ReplyShortChanIDsEnd)
if ok {
atomic.StoreUint32(&g.state, uint32(queryNewChannels))
g.setSyncState(queryNewChannels)
continue
}
@ -401,35 +472,30 @@ func (g *gossipSyncer) channelGraphSyncer() {
// This is our final terminal state where we'll only reply to
// any further queries by the remote peer.
case chansSynced:
g.Lock()
if g.syncedSignal != nil {
close(g.syncedSignal)
g.syncedSignal = nil
}
g.Unlock()
// If we haven't yet sent out our update horizon, and
// we want to receive real-time channel updates, we'll
// do so now.
if g.localUpdateHorizon == nil && g.cfg.syncChanUpdates {
// TODO(roasbeef): query DB for most recent
// update?
// We'll give an hours room in our update
// horizon to ensure we don't miss any newer
// items.
updateHorizon := time.Now().Add(-time.Hour * 1)
log.Infof("gossipSyncer(%x): applying "+
"gossipFilter(start=%v)", g.peerPub[:],
updateHorizon)
g.localUpdateHorizon = &lnwire.GossipTimestampRange{
ChainHash: g.cfg.chainHash,
FirstTimestamp: uint32(updateHorizon.Unix()),
TimestampRange: math.MaxUint32,
}
err := g.cfg.sendToPeer(g.localUpdateHorizon)
if g.localUpdateHorizon == nil && syncType == ActiveSync {
err := g.sendGossipTimestampRange(
time.Now(), math.MaxUint32,
)
if err != nil {
log.Errorf("unable to send update "+
"horizon: %v", err)
log.Errorf("Unable to send update "+
"horizon to %x: %v",
g.cfg.peerPub, err)
}
}
// With our horizon set, we'll simply reply to any new
// message and exit if needed.
// messages or process any state transitions and exit if
// needed.
select {
case msg := <-g.gossipMsgs:
err := g.replyPeerQueries(msg)
@ -438,6 +504,12 @@ func (g *gossipSyncer) channelGraphSyncer() {
"query: %v", err)
}
case req := <-g.syncTransitionReqs:
req.errChan <- g.handleSyncTransition(req)
case <-g.historicalSyncReqs:
g.handleHistoricalSync()
case <-g.quit:
return
}
@ -445,19 +517,50 @@ func (g *gossipSyncer) channelGraphSyncer() {
}
}
// sendGossipTimestampRange constructs and sets a GossipTimestampRange for the
// syncer and sends it to the remote peer.
func (g *GossipSyncer) sendGossipTimestampRange(firstTimestamp time.Time,
timestampRange uint32) error {
endTimestamp := firstTimestamp.Add(
time.Duration(timestampRange) * time.Second,
)
log.Infof("GossipSyncer(%x): applying gossipFilter(start=%v, end=%v)",
g.cfg.peerPub[:], firstTimestamp, endTimestamp)
localUpdateHorizon := &lnwire.GossipTimestampRange{
ChainHash: g.cfg.chainHash,
FirstTimestamp: uint32(firstTimestamp.Unix()),
TimestampRange: timestampRange,
}
if err := g.cfg.sendToPeer(localUpdateHorizon); err != nil {
return err
}
if firstTimestamp == zeroTimestamp && timestampRange == 0 {
g.localUpdateHorizon = nil
} else {
g.localUpdateHorizon = localUpdateHorizon
}
return nil
}
// synchronizeChanIDs is called by the channelGraphSyncer when we need to query
// the remote peer for its known set of channel IDs within a particular block
// range. This method will be called continually until the entire range has
// been queried for with a response received. We'll chunk our requests as
// required to ensure they fit into a single message. We may re-renter this
// state in the case that chunking is required.
func (g *gossipSyncer) synchronizeChanIDs() (bool, error) {
func (g *GossipSyncer) synchronizeChanIDs() (bool, error) {
// If we're in this state yet there are no more new channels to query
// for, then we'll transition to our final synced state and return true
// to signal that we're fully synchronized.
if len(g.newChansToQuery) == 0 {
log.Infof("gossipSyncer(%x): no more chans to query",
g.peerPub[:])
log.Infof("GossipSyncer(%x): no more chans to query",
g.cfg.peerPub[:])
return true, nil
}
@ -479,8 +582,8 @@ func (g *gossipSyncer) synchronizeChanIDs() (bool, error) {
g.newChansToQuery = g.newChansToQuery[g.cfg.chunkSize:]
}
log.Infof("gossipSyncer(%x): querying for %v new channels",
g.peerPub[:], len(queryChunk))
log.Infof("GossipSyncer(%x): querying for %v new channels",
g.cfg.peerPub[:], len(queryChunk))
// With our chunk obtained, we'll send over our next query, then return
// false indicating that we're net yet fully synced.
@ -493,16 +596,16 @@ func (g *gossipSyncer) synchronizeChanIDs() (bool, error) {
return false, err
}
// processChanRangeReply is called each time the gossipSyncer receives a new
// processChanRangeReply is called each time the GossipSyncer receives a new
// reply to the initial range query to discover new channels that it didn't
// previously know of.
func (g *gossipSyncer) processChanRangeReply(msg *lnwire.ReplyChannelRange) error {
func (g *GossipSyncer) processChanRangeReply(msg *lnwire.ReplyChannelRange) error {
g.bufferedChanRangeReplies = append(
g.bufferedChanRangeReplies, msg.ShortChanIDs...,
)
log.Infof("gossipSyncer(%x): buffering chan range reply of size=%v",
g.peerPub[:], len(msg.ShortChanIDs))
log.Infof("GossipSyncer(%x): buffering chan range reply of size=%v",
g.cfg.peerPub[:], len(msg.ShortChanIDs))
// If this isn't the last response, then we can exit as we've already
// buffered the latest portion of the streaming reply.
@ -510,8 +613,8 @@ func (g *gossipSyncer) processChanRangeReply(msg *lnwire.ReplyChannelRange) erro
return nil
}
log.Infof("gossipSyncer(%x): filtering through %v chans", g.peerPub[:],
len(g.bufferedChanRangeReplies))
log.Infof("GossipSyncer(%x): filtering through %v chans",
g.cfg.peerPub[:], len(g.bufferedChanRangeReplies))
// Otherwise, this is the final response, so we'll now check to see
// which channels they know of that we don't.
@ -530,28 +633,31 @@ func (g *gossipSyncer) processChanRangeReply(msg *lnwire.ReplyChannelRange) erro
// If there aren't any channels that we don't know of, then we can
// switch straight to our terminal state.
if len(newChans) == 0 {
log.Infof("gossipSyncer(%x): remote peer has no new chans",
g.peerPub[:])
log.Infof("GossipSyncer(%x): remote peer has no new chans",
g.cfg.peerPub[:])
atomic.StoreUint32(&g.state, uint32(chansSynced))
g.setSyncState(chansSynced)
return nil
}
// Otherwise, we'll set the set of channels that we need to query for
// the next state, and also transition our state.
g.newChansToQuery = newChans
atomic.StoreUint32(&g.state, uint32(queryNewChannels))
g.setSyncState(queryNewChannels)
log.Infof("gossipSyncer(%x): starting query for %v new chans",
g.peerPub[:], len(newChans))
log.Infof("GossipSyncer(%x): starting query for %v new chans",
g.cfg.peerPub[:], len(newChans))
return nil
}
// genChanRangeQuery generates the initial message we'll send to the remote
// party when we're kicking off the channel graph synchronization upon
// connection.
func (g *gossipSyncer) genChanRangeQuery() (*lnwire.QueryChannelRange, error) {
// connection. The historicalQuery boolean can be used to generate a query from
// the genesis block of the chain.
func (g *GossipSyncer) genChanRangeQuery(
historicalQuery bool) (*lnwire.QueryChannelRange, error) {
// First, we'll query our channel graph time series for its highest
// known channel ID.
newestChan, err := g.cfg.channelSeries.HighestChanID(g.cfg.chainHash)
@ -559,23 +665,23 @@ func (g *gossipSyncer) genChanRangeQuery() (*lnwire.QueryChannelRange, error) {
return nil, err
}
// Once we have the chan ID of the newest, we'll obtain the block
// height of the channel, then subtract our default horizon to ensure
// we don't miss any channels. By default, we go back 1 day from the
// newest channel.
// Once we have the chan ID of the newest, we'll obtain the block height
// of the channel, then subtract our default horizon to ensure we don't
// miss any channels. By default, we go back 1 day from the newest
// channel, unless we're attempting a historical sync, where we'll
// actually start from the genesis block instead.
var startHeight uint32
switch {
case newestChan.BlockHeight <= chanRangeQueryBuffer:
case historicalQuery:
fallthrough
case newestChan.BlockHeight == 0:
case newestChan.BlockHeight <= chanRangeQueryBuffer:
startHeight = 0
default:
startHeight = uint32(newestChan.BlockHeight - chanRangeQueryBuffer)
}
log.Infof("gossipSyncer(%x): requesting new chans from height=%v "+
"and %v blocks after", g.peerPub[:], startHeight,
log.Infof("GossipSyncer(%x): requesting new chans from height=%v "+
"and %v blocks after", g.cfg.peerPub[:], startHeight,
math.MaxUint32-startHeight)
// Finally, we'll craft the channel range query, using our starting
@ -590,7 +696,7 @@ func (g *gossipSyncer) genChanRangeQuery() (*lnwire.QueryChannelRange, error) {
// replyPeerQueries is called in response to any query by the remote peer.
// We'll examine our state and send back our best response.
func (g *gossipSyncer) replyPeerQueries(msg lnwire.Message) error {
func (g *GossipSyncer) replyPeerQueries(msg lnwire.Message) error {
reservation := g.rateLimiter.Reserve()
delay := reservation.Delay()
@ -598,8 +704,8 @@ func (g *gossipSyncer) replyPeerQueries(msg lnwire.Message) error {
// responses back to the remote peer. This can help prevent DOS attacks
// where the remote peer spams us endlessly.
if delay > 0 {
log.Infof("gossipSyncer(%x): rate limiting gossip replies, "+
"responding in %s", g.peerPub[:], delay)
log.Infof("GossipSyncer(%x): rate limiting gossip replies, "+
"responding in %s", g.cfg.peerPub[:], delay)
select {
case <-time.After(delay):
@ -630,9 +736,9 @@ func (g *gossipSyncer) replyPeerQueries(msg lnwire.Message) error {
// meet the channel range, then chunk our responses to the remote node. We also
// ensure that our final fragment carries the "complete" bit to indicate the
// end of our streaming response.
func (g *gossipSyncer) replyChanRangeQuery(query *lnwire.QueryChannelRange) error {
log.Infof("gossipSyncer(%x): filtering chan range: start_height=%v, "+
"num_blocks=%v", g.peerPub[:], query.FirstBlockHeight,
func (g *GossipSyncer) replyChanRangeQuery(query *lnwire.QueryChannelRange) error {
log.Infof("GossipSyncer(%x): filtering chan range: start_height=%v, "+
"num_blocks=%v", g.cfg.peerPub[:], query.FirstBlockHeight,
query.NumBlocks)
// Next, we'll consult the time series to obtain the set of known
@ -666,16 +772,16 @@ func (g *gossipSyncer) replyChanRangeQuery(query *lnwire.QueryChannelRange) erro
if isFinalChunk {
channelChunk = channelRange[numChansSent:]
log.Infof("gossipSyncer(%x): sending final chan "+
"range chunk, size=%v", g.peerPub[:], len(channelChunk))
log.Infof("GossipSyncer(%x): sending final chan "+
"range chunk, size=%v", g.cfg.peerPub[:],
len(channelChunk))
} else {
// Otherwise, we'll only send off a fragment exactly
// sized to the proper chunk size.
channelChunk = channelRange[numChansSent : numChansSent+g.cfg.chunkSize]
log.Infof("gossipSyncer(%x): sending range chunk of "+
"size=%v", g.peerPub[:], len(channelChunk))
log.Infof("GossipSyncer(%x): sending range chunk of "+
"size=%v", g.cfg.peerPub[:], len(channelChunk))
}
// With our chunk assembled, we'll now send to the remote peer
@ -707,7 +813,7 @@ func (g *gossipSyncer) replyChanRangeQuery(query *lnwire.QueryChannelRange) erro
// node for information concerning a set of short channel ID's. Our response
// will be sent in a streaming chunked manner to ensure that we remain below
// the current transport level message size.
func (g *gossipSyncer) replyShortChanIDs(query *lnwire.QueryShortChanIDs) error {
func (g *GossipSyncer) replyShortChanIDs(query *lnwire.QueryShortChanIDs) error {
// Before responding, we'll check to ensure that the remote peer is
// querying for the same chain that we're on. If not, we'll send back a
// response with a complete value of zero to indicate we're on a
@ -724,13 +830,13 @@ func (g *gossipSyncer) replyShortChanIDs(query *lnwire.QueryShortChanIDs) error
}
if len(query.ShortChanIDs) == 0 {
log.Infof("gossipSyncer(%x): ignoring query for blank short chan ID's",
g.peerPub[:])
log.Infof("GossipSyncer(%x): ignoring query for blank short chan ID's",
g.cfg.peerPub[:])
return nil
}
log.Infof("gossipSyncer(%x): fetching chan anns for %v chans",
g.peerPub[:], len(query.ShortChanIDs))
log.Infof("GossipSyncer(%x): fetching chan anns for %v chans",
g.cfg.peerPub[:], len(query.ShortChanIDs))
// Now that we know we're on the same chain, we'll query the channel
// time series for the set of messages that we know of which satisfies
@ -766,7 +872,7 @@ func (g *gossipSyncer) replyShortChanIDs(query *lnwire.QueryShortChanIDs) error
// ApplyGossipFilter applies a gossiper filter sent by the remote node to the
// state machine. Once applied, we'll ensure that we don't forward any messages
// to the peer that aren't within the time range of the filter.
func (g *gossipSyncer) ApplyGossipFilter(filter *lnwire.GossipTimestampRange) error {
func (g *GossipSyncer) ApplyGossipFilter(filter *lnwire.GossipTimestampRange) error {
g.Lock()
g.remoteUpdateHorizon = filter
@ -787,8 +893,8 @@ func (g *gossipSyncer) ApplyGossipFilter(filter *lnwire.GossipTimestampRange) er
return err
}
log.Infof("gossipSyncer(%x): applying new update horizon: start=%v, "+
"end=%v, backlog_size=%v", g.peerPub[:], startTime, endTime,
log.Infof("GossipSyncer(%x): applying new update horizon: start=%v, "+
"end=%v, backlog_size=%v", g.cfg.peerPub[:], startTime, endTime,
len(newUpdatestoSend))
// If we don't have any to send, then we can return early.
@ -813,17 +919,19 @@ func (g *gossipSyncer) ApplyGossipFilter(filter *lnwire.GossipTimestampRange) er
// FilterGossipMsgs takes a set of gossip messages, and only send it to a peer
// iff the message is within the bounds of their set gossip filter. If the peer
// doesn't have a gossip filter set, then no messages will be forwarded.
func (g *gossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) {
func (g *GossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) {
// If the peer doesn't have an update horizon set, then we won't send
// it any new update messages.
if g.remoteUpdateHorizon == nil {
return
}
// If we've been signalled to exit, or are exiting, then we'll stop
// If we've been signaled to exit, or are exiting, then we'll stop
// short.
if atomic.LoadUint32(&g.stopped) == 1 {
select {
case <-g.quit:
return
default:
}
// TODO(roasbeef): need to ensure that peer still online...send msg to
@ -858,7 +966,8 @@ func (g *gossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) {
passesFilter := func(timeStamp uint32) bool {
t := time.Unix(int64(timeStamp), 0)
return t.After(startTime) && t.Before(endTime)
return t.Equal(startTime) ||
(t.After(startTime) && t.Before(endTime))
}
msgsToSend := make([]lnwire.Message, 0, len(msgs))
@ -866,7 +975,7 @@ func (g *gossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) {
// If the target peer is the peer that sent us this message,
// then we'll exit early as we don't need to filter this
// message.
if _, ok := msg.senders[g.peerPub]; ok {
if _, ok := msg.senders[g.cfg.peerPub]; ok {
continue
}
@ -920,8 +1029,8 @@ func (g *gossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) {
}
}
log.Tracef("gossipSyncer(%x): filtered gossip msgs: set=%v, sent=%v",
g.peerPub[:], len(msgs), len(msgsToSend))
log.Tracef("GossipSyncer(%x): filtered gossip msgs: set=%v, sent=%v",
g.cfg.peerPub[:], len(msgs), len(msgsToSend))
if len(msgsToSend) == 0 {
return
@ -932,7 +1041,7 @@ func (g *gossipSyncer) FilterGossipMsgs(msgs ...msgWithSenders) {
// ProcessQueryMsg is used by outside callers to pass new channel time series
// queries to the internal processing goroutine.
func (g *gossipSyncer) ProcessQueryMsg(msg lnwire.Message, peerQuit <-chan struct{}) {
func (g *GossipSyncer) ProcessQueryMsg(msg lnwire.Message, peerQuit <-chan struct{}) {
select {
case g.gossipMsgs <- msg:
case <-peerQuit:
@ -940,7 +1049,149 @@ func (g *gossipSyncer) ProcessQueryMsg(msg lnwire.Message, peerQuit <-chan struc
}
}
// SyncerState returns the current syncerState of the target gossipSyncer.
func (g *gossipSyncer) SyncState() syncerState {
// setSyncState sets the gossip syncer's state to the given state.
func (g *GossipSyncer) setSyncState(state syncerState) {
atomic.StoreUint32(&g.state, uint32(state))
}
// syncState returns the current syncerState of the target GossipSyncer.
func (g *GossipSyncer) syncState() syncerState {
return syncerState(atomic.LoadUint32(&g.state))
}
// ResetSyncedSignal returns a channel that will be closed in order to serve as
// a signal for when the GossipSyncer has reached its chansSynced state.
func (g *GossipSyncer) ResetSyncedSignal() chan struct{} {
g.Lock()
defer g.Unlock()
syncedSignal := make(chan struct{})
syncState := syncerState(atomic.LoadUint32(&g.state))
if syncState == chansSynced {
close(syncedSignal)
return syncedSignal
}
g.syncedSignal = syncedSignal
return g.syncedSignal
}
// ProcessSyncTransition sends a request to the gossip syncer to transition its
// sync type to a new one.
//
// NOTE: This can only be done once the gossip syncer has reached its final
// chansSynced state.
func (g *GossipSyncer) ProcessSyncTransition(newSyncType SyncerType) error {
errChan := make(chan error, 1)
select {
case g.syncTransitionReqs <- &syncTransitionReq{
newSyncType: newSyncType,
errChan: errChan,
}:
case <-time.After(syncTransitionTimeout):
return ErrSyncTransitionTimeout
case <-g.quit:
return ErrGossipSyncerExiting
}
select {
case err := <-errChan:
return err
case <-g.quit:
return ErrGossipSyncerExiting
}
}
// handleSyncTransition handles a new sync type transition request.
//
// NOTE: The gossip syncer might have another sync state as a result of this
// transition.
func (g *GossipSyncer) handleSyncTransition(req *syncTransitionReq) error {
// Return early from any NOP sync transitions.
syncType := g.SyncType()
if syncType == req.newSyncType {
return nil
}
log.Debugf("GossipSyncer(%x): transitioning from %v to %v",
g.cfg.peerPub, syncType, req.newSyncType)
var (
firstTimestamp time.Time
timestampRange uint32
newState syncerState
)
switch req.newSyncType {
// If an active sync has been requested, then we should resume receiving
// new graph updates from the remote peer.
case ActiveSync:
firstTimestamp = time.Now()
timestampRange = math.MaxUint32
newState = syncingChans
// We'll set genHistoricalChanRangeQuery to false since in order
// to not perform another historical sync if we previously have.
g.genHistoricalChanRangeQuery = false
// If a PassiveSync transition has been requested, then we should no
// longer receive any new updates from the remote peer. We can do this
// by setting our update horizon to a range in the past ensuring no
// graph updates match the timestamp range.
case PassiveSync:
firstTimestamp = zeroTimestamp
timestampRange = 0
newState = chansSynced
default:
return fmt.Errorf("unhandled sync transition %v",
req.newSyncType)
}
err := g.sendGossipTimestampRange(firstTimestamp, timestampRange)
if err != nil {
return fmt.Errorf("unable to send local update horizon: %v", err)
}
g.setSyncState(newState)
g.setSyncType(req.newSyncType)
return nil
}
// setSyncType sets the gossip syncer's sync type to the given type.
func (g *GossipSyncer) setSyncType(syncType SyncerType) {
atomic.StoreUint32(&g.syncType, uint32(syncType))
}
// SyncType returns the current SyncerType of the target GossipSyncer.
func (g *GossipSyncer) SyncType() SyncerType {
return SyncerType(atomic.LoadUint32(&g.syncType))
}
// historicalSync sends a request to the gossip syncer to perofmr a historical
// sync.
//
// NOTE: This can only be done once the gossip syncer has reached its final
// chansSynced state.
func (g *GossipSyncer) historicalSync() error {
select {
case g.historicalSyncReqs <- struct{}{}:
return nil
case <-time.After(syncTransitionTimeout):
return ErrSyncTransitionTimeout
case <-g.quit:
return ErrGossiperShuttingDown
}
}
// handleHistoricalSync handles a request to the gossip syncer to perform a
// historical sync.
func (g *GossipSyncer) handleHistoricalSync() {
// We'll go back to our initial syncingChans state in order to request
// the remote peer to give us all of the channel IDs they know of
// starting from the genesis block.
g.genHistoricalChanRangeQuery = true
g.setSyncState(syncingChans)
}

View File

@ -13,7 +13,9 @@ import (
)
const (
defaultEncoding = lnwire.EncodingSortedPlain
defaultEncoding = lnwire.EncodingSortedPlain
latestKnownHeight = 1337
startHeight = latestKnownHeight - chanRangeQueryBuffer
)
var (
@ -116,21 +118,20 @@ var _ ChannelGraphTimeSeries = (*mockChannelGraphTimeSeries)(nil)
func newTestSyncer(hID lnwire.ShortChannelID,
encodingType lnwire.ShortChanIDEncoding, chunkSize int32,
) (chan []lnwire.Message, *gossipSyncer, *mockChannelGraphTimeSeries) {
) (chan []lnwire.Message, *GossipSyncer, *mockChannelGraphTimeSeries) {
msgChan := make(chan []lnwire.Message, 20)
cfg := gossipSyncerCfg{
syncChanUpdates: true,
channelSeries: newMockChannelGraphTimeSeries(hID),
encodingType: encodingType,
chunkSize: chunkSize,
channelSeries: newMockChannelGraphTimeSeries(hID),
encodingType: encodingType,
chunkSize: chunkSize,
sendToPeer: func(msgs ...lnwire.Message) error {
msgChan <- msgs
return nil
},
delayedQueryReplyInterval: 2 * time.Second,
}
syncer := newGossiperSyncer(cfg)
syncer := newGossipSyncer(cfg)
return msgChan, syncer, cfg.channelSeries.(*mockChannelGraphTimeSeries)
}
@ -140,7 +141,7 @@ func newTestSyncer(hID lnwire.ShortChannelID,
func TestGossipSyncerFilterGossipMsgsNoHorizon(t *testing.T) {
t.Parallel()
// First, we'll create a gossipSyncer instance with a canned sendToPeer
// First, we'll create a GossipSyncer instance with a canned sendToPeer
// message to allow us to intercept their potential sends.
msgChan, syncer, _ := newTestSyncer(
lnwire.NewShortChanIDFromInt(10), defaultEncoding,
@ -185,7 +186,7 @@ func unixStamp(a int64) uint32 {
func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) {
t.Parallel()
// First, we'll create a gossipSyncer instance with a canned sendToPeer
// First, we'll create a GossipSyncer instance with a canned sendToPeer
// message to allow us to intercept their potential sends.
msgChan, syncer, chanSeries := newTestSyncer(
lnwire.NewShortChanIDFromInt(10), defaultEncoding,
@ -315,7 +316,7 @@ func TestGossipSyncerFilterGossipMsgsAllInMemory(t *testing.T) {
func TestGossipSyncerApplyGossipFilter(t *testing.T) {
t.Parallel()
// First, we'll create a gossipSyncer instance with a canned sendToPeer
// First, we'll create a GossipSyncer instance with a canned sendToPeer
// message to allow us to intercept their potential sends.
msgChan, syncer, chanSeries := newTestSyncer(
lnwire.NewShortChanIDFromInt(10), defaultEncoding,
@ -413,7 +414,7 @@ func TestGossipSyncerApplyGossipFilter(t *testing.T) {
func TestGossipSyncerReplyShortChanIDsWrongChainHash(t *testing.T) {
t.Parallel()
// First, we'll create a gossipSyncer instance with a canned sendToPeer
// First, we'll create a GossipSyncer instance with a canned sendToPeer
// message to allow us to intercept their potential sends.
msgChan, syncer, _ := newTestSyncer(
lnwire.NewShortChanIDFromInt(10), defaultEncoding,
@ -464,7 +465,7 @@ func TestGossipSyncerReplyShortChanIDsWrongChainHash(t *testing.T) {
func TestGossipSyncerReplyShortChanIDs(t *testing.T) {
t.Parallel()
// First, we'll create a gossipSyncer instance with a canned sendToPeer
// First, we'll create a GossipSyncer instance with a canned sendToPeer
// message to allow us to intercept their potential sends.
msgChan, syncer, chanSeries := newTestSyncer(
lnwire.NewShortChanIDFromInt(10), defaultEncoding,
@ -718,7 +719,7 @@ func TestGossipSyncerReplyChanRangeQueryNoNewChans(t *testing.T) {
func TestGossipSyncerGenChanRangeQuery(t *testing.T) {
t.Parallel()
// First, we'll create a gossipSyncer instance with a canned sendToPeer
// First, we'll create a GossipSyncer instance with a canned sendToPeer
// message to allow us to intercept their potential sends.
const startingHeight = 200
_, syncer, _ := newTestSyncer(
@ -729,7 +730,7 @@ func TestGossipSyncerGenChanRangeQuery(t *testing.T) {
// If we now ask the syncer to generate an initial range query, it
// should return a start height that's back chanRangeQueryBuffer
// blocks.
rangeQuery, err := syncer.genChanRangeQuery()
rangeQuery, err := syncer.genChanRangeQuery(false)
if err != nil {
t.Fatalf("unable to resp: %v", err)
}
@ -742,7 +743,22 @@ func TestGossipSyncerGenChanRangeQuery(t *testing.T) {
}
if rangeQuery.NumBlocks != math.MaxUint32-firstHeight {
t.Fatalf("wrong num blocks: expected %v, got %v",
rangeQuery.NumBlocks, math.MaxUint32-firstHeight)
math.MaxUint32-firstHeight, rangeQuery.NumBlocks)
}
// Generating a historical range query should result in a start height
// of 0.
rangeQuery, err = syncer.genChanRangeQuery(true)
if err != nil {
t.Fatalf("unable to resp: %v", err)
}
if rangeQuery.FirstBlockHeight != 0 {
t.Fatalf("incorrect chan range query: expected %v, %v", 0,
rangeQuery.FirstBlockHeight)
}
if rangeQuery.NumBlocks != math.MaxUint32 {
t.Fatalf("wrong num blocks: expected %v, got %v",
math.MaxUint32, rangeQuery.NumBlocks)
}
}
@ -753,7 +769,7 @@ func TestGossipSyncerGenChanRangeQuery(t *testing.T) {
func TestGossipSyncerProcessChanRangeReply(t *testing.T) {
t.Parallel()
// First, we'll create a gossipSyncer instance with a canned sendToPeer
// First, we'll create a GossipSyncer instance with a canned sendToPeer
// message to allow us to intercept their potential sends.
_, syncer, chanSeries := newTestSyncer(
lnwire.NewShortChanIDFromInt(10), defaultEncoding, defaultChunkSize,
@ -827,7 +843,7 @@ func TestGossipSyncerProcessChanRangeReply(t *testing.T) {
t.Fatalf("unable to process reply: %v", err)
}
if syncer.SyncState() != queryNewChannels {
if syncer.syncState() != queryNewChannels {
t.Fatalf("wrong state: expected %v instead got %v",
queryNewChannels, syncer.state)
}
@ -860,7 +876,7 @@ func TestGossipSyncerProcessChanRangeReply(t *testing.T) {
t.Fatalf("unable to process reply: %v", err)
}
if syncer.SyncState() != chansSynced {
if syncer.syncState() != chansSynced {
t.Fatalf("wrong state: expected %v instead got %v",
chansSynced, syncer.state)
}
@ -878,7 +894,7 @@ func TestGossipSyncerSynchronizeChanIDs(t *testing.T) {
// queries: two full chunks, and one lingering chunk.
const chunkSize = 2
// First, we'll create a gossipSyncer instance with a canned sendToPeer
// First, we'll create a GossipSyncer instance with a canned sendToPeer
// message to allow us to intercept their potential sends.
msgChan, syncer, _ := newTestSyncer(
lnwire.NewShortChanIDFromInt(10), defaultEncoding, chunkSize,
@ -997,7 +1013,7 @@ func TestGossipSyncerDelayDOS(t *testing.T) {
const numDelayedQueries = 2
const delayTolerance = time.Millisecond * 200
// First, we'll create two gossipSyncer instances with a canned
// First, we'll create two GossipSyncer instances with a canned
// sendToPeer message to allow us to intercept their potential sends.
startHeight := lnwire.ShortChannelID{
BlockHeight: 1144,
@ -1390,7 +1406,7 @@ func TestGossipSyncerRoutineSync(t *testing.T) {
// queries: two full chunks, and one lingering chunk.
const chunkSize = 2
// First, we'll create two gossipSyncer instances with a canned
// First, we'll create two GossipSyncer instances with a canned
// sendToPeer message to allow us to intercept their potential sends.
startHeight := lnwire.ShortChannelID{
BlockHeight: 1144,
@ -1730,7 +1746,7 @@ func TestGossipSyncerAlreadySynced(t *testing.T) {
// queries: two full chunks, and one lingering chunk.
const chunkSize = 2
// First, we'll create two gossipSyncer instances with a canned
// First, we'll create two GossipSyncer instances with a canned
// sendToPeer message to allow us to intercept their potential sends.
startHeight := lnwire.ShortChannelID{
BlockHeight: 1144,
@ -1941,3 +1957,236 @@ func TestGossipSyncerAlreadySynced(t *testing.T) {
}
}
}
// TestGossipSyncerSyncTransitions ensures that the gossip syncer properly
// carries out its duties when accepting a new sync transition request.
func TestGossipSyncerSyncTransitions(t *testing.T) {
t.Parallel()
assertMsgSent := func(t *testing.T, msgChan chan []lnwire.Message,
msg lnwire.Message) {
t.Helper()
var msgSent lnwire.Message
select {
case msgs := <-msgChan:
if len(msgs) != 1 {
t.Fatal("expected to send a single message at "+
"a time, got %d", len(msgs))
}
msgSent = msgs[0]
case <-time.After(time.Second):
t.Fatalf("expected to send %T message", msg)
}
if !reflect.DeepEqual(msgSent, msg) {
t.Fatalf("expected to send message: %v\ngot: %v",
spew.Sdump(msg), spew.Sdump(msgSent))
}
}
tests := []struct {
name string
entrySyncType SyncerType
finalSyncType SyncerType
assert func(t *testing.T, msgChan chan []lnwire.Message,
syncer *GossipSyncer)
}{
{
name: "active to passive",
entrySyncType: ActiveSync,
finalSyncType: PassiveSync,
assert: func(t *testing.T, msgChan chan []lnwire.Message,
g *GossipSyncer) {
// When transitioning from active to passive, we
// should expect to see a new local update
// horizon sent to the remote peer indicating
// that it would not like to receive any future
// updates.
assertMsgSent(t, msgChan, &lnwire.GossipTimestampRange{
FirstTimestamp: uint32(zeroTimestamp.Unix()),
TimestampRange: 0,
})
syncState := g.syncState()
if syncState != chansSynced {
t.Fatalf("expected syncerState %v, "+
"got %v", chansSynced,
syncState)
}
},
},
{
name: "passive to active",
entrySyncType: PassiveSync,
finalSyncType: ActiveSync,
assert: func(t *testing.T, msgChan chan []lnwire.Message,
g *GossipSyncer) {
// When transitioning from historical to active,
// we should expect to see a new local update
// horizon sent to the remote peer indicating
// that it would like to receive any future
// updates.
firstTimestamp := uint32(time.Now().Unix())
assertMsgSent(t, msgChan, &lnwire.GossipTimestampRange{
FirstTimestamp: firstTimestamp,
TimestampRange: math.MaxUint32,
})
// The local update horizon should be followed
// by a QueryChannelRange message sent to the
// remote peer requesting all channels it
// knows of from the highest height the syncer
// knows of.
assertMsgSent(t, msgChan, &lnwire.QueryChannelRange{
FirstBlockHeight: startHeight,
NumBlocks: math.MaxUint32 - startHeight,
})
syncState := g.syncState()
if syncState != waitingQueryRangeReply {
t.Fatalf("expected syncerState %v, "+
"got %v", waitingQueryRangeReply,
syncState)
}
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
t.Parallel()
// We'll start each test by creating our syncer. We'll
// initialize it with a state of chansSynced, as that's
// the only time when it can process sync transitions.
msgChan, syncer, _ := newTestSyncer(
lnwire.ShortChannelID{
BlockHeight: latestKnownHeight,
},
defaultEncoding, defaultChunkSize,
)
syncer.setSyncState(chansSynced)
// We'll set the initial syncType to what the test
// demands.
syncer.setSyncType(test.entrySyncType)
// We'll then start the syncer in order to process the
// request.
syncer.Start()
defer syncer.Stop()
syncer.ProcessSyncTransition(test.finalSyncType)
// The syncer should now have the expected final
// SyncerType that the test expects.
syncType := syncer.SyncType()
if syncType != test.finalSyncType {
t.Fatalf("expected syncType %v, got %v",
test.finalSyncType, syncType)
}
// Finally, we'll run a set of assertions for each test
// to ensure the syncer performed its expected duties
// after processing its sync transition.
test.assert(t, msgChan, syncer)
})
}
}
// TestGossipSyncerHistoricalSync tests that a gossip syncer can perform a
// historical sync with the remote peer.
func TestGossipSyncerHistoricalSync(t *testing.T) {
t.Parallel()
// We'll create a new gossip syncer and manually override its state to
// chansSynced. This is necessary as the syncer can only process
// historical sync requests in this state.
msgChan, syncer, _ := newTestSyncer(
lnwire.ShortChannelID{BlockHeight: latestKnownHeight},
defaultEncoding, defaultChunkSize,
)
syncer.setSyncType(PassiveSync)
syncer.setSyncState(chansSynced)
syncer.Start()
defer syncer.Stop()
syncer.historicalSync()
// We should expect to see a single lnwire.QueryChannelRange message be
// sent to the remote peer with a FirstBlockHeight of 0.
expectedMsg := &lnwire.QueryChannelRange{
FirstBlockHeight: 0,
NumBlocks: math.MaxUint32,
}
select {
case msgs := <-msgChan:
if len(msgs) != 1 {
t.Fatalf("expected to send a single "+
"lnwire.QueryChannelRange message, got %d",
len(msgs))
}
if !reflect.DeepEqual(msgs[0], expectedMsg) {
t.Fatalf("expected to send message: %v\ngot: %v",
spew.Sdump(expectedMsg), spew.Sdump(msgs[0]))
}
case <-time.After(time.Second):
t.Fatalf("expected to send a lnwire.QueryChannelRange message")
}
}
// TestGossipSyncerSyncedSignal ensures that we receive a signal when a gossip
// syncer reaches its terminal chansSynced state.
func TestGossipSyncerSyncedSignal(t *testing.T) {
t.Parallel()
// We'll create a new gossip syncer and manually override its state to
// chansSynced.
_, syncer, _ := newTestSyncer(
lnwire.NewShortChanIDFromInt(10), defaultEncoding,
defaultChunkSize,
)
syncer.setSyncState(chansSynced)
// We'll go ahead and request a signal to be notified of when it reaches
// this state.
signalChan := syncer.ResetSyncedSignal()
// Starting the gossip syncer should cause the signal to be delivered.
syncer.Start()
select {
case <-signalChan:
case <-time.After(time.Second):
t.Fatal("expected to receive chansSynced signal")
}
syncer.Stop()
// We'll try this again, but this time we'll request the signal after
// the syncer is active and has already reached its chansSynced state.
_, syncer, _ = newTestSyncer(
lnwire.NewShortChanIDFromInt(10), defaultEncoding,
defaultChunkSize,
)
syncer.setSyncState(chansSynced)
syncer.Start()
defer syncer.Stop()
signalChan = syncer.ResetSyncedSignal()
// The signal should be delivered immediately.
select {
case <-signalChan:
case <-time.After(time.Second):
t.Fatal("expected to receive chansSynced signal")
}
}

View File

@ -55,7 +55,7 @@ func (x AddressType) String() string {
return proto.EnumName(AddressType_name, int32(x))
}
func (AddressType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{0}
return fileDescriptor_rpc_854431eb46daab93, []int{0}
}
type ChannelCloseSummary_ClosureType int32
@ -90,7 +90,39 @@ func (x ChannelCloseSummary_ClosureType) String() string {
return proto.EnumName(ChannelCloseSummary_ClosureType_name, int32(x))
}
func (ChannelCloseSummary_ClosureType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{41, 0}
return fileDescriptor_rpc_854431eb46daab93, []int{41, 0}
}
type Peer_SyncType int32
const (
// *
// Denotes that we cannot determine the peer's current sync type.
Peer_UNKNOWN_SYNC Peer_SyncType = 0
// *
// Denotes that we are actively receiving new graph updates from the peer.
Peer_ACTIVE_SYNC Peer_SyncType = 1
// *
// Denotes that we are not receiving new graph updates from the peer.
Peer_PASSIVE_SYNC Peer_SyncType = 2
)
var Peer_SyncType_name = map[int32]string{
0: "UNKNOWN_SYNC",
1: "ACTIVE_SYNC",
2: "PASSIVE_SYNC",
}
var Peer_SyncType_value = map[string]int32{
"UNKNOWN_SYNC": 0,
"ACTIVE_SYNC": 1,
"PASSIVE_SYNC": 2,
}
func (x Peer_SyncType) String() string {
return proto.EnumName(Peer_SyncType_name, int32(x))
}
func (Peer_SyncType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_rpc_854431eb46daab93, []int{44, 0}
}
type ChannelEventUpdate_UpdateType int32
@ -119,7 +151,7 @@ func (x ChannelEventUpdate_UpdateType) String() string {
return proto.EnumName(ChannelEventUpdate_UpdateType_name, int32(x))
}
func (ChannelEventUpdate_UpdateType) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{62, 0}
return fileDescriptor_rpc_854431eb46daab93, []int{62, 0}
}
type Invoice_InvoiceState int32
@ -148,7 +180,7 @@ func (x Invoice_InvoiceState) String() string {
return proto.EnumName(Invoice_InvoiceState_name, int32(x))
}
func (Invoice_InvoiceState) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{92, 0}
return fileDescriptor_rpc_854431eb46daab93, []int{92, 0}
}
type GenSeedRequest struct {
@ -169,7 +201,7 @@ func (m *GenSeedRequest) Reset() { *m = GenSeedRequest{} }
func (m *GenSeedRequest) String() string { return proto.CompactTextString(m) }
func (*GenSeedRequest) ProtoMessage() {}
func (*GenSeedRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{0}
return fileDescriptor_rpc_854431eb46daab93, []int{0}
}
func (m *GenSeedRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GenSeedRequest.Unmarshal(m, b)
@ -224,7 +256,7 @@ func (m *GenSeedResponse) Reset() { *m = GenSeedResponse{} }
func (m *GenSeedResponse) String() string { return proto.CompactTextString(m) }
func (*GenSeedResponse) ProtoMessage() {}
func (*GenSeedResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{1}
return fileDescriptor_rpc_854431eb46daab93, []int{1}
}
func (m *GenSeedResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GenSeedResponse.Unmarshal(m, b)
@ -297,7 +329,7 @@ func (m *InitWalletRequest) Reset() { *m = InitWalletRequest{} }
func (m *InitWalletRequest) String() string { return proto.CompactTextString(m) }
func (*InitWalletRequest) ProtoMessage() {}
func (*InitWalletRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{2}
return fileDescriptor_rpc_854431eb46daab93, []int{2}
}
func (m *InitWalletRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InitWalletRequest.Unmarshal(m, b)
@ -362,7 +394,7 @@ func (m *InitWalletResponse) Reset() { *m = InitWalletResponse{} }
func (m *InitWalletResponse) String() string { return proto.CompactTextString(m) }
func (*InitWalletResponse) ProtoMessage() {}
func (*InitWalletResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{3}
return fileDescriptor_rpc_854431eb46daab93, []int{3}
}
func (m *InitWalletResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InitWalletResponse.Unmarshal(m, b)
@ -412,7 +444,7 @@ func (m *UnlockWalletRequest) Reset() { *m = UnlockWalletRequest{} }
func (m *UnlockWalletRequest) String() string { return proto.CompactTextString(m) }
func (*UnlockWalletRequest) ProtoMessage() {}
func (*UnlockWalletRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{4}
return fileDescriptor_rpc_854431eb46daab93, []int{4}
}
func (m *UnlockWalletRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UnlockWalletRequest.Unmarshal(m, b)
@ -463,7 +495,7 @@ func (m *UnlockWalletResponse) Reset() { *m = UnlockWalletResponse{} }
func (m *UnlockWalletResponse) String() string { return proto.CompactTextString(m) }
func (*UnlockWalletResponse) ProtoMessage() {}
func (*UnlockWalletResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{5}
return fileDescriptor_rpc_854431eb46daab93, []int{5}
}
func (m *UnlockWalletResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_UnlockWalletResponse.Unmarshal(m, b)
@ -501,7 +533,7 @@ func (m *ChangePasswordRequest) Reset() { *m = ChangePasswordRequest{} }
func (m *ChangePasswordRequest) String() string { return proto.CompactTextString(m) }
func (*ChangePasswordRequest) ProtoMessage() {}
func (*ChangePasswordRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{6}
return fileDescriptor_rpc_854431eb46daab93, []int{6}
}
func (m *ChangePasswordRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChangePasswordRequest.Unmarshal(m, b)
@ -545,7 +577,7 @@ func (m *ChangePasswordResponse) Reset() { *m = ChangePasswordResponse{}
func (m *ChangePasswordResponse) String() string { return proto.CompactTextString(m) }
func (*ChangePasswordResponse) ProtoMessage() {}
func (*ChangePasswordResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{7}
return fileDescriptor_rpc_854431eb46daab93, []int{7}
}
func (m *ChangePasswordResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChangePasswordResponse.Unmarshal(m, b)
@ -587,7 +619,7 @@ func (m *Utxo) Reset() { *m = Utxo{} }
func (m *Utxo) String() string { return proto.CompactTextString(m) }
func (*Utxo) ProtoMessage() {}
func (*Utxo) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{8}
return fileDescriptor_rpc_854431eb46daab93, []int{8}
}
func (m *Utxo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Utxo.Unmarshal(m, b)
@ -675,7 +707,7 @@ func (m *Transaction) Reset() { *m = Transaction{} }
func (m *Transaction) String() string { return proto.CompactTextString(m) }
func (*Transaction) ProtoMessage() {}
func (*Transaction) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{9}
return fileDescriptor_rpc_854431eb46daab93, []int{9}
}
func (m *Transaction) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Transaction.Unmarshal(m, b)
@ -761,7 +793,7 @@ func (m *GetTransactionsRequest) Reset() { *m = GetTransactionsRequest{}
func (m *GetTransactionsRequest) String() string { return proto.CompactTextString(m) }
func (*GetTransactionsRequest) ProtoMessage() {}
func (*GetTransactionsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{10}
return fileDescriptor_rpc_854431eb46daab93, []int{10}
}
func (m *GetTransactionsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetTransactionsRequest.Unmarshal(m, b)
@ -793,7 +825,7 @@ func (m *TransactionDetails) Reset() { *m = TransactionDetails{} }
func (m *TransactionDetails) String() string { return proto.CompactTextString(m) }
func (*TransactionDetails) ProtoMessage() {}
func (*TransactionDetails) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{11}
return fileDescriptor_rpc_854431eb46daab93, []int{11}
}
func (m *TransactionDetails) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_TransactionDetails.Unmarshal(m, b)
@ -834,7 +866,7 @@ func (m *FeeLimit) Reset() { *m = FeeLimit{} }
func (m *FeeLimit) String() string { return proto.CompactTextString(m) }
func (*FeeLimit) ProtoMessage() {}
func (*FeeLimit) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{12}
return fileDescriptor_rpc_854431eb46daab93, []int{12}
}
func (m *FeeLimit) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FeeLimit.Unmarshal(m, b)
@ -998,7 +1030,7 @@ func (m *SendRequest) Reset() { *m = SendRequest{} }
func (m *SendRequest) String() string { return proto.CompactTextString(m) }
func (*SendRequest) ProtoMessage() {}
func (*SendRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{13}
return fileDescriptor_rpc_854431eb46daab93, []int{13}
}
func (m *SendRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendRequest.Unmarshal(m, b)
@ -1102,7 +1134,7 @@ func (m *SendResponse) Reset() { *m = SendResponse{} }
func (m *SendResponse) String() string { return proto.CompactTextString(m) }
func (*SendResponse) ProtoMessage() {}
func (*SendResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{14}
return fileDescriptor_rpc_854431eb46daab93, []int{14}
}
func (m *SendResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendResponse.Unmarshal(m, b)
@ -1172,7 +1204,7 @@ func (m *SendToRouteRequest) Reset() { *m = SendToRouteRequest{} }
func (m *SendToRouteRequest) String() string { return proto.CompactTextString(m) }
func (*SendToRouteRequest) ProtoMessage() {}
func (*SendToRouteRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{15}
return fileDescriptor_rpc_854431eb46daab93, []int{15}
}
func (m *SendToRouteRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendToRouteRequest.Unmarshal(m, b)
@ -1237,7 +1269,7 @@ func (m *ChannelPoint) Reset() { *m = ChannelPoint{} }
func (m *ChannelPoint) String() string { return proto.CompactTextString(m) }
func (*ChannelPoint) ProtoMessage() {}
func (*ChannelPoint) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{16}
return fileDescriptor_rpc_854431eb46daab93, []int{16}
}
func (m *ChannelPoint) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelPoint.Unmarshal(m, b)
@ -1383,7 +1415,7 @@ func (m *OutPoint) Reset() { *m = OutPoint{} }
func (m *OutPoint) String() string { return proto.CompactTextString(m) }
func (*OutPoint) ProtoMessage() {}
func (*OutPoint) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{17}
return fileDescriptor_rpc_854431eb46daab93, []int{17}
}
func (m *OutPoint) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OutPoint.Unmarshal(m, b)
@ -1438,7 +1470,7 @@ func (m *LightningAddress) Reset() { *m = LightningAddress{} }
func (m *LightningAddress) String() string { return proto.CompactTextString(m) }
func (*LightningAddress) ProtoMessage() {}
func (*LightningAddress) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{18}
return fileDescriptor_rpc_854431eb46daab93, []int{18}
}
func (m *LightningAddress) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_LightningAddress.Unmarshal(m, b)
@ -1486,7 +1518,7 @@ func (m *EstimateFeeRequest) Reset() { *m = EstimateFeeRequest{} }
func (m *EstimateFeeRequest) String() string { return proto.CompactTextString(m) }
func (*EstimateFeeRequest) ProtoMessage() {}
func (*EstimateFeeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{19}
return fileDescriptor_rpc_854431eb46daab93, []int{19}
}
func (m *EstimateFeeRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EstimateFeeRequest.Unmarshal(m, b)
@ -1534,7 +1566,7 @@ func (m *EstimateFeeResponse) Reset() { *m = EstimateFeeResponse{} }
func (m *EstimateFeeResponse) String() string { return proto.CompactTextString(m) }
func (*EstimateFeeResponse) ProtoMessage() {}
func (*EstimateFeeResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{20}
return fileDescriptor_rpc_854431eb46daab93, []int{20}
}
func (m *EstimateFeeResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EstimateFeeResponse.Unmarshal(m, b)
@ -1584,7 +1616,7 @@ func (m *SendManyRequest) Reset() { *m = SendManyRequest{} }
func (m *SendManyRequest) String() string { return proto.CompactTextString(m) }
func (*SendManyRequest) ProtoMessage() {}
func (*SendManyRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{21}
return fileDescriptor_rpc_854431eb46daab93, []int{21}
}
func (m *SendManyRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendManyRequest.Unmarshal(m, b)
@ -1637,7 +1669,7 @@ func (m *SendManyResponse) Reset() { *m = SendManyResponse{} }
func (m *SendManyResponse) String() string { return proto.CompactTextString(m) }
func (*SendManyResponse) ProtoMessage() {}
func (*SendManyResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{22}
return fileDescriptor_rpc_854431eb46daab93, []int{22}
}
func (m *SendManyResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendManyResponse.Unmarshal(m, b)
@ -1687,7 +1719,7 @@ func (m *SendCoinsRequest) Reset() { *m = SendCoinsRequest{} }
func (m *SendCoinsRequest) String() string { return proto.CompactTextString(m) }
func (*SendCoinsRequest) ProtoMessage() {}
func (*SendCoinsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{23}
return fileDescriptor_rpc_854431eb46daab93, []int{23}
}
func (m *SendCoinsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendCoinsRequest.Unmarshal(m, b)
@ -1754,7 +1786,7 @@ func (m *SendCoinsResponse) Reset() { *m = SendCoinsResponse{} }
func (m *SendCoinsResponse) String() string { return proto.CompactTextString(m) }
func (*SendCoinsResponse) ProtoMessage() {}
func (*SendCoinsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{24}
return fileDescriptor_rpc_854431eb46daab93, []int{24}
}
func (m *SendCoinsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SendCoinsResponse.Unmarshal(m, b)
@ -1795,7 +1827,7 @@ func (m *ListUnspentRequest) Reset() { *m = ListUnspentRequest{} }
func (m *ListUnspentRequest) String() string { return proto.CompactTextString(m) }
func (*ListUnspentRequest) ProtoMessage() {}
func (*ListUnspentRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{25}
return fileDescriptor_rpc_854431eb46daab93, []int{25}
}
func (m *ListUnspentRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListUnspentRequest.Unmarshal(m, b)
@ -1841,7 +1873,7 @@ func (m *ListUnspentResponse) Reset() { *m = ListUnspentResponse{} }
func (m *ListUnspentResponse) String() string { return proto.CompactTextString(m) }
func (*ListUnspentResponse) ProtoMessage() {}
func (*ListUnspentResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{26}
return fileDescriptor_rpc_854431eb46daab93, []int{26}
}
func (m *ListUnspentResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListUnspentResponse.Unmarshal(m, b)
@ -1880,7 +1912,7 @@ func (m *NewAddressRequest) Reset() { *m = NewAddressRequest{} }
func (m *NewAddressRequest) String() string { return proto.CompactTextString(m) }
func (*NewAddressRequest) ProtoMessage() {}
func (*NewAddressRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{27}
return fileDescriptor_rpc_854431eb46daab93, []int{27}
}
func (m *NewAddressRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NewAddressRequest.Unmarshal(m, b)
@ -1919,7 +1951,7 @@ func (m *NewAddressResponse) Reset() { *m = NewAddressResponse{} }
func (m *NewAddressResponse) String() string { return proto.CompactTextString(m) }
func (*NewAddressResponse) ProtoMessage() {}
func (*NewAddressResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{28}
return fileDescriptor_rpc_854431eb46daab93, []int{28}
}
func (m *NewAddressResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NewAddressResponse.Unmarshal(m, b)
@ -1958,7 +1990,7 @@ func (m *SignMessageRequest) Reset() { *m = SignMessageRequest{} }
func (m *SignMessageRequest) String() string { return proto.CompactTextString(m) }
func (*SignMessageRequest) ProtoMessage() {}
func (*SignMessageRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{29}
return fileDescriptor_rpc_854431eb46daab93, []int{29}
}
func (m *SignMessageRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SignMessageRequest.Unmarshal(m, b)
@ -1997,7 +2029,7 @@ func (m *SignMessageResponse) Reset() { *m = SignMessageResponse{} }
func (m *SignMessageResponse) String() string { return proto.CompactTextString(m) }
func (*SignMessageResponse) ProtoMessage() {}
func (*SignMessageResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{30}
return fileDescriptor_rpc_854431eb46daab93, []int{30}
}
func (m *SignMessageResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_SignMessageResponse.Unmarshal(m, b)
@ -2038,7 +2070,7 @@ func (m *VerifyMessageRequest) Reset() { *m = VerifyMessageRequest{} }
func (m *VerifyMessageRequest) String() string { return proto.CompactTextString(m) }
func (*VerifyMessageRequest) ProtoMessage() {}
func (*VerifyMessageRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{31}
return fileDescriptor_rpc_854431eb46daab93, []int{31}
}
func (m *VerifyMessageRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_VerifyMessageRequest.Unmarshal(m, b)
@ -2086,7 +2118,7 @@ func (m *VerifyMessageResponse) Reset() { *m = VerifyMessageResponse{} }
func (m *VerifyMessageResponse) String() string { return proto.CompactTextString(m) }
func (*VerifyMessageResponse) ProtoMessage() {}
func (*VerifyMessageResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{32}
return fileDescriptor_rpc_854431eb46daab93, []int{32}
}
func (m *VerifyMessageResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_VerifyMessageResponse.Unmarshal(m, b)
@ -2135,7 +2167,7 @@ func (m *ConnectPeerRequest) Reset() { *m = ConnectPeerRequest{} }
func (m *ConnectPeerRequest) String() string { return proto.CompactTextString(m) }
func (*ConnectPeerRequest) ProtoMessage() {}
func (*ConnectPeerRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{33}
return fileDescriptor_rpc_854431eb46daab93, []int{33}
}
func (m *ConnectPeerRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConnectPeerRequest.Unmarshal(m, b)
@ -2179,7 +2211,7 @@ func (m *ConnectPeerResponse) Reset() { *m = ConnectPeerResponse{} }
func (m *ConnectPeerResponse) String() string { return proto.CompactTextString(m) }
func (*ConnectPeerResponse) ProtoMessage() {}
func (*ConnectPeerResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{34}
return fileDescriptor_rpc_854431eb46daab93, []int{34}
}
func (m *ConnectPeerResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConnectPeerResponse.Unmarshal(m, b)
@ -2211,7 +2243,7 @@ func (m *DisconnectPeerRequest) Reset() { *m = DisconnectPeerRequest{} }
func (m *DisconnectPeerRequest) String() string { return proto.CompactTextString(m) }
func (*DisconnectPeerRequest) ProtoMessage() {}
func (*DisconnectPeerRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{35}
return fileDescriptor_rpc_854431eb46daab93, []int{35}
}
func (m *DisconnectPeerRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DisconnectPeerRequest.Unmarshal(m, b)
@ -2248,7 +2280,7 @@ func (m *DisconnectPeerResponse) Reset() { *m = DisconnectPeerResponse{}
func (m *DisconnectPeerResponse) String() string { return proto.CompactTextString(m) }
func (*DisconnectPeerResponse) ProtoMessage() {}
func (*DisconnectPeerResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{36}
return fileDescriptor_rpc_854431eb46daab93, []int{36}
}
func (m *DisconnectPeerResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DisconnectPeerResponse.Unmarshal(m, b)
@ -2282,7 +2314,7 @@ func (m *HTLC) Reset() { *m = HTLC{} }
func (m *HTLC) String() string { return proto.CompactTextString(m) }
func (*HTLC) ProtoMessage() {}
func (*HTLC) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{37}
return fileDescriptor_rpc_854431eb46daab93, []int{37}
}
func (m *HTLC) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HTLC.Unmarshal(m, b)
@ -2397,7 +2429,7 @@ func (m *Channel) Reset() { *m = Channel{} }
func (m *Channel) String() string { return proto.CompactTextString(m) }
func (*Channel) ProtoMessage() {}
func (*Channel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{38}
return fileDescriptor_rpc_854431eb46daab93, []int{38}
}
func (m *Channel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Channel.Unmarshal(m, b)
@ -2564,7 +2596,7 @@ func (m *ListChannelsRequest) Reset() { *m = ListChannelsRequest{} }
func (m *ListChannelsRequest) String() string { return proto.CompactTextString(m) }
func (*ListChannelsRequest) ProtoMessage() {}
func (*ListChannelsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{39}
return fileDescriptor_rpc_854431eb46daab93, []int{39}
}
func (m *ListChannelsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListChannelsRequest.Unmarshal(m, b)
@ -2624,7 +2656,7 @@ func (m *ListChannelsResponse) Reset() { *m = ListChannelsResponse{} }
func (m *ListChannelsResponse) String() string { return proto.CompactTextString(m) }
func (*ListChannelsResponse) ProtoMessage() {}
func (*ListChannelsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{40}
return fileDescriptor_rpc_854431eb46daab93, []int{40}
}
func (m *ListChannelsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListChannelsResponse.Unmarshal(m, b)
@ -2681,7 +2713,7 @@ func (m *ChannelCloseSummary) Reset() { *m = ChannelCloseSummary{} }
func (m *ChannelCloseSummary) String() string { return proto.CompactTextString(m) }
func (*ChannelCloseSummary) ProtoMessage() {}
func (*ChannelCloseSummary) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{41}
return fileDescriptor_rpc_854431eb46daab93, []int{41}
}
func (m *ChannelCloseSummary) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelCloseSummary.Unmarshal(m, b)
@ -2787,7 +2819,7 @@ func (m *ClosedChannelsRequest) Reset() { *m = ClosedChannelsRequest{} }
func (m *ClosedChannelsRequest) String() string { return proto.CompactTextString(m) }
func (*ClosedChannelsRequest) ProtoMessage() {}
func (*ClosedChannelsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{42}
return fileDescriptor_rpc_854431eb46daab93, []int{42}
}
func (m *ClosedChannelsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ClosedChannelsRequest.Unmarshal(m, b)
@ -2860,7 +2892,7 @@ func (m *ClosedChannelsResponse) Reset() { *m = ClosedChannelsResponse{}
func (m *ClosedChannelsResponse) String() string { return proto.CompactTextString(m) }
func (*ClosedChannelsResponse) ProtoMessage() {}
func (*ClosedChannelsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{43}
return fileDescriptor_rpc_854431eb46daab93, []int{43}
}
func (m *ClosedChannelsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ClosedChannelsResponse.Unmarshal(m, b)
@ -2903,17 +2935,19 @@ type Peer struct {
// / A channel is inbound if the counterparty initiated the channel
Inbound bool `protobuf:"varint,8,opt,name=inbound,proto3" json:"inbound,omitempty"`
// / Ping time to this peer
PingTime int64 `protobuf:"varint,9,opt,name=ping_time,proto3" json:"ping_time,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
PingTime int64 `protobuf:"varint,9,opt,name=ping_time,proto3" json:"ping_time,omitempty"`
// The type of sync we are currently performing with this peer.
SyncType Peer_SyncType `protobuf:"varint,10,opt,name=sync_type,proto3,enum=lnrpc.Peer_SyncType" json:"sync_type,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Peer) Reset() { *m = Peer{} }
func (m *Peer) String() string { return proto.CompactTextString(m) }
func (*Peer) ProtoMessage() {}
func (*Peer) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{44}
return fileDescriptor_rpc_854431eb46daab93, []int{44}
}
func (m *Peer) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Peer.Unmarshal(m, b)
@ -2989,6 +3023,13 @@ func (m *Peer) GetPingTime() int64 {
return 0
}
func (m *Peer) GetSyncType() Peer_SyncType {
if m != nil {
return m.SyncType
}
return Peer_UNKNOWN_SYNC
}
type ListPeersRequest struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
@ -2999,7 +3040,7 @@ func (m *ListPeersRequest) Reset() { *m = ListPeersRequest{} }
func (m *ListPeersRequest) String() string { return proto.CompactTextString(m) }
func (*ListPeersRequest) ProtoMessage() {}
func (*ListPeersRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{45}
return fileDescriptor_rpc_854431eb46daab93, []int{45}
}
func (m *ListPeersRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListPeersRequest.Unmarshal(m, b)
@ -3031,7 +3072,7 @@ func (m *ListPeersResponse) Reset() { *m = ListPeersResponse{} }
func (m *ListPeersResponse) String() string { return proto.CompactTextString(m) }
func (*ListPeersResponse) ProtoMessage() {}
func (*ListPeersResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{46}
return fileDescriptor_rpc_854431eb46daab93, []int{46}
}
func (m *ListPeersResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListPeersResponse.Unmarshal(m, b)
@ -3068,7 +3109,7 @@ func (m *GetInfoRequest) Reset() { *m = GetInfoRequest{} }
func (m *GetInfoRequest) String() string { return proto.CompactTextString(m) }
func (*GetInfoRequest) ProtoMessage() {}
func (*GetInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{47}
return fileDescriptor_rpc_854431eb46daab93, []int{47}
}
func (m *GetInfoRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetInfoRequest.Unmarshal(m, b)
@ -3128,7 +3169,7 @@ func (m *GetInfoResponse) Reset() { *m = GetInfoResponse{} }
func (m *GetInfoResponse) String() string { return proto.CompactTextString(m) }
func (*GetInfoResponse) ProtoMessage() {}
func (*GetInfoResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{48}
return fileDescriptor_rpc_854431eb46daab93, []int{48}
}
func (m *GetInfoResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GetInfoResponse.Unmarshal(m, b)
@ -3261,7 +3302,7 @@ func (m *Chain) Reset() { *m = Chain{} }
func (m *Chain) String() string { return proto.CompactTextString(m) }
func (*Chain) ProtoMessage() {}
func (*Chain) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{49}
return fileDescriptor_rpc_854431eb46daab93, []int{49}
}
func (m *Chain) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Chain.Unmarshal(m, b)
@ -3308,7 +3349,7 @@ func (m *ConfirmationUpdate) Reset() { *m = ConfirmationUpdate{} }
func (m *ConfirmationUpdate) String() string { return proto.CompactTextString(m) }
func (*ConfirmationUpdate) ProtoMessage() {}
func (*ConfirmationUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{50}
return fileDescriptor_rpc_854431eb46daab93, []int{50}
}
func (m *ConfirmationUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ConfirmationUpdate.Unmarshal(m, b)
@ -3360,7 +3401,7 @@ func (m *ChannelOpenUpdate) Reset() { *m = ChannelOpenUpdate{} }
func (m *ChannelOpenUpdate) String() string { return proto.CompactTextString(m) }
func (*ChannelOpenUpdate) ProtoMessage() {}
func (*ChannelOpenUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{51}
return fileDescriptor_rpc_854431eb46daab93, []int{51}
}
func (m *ChannelOpenUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelOpenUpdate.Unmarshal(m, b)
@ -3399,7 +3440,7 @@ func (m *ChannelCloseUpdate) Reset() { *m = ChannelCloseUpdate{} }
func (m *ChannelCloseUpdate) String() string { return proto.CompactTextString(m) }
func (*ChannelCloseUpdate) ProtoMessage() {}
func (*ChannelCloseUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{52}
return fileDescriptor_rpc_854431eb46daab93, []int{52}
}
func (m *ChannelCloseUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelCloseUpdate.Unmarshal(m, b)
@ -3454,7 +3495,7 @@ func (m *CloseChannelRequest) Reset() { *m = CloseChannelRequest{} }
func (m *CloseChannelRequest) String() string { return proto.CompactTextString(m) }
func (*CloseChannelRequest) ProtoMessage() {}
func (*CloseChannelRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{53}
return fileDescriptor_rpc_854431eb46daab93, []int{53}
}
func (m *CloseChannelRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CloseChannelRequest.Unmarshal(m, b)
@ -3516,7 +3557,7 @@ func (m *CloseStatusUpdate) Reset() { *m = CloseStatusUpdate{} }
func (m *CloseStatusUpdate) String() string { return proto.CompactTextString(m) }
func (*CloseStatusUpdate) ProtoMessage() {}
func (*CloseStatusUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{54}
return fileDescriptor_rpc_854431eb46daab93, []int{54}
}
func (m *CloseStatusUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_CloseStatusUpdate.Unmarshal(m, b)
@ -3659,7 +3700,7 @@ func (m *PendingUpdate) Reset() { *m = PendingUpdate{} }
func (m *PendingUpdate) String() string { return proto.CompactTextString(m) }
func (*PendingUpdate) ProtoMessage() {}
func (*PendingUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{55}
return fileDescriptor_rpc_854431eb46daab93, []int{55}
}
func (m *PendingUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingUpdate.Unmarshal(m, b)
@ -3725,7 +3766,7 @@ func (m *OpenChannelRequest) Reset() { *m = OpenChannelRequest{} }
func (m *OpenChannelRequest) String() string { return proto.CompactTextString(m) }
func (*OpenChannelRequest) ProtoMessage() {}
func (*OpenChannelRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{56}
return fileDescriptor_rpc_854431eb46daab93, []int{56}
}
func (m *OpenChannelRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OpenChannelRequest.Unmarshal(m, b)
@ -3836,7 +3877,7 @@ func (m *OpenStatusUpdate) Reset() { *m = OpenStatusUpdate{} }
func (m *OpenStatusUpdate) String() string { return proto.CompactTextString(m) }
func (*OpenStatusUpdate) ProtoMessage() {}
func (*OpenStatusUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{57}
return fileDescriptor_rpc_854431eb46daab93, []int{57}
}
func (m *OpenStatusUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_OpenStatusUpdate.Unmarshal(m, b)
@ -3992,7 +4033,7 @@ func (m *PendingHTLC) Reset() { *m = PendingHTLC{} }
func (m *PendingHTLC) String() string { return proto.CompactTextString(m) }
func (*PendingHTLC) ProtoMessage() {}
func (*PendingHTLC) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{58}
return fileDescriptor_rpc_854431eb46daab93, []int{58}
}
func (m *PendingHTLC) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingHTLC.Unmarshal(m, b)
@ -4064,7 +4105,7 @@ func (m *PendingChannelsRequest) Reset() { *m = PendingChannelsRequest{}
func (m *PendingChannelsRequest) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsRequest) ProtoMessage() {}
func (*PendingChannelsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{59}
return fileDescriptor_rpc_854431eb46daab93, []int{59}
}
func (m *PendingChannelsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsRequest.Unmarshal(m, b)
@ -4104,7 +4145,7 @@ func (m *PendingChannelsResponse) Reset() { *m = PendingChannelsResponse
func (m *PendingChannelsResponse) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsResponse) ProtoMessage() {}
func (*PendingChannelsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{60}
return fileDescriptor_rpc_854431eb46daab93, []int{60}
}
func (m *PendingChannelsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse.Unmarshal(m, b)
@ -4176,7 +4217,7 @@ func (m *PendingChannelsResponse_PendingChannel) Reset() {
func (m *PendingChannelsResponse_PendingChannel) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsResponse_PendingChannel) ProtoMessage() {}
func (*PendingChannelsResponse_PendingChannel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{60, 0}
return fileDescriptor_rpc_854431eb46daab93, []int{60, 0}
}
func (m *PendingChannelsResponse_PendingChannel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse_PendingChannel.Unmarshal(m, b)
@ -4263,7 +4304,7 @@ func (m *PendingChannelsResponse_PendingOpenChannel) String() string {
}
func (*PendingChannelsResponse_PendingOpenChannel) ProtoMessage() {}
func (*PendingChannelsResponse_PendingOpenChannel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{60, 1}
return fileDescriptor_rpc_854431eb46daab93, []int{60, 1}
}
func (m *PendingChannelsResponse_PendingOpenChannel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse_PendingOpenChannel.Unmarshal(m, b)
@ -4336,7 +4377,7 @@ func (m *PendingChannelsResponse_WaitingCloseChannel) String() string {
}
func (*PendingChannelsResponse_WaitingCloseChannel) ProtoMessage() {}
func (*PendingChannelsResponse_WaitingCloseChannel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{60, 2}
return fileDescriptor_rpc_854431eb46daab93, []int{60, 2}
}
func (m *PendingChannelsResponse_WaitingCloseChannel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse_WaitingCloseChannel.Unmarshal(m, b)
@ -4384,7 +4425,7 @@ func (m *PendingChannelsResponse_ClosedChannel) Reset() { *m = PendingCh
func (m *PendingChannelsResponse_ClosedChannel) String() string { return proto.CompactTextString(m) }
func (*PendingChannelsResponse_ClosedChannel) ProtoMessage() {}
func (*PendingChannelsResponse_ClosedChannel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{60, 3}
return fileDescriptor_rpc_854431eb46daab93, []int{60, 3}
}
func (m *PendingChannelsResponse_ClosedChannel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse_ClosedChannel.Unmarshal(m, b)
@ -4448,7 +4489,7 @@ func (m *PendingChannelsResponse_ForceClosedChannel) String() string {
}
func (*PendingChannelsResponse_ForceClosedChannel) ProtoMessage() {}
func (*PendingChannelsResponse_ForceClosedChannel) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{60, 4}
return fileDescriptor_rpc_854431eb46daab93, []int{60, 4}
}
func (m *PendingChannelsResponse_ForceClosedChannel) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PendingChannelsResponse_ForceClosedChannel.Unmarshal(m, b)
@ -4527,7 +4568,7 @@ func (m *ChannelEventSubscription) Reset() { *m = ChannelEventSubscripti
func (m *ChannelEventSubscription) String() string { return proto.CompactTextString(m) }
func (*ChannelEventSubscription) ProtoMessage() {}
func (*ChannelEventSubscription) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{61}
return fileDescriptor_rpc_854431eb46daab93, []int{61}
}
func (m *ChannelEventSubscription) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelEventSubscription.Unmarshal(m, b)
@ -4564,7 +4605,7 @@ func (m *ChannelEventUpdate) Reset() { *m = ChannelEventUpdate{} }
func (m *ChannelEventUpdate) String() string { return proto.CompactTextString(m) }
func (*ChannelEventUpdate) ProtoMessage() {}
func (*ChannelEventUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{62}
return fileDescriptor_rpc_854431eb46daab93, []int{62}
}
func (m *ChannelEventUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelEventUpdate.Unmarshal(m, b)
@ -4776,7 +4817,7 @@ func (m *WalletBalanceRequest) Reset() { *m = WalletBalanceRequest{} }
func (m *WalletBalanceRequest) String() string { return proto.CompactTextString(m) }
func (*WalletBalanceRequest) ProtoMessage() {}
func (*WalletBalanceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{63}
return fileDescriptor_rpc_854431eb46daab93, []int{63}
}
func (m *WalletBalanceRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_WalletBalanceRequest.Unmarshal(m, b)
@ -4812,7 +4853,7 @@ func (m *WalletBalanceResponse) Reset() { *m = WalletBalanceResponse{} }
func (m *WalletBalanceResponse) String() string { return proto.CompactTextString(m) }
func (*WalletBalanceResponse) ProtoMessage() {}
func (*WalletBalanceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{64}
return fileDescriptor_rpc_854431eb46daab93, []int{64}
}
func (m *WalletBalanceResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_WalletBalanceResponse.Unmarshal(m, b)
@ -4863,7 +4904,7 @@ func (m *ChannelBalanceRequest) Reset() { *m = ChannelBalanceRequest{} }
func (m *ChannelBalanceRequest) String() string { return proto.CompactTextString(m) }
func (*ChannelBalanceRequest) ProtoMessage() {}
func (*ChannelBalanceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{65}
return fileDescriptor_rpc_854431eb46daab93, []int{65}
}
func (m *ChannelBalanceRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelBalanceRequest.Unmarshal(m, b)
@ -4897,7 +4938,7 @@ func (m *ChannelBalanceResponse) Reset() { *m = ChannelBalanceResponse{}
func (m *ChannelBalanceResponse) String() string { return proto.CompactTextString(m) }
func (*ChannelBalanceResponse) ProtoMessage() {}
func (*ChannelBalanceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{66}
return fileDescriptor_rpc_854431eb46daab93, []int{66}
}
func (m *ChannelBalanceResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelBalanceResponse.Unmarshal(m, b)
@ -4967,7 +5008,7 @@ func (m *QueryRoutesRequest) Reset() { *m = QueryRoutesRequest{} }
func (m *QueryRoutesRequest) String() string { return proto.CompactTextString(m) }
func (*QueryRoutesRequest) ProtoMessage() {}
func (*QueryRoutesRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{67}
return fileDescriptor_rpc_854431eb46daab93, []int{67}
}
func (m *QueryRoutesRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_QueryRoutesRequest.Unmarshal(m, b)
@ -5062,7 +5103,7 @@ func (m *EdgeLocator) Reset() { *m = EdgeLocator{} }
func (m *EdgeLocator) String() string { return proto.CompactTextString(m) }
func (*EdgeLocator) ProtoMessage() {}
func (*EdgeLocator) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{68}
return fileDescriptor_rpc_854431eb46daab93, []int{68}
}
func (m *EdgeLocator) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_EdgeLocator.Unmarshal(m, b)
@ -5107,7 +5148,7 @@ func (m *QueryRoutesResponse) Reset() { *m = QueryRoutesResponse{} }
func (m *QueryRoutesResponse) String() string { return proto.CompactTextString(m) }
func (*QueryRoutesResponse) ProtoMessage() {}
func (*QueryRoutesResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{69}
return fileDescriptor_rpc_854431eb46daab93, []int{69}
}
func (m *QueryRoutesResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_QueryRoutesResponse.Unmarshal(m, b)
@ -5159,7 +5200,7 @@ func (m *Hop) Reset() { *m = Hop{} }
func (m *Hop) String() string { return proto.CompactTextString(m) }
func (*Hop) ProtoMessage() {}
func (*Hop) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{70}
return fileDescriptor_rpc_854431eb46daab93, []int{70}
}
func (m *Hop) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Hop.Unmarshal(m, b)
@ -5280,7 +5321,7 @@ func (m *Route) Reset() { *m = Route{} }
func (m *Route) String() string { return proto.CompactTextString(m) }
func (*Route) ProtoMessage() {}
func (*Route) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{71}
return fileDescriptor_rpc_854431eb46daab93, []int{71}
}
func (m *Route) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Route.Unmarshal(m, b)
@ -5356,7 +5397,7 @@ func (m *NodeInfoRequest) Reset() { *m = NodeInfoRequest{} }
func (m *NodeInfoRequest) String() string { return proto.CompactTextString(m) }
func (*NodeInfoRequest) ProtoMessage() {}
func (*NodeInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{72}
return fileDescriptor_rpc_854431eb46daab93, []int{72}
}
func (m *NodeInfoRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodeInfoRequest.Unmarshal(m, b)
@ -5401,7 +5442,7 @@ func (m *NodeInfo) Reset() { *m = NodeInfo{} }
func (m *NodeInfo) String() string { return proto.CompactTextString(m) }
func (*NodeInfo) ProtoMessage() {}
func (*NodeInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{73}
return fileDescriptor_rpc_854431eb46daab93, []int{73}
}
func (m *NodeInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodeInfo.Unmarshal(m, b)
@ -5462,7 +5503,7 @@ func (m *LightningNode) Reset() { *m = LightningNode{} }
func (m *LightningNode) String() string { return proto.CompactTextString(m) }
func (*LightningNode) ProtoMessage() {}
func (*LightningNode) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{74}
return fileDescriptor_rpc_854431eb46daab93, []int{74}
}
func (m *LightningNode) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_LightningNode.Unmarshal(m, b)
@ -5529,7 +5570,7 @@ func (m *NodeAddress) Reset() { *m = NodeAddress{} }
func (m *NodeAddress) String() string { return proto.CompactTextString(m) }
func (*NodeAddress) ProtoMessage() {}
func (*NodeAddress) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{75}
return fileDescriptor_rpc_854431eb46daab93, []int{75}
}
func (m *NodeAddress) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodeAddress.Unmarshal(m, b)
@ -5579,7 +5620,7 @@ func (m *RoutingPolicy) Reset() { *m = RoutingPolicy{} }
func (m *RoutingPolicy) String() string { return proto.CompactTextString(m) }
func (*RoutingPolicy) ProtoMessage() {}
func (*RoutingPolicy) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{76}
return fileDescriptor_rpc_854431eb46daab93, []int{76}
}
func (m *RoutingPolicy) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RoutingPolicy.Unmarshal(m, b)
@ -5669,7 +5710,7 @@ func (m *ChannelEdge) Reset() { *m = ChannelEdge{} }
func (m *ChannelEdge) String() string { return proto.CompactTextString(m) }
func (*ChannelEdge) ProtoMessage() {}
func (*ChannelEdge) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{77}
return fileDescriptor_rpc_854431eb46daab93, []int{77}
}
func (m *ChannelEdge) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelEdge.Unmarshal(m, b)
@ -5760,7 +5801,7 @@ func (m *ChannelGraphRequest) Reset() { *m = ChannelGraphRequest{} }
func (m *ChannelGraphRequest) String() string { return proto.CompactTextString(m) }
func (*ChannelGraphRequest) ProtoMessage() {}
func (*ChannelGraphRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{78}
return fileDescriptor_rpc_854431eb46daab93, []int{78}
}
func (m *ChannelGraphRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelGraphRequest.Unmarshal(m, b)
@ -5802,7 +5843,7 @@ func (m *ChannelGraph) Reset() { *m = ChannelGraph{} }
func (m *ChannelGraph) String() string { return proto.CompactTextString(m) }
func (*ChannelGraph) ProtoMessage() {}
func (*ChannelGraph) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{79}
return fileDescriptor_rpc_854431eb46daab93, []int{79}
}
func (m *ChannelGraph) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelGraph.Unmarshal(m, b)
@ -5851,7 +5892,7 @@ func (m *ChanInfoRequest) Reset() { *m = ChanInfoRequest{} }
func (m *ChanInfoRequest) String() string { return proto.CompactTextString(m) }
func (*ChanInfoRequest) ProtoMessage() {}
func (*ChanInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{80}
return fileDescriptor_rpc_854431eb46daab93, []int{80}
}
func (m *ChanInfoRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChanInfoRequest.Unmarshal(m, b)
@ -5888,7 +5929,7 @@ func (m *NetworkInfoRequest) Reset() { *m = NetworkInfoRequest{} }
func (m *NetworkInfoRequest) String() string { return proto.CompactTextString(m) }
func (*NetworkInfoRequest) ProtoMessage() {}
func (*NetworkInfoRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{81}
return fileDescriptor_rpc_854431eb46daab93, []int{81}
}
func (m *NetworkInfoRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NetworkInfoRequest.Unmarshal(m, b)
@ -5928,7 +5969,7 @@ func (m *NetworkInfo) Reset() { *m = NetworkInfo{} }
func (m *NetworkInfo) String() string { return proto.CompactTextString(m) }
func (*NetworkInfo) ProtoMessage() {}
func (*NetworkInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{82}
return fileDescriptor_rpc_854431eb46daab93, []int{82}
}
func (m *NetworkInfo) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NetworkInfo.Unmarshal(m, b)
@ -6028,7 +6069,7 @@ func (m *StopRequest) Reset() { *m = StopRequest{} }
func (m *StopRequest) String() string { return proto.CompactTextString(m) }
func (*StopRequest) ProtoMessage() {}
func (*StopRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{83}
return fileDescriptor_rpc_854431eb46daab93, []int{83}
}
func (m *StopRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StopRequest.Unmarshal(m, b)
@ -6058,7 +6099,7 @@ func (m *StopResponse) Reset() { *m = StopResponse{} }
func (m *StopResponse) String() string { return proto.CompactTextString(m) }
func (*StopResponse) ProtoMessage() {}
func (*StopResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{84}
return fileDescriptor_rpc_854431eb46daab93, []int{84}
}
func (m *StopResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_StopResponse.Unmarshal(m, b)
@ -6088,7 +6129,7 @@ func (m *GraphTopologySubscription) Reset() { *m = GraphTopologySubscrip
func (m *GraphTopologySubscription) String() string { return proto.CompactTextString(m) }
func (*GraphTopologySubscription) ProtoMessage() {}
func (*GraphTopologySubscription) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{85}
return fileDescriptor_rpc_854431eb46daab93, []int{85}
}
func (m *GraphTopologySubscription) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GraphTopologySubscription.Unmarshal(m, b)
@ -6121,7 +6162,7 @@ func (m *GraphTopologyUpdate) Reset() { *m = GraphTopologyUpdate{} }
func (m *GraphTopologyUpdate) String() string { return proto.CompactTextString(m) }
func (*GraphTopologyUpdate) ProtoMessage() {}
func (*GraphTopologyUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{86}
return fileDescriptor_rpc_854431eb46daab93, []int{86}
}
func (m *GraphTopologyUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GraphTopologyUpdate.Unmarshal(m, b)
@ -6176,7 +6217,7 @@ func (m *NodeUpdate) Reset() { *m = NodeUpdate{} }
func (m *NodeUpdate) String() string { return proto.CompactTextString(m) }
func (*NodeUpdate) ProtoMessage() {}
func (*NodeUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{87}
return fileDescriptor_rpc_854431eb46daab93, []int{87}
}
func (m *NodeUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_NodeUpdate.Unmarshal(m, b)
@ -6244,7 +6285,7 @@ func (m *ChannelEdgeUpdate) Reset() { *m = ChannelEdgeUpdate{} }
func (m *ChannelEdgeUpdate) String() string { return proto.CompactTextString(m) }
func (*ChannelEdgeUpdate) ProtoMessage() {}
func (*ChannelEdgeUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{88}
return fileDescriptor_rpc_854431eb46daab93, []int{88}
}
func (m *ChannelEdgeUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelEdgeUpdate.Unmarshal(m, b)
@ -6324,7 +6365,7 @@ func (m *ClosedChannelUpdate) Reset() { *m = ClosedChannelUpdate{} }
func (m *ClosedChannelUpdate) String() string { return proto.CompactTextString(m) }
func (*ClosedChannelUpdate) ProtoMessage() {}
func (*ClosedChannelUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{89}
return fileDescriptor_rpc_854431eb46daab93, []int{89}
}
func (m *ClosedChannelUpdate) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ClosedChannelUpdate.Unmarshal(m, b)
@ -6394,7 +6435,7 @@ func (m *HopHint) Reset() { *m = HopHint{} }
func (m *HopHint) String() string { return proto.CompactTextString(m) }
func (*HopHint) ProtoMessage() {}
func (*HopHint) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{90}
return fileDescriptor_rpc_854431eb46daab93, []int{90}
}
func (m *HopHint) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_HopHint.Unmarshal(m, b)
@ -6463,7 +6504,7 @@ func (m *RouteHint) Reset() { *m = RouteHint{} }
func (m *RouteHint) String() string { return proto.CompactTextString(m) }
func (*RouteHint) ProtoMessage() {}
func (*RouteHint) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{91}
return fileDescriptor_rpc_854431eb46daab93, []int{91}
}
func (m *RouteHint) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RouteHint.Unmarshal(m, b)
@ -6578,7 +6619,7 @@ func (m *Invoice) Reset() { *m = Invoice{} }
func (m *Invoice) String() string { return proto.CompactTextString(m) }
func (*Invoice) ProtoMessage() {}
func (*Invoice) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{92}
return fileDescriptor_rpc_854431eb46daab93, []int{92}
}
func (m *Invoice) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Invoice.Unmarshal(m, b)
@ -6770,7 +6811,7 @@ func (m *AddInvoiceResponse) Reset() { *m = AddInvoiceResponse{} }
func (m *AddInvoiceResponse) String() string { return proto.CompactTextString(m) }
func (*AddInvoiceResponse) ProtoMessage() {}
func (*AddInvoiceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{93}
return fileDescriptor_rpc_854431eb46daab93, []int{93}
}
func (m *AddInvoiceResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AddInvoiceResponse.Unmarshal(m, b)
@ -6827,7 +6868,7 @@ func (m *PaymentHash) Reset() { *m = PaymentHash{} }
func (m *PaymentHash) String() string { return proto.CompactTextString(m) }
func (*PaymentHash) ProtoMessage() {}
func (*PaymentHash) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{94}
return fileDescriptor_rpc_854431eb46daab93, []int{94}
}
func (m *PaymentHash) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PaymentHash.Unmarshal(m, b)
@ -6883,7 +6924,7 @@ func (m *ListInvoiceRequest) Reset() { *m = ListInvoiceRequest{} }
func (m *ListInvoiceRequest) String() string { return proto.CompactTextString(m) }
func (*ListInvoiceRequest) ProtoMessage() {}
func (*ListInvoiceRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{95}
return fileDescriptor_rpc_854431eb46daab93, []int{95}
}
func (m *ListInvoiceRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListInvoiceRequest.Unmarshal(m, b)
@ -6953,7 +6994,7 @@ func (m *ListInvoiceResponse) Reset() { *m = ListInvoiceResponse{} }
func (m *ListInvoiceResponse) String() string { return proto.CompactTextString(m) }
func (*ListInvoiceResponse) ProtoMessage() {}
func (*ListInvoiceResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{96}
return fileDescriptor_rpc_854431eb46daab93, []int{96}
}
func (m *ListInvoiceResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListInvoiceResponse.Unmarshal(m, b)
@ -7016,7 +7057,7 @@ func (m *InvoiceSubscription) Reset() { *m = InvoiceSubscription{} }
func (m *InvoiceSubscription) String() string { return proto.CompactTextString(m) }
func (*InvoiceSubscription) ProtoMessage() {}
func (*InvoiceSubscription) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{97}
return fileDescriptor_rpc_854431eb46daab93, []int{97}
}
func (m *InvoiceSubscription) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InvoiceSubscription.Unmarshal(m, b)
@ -7076,7 +7117,7 @@ func (m *Payment) Reset() { *m = Payment{} }
func (m *Payment) String() string { return proto.CompactTextString(m) }
func (*Payment) ProtoMessage() {}
func (*Payment) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{98}
return fileDescriptor_rpc_854431eb46daab93, []int{98}
}
func (m *Payment) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Payment.Unmarshal(m, b)
@ -7163,7 +7204,7 @@ func (m *ListPaymentsRequest) Reset() { *m = ListPaymentsRequest{} }
func (m *ListPaymentsRequest) String() string { return proto.CompactTextString(m) }
func (*ListPaymentsRequest) ProtoMessage() {}
func (*ListPaymentsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{99}
return fileDescriptor_rpc_854431eb46daab93, []int{99}
}
func (m *ListPaymentsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListPaymentsRequest.Unmarshal(m, b)
@ -7195,7 +7236,7 @@ func (m *ListPaymentsResponse) Reset() { *m = ListPaymentsResponse{} }
func (m *ListPaymentsResponse) String() string { return proto.CompactTextString(m) }
func (*ListPaymentsResponse) ProtoMessage() {}
func (*ListPaymentsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{100}
return fileDescriptor_rpc_854431eb46daab93, []int{100}
}
func (m *ListPaymentsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ListPaymentsResponse.Unmarshal(m, b)
@ -7232,7 +7273,7 @@ func (m *DeleteAllPaymentsRequest) Reset() { *m = DeleteAllPaymentsReque
func (m *DeleteAllPaymentsRequest) String() string { return proto.CompactTextString(m) }
func (*DeleteAllPaymentsRequest) ProtoMessage() {}
func (*DeleteAllPaymentsRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{101}
return fileDescriptor_rpc_854431eb46daab93, []int{101}
}
func (m *DeleteAllPaymentsRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeleteAllPaymentsRequest.Unmarshal(m, b)
@ -7262,7 +7303,7 @@ func (m *DeleteAllPaymentsResponse) Reset() { *m = DeleteAllPaymentsResp
func (m *DeleteAllPaymentsResponse) String() string { return proto.CompactTextString(m) }
func (*DeleteAllPaymentsResponse) ProtoMessage() {}
func (*DeleteAllPaymentsResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{102}
return fileDescriptor_rpc_854431eb46daab93, []int{102}
}
func (m *DeleteAllPaymentsResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DeleteAllPaymentsResponse.Unmarshal(m, b)
@ -7293,7 +7334,7 @@ func (m *AbandonChannelRequest) Reset() { *m = AbandonChannelRequest{} }
func (m *AbandonChannelRequest) String() string { return proto.CompactTextString(m) }
func (*AbandonChannelRequest) ProtoMessage() {}
func (*AbandonChannelRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{103}
return fileDescriptor_rpc_854431eb46daab93, []int{103}
}
func (m *AbandonChannelRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AbandonChannelRequest.Unmarshal(m, b)
@ -7330,7 +7371,7 @@ func (m *AbandonChannelResponse) Reset() { *m = AbandonChannelResponse{}
func (m *AbandonChannelResponse) String() string { return proto.CompactTextString(m) }
func (*AbandonChannelResponse) ProtoMessage() {}
func (*AbandonChannelResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{104}
return fileDescriptor_rpc_854431eb46daab93, []int{104}
}
func (m *AbandonChannelResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_AbandonChannelResponse.Unmarshal(m, b)
@ -7362,7 +7403,7 @@ func (m *DebugLevelRequest) Reset() { *m = DebugLevelRequest{} }
func (m *DebugLevelRequest) String() string { return proto.CompactTextString(m) }
func (*DebugLevelRequest) ProtoMessage() {}
func (*DebugLevelRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{105}
return fileDescriptor_rpc_854431eb46daab93, []int{105}
}
func (m *DebugLevelRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DebugLevelRequest.Unmarshal(m, b)
@ -7407,7 +7448,7 @@ func (m *DebugLevelResponse) Reset() { *m = DebugLevelResponse{} }
func (m *DebugLevelResponse) String() string { return proto.CompactTextString(m) }
func (*DebugLevelResponse) ProtoMessage() {}
func (*DebugLevelResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{106}
return fileDescriptor_rpc_854431eb46daab93, []int{106}
}
func (m *DebugLevelResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_DebugLevelResponse.Unmarshal(m, b)
@ -7446,7 +7487,7 @@ func (m *PayReqString) Reset() { *m = PayReqString{} }
func (m *PayReqString) String() string { return proto.CompactTextString(m) }
func (*PayReqString) ProtoMessage() {}
func (*PayReqString) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{107}
return fileDescriptor_rpc_854431eb46daab93, []int{107}
}
func (m *PayReqString) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PayReqString.Unmarshal(m, b)
@ -7493,7 +7534,7 @@ func (m *PayReq) Reset() { *m = PayReq{} }
func (m *PayReq) String() string { return proto.CompactTextString(m) }
func (*PayReq) ProtoMessage() {}
func (*PayReq) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{108}
return fileDescriptor_rpc_854431eb46daab93, []int{108}
}
func (m *PayReq) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PayReq.Unmarshal(m, b)
@ -7593,7 +7634,7 @@ func (m *FeeReportRequest) Reset() { *m = FeeReportRequest{} }
func (m *FeeReportRequest) String() string { return proto.CompactTextString(m) }
func (*FeeReportRequest) ProtoMessage() {}
func (*FeeReportRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{109}
return fileDescriptor_rpc_854431eb46daab93, []int{109}
}
func (m *FeeReportRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FeeReportRequest.Unmarshal(m, b)
@ -7631,7 +7672,7 @@ func (m *ChannelFeeReport) Reset() { *m = ChannelFeeReport{} }
func (m *ChannelFeeReport) String() string { return proto.CompactTextString(m) }
func (*ChannelFeeReport) ProtoMessage() {}
func (*ChannelFeeReport) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{110}
return fileDescriptor_rpc_854431eb46daab93, []int{110}
}
func (m *ChannelFeeReport) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelFeeReport.Unmarshal(m, b)
@ -7697,7 +7738,7 @@ func (m *FeeReportResponse) Reset() { *m = FeeReportResponse{} }
func (m *FeeReportResponse) String() string { return proto.CompactTextString(m) }
func (*FeeReportResponse) ProtoMessage() {}
func (*FeeReportResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{111}
return fileDescriptor_rpc_854431eb46daab93, []int{111}
}
func (m *FeeReportResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_FeeReportResponse.Unmarshal(m, b)
@ -7765,7 +7806,7 @@ func (m *PolicyUpdateRequest) Reset() { *m = PolicyUpdateRequest{} }
func (m *PolicyUpdateRequest) String() string { return proto.CompactTextString(m) }
func (*PolicyUpdateRequest) ProtoMessage() {}
func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{112}
return fileDescriptor_rpc_854431eb46daab93, []int{112}
}
func (m *PolicyUpdateRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PolicyUpdateRequest.Unmarshal(m, b)
@ -7926,7 +7967,7 @@ func (m *PolicyUpdateResponse) Reset() { *m = PolicyUpdateResponse{} }
func (m *PolicyUpdateResponse) String() string { return proto.CompactTextString(m) }
func (*PolicyUpdateResponse) ProtoMessage() {}
func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{113}
return fileDescriptor_rpc_854431eb46daab93, []int{113}
}
func (m *PolicyUpdateResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_PolicyUpdateResponse.Unmarshal(m, b)
@ -7964,7 +8005,7 @@ func (m *ForwardingHistoryRequest) Reset() { *m = ForwardingHistoryReque
func (m *ForwardingHistoryRequest) String() string { return proto.CompactTextString(m) }
func (*ForwardingHistoryRequest) ProtoMessage() {}
func (*ForwardingHistoryRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{114}
return fileDescriptor_rpc_854431eb46daab93, []int{114}
}
func (m *ForwardingHistoryRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ForwardingHistoryRequest.Unmarshal(m, b)
@ -8036,7 +8077,7 @@ func (m *ForwardingEvent) Reset() { *m = ForwardingEvent{} }
func (m *ForwardingEvent) String() string { return proto.CompactTextString(m) }
func (*ForwardingEvent) ProtoMessage() {}
func (*ForwardingEvent) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{115}
return fileDescriptor_rpc_854431eb46daab93, []int{115}
}
func (m *ForwardingEvent) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ForwardingEvent.Unmarshal(m, b)
@ -8119,7 +8160,7 @@ func (m *ForwardingHistoryResponse) Reset() { *m = ForwardingHistoryResp
func (m *ForwardingHistoryResponse) String() string { return proto.CompactTextString(m) }
func (*ForwardingHistoryResponse) ProtoMessage() {}
func (*ForwardingHistoryResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{116}
return fileDescriptor_rpc_854431eb46daab93, []int{116}
}
func (m *ForwardingHistoryResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ForwardingHistoryResponse.Unmarshal(m, b)
@ -8165,7 +8206,7 @@ func (m *ExportChannelBackupRequest) Reset() { *m = ExportChannelBackupR
func (m *ExportChannelBackupRequest) String() string { return proto.CompactTextString(m) }
func (*ExportChannelBackupRequest) ProtoMessage() {}
func (*ExportChannelBackupRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{117}
return fileDescriptor_rpc_854431eb46daab93, []int{117}
}
func (m *ExportChannelBackupRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ExportChannelBackupRequest.Unmarshal(m, b)
@ -8210,7 +8251,7 @@ func (m *ChannelBackup) Reset() { *m = ChannelBackup{} }
func (m *ChannelBackup) String() string { return proto.CompactTextString(m) }
func (*ChannelBackup) ProtoMessage() {}
func (*ChannelBackup) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{118}
return fileDescriptor_rpc_854431eb46daab93, []int{118}
}
func (m *ChannelBackup) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelBackup.Unmarshal(m, b)
@ -8262,7 +8303,7 @@ func (m *MultiChanBackup) Reset() { *m = MultiChanBackup{} }
func (m *MultiChanBackup) String() string { return proto.CompactTextString(m) }
func (*MultiChanBackup) ProtoMessage() {}
func (*MultiChanBackup) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{119}
return fileDescriptor_rpc_854431eb46daab93, []int{119}
}
func (m *MultiChanBackup) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_MultiChanBackup.Unmarshal(m, b)
@ -8306,7 +8347,7 @@ func (m *ChanBackupExportRequest) Reset() { *m = ChanBackupExportRequest
func (m *ChanBackupExportRequest) String() string { return proto.CompactTextString(m) }
func (*ChanBackupExportRequest) ProtoMessage() {}
func (*ChanBackupExportRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{120}
return fileDescriptor_rpc_854431eb46daab93, []int{120}
}
func (m *ChanBackupExportRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChanBackupExportRequest.Unmarshal(m, b)
@ -8344,7 +8385,7 @@ func (m *ChanBackupSnapshot) Reset() { *m = ChanBackupSnapshot{} }
func (m *ChanBackupSnapshot) String() string { return proto.CompactTextString(m) }
func (*ChanBackupSnapshot) ProtoMessage() {}
func (*ChanBackupSnapshot) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{121}
return fileDescriptor_rpc_854431eb46daab93, []int{121}
}
func (m *ChanBackupSnapshot) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChanBackupSnapshot.Unmarshal(m, b)
@ -8391,7 +8432,7 @@ func (m *ChannelBackups) Reset() { *m = ChannelBackups{} }
func (m *ChannelBackups) String() string { return proto.CompactTextString(m) }
func (*ChannelBackups) ProtoMessage() {}
func (*ChannelBackups) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{122}
return fileDescriptor_rpc_854431eb46daab93, []int{122}
}
func (m *ChannelBackups) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelBackups.Unmarshal(m, b)
@ -8432,7 +8473,7 @@ func (m *RestoreChanBackupRequest) Reset() { *m = RestoreChanBackupReque
func (m *RestoreChanBackupRequest) String() string { return proto.CompactTextString(m) }
func (*RestoreChanBackupRequest) ProtoMessage() {}
func (*RestoreChanBackupRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{123}
return fileDescriptor_rpc_854431eb46daab93, []int{123}
}
func (m *RestoreChanBackupRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RestoreChanBackupRequest.Unmarshal(m, b)
@ -8569,7 +8610,7 @@ func (m *RestoreBackupResponse) Reset() { *m = RestoreBackupResponse{} }
func (m *RestoreBackupResponse) String() string { return proto.CompactTextString(m) }
func (*RestoreBackupResponse) ProtoMessage() {}
func (*RestoreBackupResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{124}
return fileDescriptor_rpc_854431eb46daab93, []int{124}
}
func (m *RestoreBackupResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RestoreBackupResponse.Unmarshal(m, b)
@ -8599,7 +8640,7 @@ func (m *ChannelBackupSubscription) Reset() { *m = ChannelBackupSubscrip
func (m *ChannelBackupSubscription) String() string { return proto.CompactTextString(m) }
func (*ChannelBackupSubscription) ProtoMessage() {}
func (*ChannelBackupSubscription) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{125}
return fileDescriptor_rpc_854431eb46daab93, []int{125}
}
func (m *ChannelBackupSubscription) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_ChannelBackupSubscription.Unmarshal(m, b)
@ -8631,7 +8672,7 @@ func (m *VerifyChanBackupResponse) Reset() { *m = VerifyChanBackupRespon
func (m *VerifyChanBackupResponse) String() string { return proto.CompactTextString(m) }
func (*VerifyChanBackupResponse) ProtoMessage() {}
func (*VerifyChanBackupResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_rpc_e15c66dc1b25da1b, []int{126}
return fileDescriptor_rpc_854431eb46daab93, []int{126}
}
func (m *VerifyChanBackupResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_VerifyChanBackupResponse.Unmarshal(m, b)
@ -8802,6 +8843,7 @@ func init() {
proto.RegisterType((*VerifyChanBackupResponse)(nil), "lnrpc.VerifyChanBackupResponse")
proto.RegisterEnum("lnrpc.AddressType", AddressType_name, AddressType_value)
proto.RegisterEnum("lnrpc.ChannelCloseSummary_ClosureType", ChannelCloseSummary_ClosureType_name, ChannelCloseSummary_ClosureType_value)
proto.RegisterEnum("lnrpc.Peer_SyncType", Peer_SyncType_name, Peer_SyncType_value)
proto.RegisterEnum("lnrpc.ChannelEventUpdate_UpdateType", ChannelEventUpdate_UpdateType_name, ChannelEventUpdate_UpdateType_value)
proto.RegisterEnum("lnrpc.Invoice_InvoiceState", Invoice_InvoiceState_name, Invoice_InvoiceState_value)
}
@ -11438,489 +11480,493 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
Metadata: "rpc.proto",
}
func init() { proto.RegisterFile("rpc.proto", fileDescriptor_rpc_e15c66dc1b25da1b) }
func init() { proto.RegisterFile("rpc.proto", fileDescriptor_rpc_854431eb46daab93) }
var fileDescriptor_rpc_e15c66dc1b25da1b = []byte{
// 7696 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7c, 0x5d, 0x6c, 0x1c, 0xc9,
0x79, 0xa0, 0x7a, 0x7e, 0xc8, 0x99, 0x6f, 0x86, 0xc3, 0x61, 0xf1, 0x6f, 0x34, 0xd2, 0x6a, 0xb9,
0x6d, 0x59, 0xa2, 0xb9, 0x7b, 0xa2, 0x56, 0xb6, 0xd7, 0xf2, 0xea, 0x7c, 0x77, 0xfc, 0x93, 0x28,
0x9b, 0x4b, 0xd1, 0x4d, 0xc9, 0x3a, 0xaf, 0x7d, 0x18, 0x37, 0x67, 0x8a, 0xc3, 0x5e, 0xcd, 0x74,
0xcf, 0x76, 0xf7, 0x90, 0xa2, 0xf7, 0x74, 0x38, 0x1c, 0x0e, 0x49, 0x10, 0x24, 0x08, 0x9c, 0x20,
0x41, 0x1c, 0x24, 0x08, 0x60, 0x07, 0x48, 0x8c, 0x3c, 0xe5, 0xc1, 0x41, 0x80, 0xc4, 0x79, 0x0d,
0x60, 0x20, 0x08, 0x02, 0x3f, 0x06, 0x08, 0x10, 0x24, 0x2f, 0x49, 0x1e, 0x82, 0x04, 0xc8, 0x63,
0x80, 0xa0, 0xbe, 0xfa, 0xe9, 0xaa, 0xee, 0x1e, 0x51, 0x6b, 0x3b, 0x79, 0x9a, 0xa9, 0xaf, 0xbe,
0xae, 0xdf, 0xef, 0xbf, 0xbe, 0x2a, 0xa8, 0x86, 0xa3, 0xee, 0xad, 0x51, 0x18, 0xc4, 0x01, 0x29,
0x0f, 0xfc, 0x70, 0xd4, 0x6d, 0x5f, 0xed, 0x07, 0x41, 0x7f, 0x40, 0xd7, 0xdd, 0x91, 0xb7, 0xee,
0xfa, 0x7e, 0x10, 0xbb, 0xb1, 0x17, 0xf8, 0x11, 0x47, 0xb2, 0xbf, 0x01, 0x8d, 0x07, 0xd4, 0x3f,
0xa4, 0xb4, 0xe7, 0xd0, 0x0f, 0xc7, 0x34, 0x8a, 0xc9, 0x9b, 0x30, 0xe7, 0xd2, 0x6f, 0x52, 0xda,
0xeb, 0x8c, 0xdc, 0x28, 0x1a, 0x9d, 0x84, 0x6e, 0x44, 0x5b, 0xd6, 0x8a, 0xb5, 0x5a, 0x77, 0x9a,
0xbc, 0xe2, 0x40, 0xc1, 0xc9, 0x1b, 0x50, 0x8f, 0x18, 0x2a, 0xf5, 0xe3, 0x30, 0x18, 0x9d, 0xb7,
0x0a, 0x88, 0x57, 0x63, 0xb0, 0x1d, 0x0e, 0xb2, 0x07, 0x30, 0xab, 0x7a, 0x88, 0x46, 0x81, 0x1f,
0x51, 0x72, 0x1b, 0x16, 0xba, 0xde, 0xe8, 0x84, 0x86, 0x1d, 0xfc, 0x78, 0xe8, 0xd3, 0x61, 0xe0,
0x7b, 0xdd, 0x96, 0xb5, 0x52, 0x5c, 0xad, 0x3a, 0x84, 0xd7, 0xb1, 0x2f, 0xde, 0x13, 0x35, 0xe4,
0x26, 0xcc, 0x52, 0x9f, 0xc3, 0x69, 0x0f, 0xbf, 0x12, 0x5d, 0x35, 0x12, 0x30, 0xfb, 0xc0, 0xfe,
0xb9, 0x02, 0xcc, 0x3d, 0xf4, 0xbd, 0xf8, 0xa9, 0x3b, 0x18, 0xd0, 0x58, 0xce, 0xe9, 0x26, 0xcc,
0x9e, 0x21, 0x00, 0xe7, 0x74, 0x16, 0x84, 0x3d, 0x31, 0xa3, 0x06, 0x07, 0x1f, 0x08, 0xe8, 0xc4,
0x91, 0x15, 0x26, 0x8e, 0x2c, 0x77, 0xb9, 0x8a, 0x13, 0x96, 0xeb, 0x26, 0xcc, 0x86, 0xb4, 0x1b,
0x9c, 0xd2, 0xf0, 0xbc, 0x73, 0xe6, 0xf9, 0xbd, 0xe0, 0xac, 0x55, 0x5a, 0xb1, 0x56, 0xcb, 0x4e,
0x43, 0x82, 0x9f, 0x22, 0x94, 0x6c, 0xc2, 0x6c, 0xf7, 0xc4, 0xf5, 0x7d, 0x3a, 0xe8, 0x1c, 0xb9,
0xdd, 0x67, 0xe3, 0x51, 0xd4, 0x2a, 0xaf, 0x58, 0xab, 0xb5, 0x3b, 0x97, 0x6f, 0xe1, 0xae, 0xde,
0xda, 0x3a, 0x71, 0xfd, 0x4d, 0xac, 0x39, 0xf4, 0xdd, 0x51, 0x74, 0x12, 0xc4, 0x4e, 0x43, 0x7c,
0xc1, 0xc1, 0x91, 0xbd, 0x00, 0x44, 0x5f, 0x09, 0xbe, 0xf6, 0xf6, 0xef, 0x5b, 0x30, 0xff, 0xc4,
0x1f, 0x04, 0xdd, 0x67, 0x3f, 0xe6, 0x12, 0xe5, 0xcc, 0xa1, 0xf0, 0xaa, 0x73, 0x28, 0x7e, 0xdc,
0x39, 0x2c, 0xc1, 0x82, 0x39, 0x58, 0x31, 0x0b, 0x0a, 0x8b, 0xec, 0xeb, 0x3e, 0x95, 0xc3, 0x92,
0xd3, 0xf8, 0x14, 0x34, 0xbb, 0xe3, 0x30, 0xa4, 0x7e, 0x66, 0x1e, 0xb3, 0x02, 0xae, 0x26, 0xf2,
0x06, 0xd4, 0x7d, 0x7a, 0x96, 0xa0, 0x09, 0xda, 0xf5, 0xe9, 0x99, 0x44, 0xb1, 0x5b, 0xb0, 0x94,
0xee, 0x46, 0x0c, 0xe0, 0x6f, 0x2c, 0x28, 0x3d, 0x89, 0x9f, 0x07, 0xe4, 0x16, 0x94, 0xe2, 0xf3,
0x11, 0xe7, 0x90, 0xc6, 0x1d, 0x22, 0xa6, 0xb6, 0xd1, 0xeb, 0x85, 0x34, 0x8a, 0x1e, 0x9f, 0x8f,
0xa8, 0x53, 0x77, 0x79, 0xa1, 0xc3, 0xf0, 0x48, 0x0b, 0xa6, 0x45, 0x19, 0x3b, 0xac, 0x3a, 0xb2,
0x48, 0xae, 0x01, 0xb8, 0xc3, 0x60, 0xec, 0xc7, 0x9d, 0xc8, 0x8d, 0x71, 0xa9, 0x8a, 0x8e, 0x06,
0x21, 0x57, 0xa1, 0x3a, 0x7a, 0xd6, 0x89, 0xba, 0xa1, 0x37, 0x8a, 0x91, 0x6c, 0xaa, 0x4e, 0x02,
0x20, 0x6f, 0x42, 0x25, 0x18, 0xc7, 0xa3, 0xc0, 0xf3, 0x63, 0x41, 0x2a, 0xb3, 0x62, 0x2c, 0x8f,
0xc6, 0xf1, 0x01, 0x03, 0x3b, 0x0a, 0x81, 0x5c, 0x87, 0x99, 0x6e, 0xe0, 0x1f, 0x7b, 0xe1, 0x90,
0x0b, 0x83, 0xd6, 0x14, 0xf6, 0x66, 0x02, 0xed, 0x6f, 0x17, 0xa0, 0xf6, 0x38, 0x74, 0xfd, 0xc8,
0xed, 0x32, 0x00, 0x1b, 0x7a, 0xfc, 0xbc, 0x73, 0xe2, 0x46, 0x27, 0x38, 0xdb, 0xaa, 0x23, 0x8b,
0x64, 0x09, 0xa6, 0xf8, 0x40, 0x71, 0x4e, 0x45, 0x47, 0x94, 0xc8, 0x5b, 0x30, 0xe7, 0x8f, 0x87,
0x1d, 0xb3, 0xaf, 0x22, 0x52, 0x4b, 0xb6, 0x82, 0x2d, 0xc0, 0x11, 0xdb, 0x6b, 0xde, 0x05, 0x9f,
0xa1, 0x06, 0x21, 0x36, 0xd4, 0x45, 0x89, 0x7a, 0xfd, 0x13, 0x3e, 0xcd, 0xb2, 0x63, 0xc0, 0x58,
0x1b, 0xb1, 0x37, 0xa4, 0x9d, 0x28, 0x76, 0x87, 0x23, 0x31, 0x2d, 0x0d, 0x82, 0xf5, 0x41, 0xec,
0x0e, 0x3a, 0xc7, 0x94, 0x46, 0xad, 0x69, 0x51, 0xaf, 0x20, 0xe4, 0x06, 0x34, 0x7a, 0x34, 0x8a,
0x3b, 0x62, 0x53, 0x68, 0xd4, 0xaa, 0x20, 0xeb, 0xa7, 0xa0, 0x8c, 0x32, 0x1e, 0xd0, 0x58, 0x5b,
0x9d, 0x48, 0x50, 0xa0, 0xbd, 0x07, 0x44, 0x03, 0x6f, 0xd3, 0xd8, 0xf5, 0x06, 0x11, 0x79, 0x07,
0xea, 0xb1, 0x86, 0x8c, 0xa2, 0xae, 0xa6, 0xc8, 0x45, 0xfb, 0xc0, 0x31, 0xf0, 0xec, 0x07, 0x50,
0xb9, 0x4f, 0xe9, 0x9e, 0x37, 0xf4, 0x62, 0xb2, 0x04, 0xe5, 0x63, 0xef, 0x39, 0xe5, 0x04, 0x5d,
0xdc, 0xbd, 0xe4, 0xf0, 0x22, 0x69, 0xc3, 0xf4, 0x88, 0x86, 0x5d, 0x2a, 0x97, 0x7f, 0xf7, 0x92,
0x23, 0x01, 0x9b, 0xd3, 0x50, 0x1e, 0xb0, 0x8f, 0xed, 0x7f, 0x2e, 0x40, 0xed, 0x90, 0xfa, 0x8a,
0x51, 0x08, 0x94, 0xd8, 0x94, 0x04, 0x73, 0xe0, 0x7f, 0xf2, 0x3a, 0xd4, 0x70, 0x9a, 0x51, 0x1c,
0x7a, 0x7e, 0x5f, 0xd0, 0x27, 0x30, 0xd0, 0x21, 0x42, 0x48, 0x13, 0x8a, 0xee, 0x50, 0xd2, 0x26,
0xfb, 0xcb, 0x98, 0x68, 0xe4, 0x9e, 0x0f, 0x19, 0xbf, 0xa9, 0x5d, 0xab, 0x3b, 0x35, 0x01, 0xdb,
0x65, 0xdb, 0x76, 0x0b, 0xe6, 0x75, 0x14, 0xd9, 0x7a, 0x19, 0x5b, 0x9f, 0xd3, 0x30, 0x45, 0x27,
0x37, 0x61, 0x56, 0xe2, 0x87, 0x7c, 0xb0, 0xb8, 0x8f, 0x55, 0xa7, 0x21, 0xc0, 0x72, 0x0a, 0xab,
0xd0, 0x3c, 0xf6, 0x7c, 0x77, 0xd0, 0xe9, 0x0e, 0xe2, 0xd3, 0x4e, 0x8f, 0x0e, 0x62, 0x17, 0x77,
0xb4, 0xec, 0x34, 0x10, 0xbe, 0x35, 0x88, 0x4f, 0xb7, 0x19, 0x94, 0xbc, 0x05, 0xd5, 0x63, 0x4a,
0x3b, 0xb8, 0x12, 0xad, 0x8a, 0xc1, 0x1d, 0x72, 0x75, 0x9d, 0xca, 0xb1, 0x5c, 0xe7, 0x55, 0x68,
0x06, 0xe3, 0xb8, 0x1f, 0x78, 0x7e, 0xbf, 0xc3, 0xe4, 0x51, 0xc7, 0xeb, 0xb5, 0xaa, 0x2b, 0xd6,
0x6a, 0xc9, 0x69, 0x48, 0x38, 0x93, 0x0a, 0x0f, 0x7b, 0xe4, 0x35, 0x00, 0xec, 0x9b, 0x37, 0x0c,
0x2b, 0xd6, 0xea, 0x8c, 0x53, 0x65, 0x10, 0x6c, 0xc8, 0xfe, 0x23, 0x0b, 0xea, 0x7c, 0xcd, 0x85,
0xe2, 0xbb, 0x0e, 0x33, 0x72, 0x6a, 0x34, 0x0c, 0x83, 0x50, 0xf0, 0x91, 0x09, 0x24, 0x6b, 0xd0,
0x94, 0x80, 0x51, 0x48, 0xbd, 0xa1, 0xdb, 0xa7, 0x42, 0x38, 0x65, 0xe0, 0xe4, 0x4e, 0xd2, 0x62,
0x18, 0x8c, 0x63, 0x2a, 0x44, 0x6c, 0x5d, 0xcc, 0xce, 0x61, 0x30, 0xc7, 0x44, 0x61, 0x7c, 0x94,
0xb3, 0x67, 0x06, 0xcc, 0xfe, 0xbe, 0x05, 0x84, 0x0d, 0xfd, 0x71, 0xc0, 0x9b, 0x10, 0x4b, 0x9e,
0xde, 0x6e, 0xeb, 0x95, 0xb7, 0xbb, 0x30, 0x69, 0xbb, 0x57, 0x61, 0x0a, 0x87, 0xc5, 0x04, 0x43,
0x31, 0x3d, 0xf4, 0xcd, 0x42, 0xcb, 0x72, 0x44, 0x3d, 0xb1, 0xa1, 0xcc, 0xe7, 0x58, 0xca, 0x99,
0x23, 0xaf, 0xb2, 0xbf, 0x63, 0x41, 0x7d, 0x8b, 0xeb, 0x10, 0x14, 0x7a, 0xe4, 0x36, 0x90, 0xe3,
0xb1, 0xdf, 0x63, 0x7b, 0x19, 0x3f, 0xf7, 0x7a, 0x9d, 0xa3, 0x73, 0xd6, 0x15, 0x8e, 0x7b, 0xf7,
0x92, 0x93, 0x53, 0x47, 0xde, 0x82, 0xa6, 0x01, 0x8d, 0xe2, 0x90, 0x8f, 0x7e, 0xf7, 0x92, 0x93,
0xa9, 0x61, 0x8b, 0xc9, 0xc4, 0xea, 0x38, 0xee, 0x78, 0x7e, 0x8f, 0x3e, 0xc7, 0xf5, 0x9f, 0x71,
0x0c, 0xd8, 0x66, 0x03, 0xea, 0xfa, 0x77, 0xf6, 0x07, 0x50, 0x91, 0x42, 0x19, 0x05, 0x52, 0x6a,
0x5c, 0x8e, 0x06, 0x21, 0x6d, 0xa8, 0x98, 0xa3, 0x70, 0x2a, 0x1f, 0xa7, 0x6f, 0xfb, 0xbf, 0x41,
0x73, 0x8f, 0x49, 0x46, 0xdf, 0xf3, 0xfb, 0x42, 0x2b, 0x31, 0x71, 0x3d, 0x1a, 0x1f, 0x3d, 0xa3,
0xe7, 0x82, 0xfe, 0x44, 0x89, 0xc9, 0x84, 0x93, 0x20, 0x8a, 0x45, 0x3f, 0xf8, 0xdf, 0xfe, 0x33,
0x0b, 0xc8, 0x4e, 0x14, 0x7b, 0x43, 0x37, 0xa6, 0xf7, 0xa9, 0x22, 0x84, 0x47, 0x50, 0x67, 0xad,
0x3d, 0x0e, 0x36, 0xb8, 0xdc, 0xe7, 0xf2, 0xec, 0x4d, 0xb1, 0x25, 0xd9, 0x0f, 0x6e, 0xe9, 0xd8,
0xcc, 0x34, 0x3c, 0x77, 0x8c, 0x06, 0x98, 0xec, 0x89, 0xdd, 0xb0, 0x4f, 0x63, 0x54, 0x0a, 0xc2,
0xa4, 0x00, 0x0e, 0xda, 0x0a, 0xfc, 0xe3, 0xf6, 0x7f, 0x87, 0xb9, 0x4c, 0x1b, 0x4c, 0x20, 0x25,
0xd3, 0x60, 0x7f, 0xc9, 0x02, 0x94, 0x4f, 0xdd, 0xc1, 0x98, 0x0a, 0x4d, 0xc4, 0x0b, 0xef, 0x16,
0xee, 0x5a, 0x76, 0x17, 0xe6, 0x8d, 0x71, 0x09, 0x9e, 0x6c, 0xc1, 0x34, 0x93, 0x0d, 0x4c, 0xe7,
0xa2, 0x5c, 0x75, 0x64, 0x91, 0xdc, 0x81, 0x85, 0x63, 0x4a, 0x43, 0x37, 0xc6, 0x62, 0x67, 0x44,
0x43, 0xdc, 0x13, 0xd1, 0x72, 0x6e, 0x9d, 0xfd, 0xb7, 0x16, 0xcc, 0x32, 0xbe, 0x79, 0xcf, 0xf5,
0xcf, 0xe5, 0x5a, 0xed, 0xe5, 0xae, 0xd5, 0xaa, 0x58, 0xab, 0x14, 0xf6, 0xc7, 0x5d, 0xa8, 0x62,
0x7a, 0xa1, 0xc8, 0x0a, 0xd4, 0x8d, 0xe1, 0x96, 0xb9, 0x92, 0x8b, 0xdc, 0xf8, 0x80, 0x86, 0x9b,
0xe7, 0x31, 0xfd, 0xc9, 0x97, 0xf2, 0x06, 0x34, 0x93, 0x61, 0x8b, 0x75, 0x24, 0x50, 0x62, 0x84,
0x29, 0x1a, 0xc0, 0xff, 0xf6, 0x6f, 0x5a, 0x1c, 0x71, 0x2b, 0xf0, 0x94, 0x82, 0x64, 0x88, 0x4c,
0x8f, 0x4a, 0x44, 0xf6, 0x7f, 0xa2, 0x01, 0xf1, 0x93, 0x4f, 0x96, 0x5c, 0x86, 0x4a, 0x44, 0xfd,
0x5e, 0xc7, 0x1d, 0x0c, 0x50, 0x8f, 0x54, 0x9c, 0x69, 0x56, 0xde, 0x18, 0x0c, 0xec, 0x9b, 0x30,
0xa7, 0x8d, 0xee, 0x25, 0xf3, 0xd8, 0x07, 0xb2, 0xe7, 0x45, 0xf1, 0x13, 0x3f, 0x1a, 0x69, 0xfa,
0xe7, 0x0a, 0x54, 0x87, 0x9e, 0x8f, 0x23, 0xe3, 0x9c, 0x5b, 0x76, 0x2a, 0x43, 0xcf, 0x67, 0xe3,
0x8a, 0xb0, 0xd2, 0x7d, 0x2e, 0x2a, 0x0b, 0xa2, 0xd2, 0x7d, 0x8e, 0x95, 0xf6, 0x5d, 0x98, 0x37,
0xda, 0x13, 0x5d, 0xbf, 0x01, 0xe5, 0x71, 0xfc, 0x3c, 0x90, 0xd6, 0x41, 0x4d, 0x50, 0x08, 0xb3,
0x33, 0x1d, 0x5e, 0x63, 0xdf, 0x83, 0xb9, 0x7d, 0x7a, 0x26, 0x18, 0x59, 0x0e, 0xe4, 0xc6, 0x85,
0x36, 0x28, 0xd6, 0xdb, 0xb7, 0x80, 0xe8, 0x1f, 0x27, 0x0c, 0x20, 0x2d, 0x52, 0xcb, 0xb0, 0x48,
0xed, 0x1b, 0x40, 0x0e, 0xbd, 0xbe, 0xff, 0x1e, 0x8d, 0x22, 0xb7, 0xaf, 0x58, 0xbf, 0x09, 0xc5,
0x61, 0xd4, 0x17, 0xa2, 0x8a, 0xfd, 0xb5, 0x3f, 0x0d, 0xf3, 0x06, 0x9e, 0x68, 0xf8, 0x2a, 0x54,
0x23, 0xaf, 0xef, 0xbb, 0xf1, 0x38, 0xa4, 0xa2, 0xe9, 0x04, 0x60, 0xdf, 0x87, 0x85, 0xaf, 0xd0,
0xd0, 0x3b, 0x3e, 0xbf, 0xa8, 0x79, 0xb3, 0x9d, 0x42, 0xba, 0x9d, 0x1d, 0x58, 0x4c, 0xb5, 0x23,
0xba, 0xe7, 0xe4, 0x2b, 0x76, 0xb2, 0xe2, 0xf0, 0x82, 0x26, 0xfb, 0x0a, 0xba, 0xec, 0xb3, 0x9f,
0x00, 0xd9, 0x0a, 0x7c, 0x9f, 0x76, 0xe3, 0x03, 0x4a, 0xc3, 0xc4, 0x19, 0x4e, 0x68, 0xb5, 0x76,
0x67, 0x59, 0xac, 0x6c, 0x5a, 0xa0, 0x0a, 0x22, 0x26, 0x50, 0x1a, 0xd1, 0x70, 0x88, 0x0d, 0x57,
0x1c, 0xfc, 0x6f, 0x2f, 0xc2, 0xbc, 0xd1, 0xac, 0x70, 0x1f, 0xde, 0x86, 0xc5, 0x6d, 0x2f, 0xea,
0x66, 0x3b, 0x6c, 0xc1, 0xf4, 0x68, 0x7c, 0xd4, 0x49, 0x38, 0x51, 0x16, 0x99, 0xc5, 0x99, 0xfe,
0x44, 0x34, 0xf6, 0x33, 0x16, 0x94, 0x76, 0x1f, 0xef, 0x6d, 0x31, 0x5d, 0xe1, 0xf9, 0xdd, 0x60,
0xc8, 0xf4, 0x2d, 0x9f, 0xb4, 0x2a, 0x4f, 0xe4, 0xb0, 0xab, 0x50, 0x45, 0x35, 0xcd, 0x8c, 0x68,
0xe1, 0xb7, 0x26, 0x00, 0x66, 0xc0, 0xd3, 0xe7, 0x23, 0x2f, 0x44, 0x0b, 0x5d, 0xda, 0xdd, 0x25,
0x54, 0x33, 0xd9, 0x0a, 0xfb, 0x87, 0x65, 0x98, 0x16, 0xca, 0x17, 0xfb, 0xeb, 0xc6, 0xde, 0x29,
0x15, 0x23, 0x11, 0x25, 0x66, 0x02, 0x85, 0x74, 0x18, 0xc4, 0xb4, 0x63, 0x6c, 0x83, 0x09, 0x44,
0x07, 0x45, 0xf8, 0x8e, 0xdc, 0xa5, 0x29, 0x72, 0x2c, 0x03, 0xc8, 0x16, 0x4b, 0xda, 0x67, 0x25,
0xb4, 0xcf, 0x64, 0x91, 0xad, 0x44, 0xd7, 0x1d, 0xb9, 0x5d, 0x2f, 0x3e, 0x17, 0x22, 0x41, 0x95,
0x59, 0xdb, 0x83, 0xa0, 0xeb, 0x32, 0xaf, 0x74, 0xe0, 0xfa, 0x5d, 0x2a, 0x9d, 0x1f, 0x03, 0xc8,
0x1c, 0x01, 0x31, 0x24, 0x89, 0xc6, 0x9d, 0x85, 0x14, 0x94, 0xe9, 0xef, 0x6e, 0x30, 0x1c, 0x7a,
0x31, 0xf3, 0x1f, 0xd0, 0xb6, 0x2c, 0x3a, 0x1a, 0x84, 0xbb, 0x5a, 0x58, 0x3a, 0xe3, 0xab, 0x57,
0x95, 0xae, 0x96, 0x06, 0x64, 0xad, 0x30, 0xad, 0xc3, 0xc4, 0xd8, 0xb3, 0x33, 0x34, 0x24, 0x8b,
0x8e, 0x06, 0x61, 0xfb, 0x30, 0xf6, 0x23, 0x1a, 0xc7, 0x03, 0xda, 0x53, 0x03, 0xaa, 0x21, 0x5a,
0xb6, 0x82, 0xdc, 0x86, 0x79, 0xee, 0xd2, 0x44, 0x6e, 0x1c, 0x44, 0x27, 0x5e, 0xd4, 0x89, 0x98,
0x73, 0x50, 0x47, 0xfc, 0xbc, 0x2a, 0x72, 0x17, 0x96, 0x53, 0xe0, 0x90, 0x76, 0xa9, 0x77, 0x4a,
0x7b, 0xad, 0x19, 0xfc, 0x6a, 0x52, 0x35, 0x59, 0x81, 0x1a, 0xf3, 0xe4, 0xc6, 0xa3, 0x9e, 0xcb,
0x0c, 0x98, 0x06, 0xee, 0x83, 0x0e, 0x22, 0x6f, 0xc3, 0xcc, 0x88, 0x72, 0xeb, 0xe7, 0x24, 0x1e,
0x74, 0xa3, 0xd6, 0xac, 0x21, 0xdd, 0x18, 0xe5, 0x3a, 0x26, 0x06, 0x23, 0xca, 0x6e, 0x84, 0x26,
0xbd, 0x7b, 0xde, 0x6a, 0x0a, 0xb3, 0x5a, 0x02, 0x90, 0x47, 0x42, 0xef, 0xd4, 0x8d, 0x69, 0x6b,
0x8e, 0x0b, 0x74, 0x51, 0x64, 0xdf, 0x79, 0xbe, 0x17, 0x7b, 0x6e, 0x1c, 0x84, 0x2d, 0x82, 0x75,
0x09, 0x80, 0x2d, 0x22, 0xd2, 0x47, 0x14, 0xbb, 0xf1, 0x38, 0xea, 0x1c, 0x0f, 0xdc, 0x7e, 0xd4,
0x9a, 0xe7, 0x76, 0x69, 0xa6, 0xc2, 0xfe, 0x6d, 0x8b, 0x0b, 0x69, 0x41, 0xd0, 0x4a, 0xd8, 0xbe,
0x0e, 0x35, 0x4e, 0xca, 0x9d, 0xc0, 0x1f, 0x9c, 0x0b, 0xea, 0x06, 0x0e, 0x7a, 0xe4, 0x0f, 0xce,
0xc9, 0x27, 0x60, 0xc6, 0xf3, 0x75, 0x14, 0x2e, 0x0f, 0xea, 0x12, 0x88, 0x48, 0xaf, 0x43, 0x6d,
0x34, 0x3e, 0x1a, 0x78, 0x5d, 0x8e, 0x52, 0xe4, 0xad, 0x70, 0x10, 0x22, 0x30, 0x4b, 0x9b, 0xcf,
0x8a, 0x63, 0x94, 0x10, 0xa3, 0x26, 0x60, 0x0c, 0xc5, 0xde, 0x84, 0x05, 0x73, 0x80, 0x42, 0xf0,
0xad, 0x41, 0x45, 0xf0, 0x49, 0xd4, 0xaa, 0xe1, 0x5a, 0x37, 0xb4, 0x88, 0x8b, 0x4f, 0x07, 0x8e,
0xaa, 0xb7, 0xff, 0xb0, 0x04, 0xf3, 0x02, 0xba, 0x35, 0x08, 0x22, 0x7a, 0x38, 0x1e, 0x0e, 0xdd,
0x30, 0x87, 0x01, 0xad, 0x0b, 0x18, 0xb0, 0x60, 0x32, 0x20, 0x63, 0x8b, 0x13, 0xd7, 0xf3, 0xb9,
0x9b, 0xc0, 0xb9, 0x57, 0x83, 0x90, 0x55, 0x98, 0xed, 0x0e, 0x82, 0x88, 0x9b, 0xc4, 0xba, 0xc3,
0x9f, 0x06, 0x67, 0x05, 0x46, 0x39, 0x4f, 0x60, 0xe8, 0x0c, 0x3f, 0x95, 0x62, 0x78, 0x1b, 0xea,
0xac, 0x51, 0x2a, 0xe5, 0xd7, 0x34, 0x37, 0x93, 0x75, 0x18, 0x1b, 0x4f, 0x9a, 0xbd, 0x38, 0x2f,
0xcf, 0xe6, 0x31, 0x97, 0x37, 0xa4, 0x28, 0x1f, 0x35, 0xec, 0xaa, 0x60, 0xae, 0x6c, 0x15, 0xb9,
0xcf, 0xbc, 0x44, 0xd6, 0x17, 0x2a, 0x69, 0x40, 0x25, 0x7d, 0xc3, 0xdc, 0x11, 0x7d, 0xed, 0x6f,
0xb1, 0xc2, 0x38, 0xa4, 0xa8, 0xb8, 0xb5, 0x2f, 0xed, 0x9f, 0xb7, 0xa0, 0xa6, 0xd5, 0x91, 0x45,
0x98, 0xdb, 0x7a, 0xf4, 0xe8, 0x60, 0xc7, 0xd9, 0x78, 0xfc, 0xf0, 0x2b, 0x3b, 0x9d, 0xad, 0xbd,
0x47, 0x87, 0x3b, 0xcd, 0x4b, 0x0c, 0xbc, 0xf7, 0x68, 0x6b, 0x63, 0xaf, 0x73, 0xff, 0x91, 0xb3,
0x25, 0xc1, 0x16, 0x59, 0x02, 0xe2, 0xec, 0xbc, 0xf7, 0xe8, 0xf1, 0x8e, 0x01, 0x2f, 0x90, 0x26,
0xd4, 0x37, 0x9d, 0x9d, 0x8d, 0xad, 0x5d, 0x01, 0x29, 0x92, 0x05, 0x68, 0xde, 0x7f, 0xb2, 0xbf,
0xfd, 0x70, 0xff, 0x41, 0x67, 0x6b, 0x63, 0x7f, 0x6b, 0x67, 0x6f, 0x67, 0xbb, 0x59, 0x22, 0x33,
0x50, 0xdd, 0xd8, 0xdc, 0xd8, 0xdf, 0x7e, 0xb4, 0xbf, 0xb3, 0xdd, 0x2c, 0xdb, 0x7f, 0x6d, 0xc1,
0x22, 0x8e, 0xba, 0x97, 0x66, 0x90, 0x15, 0xa8, 0x75, 0x83, 0x60, 0xc4, 0x8c, 0xe3, 0x44, 0xfc,
0xeb, 0x20, 0x46, 0xfc, 0x5c, 0xd8, 0x1e, 0x07, 0x61, 0x97, 0x0a, 0xfe, 0x00, 0x04, 0xdd, 0x67,
0x10, 0x46, 0xfc, 0x62, 0x7b, 0x39, 0x06, 0x67, 0x8f, 0x1a, 0x87, 0x71, 0x94, 0x25, 0x98, 0x3a,
0x0a, 0xa9, 0xdb, 0x3d, 0x11, 0x9c, 0x21, 0x4a, 0xe4, 0x53, 0x89, 0xf7, 0xd6, 0x65, 0xab, 0x3f,
0xa0, 0x3d, 0xa4, 0x98, 0x8a, 0x33, 0x2b, 0xe0, 0x5b, 0x02, 0xcc, 0xa4, 0x85, 0x7b, 0xe4, 0xfa,
0xbd, 0xc0, 0xa7, 0x3d, 0x61, 0x1a, 0x26, 0x00, 0xfb, 0x00, 0x96, 0xd2, 0xf3, 0x13, 0xfc, 0xf5,
0x8e, 0xc6, 0x5f, 0xdc, 0x52, 0x6b, 0x4f, 0xde, 0x4d, 0x8d, 0xd7, 0xfe, 0xc1, 0x82, 0x12, 0x53,
0xdc, 0x93, 0x95, 0xbc, 0x6e, 0x8b, 0x15, 0x33, 0xd1, 0x41, 0x74, 0x08, 0xb9, 0x28, 0xe7, 0xea,
0x4e, 0x83, 0x24, 0xf5, 0x21, 0xed, 0x9e, 0xe2, 0x8c, 0x55, 0x3d, 0x83, 0x30, 0x06, 0x61, 0x86,
0x32, 0x7e, 0x2d, 0x18, 0x44, 0x96, 0x65, 0x1d, 0x7e, 0x39, 0x9d, 0xd4, 0xe1, 0x77, 0x2d, 0x98,
0xf6, 0xfc, 0xa3, 0x60, 0xec, 0xf7, 0x90, 0x21, 0x2a, 0x8e, 0x2c, 0x62, 0x3c, 0x12, 0x19, 0xd5,
0x1b, 0x4a, 0xf2, 0x4f, 0x00, 0x36, 0x61, 0x7e, 0x67, 0x84, 0x86, 0x8a, 0x0a, 0x8d, 0xbd, 0x03,
0x73, 0x1a, 0x2c, 0x31, 0x7a, 0x47, 0x0c, 0x90, 0x32, 0x7a, 0xd1, 0xc2, 0xe1, 0x35, 0x76, 0x13,
0x1a, 0x0f, 0x68, 0xfc, 0xd0, 0x3f, 0x0e, 0x64, 0x4b, 0xbf, 0x5b, 0x82, 0x59, 0x05, 0x12, 0x0d,
0xad, 0xc2, 0xac, 0xd7, 0xa3, 0x7e, 0xec, 0xc5, 0xe7, 0x1d, 0xc3, 0xbd, 0x4d, 0x83, 0x99, 0x65,
0xe8, 0x0e, 0x3c, 0x57, 0x46, 0x60, 0x79, 0x81, 0xb9, 0x7b, 0x4c, 0x6d, 0x49, 0x4d, 0xa4, 0xb6,
0x98, 0x7b, 0xd5, 0xb9, 0x75, 0x4c, 0x18, 0x30, 0xb8, 0x90, 0xf6, 0xea, 0x13, 0x6e, 0x21, 0xe5,
0x55, 0xb1, 0x55, 0xe3, 0x2d, 0xb1, 0x29, 0x97, 0xb9, 0x6a, 0x53, 0x80, 0x4c, 0x88, 0x73, 0x8a,
0x8b, 0xaa, 0x74, 0x88, 0x53, 0x0b, 0x93, 0x56, 0x32, 0x61, 0x52, 0x26, 0xca, 0xce, 0xfd, 0x2e,
0xed, 0x75, 0xe2, 0xa0, 0x83, 0x22, 0x17, 0x77, 0xa7, 0xe2, 0xa4, 0xc1, 0xe4, 0x2a, 0x4c, 0xc7,
0x34, 0x8a, 0x7d, 0xca, 0x63, 0x57, 0x15, 0x8c, 0xb6, 0x48, 0x10, 0x33, 0x67, 0xc7, 0xa1, 0x17,
0xb5, 0xea, 0x18, 0x00, 0xc5, 0xff, 0xe4, 0x33, 0xb0, 0x78, 0x44, 0xa3, 0xb8, 0x73, 0x42, 0xdd,
0x1e, 0x0d, 0x71, 0xa7, 0x79, 0xa4, 0x95, 0x5b, 0x09, 0xf9, 0x95, 0x8c, 0x86, 0x4e, 0x69, 0x18,
0x79, 0x81, 0x8f, 0xf6, 0x41, 0xd5, 0x91, 0x45, 0xd6, 0x1e, 0x9b, 0xbc, 0xd2, 0x97, 0x6a, 0x05,
0x67, 0x71, 0xe2, 0xf9, 0x95, 0xe4, 0x3a, 0x4c, 0xe1, 0x04, 0xa2, 0x56, 0xd3, 0x08, 0x19, 0x6d,
0x31, 0xa0, 0x23, 0xea, 0xbe, 0x58, 0xaa, 0xd4, 0x9a, 0x75, 0xfb, 0x73, 0x50, 0x46, 0x30, 0xdb,
0x74, 0xbe, 0x18, 0x9c, 0x28, 0x78, 0x81, 0x0d, 0xcd, 0xa7, 0xf1, 0x59, 0x10, 0x3e, 0x93, 0xe1,
0x78, 0x51, 0xb4, 0xbf, 0x89, 0x0e, 0x81, 0x0a, 0x4f, 0x3f, 0x41, 0x6b, 0x86, 0xb9, 0x75, 0x7c,
0xa9, 0xa3, 0x13, 0x57, 0xf8, 0x28, 0x15, 0x04, 0x1c, 0x9e, 0xb8, 0x4c, 0x6c, 0x19, 0xbb, 0xc7,
0xdd, 0xbe, 0x1a, 0xc2, 0x76, 0xf9, 0xe6, 0x5d, 0x87, 0x86, 0x0c, 0x7c, 0x47, 0x9d, 0x01, 0x3d,
0x8e, 0x65, 0xd0, 0xc6, 0x1f, 0x0f, 0xd1, 0x37, 0xdc, 0xa3, 0xc7, 0xb1, 0xbd, 0x0f, 0x73, 0x42,
0x94, 0x3c, 0x1a, 0x51, 0xd9, 0xf5, 0xe7, 0xf3, 0x54, 0x72, 0xed, 0xce, 0xbc, 0x29, 0x7b, 0x78,
0xa8, 0xdf, 0xc4, 0xb4, 0x1d, 0x20, 0xba, 0x68, 0x12, 0x0d, 0x0a, 0xbd, 0x28, 0xc3, 0x52, 0x62,
0x3a, 0x06, 0x8c, 0xad, 0x4f, 0x34, 0xee, 0x76, 0xe5, 0x71, 0x05, 0x73, 0x9e, 0x79, 0xd1, 0xfe,
0x3d, 0x0b, 0xe6, 0xb1, 0x35, 0x69, 0x54, 0x08, 0xf1, 0x7f, 0xf7, 0x63, 0x0c, 0xb3, 0xde, 0xd5,
0x43, 0x75, 0x0b, 0x50, 0xd6, 0x15, 0x02, 0x2f, 0x7c, 0xfc, 0x10, 0x40, 0x29, 0x1d, 0x02, 0xb0,
0x7f, 0xdd, 0x82, 0x39, 0x2e, 0x93, 0xd1, 0xc0, 0x13, 0xd3, 0xff, 0xaf, 0x30, 0xc3, 0x95, 0xab,
0xe0, 0x6a, 0x31, 0xd0, 0x05, 0x25, 0x80, 0x10, 0xca, 0x91, 0x77, 0x2f, 0x39, 0x26, 0x32, 0xb9,
0x87, 0x06, 0x8e, 0xdf, 0x41, 0x68, 0xce, 0xc1, 0x96, 0xb9, 0xd6, 0xbb, 0x97, 0x1c, 0x0d, 0x7d,
0xb3, 0x02, 0x53, 0xdc, 0x3a, 0xb6, 0x1f, 0xc0, 0x8c, 0xd1, 0x91, 0x11, 0x7e, 0xa8, 0xf3, 0xf0,
0x43, 0x26, 0xce, 0x57, 0xc8, 0x89, 0xf3, 0xfd, 0x41, 0x11, 0x08, 0x23, 0x96, 0xd4, 0x6e, 0x30,
0xf3, 0x3c, 0xe8, 0x19, 0xce, 0x56, 0xdd, 0xd1, 0x41, 0xe4, 0x16, 0x10, 0xad, 0x28, 0xc3, 0xb5,
0x5c, 0xfb, 0xe4, 0xd4, 0x30, 0x31, 0x29, 0x94, 0xb7, 0x50, 0xb3, 0xc2, 0xad, 0xe4, 0xcb, 0x9e,
0x5b, 0xc7, 0x14, 0xcc, 0x68, 0x1c, 0x9d, 0x60, 0x90, 0x4d, 0xb8, 0x63, 0xb2, 0x9c, 0xde, 0xdf,
0xa9, 0x0b, 0xf7, 0x77, 0x3a, 0x13, 0xe2, 0xd1, 0x1c, 0x82, 0x8a, 0xe9, 0x10, 0x5c, 0x87, 0x99,
0x21, 0x33, 0x39, 0xe3, 0x41, 0xb7, 0x33, 0x64, 0xbd, 0x0b, 0xef, 0xcb, 0x00, 0x92, 0x35, 0x68,
0x0a, 0x73, 0x23, 0xf1, 0x3a, 0x78, 0x30, 0x3f, 0x03, 0x67, 0xf2, 0x3b, 0x09, 0xfa, 0xd4, 0x70,
0xb0, 0x09, 0x80, 0xb9, 0x18, 0x11, 0xa3, 0x90, 0xce, 0xd8, 0x17, 0x67, 0x5b, 0xb4, 0x87, 0x7e,
0x57, 0xc5, 0xc9, 0x56, 0xd8, 0xbf, 0x6c, 0x41, 0x93, 0xed, 0x99, 0x41, 0x96, 0xef, 0x02, 0x72,
0xc5, 0x2b, 0x52, 0xa5, 0x81, 0x4b, 0xee, 0x42, 0x15, 0xcb, 0xc1, 0x88, 0xfa, 0x82, 0x26, 0x5b,
0x26, 0x4d, 0x26, 0xf2, 0x64, 0xf7, 0x92, 0x93, 0x20, 0x6b, 0x14, 0xf9, 0x17, 0x16, 0xd4, 0x44,
0x2f, 0x3f, 0x76, 0x50, 0xa1, 0xad, 0x1d, 0x46, 0x72, 0x4a, 0x4a, 0xce, 0x1e, 0x57, 0x61, 0x76,
0xe8, 0xc6, 0xe3, 0x90, 0xe9, 0x63, 0x23, 0xa0, 0x90, 0x06, 0x33, 0xe5, 0x8a, 0xa2, 0x33, 0xea,
0xc4, 0xde, 0xa0, 0x23, 0x6b, 0xc5, 0xb1, 0x5f, 0x5e, 0x15, 0x93, 0x20, 0x51, 0xec, 0xf6, 0xa9,
0xd0, 0x9b, 0xbc, 0x60, 0xb7, 0x60, 0x49, 0x4c, 0x28, 0x65, 0xaa, 0xda, 0x3f, 0xa8, 0xc3, 0x72,
0xa6, 0x4a, 0x25, 0x29, 0x08, 0x4f, 0x79, 0xe0, 0x0d, 0x8f, 0x02, 0x65, 0xe7, 0x5b, 0xba, 0x13,
0x6d, 0x54, 0x91, 0x3e, 0x2c, 0x4a, 0x03, 0x81, 0xad, 0x69, 0xa2, 0xcc, 0x0a, 0xa8, 0xa5, 0xde,
0x36, 0xb7, 0x30, 0xdd, 0xa1, 0x84, 0xeb, 0x4c, 0x9c, 0xdf, 0x1e, 0x39, 0x81, 0x96, 0xb2, 0x44,
0x84, 0xb0, 0xd6, 0xac, 0x15, 0xd6, 0xd7, 0x5b, 0x17, 0xf4, 0x65, 0x58, 0xb6, 0xce, 0xc4, 0xd6,
0xc8, 0x39, 0x5c, 0x93, 0x75, 0x28, 0x8d, 0xb3, 0xfd, 0x95, 0x5e, 0x69, 0x6e, 0x68, 0xb3, 0x9b,
0x9d, 0x5e, 0xd0, 0x30, 0xf9, 0x00, 0x96, 0xce, 0x5c, 0x2f, 0x96, 0xc3, 0xd2, 0x6c, 0x83, 0x32,
0x76, 0x79, 0xe7, 0x82, 0x2e, 0x9f, 0xf2, 0x8f, 0x0d, 0x15, 0x35, 0xa1, 0xc5, 0xf6, 0x0f, 0x2d,
0x68, 0x98, 0xed, 0x30, 0x32, 0x15, 0xbc, 0x2f, 0x65, 0xa0, 0xb4, 0x26, 0x53, 0xe0, 0xac, 0xab,
0x5c, 0xc8, 0x73, 0x95, 0x75, 0x07, 0xb5, 0x78, 0x51, 0x44, 0xaa, 0xf4, 0x6a, 0x11, 0xa9, 0x72,
0x5e, 0x44, 0xaa, 0xfd, 0xaf, 0x16, 0x90, 0x2c, 0x2d, 0x91, 0x07, 0xdc, 0x57, 0xf7, 0xe9, 0x40,
0x88, 0x94, 0xff, 0xf2, 0x6a, 0xf4, 0x28, 0xd7, 0x4e, 0x7e, 0xcd, 0x18, 0x43, 0x3f, 0xb7, 0xd7,
0x8d, 0x9d, 0x19, 0x27, 0xaf, 0x2a, 0x15, 0x23, 0x2b, 0x5d, 0x1c, 0x23, 0x2b, 0x5f, 0x1c, 0x23,
0x9b, 0x4a, 0xc7, 0xc8, 0xda, 0xff, 0xdf, 0x82, 0xf9, 0x9c, 0x4d, 0xff, 0xe9, 0x4d, 0x9c, 0x6d,
0x93, 0x21, 0x0b, 0x0a, 0x62, 0x9b, 0x74, 0x60, 0xfb, 0x7f, 0xc3, 0x8c, 0x41, 0xe8, 0x3f, 0xbd,
0xfe, 0xd3, 0xf6, 0x1a, 0xa7, 0x33, 0x03, 0xd6, 0xfe, 0xc7, 0x02, 0x90, 0x2c, 0xb3, 0xfd, 0xa7,
0x8e, 0x21, 0xbb, 0x4e, 0xc5, 0x9c, 0x75, 0xfa, 0x0f, 0xd5, 0x03, 0x6f, 0xc1, 0x9c, 0x48, 0x46,
0xd2, 0x22, 0x34, 0x9c, 0x62, 0xb2, 0x15, 0xcc, 0x62, 0x35, 0x03, 0x94, 0x15, 0x23, 0x39, 0x43,
0x53, 0x86, 0xa9, 0x38, 0xa5, 0xdd, 0x86, 0x96, 0x58, 0xa1, 0x9d, 0x53, 0xea, 0xc7, 0x87, 0xe3,
0x23, 0x9e, 0x8d, 0xe3, 0x05, 0xbe, 0xfd, 0xfd, 0xa2, 0x32, 0xba, 0xb1, 0x52, 0xa8, 0xf7, 0xcf,
0x40, 0x5d, 0x17, 0xe6, 0x62, 0x3b, 0x52, 0x01, 0x3a, 0xa6, 0xd8, 0x75, 0x2c, 0xb2, 0x0d, 0x0d,
0x14, 0x59, 0x3d, 0xf5, 0x5d, 0x01, 0xbf, 0x7b, 0x49, 0xe0, 0x61, 0xf7, 0x92, 0x93, 0xfa, 0x86,
0x7c, 0x01, 0x1a, 0xa6, 0x2b, 0x25, 0x6c, 0x84, 0x3c, 0xdb, 0x9c, 0x7d, 0x6e, 0x22, 0x93, 0x0d,
0x68, 0xa6, 0x7d, 0x31, 0x71, 0x14, 0x3f, 0xa1, 0x81, 0x0c, 0x3a, 0xb9, 0x2b, 0x4e, 0xaa, 0xca,
0x18, 0x04, 0xbb, 0x6e, 0x7e, 0xa6, 0x2d, 0xd3, 0x2d, 0xfe, 0xa3, 0x9d, 0x5d, 0x7d, 0x1d, 0x20,
0x81, 0x91, 0x26, 0xd4, 0x1f, 0x1d, 0xec, 0xec, 0x77, 0xb6, 0x76, 0x37, 0xf6, 0xf7, 0x77, 0xf6,
0x9a, 0x97, 0x08, 0x81, 0x06, 0xc6, 0xaf, 0xb6, 0x15, 0xcc, 0x62, 0xb0, 0x8d, 0x2d, 0x1e, 0x1b,
0x13, 0xb0, 0x02, 0x59, 0x80, 0xe6, 0xc3, 0xfd, 0x14, 0xb4, 0xb8, 0x59, 0x55, 0xfc, 0x61, 0x2f,
0xc1, 0x02, 0x4f, 0x36, 0xdb, 0xe4, 0xe4, 0x21, 0x6d, 0x85, 0xdf, 0xb2, 0x60, 0x31, 0x55, 0x91,
0x64, 0x75, 0x70, 0x73, 0xc0, 0xb4, 0x11, 0x4c, 0x20, 0x46, 0x9f, 0xa5, 0xe5, 0x97, 0x92, 0x20,
0xd9, 0x0a, 0x46, 0xf3, 0x9a, 0xa5, 0x98, 0xe2, 0xa4, 0xbc, 0x2a, 0x7b, 0x99, 0xa7, 0xc4, 0x61,
0xf2, 0x9c, 0x31, 0xf0, 0x63, 0x9e, 0xc4, 0xa6, 0x57, 0x24, 0x27, 0x7f, 0xe6, 0x90, 0x65, 0x91,
0x19, 0xf9, 0x86, 0xe9, 0x61, 0x8e, 0x37, 0xb7, 0xce, 0xfe, 0xd3, 0x02, 0x90, 0x2f, 0x8f, 0x69,
0x78, 0x8e, 0x09, 0x19, 0x2a, 0x1c, 0xb8, 0x9c, 0x0e, 0x76, 0x4d, 0x8d, 0xc6, 0x47, 0x5f, 0xa2,
0xe7, 0x32, 0x99, 0xa8, 0xa0, 0x27, 0x13, 0x01, 0x73, 0x8e, 0x55, 0x3a, 0x88, 0xb5, 0x5a, 0xc6,
0x90, 0x44, 0xd5, 0x1f, 0x0f, 0x79, 0xa3, 0xb9, 0x39, 0x3f, 0xa5, 0x8b, 0x73, 0x7e, 0xca, 0x17,
0xe5, 0xfc, 0x7c, 0x02, 0x66, 0xbc, 0xbe, 0x1f, 0x30, 0xb1, 0xc0, 0x14, 0x7b, 0xd4, 0x9a, 0x5a,
0x29, 0x32, 0x67, 0x58, 0x00, 0xf7, 0x19, 0x8c, 0x7c, 0x2e, 0x41, 0xa2, 0xbd, 0x3e, 0xe6, 0x8f,
0xe9, 0x82, 0x62, 0xa7, 0xd7, 0xa7, 0x7b, 0x41, 0xd7, 0x8d, 0x83, 0x50, 0x7d, 0xc8, 0x60, 0x11,
0xf3, 0xfa, 0xa3, 0x60, 0xcc, 0xcc, 0x1c, 0xb9, 0x14, 0x3c, 0x6c, 0x53, 0xe7, 0xd0, 0x03, 0x5c,
0x10, 0xfb, 0xab, 0x50, 0xd3, 0x9a, 0xc0, 0xe4, 0x22, 0x61, 0x42, 0x08, 0x7f, 0xb0, 0xc4, 0x2d,
0x76, 0x9f, 0x0e, 0x1e, 0xf6, 0xc8, 0x9b, 0x30, 0xd7, 0xf3, 0x42, 0x8a, 0x79, 0x62, 0x9d, 0x90,
0x9e, 0xd2, 0x30, 0x92, 0x9e, 0x73, 0x53, 0x55, 0x38, 0x1c, 0x6e, 0xdf, 0x83, 0x79, 0x63, 0x6b,
0x14, 0xe5, 0xca, 0xdc, 0x1b, 0x2b, 0x9b, 0x7b, 0x23, 0xf3, 0x6e, 0xec, 0x9f, 0x2d, 0x40, 0x71,
0x37, 0x18, 0xe9, 0xd1, 0x7e, 0xcb, 0x8c, 0xf6, 0x0b, 0x13, 0xa8, 0xa3, 0x2c, 0x1c, 0xa1, 0x19,
0x0d, 0x20, 0x59, 0x83, 0x86, 0x3b, 0x8c, 0x3b, 0x71, 0xc0, 0x4c, 0xbe, 0x33, 0x37, 0xec, 0x71,
0x72, 0xc6, 0x2d, 0x4e, 0xd5, 0x90, 0x05, 0x28, 0x2a, 0x5b, 0x01, 0x11, 0x58, 0x91, 0xf9, 0x1b,
0x78, 0xea, 0x78, 0x2e, 0x22, 0x67, 0xa2, 0xc4, 0xb8, 0xc5, 0xfc, 0x9e, 0x3b, 0x7b, 0x5c, 0xe2,
0xe7, 0x55, 0x31, 0x73, 0x8c, 0x51, 0x07, 0xa2, 0x89, 0x90, 0xa7, 0x2c, 0xeb, 0xe1, 0xd9, 0x8a,
0x79, 0x06, 0xfb, 0xf7, 0x16, 0x94, 0x71, 0x6d, 0x98, 0xf6, 0xe2, 0xec, 0xad, 0x02, 0xfe, 0xb8,
0x26, 0x33, 0x4e, 0x1a, 0x4c, 0x6c, 0x23, 0xe3, 0xb0, 0xa0, 0x26, 0xa4, 0x67, 0x1d, 0xae, 0x40,
0x95, 0x97, 0x54, 0x76, 0x1d, 0xa7, 0x7b, 0x05, 0x24, 0xd7, 0xa0, 0x74, 0x12, 0x8c, 0xa4, 0xb9,
0x0d, 0xf2, 0xec, 0x2c, 0x18, 0x39, 0x08, 0x4f, 0xc6, 0xc3, 0xda, 0xe3, 0xd3, 0xe2, 0x46, 0x54,
0x1a, 0xcc, 0xcc, 0x48, 0xd5, 0xac, 0xbe, 0x4c, 0x29, 0xa8, 0xbd, 0x06, 0xb3, 0x8c, 0xea, 0xb5,
0xa8, 0xeb, 0x44, 0x56, 0xb6, 0xff, 0xaf, 0x05, 0x15, 0x89, 0x4c, 0x56, 0xa1, 0xc4, 0x58, 0x28,
0xe5, 0xb8, 0xaa, 0x33, 0x73, 0x86, 0xe7, 0x20, 0x06, 0x33, 0x26, 0x30, 0x18, 0x96, 0xf8, 0x49,
0x32, 0x14, 0x96, 0xb8, 0x01, 0x6a, 0xb8, 0x29, 0xeb, 0x39, 0x05, 0xb5, 0xbf, 0x67, 0xc1, 0x8c,
0xd1, 0x07, 0x59, 0x81, 0xda, 0xc0, 0x8d, 0x62, 0x71, 0x0e, 0x29, 0xb6, 0x47, 0x07, 0xe9, 0x1b,
0x5d, 0x30, 0xe3, 0xf0, 0x2a, 0x42, 0x5c, 0xd4, 0x23, 0xc4, 0xb7, 0xa1, 0x9a, 0xe4, 0x85, 0x96,
0x0c, 0xde, 0x67, 0x3d, 0xca, 0x6c, 0x80, 0x04, 0x09, 0x83, 0x8e, 0xc1, 0x20, 0x08, 0xc5, 0xa1,
0x15, 0x2f, 0xd8, 0xf7, 0xa0, 0xa6, 0xe1, 0xeb, 0x31, 0x48, 0xcb, 0x88, 0x41, 0xaa, 0x54, 0x99,
0x42, 0x92, 0x2a, 0x63, 0xff, 0x93, 0x05, 0x33, 0x8c, 0x06, 0x3d, 0xbf, 0x7f, 0x10, 0x0c, 0xbc,
0xee, 0x39, 0xee, 0xbd, 0x24, 0x37, 0x21, 0x12, 0x25, 0x2d, 0x9a, 0x60, 0x46, 0xf5, 0x32, 0xf2,
0x21, 0x58, 0x54, 0x95, 0x19, 0x0f, 0x33, 0x0e, 0x38, 0x72, 0x23, 0xc1, 0x16, 0xc2, 0x6a, 0x33,
0x80, 0x8c, 0xd3, 0x18, 0x00, 0x13, 0x9f, 0x86, 0xde, 0x60, 0xe0, 0x71, 0x5c, 0x6e, 0xd3, 0xe7,
0x55, 0xb1, 0x3e, 0x7b, 0x5e, 0xe4, 0x1e, 0x25, 0x07, 0x31, 0xaa, 0x8c, 0xe1, 0x19, 0xf7, 0xb9,
0x16, 0x9e, 0x99, 0x42, 0xb9, 0x62, 0x02, 0xed, 0x3f, 0x2e, 0x40, 0x4d, 0x9a, 0x08, 0xbd, 0x3e,
0x15, 0x67, 0x8b, 0xa6, 0x60, 0xd4, 0x20, 0xb2, 0xde, 0xf0, 0xc6, 0x34, 0x48, 0x9a, 0x30, 0x8a,
0x59, 0xc2, 0xb8, 0x0a, 0x55, 0x46, 0xa0, 0x6f, 0xa3, 0xdb, 0x27, 0x52, 0xad, 0x15, 0x40, 0xd6,
0xde, 0xc1, 0xda, 0x72, 0x52, 0x8b, 0x80, 0x97, 0x9e, 0x44, 0xde, 0x85, 0xba, 0x68, 0x06, 0x77,
0x0e, 0x25, 0x4f, 0xc2, 0x22, 0xc6, 0xae, 0x3a, 0x06, 0xa6, 0xfc, 0xf2, 0x8e, 0xfc, 0xb2, 0x72,
0xd1, 0x97, 0x12, 0xd3, 0x7e, 0xa0, 0x0e, 0x78, 0x1f, 0x84, 0xee, 0xe8, 0x44, 0xf2, 0xf2, 0x6d,
0x98, 0xf7, 0xfc, 0xee, 0x60, 0xdc, 0xa3, 0x9d, 0xb1, 0xef, 0xfa, 0x7e, 0x30, 0xf6, 0xbb, 0x54,
0xe6, 0xca, 0xe4, 0x55, 0xd9, 0x3d, 0x95, 0x59, 0x89, 0x0d, 0x91, 0x35, 0x28, 0x73, 0x55, 0xc9,
0x75, 0x47, 0x3e, 0xa3, 0x73, 0x14, 0xb2, 0x0a, 0x65, 0xae, 0x31, 0x0b, 0x06, 0xd7, 0x68, 0xbb,
0xea, 0x70, 0x04, 0x26, 0x76, 0x30, 0xb9, 0xd6, 0x14, 0x3b, 0xa6, 0xde, 0x99, 0xea, 0x62, 0xfa,
0xad, 0xbd, 0x00, 0x64, 0x9f, 0x73, 0x8a, 0x7e, 0x36, 0xf4, 0x83, 0x22, 0xd4, 0x34, 0x30, 0x93,
0x20, 0x7d, 0x36, 0xe0, 0x4e, 0xcf, 0x73, 0x87, 0x34, 0xa6, 0xa1, 0xe0, 0x8e, 0x14, 0x94, 0xe1,
0xb9, 0xa7, 0xfd, 0x4e, 0x30, 0x8e, 0x3b, 0x3d, 0xda, 0x0f, 0x29, 0xd7, 0xa6, 0x4c, 0x35, 0x19,
0x50, 0x86, 0xc7, 0xe8, 0x53, 0xc3, 0xe3, 0x14, 0x94, 0x82, 0xca, 0x93, 0x1e, 0xbe, 0x46, 0xa5,
0xe4, 0xa4, 0x87, 0xaf, 0x48, 0x5a, 0xf6, 0x95, 0x73, 0x64, 0xdf, 0x3b, 0xb0, 0xc4, 0xa5, 0x9c,
0x90, 0x07, 0x9d, 0x14, 0x61, 0x4d, 0xa8, 0x25, 0x6b, 0xd0, 0x64, 0x63, 0x96, 0x2c, 0x11, 0x79,
0xdf, 0xe4, 0x51, 0x53, 0xcb, 0xc9, 0xc0, 0x19, 0x2e, 0x86, 0x2f, 0x75, 0x5c, 0x7e, 0xf2, 0x9d,
0x81, 0x23, 0xae, 0xfb, 0xdc, 0xc4, 0xad, 0x0a, 0xdc, 0x14, 0x9c, 0xdc, 0x85, 0xe5, 0x21, 0xed,
0x79, 0xae, 0xd9, 0x04, 0x46, 0x80, 0x79, 0x7a, 0xcb, 0xa4, 0x6a, 0x7b, 0x06, 0x6a, 0x87, 0x71,
0x30, 0x92, 0xdb, 0xd9, 0x80, 0x3a, 0x2f, 0x8a, 0x6c, 0xa7, 0x2b, 0x70, 0x19, 0xe9, 0xef, 0x71,
0x30, 0x0a, 0x06, 0x41, 0xff, 0xdc, 0x70, 0xba, 0xfe, 0xdc, 0x82, 0x79, 0xa3, 0x36, 0xf1, 0xba,
0x30, 0x5e, 0x23, 0xd3, 0x54, 0x38, 0xc9, 0xce, 0x69, 0xc2, 0x9b, 0x23, 0xf2, 0xd0, 0xf8, 0x13,
0x91, 0xb9, 0xb2, 0x91, 0xdc, 0x60, 0x91, 0x1f, 0x72, 0xfa, 0x6d, 0x65, 0xe9, 0x57, 0x7c, 0x2f,
0x2f, 0xb0, 0xc8, 0x26, 0xbe, 0x20, 0x72, 0x0f, 0xb8, 0x13, 0x26, 0xc3, 0x73, 0xca, 0x6d, 0xd3,
0x9d, 0x74, 0x39, 0x82, 0xae, 0x02, 0x46, 0xf6, 0x2f, 0x58, 0x00, 0xc9, 0xe8, 0xf0, 0xc4, 0x5a,
0x29, 0x20, 0x7e, 0x5b, 0x4a, 0x53, 0x36, 0x6f, 0x40, 0x5d, 0x9d, 0x74, 0x26, 0x3a, 0xad, 0x26,
0x61, 0xcc, 0xe6, 0xbe, 0x09, 0xb3, 0xfd, 0x41, 0x70, 0x84, 0x06, 0x01, 0xa6, 0xcf, 0x45, 0x22,
0xe7, 0xab, 0xc1, 0xc1, 0xf7, 0x05, 0x34, 0x51, 0x80, 0x25, 0x4d, 0x01, 0xda, 0xbf, 0x58, 0x50,
0x07, 0x53, 0xc9, 0x9c, 0x27, 0xf2, 0x27, 0xb9, 0x93, 0x11, 0xc4, 0x13, 0xce, 0x81, 0xd0, 0xac,
0x3d, 0xb8, 0x30, 0x4e, 0x76, 0x0f, 0x1a, 0x21, 0x97, 0x74, 0x52, 0x0c, 0x96, 0x5e, 0x22, 0x06,
0x67, 0x42, 0x43, 0x4b, 0x7e, 0x0a, 0x9a, 0x6e, 0xef, 0x94, 0x86, 0xb1, 0x87, 0x91, 0x0a, 0x34,
0x51, 0xb8, 0xf0, 0x9e, 0xd5, 0xe0, 0x68, 0x39, 0xdc, 0x84, 0x59, 0x91, 0x67, 0xa7, 0x30, 0xc5,
0x0d, 0x84, 0x04, 0xcc, 0x10, 0xed, 0xef, 0xca, 0x33, 0x30, 0x73, 0x0f, 0x27, 0xaf, 0x88, 0x3e,
0xbb, 0x42, 0x6a, 0x76, 0x9f, 0x10, 0xe7, 0x51, 0x3d, 0x19, 0x0e, 0x29, 0x6a, 0x79, 0x2a, 0x3d,
0x71, 0x7e, 0x68, 0x2e, 0x69, 0xe9, 0x55, 0x96, 0xd4, 0xfe, 0x91, 0x05, 0xd3, 0xbb, 0xc1, 0x68,
0x57, 0x64, 0xec, 0x20, 0x23, 0xa8, 0x04, 0x57, 0x59, 0x7c, 0x49, 0x2e, 0x4f, 0xae, 0x65, 0x30,
0x93, 0xb6, 0x0c, 0xfe, 0x07, 0x5c, 0xc1, 0x60, 0x5c, 0x18, 0x8c, 0x82, 0x90, 0x31, 0xa3, 0x3b,
0xe0, 0x66, 0x40, 0xe0, 0xc7, 0x27, 0x52, 0x00, 0xbe, 0x0c, 0x05, 0x3d, 0x64, 0xe6, 0xd5, 0x71,
0xa3, 0x5e, 0x58, 0x32, 0x5c, 0x2e, 0x66, 0x2b, 0xec, 0xcf, 0x43, 0x15, 0x4d, 0x71, 0x9c, 0xd6,
0x5b, 0x50, 0x3d, 0x09, 0x46, 0x9d, 0x13, 0xcf, 0x8f, 0x25, 0x73, 0x37, 0x12, 0x1b, 0x79, 0x17,
0x17, 0x44, 0x21, 0xd8, 0xbf, 0x3a, 0x05, 0xd3, 0x0f, 0xfd, 0xd3, 0xc0, 0xeb, 0xe2, 0x79, 0xdb,
0x90, 0x0e, 0x03, 0x99, 0xee, 0xcb, 0xfe, 0x93, 0xab, 0x30, 0x8d, 0xf9, 0x6d, 0x23, 0x4e, 0xb4,
0x75, 0x7e, 0x2e, 0x2e, 0x40, 0xcc, 0xbc, 0x08, 0x93, 0x8b, 0x19, 0x9c, 0x7d, 0x34, 0x08, 0x73,
0x52, 0x42, 0xfd, 0x62, 0x85, 0x28, 0x25, 0xe9, 0xd4, 0x65, 0x2d, 0x9d, 0x9a, 0xf5, 0x25, 0x32,
0x8c, 0x78, 0x0a, 0x0a, 0xef, 0x4b, 0x80, 0xd0, 0xb1, 0x0a, 0x29, 0x0f, 0xa6, 0xa2, 0xb1, 0x32,
0x2d, 0x1c, 0x2b, 0x1d, 0xc8, 0x0c, 0x1a, 0xfe, 0x01, 0xc7, 0xe1, 0xe2, 0x5b, 0x07, 0x31, 0x13,
0x31, 0x7d, 0xa7, 0xa6, 0xca, 0x69, 0x3f, 0x05, 0x66, 0x32, 0xbe, 0x47, 0x95, 0x40, 0xe5, 0xf3,
0x00, 0x7e, 0xf9, 0x24, 0x0d, 0xd7, 0xdc, 0x31, 0x9e, 0x8a, 0x28, 0xdd, 0x31, 0x46, 0x30, 0xee,
0x60, 0x70, 0xe4, 0x76, 0x9f, 0xe1, 0x95, 0x29, 0x3c, 0x01, 0xab, 0x3a, 0x26, 0x10, 0xf3, 0x84,
0x92, 0x5d, 0xc5, 0x0c, 0x82, 0x92, 0xa3, 0x83, 0xc8, 0x1d, 0xa8, 0xa1, 0x0b, 0x2a, 0xf6, 0xb5,
0x81, 0xfb, 0xda, 0xd4, 0x7d, 0x54, 0xdc, 0x59, 0x1d, 0x49, 0x3f, 0x0b, 0x9c, 0xcd, 0x24, 0x07,
0xba, 0xbd, 0x9e, 0x38, 0x42, 0x6d, 0x72, 0x77, 0x5a, 0x01, 0x98, 0x3e, 0x16, 0x0b, 0xc6, 0x11,
0xe6, 0x10, 0xc1, 0x80, 0x91, 0x6b, 0x50, 0x61, 0xee, 0xd1, 0xc8, 0xf5, 0x7a, 0x98, 0x5d, 0xc8,
0xbd, 0x34, 0x05, 0x63, 0x6d, 0xc8, 0xff, 0xa8, 0xe8, 0xe6, 0x71, 0x55, 0x0c, 0x18, 0x5b, 0x1b,
0x55, 0x46, 0x66, 0x5a, 0xe0, 0x3b, 0x6a, 0x00, 0xc9, 0xdb, 0x78, 0x90, 0x15, 0xd3, 0xd6, 0x22,
0x06, 0xca, 0xae, 0x88, 0x39, 0x0b, 0xa2, 0x95, 0xbf, 0x87, 0x0c, 0xc5, 0xe1, 0x98, 0xf6, 0x06,
0xd4, 0x75, 0x30, 0xa9, 0x40, 0xe9, 0xd1, 0xc1, 0xce, 0x7e, 0xf3, 0x12, 0xa9, 0xc1, 0xf4, 0xe1,
0xce, 0xe3, 0xc7, 0x7b, 0x3b, 0xdb, 0x4d, 0x8b, 0xd4, 0xa1, 0xa2, 0x92, 0xba, 0x0a, 0xac, 0xb4,
0xb1, 0xb5, 0xb5, 0x73, 0xf0, 0x78, 0x67, 0xbb, 0x59, 0xb4, 0x63, 0x20, 0x1b, 0xbd, 0x9e, 0x68,
0x45, 0x05, 0x09, 0x12, 0x7a, 0xb6, 0x0c, 0x7a, 0xce, 0xa1, 0xa9, 0x42, 0x3e, 0x4d, 0xbd, 0x74,
0xe5, 0xed, 0x1d, 0xa8, 0x1d, 0x68, 0xf7, 0x87, 0x90, 0xbd, 0xe4, 0xcd, 0x21, 0xc1, 0x96, 0x1a,
0x44, 0x1b, 0x4e, 0x41, 0x1f, 0x8e, 0xfd, 0x3b, 0x16, 0x4f, 0xd2, 0x57, 0xc3, 0xe7, 0x7d, 0xdb,
0x50, 0x57, 0xd1, 0xaa, 0x24, 0x5f, 0xd3, 0x80, 0x31, 0x1c, 0x1c, 0x4a, 0x27, 0x38, 0x3e, 0x8e,
0xa8, 0xcc, 0xae, 0x32, 0x60, 0x8c, 0x2f, 0x98, 0x6d, 0xc6, 0xec, 0x1c, 0x8f, 0xf7, 0x10, 0x89,
0x2c, 0xab, 0x0c, 0x9c, 0x49, 0x79, 0x11, 0x90, 0x91, 0x79, 0x65, 0xaa, 0xac, 0xd2, 0x4a, 0xd3,
0xab, 0xbc, 0x06, 0x15, 0xd5, 0xae, 0x29, 0xc0, 0x24, 0xa6, 0xaa, 0x67, 0x82, 0x12, 0xbd, 0x15,
0x63, 0xd0, 0x5c, 0x68, 0x67, 0x2b, 0xc8, 0x2d, 0x20, 0xc7, 0x5e, 0x98, 0x46, 0x2f, 0x22, 0x7a,
0x4e, 0x8d, 0xfd, 0x14, 0xe6, 0x25, 0x21, 0x69, 0xa6, 0x95, 0xb9, 0x89, 0xd6, 0x45, 0xec, 0x53,
0xc8, 0xb2, 0x8f, 0xfd, 0x6f, 0x16, 0x4c, 0x8b, 0x9d, 0xce, 0xdc, 0x41, 0xe3, 0xfb, 0x6c, 0xc0,
0x48, 0xcb, 0xb8, 0x7f, 0x82, 0xbc, 0x26, 0x84, 0x66, 0x46, 0x2c, 0x16, 0xf3, 0xc4, 0x22, 0x81,
0xd2, 0xc8, 0x8d, 0x4f, 0xd0, 0x53, 0xaf, 0x3a, 0xf8, 0x9f, 0x34, 0x79, 0x5c, 0x89, 0x8b, 0x60,
0x8c, 0x29, 0xe5, 0xdd, 0xb6, 0xe3, 0xda, 0x3e, 0x7b, 0xdb, 0xee, 0x2a, 0x54, 0x71, 0x00, 0x9d,
0x24, 0x6c, 0x94, 0x00, 0x18, 0xe5, 0xf2, 0x02, 0xf2, 0xb5, 0x48, 0x05, 0x4f, 0x20, 0xf6, 0x22,
0xdf, 0x79, 0xb1, 0x04, 0xea, 0x10, 0x5a, 0xa4, 0xf1, 0x26, 0xe0, 0x84, 0x22, 0xc4, 0x00, 0xd2,
0x14, 0x21, 0x50, 0x1d, 0x55, 0x6f, 0xb7, 0xa1, 0xb5, 0x4d, 0x07, 0x34, 0xa6, 0x1b, 0x83, 0x41,
0xba, 0xfd, 0x2b, 0x70, 0x39, 0xa7, 0x4e, 0x58, 0xd3, 0x5f, 0x86, 0xc5, 0x0d, 0x9e, 0xf2, 0xf8,
0xd3, 0x4a, 0xe3, 0xb1, 0x5b, 0xb0, 0x94, 0x6e, 0x52, 0x74, 0x76, 0x1f, 0xe6, 0xb6, 0xe9, 0xd1,
0xb8, 0xbf, 0x47, 0x4f, 0x93, 0x8e, 0x08, 0x94, 0xa2, 0x93, 0xe0, 0x4c, 0x30, 0x26, 0xfe, 0x27,
0xaf, 0x01, 0x0c, 0x18, 0x4e, 0x27, 0x1a, 0xd1, 0xae, 0xbc, 0xf2, 0x81, 0x90, 0xc3, 0x11, 0xed,
0xda, 0xef, 0x00, 0xd1, 0xdb, 0x11, 0xeb, 0xc5, 0xb4, 0xe0, 0xf8, 0xa8, 0x13, 0x9d, 0x47, 0x31,
0x1d, 0xca, 0xbb, 0x2c, 0x3a, 0xc8, 0xbe, 0x09, 0xf5, 0x03, 0xf7, 0xdc, 0xa1, 0x1f, 0x8a, 0xab,
0x87, 0xcb, 0x30, 0x3d, 0x72, 0xcf, 0x99, 0x98, 0x52, 0xf1, 0x2c, 0xac, 0xb6, 0xff, 0xa5, 0x00,
0x53, 0x1c, 0x93, 0xb5, 0xda, 0xa3, 0x51, 0xec, 0xf9, 0x48, 0x58, 0xb2, 0x55, 0x0d, 0x94, 0x21,
0xe5, 0x42, 0x0e, 0x29, 0x0b, 0x6f, 0x4f, 0xa6, 0xcf, 0x0b, 0x7a, 0x35, 0x60, 0x8c, 0xb8, 0x92,
0x7c, 0x3a, 0x1e, 0x50, 0x49, 0x00, 0xa9, 0xd0, 0x67, 0xa2, 0x6b, 0xf9, 0xf8, 0x24, 0x97, 0x0a,
0xca, 0xd5, 0x41, 0xb9, 0x1a, 0x7d, 0x9a, 0x13, 0x78, 0x46, 0xa3, 0x67, 0x34, 0x77, 0xe5, 0x15,
0x34, 0x37, 0x77, 0x01, 0x5f, 0xa6, 0xb9, 0xe1, 0x15, 0x34, 0xb7, 0x4d, 0xa0, 0x89, 0xf7, 0xf2,
0x98, 0x6d, 0x28, 0x69, 0xf7, 0xdb, 0x16, 0x34, 0x05, 0x15, 0xa9, 0x3a, 0xf2, 0x86, 0x61, 0x03,
0xe7, 0x26, 0xa6, 0x5f, 0x87, 0x19, 0xb4, 0x4c, 0x55, 0x8c, 0x57, 0x04, 0xa4, 0x0d, 0x20, 0x9b,
0x87, 0x3c, 0x3f, 0x1e, 0x7a, 0x03, 0xb1, 0x29, 0x3a, 0x48, 0x86, 0x89, 0x43, 0x57, 0xe4, 0x95,
0x59, 0x8e, 0x2a, 0xdb, 0x7f, 0x62, 0xc1, 0x9c, 0x36, 0x60, 0x41, 0x85, 0xf7, 0x40, 0x72, 0x03,
0x0f, 0xf8, 0x72, 0xce, 0x5d, 0x36, 0xd9, 0x26, 0xf9, 0xcc, 0x40, 0xc6, 0xcd, 0x74, 0xcf, 0x71,
0x80, 0xd1, 0x78, 0x28, 0x84, 0xa8, 0x0e, 0x62, 0x84, 0x74, 0x46, 0xe9, 0x33, 0x85, 0xc2, 0xc5,
0xb8, 0x01, 0xc3, 0xa8, 0x1a, 0xb3, 0xa8, 0x15, 0x52, 0x49, 0x44, 0xd5, 0x74, 0xa0, 0xfd, 0x57,
0x16, 0xcc, 0x73, 0xd7, 0x48, 0x38, 0x9e, 0xea, 0x06, 0xd2, 0x14, 0xf7, 0x05, 0x39, 0x47, 0xee,
0x5e, 0x72, 0x44, 0x99, 0x7c, 0xf6, 0x15, 0xdd, 0x39, 0x95, 0xec, 0x36, 0x61, 0x2f, 0x8a, 0x79,
0x7b, 0xf1, 0x92, 0x95, 0xce, 0x0b, 0x70, 0x96, 0x73, 0x03, 0x9c, 0x9b, 0xd3, 0x50, 0x8e, 0xba,
0xc1, 0x88, 0xda, 0x4b, 0xb0, 0x60, 0x4e, 0x4e, 0x88, 0xa0, 0xef, 0x58, 0xd0, 0xba, 0xcf, 0x0f,
0x02, 0x3c, 0xbf, 0xbf, 0xeb, 0x45, 0x71, 0x10, 0xaa, 0x8b, 0x9a, 0xd7, 0x00, 0xa2, 0xd8, 0x0d,
0x63, 0x9e, 0xd2, 0x2c, 0x02, 0x8b, 0x09, 0x84, 0x8d, 0x91, 0xfa, 0x3d, 0x5e, 0xcb, 0xf7, 0x46,
0x95, 0x33, 0x36, 0x84, 0x70, 0xde, 0x0c, 0x4d, 0x7c, 0x83, 0x27, 0x7f, 0x32, 0x5b, 0x81, 0x9e,
0xa2, 0x5c, 0xe7, 0x5e, 0x51, 0x0a, 0x6a, 0xff, 0xa5, 0x05, 0xb3, 0xc9, 0x20, 0xf1, 0x58, 0xd4,
0x94, 0x0e, 0x42, 0xfd, 0x26, 0xd2, 0x41, 0x86, 0x3c, 0x3d, 0xa6, 0x8f, 0xc5, 0xd8, 0x34, 0x08,
0x72, 0xac, 0x28, 0x05, 0x63, 0x69, 0xe0, 0xe8, 0x20, 0x9e, 0xca, 0xc5, 0x2c, 0x01, 0x61, 0xd5,
0x88, 0x12, 0x66, 0xa4, 0x0f, 0x63, 0xfc, 0x8a, 0x07, 0x67, 0x65, 0x51, 0xaa, 0xd2, 0x69, 0x84,
0xa2, 0x2a, 0xd5, 0x0f, 0x55, 0x2a, 0x7c, 0x7d, 0x64, 0xd9, 0xfe, 0x25, 0x0b, 0x2e, 0xe7, 0x2c,
0xbc, 0xe0, 0x9a, 0x6d, 0x98, 0x3b, 0x56, 0x95, 0x72, 0x71, 0x38, 0xeb, 0x2c, 0xc9, 0x43, 0x3b,
0x73, 0x41, 0x9c, 0xec, 0x07, 0xca, 0x2e, 0xe2, 0xcb, 0x6d, 0x24, 0x4b, 0x66, 0x2b, 0xec, 0x03,
0x68, 0xef, 0x3c, 0x67, 0x4c, 0xb8, 0xa5, 0xbf, 0x39, 0x22, 0x69, 0xe1, 0x4e, 0x46, 0xc8, 0x5c,
0xec, 0x68, 0x1f, 0xc3, 0x8c, 0xd1, 0x16, 0xf9, 0xf4, 0xab, 0x36, 0x92, 0x0a, 0x4f, 0x63, 0x89,
0x3f, 0x9a, 0x22, 0x53, 0x36, 0x35, 0x90, 0x7d, 0x0a, 0xb3, 0xef, 0x8d, 0x07, 0xb1, 0x97, 0x3c,
0xa0, 0x42, 0x3e, 0x2b, 0x3e, 0xc2, 0x26, 0xe4, 0xd2, 0xe5, 0x76, 0xa5, 0xe3, 0xb1, 0x15, 0x1b,
0xb2, 0x96, 0x3a, 0xd9, 0x1e, 0xb3, 0x15, 0xf6, 0x65, 0x58, 0x4e, 0xba, 0xe4, 0x6b, 0x27, 0x05,
0xf5, 0x77, 0x2d, 0x9e, 0xed, 0x60, 0xbe, 0xe7, 0x42, 0x1e, 0xc0, 0x7c, 0xe4, 0xf9, 0xfd, 0x01,
0xd5, 0xdb, 0x89, 0xc4, 0x4a, 0x2c, 0x9a, 0xc3, 0x13, 0x6f, 0xbe, 0x38, 0x79, 0x5f, 0x30, 0x02,
0xc9, 0x1f, 0x68, 0x42, 0x20, 0xa9, 0x25, 0xc9, 0x9b, 0xc0, 0x17, 0xa1, 0x61, 0x76, 0x46, 0xee,
0x8a, 0x6c, 0xcb, 0x64, 0x64, 0x7a, 0x2c, 0xdb, 0xa4, 0x0c, 0x03, 0xd3, 0xfe, 0x96, 0x05, 0x2d,
0x87, 0x32, 0x32, 0xa6, 0x5a, 0xa7, 0x82, 0x7a, 0xee, 0x65, 0x9a, 0x9d, 0x3c, 0x61, 0x95, 0xc5,
0x29, 0xe7, 0x7a, 0x6b, 0xe2, 0xa6, 0xec, 0x5e, 0xca, 0x99, 0xd5, 0x66, 0x05, 0xa6, 0xc4, 0xfc,
0x96, 0x61, 0x51, 0x0c, 0x49, 0x0e, 0x27, 0x09, 0x9a, 0x1a, 0x9d, 0x1a, 0x41, 0xd3, 0x23, 0x68,
0xf1, 0x1b, 0xb4, 0xfa, 0x3c, 0x92, 0xdc, 0x06, 0xbe, 0x1d, 0x51, 0x47, 0xbf, 0x4c, 0x6b, 0x02,
0x19, 0xc9, 0xf2, 0x61, 0x71, 0x1c, 0x7e, 0x0a, 0xad, 0x83, 0xd6, 0x5e, 0x40, 0x4d, 0xbb, 0x8f,
0x4c, 0x96, 0x61, 0xfe, 0xe9, 0xc3, 0xc7, 0xfb, 0x3b, 0x87, 0x87, 0x9d, 0x83, 0x27, 0x9b, 0x5f,
0xda, 0xf9, 0x6a, 0x67, 0x77, 0xe3, 0x70, 0xb7, 0x79, 0x89, 0x2c, 0x01, 0xd9, 0xdf, 0x39, 0x7c,
0xbc, 0xb3, 0x6d, 0xc0, 0x2d, 0x72, 0x0d, 0xda, 0x4f, 0xf6, 0x9f, 0x1c, 0xee, 0x6c, 0x77, 0xf2,
0xbe, 0x2b, 0x90, 0xd7, 0xe0, 0xb2, 0xa8, 0xcf, 0xf9, 0xbc, 0x78, 0xe7, 0x5b, 0x45, 0x68, 0xf0,
0xe4, 0x0d, 0xfe, 0x9c, 0x10, 0x0d, 0xc9, 0x7b, 0x30, 0x2d, 0xde, 0xa5, 0x22, 0x72, 0x5f, 0xcc,
0x97, 0xb0, 0xda, 0x4b, 0x69, 0xb0, 0x58, 0xcc, 0xf9, 0xff, 0xf7, 0xa3, 0xbf, 0xfb, 0x95, 0xc2,
0x0c, 0xa9, 0xad, 0x9f, 0xbe, 0xbd, 0xde, 0xa7, 0x7e, 0xc4, 0xda, 0xf8, 0x3a, 0x40, 0xf2, 0xda,
0x12, 0x69, 0x29, 0xdf, 0x2d, 0xf5, 0x14, 0x55, 0xfb, 0x72, 0x4e, 0x8d, 0x68, 0xf7, 0x32, 0xb6,
0x3b, 0x6f, 0x37, 0x58, 0xbb, 0x9e, 0xef, 0xc5, 0xfc, 0xe5, 0xa5, 0x77, 0xad, 0x35, 0xd2, 0x83,
0xba, 0xfe, 0x0e, 0x12, 0x91, 0x01, 0xe4, 0x9c, 0x97, 0x9c, 0xda, 0x57, 0x72, 0xeb, 0x24, 0x21,
0x60, 0x1f, 0x8b, 0x76, 0x93, 0xf5, 0x31, 0x46, 0x8c, 0xa4, 0x97, 0x01, 0x67, 0x8f, 0xe4, 0xb9,
0x23, 0x72, 0x55, 0xa3, 0xd8, 0xcc, 0x63, 0x4b, 0xed, 0xd7, 0x26, 0xd4, 0x8a, 0xbe, 0x5e, 0xc3,
0xbe, 0x96, 0x6d, 0xc2, 0xfa, 0xea, 0x22, 0x8e, 0x7c, 0x6c, 0xe9, 0x5d, 0x6b, 0xed, 0xce, 0xaf,
0xdd, 0x80, 0xaa, 0x3a, 0x2c, 0x22, 0x1f, 0xc0, 0x8c, 0x91, 0x5d, 0x43, 0xe4, 0x34, 0xf2, 0x92,
0x71, 0xda, 0x57, 0xf3, 0x2b, 0x45, 0xc7, 0xd7, 0xb0, 0xe3, 0x16, 0x59, 0x62, 0x1d, 0x8b, 0xf4,
0x94, 0x75, 0xcc, 0x13, 0xe3, 0x97, 0x3e, 0x9e, 0x69, 0x62, 0x80, 0x77, 0x76, 0x35, 0xcd, 0x99,
0x46, 0x6f, 0xaf, 0x4d, 0xa8, 0x15, 0xdd, 0x5d, 0xc5, 0xee, 0x96, 0xc8, 0x82, 0xde, 0x9d, 0x3a,
0xc4, 0xa1, 0x78, 0x53, 0x49, 0x7f, 0x29, 0x88, 0xbc, 0xa6, 0x08, 0x2b, 0xef, 0x05, 0x21, 0x45,
0x22, 0xd9, 0x67, 0x84, 0xec, 0x16, 0x76, 0x45, 0x08, 0x6e, 0x9f, 0xfe, 0x50, 0x10, 0x39, 0x82,
0x9a, 0xf6, 0xba, 0x05, 0xb9, 0x3c, 0xf1, 0x25, 0x8e, 0x76, 0x3b, 0xaf, 0x2a, 0x6f, 0x2a, 0x7a,
0xfb, 0xeb, 0x4c, 0xbf, 0x7f, 0x0d, 0xaa, 0xea, 0xbd, 0x04, 0xb2, 0xac, 0xbd, 0x5f, 0xa1, 0xbf,
0xef, 0xd0, 0x6e, 0x65, 0x2b, 0xf2, 0x88, 0x4f, 0x6f, 0x9d, 0x11, 0xdf, 0x53, 0xa8, 0x69, 0x6f,
0x22, 0xa8, 0x09, 0x64, 0xdf, 0x5d, 0x50, 0x13, 0xc8, 0x79, 0x42, 0xc1, 0x9e, 0xc3, 0x2e, 0x6a,
0xa4, 0x8a, 0xf4, 0x1d, 0x3f, 0x0f, 0x22, 0xb2, 0x07, 0x8b, 0x42, 0xdc, 0x1d, 0xd1, 0x8f, 0xb3,
0x0d, 0x39, 0x8f, 0x33, 0xdd, 0xb6, 0xc8, 0x3d, 0xa8, 0xc8, 0xa7, 0x2f, 0xc8, 0x52, 0xfe, 0x13,
0x1e, 0xed, 0xe5, 0x0c, 0x5c, 0x48, 0xd3, 0xaf, 0x02, 0x24, 0x0f, 0x30, 0x28, 0x21, 0x91, 0x79,
0xd0, 0x41, 0x51, 0x40, 0xf6, 0xb5, 0x06, 0x7b, 0x09, 0x27, 0xd8, 0x24, 0x28, 0x24, 0x7c, 0x7a,
0x26, 0xef, 0x07, 0x7e, 0x03, 0x6a, 0xda, 0x1b, 0x0c, 0x6a, 0xf9, 0xb2, 0xef, 0x37, 0xa8, 0xe5,
0xcb, 0x79, 0xb2, 0xc1, 0x6e, 0x63, 0xeb, 0x0b, 0xf6, 0x2c, 0x6b, 0x3d, 0xf2, 0xfa, 0xfe, 0x90,
0x23, 0xb0, 0x0d, 0x3a, 0x81, 0x19, 0xe3, 0xa1, 0x05, 0xc5, 0xa1, 0x79, 0xcf, 0x38, 0x28, 0x0e,
0xcd, 0x7d, 0x9b, 0x41, 0xd2, 0x99, 0x3d, 0xc7, 0xfa, 0x39, 0x45, 0x14, 0xad, 0xa7, 0xf7, 0xa1,
0xa6, 0x3d, 0x9a, 0xa0, 0xe6, 0x92, 0x7d, 0x9f, 0x41, 0xcd, 0x25, 0xef, 0x8d, 0x85, 0x05, 0xec,
0xa3, 0x61, 0x23, 0x29, 0xe0, 0xf5, 0x3a, 0xd6, 0xf6, 0x07, 0xd0, 0x30, 0x9f, 0x51, 0x50, 0xbc,
0x9f, 0xfb, 0x20, 0x83, 0xe2, 0xfd, 0x09, 0x6f, 0x2f, 0x08, 0x92, 0x5e, 0x9b, 0x57, 0x9d, 0xac,
0x7f, 0x24, 0x92, 0x48, 0x5e, 0x90, 0x2f, 0x33, 0x01, 0x27, 0xee, 0x3b, 0x92, 0x65, 0x8d, 0x6a,
0xf5, 0x5b, 0x91, 0x8a, 0x5f, 0x32, 0x57, 0x23, 0x4d, 0x62, 0xe6, 0x17, 0x04, 0x51, 0x6b, 0xe1,
0xbd, 0x47, 0x4d, 0x6b, 0xe9, 0x57, 0x23, 0x35, 0xad, 0x65, 0x5c, 0x8f, 0x4c, 0x6b, 0xad, 0xd8,
0x63, 0x6d, 0xf8, 0x30, 0x9b, 0xca, 0x00, 0x56, 0x5c, 0x91, 0x7f, 0x65, 0xa2, 0x7d, 0xed, 0xe5,
0x89, 0xc3, 0xa6, 0x04, 0x91, 0x42, 0x70, 0x5d, 0x5e, 0x50, 0xf9, 0x5f, 0x50, 0xd7, 0xaf, 0xac,
0x13, 0x9d, 0x95, 0xd3, 0x3d, 0x5d, 0xc9, 0xad, 0x33, 0x37, 0x97, 0xd4, 0xf5, 0x6e, 0xc8, 0x57,
0x60, 0x49, 0xb1, 0xba, 0x9e, 0x54, 0x1a, 0x91, 0xd7, 0x73, 0x52, 0x4d, 0x75, 0x23, 0xa8, 0x7d,
0x79, 0x62, 0x2e, 0xea, 0x6d, 0x8b, 0x11, 0x8d, 0x79, 0x17, 0x38, 0x51, 0x18, 0x79, 0x57, 0xa0,
0x13, 0x85, 0x91, 0x7b, 0x81, 0x58, 0x12, 0x0d, 0x99, 0x37, 0xd6, 0x88, 0x9f, 0xf3, 0x91, 0xf7,
0x61, 0x56, 0x4b, 0xdb, 0x3f, 0x3c, 0xf7, 0xbb, 0x8a, 0x01, 0xb2, 0xf7, 0xbb, 0xda, 0x79, 0x26,
0xbe, 0xbd, 0x8c, 0xed, 0xcf, 0xd9, 0xc6, 0xe2, 0x30, 0xe2, 0xdf, 0x82, 0x9a, 0x7e, 0x25, 0xe0,
0x25, 0xed, 0x2e, 0x6b, 0x55, 0xfa, 0xf5, 0xa4, 0xdb, 0x16, 0xf9, 0x0d, 0x0b, 0xea, 0x46, 0x82,
0xbd, 0x71, 0x9a, 0x9d, 0x6a, 0xa7, 0xa5, 0xd7, 0xe9, 0x0d, 0xd9, 0x0e, 0x0e, 0x72, 0x6f, 0xed,
0x8b, 0xc6, 0x22, 0x7c, 0x64, 0xc4, 0x71, 0x6e, 0xa5, 0x9f, 0xdb, 0x7a, 0x91, 0x46, 0xd0, 0xef,
0xc0, 0xbd, 0xb8, 0x6d, 0x91, 0xef, 0x59, 0xd0, 0x30, 0xa3, 0x8f, 0x6a, 0xab, 0x72, 0xe3, 0x9c,
0x6a, 0xab, 0x26, 0x84, 0x2c, 0xdf, 0xc7, 0x51, 0x3e, 0x5e, 0x73, 0x8c, 0x51, 0x8a, 0x5b, 0xe2,
0x3f, 0xd9, 0x68, 0xc9, 0xbb, 0xfc, 0x45, 0x3e, 0x19, 0x12, 0x27, 0x9a, 0xd6, 0x48, 0x6f, 0xaf,
0xfe, 0x8a, 0xdc, 0xaa, 0x75, 0xdb, 0x22, 0xdf, 0xe0, 0xcf, 0x4c, 0x89, 0x6f, 0x91, 0x4a, 0x5e,
0xf5, 0x7b, 0xfb, 0x3a, 0xce, 0xe9, 0x9a, 0x7d, 0xd9, 0x98, 0x53, 0x5a, 0x1f, 0x6f, 0xf0, 0xd1,
0x89, 0x07, 0xe0, 0x12, 0x85, 0x92, 0x79, 0x14, 0x6e, 0xf2, 0x20, 0x87, 0x7c, 0x90, 0x02, 0xdd,
0x20, 0xe5, 0x57, 0x6c, 0xc6, 0x5e, 0xc3, 0xb1, 0x5e, 0xb7, 0x5f, 0x9f, 0x38, 0xd6, 0x75, 0x8c,
0x21, 0xb2, 0x11, 0x1f, 0x00, 0x24, 0xc7, 0x57, 0x24, 0x75, 0x7c, 0xa2, 0x18, 0x3c, 0x7b, 0xc2,
0x65, 0xf2, 0x8b, 0x3c, 0x65, 0x61, 0x2d, 0x7e, 0x8d, 0x8b, 0xab, 0x87, 0xf2, 0xe0, 0x45, 0x37,
0x4a, 0xcc, 0x73, 0x26, 0xc3, 0x28, 0x49, 0xb7, 0x6f, 0x08, 0x2b, 0x75, 0x8a, 0xf3, 0x04, 0x66,
0xf6, 0x82, 0xe0, 0xd9, 0x78, 0xa4, 0x8e, 0xa2, 0xcd, 0xf0, 0xfe, 0xae, 0x1b, 0x9d, 0xb4, 0x53,
0xb3, 0xb0, 0x57, 0xb0, 0xa9, 0x36, 0x69, 0x69, 0x4d, 0xad, 0x7f, 0x94, 0x1c, 0x8f, 0xbd, 0x20,
0x2e, 0xcc, 0x29, 0x19, 0xa8, 0x06, 0xde, 0x36, 0x9b, 0x31, 0x24, 0x5f, 0xba, 0x0b, 0xc3, 0x7a,
0x96, 0xa3, 0x5d, 0x8f, 0x64, 0x9b, 0xb7, 0x2d, 0x72, 0x00, 0xf5, 0x6d, 0xda, 0x0d, 0x7a, 0x54,
0xc4, 0xc8, 0xe7, 0x93, 0x81, 0xab, 0xe0, 0x7a, 0x7b, 0xc6, 0x00, 0x9a, 0x7a, 0x61, 0xe4, 0x9e,
0x87, 0xf4, 0xc3, 0xf5, 0x8f, 0x44, 0xf4, 0xfd, 0x85, 0xd4, 0x0b, 0xf2, 0x78, 0xc2, 0xd0, 0x0b,
0xa9, 0xf3, 0x0c, 0x43, 0x2f, 0x64, 0xce, 0x33, 0x8c, 0xa5, 0x96, 0xc7, 0x23, 0x64, 0x00, 0x73,
0x99, 0x23, 0x10, 0xa5, 0x12, 0x26, 0x1d, 0x9c, 0xb4, 0x57, 0x26, 0x23, 0x98, 0xbd, 0xad, 0x99,
0xbd, 0x1d, 0xc2, 0xcc, 0x36, 0xe5, 0x8b, 0xc5, 0x33, 0xe5, 0x52, 0xb7, 0x34, 0xf4, 0x3c, 0xbc,
0xb4, 0x00, 0xc7, 0x3a, 0x53, 0xf1, 0x63, 0x9a, 0x1a, 0xf9, 0x1a, 0xd4, 0x1e, 0xd0, 0x58, 0xa6,
0xc6, 0x29, 0xd3, 0x33, 0x95, 0x2b, 0xd7, 0xce, 0xc9, 0xac, 0x33, 0x69, 0x06, 0x5b, 0x5b, 0xa7,
0xbd, 0x3e, 0xe5, 0xc2, 0xa9, 0xe3, 0xf5, 0x5e, 0x90, 0xff, 0x89, 0x8d, 0xab, 0x0c, 0xde, 0x25,
0x2d, 0x2f, 0x4a, 0x6f, 0x7c, 0x36, 0x05, 0xcf, 0x6b, 0xd9, 0x0f, 0x7a, 0x54, 0x33, 0x81, 0x7c,
0xa8, 0x69, 0x89, 0xe7, 0x8a, 0x81, 0xb2, 0xf7, 0x04, 0x14, 0x03, 0xe5, 0xe4, 0xa9, 0xdb, 0xab,
0xd8, 0x8f, 0x4d, 0x56, 0x92, 0x7e, 0x78, 0x6e, 0x7a, 0xd2, 0xd3, 0xfa, 0x47, 0xee, 0x30, 0x7e,
0x41, 0x9e, 0xe2, 0x53, 0x11, 0x7a, 0xfa, 0x5f, 0x62, 0x4b, 0xa7, 0x33, 0x05, 0xd5, 0x62, 0x69,
0x55, 0xa6, 0x7d, 0xcd, 0xbb, 0x42, 0x4b, 0xe9, 0xb3, 0x00, 0x87, 0x71, 0x30, 0xda, 0x76, 0xe9,
0x30, 0xf0, 0x13, 0x59, 0x9b, 0x24, 0xaa, 0x25, 0xf2, 0x4b, 0xcb, 0x56, 0x23, 0x4f, 0x35, 0xe7,
0xc3, 0xc8, 0x9e, 0x94, 0xc4, 0x35, 0x31, 0x97, 0x4d, 0x2d, 0x48, 0x4e, 0x3e, 0xdb, 0x6d, 0x8b,
0x6c, 0x00, 0x24, 0x67, 0x60, 0xca, 0x95, 0xc8, 0x1c, 0xaf, 0x29, 0xb1, 0x97, 0x73, 0x60, 0x76,
0x00, 0xd5, 0xe4, 0x50, 0x65, 0x39, 0xb9, 0x1b, 0x61, 0x1c, 0xc1, 0x28, 0x0d, 0x9e, 0x39, 0xea,
0xb0, 0x9b, 0xb8, 0x54, 0x40, 0x2a, 0x6c, 0xa9, 0xf0, 0xfc, 0xc2, 0x83, 0x79, 0x3e, 0x40, 0x65,
0x8e, 0x60, 0xea, 0x95, 0x9c, 0x49, 0xce, 0x71, 0x83, 0xe2, 0xe6, 0xdc, 0x68, 0xbd, 0x11, 0x11,
0x61, 0xd4, 0xca, 0xd3, 0xbe, 0x98, 0x68, 0x1e, 0xc2, 0x5c, 0x26, 0x9c, 0xac, 0x58, 0x7a, 0x52,
0x84, 0x5f, 0xb1, 0xf4, 0xc4, 0x48, 0xb4, 0xbd, 0x88, 0x5d, 0xce, 0xda, 0x80, 0x1e, 0xd0, 0x99,
0x17, 0x77, 0x4f, 0x58, 0x77, 0xdf, 0xb5, 0x60, 0x3e, 0x27, 0x5a, 0x4c, 0xde, 0x90, 0xce, 0xf4,
0xc4, 0x48, 0x72, 0x3b, 0x37, 0x98, 0x68, 0x1f, 0x62, 0x3f, 0xef, 0x91, 0x2f, 0x19, 0x8a, 0x8d,
0xc7, 0xf1, 0x04, 0x67, 0xbe, 0xd4, 0xa8, 0xc8, 0xb5, 0x28, 0x3e, 0x84, 0x65, 0x3e, 0x90, 0x8d,
0xc1, 0x20, 0x15, 0xe8, 0xbc, 0x96, 0x79, 0x74, 0xdb, 0x08, 0xe0, 0xb6, 0x27, 0x3f, 0xca, 0x3d,
0xc1, 0x5c, 0xe5, 0x43, 0x25, 0x63, 0x68, 0xa6, 0x83, 0x87, 0x64, 0x72, 0x5b, 0xed, 0xd7, 0x0d,
0xb7, 0x30, 0x1b, 0x70, 0xb4, 0x3f, 0x89, 0x9d, 0xbd, 0x6e, 0xb7, 0xf3, 0xd6, 0x85, 0x7b, 0x8a,
0x6c, 0x3f, 0xfe, 0x8f, 0x8a, 0x74, 0xa6, 0xe6, 0x29, 0x3b, 0x98, 0x14, 0x9a, 0x55, 0x8e, 0x69,
0x7e, 0xa0, 0xf4, 0x06, 0x76, 0xbf, 0x62, 0x5f, 0xc9, 0xeb, 0x3e, 0xe4, 0x9f, 0x70, 0x17, 0x75,
0x39, 0xcd, 0xd7, 0x72, 0x04, 0x2b, 0x79, 0xfb, 0x3d, 0xd1, 0xd7, 0x48, 0xad, 0xf5, 0xa5, 0xdb,
0xd6, 0xe6, 0xcd, 0xf7, 0x3f, 0xd9, 0xf7, 0xe2, 0x93, 0xf1, 0xd1, 0xad, 0x6e, 0x30, 0x5c, 0x1f,
0xc8, 0x10, 0x99, 0x48, 0xf3, 0x5d, 0x1f, 0xf8, 0xbd, 0x75, 0xfc, 0xfe, 0x68, 0x0a, 0xdf, 0xf0,
0xff, 0xf4, 0xbf, 0x07, 0x00, 0x00, 0xff, 0xff, 0x5d, 0xb7, 0x1a, 0xb7, 0xf5, 0x5f, 0x00, 0x00,
var fileDescriptor_rpc_854431eb46daab93 = []byte{
// 7755 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7d, 0x5b, 0x6c, 0x24, 0xd9,
0x59, 0xb0, 0xab, 0x2f, 0x76, 0xf7, 0xd7, 0x6d, 0xbb, 0x7d, 0x7c, 0xeb, 0xe9, 0x99, 0x9d, 0xf5,
0x56, 0x26, 0x33, 0x8e, 0x77, 0xff, 0xf1, 0xec, 0x24, 0xd9, 0x4c, 0x76, 0xfe, 0xfc, 0xf9, 0x7d,
0x9b, 0xf1, 0x64, 0xbd, 0x1e, 0xa7, 0x3c, 0x93, 0xf9, 0x77, 0x93, 0x5f, 0x9d, 0x72, 0xf7, 0x71,
0xbb, 0x76, 0xba, 0xab, 0x7a, 0xab, 0xaa, 0xed, 0x71, 0x96, 0x41, 0x08, 0x21, 0x40, 0x08, 0x84,
0x02, 0x02, 0x11, 0x04, 0x42, 0x4a, 0x90, 0x20, 0xe2, 0x89, 0x87, 0x20, 0x24, 0x08, 0xaf, 0x48,
0x91, 0x10, 0x42, 0x79, 0x44, 0x02, 0x21, 0x78, 0x41, 0x3c, 0x20, 0x90, 0x78, 0x44, 0x42, 0xe7,
0x3b, 0x97, 0x3a, 0xa7, 0xaa, 0x7a, 0x3c, 0x9b, 0x04, 0x9e, 0xdc, 0xe7, 0x3b, 0x5f, 0x9d, 0xeb,
0x77, 0x3f, 0xdf, 0x39, 0x86, 0x6a, 0x38, 0xec, 0xdc, 0x1c, 0x86, 0x41, 0x1c, 0x90, 0x72, 0xdf,
0x0f, 0x87, 0x9d, 0xd6, 0x95, 0x5e, 0x10, 0xf4, 0xfa, 0x74, 0xdd, 0x1d, 0x7a, 0xeb, 0xae, 0xef,
0x07, 0xb1, 0x1b, 0x7b, 0x81, 0x1f, 0x71, 0x24, 0xfb, 0xeb, 0x30, 0x73, 0x9f, 0xfa, 0x87, 0x94,
0x76, 0x1d, 0xfa, 0xe1, 0x88, 0x46, 0x31, 0x79, 0x1d, 0xe6, 0x5c, 0xfa, 0x0d, 0x4a, 0xbb, 0xed,
0xa1, 0x1b, 0x45, 0xc3, 0x93, 0xd0, 0x8d, 0x68, 0xd3, 0x5a, 0xb1, 0x56, 0xeb, 0x4e, 0x83, 0x57,
0x1c, 0x28, 0x38, 0x79, 0x0d, 0xea, 0x11, 0x43, 0xa5, 0x7e, 0x1c, 0x06, 0xc3, 0xf3, 0x66, 0x01,
0xf1, 0x6a, 0x0c, 0xb6, 0xc3, 0x41, 0x76, 0x1f, 0x66, 0x55, 0x0f, 0xd1, 0x30, 0xf0, 0x23, 0x4a,
0x6e, 0xc1, 0x42, 0xc7, 0x1b, 0x9e, 0xd0, 0xb0, 0x8d, 0x1f, 0x0f, 0x7c, 0x3a, 0x08, 0x7c, 0xaf,
0xd3, 0xb4, 0x56, 0x8a, 0xab, 0x55, 0x87, 0xf0, 0x3a, 0xf6, 0xc5, 0xbb, 0xa2, 0x86, 0xdc, 0x80,
0x59, 0xea, 0x73, 0x38, 0xed, 0xe2, 0x57, 0xa2, 0xab, 0x99, 0x04, 0xcc, 0x3e, 0xb0, 0x7f, 0xb1,
0x00, 0x73, 0x0f, 0x7c, 0x2f, 0x7e, 0xe2, 0xf6, 0xfb, 0x34, 0x96, 0x73, 0xba, 0x01, 0xb3, 0x67,
0x08, 0xc0, 0x39, 0x9d, 0x05, 0x61, 0x57, 0xcc, 0x68, 0x86, 0x83, 0x0f, 0x04, 0x74, 0xec, 0xc8,
0x0a, 0x63, 0x47, 0x96, 0xbb, 0x5c, 0xc5, 0x31, 0xcb, 0x75, 0x03, 0x66, 0x43, 0xda, 0x09, 0x4e,
0x69, 0x78, 0xde, 0x3e, 0xf3, 0xfc, 0x6e, 0x70, 0xd6, 0x2c, 0xad, 0x58, 0xab, 0x65, 0x67, 0x46,
0x82, 0x9f, 0x20, 0x94, 0x6c, 0xc2, 0x6c, 0xe7, 0xc4, 0xf5, 0x7d, 0xda, 0x6f, 0x1f, 0xb9, 0x9d,
0xa7, 0xa3, 0x61, 0xd4, 0x2c, 0xaf, 0x58, 0xab, 0xb5, 0xdb, 0x97, 0x6e, 0xe2, 0xae, 0xde, 0xdc,
0x3a, 0x71, 0xfd, 0x4d, 0xac, 0x39, 0xf4, 0xdd, 0x61, 0x74, 0x12, 0xc4, 0xce, 0x8c, 0xf8, 0x82,
0x83, 0x23, 0x7b, 0x01, 0x88, 0xbe, 0x12, 0x7c, 0xed, 0xed, 0x3f, 0xb2, 0x60, 0xfe, 0xb1, 0xdf,
0x0f, 0x3a, 0x4f, 0x7f, 0xc4, 0x25, 0xca, 0x99, 0x43, 0xe1, 0x65, 0xe7, 0x50, 0xfc, 0xb8, 0x73,
0x58, 0x82, 0x05, 0x73, 0xb0, 0x62, 0x16, 0x14, 0x16, 0xd9, 0xd7, 0x3d, 0x2a, 0x87, 0x25, 0xa7,
0xf1, 0x29, 0x68, 0x74, 0x46, 0x61, 0x48, 0xfd, 0xcc, 0x3c, 0x66, 0x05, 0x5c, 0x4d, 0xe4, 0x35,
0xa8, 0xfb, 0xf4, 0x2c, 0x41, 0x13, 0xb4, 0xeb, 0xd3, 0x33, 0x89, 0x62, 0x37, 0x61, 0x29, 0xdd,
0x8d, 0x18, 0xc0, 0x3f, 0x58, 0x50, 0x7a, 0x1c, 0x3f, 0x0b, 0xc8, 0x4d, 0x28, 0xc5, 0xe7, 0x43,
0xce, 0x21, 0x33, 0xb7, 0x89, 0x98, 0xda, 0x46, 0xb7, 0x1b, 0xd2, 0x28, 0x7a, 0x74, 0x3e, 0xa4,
0x4e, 0xdd, 0xe5, 0x85, 0x36, 0xc3, 0x23, 0x4d, 0x98, 0x12, 0x65, 0xec, 0xb0, 0xea, 0xc8, 0x22,
0xb9, 0x0a, 0xe0, 0x0e, 0x82, 0x91, 0x1f, 0xb7, 0x23, 0x37, 0xc6, 0xa5, 0x2a, 0x3a, 0x1a, 0x84,
0x5c, 0x81, 0xea, 0xf0, 0x69, 0x3b, 0xea, 0x84, 0xde, 0x30, 0x46, 0xb2, 0xa9, 0x3a, 0x09, 0x80,
0xbc, 0x0e, 0x95, 0x60, 0x14, 0x0f, 0x03, 0xcf, 0x8f, 0x05, 0xa9, 0xcc, 0x8a, 0xb1, 0x3c, 0x1c,
0xc5, 0x07, 0x0c, 0xec, 0x28, 0x04, 0x72, 0x0d, 0xa6, 0x3b, 0x81, 0x7f, 0xec, 0x85, 0x03, 0x2e,
0x0c, 0x9a, 0x93, 0xd8, 0x9b, 0x09, 0xb4, 0xbf, 0x55, 0x80, 0xda, 0xa3, 0xd0, 0xf5, 0x23, 0xb7,
0xc3, 0x00, 0x6c, 0xe8, 0xf1, 0xb3, 0xf6, 0x89, 0x1b, 0x9d, 0xe0, 0x6c, 0xab, 0x8e, 0x2c, 0x92,
0x25, 0x98, 0xe4, 0x03, 0xc5, 0x39, 0x15, 0x1d, 0x51, 0x22, 0x6f, 0xc0, 0x9c, 0x3f, 0x1a, 0xb4,
0xcd, 0xbe, 0x8a, 0x48, 0x2d, 0xd9, 0x0a, 0xb6, 0x00, 0x47, 0x6c, 0xaf, 0x79, 0x17, 0x7c, 0x86,
0x1a, 0x84, 0xd8, 0x50, 0x17, 0x25, 0xea, 0xf5, 0x4e, 0xf8, 0x34, 0xcb, 0x8e, 0x01, 0x63, 0x6d,
0xc4, 0xde, 0x80, 0xb6, 0xa3, 0xd8, 0x1d, 0x0c, 0xc5, 0xb4, 0x34, 0x08, 0xd6, 0x07, 0xb1, 0xdb,
0x6f, 0x1f, 0x53, 0x1a, 0x35, 0xa7, 0x44, 0xbd, 0x82, 0x90, 0xeb, 0x30, 0xd3, 0xa5, 0x51, 0xdc,
0x16, 0x9b, 0x42, 0xa3, 0x66, 0x05, 0x59, 0x3f, 0x05, 0x65, 0x94, 0x71, 0x9f, 0xc6, 0xda, 0xea,
0x44, 0x82, 0x02, 0xed, 0x3d, 0x20, 0x1a, 0x78, 0x9b, 0xc6, 0xae, 0xd7, 0x8f, 0xc8, 0x5b, 0x50,
0x8f, 0x35, 0x64, 0x14, 0x75, 0x35, 0x45, 0x2e, 0xda, 0x07, 0x8e, 0x81, 0x67, 0xdf, 0x87, 0xca,
0x3d, 0x4a, 0xf7, 0xbc, 0x81, 0x17, 0x93, 0x25, 0x28, 0x1f, 0x7b, 0xcf, 0x28, 0x27, 0xe8, 0xe2,
0xee, 0x84, 0xc3, 0x8b, 0xa4, 0x05, 0x53, 0x43, 0x1a, 0x76, 0xa8, 0x5c, 0xfe, 0xdd, 0x09, 0x47,
0x02, 0x36, 0xa7, 0xa0, 0xdc, 0x67, 0x1f, 0xdb, 0xff, 0x56, 0x80, 0xda, 0x21, 0xf5, 0x15, 0xa3,
0x10, 0x28, 0xb1, 0x29, 0x09, 0xe6, 0xc0, 0xdf, 0xe4, 0x55, 0xa8, 0xe1, 0x34, 0xa3, 0x38, 0xf4,
0xfc, 0x9e, 0xa0, 0x4f, 0x60, 0xa0, 0x43, 0x84, 0x90, 0x06, 0x14, 0xdd, 0x81, 0xa4, 0x4d, 0xf6,
0x93, 0x31, 0xd1, 0xd0, 0x3d, 0x1f, 0x30, 0x7e, 0x53, 0xbb, 0x56, 0x77, 0x6a, 0x02, 0xb6, 0xcb,
0xb6, 0xed, 0x26, 0xcc, 0xeb, 0x28, 0xb2, 0xf5, 0x32, 0xb6, 0x3e, 0xa7, 0x61, 0x8a, 0x4e, 0x6e,
0xc0, 0xac, 0xc4, 0x0f, 0xf9, 0x60, 0x71, 0x1f, 0xab, 0xce, 0x8c, 0x00, 0xcb, 0x29, 0xac, 0x42,
0xe3, 0xd8, 0xf3, 0xdd, 0x7e, 0xbb, 0xd3, 0x8f, 0x4f, 0xdb, 0x5d, 0xda, 0x8f, 0x5d, 0xdc, 0xd1,
0xb2, 0x33, 0x83, 0xf0, 0xad, 0x7e, 0x7c, 0xba, 0xcd, 0xa0, 0xe4, 0x0d, 0xa8, 0x1e, 0x53, 0xda,
0xc6, 0x95, 0x68, 0x56, 0x0c, 0xee, 0x90, 0xab, 0xeb, 0x54, 0x8e, 0xe5, 0x3a, 0xaf, 0x42, 0x23,
0x18, 0xc5, 0xbd, 0xc0, 0xf3, 0x7b, 0x6d, 0x26, 0x8f, 0xda, 0x5e, 0xb7, 0x59, 0x5d, 0xb1, 0x56,
0x4b, 0xce, 0x8c, 0x84, 0x33, 0xa9, 0xf0, 0xa0, 0x4b, 0x5e, 0x01, 0xc0, 0xbe, 0x79, 0xc3, 0xb0,
0x62, 0xad, 0x4e, 0x3b, 0x55, 0x06, 0xc1, 0x86, 0xec, 0x3f, 0xb5, 0xa0, 0xce, 0xd7, 0x5c, 0x28,
0xbe, 0x6b, 0x30, 0x2d, 0xa7, 0x46, 0xc3, 0x30, 0x08, 0x05, 0x1f, 0x99, 0x40, 0xb2, 0x06, 0x0d,
0x09, 0x18, 0x86, 0xd4, 0x1b, 0xb8, 0x3d, 0x2a, 0x84, 0x53, 0x06, 0x4e, 0x6e, 0x27, 0x2d, 0x86,
0xc1, 0x28, 0xa6, 0x42, 0xc4, 0xd6, 0xc5, 0xec, 0x1c, 0x06, 0x73, 0x4c, 0x14, 0xc6, 0x47, 0x39,
0x7b, 0x66, 0xc0, 0xec, 0xef, 0x59, 0x40, 0xd8, 0xd0, 0x1f, 0x05, 0xbc, 0x09, 0xb1, 0xe4, 0xe9,
0xed, 0xb6, 0x5e, 0x7a, 0xbb, 0x0b, 0xe3, 0xb6, 0x7b, 0x15, 0x26, 0x71, 0x58, 0x4c, 0x30, 0x14,
0xd3, 0x43, 0xdf, 0x2c, 0x34, 0x2d, 0x47, 0xd4, 0x13, 0x1b, 0xca, 0x7c, 0x8e, 0xa5, 0x9c, 0x39,
0xf2, 0x2a, 0xfb, 0xdb, 0x16, 0xd4, 0xb7, 0xb8, 0x0e, 0x41, 0xa1, 0x47, 0x6e, 0x01, 0x39, 0x1e,
0xf9, 0x5d, 0xb6, 0x97, 0xf1, 0x33, 0xaf, 0xdb, 0x3e, 0x3a, 0x67, 0x5d, 0xe1, 0xb8, 0x77, 0x27,
0x9c, 0x9c, 0x3a, 0xf2, 0x06, 0x34, 0x0c, 0x68, 0x14, 0x87, 0x7c, 0xf4, 0xbb, 0x13, 0x4e, 0xa6,
0x86, 0x2d, 0x26, 0x13, 0xab, 0xa3, 0xb8, 0xed, 0xf9, 0x5d, 0xfa, 0x0c, 0xd7, 0x7f, 0xda, 0x31,
0x60, 0x9b, 0x33, 0x50, 0xd7, 0xbf, 0xb3, 0x3f, 0x80, 0x8a, 0x14, 0xca, 0x28, 0x90, 0x52, 0xe3,
0x72, 0x34, 0x08, 0x69, 0x41, 0xc5, 0x1c, 0x85, 0x53, 0xf9, 0x38, 0x7d, 0xdb, 0xff, 0x07, 0x1a,
0x7b, 0x4c, 0x32, 0xfa, 0x9e, 0xdf, 0x13, 0x5a, 0x89, 0x89, 0xeb, 0xe1, 0xe8, 0xe8, 0x29, 0x3d,
0x17, 0xf4, 0x27, 0x4a, 0x4c, 0x26, 0x9c, 0x04, 0x51, 0x2c, 0xfa, 0xc1, 0xdf, 0xf6, 0x5f, 0x5a,
0x40, 0x76, 0xa2, 0xd8, 0x1b, 0xb8, 0x31, 0xbd, 0x47, 0x15, 0x21, 0x3c, 0x84, 0x3a, 0x6b, 0xed,
0x51, 0xb0, 0xc1, 0xe5, 0x3e, 0x97, 0x67, 0xaf, 0x8b, 0x2d, 0xc9, 0x7e, 0x70, 0x53, 0xc7, 0x66,
0xa6, 0xe1, 0xb9, 0x63, 0x34, 0xc0, 0x64, 0x4f, 0xec, 0x86, 0x3d, 0x1a, 0xa3, 0x52, 0x10, 0x26,
0x05, 0x70, 0xd0, 0x56, 0xe0, 0x1f, 0xb7, 0xbe, 0x08, 0x73, 0x99, 0x36, 0x98, 0x40, 0x4a, 0xa6,
0xc1, 0x7e, 0x92, 0x05, 0x28, 0x9f, 0xba, 0xfd, 0x11, 0x15, 0x9a, 0x88, 0x17, 0xde, 0x2e, 0xdc,
0xb1, 0xec, 0x0e, 0xcc, 0x1b, 0xe3, 0x12, 0x3c, 0xd9, 0x84, 0x29, 0x26, 0x1b, 0x98, 0xce, 0x45,
0xb9, 0xea, 0xc8, 0x22, 0xb9, 0x0d, 0x0b, 0xc7, 0x94, 0x86, 0x6e, 0x8c, 0xc5, 0xf6, 0x90, 0x86,
0xb8, 0x27, 0xa2, 0xe5, 0xdc, 0x3a, 0xfb, 0x1f, 0x2d, 0x98, 0x65, 0x7c, 0xf3, 0xae, 0xeb, 0x9f,
0xcb, 0xb5, 0xda, 0xcb, 0x5d, 0xab, 0x55, 0xb1, 0x56, 0x29, 0xec, 0x8f, 0xbb, 0x50, 0xc5, 0xf4,
0x42, 0x91, 0x15, 0xa8, 0x1b, 0xc3, 0x2d, 0x73, 0x25, 0x17, 0xb9, 0xf1, 0x01, 0x0d, 0x37, 0xcf,
0x63, 0xfa, 0xe3, 0x2f, 0xe5, 0x75, 0x68, 0x24, 0xc3, 0x16, 0xeb, 0x48, 0xa0, 0xc4, 0x08, 0x53,
0x34, 0x80, 0xbf, 0xed, 0xdf, 0xb1, 0x38, 0xe2, 0x56, 0xe0, 0x29, 0x05, 0xc9, 0x10, 0x99, 0x1e,
0x95, 0x88, 0xec, 0xf7, 0x58, 0x03, 0xe2, 0xc7, 0x9f, 0x2c, 0xb9, 0x04, 0x95, 0x88, 0xfa, 0xdd,
0xb6, 0xdb, 0xef, 0xa3, 0x1e, 0xa9, 0x38, 0x53, 0xac, 0xbc, 0xd1, 0xef, 0xdb, 0x37, 0x60, 0x4e,
0x1b, 0xdd, 0x0b, 0xe6, 0xb1, 0x0f, 0x64, 0xcf, 0x8b, 0xe2, 0xc7, 0x7e, 0x34, 0xd4, 0xf4, 0xcf,
0x65, 0xa8, 0x0e, 0x3c, 0x1f, 0x47, 0xc6, 0x39, 0xb7, 0xec, 0x54, 0x06, 0x9e, 0xcf, 0xc6, 0x15,
0x61, 0xa5, 0xfb, 0x4c, 0x54, 0x16, 0x44, 0xa5, 0xfb, 0x0c, 0x2b, 0xed, 0x3b, 0x30, 0x6f, 0xb4,
0x27, 0xba, 0x7e, 0x0d, 0xca, 0xa3, 0xf8, 0x59, 0x20, 0xad, 0x83, 0x9a, 0xa0, 0x10, 0x66, 0x67,
0x3a, 0xbc, 0xc6, 0xbe, 0x0b, 0x73, 0xfb, 0xf4, 0x4c, 0x30, 0xb2, 0x1c, 0xc8, 0xf5, 0x0b, 0x6d,
0x50, 0xac, 0xb7, 0x6f, 0x02, 0xd1, 0x3f, 0x4e, 0x18, 0x40, 0x5a, 0xa4, 0x96, 0x61, 0x91, 0xda,
0xd7, 0x81, 0x1c, 0x7a, 0x3d, 0xff, 0x5d, 0x1a, 0x45, 0x6e, 0x4f, 0xb1, 0x7e, 0x03, 0x8a, 0x83,
0xa8, 0x27, 0x44, 0x15, 0xfb, 0x69, 0x7f, 0x1a, 0xe6, 0x0d, 0x3c, 0xd1, 0xf0, 0x15, 0xa8, 0x46,
0x5e, 0xcf, 0x77, 0xe3, 0x51, 0x48, 0x45, 0xd3, 0x09, 0xc0, 0xbe, 0x07, 0x0b, 0x5f, 0xa1, 0xa1,
0x77, 0x7c, 0x7e, 0x51, 0xf3, 0x66, 0x3b, 0x85, 0x74, 0x3b, 0x3b, 0xb0, 0x98, 0x6a, 0x47, 0x74,
0xcf, 0xc9, 0x57, 0xec, 0x64, 0xc5, 0xe1, 0x05, 0x4d, 0xf6, 0x15, 0x74, 0xd9, 0x67, 0x3f, 0x06,
0xb2, 0x15, 0xf8, 0x3e, 0xed, 0xc4, 0x07, 0x94, 0x86, 0x89, 0x33, 0x9c, 0xd0, 0x6a, 0xed, 0xf6,
0xb2, 0x58, 0xd9, 0xb4, 0x40, 0x15, 0x44, 0x4c, 0xa0, 0x34, 0xa4, 0xe1, 0x00, 0x1b, 0xae, 0x38,
0xf8, 0xdb, 0x5e, 0x84, 0x79, 0xa3, 0x59, 0xe1, 0x3e, 0xbc, 0x09, 0x8b, 0xdb, 0x5e, 0xd4, 0xc9,
0x76, 0xd8, 0x84, 0xa9, 0xe1, 0xe8, 0xa8, 0x9d, 0x70, 0xa2, 0x2c, 0x32, 0x8b, 0x33, 0xfd, 0x89,
0x68, 0xec, 0xe7, 0x2d, 0x28, 0xed, 0x3e, 0xda, 0xdb, 0x62, 0xba, 0xc2, 0xf3, 0x3b, 0xc1, 0x80,
0xe9, 0x5b, 0x3e, 0x69, 0x55, 0x1e, 0xcb, 0x61, 0x57, 0xa0, 0x8a, 0x6a, 0x9a, 0x19, 0xd1, 0xc2,
0x6f, 0x4d, 0x00, 0xcc, 0x80, 0xa7, 0xcf, 0x86, 0x5e, 0x88, 0x16, 0xba, 0xb4, 0xbb, 0x4b, 0xa8,
0x66, 0xb2, 0x15, 0xf6, 0x0f, 0xca, 0x30, 0x25, 0x94, 0x2f, 0xf6, 0xd7, 0x89, 0xbd, 0x53, 0x2a,
0x46, 0x22, 0x4a, 0xcc, 0x04, 0x0a, 0xe9, 0x20, 0x88, 0x69, 0xdb, 0xd8, 0x06, 0x13, 0x88, 0x0e,
0x8a, 0xf0, 0x1d, 0xb9, 0x4b, 0x53, 0xe4, 0x58, 0x06, 0x90, 0x2d, 0x96, 0xb4, 0xcf, 0x4a, 0x68,
0x9f, 0xc9, 0x22, 0x5b, 0x89, 0x8e, 0x3b, 0x74, 0x3b, 0x5e, 0x7c, 0x2e, 0x44, 0x82, 0x2a, 0xb3,
0xb6, 0xfb, 0x41, 0xc7, 0x65, 0x5e, 0x69, 0xdf, 0xf5, 0x3b, 0x54, 0x3a, 0x3f, 0x06, 0x90, 0x39,
0x02, 0x62, 0x48, 0x12, 0x8d, 0x3b, 0x0b, 0x29, 0x28, 0xd3, 0xdf, 0x9d, 0x60, 0x30, 0xf0, 0x62,
0xe6, 0x3f, 0xa0, 0x6d, 0x59, 0x74, 0x34, 0x08, 0x77, 0xb5, 0xb0, 0x74, 0xc6, 0x57, 0xaf, 0x2a,
0x5d, 0x2d, 0x0d, 0xc8, 0x5a, 0x61, 0x5a, 0x87, 0x89, 0xb1, 0xa7, 0x67, 0x68, 0x48, 0x16, 0x1d,
0x0d, 0xc2, 0xf6, 0x61, 0xe4, 0x47, 0x34, 0x8e, 0xfb, 0xb4, 0xab, 0x06, 0x54, 0x43, 0xb4, 0x6c,
0x05, 0xb9, 0x05, 0xf3, 0xdc, 0xa5, 0x89, 0xdc, 0x38, 0x88, 0x4e, 0xbc, 0xa8, 0x1d, 0x31, 0xe7,
0xa0, 0x8e, 0xf8, 0x79, 0x55, 0xe4, 0x0e, 0x2c, 0xa7, 0xc0, 0x21, 0xed, 0x50, 0xef, 0x94, 0x76,
0x9b, 0xd3, 0xf8, 0xd5, 0xb8, 0x6a, 0xb2, 0x02, 0x35, 0xe6, 0xc9, 0x8d, 0x86, 0x5d, 0x97, 0x19,
0x30, 0x33, 0xb8, 0x0f, 0x3a, 0x88, 0xbc, 0x09, 0xd3, 0x43, 0xca, 0xad, 0x9f, 0x93, 0xb8, 0xdf,
0x89, 0x9a, 0xb3, 0x86, 0x74, 0x63, 0x94, 0xeb, 0x98, 0x18, 0x8c, 0x28, 0x3b, 0x11, 0x9a, 0xf4,
0xee, 0x79, 0xb3, 0x21, 0xcc, 0x6a, 0x09, 0x40, 0x1e, 0x09, 0xbd, 0x53, 0x37, 0xa6, 0xcd, 0x39,
0x2e, 0xd0, 0x45, 0x91, 0x7d, 0xe7, 0xf9, 0x5e, 0xec, 0xb9, 0x71, 0x10, 0x36, 0x09, 0xd6, 0x25,
0x00, 0xb6, 0x88, 0x48, 0x1f, 0x51, 0xec, 0xc6, 0xa3, 0xa8, 0x7d, 0xdc, 0x77, 0x7b, 0x51, 0x73,
0x9e, 0xdb, 0xa5, 0x99, 0x0a, 0xfb, 0xf7, 0x2c, 0x2e, 0xa4, 0x05, 0x41, 0x2b, 0x61, 0xfb, 0x2a,
0xd4, 0x38, 0x29, 0xb7, 0x03, 0xbf, 0x7f, 0x2e, 0xa8, 0x1b, 0x38, 0xe8, 0xa1, 0xdf, 0x3f, 0x27,
0x9f, 0x80, 0x69, 0xcf, 0xd7, 0x51, 0xb8, 0x3c, 0xa8, 0x4b, 0x20, 0x22, 0xbd, 0x0a, 0xb5, 0xe1,
0xe8, 0xa8, 0xef, 0x75, 0x38, 0x4a, 0x91, 0xb7, 0xc2, 0x41, 0x88, 0xc0, 0x2c, 0x6d, 0x3e, 0x2b,
0x8e, 0x51, 0x42, 0x8c, 0x9a, 0x80, 0x31, 0x14, 0x7b, 0x13, 0x16, 0xcc, 0x01, 0x0a, 0xc1, 0xb7,
0x06, 0x15, 0xc1, 0x27, 0x51, 0xb3, 0x86, 0x6b, 0x3d, 0xa3, 0x45, 0x5c, 0x7c, 0xda, 0x77, 0x54,
0xbd, 0xfd, 0x27, 0x25, 0x98, 0x17, 0xd0, 0xad, 0x7e, 0x10, 0xd1, 0xc3, 0xd1, 0x60, 0xe0, 0x86,
0x39, 0x0c, 0x68, 0x5d, 0xc0, 0x80, 0x05, 0x93, 0x01, 0x19, 0x5b, 0x9c, 0xb8, 0x9e, 0xcf, 0xdd,
0x04, 0xce, 0xbd, 0x1a, 0x84, 0xac, 0xc2, 0x6c, 0xa7, 0x1f, 0x44, 0xdc, 0x24, 0xd6, 0x1d, 0xfe,
0x34, 0x38, 0x2b, 0x30, 0xca, 0x79, 0x02, 0x43, 0x67, 0xf8, 0xc9, 0x14, 0xc3, 0xdb, 0x50, 0x67,
0x8d, 0x52, 0x29, 0xbf, 0xa6, 0xb8, 0x99, 0xac, 0xc3, 0xd8, 0x78, 0xd2, 0xec, 0xc5, 0x79, 0x79,
0x36, 0x8f, 0xb9, 0xbc, 0x01, 0x45, 0xf9, 0xa8, 0x61, 0x57, 0x05, 0x73, 0x65, 0xab, 0xc8, 0x3d,
0xe6, 0x25, 0xb2, 0xbe, 0x50, 0x49, 0x03, 0x2a, 0xe9, 0xeb, 0xe6, 0x8e, 0xe8, 0x6b, 0x7f, 0x93,
0x15, 0x46, 0x21, 0x45, 0xc5, 0xad, 0x7d, 0x69, 0xff, 0x92, 0x05, 0x35, 0xad, 0x8e, 0x2c, 0xc2,
0xdc, 0xd6, 0xc3, 0x87, 0x07, 0x3b, 0xce, 0xc6, 0xa3, 0x07, 0x5f, 0xd9, 0x69, 0x6f, 0xed, 0x3d,
0x3c, 0xdc, 0x69, 0x4c, 0x30, 0xf0, 0xde, 0xc3, 0xad, 0x8d, 0xbd, 0xf6, 0xbd, 0x87, 0xce, 0x96,
0x04, 0x5b, 0x64, 0x09, 0x88, 0xb3, 0xf3, 0xee, 0xc3, 0x47, 0x3b, 0x06, 0xbc, 0x40, 0x1a, 0x50,
0xdf, 0x74, 0x76, 0x36, 0xb6, 0x76, 0x05, 0xa4, 0x48, 0x16, 0xa0, 0x71, 0xef, 0xf1, 0xfe, 0xf6,
0x83, 0xfd, 0xfb, 0xed, 0xad, 0x8d, 0xfd, 0xad, 0x9d, 0xbd, 0x9d, 0xed, 0x46, 0x89, 0x4c, 0x43,
0x75, 0x63, 0x73, 0x63, 0x7f, 0xfb, 0xe1, 0xfe, 0xce, 0x76, 0xa3, 0x6c, 0xff, 0x9d, 0x05, 0x8b,
0x38, 0xea, 0x6e, 0x9a, 0x41, 0x56, 0xa0, 0xd6, 0x09, 0x82, 0x21, 0x33, 0x8e, 0x13, 0xf1, 0xaf,
0x83, 0x18, 0xf1, 0x73, 0x61, 0x7b, 0x1c, 0x84, 0x1d, 0x2a, 0xf8, 0x03, 0x10, 0x74, 0x8f, 0x41,
0x18, 0xf1, 0x8b, 0xed, 0xe5, 0x18, 0x9c, 0x3d, 0x6a, 0x1c, 0xc6, 0x51, 0x96, 0x60, 0xf2, 0x28,
0xa4, 0x6e, 0xe7, 0x44, 0x70, 0x86, 0x28, 0x91, 0x4f, 0x25, 0xde, 0x5b, 0x87, 0xad, 0x7e, 0x9f,
0x76, 0x91, 0x62, 0x2a, 0xce, 0xac, 0x80, 0x6f, 0x09, 0x30, 0x93, 0x16, 0xee, 0x91, 0xeb, 0x77,
0x03, 0x9f, 0x76, 0x85, 0x69, 0x98, 0x00, 0xec, 0x03, 0x58, 0x4a, 0xcf, 0x4f, 0xf0, 0xd7, 0x5b,
0x1a, 0x7f, 0x71, 0x4b, 0xad, 0x35, 0x7e, 0x37, 0x35, 0x5e, 0xfb, 0xfb, 0x02, 0x94, 0x98, 0xe2,
0x1e, 0xaf, 0xe4, 0x75, 0x5b, 0xac, 0x98, 0x89, 0x0e, 0xa2, 0x43, 0xc8, 0x45, 0x39, 0x57, 0x77,
0x1a, 0x24, 0xa9, 0x0f, 0x69, 0xe7, 0x14, 0x67, 0xac, 0xea, 0x19, 0x84, 0x31, 0x08, 0x33, 0x94,
0xf1, 0x6b, 0xc1, 0x20, 0xb2, 0x2c, 0xeb, 0xf0, 0xcb, 0xa9, 0xa4, 0x0e, 0xbf, 0x6b, 0xc2, 0x94,
0xe7, 0x1f, 0x05, 0x23, 0xbf, 0x8b, 0x0c, 0x51, 0x71, 0x64, 0x11, 0xe3, 0x91, 0xc8, 0xa8, 0xde,
0x40, 0x92, 0x7f, 0x02, 0x20, 0xb7, 0xa1, 0x1a, 0x9d, 0xfb, 0x1d, 0x9d, 0xe6, 0x17, 0xc4, 0x2a,
0xb1, 0x35, 0xb8, 0x79, 0x78, 0xee, 0x77, 0x90, 0xc2, 0x13, 0x34, 0xfb, 0x8b, 0x50, 0x91, 0x60,
0x46, 0x96, 0x8f, 0xf7, 0xdf, 0xd9, 0x7f, 0xf8, 0x64, 0xbf, 0x7d, 0xf8, 0xde, 0xfe, 0x56, 0x63,
0x82, 0xcc, 0x42, 0x6d, 0x63, 0x0b, 0x29, 0x1d, 0x01, 0x16, 0x43, 0x39, 0xd8, 0x38, 0x3c, 0x54,
0x90, 0x82, 0x4d, 0x98, 0xb3, 0x1b, 0xa1, 0x75, 0xa4, 0xe2, 0x71, 0x6f, 0xc1, 0x9c, 0x06, 0x4b,
0x2c, 0xed, 0x21, 0x03, 0xa4, 0x2c, 0x6d, 0x34, 0xab, 0x78, 0x8d, 0xdd, 0x80, 0x99, 0xfb, 0x34,
0x7e, 0xe0, 0x1f, 0x07, 0xb2, 0xa5, 0x3f, 0x28, 0xc1, 0xac, 0x02, 0x89, 0x86, 0x56, 0x61, 0xd6,
0xeb, 0x52, 0x3f, 0xf6, 0xe2, 0xf3, 0xb6, 0xe1, 0x53, 0xa7, 0xc1, 0xcc, 0x1c, 0x75, 0xfb, 0x9e,
0x2b, 0xc3, 0xbe, 0xbc, 0xc0, 0x7c, 0x4c, 0xa6, 0x2b, 0xa5, 0xfa, 0x53, 0x74, 0xc5, 0x5d, 0xf9,
0xdc, 0x3a, 0x26, 0x81, 0x18, 0x5c, 0xa8, 0x18, 0xf5, 0x09, 0x37, 0xcb, 0xf2, 0xaa, 0xd8, 0x56,
0xf1, 0x96, 0xd8, 0x94, 0xcb, 0x5c, 0x9f, 0x2a, 0x40, 0x26, 0xae, 0x3a, 0xc9, 0xe5, 0x63, 0x3a,
0xae, 0xaa, 0xc5, 0x66, 0x2b, 0x99, 0xd8, 0x2c, 0x93, 0x9f, 0xe7, 0x7e, 0x87, 0x76, 0xdb, 0x71,
0xd0, 0x46, 0x39, 0x8f, 0x24, 0x51, 0x71, 0xd2, 0x60, 0x72, 0x05, 0xa6, 0x62, 0x1a, 0xc5, 0x3e,
0xe5, 0x01, 0xb3, 0x0a, 0x86, 0x78, 0x24, 0x88, 0xd9, 0xd0, 0xa3, 0xd0, 0x8b, 0x9a, 0x75, 0x8c,
0xba, 0xe2, 0x6f, 0xf2, 0x19, 0x58, 0x3c, 0xa2, 0x51, 0xdc, 0x3e, 0xa1, 0x6e, 0x97, 0x86, 0x48,
0x5e, 0x3c, 0xbc, 0xcb, 0x4d, 0x93, 0xfc, 0x4a, 0x46, 0xb8, 0xa7, 0x34, 0x8c, 0xbc, 0xc0, 0x47,
0xa3, 0xa4, 0xea, 0xc8, 0x22, 0x6b, 0x8f, 0x4d, 0x5e, 0x29, 0x69, 0xb5, 0x82, 0xb3, 0x38, 0xf1,
0xfc, 0x4a, 0x72, 0x0d, 0x26, 0x71, 0x02, 0x51, 0xb3, 0x61, 0xc4, 0xa9, 0xb6, 0x18, 0xd0, 0x11,
0x75, 0x5f, 0x2a, 0x55, 0x6a, 0x8d, 0xba, 0xfd, 0x39, 0x28, 0x23, 0x98, 0x6d, 0x3a, 0x5f, 0x0c,
0x4e, 0x14, 0xbc, 0xc0, 0x86, 0xe6, 0xd3, 0xf8, 0x2c, 0x08, 0x9f, 0xca, 0x33, 0x00, 0x51, 0xb4,
0xbf, 0x81, 0x5e, 0x88, 0x8a, 0x89, 0x3f, 0x46, 0x13, 0x8a, 0xf9, 0x92, 0x7c, 0xa9, 0xa3, 0x13,
0x57, 0x38, 0x46, 0x15, 0x04, 0x1c, 0x9e, 0xb8, 0x4c, 0x56, 0x1a, 0xbb, 0xc7, 0x7d, 0xcd, 0x1a,
0xc2, 0x76, 0xf9, 0xe6, 0x5d, 0x83, 0x19, 0x19, 0x6d, 0x8f, 0xda, 0x7d, 0x7a, 0x1c, 0xcb, 0x48,
0x91, 0x3f, 0x1a, 0xa0, 0x43, 0xba, 0x47, 0x8f, 0x63, 0x7b, 0x1f, 0xe6, 0x84, 0xfc, 0x7a, 0x38,
0xa4, 0xb2, 0xeb, 0xcf, 0xe7, 0xd9, 0x01, 0xb5, 0xdb, 0xf3, 0xa6, 0xc0, 0xe3, 0xe7, 0x0b, 0x26,
0xa6, 0xed, 0x00, 0xd1, 0xe5, 0xa1, 0x68, 0x50, 0x28, 0x63, 0x19, 0x0b, 0x13, 0xd3, 0x31, 0x60,
0x6c, 0x7d, 0xa2, 0x51, 0xa7, 0x23, 0xcf, 0x48, 0x98, 0xc7, 0xce, 0x8b, 0xf6, 0x1f, 0x5a, 0x30,
0x8f, 0xad, 0x49, 0x4b, 0x46, 0xe8, 0x9c, 0x3b, 0x1f, 0x63, 0x98, 0xf5, 0x8e, 0x1e, 0x1f, 0x5c,
0x80, 0xb2, 0xae, 0x85, 0x78, 0xe1, 0xe3, 0xc7, 0x1d, 0x4a, 0xe9, 0xb8, 0x83, 0xfd, 0x5b, 0x16,
0xcc, 0x71, 0x45, 0x80, 0x56, 0xa5, 0x98, 0xfe, 0xff, 0x86, 0x69, 0xae, 0xd1, 0x05, 0x57, 0x8b,
0x81, 0x26, 0xa2, 0x11, 0xa1, 0x1c, 0x79, 0x77, 0xc2, 0x31, 0x91, 0xc9, 0x5d, 0xb4, 0xaa, 0xfc,
0x36, 0x42, 0x73, 0x4e, 0xd3, 0xcc, 0xb5, 0xde, 0x9d, 0x70, 0x34, 0xf4, 0xcd, 0x0a, 0x4c, 0x72,
0x93, 0xdc, 0xbe, 0x0f, 0xd3, 0x46, 0x47, 0x46, 0xcc, 0xa3, 0xce, 0x63, 0x1e, 0x99, 0xe0, 0x62,
0x21, 0x27, 0xb8, 0xf8, 0xc7, 0x45, 0x20, 0x8c, 0x58, 0x52, 0xbb, 0xc1, 0x7c, 0x82, 0xa0, 0x6b,
0x78, 0x78, 0x75, 0x47, 0x07, 0x91, 0x9b, 0x40, 0xb4, 0xa2, 0x8c, 0x11, 0x73, 0x95, 0x97, 0x53,
0xc3, 0xc4, 0xa4, 0xb0, 0x18, 0x84, 0x6e, 0x17, 0xbe, 0x2c, 0x5f, 0xf6, 0xdc, 0x3a, 0xa6, 0xd5,
0x86, 0xa3, 0xe8, 0x04, 0x23, 0x7b, 0xc2, 0x07, 0x94, 0xe5, 0xf4, 0xfe, 0x4e, 0x5e, 0xb8, 0xbf,
0x53, 0x99, 0xb8, 0x92, 0xe6, 0x85, 0x54, 0x4c, 0x2f, 0xe4, 0x1a, 0x4c, 0x0f, 0x98, 0x9d, 0x1b,
0xf7, 0x3b, 0xed, 0x01, 0xeb, 0x5d, 0xb8, 0x7c, 0x06, 0x90, 0xac, 0x41, 0x43, 0xd8, 0x38, 0x89,
0xab, 0xc3, 0x4f, 0x10, 0x32, 0x70, 0x26, 0xbf, 0x93, 0x48, 0x53, 0x0d, 0x07, 0x9b, 0x00, 0x98,
0x5f, 0x13, 0x31, 0x0a, 0x69, 0x8f, 0x7c, 0x71, 0xa0, 0x46, 0xbb, 0xe8, 0xec, 0x55, 0x9c, 0x6c,
0x85, 0xfd, 0x6b, 0x16, 0x34, 0xd8, 0x9e, 0x19, 0x64, 0xf9, 0x36, 0x20, 0x57, 0xbc, 0x24, 0x55,
0x1a, 0xb8, 0xe4, 0x0e, 0x54, 0xb1, 0x1c, 0x0c, 0xa9, 0x2f, 0x68, 0xb2, 0x69, 0xd2, 0x64, 0x22,
0x4f, 0x76, 0x27, 0x9c, 0x04, 0x59, 0xa3, 0xc8, 0xbf, 0xb6, 0xa0, 0x26, 0x7a, 0xf9, 0x91, 0x23,
0x19, 0x2d, 0xed, 0x04, 0x94, 0x53, 0x52, 0x72, 0xe0, 0xb9, 0x0a, 0xb3, 0x03, 0x37, 0x1e, 0x85,
0x4c, 0x1f, 0x1b, 0x51, 0x8c, 0x34, 0x98, 0x29, 0x57, 0x14, 0x9d, 0x51, 0x3b, 0xf6, 0xfa, 0x6d,
0x59, 0x2b, 0xce, 0x1a, 0xf3, 0xaa, 0x98, 0x04, 0x89, 0x62, 0xb7, 0x47, 0x85, 0xde, 0xe4, 0x05,
0xbb, 0x09, 0x4b, 0x62, 0x42, 0x29, 0xfb, 0xd8, 0xfe, 0x7e, 0x1d, 0x96, 0x33, 0x55, 0x2a, 0x33,
0x42, 0xb8, 0xe7, 0x7d, 0x6f, 0x70, 0x14, 0x28, 0xe7, 0xc2, 0xd2, 0x3d, 0x77, 0xa3, 0x8a, 0xf4,
0x60, 0x51, 0x1a, 0x08, 0x6c, 0x4d, 0x13, 0x65, 0x56, 0x40, 0x2d, 0xf5, 0xa6, 0xb9, 0x85, 0xe9,
0x0e, 0x25, 0x5c, 0x67, 0xe2, 0xfc, 0xf6, 0xc8, 0x09, 0x34, 0x95, 0x25, 0x22, 0x84, 0xb5, 0x66,
0xad, 0xb0, 0xbe, 0xde, 0xb8, 0xa0, 0x2f, 0xc3, 0x9c, 0x76, 0xc6, 0xb6, 0x46, 0xce, 0xe1, 0xaa,
0xac, 0x43, 0x69, 0x9c, 0xed, 0xaf, 0xf4, 0x52, 0x73, 0x43, 0x47, 0xc1, 0xec, 0xf4, 0x82, 0x86,
0xc9, 0x07, 0xb0, 0x74, 0xe6, 0x7a, 0xb1, 0x1c, 0x96, 0x66, 0x1b, 0x94, 0xb1, 0xcb, 0xdb, 0x17,
0x74, 0xf9, 0x84, 0x7f, 0x6c, 0xa8, 0xa8, 0x31, 0x2d, 0xb6, 0x7e, 0x60, 0xc1, 0x8c, 0xd9, 0x0e,
0x23, 0x53, 0xc1, 0xfb, 0x52, 0x06, 0x4a, 0x6b, 0x32, 0x05, 0xce, 0xfa, 0xe7, 0x85, 0x3c, 0xff,
0x5c, 0xf7, 0x8a, 0x8b, 0x17, 0x85, 0xc1, 0x4a, 0x2f, 0x17, 0x06, 0x2b, 0xe7, 0x85, 0xc1, 0x5a,
0xff, 0x61, 0x01, 0xc9, 0xd2, 0x12, 0xb9, 0xcf, 0x03, 0x04, 0x3e, 0xed, 0x0b, 0x91, 0xf2, 0xbf,
0x5e, 0x8e, 0x1e, 0xe5, 0xda, 0xc9, 0xaf, 0x19, 0x63, 0xe8, 0xc9, 0x02, 0xba, 0xb1, 0x33, 0xed,
0xe4, 0x55, 0xa5, 0x02, 0x73, 0xa5, 0x8b, 0x03, 0x73, 0xe5, 0x8b, 0x03, 0x73, 0x93, 0xe9, 0xc0,
0x5c, 0xeb, 0xe7, 0x2c, 0x98, 0xcf, 0xd9, 0xf4, 0x9f, 0xdc, 0xc4, 0xd9, 0x36, 0x19, 0xb2, 0xa0,
0x20, 0xb6, 0x49, 0x07, 0xb6, 0x7e, 0x0a, 0xa6, 0x0d, 0x42, 0xff, 0xc9, 0xf5, 0x9f, 0xb6, 0xd7,
0x38, 0x9d, 0x19, 0xb0, 0xd6, 0xbf, 0x14, 0x80, 0x64, 0x99, 0xed, 0x7f, 0x74, 0x0c, 0xd9, 0x75,
0x2a, 0xe6, 0xac, 0xd3, 0x7f, 0xab, 0x1e, 0x78, 0x03, 0xe6, 0x44, 0x06, 0x94, 0x16, 0x16, 0xe2,
0x14, 0x93, 0xad, 0x60, 0x16, 0xab, 0x19, 0x15, 0xad, 0x18, 0x19, 0x21, 0x9a, 0x32, 0x4c, 0x05,
0x47, 0xed, 0x16, 0x34, 0xc5, 0x0a, 0xed, 0x9c, 0x52, 0x3f, 0x3e, 0x1c, 0x1d, 0xf1, 0x14, 0x20,
0x2f, 0xf0, 0xed, 0xef, 0x15, 0x95, 0xd1, 0x8d, 0x95, 0x42, 0xbd, 0x7f, 0x06, 0xea, 0xba, 0x30,
0x17, 0xdb, 0x91, 0x8a, 0x0a, 0x32, 0xc5, 0xae, 0x63, 0x91, 0x6d, 0x98, 0x41, 0x91, 0xd5, 0x55,
0xdf, 0x15, 0xf0, 0xbb, 0x17, 0x44, 0x3b, 0x76, 0x27, 0x9c, 0xd4, 0x37, 0xe4, 0x0b, 0x30, 0x63,
0xba, 0x52, 0xc2, 0x46, 0xc8, 0xb3, 0xcd, 0xd9, 0xe7, 0x26, 0x32, 0xd9, 0x80, 0x46, 0xda, 0x17,
0x13, 0xe7, 0xff, 0x63, 0x1a, 0xc8, 0xa0, 0x93, 0x3b, 0xe2, 0x78, 0xac, 0x8c, 0x51, 0x88, 0x6b,
0xe6, 0x67, 0xda, 0x32, 0xdd, 0xe4, 0x7f, 0xb4, 0x03, 0xb3, 0xaf, 0x01, 0x24, 0x30, 0xd2, 0x80,
0xfa, 0xc3, 0x83, 0x9d, 0xfd, 0xf6, 0xd6, 0xee, 0xc6, 0xfe, 0xfe, 0xce, 0x5e, 0x63, 0x82, 0x10,
0x98, 0xc1, 0xa0, 0xd9, 0xb6, 0x82, 0x59, 0x0c, 0x26, 0xc2, 0x14, 0x12, 0x56, 0x20, 0x0b, 0xd0,
0x78, 0xb0, 0x9f, 0x82, 0x16, 0x37, 0xab, 0x8a, 0x3f, 0xec, 0x25, 0x58, 0xe0, 0x19, 0x6e, 0x9b,
0x9c, 0x3c, 0xa4, 0xad, 0xf0, 0xbb, 0x16, 0x2c, 0xa6, 0x2a, 0x92, 0x54, 0x12, 0x6e, 0x0e, 0x98,
0x36, 0x82, 0x09, 0xc4, 0x90, 0xb7, 0xb4, 0xfc, 0x52, 0x12, 0x24, 0x5b, 0xc1, 0x68, 0x5e, 0xb3,
0x14, 0x53, 0x9c, 0x94, 0x57, 0x65, 0x2f, 0xf3, 0x3c, 0x3c, 0xcc, 0xd8, 0x33, 0x06, 0x7e, 0xcc,
0x33, 0xe7, 0xf4, 0x8a, 0xe4, 0xb8, 0xd1, 0x1c, 0xb2, 0x2c, 0x32, 0x23, 0xdf, 0x30, 0x3d, 0xcc,
0xf1, 0xe6, 0xd6, 0xd9, 0x7f, 0x51, 0x00, 0xf2, 0xe5, 0x11, 0x0d, 0xcf, 0x31, 0x0b, 0x44, 0xc5,
0x20, 0x97, 0xd3, 0x11, 0xb6, 0xc9, 0xe1, 0xe8, 0xe8, 0x1d, 0x7a, 0x2e, 0x33, 0x98, 0x0a, 0x7a,
0x06, 0x13, 0x30, 0xe7, 0x58, 0xe5, 0xa0, 0x58, 0xab, 0x65, 0x0c, 0x49, 0x54, 0xfd, 0xd1, 0x80,
0x37, 0x9a, 0x9b, 0x68, 0x54, 0xba, 0x38, 0xd1, 0xa8, 0x7c, 0x51, 0xa2, 0xd1, 0x27, 0x60, 0xda,
0xeb, 0xf9, 0x01, 0x13, 0x0b, 0x4c, 0xb1, 0x47, 0xcd, 0xc9, 0x95, 0x22, 0x73, 0x86, 0x05, 0x70,
0x9f, 0xc1, 0xc8, 0xe7, 0x12, 0x24, 0xda, 0xed, 0x61, 0xd2, 0x9a, 0x2e, 0x28, 0x76, 0xba, 0x3d,
0xba, 0x17, 0x74, 0xdc, 0x38, 0x08, 0xd5, 0x87, 0x0c, 0x16, 0x31, 0xaf, 0x3f, 0x0a, 0x46, 0xcc,
0xcc, 0x91, 0x4b, 0xc1, 0xc3, 0x36, 0x75, 0x0e, 0x3d, 0xc0, 0x05, 0xb1, 0xdf, 0x83, 0x9a, 0xd6,
0x04, 0x66, 0x34, 0x09, 0x13, 0x42, 0xf8, 0x83, 0x25, 0x6e, 0xb1, 0xfb, 0xb4, 0xff, 0xa0, 0x4b,
0x5e, 0x87, 0xb9, 0xae, 0x17, 0x52, 0x4c, 0x4e, 0x6b, 0x87, 0xf4, 0x94, 0x86, 0x91, 0xf4, 0x9c,
0x1b, 0xaa, 0xc2, 0xe1, 0x70, 0xfb, 0x2e, 0xcc, 0x1b, 0x5b, 0xa3, 0x28, 0x57, 0x26, 0xfc, 0x58,
0xd9, 0x84, 0x1f, 0x99, 0xec, 0x63, 0xff, 0x42, 0x01, 0x8a, 0xbb, 0xc1, 0x50, 0x3f, 0x62, 0xb0,
0xcc, 0x23, 0x06, 0x61, 0x02, 0xb5, 0x95, 0x85, 0x23, 0x34, 0xa3, 0x01, 0x24, 0x6b, 0x30, 0xe3,
0x0e, 0xe2, 0x76, 0x1c, 0x30, 0x93, 0xef, 0xcc, 0x0d, 0xbb, 0x9c, 0x9c, 0x71, 0x8b, 0x53, 0x35,
0x64, 0x01, 0x8a, 0xca, 0x56, 0x40, 0x04, 0x56, 0x64, 0xfe, 0x06, 0x1e, 0x75, 0x9e, 0x8b, 0xc8,
0x99, 0x28, 0x31, 0x6e, 0x31, 0xbf, 0xe7, 0xce, 0x1e, 0x97, 0xf8, 0x79, 0x55, 0xcc, 0x1c, 0x63,
0xd4, 0x81, 0x68, 0x22, 0xce, 0x2a, 0xcb, 0x7a, 0x4c, 0xb8, 0x62, 0x1e, 0xfc, 0xfe, 0xb3, 0x05,
0x65, 0x5c, 0x1b, 0xa6, 0xbd, 0x38, 0x7b, 0xab, 0x53, 0x06, 0x5c, 0x93, 0x69, 0x27, 0x0d, 0x26,
0xb6, 0x91, 0xe6, 0x58, 0x50, 0x13, 0xd2, 0x53, 0x1d, 0x57, 0xa0, 0xca, 0x4b, 0x2a, 0xa5, 0x8f,
0xd3, 0xbd, 0x02, 0x92, 0xab, 0x50, 0x3a, 0x09, 0x86, 0xd2, 0xdc, 0x06, 0x79, 0x60, 0x17, 0x0c,
0x1d, 0x84, 0x27, 0xe3, 0x61, 0xed, 0xf1, 0x69, 0x71, 0x23, 0x2a, 0x0d, 0x66, 0x66, 0xa4, 0x6a,
0x56, 0x5f, 0xa6, 0x14, 0xd4, 0x5e, 0x83, 0x59, 0x46, 0xf5, 0x5a, 0xd4, 0x75, 0x2c, 0x2b, 0xdb,
0x3f, 0x63, 0x41, 0x45, 0x22, 0x93, 0x55, 0x28, 0x31, 0x16, 0x4a, 0x39, 0xae, 0xea, 0xa0, 0x9e,
0xe1, 0x39, 0x88, 0xc1, 0x8c, 0x09, 0x0c, 0x86, 0x25, 0x7e, 0x92, 0x0c, 0x85, 0x25, 0x6e, 0x80,
0x1a, 0x6e, 0xca, 0x7a, 0x4e, 0x41, 0xed, 0xef, 0x5a, 0x30, 0x6d, 0xf4, 0x41, 0x56, 0xa0, 0xd6,
0x77, 0xa3, 0x58, 0x1c, 0x7e, 0x8a, 0xed, 0xd1, 0x41, 0xfa, 0x46, 0x17, 0xcc, 0xe0, 0xbf, 0x8a,
0x10, 0x17, 0xf5, 0x08, 0xf1, 0x2d, 0xa8, 0x26, 0xc9, 0xa8, 0x25, 0x83, 0xf7, 0x59, 0x8f, 0x32,
0x05, 0x21, 0x41, 0xc2, 0xa0, 0x63, 0xd0, 0x0f, 0x42, 0x71, 0x52, 0xc6, 0x0b, 0xf6, 0x5d, 0xa8,
0x69, 0xf8, 0x7a, 0x0c, 0xd2, 0x32, 0x62, 0x90, 0x2a, 0x3f, 0xa7, 0x90, 0xe4, 0xe7, 0xd8, 0xff,
0x6a, 0xc1, 0x34, 0xa3, 0x41, 0xcf, 0xef, 0x1d, 0x04, 0x7d, 0xaf, 0x73, 0x8e, 0x7b, 0x2f, 0xc9,
0x4d, 0x88, 0x44, 0x49, 0x8b, 0x26, 0x98, 0x51, 0xbd, 0x8c, 0x7c, 0x08, 0x16, 0x55, 0x65, 0xc6,
0xc3, 0x8c, 0x03, 0x8e, 0xdc, 0x48, 0xb0, 0x85, 0xb0, 0xda, 0x0c, 0x20, 0xe3, 0x34, 0x06, 0xc0,
0x6c, 0xab, 0x81, 0xd7, 0xef, 0x7b, 0x1c, 0x97, 0xdb, 0xf4, 0x79, 0x55, 0xac, 0xcf, 0xae, 0x17,
0xb9, 0x47, 0xc9, 0xe9, 0x8f, 0x2a, 0x63, 0x78, 0xc6, 0x7d, 0xa6, 0x85, 0x67, 0x26, 0x51, 0xae,
0x98, 0x40, 0xfb, 0xcf, 0x0a, 0x50, 0x93, 0x26, 0x42, 0xb7, 0x47, 0xc5, 0x81, 0xa6, 0x29, 0x18,
0x35, 0x88, 0xac, 0x37, 0xbc, 0x31, 0x0d, 0x92, 0x26, 0x8c, 0x62, 0x96, 0x30, 0xae, 0x40, 0x95,
0x11, 0xe8, 0x9b, 0xe8, 0xf6, 0x89, 0xfc, 0x6e, 0x05, 0x90, 0xb5, 0xb7, 0xb1, 0xb6, 0x9c, 0xd4,
0x22, 0xe0, 0x85, 0xc7, 0x9f, 0x77, 0xa0, 0x2e, 0x9a, 0xc1, 0x9d, 0x43, 0xc9, 0x93, 0xb0, 0x88,
0xb1, 0xab, 0x8e, 0x81, 0x29, 0xbf, 0xbc, 0x2d, 0xbf, 0xac, 0x5c, 0xf4, 0xa5, 0xc4, 0xb4, 0xef,
0xab, 0x53, 0xe5, 0xfb, 0xa1, 0x3b, 0x3c, 0x91, 0xbc, 0x7c, 0x0b, 0xe6, 0x3d, 0xbf, 0xd3, 0x1f,
0x75, 0x69, 0x7b, 0xe4, 0xbb, 0xbe, 0x1f, 0x8c, 0xfc, 0x0e, 0x95, 0x09, 0x3a, 0x79, 0x55, 0x76,
0x57, 0xa5, 0x73, 0x62, 0x43, 0x64, 0x0d, 0xca, 0x5c, 0x55, 0x72, 0xdd, 0x91, 0xcf, 0xe8, 0x1c,
0x85, 0xac, 0x42, 0x99, 0x6b, 0xcc, 0x82, 0xc1, 0x35, 0xda, 0xae, 0x3a, 0x1c, 0x81, 0x89, 0x1d,
0xcc, 0xe8, 0x35, 0xc5, 0x8e, 0xa9, 0x77, 0x26, 0x3b, 0x98, 0xf3, 0x6b, 0x2f, 0x00, 0xd9, 0xe7,
0x9c, 0xa2, 0x9f, 0x0d, 0x7d, 0xbf, 0x08, 0x35, 0x0d, 0xcc, 0x24, 0x48, 0x8f, 0x0d, 0xb8, 0xdd,
0xf5, 0xdc, 0x01, 0x8d, 0x69, 0x28, 0xb8, 0x23, 0x05, 0x65, 0x78, 0xee, 0x69, 0xaf, 0x1d, 0x8c,
0xe2, 0x76, 0x97, 0xf6, 0x42, 0xca, 0xb5, 0x29, 0x53, 0x4d, 0x06, 0x94, 0xe1, 0x31, 0xfa, 0xd4,
0xf0, 0x38, 0x05, 0xa5, 0xa0, 0xf2, 0xa4, 0x87, 0xaf, 0x51, 0x29, 0x39, 0xe9, 0xe1, 0x2b, 0x92,
0x96, 0x7d, 0xe5, 0x1c, 0xd9, 0xf7, 0x16, 0x2c, 0x71, 0x29, 0x27, 0xe4, 0x41, 0x3b, 0x45, 0x58,
0x63, 0x6a, 0xc9, 0x1a, 0x34, 0xd8, 0x98, 0x25, 0x4b, 0x44, 0xde, 0x37, 0x78, 0xd4, 0xd4, 0x72,
0x32, 0x70, 0x86, 0x8b, 0xe1, 0x4b, 0x1d, 0x97, 0x1f, 0xb7, 0x67, 0xe0, 0x88, 0xeb, 0x3e, 0x33,
0x71, 0xab, 0x02, 0x37, 0x05, 0x27, 0x77, 0x60, 0x79, 0x40, 0xbb, 0x9e, 0x6b, 0x36, 0x81, 0x11,
0x60, 0x9e, 0x53, 0x33, 0xae, 0xda, 0x9e, 0x86, 0xda, 0x61, 0x1c, 0x0c, 0xe5, 0x76, 0xce, 0x40,
0x9d, 0x17, 0x45, 0x8a, 0xd5, 0x65, 0xb8, 0x84, 0xf4, 0xf7, 0x28, 0x18, 0x06, 0xfd, 0xa0, 0x77,
0x6e, 0x38, 0x5d, 0x7f, 0x65, 0xc1, 0xbc, 0x51, 0x9b, 0x78, 0x5d, 0x18, 0xaf, 0x91, 0xb9, 0x31,
0x9c, 0x64, 0xe7, 0x34, 0xe1, 0xcd, 0x11, 0x79, 0x68, 0xfc, 0xb1, 0x48, 0x97, 0xd9, 0x48, 0xae,
0xcd, 0xc8, 0x0f, 0x39, 0xfd, 0x36, 0xb3, 0xf4, 0x2b, 0xbe, 0x97, 0xb7, 0x66, 0x64, 0x13, 0x5f,
0x10, 0x09, 0x0f, 0xdc, 0x09, 0x93, 0xe1, 0x39, 0xe5, 0xb6, 0xe9, 0x4e, 0xba, 0x1c, 0x41, 0x47,
0x01, 0x23, 0xfb, 0x97, 0x2d, 0x80, 0x64, 0x74, 0x78, 0x4c, 0xae, 0x14, 0x10, 0xbf, 0xa2, 0xa5,
0x29, 0x9b, 0xd7, 0xa0, 0xae, 0x4e, 0x3a, 0x13, 0x9d, 0x56, 0x93, 0x30, 0x66, 0x73, 0xdf, 0x80,
0xd9, 0x5e, 0x3f, 0x38, 0x42, 0x83, 0x00, 0x73, 0xf6, 0x22, 0x91, 0x68, 0x36, 0xc3, 0xc1, 0xf7,
0x04, 0x34, 0x51, 0x80, 0x25, 0x4d, 0x01, 0xda, 0xbf, 0x52, 0x50, 0x07, 0x53, 0xc9, 0x9c, 0xc7,
0xf2, 0x27, 0xb9, 0x9d, 0x11, 0xc4, 0x63, 0xce, 0x81, 0xd0, 0xac, 0x3d, 0xb8, 0x30, 0x4e, 0x76,
0x17, 0x66, 0x42, 0x2e, 0xe9, 0xa4, 0x18, 0x2c, 0xbd, 0x40, 0x0c, 0x4e, 0x87, 0x86, 0x96, 0xfc,
0x14, 0x34, 0xdc, 0xee, 0x29, 0x0d, 0x63, 0x0f, 0x23, 0x15, 0x68, 0xa2, 0x70, 0xe1, 0x3d, 0xab,
0xc1, 0xd1, 0x72, 0xb8, 0x01, 0xb3, 0x22, 0xb9, 0x4f, 0x61, 0x8a, 0x6b, 0x0f, 0x09, 0x98, 0x21,
0xda, 0xdf, 0x91, 0x67, 0x60, 0xe6, 0x1e, 0x8e, 0x5f, 0x11, 0x7d, 0x76, 0x85, 0xd4, 0xec, 0x3e,
0x21, 0xce, 0xa3, 0xba, 0x32, 0x1c, 0x52, 0xd4, 0x92, 0x63, 0xba, 0xe2, 0xfc, 0xd0, 0x5c, 0xd2,
0xd2, 0xcb, 0x2c, 0xa9, 0xfd, 0x43, 0x0b, 0xa6, 0x76, 0x83, 0xe1, 0xae, 0x48, 0x13, 0x42, 0x46,
0x50, 0x59, 0xb5, 0xb2, 0xf8, 0x82, 0x04, 0xa2, 0x5c, 0xcb, 0x60, 0x3a, 0x6d, 0x19, 0xfc, 0x5f,
0xb8, 0x8c, 0xc1, 0xb8, 0x30, 0x18, 0x06, 0x21, 0x63, 0x46, 0xb7, 0xcf, 0xcd, 0x80, 0xc0, 0x8f,
0x4f, 0xa4, 0x00, 0x7c, 0x11, 0x0a, 0x7a, 0xc8, 0xcc, 0xab, 0xe3, 0x46, 0xbd, 0xb0, 0x64, 0xb8,
0x5c, 0xcc, 0x56, 0xd8, 0x9f, 0x87, 0x2a, 0x9a, 0xe2, 0x38, 0xad, 0x37, 0xa0, 0x7a, 0x12, 0x0c,
0xdb, 0x27, 0x9e, 0x1f, 0x4b, 0xe6, 0x9e, 0x49, 0x6c, 0xe4, 0x5d, 0x5c, 0x10, 0x85, 0x60, 0xff,
0xc6, 0x24, 0x4c, 0x3d, 0xf0, 0x4f, 0x03, 0xaf, 0x83, 0xe7, 0x6d, 0x03, 0x3a, 0x08, 0x64, 0x8e,
0x31, 0xfb, 0x4d, 0xae, 0xc0, 0x14, 0x26, 0xd5, 0x0d, 0x39, 0xd1, 0xd6, 0xf9, 0xb9, 0xb8, 0x00,
0x31, 0xf3, 0x22, 0x4c, 0x6e, 0x83, 0x70, 0xf6, 0xd1, 0x20, 0xcc, 0x49, 0x09, 0xf5, 0xdb, 0x1c,
0xa2, 0x94, 0xe4, 0x70, 0x97, 0xb5, 0x1c, 0x6e, 0xd6, 0x97, 0x48, 0x6b, 0xe2, 0x79, 0x2f, 0xbc,
0x2f, 0x01, 0x42, 0xc7, 0x2a, 0xa4, 0x3c, 0x98, 0x8a, 0xc6, 0xca, 0x94, 0x70, 0xac, 0x74, 0x20,
0x33, 0x68, 0xf8, 0x07, 0x1c, 0x87, 0x8b, 0x6f, 0x1d, 0xc4, 0x4c, 0xc4, 0xf4, 0x45, 0x9e, 0x2a,
0xa7, 0xfd, 0x14, 0x98, 0xc9, 0xf8, 0x2e, 0x55, 0x02, 0x95, 0xcf, 0x03, 0xf8, 0x8d, 0x97, 0x34,
0x5c, 0x73, 0xc7, 0x78, 0xfe, 0xa3, 0x74, 0xc7, 0x18, 0xc1, 0xb8, 0xfd, 0xfe, 0x91, 0xdb, 0x79,
0x8a, 0xf7, 0xb4, 0xf0, 0x04, 0xac, 0xea, 0x98, 0x40, 0x4c, 0x4e, 0x4a, 0x76, 0x15, 0x33, 0x08,
0x4a, 0x8e, 0x0e, 0x22, 0xb7, 0xa1, 0x86, 0x2e, 0xa8, 0xd8, 0xd7, 0x19, 0xdc, 0xd7, 0x86, 0xee,
0xa3, 0xe2, 0xce, 0xea, 0x48, 0xfa, 0x59, 0xe0, 0x6c, 0x26, 0x23, 0xd1, 0xed, 0x76, 0xc5, 0x11,
0x6a, 0x83, 0xbb, 0xd3, 0x0a, 0xc0, 0xf4, 0xb1, 0x58, 0x30, 0x8e, 0x30, 0x87, 0x08, 0x06, 0x8c,
0x5c, 0x85, 0x0a, 0x73, 0x8f, 0x86, 0xae, 0xd7, 0xc5, 0x94, 0x46, 0xee, 0xa5, 0x29, 0x18, 0x6b,
0x43, 0xfe, 0x46, 0x45, 0x37, 0x8f, 0xab, 0x62, 0xc0, 0xd8, 0xda, 0xa8, 0x32, 0x32, 0xd3, 0x02,
0xdf, 0x51, 0x03, 0x48, 0xde, 0xc4, 0x83, 0xac, 0x98, 0x36, 0x17, 0x31, 0x50, 0x76, 0x59, 0xcc,
0x59, 0x10, 0xad, 0xfc, 0x7b, 0xc8, 0x50, 0x1c, 0x8e, 0x69, 0x6f, 0x40, 0x5d, 0x07, 0x93, 0x0a,
0x94, 0x1e, 0x1e, 0xec, 0xec, 0x37, 0x26, 0x48, 0x0d, 0xa6, 0x0e, 0x77, 0x1e, 0x3d, 0xda, 0xdb,
0xd9, 0x6e, 0x58, 0xa4, 0x0e, 0x15, 0x95, 0x49, 0x56, 0x60, 0xa5, 0x8d, 0xad, 0xad, 0x9d, 0x83,
0x47, 0x3b, 0xdb, 0x8d, 0xa2, 0x1d, 0x03, 0xd9, 0xe8, 0x76, 0x45, 0x2b, 0x2a, 0x48, 0x90, 0xd0,
0xb3, 0x65, 0xd0, 0x73, 0x0e, 0x4d, 0x15, 0xf2, 0x69, 0xea, 0x85, 0x2b, 0x6f, 0xef, 0x40, 0xed,
0x40, 0xbb, 0xb4, 0x84, 0xec, 0x25, 0xaf, 0x2b, 0x09, 0xb6, 0xd4, 0x20, 0xda, 0x70, 0x0a, 0xfa,
0x70, 0xec, 0xdf, 0xb7, 0xf8, 0xcd, 0x00, 0x35, 0x7c, 0xde, 0xb7, 0x0d, 0x75, 0x15, 0xad, 0x4a,
0x92, 0x44, 0x0d, 0x18, 0xc3, 0xc1, 0xa1, 0xb4, 0x83, 0xe3, 0xe3, 0x88, 0xca, 0x94, 0x2e, 0x03,
0xc6, 0xf8, 0x82, 0xd9, 0x66, 0xcc, 0xce, 0xf1, 0x78, 0x0f, 0x91, 0x48, 0xed, 0xca, 0xc0, 0x99,
0x94, 0x17, 0x01, 0x19, 0x99, 0xcc, 0xa6, 0xca, 0x2a, 0x97, 0x35, 0xbd, 0xca, 0x6b, 0x50, 0x51,
0xed, 0x9a, 0x02, 0x4c, 0x62, 0xaa, 0x7a, 0x26, 0x28, 0xd1, 0x5b, 0x31, 0x06, 0xcd, 0x85, 0x76,
0xb6, 0x82, 0xdc, 0x04, 0x72, 0xec, 0x85, 0x69, 0xf4, 0x22, 0xa2, 0xe7, 0xd4, 0xd8, 0x4f, 0x60,
0x5e, 0x12, 0x92, 0x66, 0x5a, 0x99, 0x9b, 0x68, 0x5d, 0xc4, 0x3e, 0x85, 0x2c, 0xfb, 0xd8, 0xff,
0x69, 0xc1, 0x94, 0xd8, 0xe9, 0xcc, 0xc5, 0x37, 0xbe, 0xcf, 0x06, 0x8c, 0x34, 0x8d, 0x4b, 0x2f,
0xc8, 0x6b, 0x42, 0x68, 0x66, 0xc4, 0x62, 0x31, 0x4f, 0x2c, 0x12, 0x28, 0x0d, 0xdd, 0xf8, 0x04,
0x3d, 0xf5, 0xaa, 0x83, 0xbf, 0x49, 0x83, 0xc7, 0x95, 0xb8, 0x08, 0xc6, 0x98, 0x52, 0xde, 0x15,
0x3f, 0xae, 0xed, 0xb3, 0x57, 0xfc, 0xae, 0x40, 0x15, 0x07, 0xd0, 0x4e, 0xc2, 0x46, 0x09, 0x80,
0x51, 0x2e, 0x2f, 0x20, 0x5f, 0x8b, 0xfc, 0xf3, 0x04, 0x62, 0x2f, 0xf2, 0x9d, 0x17, 0x4b, 0xa0,
0x0e, 0xa1, 0x45, 0xee, 0x70, 0x02, 0x4e, 0x28, 0x42, 0x0c, 0x20, 0x4d, 0x11, 0x02, 0xd5, 0x51,
0xf5, 0x76, 0x0b, 0x9a, 0xdb, 0xb4, 0x4f, 0x63, 0xba, 0xd1, 0xef, 0xa7, 0xdb, 0xbf, 0x0c, 0x97,
0x72, 0xea, 0x84, 0x35, 0xfd, 0x65, 0x58, 0xdc, 0xe0, 0x79, 0x96, 0x3f, 0xa9, 0x34, 0x1e, 0xbb,
0x09, 0x4b, 0xe9, 0x26, 0x45, 0x67, 0xf7, 0x60, 0x6e, 0x9b, 0x1e, 0x8d, 0x7a, 0x7b, 0xf4, 0x34,
0xe9, 0x88, 0x40, 0x29, 0x3a, 0x09, 0xce, 0x04, 0x63, 0xe2, 0x6f, 0xf2, 0x0a, 0x40, 0x9f, 0xe1,
0xb4, 0xa3, 0x21, 0xed, 0xc8, 0x7b, 0x26, 0x08, 0x39, 0x1c, 0xd2, 0x8e, 0xfd, 0x16, 0x10, 0xbd,
0x1d, 0xb1, 0x5e, 0x4c, 0x0b, 0x8e, 0x8e, 0xda, 0xd1, 0x79, 0x14, 0xd3, 0x81, 0xbc, 0x40, 0xa3,
0x83, 0xec, 0x1b, 0x50, 0x3f, 0x70, 0xcf, 0x1d, 0xfa, 0xa1, 0xb8, 0xef, 0xb8, 0x0c, 0x53, 0x43,
0xf7, 0x9c, 0x89, 0x29, 0x15, 0xcf, 0xc2, 0x6a, 0xfb, 0xdf, 0x0b, 0x30, 0xc9, 0x31, 0x59, 0xab,
0x5d, 0x1a, 0xc5, 0x9e, 0x8f, 0x84, 0x25, 0x5b, 0xd5, 0x40, 0x19, 0x52, 0x2e, 0xe4, 0x90, 0xb2,
0xf0, 0xf6, 0x64, 0xce, 0xbe, 0xa0, 0x57, 0x03, 0xc6, 0x88, 0x2b, 0xc9, 0xa7, 0xe3, 0x01, 0x95,
0x04, 0x90, 0x0a, 0x7d, 0x26, 0xba, 0x96, 0x8f, 0x4f, 0x72, 0xa9, 0xa0, 0x5c, 0x1d, 0x94, 0xab,
0xd1, 0xa7, 0x38, 0x81, 0x67, 0x34, 0x7a, 0x46, 0x73, 0x57, 0x5e, 0x42, 0x73, 0x73, 0x17, 0xf0,
0x45, 0x9a, 0x1b, 0x5e, 0x42, 0x73, 0xdb, 0x04, 0x1a, 0x78, 0x19, 0x90, 0xd9, 0x86, 0x92, 0x76,
0xbf, 0x65, 0x41, 0x43, 0x50, 0x91, 0xaa, 0x23, 0xaf, 0x19, 0x36, 0x70, 0x6e, 0x36, 0xfc, 0x35,
0x98, 0x46, 0xcb, 0x54, 0xc5, 0x78, 0x45, 0x40, 0xda, 0x00, 0xb2, 0x79, 0xc8, 0xf3, 0xe3, 0x81,
0xd7, 0x17, 0x9b, 0xa2, 0x83, 0x64, 0x98, 0x38, 0x74, 0x45, 0x5e, 0x99, 0xe5, 0xa8, 0xb2, 0xfd,
0xe7, 0x16, 0xcc, 0x69, 0x03, 0x16, 0x54, 0x78, 0x17, 0x24, 0x37, 0xf0, 0x80, 0x2f, 0xe7, 0xdc,
0x65, 0x93, 0x6d, 0x92, 0xcf, 0x0c, 0x64, 0xdc, 0x4c, 0xf7, 0x1c, 0x07, 0x18, 0x8d, 0x06, 0x42,
0x88, 0xea, 0x20, 0x46, 0x48, 0x67, 0x94, 0x3e, 0x55, 0x28, 0x5c, 0x8c, 0x1b, 0x30, 0x8c, 0xaa,
0x31, 0x8b, 0x5a, 0x21, 0x95, 0x44, 0x54, 0x4d, 0x07, 0xda, 0x7f, 0x6b, 0xc1, 0x3c, 0x77, 0x8d,
0x84, 0xe3, 0xa9, 0xae, 0x3d, 0x4d, 0x72, 0x5f, 0x90, 0x73, 0xe4, 0xee, 0x84, 0x23, 0xca, 0xe4,
0xb3, 0x2f, 0xe9, 0xce, 0xa9, 0x64, 0xb7, 0x31, 0x7b, 0x51, 0xcc, 0xdb, 0x8b, 0x17, 0xac, 0x74,
0x5e, 0x80, 0xb3, 0x9c, 0x1b, 0xe0, 0xdc, 0x9c, 0x82, 0x72, 0xd4, 0x09, 0x86, 0xd4, 0x5e, 0x82,
0x05, 0x73, 0x72, 0x42, 0x04, 0x7d, 0xdb, 0x82, 0xe6, 0x3d, 0x7e, 0x10, 0xe0, 0xf9, 0xbd, 0x5d,
0x2f, 0x8a, 0x83, 0x50, 0xdd, 0x0e, 0xbd, 0x0a, 0x10, 0xc5, 0x6e, 0x18, 0xf3, 0x3c, 0x6a, 0x11,
0x58, 0x4c, 0x20, 0x6c, 0x8c, 0xd4, 0xef, 0xf2, 0x5a, 0xbe, 0x37, 0xaa, 0x9c, 0xb1, 0x21, 0x84,
0xf3, 0x66, 0x68, 0xe2, 0xeb, 0x3c, 0xf9, 0x93, 0xd9, 0x0a, 0xf4, 0x14, 0xe5, 0x3a, 0xf7, 0x8a,
0x52, 0x50, 0xfb, 0x6f, 0x2c, 0x98, 0x4d, 0x06, 0x89, 0xc7, 0xa2, 0xa6, 0x74, 0x10, 0xea, 0x37,
0x91, 0x0e, 0x32, 0xe4, 0xe9, 0x31, 0x7d, 0x2c, 0xc6, 0xa6, 0x41, 0x90, 0x63, 0x45, 0x29, 0x18,
0x49, 0x03, 0x47, 0x07, 0xf1, 0x54, 0x2e, 0x66, 0x09, 0x08, 0xab, 0x46, 0x94, 0x30, 0x0d, 0x7e,
0x10, 0xe3, 0x57, 0x3c, 0x38, 0x2b, 0x8b, 0x52, 0x95, 0x4e, 0x21, 0x14, 0x55, 0xa9, 0x7e, 0xa8,
0x52, 0xe1, 0xeb, 0x23, 0xcb, 0xf6, 0xaf, 0x5a, 0x70, 0x29, 0x67, 0xe1, 0x05, 0xd7, 0x6c, 0xc3,
0xdc, 0xb1, 0xaa, 0x94, 0x8b, 0xc3, 0x59, 0x67, 0x49, 0x1e, 0xda, 0x99, 0x0b, 0xe2, 0x64, 0x3f,
0x50, 0x76, 0x11, 0x5f, 0x6e, 0x23, 0x59, 0x32, 0x5b, 0x61, 0x1f, 0x40, 0x6b, 0xe7, 0x19, 0x63,
0xc2, 0x2d, 0xfd, 0xa1, 0x13, 0x49, 0x0b, 0xb7, 0x33, 0x42, 0xe6, 0x62, 0x47, 0xfb, 0x18, 0xa6,
0x8d, 0xb6, 0xc8, 0xa7, 0x5f, 0xb6, 0x91, 0x54, 0x78, 0x1a, 0x4b, 0xfc, 0xa5, 0x16, 0x99, 0xb2,
0xa9, 0x81, 0xec, 0x53, 0x98, 0x7d, 0x77, 0xd4, 0x8f, 0xbd, 0xe4, 0xd5, 0x16, 0xf2, 0x59, 0xf1,
0x11, 0x36, 0x21, 0x97, 0x2e, 0xb7, 0x2b, 0x1d, 0x8f, 0xad, 0xd8, 0x80, 0xb5, 0xd4, 0xce, 0xf6,
0x98, 0xad, 0xb0, 0x2f, 0xc1, 0x72, 0xd2, 0x25, 0x5f, 0x3b, 0x29, 0xa8, 0xbf, 0x63, 0xf1, 0x6c,
0x07, 0xf3, 0x11, 0x19, 0x72, 0x1f, 0xe6, 0x23, 0xcf, 0xef, 0xf5, 0xa9, 0xde, 0x4e, 0x24, 0x56,
0x62, 0xd1, 0x1c, 0x9e, 0x78, 0x68, 0xc6, 0xc9, 0xfb, 0x82, 0x11, 0x48, 0xfe, 0x40, 0x13, 0x02,
0x49, 0x2d, 0x49, 0xde, 0x04, 0xbe, 0x04, 0x33, 0x66, 0x67, 0xe4, 0x8e, 0xc8, 0xb6, 0x4c, 0x46,
0xa6, 0xc7, 0xb2, 0x4d, 0xca, 0x30, 0x30, 0xed, 0x6f, 0x5a, 0xd0, 0x74, 0x28, 0x23, 0x63, 0xaa,
0x75, 0x2a, 0xa8, 0xe7, 0x6e, 0xa6, 0xd9, 0xf1, 0x13, 0x56, 0x59, 0x9c, 0x72, 0xae, 0x37, 0xc7,
0x6e, 0xca, 0xee, 0x44, 0xce, 0xac, 0x36, 0x2b, 0x30, 0x29, 0xe6, 0xb7, 0x0c, 0x8b, 0x62, 0x48,
0x72, 0x38, 0x49, 0xd0, 0xd4, 0xe8, 0xd4, 0x08, 0x9a, 0x1e, 0x41, 0x93, 0x5f, 0xdb, 0xd5, 0xe7,
0x91, 0xe4, 0x36, 0xf0, 0xed, 0x88, 0xda, 0xfa, 0x0d, 0x5e, 0x13, 0xc8, 0x48, 0x96, 0x0f, 0x8b,
0xe3, 0xf0, 0x53, 0x68, 0x1d, 0xb4, 0xf6, 0x1c, 0x6a, 0xda, 0x25, 0x68, 0xb2, 0x0c, 0xf3, 0x4f,
0x1e, 0x3c, 0xda, 0xdf, 0x39, 0x3c, 0x6c, 0x1f, 0x3c, 0xde, 0x7c, 0x67, 0xe7, 0xbd, 0xf6, 0xee,
0xc6, 0xe1, 0x6e, 0x63, 0x82, 0x2c, 0x01, 0xd9, 0xdf, 0x39, 0x7c, 0xb4, 0xb3, 0x6d, 0xc0, 0x2d,
0x72, 0x15, 0x5a, 0x8f, 0xf7, 0x1f, 0x1f, 0xee, 0x6c, 0xb7, 0xf3, 0xbe, 0x2b, 0x90, 0x57, 0xe0,
0x92, 0xa8, 0xcf, 0xf9, 0xbc, 0x78, 0xfb, 0x9b, 0x45, 0x98, 0xe1, 0xc9, 0x1b, 0xfc, 0x0d, 0x23,
0x1a, 0x92, 0x77, 0x61, 0x4a, 0x3c, 0x86, 0x45, 0xe4, 0xbe, 0x98, 0xcf, 0x6f, 0xb5, 0x96, 0xd2,
0x60, 0xb1, 0x98, 0xf3, 0x3f, 0xfb, 0xc3, 0x7f, 0xfa, 0xf5, 0xc2, 0x34, 0xa9, 0xad, 0x9f, 0xbe,
0xb9, 0xde, 0xa3, 0x7e, 0xc4, 0xda, 0xf8, 0x1a, 0x40, 0xf2, 0xc4, 0x13, 0x69, 0x2a, 0xdf, 0x2d,
0xf5, 0xfe, 0x55, 0xeb, 0x52, 0x4e, 0x8d, 0x68, 0xf7, 0x12, 0xb6, 0x3b, 0x6f, 0xcf, 0xb0, 0x76,
0x3d, 0xdf, 0x8b, 0xf9, 0x73, 0x4f, 0x6f, 0x5b, 0x6b, 0xa4, 0x0b, 0x75, 0xfd, 0xf1, 0x25, 0x22,
0x03, 0xc8, 0x39, 0xcf, 0x47, 0xb5, 0x2e, 0xe7, 0xd6, 0x49, 0x42, 0xc0, 0x3e, 0x16, 0xed, 0x06,
0xeb, 0x63, 0x84, 0x18, 0x49, 0x2f, 0x7d, 0xce, 0x1e, 0xc9, 0x1b, 0x4b, 0xe4, 0x8a, 0x46, 0xb1,
0x99, 0x17, 0x9e, 0x5a, 0xaf, 0x8c, 0xa9, 0x15, 0x7d, 0xbd, 0x82, 0x7d, 0x2d, 0xdb, 0x84, 0xf5,
0xd5, 0x41, 0x1c, 0xf9, 0xc2, 0xd3, 0xdb, 0xd6, 0xda, 0xed, 0xdf, 0xbc, 0x0e, 0x55, 0x75, 0x58,
0x44, 0x3e, 0x80, 0x69, 0x23, 0xbb, 0x86, 0xc8, 0x69, 0xe4, 0x25, 0xe3, 0xb4, 0xae, 0xe4, 0x57,
0x8a, 0x8e, 0xaf, 0x62, 0xc7, 0x4d, 0xb2, 0xc4, 0x3a, 0x16, 0xe9, 0x29, 0xeb, 0x98, 0x27, 0xc6,
0x2f, 0x7d, 0x3c, 0xd5, 0xc4, 0x00, 0xef, 0xec, 0x4a, 0x9a, 0x33, 0x8d, 0xde, 0x5e, 0x19, 0x53,
0x2b, 0xba, 0xbb, 0x82, 0xdd, 0x2d, 0x91, 0x05, 0xbd, 0x3b, 0x75, 0x88, 0x43, 0xf1, 0xa6, 0x92,
0xfe, 0x3c, 0x11, 0x79, 0x45, 0x11, 0x56, 0xde, 0xb3, 0x45, 0x8a, 0x44, 0xb2, 0x6f, 0x17, 0xd9,
0x4d, 0xec, 0x8a, 0x10, 0xdc, 0x3e, 0xfd, 0x75, 0x22, 0x72, 0x04, 0x35, 0xed, 0x49, 0x0d, 0x72,
0x69, 0xec, 0xf3, 0x1f, 0xad, 0x56, 0x5e, 0x55, 0xde, 0x54, 0xf4, 0xf6, 0xd7, 0x99, 0x7e, 0xff,
0x2a, 0x54, 0xd5, 0x23, 0x0d, 0x64, 0x59, 0x7b, 0x34, 0x43, 0x7f, 0x54, 0xa2, 0xd5, 0xcc, 0x56,
0xe4, 0x11, 0x9f, 0xde, 0x3a, 0x23, 0xbe, 0x27, 0x50, 0xd3, 0x1e, 0x62, 0x50, 0x13, 0xc8, 0x3e,
0xf6, 0xa0, 0x26, 0x90, 0xf3, 0x6e, 0x83, 0x3d, 0x87, 0x5d, 0xd4, 0x48, 0x15, 0xe9, 0x3b, 0x7e,
0x16, 0x44, 0x64, 0x0f, 0x16, 0x85, 0xb8, 0x3b, 0xa2, 0x1f, 0x67, 0x1b, 0x72, 0x5e, 0x84, 0xba,
0x65, 0x91, 0xbb, 0x50, 0x91, 0xef, 0x6d, 0x90, 0xa5, 0xfc, 0x77, 0x43, 0x5a, 0xcb, 0x19, 0xb8,
0x90, 0xa6, 0xef, 0x01, 0x24, 0xaf, 0x3e, 0x28, 0x21, 0x91, 0x79, 0x45, 0x42, 0x51, 0x40, 0xf6,
0x89, 0x08, 0x7b, 0x09, 0x27, 0xd8, 0x20, 0x28, 0x24, 0x7c, 0x7a, 0x26, 0x2f, 0x25, 0x7e, 0x1d,
0x6a, 0xda, 0xc3, 0x0f, 0x6a, 0xf9, 0xb2, 0x8f, 0x46, 0xa8, 0xe5, 0xcb, 0x79, 0x27, 0xc2, 0x6e,
0x61, 0xeb, 0x0b, 0xf6, 0x2c, 0x6b, 0x3d, 0xf2, 0x7a, 0xfe, 0x80, 0x23, 0xb0, 0x0d, 0x3a, 0x81,
0x69, 0xe3, 0x75, 0x07, 0xc5, 0xa1, 0x79, 0x6f, 0x47, 0x28, 0x0e, 0xcd, 0x7d, 0x10, 0x42, 0xd2,
0x99, 0x3d, 0xc7, 0xfa, 0x39, 0x45, 0x14, 0xad, 0xa7, 0xf7, 0xa1, 0xa6, 0xbd, 0xd4, 0xa0, 0xe6,
0x92, 0x7d, 0x14, 0x42, 0xcd, 0x25, 0xef, 0x61, 0x87, 0x05, 0xec, 0x63, 0xc6, 0x46, 0x52, 0xc0,
0xeb, 0x75, 0xac, 0xed, 0x0f, 0x60, 0xc6, 0x7c, 0xbb, 0x41, 0xf1, 0x7e, 0xee, 0x2b, 0x10, 0x8a,
0xf7, 0xc7, 0x3c, 0xf8, 0x20, 0x48, 0x7a, 0x6d, 0x5e, 0x75, 0xb2, 0xfe, 0x91, 0x48, 0x22, 0x79,
0x4e, 0xbe, 0xcc, 0x04, 0x9c, 0xb8, 0xef, 0x48, 0x96, 0x35, 0xaa, 0xd5, 0x6f, 0x45, 0x2a, 0x7e,
0xc9, 0x5c, 0x8d, 0x34, 0x89, 0x99, 0x5f, 0x10, 0x44, 0xad, 0x85, 0xf7, 0x1e, 0x35, 0xad, 0xa5,
0x5f, 0x8d, 0xd4, 0xb4, 0x96, 0x71, 0x3d, 0x32, 0xad, 0xb5, 0x62, 0x8f, 0xb5, 0xe1, 0xc3, 0x6c,
0x2a, 0x03, 0x58, 0x71, 0x45, 0xfe, 0x95, 0x89, 0xd6, 0xd5, 0x17, 0x27, 0x0e, 0x9b, 0x12, 0x44,
0x0a, 0xc1, 0x75, 0x79, 0x41, 0xe5, 0xff, 0x43, 0x5d, 0xbf, 0x27, 0x4f, 0x74, 0x56, 0x4e, 0xf7,
0x74, 0x39, 0xb7, 0xce, 0xdc, 0x5c, 0x52, 0xd7, 0xbb, 0x21, 0x5f, 0x81, 0x25, 0xc5, 0xea, 0x7a,
0x52, 0x69, 0x44, 0x5e, 0xcd, 0x49, 0x35, 0xd5, 0x8d, 0xa0, 0xd6, 0xa5, 0xb1, 0xb9, 0xa8, 0xb7,
0x2c, 0x46, 0x34, 0xe6, 0x05, 0xe4, 0x44, 0x61, 0xe4, 0xdd, 0xbb, 0x4e, 0x14, 0x46, 0xee, 0xad,
0x65, 0x49, 0x34, 0x64, 0xde, 0x58, 0x23, 0x7e, 0xce, 0x47, 0xde, 0x87, 0x59, 0x2d, 0x6d, 0xff,
0xf0, 0xdc, 0xef, 0x28, 0x06, 0xc8, 0xde, 0xef, 0x6a, 0xe5, 0x99, 0xf8, 0xf6, 0x32, 0xb6, 0x3f,
0x67, 0x1b, 0x8b, 0xc3, 0x88, 0x7f, 0x0b, 0x6a, 0xfa, 0x95, 0x80, 0x17, 0xb4, 0xbb, 0xac, 0x55,
0xe9, 0xd7, 0x93, 0x6e, 0x59, 0xe4, 0xb7, 0x2d, 0xa8, 0x1b, 0x09, 0xf6, 0xc6, 0x69, 0x76, 0xaa,
0x9d, 0xa6, 0x5e, 0xa7, 0x37, 0x64, 0x3b, 0x38, 0xc8, 0xbd, 0xb5, 0x2f, 0x19, 0x8b, 0xf0, 0x91,
0x11, 0xc7, 0xb9, 0x99, 0x7e, 0xe3, 0xeb, 0x79, 0x1a, 0x41, 0xbf, 0x03, 0xf7, 0xfc, 0x96, 0x45,
0xbe, 0x6b, 0xc1, 0x8c, 0x19, 0x7d, 0x54, 0x5b, 0x95, 0x1b, 0xe7, 0x54, 0x5b, 0x35, 0x26, 0x64,
0xf9, 0x3e, 0x8e, 0xf2, 0xd1, 0x9a, 0x63, 0x8c, 0x52, 0x5c, 0x4d, 0xff, 0xf1, 0x46, 0x4b, 0xde,
0xe6, 0xcf, 0x00, 0xca, 0x90, 0x38, 0xd1, 0xb4, 0x46, 0x7a, 0x7b, 0xf5, 0xa7, 0xeb, 0x56, 0xad,
0x5b, 0x16, 0xf9, 0x3a, 0x7f, 0xdb, 0x4a, 0x7c, 0x8b, 0x54, 0xf2, 0xb2, 0xdf, 0xdb, 0xd7, 0x70,
0x4e, 0x57, 0xed, 0x4b, 0xc6, 0x9c, 0xd2, 0xfa, 0x78, 0x83, 0x8f, 0x4e, 0xbc, 0x3a, 0x97, 0x28,
0x94, 0xcc, 0x4b, 0x74, 0xe3, 0x07, 0x39, 0xe0, 0x83, 0x14, 0xe8, 0x06, 0x29, 0xbf, 0x64, 0x33,
0xf6, 0x1a, 0x8e, 0xf5, 0x9a, 0xfd, 0xea, 0xd8, 0xb1, 0xae, 0x63, 0x0c, 0x91, 0x8d, 0xf8, 0x00,
0x20, 0x39, 0xbe, 0x22, 0xa9, 0xe3, 0x13, 0xc5, 0xe0, 0xd9, 0x13, 0x2e, 0x93, 0x5f, 0xe4, 0x29,
0x0b, 0x6b, 0xf1, 0xab, 0x5c, 0x5c, 0x3d, 0x90, 0x07, 0x2f, 0xba, 0x51, 0x62, 0x9e, 0x33, 0x19,
0x46, 0x49, 0xba, 0x7d, 0x43, 0x58, 0xa9, 0x53, 0x9c, 0xc7, 0x30, 0xbd, 0x17, 0x04, 0x4f, 0x47,
0x43, 0x75, 0x14, 0x6d, 0x86, 0xf7, 0x77, 0xdd, 0xe8, 0xa4, 0x95, 0x9a, 0x85, 0xbd, 0x82, 0x4d,
0xb5, 0x48, 0x53, 0x6b, 0x6a, 0xfd, 0xa3, 0xe4, 0x78, 0xec, 0x39, 0x71, 0x61, 0x4e, 0xc9, 0x40,
0x35, 0xf0, 0x96, 0xd9, 0x8c, 0x21, 0xf9, 0xd2, 0x5d, 0x18, 0xd6, 0xb3, 0x1c, 0xed, 0x7a, 0x24,
0xdb, 0xbc, 0x65, 0x91, 0x03, 0xa8, 0x6f, 0xd3, 0x4e, 0xd0, 0xa5, 0x22, 0x46, 0x3e, 0x9f, 0x0c,
0x5c, 0x05, 0xd7, 0x5b, 0xd3, 0x06, 0xd0, 0xd4, 0x0b, 0x43, 0xf7, 0x3c, 0xa4, 0x1f, 0xae, 0x7f,
0x24, 0xa2, 0xef, 0xcf, 0xa5, 0x5e, 0x90, 0xc7, 0x13, 0x86, 0x5e, 0x48, 0x9d, 0x67, 0x18, 0x7a,
0x21, 0x73, 0x9e, 0x61, 0x2c, 0xb5, 0x3c, 0x1e, 0x21, 0x7d, 0x98, 0xcb, 0x1c, 0x81, 0x28, 0x95,
0x30, 0xee, 0xe0, 0xa4, 0xb5, 0x32, 0x1e, 0xc1, 0xec, 0x6d, 0xcd, 0xec, 0xed, 0x10, 0xa6, 0xb7,
0x29, 0x5f, 0x2c, 0x9e, 0x29, 0x97, 0xba, 0xa5, 0xa1, 0xe7, 0xe1, 0xa5, 0x05, 0x38, 0xd6, 0x99,
0x8a, 0x1f, 0xd3, 0xd4, 0xc8, 0x57, 0xa1, 0x76, 0x9f, 0xc6, 0x32, 0x35, 0x4e, 0x99, 0x9e, 0xa9,
0x5c, 0xb9, 0x56, 0x4e, 0x66, 0x9d, 0x49, 0x33, 0xd8, 0xda, 0x3a, 0xed, 0xf6, 0x28, 0x17, 0x4e,
0x6d, 0xaf, 0xfb, 0x9c, 0xfc, 0x3f, 0x6c, 0x5c, 0x65, 0xf0, 0x2e, 0x69, 0x79, 0x51, 0x7a, 0xe3,
0xb3, 0x29, 0x78, 0x5e, 0xcb, 0x7e, 0xd0, 0xa5, 0x9a, 0x09, 0xe4, 0x43, 0x4d, 0x4b, 0x3c, 0x57,
0x0c, 0x94, 0xbd, 0x27, 0xa0, 0x18, 0x28, 0x27, 0x4f, 0xdd, 0x5e, 0xc5, 0x7e, 0x6c, 0xb2, 0x92,
0xf4, 0xc3, 0x73, 0xd3, 0x93, 0x9e, 0xd6, 0x3f, 0x72, 0x07, 0xf1, 0x73, 0xf2, 0x04, 0x9f, 0x8a,
0xd0, 0xd3, 0xff, 0x12, 0x5b, 0x3a, 0x9d, 0x29, 0xa8, 0x16, 0x4b, 0xab, 0x32, 0xed, 0x6b, 0xde,
0x15, 0x5a, 0x4a, 0x9f, 0x05, 0x38, 0x8c, 0x83, 0xe1, 0xb6, 0x4b, 0x07, 0x81, 0x9f, 0xc8, 0xda,
0x24, 0x51, 0x2d, 0x91, 0x5f, 0x5a, 0xb6, 0x1a, 0x79, 0xa2, 0x39, 0x1f, 0x46, 0xf6, 0xa4, 0x24,
0xae, 0xb1, 0xb9, 0x6c, 0x6a, 0x41, 0x72, 0xf2, 0xd9, 0x6e, 0x59, 0x64, 0x03, 0x20, 0x39, 0x03,
0x53, 0xae, 0x44, 0xe6, 0x78, 0x4d, 0x89, 0xbd, 0x9c, 0x03, 0xb3, 0x03, 0xa8, 0x26, 0x87, 0x2a,
0xcb, 0xc9, 0xdd, 0x08, 0xe3, 0x08, 0x46, 0x69, 0xf0, 0xcc, 0x51, 0x87, 0xdd, 0xc0, 0xa5, 0x02,
0x52, 0x61, 0x4b, 0x85, 0xe7, 0x17, 0x1e, 0xcc, 0xf3, 0x01, 0x2a, 0x73, 0x04, 0x53, 0xaf, 0xe4,
0x4c, 0x72, 0x8e, 0x1b, 0x14, 0x37, 0xe7, 0x46, 0xeb, 0x8d, 0x88, 0x08, 0xa3, 0x56, 0x9e, 0xf6,
0xc5, 0x44, 0xf3, 0x00, 0xe6, 0x32, 0xe1, 0x64, 0xc5, 0xd2, 0xe3, 0x22, 0xfc, 0x8a, 0xa5, 0xc7,
0x46, 0xa2, 0xed, 0x45, 0xec, 0x72, 0xd6, 0x06, 0xf4, 0x80, 0xce, 0xbc, 0xb8, 0x73, 0xc2, 0xba,
0xfb, 0x8e, 0x05, 0xf3, 0x39, 0xd1, 0x62, 0xf2, 0x9a, 0x74, 0xa6, 0xc7, 0x46, 0x92, 0x5b, 0xb9,
0xc1, 0x44, 0xfb, 0x10, 0xfb, 0x79, 0x97, 0xbc, 0x63, 0x28, 0x36, 0x1e, 0xc7, 0x13, 0x9c, 0xf9,
0x42, 0xa3, 0x22, 0xd7, 0xa2, 0xf8, 0x10, 0x96, 0xf9, 0x40, 0x36, 0xfa, 0xfd, 0x54, 0xa0, 0xf3,
0x6a, 0xe6, 0xa5, 0x6f, 0x23, 0x80, 0xdb, 0x1a, 0xff, 0x12, 0xf8, 0x18, 0x73, 0x95, 0x0f, 0x95,
0x8c, 0xa0, 0x91, 0x0e, 0x1e, 0x92, 0xf1, 0x6d, 0xb5, 0x5e, 0x35, 0xdc, 0xc2, 0x6c, 0xc0, 0xd1,
0xfe, 0x24, 0x76, 0xf6, 0xaa, 0xdd, 0xca, 0x5b, 0x17, 0xee, 0x29, 0xb2, 0xfd, 0xf8, 0x69, 0x15,
0xe9, 0x4c, 0xcd, 0x53, 0x76, 0x30, 0x2e, 0x34, 0xab, 0x1c, 0xd3, 0xfc, 0x40, 0xe9, 0x75, 0xec,
0x7e, 0xc5, 0xbe, 0x9c, 0xd7, 0x7d, 0xc8, 0x3f, 0xe1, 0x2e, 0xea, 0x72, 0x9a, 0xaf, 0xe5, 0x08,
0x56, 0xf2, 0xf6, 0x7b, 0xac, 0xaf, 0x91, 0x5a, 0xeb, 0x89, 0x5b, 0xd6, 0xe6, 0x8d, 0xf7, 0x3f,
0xd9, 0xf3, 0xe2, 0x93, 0xd1, 0xd1, 0xcd, 0x4e, 0x30, 0x58, 0xef, 0xcb, 0x10, 0x99, 0x48, 0xf3,
0x5d, 0xef, 0xfb, 0xdd, 0x75, 0xfc, 0xfe, 0x68, 0x12, 0xff, 0x71, 0xc0, 0xa7, 0xff, 0x2b, 0x00,
0x00, 0xff, 0xff, 0x1d, 0xe9, 0x2d, 0xa2, 0x6a, 0x60, 0x00, 0x00,
}

View File

@ -1257,6 +1257,26 @@ message Peer {
/// Ping time to this peer
int64 ping_time = 9 [json_name = "ping_time"];
enum SyncType {
/**
Denotes that we cannot determine the peer's current sync type.
*/
UNKNOWN_SYNC = 0;
/**
Denotes that we are actively receiving new graph updates from the peer.
*/
ACTIVE_SYNC = 1;
/**
Denotes that we are not receiving new graph updates from the peer.
*/
PASSIVE_SYNC = 2;
}
// The type of sync we are currently performing with this peer.
SyncType sync_type = 10 [json_name = "sync_type"];
}
message ListPeersRequest {

View File

@ -1305,6 +1305,16 @@
],
"default": "OPEN"
},
"PeerSyncType": {
"type": "string",
"enum": [
"UNKNOWN_SYNC",
"ACTIVE_SYNC",
"PASSIVE_SYNC"
],
"default": "UNKNOWN_SYNC",
"description": " - UNKNOWN_SYNC: *\nDenotes that we cannot determine the peer's current sync type.\n - ACTIVE_SYNC: *\nDenotes that we are actively receiving new graph updates from the peer.\n - PASSIVE_SYNC: *\nDenotes that we are not receiving new graph updates from the peer."
},
"PendingChannelsResponseClosedChannel": {
"type": "object",
"properties": {
@ -2856,6 +2866,10 @@
"type": "string",
"format": "int64",
"title": "/ Ping time to this peer"
},
"sync_type": {
"$ref": "#/definitions/PeerSyncType",
"description": "The type of sync we are currently performing with this peer."
}
}
},

15
peer.go
View File

@ -396,19 +396,16 @@ func (p *peer) initGossipSync() {
srvrLog.Infof("Negotiated chan series queries with %x",
p.pubKeyBytes[:])
// We'll only request channel updates from the remote peer if
// its enabled in the config, or we're already getting updates
// from enough peers.
//
// TODO(roasbeef): craft s.t. we only get updates from a few
// peers
recvUpdates := !cfg.NoChanUpdates
// Register the this peer's for gossip syncer with the gossiper.
// This is blocks synchronously to ensure the gossip syncer is
// registered with the gossiper before attempting to read
// messages from the remote peer.
p.server.authGossiper.InitSyncState(p, recvUpdates)
//
// TODO(wilmer): Only sync updates from non-channel peers. This
// requires an improved version of the current network
// bootstrapper to ensure we can find and connect to non-channel
// peers.
p.server.authGossiper.InitSyncState(p)
// If the remote peer has the initial sync feature bit set, then we'll
// being the synchronization protocol to exchange authenticated channel

View File

@ -34,6 +34,7 @@ import (
"github.com/lightningnetwork/lnd/chanbackup"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/channelnotifier"
"github.com/lightningnetwork/lnd/discovery"
"github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/invoices"
@ -2012,9 +2013,36 @@ func (r *rpcServer) ListPeers(ctx context.Context,
satRecv += int64(c.TotalMSatReceived.ToSatoshis())
}
nodePub := serverPeer.addr.IdentityKey.SerializeCompressed()
nodePub := serverPeer.PubKey()
// Retrieve the peer's sync type. If we don't currently have a
// syncer for the peer, then we'll default to a passive sync.
// This can happen if the RPC is called while a peer is
// initializing.
syncer, ok := r.server.authGossiper.SyncManager().GossipSyncer(
nodePub,
)
var lnrpcSyncType lnrpc.Peer_SyncType
if !ok {
rpcsLog.Warnf("Gossip syncer for peer=%x not found",
nodePub)
lnrpcSyncType = lnrpc.Peer_UNKNOWN_SYNC
} else {
syncType := syncer.SyncType()
switch syncType {
case discovery.ActiveSync:
lnrpcSyncType = lnrpc.Peer_ACTIVE_SYNC
case discovery.PassiveSync:
lnrpcSyncType = lnrpc.Peer_PASSIVE_SYNC
default:
return nil, fmt.Errorf("unhandled sync type %v",
syncType)
}
}
peer := &lnrpc.Peer{
PubKey: hex.EncodeToString(nodePub),
PubKey: hex.EncodeToString(nodePub[:]),
Address: serverPeer.conn.RemoteAddr().String(),
Inbound: serverPeer.inbound,
BytesRecv: atomic.LoadUint64(&serverPeer.bytesReceived),
@ -2022,6 +2050,7 @@ func (r *rpcServer) ListPeers(ctx context.Context,
SatSent: satSent,
SatRecv: satRecv,
PingTime: serverPeer.PingTime(),
SyncType: lnrpcSyncType,
}
resp.Peers = append(resp.Peers, peer)

View File

@ -636,10 +636,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl,
return nil, fmt.Errorf("can't create router: %v", err)
}
chanSeries := discovery.NewChanSeries(
s.chanDB.ChannelGraph(),
)
chanSeries := discovery.NewChanSeries(s.chanDB.ChannelGraph())
gossipMessageStore, err := discovery.NewMessageStore(s.chanDB)
if err != nil {
return nil, err
@ -650,19 +647,23 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl,
}
s.authGossiper = discovery.New(discovery.Config{
Router: s.chanRouter,
Notifier: s.cc.chainNotifier,
ChainHash: *activeNetParams.GenesisHash,
Broadcast: s.BroadcastMessage,
ChanSeries: chanSeries,
NotifyWhenOnline: s.NotifyWhenOnline,
NotifyWhenOffline: s.NotifyWhenOffline,
ProofMatureDelta: 0,
TrickleDelay: time.Millisecond * time.Duration(cfg.TrickleDelay),
RetransmitDelay: time.Minute * 30,
WaitingProofStore: waitingProofStore,
MessageStore: gossipMessageStore,
AnnSigner: s.nodeSigner,
Router: s.chanRouter,
Notifier: s.cc.chainNotifier,
ChainHash: *activeNetParams.GenesisHash,
Broadcast: s.BroadcastMessage,
ChanSeries: chanSeries,
NotifyWhenOnline: s.NotifyWhenOnline,
NotifyWhenOffline: s.NotifyWhenOffline,
ProofMatureDelta: 0,
TrickleDelay: time.Millisecond * time.Duration(cfg.TrickleDelay),
RetransmitDelay: time.Minute * 30,
WaitingProofStore: waitingProofStore,
MessageStore: gossipMessageStore,
AnnSigner: s.nodeSigner,
RotateTicker: ticker.New(discovery.DefaultSyncerRotationInterval),
HistoricalSyncTicker: ticker.New(discovery.DefaultHistoricalSyncInterval),
ActiveSyncerTimeoutTicker: ticker.New(discovery.DefaultActiveSyncerTimeout),
NumActiveSyncers: cfg.NumGraphSyncPeers,
},
s.identityPriv.PubKey(),
)
@ -2622,7 +2623,7 @@ func (s *server) peerTerminationWatcher(p *peer, ready chan struct{}) {
// We'll also inform the gossiper that this peer is no longer active,
// so we don't need to maintain sync state for it any longer.
s.authGossiper.PruneSyncState(pubKey)
s.authGossiper.PruneSyncState(p.PubKey())
// Tell the switch to remove all links associated with this peer.
// Passing nil as the target link indicates that all links associated