Merge pull request #506 from halseth/dynamic-channel-params

Dynamic and user definable channel params
This commit is contained in:
Olaoluwa Osuntokun 2018-01-12 15:38:12 -08:00 committed by GitHub
commit db06a8a7ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 1074 additions and 572 deletions

View File

@ -18,7 +18,6 @@ import (
"github.com/lightningnetwork/lnd/htlcswitch" "github.com/lightningnetwork/lnd/htlcswitch"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet" "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/chainview" "github.com/lightningnetwork/lnd/routing/chainview"
"github.com/roasbeef/btcd/chaincfg/chainhash" "github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/rpcclient" "github.com/roasbeef/btcd/rpcclient"
@ -27,28 +26,11 @@ import (
"github.com/roasbeef/btcwallet/walletdb" "github.com/roasbeef/btcwallet/walletdb"
) )
// defaultBitcoinForwardingPolicy is the default forwarding policy used for
// Bitcoin channels.
var defaultBitcoinForwardingPolicy = htlcswitch.ForwardingPolicy{
MinHTLC: lnwire.NewMSatFromSatoshis(1),
BaseFee: lnwire.NewMSatFromSatoshis(1),
FeeRate: 1,
TimeLockDelta: 144,
}
// defaultLitecoinForwardingPolicy is the default forwarding policy used for
// Litecoin channels.
var defaultLitecoinForwardingPolicy = htlcswitch.ForwardingPolicy{
MinHTLC: lnwire.NewMSatFromSatoshis(1),
BaseFee: 1,
FeeRate: 1,
TimeLockDelta: 576,
}
// defaultChannelConstraints is the default set of channel constraints that are // defaultChannelConstraints is the default set of channel constraints that are
// meant to be used when initially funding a channel. // meant to be used when initially funding a channel.
// //
// TODO(roasbeef): have one for both chains // TODO(roasbeef): have one for both chains
// TODO(halseth): make configurable at startup?
var defaultChannelConstraints = channeldb.ChannelConstraints{ var defaultChannelConstraints = channeldb.ChannelConstraints{
DustLimit: lnwallet.DefaultDustLimit(), DustLimit: lnwallet.DefaultDustLimit(),
MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2, MaxAcceptedHtlcs: lnwallet.MaxHTLCNumber / 2,
@ -119,12 +101,22 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
switch registeredChains.PrimaryChain() { switch registeredChains.PrimaryChain() {
case bitcoinChain: case bitcoinChain:
cc.routingPolicy = defaultBitcoinForwardingPolicy cc.routingPolicy = htlcswitch.ForwardingPolicy{
MinHTLC: cfg.Bitcoin.MinHTLC,
BaseFee: cfg.Bitcoin.BaseFee,
FeeRate: cfg.Bitcoin.FeeRate,
TimeLockDelta: cfg.Bitcoin.TimeLockDelta,
}
cc.feeEstimator = lnwallet.StaticFeeEstimator{ cc.feeEstimator = lnwallet.StaticFeeEstimator{
FeeRate: 50, FeeRate: 50,
} }
case litecoinChain: case litecoinChain:
cc.routingPolicy = defaultLitecoinForwardingPolicy cc.routingPolicy = htlcswitch.ForwardingPolicy{
MinHTLC: cfg.Litecoin.MinHTLC,
BaseFee: cfg.Litecoin.BaseFee,
FeeRate: cfg.Litecoin.FeeRate,
TimeLockDelta: cfg.Litecoin.TimeLockDelta,
}
cc.feeEstimator = lnwallet.StaticFeeEstimator{ cc.feeEstimator = lnwallet.StaticFeeEstimator{
FeeRate: 100, FeeRate: 100,
} }

View File

@ -431,6 +431,11 @@ var openChannelCommand = cli.Command{
"must be explicitly told about it to be able " + "must be explicitly told about it to be able " +
"to route through it", "to route through it",
}, },
cli.Int64Flag{
Name: "min_htlc_msat",
Usage: "(optional) the minimum value we will require " +
"for incoming HTLCs on the channel",
},
}, },
Action: actionDecorator(openChannel), Action: actionDecorator(openChannel),
} }
@ -456,8 +461,9 @@ func openChannel(ctx *cli.Context) error {
} }
req := &lnrpc.OpenChannelRequest{ req := &lnrpc.OpenChannelRequest{
TargetConf: int32(ctx.Int64("conf_target")), TargetConf: int32(ctx.Int64("conf_target")),
SatPerByte: ctx.Int64("sat_per_byte"), SatPerByte: ctx.Int64("sat_per_byte"),
MinHtlcMsat: ctx.Int64("min_htlc_msat"),
} }
switch { switch {
@ -1970,7 +1976,7 @@ var feeReportCommand = cli.Command{
Usage: "display the current fee policies of all active channels", Usage: "display the current fee policies of all active channels",
Description: ` Description: `
Returns the current fee policies of all active channels. Returns the current fee policies of all active channels.
Fee policies can be updated using the updateFees command.`, Fee policies can be updated using the updatechanpolicy command.`,
Action: actionDecorator(feeReport), Action: actionDecorator(feeReport),
} }
@ -1989,13 +1995,13 @@ func feeReport(ctx *cli.Context) error {
return nil return nil
} }
var updateFeesCommand = cli.Command{ var updateChannelPolicyCommand = cli.Command{
Name: "updatefees", Name: "updatechanpolicy",
Usage: "update the fee policy for all channels, or a single channel", Usage: "update the channel policy for all channels, or a single channel",
ArgsUsage: "base_fee_msat fee_rate [channel_point]", ArgsUsage: "base_fee_msat fee_rate time_lock_delta [channel_point]",
Description: ` Description: `
Updates the fee policy for all channels, or just a particular channel Updates the channel policy for all channels, or just a particular channel
identified by it's channel point. The fee update will be committed, and identified by its channel point. The update will be committed, and
broadcast to the rest of the network within the next batch. broadcast to the rest of the network within the next batch.
Channel points are encoded as: funding_txid:output_index`, Channel points are encoded as: funding_txid:output_index`,
Flags: []cli.Flag{ Flags: []cli.Flag{
@ -2011,6 +2017,11 @@ var updateFeesCommand = cli.Command{
"proportionally based on the value of each " + "proportionally based on the value of each " +
"forwarded HTLC, the lowest possible rate is 0.000001", "forwarded HTLC, the lowest possible rate is 0.000001",
}, },
cli.Int64Flag{
Name: "time_lock_delta",
Usage: "the CLTV delta that will be applied to all " +
"forwarded HTLCs",
},
cli.StringFlag{ cli.StringFlag{
Name: "chan_point", Name: "chan_point",
Usage: "The channel whose fee policy should be " + Usage: "The channel whose fee policy should be " +
@ -2018,18 +2029,19 @@ var updateFeesCommand = cli.Command{
"will be updated. Takes the form of: txid:output_index", "will be updated. Takes the form of: txid:output_index",
}, },
}, },
Action: actionDecorator(updateFees), Action: actionDecorator(updateChannelPolicy),
} }
func updateFees(ctx *cli.Context) error { func updateChannelPolicy(ctx *cli.Context) error {
ctxb := context.Background() ctxb := context.Background()
client, cleanUp := getClient(ctx) client, cleanUp := getClient(ctx)
defer cleanUp() defer cleanUp()
var ( var (
baseFee int64 baseFee int64
feeRate float64 feeRate float64
err error timeLockDelta int64
err error
) )
args := ctx.Args() args := ctx.Args()
@ -2060,10 +2072,26 @@ func updateFees(ctx *cli.Context) error {
return fmt.Errorf("fee_rate argument missing") return fmt.Errorf("fee_rate argument missing")
} }
switch {
case ctx.IsSet("time_lock_delta"):
timeLockDelta = ctx.Int64("time_lock_delta")
case args.Present():
timeLockDelta, err = strconv.ParseInt(args.First(), 10, 64)
if err != nil {
return fmt.Errorf("unable to decode time_lock_delta: %v",
err)
}
args = args.Tail()
default:
return fmt.Errorf("time_lock_delta argument missing")
}
var ( var (
chanPoint *lnrpc.ChannelPoint chanPoint *lnrpc.ChannelPoint
chanPointStr string chanPointStr string
) )
switch { switch {
case ctx.IsSet("chan_point"): case ctx.IsSet("chan_point"):
chanPointStr = ctx.String("chan_point") chanPointStr = ctx.String("chan_point")
@ -2093,22 +2121,23 @@ func updateFees(ctx *cli.Context) error {
} }
} }
req := &lnrpc.FeeUpdateRequest{ req := &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee, BaseFeeMsat: baseFee,
FeeRate: feeRate, FeeRate: feeRate,
TimeLockDelta: uint32(timeLockDelta),
} }
if chanPoint != nil { if chanPoint != nil {
req.Scope = &lnrpc.FeeUpdateRequest_ChanPoint{ req.Scope = &lnrpc.PolicyUpdateRequest_ChanPoint{
ChanPoint: chanPoint, ChanPoint: chanPoint,
} }
} else { } else {
req.Scope = &lnrpc.FeeUpdateRequest_Global{ req.Scope = &lnrpc.PolicyUpdateRequest_Global{
Global: true, Global: true,
} }
} }
resp, err := client.UpdateFees(ctxb, req) resp, err := client.UpdateChannelPolicy(ctxb, req)
if err != nil { if err != nil {
return err return err
} }

View File

@ -193,7 +193,7 @@ func main() {
signMessageCommand, signMessageCommand,
verifyMessageCommand, verifyMessageCommand,
feeReportCommand, feeReportCommand,
updateFeesCommand, updateChannelPolicyCommand,
} }
if err := app.Run(os.Args); err != nil { if err := app.Run(os.Args); err != nil {

View File

@ -38,9 +38,22 @@ const (
defaultPeerPort = 9735 defaultPeerPort = 9735
defaultRPCHost = "localhost" defaultRPCHost = "localhost"
defaultMaxPendingChannels = 1 defaultMaxPendingChannels = 1
defaultNumChanConfs = 3
defaultNoEncryptWallet = false defaultNoEncryptWallet = false
defaultTrickleDelay = 30 * 1000 defaultTrickleDelay = 30 * 1000
// minTimeLockDelta is the minimum timelock we require for incoming
// HTLCs on our channels.
minTimeLockDelta = 4
defaultBitcoinMinHTLCMSat = 1000
defaultBitcoinBaseFeeMSat = 1000
defaultBitcoinFeeRate = 1
defaultBitcoinTimeLockDelta = 144
defaultLitecoinMinHTLCMSat = 1000
defaultLitecoinBaseFeeMSat = 1000
defaultLitecoinFeeRate = 1
defaultLitecoinTimeLockDelta = 576
) )
var ( var (
@ -74,6 +87,13 @@ type chainConfig struct {
TestNet3 bool `long:"testnet" description:"Use the test network"` TestNet3 bool `long:"testnet" description:"Use the test network"`
SimNet bool `long:"simnet" description:"Use the simulation test network"` SimNet bool `long:"simnet" description:"Use the simulation test network"`
RegTest bool `long:"regtest" description:"Use the regression test network"` RegTest bool `long:"regtest" description:"Use the regression test network"`
DefaultNumChanConfs int `long:"defaultchanconfs" description:"The default number of confirmations a channel must have before it's considered open. If this is not set, we will scale the value according to the channel size."`
DefaultRemoteDelay int `long:"defaultremotedelay" description:"The default number of blocks we will require our channel counterparty to wait before accessing its funds in case of unilateral close. If this is not set, we will scale the value according to the channel size."`
MinHTLC lnwire.MilliSatoshi `long:"minhtlc" description:"The smallest HTLC we are willing to forward on our channels, in millisatoshi"`
BaseFee lnwire.MilliSatoshi `long:"basefee" description:"The base fee in millisatoshi we will charge for forwarding payments on our channels"`
FeeRate lnwire.MilliSatoshi `long:"feerate" description:"The fee rate used when forwarding payments on our channels. The total fee charged is basefee + (amount * feerate / 1000000), where amount is the forwarded amount."`
TimeLockDelta uint32 `long:"timelockdelta" description:"The CLTV delta we will subtract from a forwarded HTLC's timelock value"`
} }
type neutrinoConfig struct { type neutrinoConfig struct {
@ -127,8 +147,6 @@ type config struct {
Litecoin *chainConfig `group:"Litecoin" namespace:"litecoin"` Litecoin *chainConfig `group:"Litecoin" namespace:"litecoin"`
Bitcoin *chainConfig `group:"Bitcoin" namespace:"bitcoin"` Bitcoin *chainConfig `group:"Bitcoin" namespace:"bitcoin"`
DefaultNumChanConfs int `long:"defaultchanconfs" description:"The default number of confirmations a channel must have before it's considered open."`
NeutrinoMode *neutrinoConfig `group:"neutrino" namespace:"neutrino"` NeutrinoMode *neutrinoConfig `group:"neutrino" namespace:"neutrino"`
Autopilot *autoPilotConfig `group:"autopilot" namespace:"autopilot"` Autopilot *autoPilotConfig `group:"autopilot" namespace:"autopilot"`
@ -150,27 +168,34 @@ type config struct {
// 4) Parse CLI options and overwrite/add any specified options // 4) Parse CLI options and overwrite/add any specified options
func loadConfig() (*config, error) { func loadConfig() (*config, error) {
defaultCfg := config{ defaultCfg := config{
ConfigFile: defaultConfigFile, ConfigFile: defaultConfigFile,
DataDir: defaultDataDir, DataDir: defaultDataDir,
DebugLevel: defaultLogLevel, DebugLevel: defaultLogLevel,
TLSCertPath: defaultTLSCertPath, TLSCertPath: defaultTLSCertPath,
TLSKeyPath: defaultTLSKeyPath, TLSKeyPath: defaultTLSKeyPath,
AdminMacPath: defaultAdminMacPath, AdminMacPath: defaultAdminMacPath,
ReadMacPath: defaultReadMacPath, ReadMacPath: defaultReadMacPath,
LogDir: defaultLogDir, LogDir: defaultLogDir,
PeerPort: defaultPeerPort, PeerPort: defaultPeerPort,
RPCPort: defaultRPCPort, RPCPort: defaultRPCPort,
RESTPort: defaultRESTPort, RESTPort: defaultRESTPort,
MaxPendingChannels: defaultMaxPendingChannels, MaxPendingChannels: defaultMaxPendingChannels,
DefaultNumChanConfs: defaultNumChanConfs, NoEncryptWallet: defaultNoEncryptWallet,
NoEncryptWallet: defaultNoEncryptWallet,
Bitcoin: &chainConfig{ Bitcoin: &chainConfig{
RPCHost: defaultRPCHost, RPCHost: defaultRPCHost,
RPCCert: defaultBtcdRPCCertFile, RPCCert: defaultBtcdRPCCertFile,
MinHTLC: defaultBitcoinMinHTLCMSat,
BaseFee: defaultBitcoinBaseFeeMSat,
FeeRate: defaultBitcoinFeeRate,
TimeLockDelta: defaultBitcoinTimeLockDelta,
}, },
Litecoin: &chainConfig{ Litecoin: &chainConfig{
RPCHost: defaultRPCHost, RPCHost: defaultRPCHost,
RPCCert: defaultLtcdRPCCertFile, RPCCert: defaultLtcdRPCCertFile,
MinHTLC: defaultLitecoinMinHTLCMSat,
BaseFee: defaultLitecoinBaseFeeMSat,
FeeRate: defaultLitecoinFeeRate,
TimeLockDelta: defaultLitecoinTimeLockDelta,
}, },
Autopilot: &autoPilotConfig{ Autopilot: &autoPilotConfig{
MaxChannels: 5, MaxChannels: 5,
@ -256,6 +281,11 @@ func loadConfig() (*config, error) {
return nil, fmt.Errorf(str, funcName) return nil, fmt.Errorf(str, funcName)
} }
if cfg.Litecoin.TimeLockDelta < minTimeLockDelta {
return nil, fmt.Errorf("timelockdelta must be at least %v",
minTimeLockDelta)
}
// The litecoin chain is the current active chain. However // The litecoin chain is the current active chain. However
// throughout the codebase we required chiancfg.Params. So as a // throughout the codebase we required chiancfg.Params. So as a
// temporary hack, we'll mutate the default net params for // temporary hack, we'll mutate the default net params for
@ -305,6 +335,11 @@ func loadConfig() (*config, error) {
return nil, err return nil, err
} }
if cfg.Bitcoin.TimeLockDelta < minTimeLockDelta {
return nil, fmt.Errorf("timelockdelta must be at least %v",
minTimeLockDelta)
}
if !cfg.NeutrinoMode.Active { if !cfg.NeutrinoMode.Active {
// If needed, we'll attempt to automatically configure // If needed, we'll attempt to automatically configure
// the RPC control plan for the target btcd node. // the RPC control plan for the target btcd node.

View File

@ -42,13 +42,13 @@ type networkMsg struct {
err chan error err chan error
} }
// feeUpdateRequest is a request that is sent to the server when a caller // chanPolicyUpdateRequest is a request that is sent to the server when a caller
// wishes to update the fees for a particular set of channels. New UpdateFee // wishes to update the channel policy (fees e.g.) for a particular set of
// messages will be crafted to be sent out during the next broadcast epoch and // channels. New ChannelUpdate messages will be crafted to be sent out during
// the fee updates committed to the lower layer. // the next broadcast epoch and the fee updates committed to the lower layer.
type feeUpdateRequest struct { type chanPolicyUpdateRequest struct {
targetChans []wire.OutPoint targetChans []wire.OutPoint
newSchema routing.FeeSchema newSchema routing.ChannelPolicy
errResp chan error errResp chan error
} }
@ -175,9 +175,9 @@ type AuthenticatedGossiper struct {
// networkHandler. // networkHandler.
networkMsgs chan *networkMsg networkMsgs chan *networkMsg
// feeUpdates is a channel that requests to update the fee schedule of // chanPolicyUpdates is a channel that requests to update the forwarding
// a set of channels is sent over. // policy of a set of channels is sent over.
feeUpdates chan *feeUpdateRequest chanPolicyUpdates chan *chanPolicyUpdateRequest
// bestHeight is the height of the block at the tip of the main chain // bestHeight is the height of the block at the tip of the main chain
// as we know it. // as we know it.
@ -202,7 +202,7 @@ func New(cfg Config, selfKey *btcec.PublicKey) (*AuthenticatedGossiper, error) {
cfg: &cfg, cfg: &cfg,
networkMsgs: make(chan *networkMsg), networkMsgs: make(chan *networkMsg),
quit: make(chan struct{}), quit: make(chan struct{}),
feeUpdates: make(chan *feeUpdateRequest), chanPolicyUpdates: make(chan *chanPolicyUpdateRequest),
prematureAnnouncements: make(map[uint32][]*networkMsg), prematureAnnouncements: make(map[uint32][]*networkMsg),
prematureChannelUpdates: make(map[uint64][]*networkMsg), prematureChannelUpdates: make(map[uint64][]*networkMsg),
waitingProofs: storage, waitingProofs: storage,
@ -296,24 +296,24 @@ func (d *AuthenticatedGossiper) SynchronizeNode(pub *btcec.PublicKey) error {
return d.cfg.SendToPeer(pub, announceMessages...) return d.cfg.SendToPeer(pub, announceMessages...)
} }
// PropagateFeeUpdate signals the AuthenticatedGossiper to update the fee // PropagateChanPolicyUpdate signals the AuthenticatedGossiper to update the
// schema for the specified channels. If no channels are specified, then the // channel forwarding policies for the specified channels. If no channels are
// fee update will be applied to all outgoing channels from the source node. // specified, then the update will be applied to all outgoing channels from the
// Fee updates are done in two stages: first, the AuthenticatedGossiper ensures // source node. Policy updates are done in two stages: first, the
// the updated has been committed by dependant sub-systems, then it signs and // AuthenticatedGossiper ensures the update has been committed by dependant
// broadcasts new updates to the network. // sub-systems, then it signs and broadcasts new updates to the network.
func (d *AuthenticatedGossiper) PropagateFeeUpdate(newSchema routing.FeeSchema, func (d *AuthenticatedGossiper) PropagateChanPolicyUpdate(
chanPoints ...wire.OutPoint) error { newSchema routing.ChannelPolicy, chanPoints ...wire.OutPoint) error {
errChan := make(chan error, 1) errChan := make(chan error, 1)
feeUpdate := &feeUpdateRequest{ policyUpdate := &chanPolicyUpdateRequest{
targetChans: chanPoints, targetChans: chanPoints,
newSchema: newSchema, newSchema: newSchema,
errResp: errChan, errResp: errChan,
} }
select { select {
case d.feeUpdates <- feeUpdate: case d.chanPolicyUpdates <- policyUpdate:
return <-errChan return <-errChan
case <-d.quit: case <-d.quit:
return fmt.Errorf("AuthenticatedGossiper shutting down") return fmt.Errorf("AuthenticatedGossiper shutting down")
@ -823,17 +823,18 @@ func (d *AuthenticatedGossiper) networkHandler() {
for { for {
select { select {
// A new fee update has arrived. We'll commit it to the // A new policy update has arrived. We'll commit it to the
// sub-systems below us, then craft, sign, and broadcast a new // sub-systems below us, then craft, sign, and broadcast a new
// ChannelUpdate for the set of affected clients. // ChannelUpdate for the set of affected clients.
case feeUpdate := <-d.feeUpdates: case policyUpdate := <-d.chanPolicyUpdates:
// First, we'll now create new fully signed updates for // First, we'll now create new fully signed updates for
// the affected channels and also update the underlying // the affected channels and also update the underlying
// graph with the new state. // graph with the new state.
newChanUpdates, err := d.processFeeChanUpdate(feeUpdate) newChanUpdates, err := d.processChanPolicyUpdate(policyUpdate)
if err != nil { if err != nil {
log.Errorf("Unable to craft fee updates: %v", err) log.Errorf("Unable to craft policy updates: %v",
feeUpdate.errResp <- err err)
policyUpdate.errResp <- err
continue continue
} }
@ -842,7 +843,7 @@ func (d *AuthenticatedGossiper) networkHandler() {
// start of the next epoch. // start of the next epoch.
announcements.AddMsgs(newChanUpdates...) announcements.AddMsgs(newChanUpdates...)
feeUpdate.errResp <- nil policyUpdate.errResp <- nil
case announcement := <-d.networkMsgs: case announcement := <-d.networkMsgs:
// Channel annoucnement signatures are the only message // Channel annoucnement signatures are the only message
@ -1072,18 +1073,19 @@ func (d *AuthenticatedGossiper) retransmitStaleChannels() error {
return nil return nil
} }
// processFeeChanUpdate generates a new set of channel updates with the new fee // processChanPolicyUpdate generates a new set of channel updates with the new
// schema applied for each specified channel identified by its channel point. // channel policy applied for each specified channel identified by its channel
// In the case that no channel points are specified, then the fee update will // point. In the case that no channel points are specified, then the update will
// be applied to all channels. Finally, the backing ChannelGraphSource is // be applied to all channels. Finally, the backing ChannelGraphSource is
// updated with the latest information reflecting the applied fee updates. // updated with the latest information reflecting the applied updates.
// //
// TODO(roasbeef): generalize into generic for any channel update // TODO(roasbeef): generalize into generic for any channel update
func (d *AuthenticatedGossiper) processFeeChanUpdate(feeUpdate *feeUpdateRequest) ([]networkMsg, error) { func (d *AuthenticatedGossiper) processChanPolicyUpdate(
policyUpdate *chanPolicyUpdateRequest) ([]networkMsg, error) {
// First, we'll construct a set of all the channels that need to be // First, we'll construct a set of all the channels that need to be
// updated. // updated.
chansToUpdate := make(map[wire.OutPoint]struct{}) chansToUpdate := make(map[wire.OutPoint]struct{})
for _, chanPoint := range feeUpdate.targetChans { for _, chanPoint := range policyUpdate.targetChans {
chansToUpdate[chanPoint] = struct{}{} chansToUpdate[chanPoint] = struct{}{}
} }
@ -1104,11 +1106,14 @@ func (d *AuthenticatedGossiper) processFeeChanUpdate(feeUpdate *feeUpdateRequest
} }
// Apply the new fee schema to the edge. // Apply the new fee schema to the edge.
edge.FeeBaseMSat = feeUpdate.newSchema.BaseFee edge.FeeBaseMSat = policyUpdate.newSchema.BaseFee
edge.FeeProportionalMillionths = lnwire.MilliSatoshi( edge.FeeProportionalMillionths = lnwire.MilliSatoshi(
feeUpdate.newSchema.FeeRate, policyUpdate.newSchema.FeeRate,
) )
// Apply the new TimeLockDelta.
edge.TimeLockDelta = uint16(policyUpdate.newSchema.TimeLockDelta)
// Re-sign and update the backing ChannelGraphSource, and // Re-sign and update the backing ChannelGraphSource, and
// retrieve our ChannelUpdate to broadcast. // retrieve our ChannelUpdate to broadcast.
_, chanUpdate, err := d.updateChannel(info, edge) _, chanUpdate, err := d.updateChannel(info, edge)
@ -1981,12 +1986,18 @@ func (d *AuthenticatedGossiper) sendAnnSigReliably(
func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo, func (d *AuthenticatedGossiper) updateChannel(info *channeldb.ChannelEdgeInfo,
edge *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, *lnwire.ChannelUpdate, error) { edge *channeldb.ChannelEdgePolicy) (*lnwire.ChannelAnnouncement, *lnwire.ChannelUpdate, error) {
edge.LastUpdate = time.Now() // Make sure timestamp is always increased, such that our update
// gets propagated.
timestamp := time.Now().Unix()
if timestamp <= edge.LastUpdate.Unix() {
timestamp = edge.LastUpdate.Unix() + 1
}
edge.LastUpdate = time.Unix(timestamp, 0)
chanUpdate := &lnwire.ChannelUpdate{ chanUpdate := &lnwire.ChannelUpdate{
Signature: edge.Signature, Signature: edge.Signature,
ChainHash: info.ChainHash, ChainHash: info.ChainHash,
ShortChannelID: lnwire.NewShortChanIDFromInt(edge.ChannelID), ShortChannelID: lnwire.NewShortChanIDFromInt(edge.ChannelID),
Timestamp: uint32(edge.LastUpdate.Unix()), Timestamp: uint32(timestamp),
Flags: edge.Flags, Flags: edge.Flags,
TimeLockDelta: edge.TimeLockDelta, TimeLockDelta: edge.TimeLockDelta,
HtlcMinimumMsat: edge.MinHTLC, HtlcMinimumMsat: edge.MinHTLC,

View File

@ -32,8 +32,6 @@ const (
// TODO(roasbeef): tune // TODO(roasbeef): tune
msgBufferSize = 50 msgBufferSize = 50
defaultCsvDelay = 4
// maxFundingAmount is a soft-limit of the maximum channel size // maxFundingAmount is a soft-limit of the maximum channel size
// accepted within the Lightning Protocol Currently. This limit is // accepted within the Lightning Protocol Currently. This limit is
// currently defined in BOLT-0002, and serves as an initial // currently defined in BOLT-0002, and serves as an initial
@ -43,6 +41,13 @@ const (
// TODO(roasbeef): add command line param to modify // TODO(roasbeef): add command line param to modify
maxFundingAmount = btcutil.Amount(1 << 24) maxFundingAmount = btcutil.Amount(1 << 24)
// minRemoteDelay and maxRemoteDelay is the extremes of the CSV delay
// we will require the remote to use for its commitment transaction.
// The actual delay we will require will be somewhere between these
// values, depending on channel size.
minRemoteDelay = 144
maxRemoteDelay = 2016
// maxWaitNumBlocksFundingConf is the maximum number of blocks to wait // maxWaitNumBlocksFundingConf is the maximum number of blocks to wait
// for the funding transaction to be confirmed before forgetting about // for the funding transaction to be confirmed before forgetting about
// the channel. 288 blocks is ~48 hrs // the channel. 288 blocks is ~48 hrs
@ -2101,6 +2106,8 @@ func (f *fundingManager) newChanAnnouncement(localPubKey, remotePubKey *btcec.Pu
chanFlags = 1 chanFlags = 1
} }
// We announce the channel with the default values. Some of
// these values can later be changed by crafting a new ChannelUpdate.
chanUpdateAnn := &lnwire.ChannelUpdate{ chanUpdateAnn := &lnwire.ChannelUpdate{
ShortChannelID: shortChanID, ShortChannelID: shortChanID,
ChainHash: chainHash, ChainHash: chainHash,
@ -2252,6 +2259,7 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
remoteAmt = msg.remoteFundingAmt remoteAmt = msg.remoteFundingAmt
capacity = localAmt + remoteAmt capacity = localAmt + remoteAmt
ourDustLimit = lnwallet.DefaultDustLimit() ourDustLimit = lnwallet.DefaultDustLimit()
minHtlc = msg.minHtlc
) )
fndgLog.Infof("Initiating fundingRequest(localAmt=%v, remoteAmt=%v, "+ fndgLog.Infof("Initiating fundingRequest(localAmt=%v, remoteAmt=%v, "+
@ -2321,9 +2329,14 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
// delay we require given the total amount of funds within the channel. // delay we require given the total amount of funds within the channel.
remoteCsvDelay := f.cfg.RequiredRemoteDelay(capacity) remoteCsvDelay := f.cfg.RequiredRemoteDelay(capacity)
// If no minimum HTLC value was specified, use the default one.
if minHtlc == 0 {
minHtlc = f.cfg.DefaultRoutingPolicy.MinHTLC
}
// Once the reservation has been created, and indexed, queue a funding // Once the reservation has been created, and indexed, queue a funding
// request to the remote peer, kicking off the funding workflow. // request to the remote peer, kicking off the funding workflow.
reservation.RegisterMinHTLC(f.cfg.DefaultRoutingPolicy.MinHTLC) reservation.RegisterMinHTLC(minHtlc)
ourContribution := reservation.OurContribution() ourContribution := reservation.OurContribution()
// Finally, we'll use the current value of the channels and our default // Finally, we'll use the current value of the channels and our default

View File

@ -264,8 +264,7 @@ func createTestFundingManager(t *testing.T, privKey *btcec.PrivateKey,
}, },
NumRequiredConfs: func(chanAmt btcutil.Amount, NumRequiredConfs: func(chanAmt btcutil.Amount,
pushAmt lnwire.MilliSatoshi) uint16 { pushAmt lnwire.MilliSatoshi) uint16 {
return 3
return uint16(cfg.DefaultNumChanConfs)
}, },
RequiredRemoteDelay: func(amt btcutil.Amount) uint16 { RequiredRemoteDelay: func(amt btcutil.Amount) uint16 {
return 4 return 4

View File

@ -39,7 +39,9 @@ const (
// the error possibly carrying along a ChannelUpdate message that includes the // the error possibly carrying along a ChannelUpdate message that includes the
// latest policy. // latest policy.
type ForwardingPolicy struct { type ForwardingPolicy struct {
// MinHTLC is the smallest HTLC that is to be forwarded. // MinHTLC is the smallest HTLC that is to be forwarded. This is
// set when a channel is first opened, and will be static for the
// lifetime of the channel.
MinHTLC lnwire.MilliSatoshi MinHTLC lnwire.MilliSatoshi
// BaseFee is the base fee, expressed in milli-satoshi that must be // BaseFee is the base fee, expressed in milli-satoshi that must be
@ -604,9 +606,6 @@ out:
// with a "null" field in the new policy, we'll // with a "null" field in the new policy, we'll
// only update to the set sub policy if the new // only update to the set sub policy if the new
// value isn't uninitialized. // value isn't uninitialized.
if req.policy.MinHTLC != 0 {
l.cfg.FwrdingPolicy.MinHTLC = req.policy.MinHTLC
}
if req.policy.BaseFee != 0 { if req.policy.BaseFee != 0 {
l.cfg.FwrdingPolicy.BaseFee = req.policy.BaseFee l.cfg.FwrdingPolicy.BaseFee = req.policy.BaseFee
} }

71
lnd.go
View File

@ -298,16 +298,71 @@ func lndMain() error {
return nil, fmt.Errorf("unable to find channel") return nil, fmt.Errorf("unable to find channel")
}, },
DefaultRoutingPolicy: activeChainControl.routingPolicy, DefaultRoutingPolicy: activeChainControl.routingPolicy,
NumRequiredConfs: func(chanAmt btcutil.Amount, pushAmt lnwire.MilliSatoshi) uint16 { NumRequiredConfs: func(chanAmt btcutil.Amount,
// TODO(roasbeef): add configurable mapping pushAmt lnwire.MilliSatoshi) uint16 {
// * simple switch initially // For large channels we increase the number
// * assign coefficient, etc // of confirmations we require for the
return uint16(cfg.DefaultNumChanConfs) // channel to be considered open. As it is
// always the responder that gets to choose
// value, the pushAmt is value being pushed
// to us. This means we have more to lose
// in the case this gets re-orged out, and
// we will require more confirmations before
// we consider it open.
// TODO(halseth): Use Litecoin params in case
// of LTC channels.
// In case the user has explicitly specified
// a default value for the number of
// confirmations, we use it.
defaultConf := uint16(cfg.Bitcoin.DefaultNumChanConfs)
if defaultConf != 0 {
return defaultConf
}
// If not we return a value scaled linearly
// between 3 and 6, depending on channel size.
// TODO(halseth): Use 1 as minimum?
minConf := uint64(3)
maxConf := uint64(6)
maxChannelSize := uint64(
lnwire.NewMSatFromSatoshis(maxFundingAmount))
stake := lnwire.NewMSatFromSatoshis(chanAmt) + pushAmt
conf := maxConf * uint64(stake) / maxChannelSize
if conf < minConf {
conf = minConf
}
if conf > maxConf {
conf = maxConf
}
return uint16(conf)
}, },
RequiredRemoteDelay: func(chanAmt btcutil.Amount) uint16 { RequiredRemoteDelay: func(chanAmt btcutil.Amount) uint16 {
// TODO(roasbeef): add additional hooks for // We scale the remote CSV delay (the time the
// configuration // remote have to claim funds in case of a unilateral
return 4 // close) linearly from minRemoteDelay blocks
// for small channels, to maxRemoteDelay blocks
// for channels of size maxFundingAmount.
// TODO(halseth): Litecoin parameter for LTC.
// In case the user has explicitly specified
// a default value for the remote delay, we
// use it.
defaultDelay := uint16(cfg.Bitcoin.DefaultRemoteDelay)
if defaultDelay > 0 {
return defaultDelay
}
// If not we scale according to channel size.
delay := uint16(maxRemoteDelay *
chanAmt / maxFundingAmount)
if delay < minRemoteDelay {
delay = minRemoteDelay
}
if delay > maxRemoteDelay {
delay = maxRemoteDelay
}
return delay
}, },
}) })
if err != nil { if err != nil {

View File

@ -467,6 +467,290 @@ func testBasicChannelFunding(net *lntest.NetworkHarness, t *harnessTest) {
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false) closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
} }
// testUpdateChannelPolicy tests that policy updates made to a channel
// gets propagated to other nodes in the network.
func testUpdateChannelPolicy(net *lntest.NetworkHarness, t *harnessTest) {
timeout := time.Duration(time.Second * 5)
ctxb := context.Background()
// Launch notification clients for all nodes, such that we can
// get notified when they discover new channels and updates
// in the graph.
aliceUpdates, aQuit := subscribeGraphNotifications(t, ctxb, net.Alice)
defer close(aQuit)
bobUpdates, bQuit := subscribeGraphNotifications(t, ctxb, net.Bob)
defer close(bQuit)
chanAmt := maxFundingAmount
pushAmt := btcutil.Amount(100000)
// Create a channel Alice->Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, pushAmt)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
err := net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint)
if err != nil {
t.Fatalf("alice didn't report channel: %v", err)
}
err = net.Bob.WaitForNetworkChannelOpen(ctxt, chanPoint)
if err != nil {
t.Fatalf("bob didn't report channel: %v", err)
}
// Create Carol and a new channel Bob->Carol.
carol, err := net.NewNode(nil)
if err != nil {
t.Fatalf("unable to create new nodes: %v", err)
}
carolUpdates, cQuit := subscribeGraphNotifications(t, ctxb, carol)
defer close(cQuit)
if err := net.ConnectNodes(ctxb, carol, net.Bob); err != nil {
t.Fatalf("unable to connect dave to alice: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPoint2 := openChannelAndAssert(ctxt, t, net, net.Bob, carol,
chanAmt, pushAmt)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
err = net.Bob.WaitForNetworkChannelOpen(ctxt, chanPoint2)
if err != nil {
t.Fatalf("bob didn't report channel: %v", err)
}
err = carol.WaitForNetworkChannelOpen(ctxt, chanPoint2)
if err != nil {
t.Fatalf("carol didn't report channel: %v", err)
}
// Update the fees for the channel Alice->Bob, and make sure
// all nodes learn about it.
const feeBase = 1000000
baseFee := int64(1500)
feeRate := int64(12)
timeLockDelta := uint32(66)
req := &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee,
FeeRate: float64(feeRate),
TimeLockDelta: timeLockDelta,
}
req.Scope = &lnrpc.PolicyUpdateRequest_ChanPoint{
ChanPoint: chanPoint,
}
_, err = net.Alice.UpdateChannelPolicy(ctxb, req)
if err != nil {
t.Fatalf("unable to get alice's balance: %v", err)
}
// txStr returns the string representation of the channel's
// funding tx.
txStr := func(chanPoint *lnrpc.ChannelPoint) string {
fundingTxID, err := chainhash.NewHash(chanPoint.FundingTxid)
if err != nil {
return ""
}
cp := wire.OutPoint{
Hash: *fundingTxID,
Index: chanPoint.OutputIndex,
}
return cp.String()
}
// A closure that is used to wait for a channel updates that matches
// the channel policy update done by Alice.
waitForChannelUpdate := func(graphUpdates chan *lnrpc.GraphTopologyUpdate,
chanPoints ...*lnrpc.ChannelPoint) {
// Create a map containing all the channel points we are
// waiting for updates for.
cps := make(map[string]bool)
for _, chanPoint := range chanPoints {
cps[txStr(chanPoint)] = true
}
Loop:
for {
select {
case graphUpdate := <-graphUpdates:
if len(graphUpdate.ChannelUpdates) == 0 {
continue
}
chanUpdate := graphUpdate.ChannelUpdates[0]
fundingTxStr := txStr(chanUpdate.ChanPoint)
if _, ok := cps[fundingTxStr]; !ok {
continue
}
if chanUpdate.AdvertisingNode != net.Alice.PubKeyStr {
continue
}
policy := chanUpdate.RoutingPolicy
if policy.FeeBaseMsat != baseFee {
continue
}
if policy.FeeRateMilliMsat != feeRate*feeBase {
continue
}
if policy.TimeLockDelta != timeLockDelta {
continue
}
// We got a policy update that matched the
// values and channel point of what we
// expected, delete it from the map.
delete(cps, fundingTxStr)
// If we have no more channel points we are
// waiting for, break out of the loop.
if len(cps) == 0 {
break Loop
}
case <-time.After(20 * time.Second):
t.Fatalf("did not receive channel update")
}
}
}
// Wait for all nodes to have seen the policy update done by Alice.
waitForChannelUpdate(aliceUpdates, chanPoint)
waitForChannelUpdate(bobUpdates, chanPoint)
waitForChannelUpdate(carolUpdates, chanPoint)
// assertChannelPolicy asserts that the passed node's known channel
// policy for the passed chanPoint is consistent with Alice's current
// expected policy values.
assertChannelPolicy := func(node *lntest.HarnessNode,
chanPoint *lnrpc.ChannelPoint) {
// Get a DescribeGraph from the node.
descReq := &lnrpc.ChannelGraphRequest{}
chanGraph, err := node.DescribeGraph(ctxb, descReq)
if err != nil {
t.Fatalf("unable to query for alice's routing table: %v",
err)
}
edgeFound := false
for _, e := range chanGraph.Edges {
if e.ChanPoint == txStr(chanPoint) {
edgeFound = true
if e.Node1Pub == net.Alice.PubKeyStr {
if e.Node1Policy.FeeBaseMsat != baseFee {
t.Fatalf("expected base fee "+
"%v, got %v", baseFee,
e.Node1Policy.FeeBaseMsat)
}
if e.Node1Policy.FeeRateMilliMsat != feeRate*feeBase {
t.Fatalf("expected fee rate "+
"%v, got %v", feeRate*feeBase,
e.Node1Policy.FeeRateMilliMsat)
}
if e.Node1Policy.TimeLockDelta != timeLockDelta {
t.Fatalf("expected time lock "+
"delta %v, got %v",
timeLockDelta,
e.Node1Policy.TimeLockDelta)
}
} else {
if e.Node2Policy.FeeBaseMsat != baseFee {
t.Fatalf("expected base fee "+
"%v, got %v", baseFee,
e.Node2Policy.FeeBaseMsat)
}
if e.Node2Policy.FeeRateMilliMsat != feeRate*feeBase {
t.Fatalf("expected fee rate "+
"%v, got %v", feeRate*feeBase,
e.Node2Policy.FeeRateMilliMsat)
}
if e.Node2Policy.TimeLockDelta != timeLockDelta {
t.Fatalf("expected time lock "+
"delta %v, got %v",
timeLockDelta,
e.Node2Policy.TimeLockDelta)
}
}
}
}
if !edgeFound {
t.Fatalf("did not find edge")
}
}
// Check that all nodes now know about Alice's updated policy.
assertChannelPolicy(net.Alice, chanPoint)
assertChannelPolicy(net.Bob, chanPoint)
assertChannelPolicy(carol, chanPoint)
// Open channel to Carol.
if err := net.ConnectNodes(ctxb, net.Alice, carol); err != nil {
t.Fatalf("unable to connect dave to alice: %v", err)
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
chanPoint3 := openChannelAndAssert(ctxt, t, net, net.Alice, carol,
chanAmt, pushAmt)
ctxt, _ = context.WithTimeout(ctxb, time.Second*15)
err = net.Alice.WaitForNetworkChannelOpen(ctxt, chanPoint3)
if err != nil {
t.Fatalf("alice didn't report channel: %v", err)
}
err = carol.WaitForNetworkChannelOpen(ctxt, chanPoint3)
if err != nil {
t.Fatalf("bob didn't report channel: %v", err)
}
// Make a global update, and check that both channels'
// new policies get propagated.
baseFee = int64(800)
feeRate = int64(123)
timeLockDelta = uint32(22)
req = &lnrpc.PolicyUpdateRequest{
BaseFeeMsat: baseFee,
FeeRate: float64(feeRate),
TimeLockDelta: timeLockDelta,
}
req.Scope = &lnrpc.PolicyUpdateRequest_Global{}
_, err = net.Alice.UpdateChannelPolicy(ctxb, req)
if err != nil {
t.Fatalf("unable to get alice's balance: %v", err)
}
// Wait for all nodes to have seen the policy updates
// for both of Alice's channels.
waitForChannelUpdate(aliceUpdates, chanPoint, chanPoint3)
waitForChannelUpdate(bobUpdates, chanPoint, chanPoint3)
waitForChannelUpdate(carolUpdates, chanPoint, chanPoint3)
// And finally check that all nodes remembers the policy
// update they received.
assertChannelPolicy(net.Alice, chanPoint)
assertChannelPolicy(net.Bob, chanPoint)
assertChannelPolicy(carol, chanPoint)
assertChannelPolicy(net.Alice, chanPoint3)
assertChannelPolicy(net.Bob, chanPoint3)
assertChannelPolicy(carol, chanPoint3)
// Close the channels.
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint, false)
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Bob, chanPoint2, false)
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPoint3, false)
ctxt, _ = context.WithTimeout(ctxb, timeout)
// Clean up carol's node.
if err := net.ShutdownNode(carol); err != nil {
t.Fatalf("unable to shutdown carol: %v", err)
}
}
// testOpenChannelAfterReorg tests that in the case where we have an open // testOpenChannelAfterReorg tests that in the case where we have an open
// channel where the funding tx gets reorged out, the channel will no // channel where the funding tx gets reorged out, the channel will no
// longer be present in the node's routing table. // longer be present in the node's routing table.
@ -793,7 +1077,7 @@ func testChannelFundingPersistence(net *lntest.NetworkHarness, t *harnessTest) {
// confirmation before it's open, with the current set of defaults, // confirmation before it's open, with the current set of defaults,
// we'll need to create a new node instance. // we'll need to create a new node instance.
const numConfs = 5 const numConfs = 5
carolArgs := []string{fmt.Sprintf("--defaultchanconfs=%v", numConfs)} carolArgs := []string{fmt.Sprintf("--bitcoin.defaultchanconfs=%v", numConfs)}
carol, err := net.NewNode(carolArgs) carol, err := net.NewNode(carolArgs)
if err != nil { if err != nil {
t.Fatalf("unable to create new node: %v", err) t.Fatalf("unable to create new node: %v", err)
@ -1095,7 +1379,7 @@ func testChannelForceClosure(net *lntest.NetworkHarness, t *harnessTest) {
// TODO(roasbeef): should check default value in config here // TODO(roasbeef): should check default value in config here
// instead, or make delay a param // instead, or make delay a param
defaultCSV := uint32(4) defaultCSV := uint32(4)
defaultCLTV := defaultBitcoinForwardingPolicy.TimeLockDelta defaultCLTV := uint32(defaultBitcoinTimeLockDelta)
// Since we'd like to test failure scenarios with outstanding htlcs, // Since we'd like to test failure scenarios with outstanding htlcs,
// we'll introduce another node into our test network: Carol. // we'll introduce another node into our test network: Carol.
@ -3878,28 +4162,22 @@ out:
} }
} }
func testGraphTopologyNotifications(net *lntest.NetworkHarness, t *harnessTest) { // subscribeGraphNotifications subscribes to channel graph updates and launches
const chanAmt = maxFundingAmount // a goroutine that forwards these to the returned channel.
timeout := time.Duration(time.Second * 5) func subscribeGraphNotifications(t *harnessTest, ctxb context.Context,
ctxb := context.Background() node *lntest.HarnessNode) (chan *lnrpc.GraphTopologyUpdate, chan struct{}) {
// We'll first start by establishing a notification client which will
// We'll first start by establishing a notification client to Alice which // send us notifications upon detected changes in the channel graph.
// will send us notifications upon detected changes in the channel graph.
req := &lnrpc.GraphTopologySubscription{} req := &lnrpc.GraphTopologySubscription{}
topologyClient, err := net.Alice.SubscribeChannelGraph(ctxb, req) topologyClient, err := node.SubscribeChannelGraph(ctxb, req)
if err != nil { if err != nil {
t.Fatalf("unable to create topology client: %v", err) t.Fatalf("unable to create topology client: %v", err)
} }
// Open a new channel between Alice and Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, 0)
// We'll launch a goroutine that'll be responsible for proxying all // We'll launch a goroutine that'll be responsible for proxying all
// notifications recv'd from the client into the channel below. // notifications recv'd from the client into the channel below.
quit := make(chan struct{}) quit := make(chan struct{})
graphUpdates := make(chan *lnrpc.GraphTopologyUpdate, 4) graphUpdates := make(chan *lnrpc.GraphTopologyUpdate, 20)
go func() { go func() {
for { for {
select { select {
@ -3916,7 +4194,8 @@ func testGraphTopologyNotifications(net *lntest.NetworkHarness, t *harnessTest)
if err == io.EOF { if err == io.EOF {
return return
} else if err != nil { } else if err != nil {
t.Fatalf("unable to recv graph update: %v", err) t.Fatalf("unable to recv graph update: %v",
err)
} }
select { select {
@ -3927,6 +4206,21 @@ func testGraphTopologyNotifications(net *lntest.NetworkHarness, t *harnessTest)
} }
} }
}() }()
return graphUpdates, quit
}
func testGraphTopologyNotifications(net *lntest.NetworkHarness, t *harnessTest) {
const chanAmt = maxFundingAmount
timeout := time.Duration(time.Second * 5)
ctxb := context.Background()
// Let Alice subscribe to graph notifications.
graphUpdates, quit := subscribeGraphNotifications(t, ctxb, net.Alice)
// Open a new channel between Alice and Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
chanAmt, 0)
// The channel opening above should've triggered a few notifications // The channel opening above should've triggered a few notifications
// sent to the notification client. We'll expect two channel updates, // sent to the notification client. We'll expect two channel updates,
@ -4680,6 +4974,10 @@ var testsCases = []*testCase{
name: "basic funding flow", name: "basic funding flow",
test: testBasicChannelFunding, test: testBasicChannelFunding,
}, },
{
name: "update channel policy",
test: testUpdateChannelPolicy,
},
{ {
name: "open channel reorg test", name: "open channel reorg test",
test: testOpenChannelAfterReorg, test: testOpenChannelAfterReorg,

View File

@ -98,8 +98,8 @@ It has these top-level messages:
FeeReportRequest FeeReportRequest
ChannelFeeReport ChannelFeeReport
FeeReportResponse FeeReportResponse
FeeUpdateRequest PolicyUpdateRequest
FeeUpdateResponse PolicyUpdateResponse
*/ */
package lnrpc package lnrpc
@ -1555,6 +1555,8 @@ type OpenChannelRequest struct {
SatPerByte int64 `protobuf:"varint,7,opt,name=sat_per_byte,json=satPerByte" json:"sat_per_byte,omitempty"` SatPerByte int64 `protobuf:"varint,7,opt,name=sat_per_byte,json=satPerByte" json:"sat_per_byte,omitempty"`
// / Whether this channel should be private, not announced to the greater network. // / Whether this channel should be private, not announced to the greater network.
Private bool `protobuf:"varint,8,opt,name=private" json:"private,omitempty"` Private bool `protobuf:"varint,8,opt,name=private" json:"private,omitempty"`
// / The minimum value in millisatoshi we will require for incoming HTLCs on the channel.
MinHtlcMsat int64 `protobuf:"varint,9,opt,name=min_htlc_msat" json:"min_htlc_msat,omitempty"`
} }
func (m *OpenChannelRequest) Reset() { *m = OpenChannelRequest{} } func (m *OpenChannelRequest) Reset() { *m = OpenChannelRequest{} }
@ -1618,6 +1620,13 @@ func (m *OpenChannelRequest) GetPrivate() bool {
return false return false
} }
func (m *OpenChannelRequest) GetMinHtlcMsat() int64 {
if m != nil {
return m.MinHtlcMsat
}
return 0
}
type OpenStatusUpdate struct { type OpenStatusUpdate struct {
// Types that are valid to be assigned to Update: // Types that are valid to be assigned to Update:
// *OpenStatusUpdate_ChanPending // *OpenStatusUpdate_ChanPending
@ -3477,111 +3486,120 @@ func (m *FeeReportResponse) GetChannelFees() []*ChannelFeeReport {
return nil return nil
} }
type FeeUpdateRequest struct { type PolicyUpdateRequest struct {
// Types that are valid to be assigned to Scope: // Types that are valid to be assigned to Scope:
// *FeeUpdateRequest_Global // *PolicyUpdateRequest_Global
// *FeeUpdateRequest_ChanPoint // *PolicyUpdateRequest_ChanPoint
Scope isFeeUpdateRequest_Scope `protobuf_oneof:"scope"` Scope isPolicyUpdateRequest_Scope `protobuf_oneof:"scope"`
// / The base fee charged regardless of the number of milli-satoshis sent. // / The base fee charged regardless of the number of milli-satoshis sent.
BaseFeeMsat int64 `protobuf:"varint,3,opt,name=base_fee_msat" json:"base_fee_msat,omitempty"` BaseFeeMsat int64 `protobuf:"varint,3,opt,name=base_fee_msat" json:"base_fee_msat,omitempty"`
// / The effective fee rate in milli-satoshis. The precision of this value goes up to 6 decimal places, so 1e-6. // / The effective fee rate in milli-satoshis. The precision of this value goes up to 6 decimal places, so 1e-6.
FeeRate float64 `protobuf:"fixed64,4,opt,name=fee_rate" json:"fee_rate,omitempty"` FeeRate float64 `protobuf:"fixed64,4,opt,name=fee_rate" json:"fee_rate,omitempty"`
// / The required timelock delta for HTLCs forwarded over the channel.
TimeLockDelta uint32 `protobuf:"varint,5,opt,name=time_lock_delta" json:"time_lock_delta,omitempty"`
} }
func (m *FeeUpdateRequest) Reset() { *m = FeeUpdateRequest{} } func (m *PolicyUpdateRequest) Reset() { *m = PolicyUpdateRequest{} }
func (m *FeeUpdateRequest) String() string { return proto.CompactTextString(m) } func (m *PolicyUpdateRequest) String() string { return proto.CompactTextString(m) }
func (*FeeUpdateRequest) ProtoMessage() {} func (*PolicyUpdateRequest) ProtoMessage() {}
func (*FeeUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{90} } func (*PolicyUpdateRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{90} }
type isFeeUpdateRequest_Scope interface { type isPolicyUpdateRequest_Scope interface {
isFeeUpdateRequest_Scope() isPolicyUpdateRequest_Scope()
} }
type FeeUpdateRequest_Global struct { type PolicyUpdateRequest_Global struct {
Global bool `protobuf:"varint,1,opt,name=global,oneof"` Global bool `protobuf:"varint,1,opt,name=global,oneof"`
} }
type FeeUpdateRequest_ChanPoint struct { type PolicyUpdateRequest_ChanPoint struct {
ChanPoint *ChannelPoint `protobuf:"bytes,2,opt,name=chan_point,oneof"` ChanPoint *ChannelPoint `protobuf:"bytes,2,opt,name=chan_point,oneof"`
} }
func (*FeeUpdateRequest_Global) isFeeUpdateRequest_Scope() {} func (*PolicyUpdateRequest_Global) isPolicyUpdateRequest_Scope() {}
func (*FeeUpdateRequest_ChanPoint) isFeeUpdateRequest_Scope() {} func (*PolicyUpdateRequest_ChanPoint) isPolicyUpdateRequest_Scope() {}
func (m *FeeUpdateRequest) GetScope() isFeeUpdateRequest_Scope { func (m *PolicyUpdateRequest) GetScope() isPolicyUpdateRequest_Scope {
if m != nil { if m != nil {
return m.Scope return m.Scope
} }
return nil return nil
} }
func (m *FeeUpdateRequest) GetGlobal() bool { func (m *PolicyUpdateRequest) GetGlobal() bool {
if x, ok := m.GetScope().(*FeeUpdateRequest_Global); ok { if x, ok := m.GetScope().(*PolicyUpdateRequest_Global); ok {
return x.Global return x.Global
} }
return false return false
} }
func (m *FeeUpdateRequest) GetChanPoint() *ChannelPoint { func (m *PolicyUpdateRequest) GetChanPoint() *ChannelPoint {
if x, ok := m.GetScope().(*FeeUpdateRequest_ChanPoint); ok { if x, ok := m.GetScope().(*PolicyUpdateRequest_ChanPoint); ok {
return x.ChanPoint return x.ChanPoint
} }
return nil return nil
} }
func (m *FeeUpdateRequest) GetBaseFeeMsat() int64 { func (m *PolicyUpdateRequest) GetBaseFeeMsat() int64 {
if m != nil { if m != nil {
return m.BaseFeeMsat return m.BaseFeeMsat
} }
return 0 return 0
} }
func (m *FeeUpdateRequest) GetFeeRate() float64 { func (m *PolicyUpdateRequest) GetFeeRate() float64 {
if m != nil { if m != nil {
return m.FeeRate return m.FeeRate
} }
return 0 return 0
} }
func (m *PolicyUpdateRequest) GetTimeLockDelta() uint32 {
if m != nil {
return m.TimeLockDelta
}
return 0
}
// XXX_OneofFuncs is for the internal use of the proto package. // XXX_OneofFuncs is for the internal use of the proto package.
func (*FeeUpdateRequest) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { func (*PolicyUpdateRequest) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) {
return _FeeUpdateRequest_OneofMarshaler, _FeeUpdateRequest_OneofUnmarshaler, _FeeUpdateRequest_OneofSizer, []interface{}{ return _PolicyUpdateRequest_OneofMarshaler, _PolicyUpdateRequest_OneofUnmarshaler, _PolicyUpdateRequest_OneofSizer, []interface{}{
(*FeeUpdateRequest_Global)(nil), (*PolicyUpdateRequest_Global)(nil),
(*FeeUpdateRequest_ChanPoint)(nil), (*PolicyUpdateRequest_ChanPoint)(nil),
} }
} }
func _FeeUpdateRequest_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { func _PolicyUpdateRequest_OneofMarshaler(msg proto.Message, b *proto.Buffer) error {
m := msg.(*FeeUpdateRequest) m := msg.(*PolicyUpdateRequest)
// scope // scope
switch x := m.Scope.(type) { switch x := m.Scope.(type) {
case *FeeUpdateRequest_Global: case *PolicyUpdateRequest_Global:
t := uint64(0) t := uint64(0)
if x.Global { if x.Global {
t = 1 t = 1
} }
b.EncodeVarint(1<<3 | proto.WireVarint) b.EncodeVarint(1<<3 | proto.WireVarint)
b.EncodeVarint(t) b.EncodeVarint(t)
case *FeeUpdateRequest_ChanPoint: case *PolicyUpdateRequest_ChanPoint:
b.EncodeVarint(2<<3 | proto.WireBytes) b.EncodeVarint(2<<3 | proto.WireBytes)
if err := b.EncodeMessage(x.ChanPoint); err != nil { if err := b.EncodeMessage(x.ChanPoint); err != nil {
return err return err
} }
case nil: case nil:
default: default:
return fmt.Errorf("FeeUpdateRequest.Scope has unexpected type %T", x) return fmt.Errorf("PolicyUpdateRequest.Scope has unexpected type %T", x)
} }
return nil return nil
} }
func _FeeUpdateRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { func _PolicyUpdateRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) {
m := msg.(*FeeUpdateRequest) m := msg.(*PolicyUpdateRequest)
switch tag { switch tag {
case 1: // scope.global case 1: // scope.global
if wire != proto.WireVarint { if wire != proto.WireVarint {
return true, proto.ErrInternalBadWireType return true, proto.ErrInternalBadWireType
} }
x, err := b.DecodeVarint() x, err := b.DecodeVarint()
m.Scope = &FeeUpdateRequest_Global{x != 0} m.Scope = &PolicyUpdateRequest_Global{x != 0}
return true, err return true, err
case 2: // scope.chan_point case 2: // scope.chan_point
if wire != proto.WireBytes { if wire != proto.WireBytes {
@ -3589,21 +3607,21 @@ func _FeeUpdateRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *pro
} }
msg := new(ChannelPoint) msg := new(ChannelPoint)
err := b.DecodeMessage(msg) err := b.DecodeMessage(msg)
m.Scope = &FeeUpdateRequest_ChanPoint{msg} m.Scope = &PolicyUpdateRequest_ChanPoint{msg}
return true, err return true, err
default: default:
return false, nil return false, nil
} }
} }
func _FeeUpdateRequest_OneofSizer(msg proto.Message) (n int) { func _PolicyUpdateRequest_OneofSizer(msg proto.Message) (n int) {
m := msg.(*FeeUpdateRequest) m := msg.(*PolicyUpdateRequest)
// scope // scope
switch x := m.Scope.(type) { switch x := m.Scope.(type) {
case *FeeUpdateRequest_Global: case *PolicyUpdateRequest_Global:
n += proto.SizeVarint(1<<3 | proto.WireVarint) n += proto.SizeVarint(1<<3 | proto.WireVarint)
n += 1 n += 1
case *FeeUpdateRequest_ChanPoint: case *PolicyUpdateRequest_ChanPoint:
s := proto.Size(x.ChanPoint) s := proto.Size(x.ChanPoint)
n += proto.SizeVarint(2<<3 | proto.WireBytes) n += proto.SizeVarint(2<<3 | proto.WireBytes)
n += proto.SizeVarint(uint64(s)) n += proto.SizeVarint(uint64(s))
@ -3615,13 +3633,13 @@ func _FeeUpdateRequest_OneofSizer(msg proto.Message) (n int) {
return n return n
} }
type FeeUpdateResponse struct { type PolicyUpdateResponse struct {
} }
func (m *FeeUpdateResponse) Reset() { *m = FeeUpdateResponse{} } func (m *PolicyUpdateResponse) Reset() { *m = PolicyUpdateResponse{} }
func (m *FeeUpdateResponse) String() string { return proto.CompactTextString(m) } func (m *PolicyUpdateResponse) String() string { return proto.CompactTextString(m) }
func (*FeeUpdateResponse) ProtoMessage() {} func (*PolicyUpdateResponse) ProtoMessage() {}
func (*FeeUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{91} } func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{91} }
func init() { func init() {
proto.RegisterType((*CreateWalletRequest)(nil), "lnrpc.CreateWalletRequest") proto.RegisterType((*CreateWalletRequest)(nil), "lnrpc.CreateWalletRequest")
@ -3718,8 +3736,8 @@ func init() {
proto.RegisterType((*FeeReportRequest)(nil), "lnrpc.FeeReportRequest") proto.RegisterType((*FeeReportRequest)(nil), "lnrpc.FeeReportRequest")
proto.RegisterType((*ChannelFeeReport)(nil), "lnrpc.ChannelFeeReport") proto.RegisterType((*ChannelFeeReport)(nil), "lnrpc.ChannelFeeReport")
proto.RegisterType((*FeeReportResponse)(nil), "lnrpc.FeeReportResponse") proto.RegisterType((*FeeReportResponse)(nil), "lnrpc.FeeReportResponse")
proto.RegisterType((*FeeUpdateRequest)(nil), "lnrpc.FeeUpdateRequest") proto.RegisterType((*PolicyUpdateRequest)(nil), "lnrpc.PolicyUpdateRequest")
proto.RegisterType((*FeeUpdateResponse)(nil), "lnrpc.FeeUpdateResponse") proto.RegisterType((*PolicyUpdateResponse)(nil), "lnrpc.PolicyUpdateResponse")
proto.RegisterEnum("lnrpc.NewAddressRequest_AddressType", NewAddressRequest_AddressType_name, NewAddressRequest_AddressType_value) proto.RegisterEnum("lnrpc.NewAddressRequest_AddressType", NewAddressRequest_AddressType_name, NewAddressRequest_AddressType_value)
} }
@ -4033,10 +4051,10 @@ type LightningClient interface {
// FeeReport allows the caller to obtain a report detailing the current fee // FeeReport allows the caller to obtain a report detailing the current fee
// schedule enforced by the node globally for each channel. // schedule enforced by the node globally for each channel.
FeeReport(ctx context.Context, in *FeeReportRequest, opts ...grpc.CallOption) (*FeeReportResponse, error) FeeReport(ctx context.Context, in *FeeReportRequest, opts ...grpc.CallOption) (*FeeReportResponse, error)
// * lncli: `updatefees` // * lncli: `updatechanpolicy`
// UpdateFees allows the caller to update the fee schedule for all channels // UpdateChannelPolicy allows the caller to update the fee schedule and
// globally, or a particular channel. // channel policies for all channels globally, or a particular channel.
UpdateFees(ctx context.Context, in *FeeUpdateRequest, opts ...grpc.CallOption) (*FeeUpdateResponse, error) UpdateChannelPolicy(ctx context.Context, in *PolicyUpdateRequest, opts ...grpc.CallOption) (*PolicyUpdateResponse, error)
} }
type lightningClient struct { type lightningClient struct {
@ -4517,9 +4535,9 @@ func (c *lightningClient) FeeReport(ctx context.Context, in *FeeReportRequest, o
return out, nil return out, nil
} }
func (c *lightningClient) UpdateFees(ctx context.Context, in *FeeUpdateRequest, opts ...grpc.CallOption) (*FeeUpdateResponse, error) { func (c *lightningClient) UpdateChannelPolicy(ctx context.Context, in *PolicyUpdateRequest, opts ...grpc.CallOption) (*PolicyUpdateResponse, error) {
out := new(FeeUpdateResponse) out := new(PolicyUpdateResponse)
err := grpc.Invoke(ctx, "/lnrpc.Lightning/UpdateFees", in, out, c.cc, opts...) err := grpc.Invoke(ctx, "/lnrpc.Lightning/UpdateChannelPolicy", in, out, c.cc, opts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -4719,10 +4737,10 @@ type LightningServer interface {
// FeeReport allows the caller to obtain a report detailing the current fee // FeeReport allows the caller to obtain a report detailing the current fee
// schedule enforced by the node globally for each channel. // schedule enforced by the node globally for each channel.
FeeReport(context.Context, *FeeReportRequest) (*FeeReportResponse, error) FeeReport(context.Context, *FeeReportRequest) (*FeeReportResponse, error)
// * lncli: `updatefees` // * lncli: `updatechanpolicy`
// UpdateFees allows the caller to update the fee schedule for all channels // UpdateChannelPolicy allows the caller to update the fee schedule and
// globally, or a particular channel. // channel policies for all channels globally, or a particular channel.
UpdateFees(context.Context, *FeeUpdateRequest) (*FeeUpdateResponse, error) UpdateChannelPolicy(context.Context, *PolicyUpdateRequest) (*PolicyUpdateResponse, error)
} }
func RegisterLightningServer(s *grpc.Server, srv LightningServer) { func RegisterLightningServer(s *grpc.Server, srv LightningServer) {
@ -5418,20 +5436,20 @@ func _Lightning_FeeReport_Handler(srv interface{}, ctx context.Context, dec func
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Lightning_UpdateFees_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _Lightning_UpdateChannelPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(FeeUpdateRequest) in := new(PolicyUpdateRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
return nil, err return nil, err
} }
if interceptor == nil { if interceptor == nil {
return srv.(LightningServer).UpdateFees(ctx, in) return srv.(LightningServer).UpdateChannelPolicy(ctx, in)
} }
info := &grpc.UnaryServerInfo{ info := &grpc.UnaryServerInfo{
Server: srv, Server: srv,
FullMethod: "/lnrpc.Lightning/UpdateFees", FullMethod: "/lnrpc.Lightning/UpdateChannelPolicy",
} }
handler := func(ctx context.Context, req interface{}) (interface{}, error) { handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LightningServer).UpdateFees(ctx, req.(*FeeUpdateRequest)) return srv.(LightningServer).UpdateChannelPolicy(ctx, req.(*PolicyUpdateRequest))
} }
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
@ -5565,8 +5583,8 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
Handler: _Lightning_FeeReport_Handler, Handler: _Lightning_FeeReport_Handler,
}, },
{ {
MethodName: "UpdateFees", MethodName: "UpdateChannelPolicy",
Handler: _Lightning_UpdateFees_Handler, Handler: _Lightning_UpdateChannelPolicy_Handler,
}, },
}, },
Streams: []grpc.StreamDesc{ Streams: []grpc.StreamDesc{
@ -5608,309 +5626,310 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) } func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 4855 bytes of a gzipped FileDescriptorProto // 4877 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x7b, 0xcd, 0x8f, 0x1c, 0x49, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x3b, 0x4d, 0x8f, 0x1c, 0x49,
0x56, 0xb8, 0xb3, 0xba, 0xfa, 0xa3, 0x5e, 0x55, 0xf5, 0x47, 0x74, 0xbb, 0xbb, 0x5c, 0xf6, 0x78, 0x56, 0xce, 0xea, 0xea, 0x8f, 0x7a, 0x55, 0xd5, 0x1f, 0xd1, 0xed, 0xee, 0x72, 0xd9, 0xeb, 0xb5,
0xed, 0xd8, 0xd1, 0x4c, 0xff, 0xfc, 0x1b, 0xdc, 0x76, 0x2f, 0x3b, 0xcc, 0x8e, 0x81, 0x91, 0xbf, 0x63, 0x47, 0x33, 0x8d, 0x19, 0xdc, 0x76, 0x2f, 0x3b, 0xcc, 0x8e, 0x81, 0x91, 0xbf, 0x7b, 0xd8,
0x7b, 0xd8, 0x1e, 0x4f, 0x6f, 0xb6, 0x67, 0x06, 0x76, 0x85, 0x8a, 0xec, 0xaa, 0xe8, 0xea, 0x5c, 0x1e, 0x4f, 0x6f, 0xb6, 0x67, 0x06, 0x76, 0x85, 0x8a, 0xec, 0xaa, 0xe8, 0xea, 0x5c, 0x67, 0x65,
0x67, 0x65, 0xe6, 0x64, 0x46, 0x75, 0xbb, 0xd6, 0x58, 0x82, 0x05, 0x21, 0x21, 0x81, 0xf6, 0x00, 0xe6, 0x64, 0x46, 0x75, 0xbb, 0xd6, 0x58, 0x82, 0x05, 0x21, 0x21, 0x81, 0xf6, 0x00, 0x02, 0xed,
0x02, 0xed, 0x61, 0xb9, 0x70, 0x81, 0x03, 0xfc, 0x03, 0x48, 0xfc, 0x01, 0x2b, 0x21, 0x90, 0xf6, 0x61, 0xb9, 0x70, 0x81, 0x03, 0xbf, 0x00, 0x89, 0x1f, 0xb0, 0x12, 0x02, 0x69, 0x4f, 0x08, 0x2e,
0x84, 0xc4, 0x0d, 0x4e, 0xcb, 0x99, 0x3b, 0x7a, 0xf1, 0x95, 0x11, 0x99, 0xd9, 0xb6, 0x97, 0x5d, 0x08, 0x4e, 0x70, 0xe6, 0xc0, 0x0d, 0xbd, 0xf8, 0xca, 0x88, 0xcc, 0x6c, 0xdb, 0xcb, 0x2e, 0xdc,
0xb8, 0x55, 0xbc, 0x78, 0xf9, 0x22, 0xe2, 0xc5, 0x8b, 0xf7, 0x5d, 0xd0, 0xca, 0xd2, 0xe1, 0xcd, 0x2a, 0x5e, 0xbc, 0x7c, 0x11, 0xf1, 0xe2, 0xc5, 0xfb, 0x2e, 0x68, 0x65, 0xe9, 0xf0, 0x66, 0x9a,
0x34, 0x4b, 0x78, 0x42, 0xe6, 0xa3, 0x38, 0x4b, 0x87, 0xfd, 0x2b, 0xe3, 0x24, 0x19, 0x47, 0x6c, 0x25, 0x3c, 0x21, 0xf3, 0x51, 0x9c, 0xa5, 0xc3, 0xfe, 0x95, 0x71, 0x92, 0x8c, 0x23, 0xb6, 0x13,
0x27, 0x48, 0xc3, 0x9d, 0x20, 0x8e, 0x13, 0x1e, 0xf0, 0x30, 0x89, 0x73, 0x89, 0x44, 0x6f, 0xc3, 0xa4, 0xe1, 0x4e, 0x10, 0xc7, 0x09, 0x0f, 0x78, 0x98, 0xc4, 0xb9, 0x44, 0xa2, 0xb7, 0x61, 0xfd,
0xfa, 0xfd, 0x8c, 0x05, 0x9c, 0x7d, 0x11, 0x44, 0x11, 0xe3, 0x3e, 0xfb, 0x72, 0xca, 0x72, 0x4e, 0x7e, 0xc6, 0x02, 0xce, 0x3e, 0x0f, 0xa2, 0x88, 0x71, 0x9f, 0x7d, 0x31, 0x65, 0x39, 0x27, 0x7d,
0xfa, 0xb0, 0x94, 0x06, 0x79, 0x7e, 0x96, 0x64, 0xa3, 0x9e, 0x77, 0xcd, 0xdb, 0xee, 0xf8, 0x66, 0x58, 0x4a, 0x83, 0x3c, 0x3f, 0x4b, 0xb2, 0x51, 0xcf, 0xbb, 0xe6, 0x6d, 0x77, 0x7c, 0x33, 0xa6,
0x4c, 0x37, 0x61, 0xc3, 0xfd, 0x24, 0x4f, 0x93, 0x38, 0x67, 0x48, 0xea, 0xb3, 0x38, 0x4a, 0x86, 0x9b, 0xb0, 0xe1, 0x7e, 0x92, 0xa7, 0x49, 0x9c, 0x33, 0x24, 0xf5, 0x69, 0x1c, 0x25, 0xc3, 0x67,
0xcf, 0x7e, 0x26, 0x52, 0xee, 0x27, 0x8a, 0xd4, 0x0f, 0x1b, 0xd0, 0x7e, 0x9a, 0x05, 0x71, 0x1e, 0x3f, 0x11, 0x29, 0xf7, 0x13, 0x45, 0xea, 0x07, 0x0d, 0x68, 0x3f, 0xcd, 0x82, 0x38, 0x0f, 0x86,
0x0c, 0x71, 0xb3, 0xa4, 0x07, 0x8b, 0xfc, 0xf9, 0xe0, 0x24, 0xc8, 0x4f, 0x04, 0x89, 0x96, 0xaf, 0xb8, 0x59, 0xd2, 0x83, 0x45, 0xfe, 0x7c, 0x70, 0x12, 0xe4, 0x27, 0x82, 0x44, 0xcb, 0xd7, 0x43,
0x87, 0x64, 0x13, 0x16, 0x82, 0x49, 0x32, 0x8d, 0x79, 0xaf, 0x71, 0xcd, 0xdb, 0x9e, 0xf3, 0xd5, 0xb2, 0x09, 0x0b, 0xc1, 0x24, 0x99, 0xc6, 0xbc, 0xd7, 0xb8, 0xe6, 0x6d, 0xcf, 0xf9, 0x6a, 0x44,
0x88, 0xbc, 0x07, 0x6b, 0xf1, 0x74, 0x32, 0x18, 0x26, 0xf1, 0x71, 0x98, 0x4d, 0xe4, 0x91, 0x7b, 0xde, 0x85, 0xb5, 0x78, 0x3a, 0x19, 0x0c, 0x93, 0xf8, 0x38, 0xcc, 0x26, 0xf2, 0xc8, 0xbd, 0xb9,
0x73, 0xd7, 0xbc, 0xed, 0x79, 0xbf, 0x3a, 0x41, 0xae, 0x02, 0x1c, 0xe1, 0x36, 0xe4, 0x12, 0x4d, 0x6b, 0xde, 0xf6, 0xbc, 0x5f, 0x9d, 0x20, 0x57, 0x01, 0x8e, 0x70, 0x1b, 0x72, 0x89, 0xa6, 0x58,
0xb1, 0x84, 0x05, 0x21, 0x14, 0x3a, 0x6a, 0xc4, 0xc2, 0xf1, 0x09, 0xef, 0xcd, 0x0b, 0x42, 0x0e, 0xc2, 0x82, 0x10, 0x0a, 0x1d, 0x35, 0x62, 0xe1, 0xf8, 0x84, 0xf7, 0xe6, 0x05, 0x21, 0x07, 0x86,
0x0c, 0x69, 0xf0, 0x70, 0xc2, 0x06, 0x39, 0x0f, 0x26, 0x69, 0x6f, 0x41, 0xec, 0xc6, 0x82, 0x88, 0x34, 0x78, 0x38, 0x61, 0x83, 0x9c, 0x07, 0x93, 0xb4, 0xb7, 0x20, 0x76, 0x63, 0x41, 0xc4, 0x7c,
0xf9, 0x84, 0x07, 0xd1, 0xe0, 0x98, 0xb1, 0xbc, 0xb7, 0xa8, 0xe6, 0x0d, 0x84, 0xbc, 0x03, 0xcb, 0xc2, 0x83, 0x68, 0x70, 0xcc, 0x58, 0xde, 0x5b, 0x54, 0xf3, 0x06, 0x42, 0xde, 0x86, 0xe5, 0x11,
0x23, 0x96, 0xf3, 0x41, 0x30, 0x1a, 0x65, 0x2c, 0xcf, 0x59, 0xde, 0x5b, 0xba, 0x36, 0xb7, 0xdd, 0xcb, 0xf9, 0x20, 0x18, 0x8d, 0x32, 0x96, 0xe7, 0x2c, 0xef, 0x2d, 0x5d, 0x9b, 0xdb, 0x6e, 0xf9,
0xf2, 0x4b, 0x50, 0xda, 0x83, 0xcd, 0xc7, 0x8c, 0x5b, 0xdc, 0xc9, 0x15, 0xa7, 0xe9, 0x3e, 0x10, 0x25, 0x28, 0xed, 0xc1, 0xe6, 0x63, 0xc6, 0x2d, 0xee, 0xe4, 0x8a, 0xd3, 0x74, 0x1f, 0x88, 0x05,
0x0b, 0xfc, 0x80, 0xf1, 0x20, 0x8c, 0x72, 0xf2, 0x3e, 0x74, 0xb8, 0x85, 0xdc, 0xf3, 0xae, 0xcd, 0x7e, 0xc0, 0x78, 0x10, 0x46, 0x39, 0x79, 0x0f, 0x3a, 0xdc, 0x42, 0xee, 0x79, 0xd7, 0xe6, 0xb6,
0x6d, 0xb7, 0x77, 0xc9, 0x4d, 0x21, 0x1d, 0x37, 0xad, 0x0f, 0x7c, 0x07, 0x8f, 0xfe, 0x8b, 0x07, 0xdb, 0xbb, 0xe4, 0xa6, 0x90, 0x8e, 0x9b, 0xd6, 0x07, 0xbe, 0x83, 0x47, 0xff, 0xd1, 0x83, 0xf6,
0xed, 0x43, 0x16, 0x8f, 0xf4, 0x3d, 0x12, 0x68, 0xe2, 0x4e, 0xd4, 0x1d, 0x8a, 0xdf, 0xe4, 0x2b, 0x21, 0x8b, 0x47, 0xfa, 0x1e, 0x09, 0x34, 0x71, 0x27, 0xea, 0x0e, 0xc5, 0x6f, 0xf2, 0x65, 0x68,
0xd0, 0x16, 0xbb, 0xcb, 0x79, 0x16, 0xc6, 0x63, 0x71, 0x05, 0x2d, 0x1f, 0x10, 0x74, 0x28, 0x20, 0x8b, 0xdd, 0xe5, 0x3c, 0x0b, 0xe3, 0xb1, 0xb8, 0x82, 0x96, 0x0f, 0x08, 0x3a, 0x14, 0x10, 0xb2,
0x64, 0x15, 0xe6, 0x82, 0x09, 0x17, 0x8c, 0x9f, 0xf3, 0xf1, 0x27, 0xb9, 0x0e, 0x9d, 0x34, 0x98, 0x0a, 0x73, 0xc1, 0x84, 0x0b, 0xc6, 0xcf, 0xf9, 0xf8, 0x93, 0x5c, 0x87, 0x4e, 0x1a, 0xcc, 0x26,
0x4d, 0x58, 0xcc, 0x0b, 0x66, 0x77, 0xfc, 0xb6, 0x82, 0xed, 0x21, 0xb7, 0x6f, 0xc2, 0xba, 0x8d, 0x2c, 0xe6, 0x05, 0xb3, 0x3b, 0x7e, 0x5b, 0xc1, 0xf6, 0x90, 0xdb, 0x37, 0x61, 0xdd, 0x46, 0xd1,
0xa2, 0xa9, 0xcf, 0x0b, 0xea, 0x6b, 0x16, 0xa6, 0x5a, 0xe4, 0x5d, 0x58, 0xd1, 0xf8, 0x99, 0xdc, 0xd4, 0xe7, 0x05, 0xf5, 0x35, 0x0b, 0x53, 0x2d, 0xf2, 0x0e, 0xac, 0x68, 0xfc, 0x4c, 0x6e, 0x56,
0xac, 0x60, 0x7f, 0xcb, 0x5f, 0x56, 0x60, 0xcd, 0xa0, 0x3f, 0xf7, 0xa0, 0x23, 0x8f, 0x24, 0xe5, 0xb0, 0xbf, 0xe5, 0x2f, 0x2b, 0xb0, 0x66, 0xd0, 0x9f, 0x7a, 0xd0, 0x91, 0x47, 0x92, 0x72, 0x46,
0x8c, 0xbc, 0x0d, 0x5d, 0xfd, 0x25, 0xcb, 0xb2, 0x24, 0x53, 0xd2, 0xe5, 0x02, 0xc9, 0x0d, 0x58, 0xde, 0x82, 0xae, 0xfe, 0x92, 0x65, 0x59, 0x92, 0x29, 0xe9, 0x72, 0x81, 0xe4, 0x06, 0xac, 0x6a,
0xd5, 0x80, 0x34, 0x63, 0xe1, 0x24, 0x18, 0x33, 0x71, 0xd4, 0x8e, 0x5f, 0x81, 0x93, 0xdd, 0x82, 0x40, 0x9a, 0xb1, 0x70, 0x12, 0x8c, 0x99, 0x38, 0x6a, 0xc7, 0xaf, 0xc0, 0xc9, 0x6e, 0x41, 0x31,
0x62, 0x96, 0x4c, 0x39, 0x13, 0x47, 0x6f, 0xef, 0x76, 0x14, 0xbb, 0x7d, 0x84, 0xf9, 0x2e, 0x0a, 0x4b, 0xa6, 0x9c, 0x89, 0xa3, 0xb7, 0x77, 0x3b, 0x8a, 0xdd, 0x3e, 0xc2, 0x7c, 0x17, 0x85, 0x7e,
0xfd, 0xbe, 0x07, 0x9d, 0xfb, 0x27, 0x41, 0x1c, 0xb3, 0xe8, 0x20, 0x09, 0x63, 0x8e, 0xe2, 0x76, 0xcf, 0x83, 0xce, 0xfd, 0x93, 0x20, 0x8e, 0x59, 0x74, 0x90, 0x84, 0x31, 0x47, 0x71, 0x3b, 0x9e,
0x3c, 0x8d, 0x47, 0x61, 0x3c, 0x1e, 0xf0, 0xe7, 0xa1, 0x7e, 0x36, 0x0e, 0x0c, 0x37, 0x65, 0x8f, 0xc6, 0xa3, 0x30, 0x1e, 0x0f, 0xf8, 0xf3, 0x50, 0x3f, 0x1b, 0x07, 0x86, 0x9b, 0xb2, 0xc7, 0xc8,
0x91, 0x49, 0x8a, 0xff, 0x15, 0x38, 0xd2, 0x4b, 0xa6, 0x3c, 0x9d, 0xf2, 0x41, 0x18, 0x8f, 0xd8, 0x24, 0xc5, 0xff, 0x0a, 0x1c, 0xe9, 0x25, 0x53, 0x9e, 0x4e, 0xf9, 0x20, 0x8c, 0x47, 0xec, 0xb9,
0x73, 0xb1, 0xa7, 0xae, 0xef, 0xc0, 0xe8, 0xaf, 0xc3, 0xea, 0x3e, 0xca, 0x71, 0x1c, 0xc6, 0xe3, 0xd8, 0x53, 0xd7, 0x77, 0x60, 0xf4, 0x57, 0x61, 0x75, 0x1f, 0xe5, 0x38, 0x0e, 0xe3, 0xf1, 0x5d,
0xbb, 0x52, 0xd8, 0xf0, 0x71, 0xa5, 0xd3, 0xa3, 0x67, 0x6c, 0xa6, 0xf8, 0xa2, 0x46, 0x28, 0x0a, 0x29, 0x6c, 0xf8, 0xb8, 0xd2, 0xe9, 0xd1, 0x33, 0x36, 0x53, 0x7c, 0x51, 0x23, 0x14, 0x85, 0x93,
0x27, 0x49, 0xce, 0xd5, 0x7a, 0xe2, 0x37, 0xfd, 0x77, 0x0f, 0x56, 0x90, 0xb7, 0x9f, 0x04, 0xf1, 0x24, 0xe7, 0x6a, 0x3d, 0xf1, 0x9b, 0xfe, 0x9b, 0x07, 0x2b, 0xc8, 0xdb, 0x8f, 0x83, 0x78, 0xa6,
0x4c, 0x8b, 0xcc, 0x3e, 0x74, 0x90, 0xd4, 0xd3, 0xe4, 0xae, 0x7c, 0xa2, 0x52, 0xf4, 0xb6, 0x15, 0x45, 0x66, 0x1f, 0x3a, 0x48, 0xea, 0x69, 0x72, 0x57, 0x3e, 0x51, 0x29, 0x7a, 0xdb, 0x8a, 0x17,
0x2f, 0x4a, 0xd8, 0x37, 0x6d, 0xd4, 0x87, 0x31, 0xcf, 0x66, 0xbe, 0xf3, 0x35, 0x0a, 0x1b, 0x0f, 0x25, 0xec, 0x9b, 0x36, 0xea, 0xc3, 0x98, 0x67, 0x33, 0xdf, 0xf9, 0x1a, 0x85, 0x8d, 0x07, 0xd9,
0xb2, 0x31, 0xe3, 0xe2, 0xf1, 0xaa, 0xc7, 0x0c, 0x12, 0x74, 0x3f, 0x89, 0x8f, 0xc9, 0x35, 0xe8, 0x98, 0x71, 0xf1, 0x78, 0xd5, 0x63, 0x06, 0x09, 0xba, 0x9f, 0xc4, 0xc7, 0xe4, 0x1a, 0x74, 0xf2,
0xe4, 0x01, 0x1f, 0xa4, 0x2c, 0x1b, 0x1c, 0xcd, 0x38, 0x13, 0x02, 0x33, 0xe7, 0x43, 0x1e, 0xf0, 0x80, 0x0f, 0x52, 0x96, 0x0d, 0x8e, 0x66, 0x9c, 0x09, 0x81, 0x99, 0xf3, 0x21, 0x0f, 0xf8, 0x01,
0x03, 0x96, 0xdd, 0x9b, 0x71, 0xd6, 0xff, 0x08, 0xd6, 0x2a, 0xab, 0xa0, 0x8c, 0x16, 0x47, 0xc4, 0xcb, 0xee, 0xcd, 0x38, 0xeb, 0x7f, 0x08, 0x6b, 0x95, 0x55, 0x50, 0x46, 0x8b, 0x23, 0xe2, 0x4f,
0x9f, 0x64, 0x03, 0xe6, 0x4f, 0x83, 0x68, 0xca, 0x94, 0x4e, 0x91, 0x83, 0x0f, 0x1b, 0x1f, 0x78, 0xb2, 0x01, 0xf3, 0xa7, 0x41, 0x34, 0x65, 0x4a, 0xa7, 0xc8, 0xc1, 0x07, 0x8d, 0xf7, 0x3d, 0xfa,
0xf4, 0x1d, 0x58, 0x2d, 0xb6, 0xad, 0x84, 0x88, 0x40, 0xd3, 0xdc, 0x52, 0xcb, 0x17, 0xbf, 0xe9, 0x36, 0xac, 0x16, 0xdb, 0x56, 0x42, 0x44, 0xa0, 0x69, 0x6e, 0xa9, 0xe5, 0x8b, 0xdf, 0xf4, 0x77,
0xef, 0x7b, 0x12, 0xf1, 0x7e, 0x12, 0x9a, 0xf7, 0x89, 0x88, 0xf8, 0x8c, 0x35, 0x22, 0xfe, 0x3e, 0x3d, 0x89, 0x78, 0x3f, 0x09, 0xcd, 0xfb, 0x44, 0x44, 0x7c, 0xc6, 0x1a, 0x11, 0x7f, 0x9f, 0xab,
0x57, 0x7f, 0xfd, 0xfc, 0x87, 0xa5, 0xef, 0xc2, 0x9a, 0xb5, 0x85, 0x57, 0x6c, 0xf6, 0xaf, 0x3c, 0xbf, 0x7e, 0xfa, 0xc3, 0xd2, 0x77, 0x60, 0xcd, 0xda, 0xc2, 0x2b, 0x36, 0xfb, 0x17, 0x1e, 0xac,
0x58, 0x7b, 0xc2, 0xce, 0xd4, 0xad, 0xeb, 0xdd, 0x7e, 0x00, 0x4d, 0x3e, 0x4b, 0x99, 0xc0, 0x5c, 0x3d, 0x61, 0x67, 0xea, 0xd6, 0xf5, 0x6e, 0xdf, 0x87, 0x26, 0x9f, 0xa5, 0x4c, 0x60, 0x2e, 0xef,
0xde, 0x7d, 0x5b, 0x5d, 0x5a, 0x05, 0xef, 0xa6, 0x1a, 0x3e, 0x9d, 0xa5, 0xcc, 0x17, 0x5f, 0xd0, 0xbe, 0xa5, 0x2e, 0xad, 0x82, 0x77, 0x53, 0x0d, 0x9f, 0xce, 0x52, 0xe6, 0x8b, 0x2f, 0xe8, 0x27,
0x4f, 0xa1, 0x6d, 0x01, 0xc9, 0x16, 0xac, 0x7f, 0xf1, 0xf1, 0xd3, 0x27, 0x0f, 0x0f, 0x0f, 0x07, 0xd0, 0xb6, 0x80, 0x64, 0x0b, 0xd6, 0x3f, 0xff, 0xe8, 0xe9, 0x93, 0x87, 0x87, 0x87, 0x83, 0x83,
0x07, 0x9f, 0xdd, 0xfb, 0xe6, 0xc3, 0xdf, 0x1a, 0xec, 0xdd, 0x3d, 0xdc, 0x5b, 0xbd, 0x40, 0x36, 0x4f, 0xef, 0x7d, 0xe3, 0xe1, 0x6f, 0x0c, 0xf6, 0xee, 0x1e, 0xee, 0xad, 0x5e, 0x20, 0x9b, 0x40,
0x81, 0x3c, 0x79, 0x78, 0xf8, 0xf4, 0xe1, 0x03, 0x07, 0xee, 0x91, 0x15, 0x68, 0xdb, 0x80, 0x06, 0x9e, 0x3c, 0x3c, 0x7c, 0xfa, 0xf0, 0x81, 0x03, 0xf7, 0xc8, 0x0a, 0xb4, 0x6d, 0x40, 0x83, 0xf6,
0xed, 0x43, 0xef, 0x09, 0x3b, 0xfb, 0x22, 0xe4, 0x31, 0xcb, 0x73, 0x77, 0x79, 0x7a, 0x13, 0x88, 0xa1, 0xf7, 0x84, 0x9d, 0x7d, 0x1e, 0xf2, 0x98, 0xe5, 0xb9, 0xbb, 0x3c, 0xbd, 0x09, 0xc4, 0xde,
0xbd, 0x27, 0x75, 0xcc, 0x1e, 0x2c, 0x2a, 0x8d, 0xa9, 0x0d, 0x86, 0x1a, 0xd2, 0x77, 0x80, 0x1c, 0x93, 0x3a, 0x66, 0x0f, 0x16, 0x95, 0xc6, 0xd4, 0x06, 0x43, 0x0d, 0xe9, 0xdb, 0x40, 0x0e, 0xc3,
0x86, 0xe3, 0xf8, 0x13, 0x96, 0xe7, 0xc1, 0x98, 0xe9, 0xc3, 0xae, 0xc2, 0xdc, 0x24, 0x1f, 0xab, 0x71, 0xfc, 0x31, 0xcb, 0xf3, 0x60, 0xcc, 0xf4, 0x61, 0x57, 0x61, 0x6e, 0x92, 0x8f, 0xd5, 0x43,
0x87, 0x86, 0x3f, 0xe9, 0xd7, 0x60, 0xdd, 0xc1, 0x53, 0x84, 0xaf, 0x40, 0x2b, 0x0f, 0xc7, 0x71, 0xc3, 0x9f, 0xf4, 0xab, 0xb0, 0xee, 0xe0, 0x29, 0xc2, 0x57, 0xa0, 0x95, 0x87, 0xe3, 0x38, 0xe0,
0xc0, 0xa7, 0x19, 0x53, 0xa4, 0x0b, 0x00, 0x7d, 0x04, 0x1b, 0x9f, 0xb3, 0x2c, 0x3c, 0x9e, 0xbd, 0xd3, 0x8c, 0x29, 0xd2, 0x05, 0x80, 0x3e, 0x82, 0x8d, 0xcf, 0x58, 0x16, 0x1e, 0xcf, 0x5e, 0x47,
0x8e, 0xbc, 0x4b, 0xa7, 0x51, 0xa6, 0xf3, 0x10, 0x2e, 0x96, 0xe8, 0xa8, 0xe5, 0xa5, 0x64, 0xaa, 0xde, 0xa5, 0xd3, 0x28, 0xd3, 0x79, 0x08, 0x17, 0x4b, 0x74, 0xd4, 0xf2, 0x52, 0x32, 0xd5, 0xfd,
0xfb, 0x5b, 0xf2, 0xe5, 0xc0, 0x7a, 0xa7, 0x0d, 0xfb, 0x9d, 0xd2, 0xcf, 0x80, 0xdc, 0x4f, 0xe2, 0x2d, 0xf9, 0x72, 0x60, 0xbd, 0xd3, 0x86, 0xfd, 0x4e, 0xe9, 0xa7, 0x40, 0xee, 0x27, 0x71, 0xcc,
0x98, 0x0d, 0xf9, 0x01, 0x63, 0x99, 0xde, 0xcc, 0xff, 0xb7, 0xc4, 0xb0, 0xbd, 0xbb, 0xa5, 0x2e, 0x86, 0xfc, 0x80, 0xb1, 0x4c, 0x6f, 0xe6, 0xe7, 0x2d, 0x31, 0x6c, 0xef, 0x6e, 0xa9, 0x8b, 0x2d,
0xb6, 0xfc, 0xf8, 0x95, 0x7c, 0x12, 0x68, 0xa6, 0x2c, 0x9b, 0x08, 0xc2, 0x4b, 0xbe, 0xf8, 0x4d, 0x3f, 0x7e, 0x25, 0x9f, 0x04, 0x9a, 0x29, 0xcb, 0x26, 0x82, 0xf0, 0x92, 0x2f, 0x7e, 0xd3, 0x1d,
0x77, 0x60, 0xdd, 0x21, 0x5b, 0xf0, 0x3c, 0x65, 0x2c, 0x1b, 0xa8, 0xdd, 0xcd, 0xfb, 0x7a, 0x48, 0x58, 0x77, 0xc8, 0x16, 0x3c, 0x4f, 0x19, 0xcb, 0x06, 0x6a, 0x77, 0xf3, 0xbe, 0x1e, 0xd2, 0xdb,
0x6f, 0xc3, 0xc5, 0x07, 0x61, 0x3e, 0xac, 0x6e, 0x05, 0x3f, 0x99, 0x1e, 0x0d, 0x8a, 0xe7, 0xa7, 0x70, 0xf1, 0x41, 0x98, 0x0f, 0xab, 0x5b, 0xc1, 0x4f, 0xa6, 0x47, 0x83, 0xe2, 0xf9, 0xe9, 0x21,
0x87, 0x68, 0xe5, 0xca, 0x9f, 0x28, 0xdf, 0xe0, 0x8f, 0x3c, 0x68, 0xee, 0x3d, 0xdd, 0xbf, 0x8f, 0x5a, 0xb9, 0xf2, 0x27, 0xca, 0x37, 0xf8, 0x03, 0x0f, 0x9a, 0x7b, 0x4f, 0xf7, 0xef, 0xa3, 0x63,
0x8e, 0x45, 0x18, 0x0f, 0x93, 0x09, 0xda, 0x06, 0xc9, 0x0e, 0x33, 0x3e, 0xf7, 0x59, 0x5d, 0x81, 0x11, 0xc6, 0xc3, 0x64, 0x82, 0xb6, 0x41, 0xb2, 0xc3, 0x8c, 0xcf, 0x7d, 0x56, 0x57, 0xa0, 0x25,
0x96, 0x30, 0x29, 0x68, 0xb8, 0xc5, 0xa3, 0xea, 0xf8, 0x05, 0x00, 0x9d, 0x06, 0xf6, 0x3c, 0x0d, 0x4c, 0x0a, 0x1a, 0x6e, 0xf1, 0xa8, 0x3a, 0x7e, 0x01, 0x40, 0xa7, 0x81, 0x3d, 0x4f, 0xc3, 0x4c,
0x33, 0xe1, 0x15, 0x68, 0x5b, 0xdf, 0x14, 0xca, 0xb2, 0x3a, 0x41, 0x7f, 0xda, 0x84, 0xee, 0xdd, 0x78, 0x05, 0xda, 0xd6, 0x37, 0x85, 0xb2, 0xac, 0x4e, 0xd0, 0xff, 0x68, 0x42, 0xf7, 0xee, 0x90,
0x21, 0x0f, 0x4f, 0x99, 0x52, 0xde, 0x62, 0x55, 0x01, 0x50, 0xfb, 0x51, 0x23, 0x34, 0x33, 0x19, 0x87, 0xa7, 0x4c, 0x29, 0x6f, 0xb1, 0xaa, 0x00, 0xa8, 0xfd, 0xa8, 0x11, 0x9a, 0x99, 0x8c, 0x4d,
0x9b, 0x24, 0x9c, 0x0d, 0x9c, 0x6b, 0x72, 0x81, 0x88, 0x35, 0x94, 0x84, 0x06, 0x29, 0x9a, 0x01, 0x12, 0xce, 0x06, 0xce, 0x35, 0xb9, 0x40, 0xc4, 0x1a, 0x4a, 0x42, 0x83, 0x14, 0xcd, 0x80, 0xd8,
0xb1, 0xbf, 0x96, 0xef, 0x02, 0x91, 0x65, 0x08, 0x40, 0x2e, 0xe3, 0xce, 0x9a, 0xbe, 0x1e, 0x22, 0x5f, 0xcb, 0x77, 0x81, 0xc8, 0x32, 0x04, 0x20, 0x97, 0x71, 0x67, 0x4d, 0x5f, 0x0f, 0x91, 0x1f,
0x3f, 0x86, 0x41, 0x1a, 0x0c, 0x43, 0x3e, 0x53, 0xda, 0xc0, 0x8c, 0x91, 0x76, 0x94, 0x0c, 0x83, 0xc3, 0x20, 0x0d, 0x86, 0x21, 0x9f, 0x29, 0x6d, 0x60, 0xc6, 0x48, 0x3b, 0x4a, 0x86, 0x41, 0x34,
0x68, 0x70, 0x14, 0x44, 0x41, 0x3c, 0x64, 0xca, 0x3f, 0x71, 0x81, 0xe8, 0x82, 0xa8, 0x2d, 0x69, 0x38, 0x0a, 0xa2, 0x20, 0x1e, 0x32, 0xe5, 0x9f, 0xb8, 0x40, 0x74, 0x41, 0xd4, 0x96, 0x34, 0x9a,
0x34, 0xe9, 0xa6, 0x94, 0xa0, 0xe8, 0xca, 0x0c, 0x93, 0xc9, 0x24, 0xe4, 0xe8, 0xb9, 0xf4, 0x96, 0x74, 0x53, 0x4a, 0x50, 0x74, 0x65, 0x86, 0xc9, 0x64, 0x12, 0x72, 0xf4, 0x5c, 0x7a, 0x4b, 0x52,
0xa4, 0xe6, 0x29, 0x20, 0xe2, 0x24, 0x72, 0x74, 0x26, 0x79, 0xd8, 0x92, 0xab, 0x39, 0x40, 0xa4, 0xf3, 0x14, 0x10, 0x71, 0x12, 0x39, 0x3a, 0x93, 0x3c, 0x6c, 0xc9, 0xd5, 0x1c, 0x20, 0x52, 0x39,
0x72, 0xcc, 0x98, 0xd0, 0x60, 0xcf, 0xce, 0x7a, 0x20, 0xa9, 0x14, 0x10, 0xbc, 0x8d, 0x69, 0x9c, 0x66, 0x4c, 0x68, 0xb0, 0x67, 0x67, 0x3d, 0x90, 0x54, 0x0a, 0x08, 0xde, 0xc6, 0x34, 0xce, 0x19,
0x33, 0xce, 0x23, 0x36, 0x32, 0x1b, 0x6a, 0x0b, 0xb4, 0xea, 0x04, 0xb9, 0x05, 0xeb, 0xd2, 0x99, 0xe7, 0x11, 0x1b, 0x99, 0x0d, 0xb5, 0x05, 0x5a, 0x75, 0x82, 0xdc, 0x82, 0x75, 0xe9, 0x4c, 0xe5,
0xca, 0x03, 0x9e, 0xe4, 0x27, 0x61, 0x3e, 0xc8, 0x59, 0xcc, 0x7b, 0x1d, 0x81, 0x5f, 0x37, 0x45, 0x01, 0x4f, 0xf2, 0x93, 0x30, 0x1f, 0xe4, 0x2c, 0xe6, 0xbd, 0x8e, 0xc0, 0xaf, 0x9b, 0x22, 0xef,
0x3e, 0x80, 0xad, 0x12, 0x38, 0x63, 0x43, 0x16, 0x9e, 0xb2, 0x51, 0xaf, 0x2b, 0xbe, 0x3a, 0x6f, 0xc3, 0x56, 0x09, 0x9c, 0xb1, 0x21, 0x0b, 0x4f, 0xd9, 0xa8, 0xd7, 0x15, 0x5f, 0x9d, 0x37, 0x4d,
0x9a, 0x5c, 0x83, 0x36, 0xfa, 0x90, 0xd3, 0x74, 0x14, 0x70, 0x96, 0xf7, 0x96, 0xc5, 0x3d, 0xd8, 0xae, 0x41, 0x1b, 0x7d, 0xc8, 0x69, 0x3a, 0x0a, 0x38, 0xcb, 0x7b, 0xcb, 0xe2, 0x1e, 0x6c, 0x10,
0x20, 0x72, 0x1b, 0xba, 0x29, 0x93, 0x56, 0xf8, 0x84, 0x47, 0xc3, 0xbc, 0xb7, 0x22, 0x4c, 0x5f, 0xb9, 0x0d, 0xdd, 0x94, 0x49, 0x2b, 0x7c, 0xc2, 0xa3, 0x61, 0xde, 0x5b, 0x11, 0xa6, 0xaf, 0xad,
0x5b, 0x3d, 0x36, 0x94, 0x5f, 0xdf, 0xc5, 0x40, 0xd1, 0x1c, 0xe6, 0xa7, 0x83, 0x11, 0x8b, 0x82, 0x1e, 0x1b, 0xca, 0xaf, 0xef, 0x62, 0xa0, 0x68, 0x0e, 0xf3, 0xd3, 0xc1, 0x88, 0x45, 0xc1, 0xac,
0x59, 0x6f, 0x55, 0x08, 0x5d, 0x01, 0xa0, 0x17, 0x61, 0x7d, 0x3f, 0xcc, 0xb9, 0x92, 0x34, 0xa3, 0xb7, 0x2a, 0x84, 0xae, 0x00, 0xd0, 0x8b, 0xb0, 0xbe, 0x1f, 0xe6, 0x5c, 0x49, 0x9a, 0xd1, 0x7e,
0xfd, 0xf6, 0x60, 0xc3, 0x05, 0xab, 0xb7, 0x78, 0x0b, 0x96, 0x94, 0xd8, 0xe4, 0xbd, 0xb6, 0x58, 0x7b, 0xb0, 0xe1, 0x82, 0xd5, 0x5b, 0xbc, 0x05, 0x4b, 0x4a, 0x6c, 0xf2, 0x5e, 0x5b, 0x2c, 0xbd,
0x7a, 0x43, 0x2d, 0xed, 0x48, 0xac, 0x6f, 0xb0, 0xe8, 0x1f, 0x36, 0xa0, 0x89, 0xef, 0xec, 0xfc, 0xa1, 0x96, 0x76, 0x24, 0xd6, 0x37, 0x58, 0xf4, 0xf7, 0x1b, 0xd0, 0xc4, 0x77, 0x76, 0xfe, 0x9b,
0x37, 0x69, 0x3f, 0xf0, 0x86, 0xf3, 0xc0, 0x6d, 0x75, 0x3b, 0xe7, 0xa8, 0x5b, 0xe1, 0x59, 0xcf, 0xb4, 0x1f, 0x78, 0xc3, 0x79, 0xe0, 0xb6, 0xba, 0x9d, 0x73, 0xd4, 0xad, 0xf0, 0xac, 0x67, 0x9c,
0x38, 0x53, 0xb7, 0x21, 0x25, 0xd6, 0x82, 0x14, 0xf3, 0x19, 0x1b, 0x9e, 0x0a, 0xb1, 0x35, 0xf3, 0xa9, 0xdb, 0x90, 0x12, 0x6b, 0x41, 0x8a, 0xf9, 0x8c, 0x0d, 0x4f, 0x85, 0xd8, 0x9a, 0x79, 0x84,
0x08, 0x41, 0xa1, 0x46, 0x33, 0x27, 0xbe, 0x96, 0x32, 0x6b, 0xc6, 0x7a, 0x4e, 0x7c, 0xb9, 0x58, 0xa0, 0x50, 0xa3, 0x99, 0x13, 0x5f, 0x4b, 0x99, 0x35, 0x63, 0x3d, 0x27, 0xbe, 0x5c, 0x2c, 0xe6,
0xcc, 0x89, 0xef, 0x7a, 0xb0, 0x18, 0xc6, 0x47, 0xc9, 0x34, 0x1e, 0x09, 0xf9, 0x5c, 0xf2, 0xf5, 0xc4, 0x77, 0x3d, 0x58, 0x0c, 0xe3, 0xa3, 0x64, 0x1a, 0x8f, 0x84, 0x7c, 0x2e, 0xf9, 0x7a, 0x88,
0x10, 0xf9, 0x9c, 0x0a, 0xef, 0x28, 0x9c, 0x30, 0x25, 0x98, 0x05, 0x80, 0x12, 0x74, 0x83, 0x72, 0x7c, 0x4e, 0x85, 0x77, 0x14, 0x4e, 0x98, 0x12, 0xcc, 0x02, 0x40, 0x09, 0xba, 0x41, 0xb9, 0xd0,
0xa1, 0x71, 0x0c, 0x93, 0xdf, 0x87, 0x35, 0x0b, 0xa6, 0x38, 0x7c, 0x1d, 0xe6, 0xf1, 0xf4, 0xda, 0x38, 0x86, 0xc9, 0xef, 0xc1, 0x9a, 0x05, 0x53, 0x1c, 0xbe, 0x0e, 0xf3, 0x78, 0x7a, 0xed, 0x4f,
0x9f, 0xd6, 0x37, 0x2b, 0x54, 0x95, 0x9c, 0xa1, 0xab, 0xb0, 0xfc, 0x98, 0xf1, 0x8f, 0xe3, 0xe3, 0xeb, 0x9b, 0x15, 0xaa, 0x4a, 0xce, 0xd0, 0x55, 0x58, 0x7e, 0xcc, 0xf8, 0x47, 0xf1, 0x71, 0xa2,
0x44, 0x53, 0xfa, 0xe3, 0x39, 0x58, 0x31, 0x20, 0x45, 0x68, 0x1b, 0x56, 0xc2, 0x11, 0x8b, 0x79, 0x29, 0xfd, 0xe1, 0x1c, 0xac, 0x18, 0x90, 0x22, 0xb4, 0x0d, 0x2b, 0xe1, 0x88, 0xc5, 0x3c, 0xe4,
0xc8, 0x67, 0x03, 0xc7, 0xdb, 0x2a, 0x83, 0x51, 0xf9, 0x07, 0x51, 0x18, 0xe4, 0x4a, 0x7d, 0xc8, 0xb3, 0x81, 0xe3, 0x6d, 0x95, 0xc1, 0xa8, 0xfc, 0x83, 0x28, 0x0c, 0x72, 0xa5, 0x3e, 0xe4, 0x80,
0x01, 0xd9, 0x85, 0x0d, 0x94, 0x3c, 0x2d, 0x4c, 0xe6, 0xda, 0xa5, 0x93, 0x57, 0x3b, 0x87, 0x8f, 0xec, 0xc2, 0x06, 0x4a, 0x9e, 0x16, 0x26, 0x73, 0xed, 0xd2, 0xc9, 0xab, 0x9d, 0xc3, 0xc7, 0x82,
0x05, 0xe1, 0x52, 0x3d, 0x15, 0x9f, 0x48, 0x55, 0x57, 0x37, 0x85, 0x5c, 0x93, 0x94, 0xf0, 0xc8, 0x70, 0xa9, 0x9e, 0x8a, 0x4f, 0xa4, 0xaa, 0xab, 0x9b, 0x42, 0xae, 0x49, 0x4a, 0x78, 0xe4, 0x79,
0xf3, 0x52, 0x3a, 0x0d, 0xa0, 0x12, 0x1f, 0x2d, 0x48, 0x07, 0xb3, 0x1c, 0x1f, 0x59, 0x31, 0xd6, 0x29, 0x9d, 0x06, 0x50, 0x89, 0x8f, 0x16, 0xa4, 0x83, 0x59, 0x8e, 0x8f, 0xac, 0x18, 0x6b, 0xa9,
0x52, 0x25, 0xc6, 0xda, 0x86, 0x95, 0x7c, 0x16, 0x0f, 0xd9, 0x68, 0xc0, 0x13, 0x5c, 0x37, 0x8c, 0x12, 0x63, 0x6d, 0xc3, 0x4a, 0x3e, 0x8b, 0x87, 0x6c, 0x34, 0xe0, 0x09, 0xae, 0x1b, 0xc6, 0xe2,
0xc5, 0xed, 0x2c, 0xf9, 0x65, 0xb0, 0x88, 0x06, 0x59, 0xce, 0x63, 0xc6, 0x85, 0xd6, 0x58, 0xf2, 0x76, 0x96, 0xfc, 0x32, 0x58, 0x44, 0x83, 0x2c, 0xe7, 0x31, 0xe3, 0x42, 0x6b, 0x2c, 0xf9, 0x7a,
0xf5, 0x10, 0x15, 0xb0, 0x40, 0x91, 0x42, 0xdf, 0xf2, 0xd5, 0x08, 0xad, 0xd8, 0x34, 0x0b, 0xf3, 0x88, 0x0a, 0x58, 0xa0, 0x48, 0xa1, 0x6f, 0xf9, 0x6a, 0x84, 0x56, 0x6c, 0x9a, 0x85, 0x79, 0xaf,
0x5e, 0x47, 0x40, 0xc5, 0x6f, 0xfa, 0x3d, 0x61, 0x1c, 0x4d, 0x10, 0xf8, 0x99, 0x78, 0xb9, 0xe4, 0x23, 0xa0, 0xe2, 0x37, 0xfd, 0xae, 0x30, 0x8e, 0x26, 0x08, 0xfc, 0x54, 0xbc, 0x5c, 0x72, 0x19,
0x32, 0xb4, 0xe4, 0x9e, 0xf2, 0x93, 0x40, 0x87, 0xab, 0x02, 0x70, 0x78, 0x12, 0x60, 0xec, 0xe2, 0x5a, 0x72, 0x4f, 0xf9, 0x49, 0xa0, 0xc3, 0x55, 0x01, 0x38, 0x3c, 0x09, 0x30, 0x76, 0x71, 0x8e,
0x1c, 0x53, 0xbe, 0x82, 0xb6, 0x80, 0xed, 0xc9, 0x53, 0xbe, 0x0d, 0xcb, 0x3a, 0xbc, 0xcc, 0x07, 0x29, 0x5f, 0x41, 0x5b, 0xc0, 0xf6, 0xe4, 0x29, 0xdf, 0x82, 0x65, 0x1d, 0x5e, 0xe6, 0x83, 0x88,
0x11, 0x3b, 0xe6, 0xda, 0xd9, 0x8e, 0xa7, 0x13, 0x5c, 0x2e, 0xdf, 0x67, 0xc7, 0x9c, 0x3e, 0x81, 0x1d, 0x73, 0xed, 0x6c, 0xc7, 0xd3, 0x09, 0x2e, 0x97, 0xef, 0xb3, 0x63, 0x4e, 0x9f, 0xc0, 0x9a,
0x35, 0xf5, 0x02, 0x3f, 0x4d, 0x99, 0x5e, 0xfa, 0x1b, 0x65, 0xfd, 0x2f, 0x0d, 0xf4, 0xba, 0x92, 0x7a, 0x81, 0x9f, 0xa4, 0x4c, 0x2f, 0xfd, 0xf5, 0xb2, 0xfe, 0x97, 0x06, 0x7a, 0x5d, 0x49, 0x96,
0x2c, 0x3b, 0x42, 0x28, 0x19, 0x05, 0xea, 0x03, 0x51, 0xd3, 0xf7, 0xa3, 0x24, 0x67, 0x8a, 0x20, 0x1d, 0x21, 0x94, 0x8c, 0x02, 0xf5, 0x81, 0xa8, 0xe9, 0xfb, 0x51, 0x92, 0x33, 0x45, 0x90, 0x42,
0x85, 0xce, 0x30, 0x4a, 0xf2, 0x72, 0x18, 0x61, 0xc3, 0x90, 0x97, 0xf9, 0x74, 0x38, 0xc4, 0x97, 0x67, 0x18, 0x25, 0x79, 0x39, 0x8c, 0xb0, 0x61, 0xc8, 0xcb, 0x7c, 0x3a, 0x1c, 0xe2, 0xcb, 0x95,
0x2b, 0x4d, 0xbc, 0x1e, 0xd2, 0xbf, 0xf1, 0x60, 0x5d, 0x50, 0xd3, 0xba, 0xc2, 0xf8, 0x85, 0x6f, 0x26, 0x5e, 0x0f, 0xe9, 0x5f, 0x79, 0xb0, 0x2e, 0xa8, 0x69, 0x5d, 0x61, 0xfc, 0xc2, 0x37, 0xdf,
0xbe, 0xcd, 0xce, 0xd0, 0x0e, 0x6b, 0x36, 0x60, 0xfe, 0x38, 0xc9, 0x86, 0x4c, 0xad, 0x24, 0x07, 0x66, 0x67, 0x68, 0x87, 0x35, 0x1b, 0x30, 0x7f, 0x9c, 0x64, 0x43, 0xa6, 0x56, 0x92, 0x83, 0x9f,
0xbf, 0x08, 0x4f, 0xf7, 0x5f, 0x3d, 0x58, 0x13, 0x5b, 0x3d, 0xe4, 0x01, 0x9f, 0xe6, 0xea, 0xf8, 0x85, 0xa7, 0xfb, 0x4f, 0x1e, 0xac, 0x89, 0xad, 0x1e, 0xf2, 0x80, 0x4f, 0x73, 0x75, 0xfc, 0x5f,
0xbf, 0x0a, 0x5d, 0x3c, 0x2a, 0xd3, 0xe2, 0xaf, 0x36, 0xba, 0x61, 0x5e, 0xaa, 0x80, 0x4a, 0xe4, 0x86, 0x2e, 0x1e, 0x95, 0x69, 0xf1, 0x57, 0x1b, 0xdd, 0x30, 0x2f, 0x55, 0x40, 0x25, 0xf2, 0xde,
0xbd, 0x0b, 0xbe, 0x8b, 0x4c, 0x3e, 0x82, 0x8e, 0x9d, 0x23, 0x10, 0x7b, 0x6e, 0xef, 0x5e, 0xd2, 0x05, 0xdf, 0x45, 0x26, 0x1f, 0x42, 0xc7, 0xce, 0x11, 0x88, 0x3d, 0xb7, 0x77, 0x2f, 0xe9, 0x53,
0xa7, 0xac, 0x48, 0xce, 0xde, 0x05, 0xdf, 0xf9, 0x80, 0xdc, 0x01, 0x10, 0x96, 0x59, 0x90, 0x55, 0x56, 0x24, 0x67, 0xef, 0x82, 0xef, 0x7c, 0x40, 0xee, 0x00, 0x08, 0xcb, 0x2c, 0xc8, 0xaa, 0x30,
0x61, 0xe0, 0x25, 0x97, 0x49, 0xd6, 0x65, 0xed, 0x5d, 0xf0, 0x2d, 0xf4, 0x7b, 0x4b, 0xb0, 0x20, 0xf0, 0x92, 0xcb, 0x24, 0xeb, 0xb2, 0xf6, 0x2e, 0xf8, 0x16, 0xfa, 0xbd, 0x25, 0x58, 0x90, 0xa6,
0x4d, 0x09, 0x7d, 0x0c, 0x5d, 0x67, 0xa7, 0x8e, 0x07, 0xdf, 0x91, 0x1e, 0x7c, 0x25, 0xc0, 0x6b, 0x84, 0x3e, 0x86, 0xae, 0xb3, 0x53, 0xc7, 0x83, 0xef, 0x48, 0x0f, 0xbe, 0x12, 0xe0, 0x35, 0x6a,
0xd4, 0x04, 0x78, 0xff, 0xd0, 0x00, 0x82, 0xd2, 0x56, 0xba, 0xce, 0x77, 0x60, 0x59, 0xb1, 0xdf, 0x02, 0xbc, 0x7f, 0x6d, 0x00, 0x41, 0x69, 0x2b, 0x5d, 0xe7, 0xdb, 0xb0, 0xac, 0xd8, 0xef, 0x3a,
0x75, 0xde, 0x4a, 0x50, 0x61, 0xf3, 0x92, 0x91, 0xe3, 0xc1, 0x74, 0x7c, 0x1b, 0x44, 0x6e, 0x02, 0x6f, 0x25, 0xa8, 0xb0, 0x79, 0xc9, 0xc8, 0xf1, 0x60, 0x3a, 0xbe, 0x0d, 0x22, 0x37, 0x81, 0x58,
0xb1, 0x86, 0x3a, 0x6a, 0x97, 0xf6, 0xa0, 0x66, 0x06, 0x15, 0x97, 0x74, 0x3f, 0x74, 0xbc, 0xaa, 0x43, 0x1d, 0xb5, 0x4b, 0x7b, 0x50, 0x33, 0x83, 0x8a, 0x4b, 0xba, 0x1f, 0x3a, 0x5e, 0x55, 0x1e,
0x3c, 0xb6, 0xa6, 0xb8, 0xdf, 0xda, 0x39, 0x91, 0x4c, 0x9a, 0xe6, 0x27, 0x68, 0x93, 0xb5, 0x8f, 0x5b, 0x53, 0xdc, 0x6f, 0xed, 0x9c, 0x48, 0x26, 0x4d, 0xf3, 0x13, 0xb4, 0xc9, 0xda, 0xc7, 0xd1,
0xa3, 0xc7, 0x65, 0x41, 0x5a, 0x78, 0xad, 0x20, 0x2d, 0x96, 0x05, 0x49, 0x58, 0xb8, 0x2c, 0x3c, 0xe3, 0xb2, 0x20, 0x2d, 0xbc, 0x56, 0x90, 0x16, 0xcb, 0x82, 0x24, 0x2c, 0x5c, 0x16, 0x9e, 0x06,
0x0d, 0x38, 0xd3, 0x56, 0x43, 0x0d, 0xe9, 0x4f, 0x3c, 0x58, 0x45, 0xee, 0x39, 0x12, 0xf6, 0x21, 0x9c, 0x69, 0xab, 0xa1, 0x86, 0xe8, 0xd2, 0x4c, 0xc2, 0x58, 0x98, 0xea, 0xc1, 0x04, 0x57, 0x57,
0x08, 0x01, 0x7f, 0x43, 0x01, 0x73, 0x70, 0x7f, 0x7e, 0xf9, 0xfa, 0x00, 0x5a, 0x82, 0x60, 0x92, 0x2e, 0x8d, 0x03, 0xa4, 0x3f, 0xf6, 0x60, 0x15, 0x79, 0xec, 0xc8, 0xe1, 0x07, 0x20, 0x9e, 0xc1,
0xb2, 0x58, 0x89, 0x57, 0xcf, 0x15, 0xaf, 0x42, 0xb7, 0xec, 0x5d, 0xf0, 0x0b, 0x64, 0x4b, 0xb8, 0x1b, 0x8a, 0xa1, 0x83, 0xfb, 0xd3, 0x4b, 0xe1, 0xfb, 0xd0, 0x12, 0x04, 0x93, 0x94, 0xc5, 0x4a,
0xfe, 0xd9, 0x83, 0xb6, 0xda, 0xe6, 0xff, 0xd8, 0xa5, 0xee, 0xc3, 0x12, 0xca, 0x99, 0xe5, 0xb1, 0x08, 0x7b, 0xae, 0x10, 0x16, 0x1a, 0x68, 0xef, 0x82, 0x5f, 0x20, 0x5b, 0x22, 0xf8, 0x0f, 0x1e,
0x9a, 0x31, 0xea, 0xf4, 0x09, 0x46, 0x34, 0x68, 0xc4, 0x1c, 0x77, 0xba, 0x0c, 0x46, 0x8b, 0x24, 0xb4, 0xd5, 0x36, 0xff, 0xd7, 0x8e, 0x77, 0x1f, 0x96, 0x50, 0x1a, 0x2d, 0xbf, 0xd6, 0x8c, 0x51,
0xd4, 0x68, 0x3e, 0xe0, 0x61, 0x34, 0xd0, 0xb3, 0x2a, 0xd1, 0x56, 0x37, 0x85, 0xda, 0x24, 0xe7, 0xf3, 0x4f, 0x30, 0xee, 0x41, 0x53, 0xe7, 0x38, 0xdd, 0x65, 0x30, 0xda, 0x2d, 0xa1, 0x6c, 0xf3,
0xc1, 0x98, 0x29, 0x63, 0x23, 0x07, 0x18, 0x37, 0xa8, 0x03, 0x95, 0x5d, 0xa5, 0x1f, 0x03, 0x6c, 0x01, 0x0f, 0xa3, 0x81, 0x9e, 0x55, 0xe9, 0xb8, 0xba, 0x29, 0xd4, 0x39, 0x39, 0x0f, 0xc6, 0x4c,
0x55, 0xa6, 0x8c, 0xbb, 0xa4, 0x3c, 0xc4, 0x28, 0x9c, 0x1c, 0x25, 0xc6, 0xd9, 0xf4, 0x6c, 0xe7, 0x99, 0x24, 0x39, 0xc0, 0xe8, 0x42, 0x1d, 0xa8, 0xec, 0x50, 0xfd, 0x08, 0x60, 0xab, 0x32, 0x65,
0xd1, 0x99, 0x22, 0x63, 0xb8, 0xa8, 0xad, 0x2a, 0xf2, 0xb4, 0xb0, 0xa1, 0x0d, 0xe1, 0x0e, 0xdc, 0x9c, 0x2a, 0xe5, 0x47, 0x46, 0xe1, 0xe4, 0x28, 0x31, 0x2e, 0xa9, 0x67, 0xbb, 0x98, 0xce, 0x14,
0x76, 0x65, 0xa0, 0xbc, 0xa0, 0x86, 0xdb, 0xef, 0xb1, 0x9e, 0x1e, 0x39, 0x81, 0x9e, 0x31, 0xdf, 0x19, 0xc3, 0x45, 0x6d, 0x7b, 0x91, 0xa7, 0x85, 0xa5, 0x6d, 0x08, 0xa7, 0xe1, 0xb6, 0x2b, 0x03,
0x4a, 0x71, 0x5b, 0x26, 0x1e, 0xd7, 0x7a, 0xef, 0x35, 0x6b, 0x09, 0x2d, 0x33, 0xd2, 0xcb, 0x9c, 0xe5, 0x05, 0x35, 0xdc, 0x7e, 0xb5, 0xf5, 0xf4, 0xc8, 0x09, 0xf4, 0x8c, 0x91, 0x57, 0xea, 0xdd,
0x4b, 0x8d, 0xcc, 0xe0, 0xaa, 0x9e, 0x13, 0x9a, 0xb9, 0xba, 0x5e, 0xf3, 0x8d, 0xce, 0xf6, 0x08, 0x72, 0x04, 0x70, 0xad, 0x77, 0x5f, 0xb3, 0x96, 0xd0, 0x45, 0x23, 0xbd, 0xcc, 0xb9, 0xd4, 0xc8,
0x3f, 0x76, 0x17, 0x7d, 0x0d, 0xe1, 0xfe, 0x8f, 0x3d, 0x58, 0x76, 0xc9, 0xa1, 0xe8, 0xa8, 0xa8, 0x0c, 0xae, 0xea, 0x39, 0xa1, 0xbf, 0xab, 0xeb, 0x35, 0xdf, 0xe8, 0x6c, 0x8f, 0xf0, 0x63, 0x77,
0x43, 0xab, 0x0e, 0xed, 0x16, 0x95, 0xc0, 0xd5, 0xb8, 0xa9, 0x51, 0x17, 0x37, 0xd9, 0xd1, 0xd1, 0xd1, 0xd7, 0x10, 0xee, 0xff, 0xc8, 0x83, 0x65, 0x97, 0x1c, 0x8a, 0x8e, 0x8a, 0x4d, 0xb4, 0x82,
0xdc, 0xeb, 0xa2, 0xa3, 0xe6, 0x9b, 0x45, 0x47, 0xf3, 0x75, 0xd1, 0x51, 0xff, 0xbf, 0x3c, 0x20, 0xd1, 0xce, 0x53, 0x09, 0x5c, 0x8d, 0xae, 0x1a, 0x75, 0xd1, 0x95, 0x1d, 0x43, 0xcd, 0xbd, 0x2e,
0xd5, 0xfb, 0x25, 0x8f, 0x65, 0xe0, 0x16, 0xb3, 0x48, 0xe9, 0x89, 0x5f, 0x7a, 0x33, 0x19, 0xd1, 0x86, 0x6a, 0xbe, 0x59, 0x0c, 0x35, 0x5f, 0x17, 0x43, 0xf5, 0xff, 0xcb, 0x03, 0x52, 0xbd, 0x5f,
0x3c, 0xd4, 0x5f, 0xa3, 0xb0, 0xda, 0x8a, 0xc0, 0x76, 0x46, 0xba, 0x7e, 0xdd, 0x54, 0x29, 0x5e, 0xf2, 0x58, 0x86, 0x77, 0x31, 0x8b, 0x94, 0x9e, 0xf8, 0x85, 0x37, 0x93, 0x11, 0xcd, 0x43, 0xfd,
0x6b, 0xbe, 0x3e, 0x5e, 0x9b, 0x7f, 0x7d, 0xbc, 0xb6, 0x50, 0x8e, 0xd7, 0xfa, 0xbf, 0x0b, 0x5d, 0x35, 0x0a, 0xab, 0xad, 0x08, 0x6c, 0x97, 0xa5, 0xeb, 0xd7, 0x4d, 0x95, 0xa2, 0xba, 0xe6, 0xeb,
0xe7, 0xd6, 0x7f, 0x71, 0x27, 0x2e, 0x3b, 0x32, 0xf2, 0x82, 0x1d, 0x58, 0xff, 0x3f, 0x1b, 0x40, 0xa3, 0xba, 0xf9, 0xd7, 0x47, 0x75, 0x0b, 0xe5, 0xa8, 0xae, 0xff, 0xdb, 0xd0, 0x75, 0x6e, 0xfd,
0xaa, 0x92, 0xf7, 0x7f, 0xba, 0x07, 0x21, 0x47, 0x8e, 0x02, 0x99, 0x53, 0x72, 0xe4, 0xa8, 0x8e, 0x67, 0x77, 0xe2, 0xb2, 0xbb, 0x23, 0x2f, 0xd8, 0x81, 0xf5, 0xff, 0xb3, 0x01, 0xa4, 0x2a, 0x79,
0xff, 0x4d, 0xa5, 0xf8, 0x1e, 0xac, 0x65, 0x6c, 0x98, 0x9c, 0xb2, 0xcc, 0x8a, 0x99, 0xe5, 0x55, 0xff, 0xaf, 0x7b, 0x10, 0x72, 0xe4, 0x28, 0x90, 0x39, 0x25, 0x47, 0x8e, 0xea, 0xf8, 0xbf, 0x54,
0x55, 0x27, 0xd0, 0x95, 0x73, 0xa3, 0xd4, 0x25, 0xa7, 0x36, 0x60, 0x59, 0x86, 0x52, 0xb0, 0x4a, 0x8a, 0xef, 0xc2, 0x5a, 0xc6, 0x86, 0xc9, 0x29, 0xcb, 0xac, 0xc8, 0x5a, 0x5e, 0x55, 0x75, 0x02,
0xbf, 0x01, 0x1b, 0xb2, 0x64, 0x73, 0x4f, 0x92, 0xd2, 0xde, 0xc4, 0x75, 0xe8, 0x9c, 0xc9, 0x34, 0x1d, 0x3e, 0x37, 0x96, 0x5d, 0x72, 0x2a, 0x08, 0x96, 0x65, 0x28, 0x85, 0xb4, 0xf4, 0xeb, 0xb0,
0xdd, 0x20, 0x89, 0xa3, 0x99, 0x32, 0x22, 0x6d, 0x05, 0xfb, 0x34, 0x8e, 0x66, 0xf4, 0x47, 0x1e, 0x21, 0x0b, 0x3b, 0xf7, 0x24, 0x29, 0xed, 0x73, 0x5c, 0x87, 0xce, 0x99, 0x4c, 0xe6, 0x0d, 0x92,
0x5c, 0x2c, 0x7d, 0x5b, 0x64, 0xe3, 0xa5, 0xaa, 0x75, 0xf5, 0xaf, 0x0b, 0xc4, 0x23, 0x2a, 0x19, 0x38, 0x9a, 0x29, 0x23, 0xd2, 0x56, 0xb0, 0x4f, 0xe2, 0x68, 0x46, 0x7f, 0xe8, 0xc1, 0xc5, 0xd2,
0xb7, 0x8e, 0x28, 0x4d, 0x52, 0x75, 0x02, 0x59, 0x38, 0x8d, 0xab, 0xf8, 0xf2, 0x62, 0xea, 0xa6, 0xb7, 0x45, 0xce, 0x5e, 0xaa, 0x5a, 0x57, 0xff, 0xba, 0x40, 0x3c, 0xa2, 0x92, 0x71, 0xeb, 0x88,
0xe8, 0x16, 0x5c, 0x54, 0x97, 0xef, 0x9e, 0x8d, 0xee, 0xc2, 0x66, 0x79, 0xa2, 0xc8, 0x7c, 0xb9, 0xd2, 0x24, 0x55, 0x27, 0x90, 0x85, 0xd3, 0xb8, 0x8a, 0x2f, 0x2f, 0xa6, 0x6e, 0x8a, 0x6e, 0xc1,
0x5b, 0xd6, 0x43, 0xfa, 0x11, 0x90, 0x6f, 0x4d, 0x59, 0x36, 0x13, 0x79, 0x7f, 0x93, 0x5a, 0xdd, 0x45, 0x75, 0xf9, 0xee, 0xd9, 0xe8, 0x2e, 0x6c, 0x96, 0x27, 0x8a, 0xfc, 0x98, 0xbb, 0x65, 0x3d,
0x2a, 0x87, 0xd8, 0x0b, 0xe9, 0xf4, 0xe8, 0x9b, 0x6c, 0xa6, 0xcb, 0x25, 0x0d, 0x53, 0x2e, 0xa1, 0xa4, 0x1f, 0x02, 0xf9, 0xe6, 0x94, 0x65, 0x33, 0x51, 0x1d, 0x30, 0x09, 0xd8, 0xad, 0x72, 0x20,
0x77, 0x60, 0xdd, 0x21, 0x60, 0x58, 0xb5, 0x20, 0x6a, 0x07, 0x3a, 0xfc, 0x74, 0xeb, 0x0b, 0x6a, 0xbe, 0x90, 0x4e, 0x8f, 0xbe, 0xc1, 0x66, 0xba, 0xa8, 0xd2, 0x30, 0x45, 0x15, 0x7a, 0x07, 0xd6,
0x8e, 0xfe, 0xa5, 0x07, 0x73, 0x7b, 0x49, 0x6a, 0xe7, 0x8c, 0x3c, 0x37, 0x67, 0xa4, 0x74, 0xe7, 0x1d, 0x02, 0x86, 0x55, 0x0b, 0xa2, 0xc2, 0xa0, 0x83, 0x54, 0xb7, 0x0a, 0xa1, 0xe6, 0xe8, 0x9f,
0xc0, 0xa8, 0xc6, 0x86, 0x7a, 0xf9, 0x36, 0x10, 0x35, 0x5f, 0x30, 0xe1, 0x18, 0x80, 0x1d, 0x27, 0x7b, 0x30, 0xb7, 0x97, 0xa4, 0x76, 0x66, 0xc9, 0x73, 0x33, 0x4b, 0x4a, 0x77, 0x0e, 0x8c, 0x6a,
0xd9, 0x59, 0x90, 0x8d, 0x14, 0xff, 0x4a, 0x50, 0xdc, 0x7e, 0xa1, 0x60, 0xf0, 0x27, 0x3a, 0x0d, 0x6c, 0xa8, 0x97, 0x6f, 0x03, 0x51, 0xf3, 0x05, 0x13, 0x8e, 0x61, 0xda, 0x71, 0x92, 0x9d, 0x05,
0x22, 0x71, 0x36, 0x53, 0x31, 0xa3, 0x1a, 0xd1, 0x1f, 0x78, 0x30, 0x2f, 0xf6, 0x8a, 0xaf, 0x41, 0xd9, 0x48, 0xf1, 0xaf, 0x04, 0xc5, 0xed, 0x17, 0x0a, 0x06, 0x7f, 0xa2, 0xd3, 0x20, 0xd2, 0x6b,
0xde, 0xaf, 0x28, 0x95, 0x89, 0xbc, 0x9c, 0x27, 0x5f, 0x43, 0x09, 0x5c, 0x2a, 0xa0, 0x35, 0x2a, 0x33, 0x15, 0x59, 0xaa, 0x11, 0xfd, 0xbe, 0x07, 0xf3, 0x62, 0xaf, 0xf8, 0x1a, 0xe4, 0xfd, 0x8a,
0x05, 0xb4, 0x2b, 0xd0, 0x92, 0xa3, 0xa2, 0xe2, 0x54, 0x00, 0xc8, 0x55, 0x68, 0x9e, 0x24, 0xa9, 0x82, 0x9a, 0xc8, 0xde, 0x79, 0xf2, 0x35, 0x94, 0xc0, 0xa5, 0x32, 0x5b, 0xa3, 0x52, 0x66, 0xbb,
0xb6, 0x61, 0xa0, 0x13, 0x31, 0x49, 0xea, 0x0b, 0x38, 0xbd, 0x01, 0x2b, 0x4f, 0x92, 0x11, 0xb3, 0x02, 0x2d, 0x39, 0x2a, 0xea, 0x52, 0x05, 0x80, 0x5c, 0x85, 0xe6, 0x49, 0x92, 0x6a, 0x1b, 0x06,
0xa2, 0xf5, 0x73, 0xaf, 0x89, 0xfe, 0x9e, 0x07, 0x4b, 0x1a, 0x99, 0x6c, 0x43, 0x13, 0x4d, 0x51, 0x3a, 0x5d, 0x93, 0xa4, 0xbe, 0x80, 0xd3, 0x1b, 0xb0, 0xf2, 0x24, 0x19, 0x31, 0x2b, 0xa6, 0x3f,
0xc9, 0xf9, 0x33, 0xe9, 0x54, 0xc4, 0xf3, 0x05, 0x06, 0xaa, 0x10, 0x11, 0x1b, 0x16, 0xae, 0x82, 0xf7, 0x9a, 0xe8, 0xef, 0x78, 0xb0, 0xa4, 0x91, 0xc9, 0x36, 0x34, 0xd1, 0x14, 0x95, 0x9c, 0x3f,
0x8e, 0x0c, 0x0b, 0x23, 0x8c, 0xee, 0xb8, 0xd8, 0x73, 0xc9, 0x58, 0x95, 0xa0, 0xf4, 0x6f, 0x3d, 0x93, 0x74, 0x45, 0x3c, 0x5f, 0x60, 0xa0, 0x0a, 0x11, 0x11, 0x64, 0xe1, 0x2a, 0xe8, 0xf8, 0xb1,
0xe8, 0x3a, 0x6b, 0xa0, 0x83, 0x1e, 0x05, 0x39, 0x57, 0x29, 0x28, 0xc5, 0x44, 0x1b, 0x64, 0x67, 0x30, 0xc2, 0xe8, 0xb4, 0x8b, 0x3d, 0x97, 0x8c, 0x55, 0x09, 0x4a, 0xff, 0xda, 0x83, 0xae, 0xb3,
0x76, 0x1a, 0x6e, 0x66, 0xc7, 0x64, 0x16, 0xe6, 0xec, 0xcc, 0xc2, 0x2d, 0x68, 0x15, 0xc5, 0xc8, 0x06, 0xba, 0xf1, 0x51, 0x90, 0x73, 0x95, 0xa8, 0x52, 0x4c, 0xb4, 0x41, 0x76, 0xfe, 0xa7, 0xe1,
0xa6, 0xa3, 0x1a, 0x70, 0x45, 0x9d, 0x28, 0x2e, 0x90, 0x90, 0xce, 0x30, 0x89, 0x92, 0x4c, 0xd5, 0xe6, 0x7f, 0x4c, 0xfe, 0x61, 0xce, 0xce, 0x3f, 0xdc, 0x82, 0x56, 0x51, 0xb2, 0x6c, 0x3a, 0xaa,
0xea, 0xe4, 0x80, 0xde, 0x81, 0xb6, 0x85, 0x8f, 0xdb, 0x88, 0x19, 0x3f, 0x4b, 0xb2, 0x67, 0x3a, 0x01, 0x57, 0xd4, 0xe9, 0xe4, 0x02, 0x09, 0xe9, 0x0c, 0x93, 0x28, 0xc9, 0x54, 0x45, 0x4f, 0x0e,
0xc1, 0xa4, 0x86, 0xa6, 0x40, 0xd2, 0x28, 0x0a, 0x24, 0xf4, 0xef, 0x3c, 0xe8, 0xa2, 0xa4, 0x84, 0xe8, 0x1d, 0x68, 0x5b, 0xf8, 0xb8, 0x8d, 0x98, 0xf1, 0xb3, 0x24, 0x7b, 0xa6, 0xd3, 0x50, 0x6a,
0xf1, 0xf8, 0x20, 0x89, 0xc2, 0xe1, 0x4c, 0x48, 0x8c, 0x16, 0x8a, 0xc1, 0x88, 0x45, 0x3c, 0x30, 0x68, 0xca, 0x28, 0x8d, 0xa2, 0x8c, 0x42, 0xff, 0xc6, 0x83, 0x2e, 0x4a, 0x4a, 0x18, 0x8f, 0x0f,
0x12, 0xe3, 0x82, 0xd1, 0xe6, 0x4f, 0xc2, 0x58, 0xa8, 0x2c, 0x25, 0x2f, 0x66, 0x8c, 0x92, 0x8f, 0x92, 0x28, 0x1c, 0xce, 0x84, 0xc4, 0x68, 0xa1, 0x18, 0x8c, 0x58, 0xc4, 0x03, 0x23, 0x31, 0x2e,
0xb6, 0xeb, 0x28, 0xc8, 0xd9, 0x60, 0x82, 0xe1, 0x84, 0xd2, 0xd5, 0x0e, 0x10, 0xd5, 0x07, 0x02, 0x18, 0x6d, 0xbe, 0xf6, 0xe2, 0x95, 0xbc, 0x98, 0x31, 0x4a, 0x3e, 0xda, 0xae, 0xa3, 0x20, 0x67,
0xb2, 0x80, 0xb3, 0xc1, 0x24, 0x8c, 0xa2, 0x50, 0xe2, 0x4a, 0x09, 0xaf, 0x9b, 0xc2, 0x30, 0xab, 0xd2, 0xed, 0x57, 0xba, 0xda, 0x01, 0xa2, 0xfa, 0x40, 0x40, 0x16, 0x70, 0x36, 0x98, 0x84, 0x51,
0xad, 0xd4, 0xc4, 0xc3, 0xd1, 0x58, 0xe6, 0x4a, 0x95, 0x23, 0x62, 0x9e, 0x9f, 0x05, 0xd1, 0xf3, 0x14, 0x4a, 0x5c, 0x29, 0xe1, 0x75, 0x53, 0xf4, 0x6f, 0x1b, 0xd0, 0x56, 0x6a, 0xe2, 0xe1, 0x68,
0x8e, 0xeb, 0x62, 0x41, 0xca, 0xd7, 0x3a, 0x57, 0xbd, 0xd6, 0x2b, 0xd0, 0x42, 0xf1, 0xba, 0x2d, 0x2c, 0x33, 0xaa, 0xca, 0x11, 0x31, 0xcf, 0xcf, 0x82, 0xe8, 0x79, 0xc7, 0x75, 0xb1, 0x20, 0xe5,
0x7c, 0x24, 0x59, 0xbb, 0x2e, 0x00, 0x7a, 0x76, 0x57, 0xcc, 0xce, 0x17, 0xb3, 0x02, 0xe0, 0x78, 0x6b, 0x9d, 0xab, 0x5e, 0xeb, 0x15, 0x68, 0xa1, 0x78, 0xdd, 0x16, 0x3e, 0x92, 0xac, 0x70, 0x17,
0x45, 0x0b, 0x25, 0xaf, 0xe8, 0x03, 0xe8, 0x28, 0x32, 0x82, 0xef, 0x22, 0x5c, 0x2a, 0x04, 0xdc, 0x00, 0x3d, 0xbb, 0x2b, 0x66, 0xe7, 0x8b, 0x59, 0x01, 0x70, 0xbc, 0xa2, 0x85, 0x92, 0x57, 0xf4,
0xb9, 0x13, 0xdf, 0xc1, 0xd4, 0x5f, 0xee, 0xea, 0x2f, 0x97, 0x5e, 0xf7, 0xa5, 0xc6, 0xa4, 0x17, 0x3e, 0x74, 0x14, 0x19, 0xc1, 0x77, 0x11, 0x54, 0x15, 0x02, 0xee, 0xdc, 0x89, 0xef, 0x60, 0xea,
0x61, 0x5d, 0x31, 0xef, 0x71, 0x16, 0xa4, 0x27, 0x5a, 0xf5, 0x8e, 0x4c, 0x81, 0x54, 0x80, 0xc9, 0x2f, 0x77, 0xf5, 0x97, 0x4b, 0xaf, 0xfb, 0x52, 0x63, 0xd2, 0x8b, 0xb0, 0xae, 0x98, 0xf7, 0x38,
0x0d, 0x98, 0xc7, 0xcf, 0xb4, 0xf6, 0xab, 0x7f, 0x74, 0x12, 0x85, 0x6c, 0xc3, 0x3c, 0x1b, 0x8d, 0x0b, 0xd2, 0x13, 0xad, 0x7a, 0x47, 0xa6, 0x8c, 0x2a, 0xc0, 0xe4, 0x06, 0xcc, 0xe3, 0x67, 0x5a,
0x99, 0xf6, 0xcc, 0x89, 0x1b, 0x23, 0xe1, 0x1d, 0xf9, 0x12, 0x01, 0x55, 0x00, 0x42, 0x4b, 0x2a, 0xfb, 0xd5, 0x3f, 0x3a, 0x89, 0x42, 0xb6, 0x61, 0x9e, 0x8d, 0xc6, 0x4c, 0x7b, 0xe6, 0xc4, 0x8d,
0xc0, 0xd5, 0x9c, 0x0b, 0x38, 0xfc, 0x78, 0x44, 0x37, 0x80, 0x3c, 0x91, 0x52, 0x6b, 0xe7, 0xf7, 0x91, 0xf0, 0x8e, 0x7c, 0x89, 0x80, 0x2a, 0x00, 0xa1, 0x25, 0x15, 0xe0, 0x6a, 0xce, 0x05, 0x1c,
0xfe, 0x60, 0x0e, 0xda, 0x16, 0x18, 0x5f, 0xf3, 0x18, 0x37, 0x3c, 0x18, 0x85, 0xc1, 0x84, 0x71, 0x7e, 0x34, 0xa2, 0x1b, 0x40, 0x9e, 0x48, 0xa9, 0xb5, 0xb3, 0x80, 0xbf, 0x37, 0x07, 0x6d, 0x0b,
0x96, 0x29, 0x49, 0x2d, 0x41, 0x85, 0x82, 0x3d, 0x1d, 0x0f, 0x92, 0x29, 0x1f, 0x8c, 0xd8, 0x38, 0x8c, 0xaf, 0x79, 0x8c, 0x1b, 0x1e, 0x8c, 0xc2, 0x60, 0xc2, 0x38, 0xcb, 0x94, 0xa4, 0x96, 0xa0,
0x63, 0xd2, 0xa0, 0x79, 0x7e, 0x09, 0x8a, 0x78, 0x93, 0xe0, 0xb9, 0x8d, 0x27, 0xe5, 0xa1, 0x04, 0x42, 0xc1, 0x9e, 0x8e, 0x07, 0xc9, 0x94, 0x0f, 0x46, 0x6c, 0x9c, 0x31, 0x69, 0xd0, 0x3c, 0xbf,
0xd5, 0xd9, 0x3a, 0xc9, 0xa3, 0x66, 0x91, 0xad, 0x93, 0x1c, 0x29, 0xeb, 0xa1, 0xf9, 0x1a, 0x3d, 0x04, 0x45, 0xbc, 0x49, 0xf0, 0xdc, 0xc6, 0x93, 0xf2, 0x50, 0x82, 0xea, 0x9c, 0x9e, 0xe4, 0x51,
0xf4, 0x3e, 0x6c, 0x4a, 0x8d, 0xa3, 0xde, 0xe6, 0xa0, 0x24, 0x26, 0xe7, 0xcc, 0x92, 0x1b, 0xb0, 0xb3, 0xc8, 0xe9, 0x49, 0x8e, 0x94, 0xf5, 0xd0, 0x7c, 0x8d, 0x1e, 0x7a, 0x0f, 0x36, 0xa5, 0xc6,
0x8a, 0x7b, 0xd6, 0x02, 0x9e, 0x87, 0xdf, 0x93, 0x71, 0xb6, 0xe7, 0x57, 0xe0, 0x88, 0x8b, 0xcf, 0x51, 0x6f, 0x73, 0x50, 0x12, 0x93, 0x73, 0x66, 0xc9, 0x0d, 0x58, 0xc5, 0x3d, 0x6b, 0x01, 0xcf,
0xd1, 0xc1, 0x95, 0xc5, 0x84, 0x0a, 0x5c, 0xe0, 0x06, 0xcf, 0x5d, 0xdc, 0x96, 0xc2, 0x2d, 0xc1, 0xc3, 0xef, 0xca, 0x68, 0xdc, 0xf3, 0x2b, 0x70, 0xc4, 0xc5, 0xe7, 0xe8, 0xe0, 0xca, 0x92, 0x43,
0x69, 0x17, 0xda, 0x87, 0x3c, 0x49, 0xf5, 0xa5, 0x2c, 0x43, 0x47, 0x0e, 0x55, 0x01, 0xe9, 0x32, 0x05, 0x2e, 0x70, 0x83, 0xe7, 0x2e, 0x6e, 0x4b, 0xe1, 0x96, 0xe0, 0xb4, 0x0b, 0xed, 0x43, 0x9e,
0x5c, 0x12, 0x52, 0xf4, 0x34, 0x49, 0x93, 0x28, 0x19, 0xcf, 0x0e, 0xa7, 0x47, 0xf9, 0x30, 0x0b, 0xa4, 0xfa, 0x52, 0x96, 0xa1, 0x23, 0x87, 0xaa, 0xcc, 0x74, 0x19, 0x2e, 0x09, 0x29, 0x7a, 0x9a,
0x53, 0xf4, 0x98, 0xe9, 0x3f, 0x79, 0xb0, 0xee, 0xcc, 0xaa, 0x50, 0xff, 0x97, 0xa5, 0x48, 0x9b, 0xa4, 0x49, 0x94, 0x8c, 0x67, 0x87, 0xd3, 0xa3, 0x7c, 0x98, 0x85, 0x29, 0x7a, 0xcc, 0xf4, 0xef,
0x9c, 0xbf, 0x14, 0xbc, 0x35, 0x4b, 0x1d, 0x4a, 0x44, 0x99, 0x12, 0xf9, 0x4c, 0x95, 0x01, 0xee, 0x3d, 0x58, 0x77, 0x66, 0x55, 0xa8, 0xff, 0x8b, 0x52, 0xa4, 0x4d, 0x65, 0x40, 0x0a, 0xde, 0x9a,
0xc2, 0x8a, 0xde, 0x99, 0xfe, 0x50, 0x4a, 0x61, 0xaf, 0x2a, 0x85, 0xea, 0xfb, 0x65, 0xf5, 0x81, 0xa5, 0x0e, 0x25, 0xa2, 0x4c, 0x9c, 0x7c, 0xaa, 0x8a, 0x05, 0x77, 0x61, 0x45, 0xef, 0x4c, 0x7f,
0x26, 0xf1, 0x6b, 0xd2, 0xef, 0x64, 0x23, 0x71, 0x46, 0x1d, 0xf3, 0xf5, 0xf5, 0xf7, 0xb6, 0xb3, 0x28, 0xa5, 0xb0, 0x57, 0x95, 0x42, 0xf5, 0xfd, 0xb2, 0xfa, 0x40, 0x93, 0xf8, 0x15, 0xe9, 0x77,
0xab, 0x77, 0x30, 0x34, 0xc0, 0x9c, 0xfe, 0x89, 0x07, 0x50, 0xec, 0x0e, 0x05, 0xa3, 0x50, 0xe9, 0xb2, 0x91, 0x38, 0xa3, 0x8e, 0xf9, 0xfa, 0xfa, 0x7b, 0xdb, 0xd9, 0xd5, 0x3b, 0x18, 0x1a, 0x60,
0x9e, 0xc8, 0x86, 0x5a, 0xea, 0xfb, 0x3a, 0x74, 0x4c, 0xce, 0xb9, 0xb0, 0x12, 0x6d, 0x0d, 0x43, 0x4e, 0xff, 0xc8, 0x03, 0x28, 0x76, 0x87, 0x82, 0x51, 0xa8, 0x74, 0x4f, 0xe4, 0x4c, 0x2d, 0xf5,
0x0f, 0xe5, 0x5d, 0x58, 0x19, 0x47, 0xc9, 0x91, 0xb0, 0xb9, 0xa2, 0x56, 0x99, 0xab, 0x32, 0xda, 0x7d, 0x1d, 0x3a, 0x26, 0x33, 0x5d, 0x58, 0x89, 0xb6, 0x86, 0xa1, 0x87, 0xf2, 0x0e, 0xac, 0x8c,
0xb2, 0x04, 0x3f, 0x52, 0xd0, 0xc2, 0xa4, 0x34, 0x2d, 0x93, 0x42, 0xff, 0xb4, 0x61, 0x32, 0x9f, 0xa3, 0xe4, 0x48, 0xd8, 0x5c, 0x51, 0xd1, 0xcc, 0x55, 0xb1, 0x6d, 0x59, 0x82, 0x1f, 0x29, 0x68,
0xc5, 0x99, 0xcf, 0x7d, 0x65, 0x64, 0xb7, 0xa2, 0x1c, 0xcf, 0x49, 0x34, 0x8a, 0xec, 0xc6, 0xc1, 0x61, 0x52, 0x9a, 0x96, 0x49, 0xa1, 0x7f, 0xdc, 0x30, 0xf9, 0xd1, 0xe2, 0xcc, 0xe7, 0xbe, 0x32,
0x6b, 0x03, 0xbd, 0x3b, 0xb0, 0x9c, 0x49, 0xed, 0xa3, 0x55, 0x53, 0xf3, 0x15, 0xaa, 0xa9, 0x9b, 0xb2, 0x5b, 0x51, 0x8e, 0xe7, 0xa4, 0x23, 0x45, 0x76, 0xe3, 0xe0, 0xb5, 0x81, 0xde, 0x1d, 0x58,
0x39, 0x76, 0xe7, 0xff, 0xc1, 0x6a, 0x30, 0x3a, 0x65, 0x19, 0x0f, 0x85, 0xc7, 0x2f, 0x8c, 0xbe, 0xce, 0xa4, 0xf6, 0xd1, 0xaa, 0xa9, 0xf9, 0x0a, 0xd5, 0xd4, 0xcd, 0x1c, 0xbb, 0xf3, 0x73, 0xb0,
0x54, 0xa8, 0x2b, 0x16, 0x5c, 0xd8, 0xe2, 0x77, 0x61, 0x45, 0x95, 0x2e, 0x0d, 0xa6, 0xea, 0x48, 0x1a, 0x8c, 0x4e, 0x59, 0xc6, 0x43, 0xe1, 0xf1, 0x0b, 0xa3, 0x2f, 0x15, 0xea, 0x8a, 0x05, 0x17,
0x29, 0xc0, 0x88, 0x48, 0xff, 0x5a, 0x27, 0x59, 0xdd, 0x3b, 0x3c, 0x9f, 0x23, 0xf6, 0xe9, 0x1a, 0xb6, 0xf8, 0x1d, 0x58, 0x51, 0x05, 0x4e, 0x83, 0xa9, 0xfa, 0x56, 0x0a, 0x30, 0x22, 0xd2, 0xbf,
0xa5, 0xd3, 0x7d, 0x55, 0x25, 0x3c, 0x47, 0x3a, 0xac, 0x50, 0xa9, 0x67, 0x09, 0x54, 0x09, 0x6a, 0xd4, 0xa9, 0x58, 0xf7, 0x0e, 0xcf, 0xe7, 0x88, 0x7d, 0xba, 0x46, 0xe9, 0x74, 0x5f, 0x51, 0x69,
0x97, 0xa5, 0xcd, 0x37, 0x61, 0x29, 0xfd, 0xd1, 0x1c, 0x2c, 0x7e, 0x1c, 0x9f, 0x26, 0xe1, 0x50, 0xd1, 0x91, 0x0e, 0x2b, 0x54, 0x82, 0x5a, 0x02, 0x55, 0x1a, 0xdb, 0x65, 0x69, 0xf3, 0x4d, 0x58,
0xa4, 0x1f, 0x27, 0x6c, 0x92, 0xe8, 0x06, 0x02, 0xfc, 0x8d, 0x16, 0x5d, 0xd4, 0xc6, 0x52, 0xae, 0x4a, 0x7f, 0x38, 0x07, 0x8b, 0x1f, 0xc5, 0xa7, 0x49, 0x38, 0x14, 0x49, 0xca, 0x09, 0x9b, 0x24,
0xf2, 0x82, 0x7a, 0x88, 0xd6, 0x2d, 0x2b, 0x9a, 0x66, 0xa4, 0xa4, 0x58, 0x10, 0xf4, 0x0f, 0x33, 0xba, 0xcd, 0x00, 0x7f, 0xa3, 0x45, 0x17, 0x15, 0xb4, 0x94, 0xab, 0xec, 0xa1, 0x1e, 0xa2, 0x75,
0xbb, 0x0f, 0x48, 0x8d, 0x8a, 0x0e, 0x8c, 0x79, 0xab, 0x03, 0x43, 0x24, 0xab, 0x65, 0xd9, 0x4f, 0xcb, 0x8a, 0xd6, 0x1a, 0x29, 0x29, 0x16, 0x04, 0xfd, 0xc3, 0xcc, 0xee, 0x16, 0x52, 0xa3, 0xa2,
0xb0, 0x73, 0xc9, 0xd7, 0x43, 0xe1, 0xc7, 0x66, 0x4c, 0x06, 0xbd, 0xc2, 0x4e, 0x2e, 0x2a, 0x3f, 0x4f, 0x63, 0xde, 0xea, 0xd3, 0x10, 0x29, 0x6d, 0x59, 0x1c, 0x14, 0xec, 0x5c, 0xf2, 0xf5, 0x50,
0xd6, 0x06, 0xa2, 0x2d, 0x95, 0x1f, 0x48, 0x1c, 0xa9, 0x6b, 0x6c, 0x10, 0xfa, 0x16, 0xe5, 0x56, 0xf8, 0xb1, 0x19, 0x93, 0x41, 0xaf, 0xb0, 0x93, 0x8b, 0xca, 0x8f, 0xb5, 0x81, 0x68, 0x4b, 0xe5,
0xa2, 0x96, 0xbc, 0xe2, 0x12, 0x18, 0x15, 0xd2, 0x88, 0x19, 0xbd, 0x21, 0xcf, 0x00, 0xb2, 0x29, 0x07, 0x12, 0x47, 0xea, 0x1a, 0x1b, 0x84, 0xbe, 0x45, 0xb9, 0xe1, 0xa8, 0x25, 0xaf, 0xb8, 0x04,
0xa8, 0x0c, 0xb7, 0xbc, 0x60, 0x59, 0xbe, 0x54, 0x23, 0xe1, 0x83, 0x04, 0x51, 0x74, 0x14, 0x0c, 0x46, 0x85, 0x34, 0x62, 0x46, 0x6f, 0xc8, 0x33, 0x80, 0x6c, 0x1d, 0x2a, 0xc3, 0x2d, 0x2f, 0x58,
0x9f, 0x89, 0x06, 0x2f, 0x51, 0xad, 0x6c, 0xf9, 0x2e, 0x10, 0x77, 0x3d, 0x8c, 0xf8, 0xe9, 0x40, 0x16, 0x39, 0xd5, 0x48, 0xf8, 0x20, 0x41, 0x14, 0x1d, 0x05, 0xc3, 0x67, 0xa2, 0x0d, 0x4c, 0xd4,
0x91, 0xe8, 0xca, 0x6a, 0xa3, 0x05, 0xa2, 0x9f, 0x03, 0xb9, 0x3b, 0x1a, 0xa9, 0x1b, 0x32, 0x31, 0x34, 0x5b, 0xbe, 0x0b, 0xc4, 0x5d, 0x0f, 0x23, 0x7e, 0x3a, 0x50, 0x24, 0xba, 0xb2, 0x26, 0x69,
0x42, 0xc1, 0x5b, 0xcf, 0xe1, 0x6d, 0xcd, 0x19, 0x1b, 0xb5, 0x67, 0xa4, 0x0f, 0xa1, 0x7d, 0x60, 0x81, 0xe8, 0x67, 0x40, 0xee, 0x8e, 0x46, 0xea, 0x86, 0x4c, 0x8c, 0x50, 0xf0, 0xd6, 0x73, 0x78,
0xf5, 0x65, 0x89, 0xcb, 0xd4, 0x1d, 0x59, 0x4a, 0x00, 0x2c, 0x88, 0xb5, 0x60, 0xc3, 0x5e, 0x90, 0x5b, 0x73, 0xc6, 0x46, 0xed, 0x19, 0xe9, 0x43, 0x68, 0x1f, 0x58, 0xdd, 0x5b, 0xe2, 0x32, 0x75,
0xfe, 0x0a, 0x90, 0xfd, 0x30, 0xe7, 0x66, 0x7f, 0x26, 0x54, 0x34, 0x19, 0x2f, 0x2b, 0x54, 0x54, 0xdf, 0x96, 0x12, 0x00, 0x0b, 0x62, 0x2d, 0xd8, 0xb0, 0x17, 0xa4, 0xbf, 0x04, 0x64, 0x3f, 0xcc,
0x30, 0x11, 0x2a, 0xde, 0x95, 0x45, 0xcf, 0xf2, 0xc1, 0x6e, 0xc0, 0x52, 0x28, 0x41, 0x5a, 0x0f, 0xb9, 0xd9, 0x9f, 0x09, 0x15, 0x4d, 0xc6, 0xcb, 0x0a, 0x15, 0x15, 0x4c, 0x84, 0x8a, 0x77, 0x65,
0x2f, 0x2b, 0x01, 0xd6, 0x98, 0x66, 0x1e, 0x1d, 0x0a, 0x05, 0x74, 0xd4, 0xfc, 0x0f, 0x3c, 0x58, 0x69, 0xb4, 0x7c, 0xb0, 0x1b, 0xb0, 0x14, 0x4a, 0x90, 0xd6, 0xc3, 0xcb, 0x4a, 0x80, 0x35, 0xa6,
0x54, 0x47, 0x43, 0x73, 0xe8, 0x74, 0xa4, 0xc9, 0x83, 0x39, 0xb0, 0xfa, 0x8e, 0xa0, 0xaa, 0xd4, 0x99, 0x47, 0x87, 0x42, 0x01, 0x1d, 0x35, 0xff, 0x7d, 0x0f, 0x16, 0xd5, 0xd1, 0xd0, 0x1c, 0x3a,
0xcd, 0xd5, 0x49, 0x1d, 0x81, 0x66, 0x1a, 0xf0, 0x13, 0xe1, 0x41, 0xb7, 0x7c, 0xf1, 0x5b, 0x47, 0x7d, 0x6b, 0xf2, 0x60, 0x0e, 0xac, 0xbe, 0x6f, 0xa8, 0x2a, 0x75, 0x73, 0x75, 0x52, 0x47, 0xa0,
0x4a, 0xf3, 0x26, 0x52, 0xd2, 0x05, 0x5e, 0xb5, 0x29, 0x93, 0xb5, 0xbc, 0x27, 0x0b, 0xbc, 0x05, 0x99, 0x06, 0xfc, 0x44, 0x78, 0xd0, 0x2d, 0x5f, 0xfc, 0xd6, 0x91, 0xd2, 0xbc, 0x89, 0x94, 0x74,
0xb8, 0xe0, 0x81, 0xda, 0x60, 0x99, 0x07, 0x0a, 0xd5, 0x37, 0xf3, 0xb4, 0x0f, 0xbd, 0x07, 0x2c, 0x19, 0x58, 0x6d, 0xca, 0x64, 0x2d, 0xef, 0xc9, 0x32, 0x70, 0x01, 0x2e, 0x78, 0xa0, 0x36, 0x58,
0x62, 0x9c, 0xdd, 0x8d, 0xa2, 0x32, 0xfd, 0xcb, 0x70, 0xa9, 0x66, 0x4e, 0x59, 0xca, 0x47, 0xb0, 0xe6, 0x81, 0x42, 0xf5, 0xcd, 0x3c, 0xed, 0x43, 0xef, 0x01, 0x8b, 0x18, 0x67, 0x77, 0xa3, 0xa8,
0xf6, 0x80, 0x1d, 0x4d, 0xc7, 0xfb, 0xec, 0xb4, 0x28, 0x18, 0x10, 0x68, 0xe6, 0x27, 0xc9, 0x99, 0x4c, 0xff, 0x32, 0x5c, 0xaa, 0x99, 0x53, 0x96, 0xf2, 0x11, 0xac, 0x3d, 0x60, 0x47, 0xd3, 0xf1,
0xba, 0x2f, 0xf1, 0x9b, 0xbc, 0x05, 0x10, 0x21, 0xce, 0x20, 0x4f, 0xd9, 0x50, 0xb7, 0xb3, 0x08, 0x3e, 0x3b, 0x2d, 0xca, 0x0a, 0x04, 0x9a, 0xf9, 0x49, 0x72, 0xa6, 0xee, 0x4b, 0xfc, 0x26, 0x5f,
0xc8, 0x61, 0xca, 0x86, 0xf4, 0x7d, 0x20, 0x36, 0x1d, 0x75, 0x04, 0x7c, 0x8d, 0xd3, 0xa3, 0x41, 0x02, 0x88, 0x10, 0x67, 0x90, 0xa7, 0x6c, 0xa8, 0x9b, 0x5e, 0x04, 0xe4, 0x30, 0x65, 0x43, 0xfa,
0x3e, 0xcb, 0x39, 0x9b, 0xe8, 0x3e, 0x1d, 0x1b, 0x44, 0xdf, 0x85, 0xce, 0x41, 0x30, 0xf3, 0xd9, 0x1e, 0x10, 0x9b, 0x8e, 0x3a, 0x02, 0xbe, 0xc6, 0xe9, 0xd1, 0x20, 0x9f, 0xe5, 0x9c, 0x4d, 0x74,
0x97, 0xaa, 0xd1, 0x0f, 0x03, 0xb2, 0x60, 0x86, 0xe2, 0x69, 0x02, 0x32, 0x31, 0x4d, 0xff, 0xb1, 0x37, 0x8f, 0x0d, 0xa2, 0xef, 0x40, 0xe7, 0x20, 0x98, 0xf9, 0xec, 0x0b, 0xd5, 0x0e, 0x88, 0x01,
0x01, 0x0b, 0x12, 0x13, 0xa9, 0x8e, 0x58, 0xce, 0xc3, 0x58, 0xa6, 0xd5, 0x15, 0x55, 0x0b, 0x54, 0x59, 0x30, 0x43, 0xf1, 0x34, 0x01, 0x99, 0x98, 0xa6, 0x7f, 0xd7, 0x80, 0x05, 0x89, 0x89, 0x54,
0xb9, 0xef, 0x46, 0xcd, 0x7d, 0x2b, 0x17, 0x49, 0x97, 0xfe, 0xd5, 0xc5, 0x3a, 0x30, 0x11, 0x6f, 0x47, 0x2c, 0xe7, 0x61, 0x2c, 0xd3, 0xea, 0x8a, 0xaa, 0x05, 0xaa, 0xdc, 0x77, 0xa3, 0xe6, 0xbe,
0x86, 0x13, 0x26, 0xfb, 0x3d, 0x9b, 0x2a, 0xde, 0xd4, 0x80, 0x52, 0xe4, 0x5b, 0xbc, 0x79, 0xb9, 0x95, 0x8b, 0xa4, 0x1b, 0x04, 0xd4, 0xc5, 0x3a, 0x30, 0x11, 0x6f, 0x86, 0x13, 0x26, 0xbb, 0x42,
0x3f, 0x2d, 0x88, 0xca, 0x2c, 0xd8, 0xa0, 0x5a, 0xcd, 0xb2, 0x28, 0x3b, 0xfb, 0x2a, 0x9a, 0xa5, 0x9b, 0x2a, 0xde, 0xd4, 0x80, 0x52, 0xe4, 0x5b, 0xbc, 0x79, 0xb9, 0x3f, 0x2d, 0x88, 0xca, 0x2c,
0xa2, 0x41, 0x96, 0xde, 0x40, 0x83, 0x48, 0xbf, 0xc9, 0xd1, 0x20, 0x04, 0x56, 0x1f, 0x31, 0xe6, 0xd8, 0xa0, 0x5a, 0xcd, 0xb2, 0x28, 0xfb, 0xff, 0x2a, 0x9a, 0xa5, 0xa2, 0x41, 0x96, 0xde, 0x40,
0xb3, 0x34, 0xc9, 0x4c, 0xb7, 0xe4, 0x0f, 0x3d, 0x58, 0x55, 0x16, 0xc1, 0xcc, 0x91, 0xeb, 0x8e, 0x83, 0x48, 0xbf, 0xc9, 0xd1, 0x20, 0x04, 0x56, 0x1f, 0x31, 0xe6, 0xb3, 0x34, 0xc9, 0x4c, 0x4f,
0xf9, 0xf0, 0xea, 0x32, 0xad, 0x6f, 0x43, 0x57, 0x04, 0x50, 0x18, 0x1d, 0x89, 0x68, 0x49, 0xe5, 0xe5, 0x0f, 0x3c, 0x58, 0x55, 0x16, 0xc1, 0xcc, 0x91, 0xeb, 0x8e, 0xf9, 0xf0, 0xea, 0x32, 0xad,
0x14, 0x1c, 0x20, 0xee, 0x49, 0xe7, 0x0e, 0x27, 0x61, 0xa4, 0x18, 0x6c, 0x83, 0xd0, 0xd4, 0xe9, 0x6f, 0x41, 0x57, 0x04, 0x50, 0x18, 0x1d, 0x89, 0x68, 0x49, 0xe5, 0x14, 0x1c, 0x20, 0xee, 0x49,
0x00, 0x4b, 0xb0, 0xd7, 0xf3, 0xcd, 0x98, 0x1e, 0xc0, 0x9a, 0xb5, 0x5f, 0x25, 0x50, 0x77, 0x40, 0xe7, 0x0e, 0x27, 0x61, 0xa4, 0x18, 0x6c, 0x83, 0xd0, 0xd4, 0xe9, 0x00, 0x4b, 0xb0, 0xd7, 0xf3,
0xd7, 0x1b, 0x65, 0x8a, 0x40, 0xbe, 0x8b, 0x2d, 0xd7, 0xb8, 0x15, 0x9f, 0x39, 0xc8, 0xf4, 0xef, 0xcd, 0x98, 0x1e, 0xc0, 0x9a, 0xb5, 0x5f, 0x25, 0x50, 0x77, 0x40, 0x57, 0x25, 0x65, 0x8a, 0x40,
0x3d, 0xc1, 0x02, 0xe5, 0x43, 0x99, 0xfe, 0xa4, 0x05, 0xe9, 0xd6, 0x48, 0x69, 0xdf, 0xbb, 0xe0, 0xbe, 0x8b, 0x2d, 0xd7, 0xb8, 0x15, 0x9f, 0x39, 0xc8, 0xf4, 0x9f, 0x3d, 0x58, 0x97, 0x86, 0x5e,
0xab, 0x31, 0xf9, 0xfa, 0x1b, 0x7a, 0x26, 0xa6, 0xae, 0x77, 0x0e, 0x6f, 0xe6, 0xea, 0x78, 0xf3, 0xb9, 0x51, 0xa6, 0x91, 0x69, 0x41, 0x7a, 0x36, 0x52, 0xe0, 0xf7, 0x2e, 0xf8, 0x6a, 0x4c, 0xbe,
0x8a, 0x93, 0xdf, 0x5b, 0x84, 0xf9, 0x7c, 0x98, 0xa4, 0x8c, 0xae, 0x0b, 0x16, 0xe8, 0xfd, 0x4a, 0xf6, 0x86, 0xce, 0x89, 0x29, 0x00, 0x9e, 0xc3, 0x9e, 0xb9, 0x3a, 0xf6, 0xbc, 0xe2, 0xf0, 0x75,
0x16, 0xec, 0xfe, 0x9b, 0x07, 0xcb, 0x32, 0xb9, 0x26, 0xfb, 0xaa, 0x59, 0x46, 0x30, 0x76, 0xb2, 0x01, 0xf0, 0x7c, 0x6d, 0x00, 0x7c, 0x6f, 0x11, 0xe6, 0xf3, 0x61, 0x92, 0x32, 0xba, 0x09, 0x1b,
0xda, 0xb5, 0x89, 0x71, 0x1d, 0xab, 0x6d, 0xdf, 0xfd, 0xcb, 0xb5, 0x73, 0xda, 0x6f, 0xfe, 0xfe, 0xee, 0xe1, 0x24, 0xcb, 0x76, 0xff, 0xc5, 0x83, 0x65, 0x99, 0x8c, 0x93, 0xdd, 0xda, 0x2c, 0x23,
0x4f, 0xfe, 0xe3, 0xcf, 0x1a, 0x17, 0xe9, 0xea, 0xce, 0xe9, 0xed, 0x1d, 0xa1, 0xe2, 0xd8, 0x99, 0x18, 0x6b, 0x59, 0x4d, 0xe0, 0xc4, 0xb8, 0x9a, 0xd5, 0x66, 0xf2, 0xfe, 0xe5, 0xda, 0x39, 0xed,
0xc0, 0xf8, 0xd0, 0xbb, 0x81, 0xab, 0xd8, 0x9d, 0xdc, 0x66, 0x95, 0x9a, 0x8e, 0x70, 0xb3, 0x4a, 0x67, 0x7f, 0xef, 0xc7, 0xff, 0xfe, 0x27, 0x8d, 0x8b, 0x74, 0x75, 0xe7, 0xf4, 0xf6, 0x8e, 0x50,
0x6d, 0xeb, 0xb7, 0xb3, 0xca, 0x54, 0x60, 0x98, 0x55, 0x76, 0x7f, 0xda, 0x87, 0x96, 0x09, 0xf2, 0x89, 0xec, 0x4c, 0x60, 0x7c, 0xe0, 0xdd, 0xc0, 0x55, 0xec, 0xfe, 0x70, 0xb3, 0x4a, 0x4d, 0x9f,
0xc8, 0x77, 0xa1, 0xeb, 0x24, 0x12, 0x89, 0x26, 0x5c, 0x97, 0x9a, 0xec, 0x5f, 0xa9, 0x9f, 0x54, 0xb9, 0x59, 0xa5, 0xb6, 0xa1, 0xdc, 0x59, 0x65, 0x2a, 0x30, 0xcc, 0x2a, 0xbb, 0xff, 0xdd, 0x87,
0xcb, 0x5e, 0x15, 0xcb, 0xf6, 0xc8, 0x26, 0x2e, 0xab, 0xb2, 0x77, 0x3b, 0x22, 0xc3, 0x2a, 0x7b, 0x96, 0x09, 0x0a, 0xc9, 0x77, 0xa0, 0xeb, 0x24, 0x1e, 0x89, 0x26, 0x5c, 0x97, 0xca, 0xec, 0x5f,
0x0e, 0x9e, 0xc1, 0xb2, 0x9b, 0xfc, 0x23, 0x57, 0xdc, 0xdb, 0x2e, 0xad, 0xf6, 0xd6, 0x39, 0xb3, 0xa9, 0x9f, 0x54, 0xcb, 0x5e, 0x15, 0xcb, 0xf6, 0xc8, 0x26, 0x2e, 0xab, 0xb2, 0x7d, 0x3b, 0x22,
0x6a, 0xb9, 0x2b, 0x62, 0xb9, 0x4d, 0xb2, 0x61, 0x2f, 0x67, 0x82, 0x2f, 0x26, 0xba, 0x44, 0xec, 0x23, 0x2b, 0x3b, 0x19, 0x9e, 0xc1, 0xb2, 0x9b, 0x2c, 0x24, 0x57, 0x5c, 0xd1, 0x28, 0xad, 0xf6,
0x16, 0x6f, 0xa2, 0xe9, 0xd5, 0xb7, 0x7e, 0xf7, 0x2f, 0x55, 0xdb, 0xb9, 0x55, 0xff, 0x37, 0xed, 0xa5, 0x73, 0x66, 0xd5, 0x72, 0x57, 0xc4, 0x72, 0x9b, 0x64, 0xc3, 0x5e, 0xce, 0x04, 0x6b, 0x4c,
0x89, 0xa5, 0x08, 0x11, 0x0c, 0xb5, 0x3b, 0xbc, 0xc9, 0x77, 0xa0, 0x65, 0x1a, 0x44, 0xc9, 0x96, 0xf4, 0x9e, 0xd8, 0x8d, 0xe3, 0x44, 0xd3, 0xab, 0x6f, 0x28, 0xef, 0x5f, 0xaa, 0x36, 0x89, 0xab,
0xd5, 0x95, 0x6b, 0x77, 0xad, 0xf6, 0x7b, 0xd5, 0x89, 0xba, 0xab, 0xb2, 0x29, 0xa3, 0x40, 0xec, 0xae, 0x72, 0xda, 0x13, 0x4b, 0x11, 0x22, 0x18, 0x6a, 0xf7, 0x8d, 0x93, 0x6f, 0x43, 0xcb, 0xb4,
0xc3, 0x45, 0x65, 0x71, 0x8f, 0xd8, 0xcf, 0x72, 0x92, 0x9a, 0xc6, 0xf4, 0x5b, 0x1e, 0xb9, 0x03, 0x9d, 0x92, 0x2d, 0xab, 0xd7, 0xd7, 0xee, 0x85, 0xed, 0xf7, 0xaa, 0x13, 0x75, 0x57, 0x65, 0x53,
0x4b, 0xba, 0xef, 0x96, 0x6c, 0xd6, 0xf7, 0x0f, 0xf7, 0xb7, 0x2a, 0x70, 0xa5, 0x17, 0xee, 0x02, 0x46, 0x81, 0xd8, 0x87, 0x8b, 0xca, 0x42, 0x1f, 0xb1, 0x9f, 0xe4, 0x24, 0x35, 0xed, 0xee, 0xb7,
0x14, 0x2d, 0xa2, 0xa4, 0x77, 0x5e, 0x27, 0xab, 0x61, 0x62, 0x4d, 0x3f, 0xe9, 0x58, 0x74, 0xc8, 0x3c, 0x72, 0x07, 0x96, 0x74, 0x37, 0x2f, 0xd9, 0xac, 0xef, 0x4a, 0xee, 0x6f, 0x55, 0xe0, 0x4a,
0xba, 0x1d, 0xa8, 0xe4, 0x2b, 0x05, 0x7e, 0x6d, 0x6f, 0xea, 0x2b, 0x08, 0xd2, 0x4d, 0xc1, 0xbb, 0x8f, 0xdc, 0x05, 0x28, 0x1a, 0x4f, 0x49, 0xef, 0xbc, 0xfe, 0x58, 0xc3, 0xc4, 0x9a, 0x2e, 0xd5,
0x55, 0xb2, 0x8c, 0xbc, 0x8b, 0xd9, 0x99, 0xee, 0x97, 0x7a, 0x00, 0x6d, 0xab, 0xed, 0x94, 0x68, 0xb1, 0xe8, 0xbb, 0x75, 0xfb, 0x5a, 0xc9, 0x97, 0x0b, 0xfc, 0xda, 0x8e, 0xd7, 0x57, 0x10, 0xa4,
0x0a, 0xd5, 0x96, 0xd5, 0x7e, 0xbf, 0x6e, 0x4a, 0x6d, 0xf7, 0x37, 0xa0, 0xeb, 0xf4, 0x8f, 0x9a, 0x9b, 0x82, 0x77, 0xab, 0x64, 0x19, 0x79, 0x17, 0xb3, 0x33, 0xdd, 0x85, 0xf5, 0x00, 0xda, 0x56,
0x97, 0x51, 0xd7, 0x9d, 0x6a, 0x5e, 0x46, 0x7d, 0xcb, 0xe9, 0xb7, 0xa1, 0x6d, 0x75, 0x7b, 0x12, 0x33, 0x2b, 0xd1, 0x14, 0xaa, 0x8d, 0xb0, 0xfd, 0x7e, 0xdd, 0x94, 0xda, 0xee, 0xaf, 0x41, 0xd7,
0xab, 0x42, 0x5d, 0xea, 0xe6, 0x34, 0x3b, 0xaa, 0x69, 0x0e, 0xa5, 0x1b, 0xe2, 0xbc, 0xcb, 0xb4, 0xe9, 0x4a, 0x35, 0x2f, 0xa3, 0xae, 0xe7, 0xd5, 0xbc, 0x8c, 0xfa, 0x46, 0xd6, 0x6f, 0x41, 0xdb,
0x85, 0xe7, 0x15, 0x4d, 0x43, 0x28, 0x24, 0xdf, 0x85, 0x65, 0xb7, 0xcb, 0xd3, 0xbc, 0xaa, 0xda, 0xea, 0x21, 0x25, 0x56, 0x45, 0xbb, 0xd4, 0x23, 0x6a, 0x76, 0x54, 0xd3, 0x72, 0x4a, 0x37, 0xc4,
0x7e, 0x51, 0xf3, 0xaa, 0xce, 0x69, 0x0d, 0x55, 0x02, 0x79, 0x63, 0xdd, 0x2c, 0xb2, 0xf3, 0x42, 0x79, 0x97, 0x69, 0x0b, 0xcf, 0x2b, 0x5a, 0x91, 0x50, 0x48, 0xbe, 0x03, 0xcb, 0x6e, 0xef, 0xa8,
0xa5, 0x38, 0x5f, 0x92, 0x6f, 0xa1, 0xea, 0x50, 0x5d, 0x5c, 0xa4, 0xe8, 0x7a, 0x75, 0x7b, 0xbd, 0x79, 0x55, 0xb5, 0x5d, 0xa8, 0xe6, 0x55, 0x9d, 0xd3, 0x70, 0xaa, 0x04, 0xf2, 0xc6, 0xba, 0x59,
0x8c, 0xb4, 0x57, 0x1a, 0xbe, 0xe8, 0x9a, 0x20, 0xde, 0x26, 0xc5, 0x09, 0xc8, 0x27, 0xb0, 0xa8, 0x64, 0xe7, 0x85, 0x4a, 0x89, 0xbe, 0x24, 0xdf, 0x44, 0xd5, 0xa1, 0x7a, 0xc3, 0x48, 0xd1, 0x4b,
0xba, 0xb9, 0xc8, 0xc5, 0x42, 0xaa, 0xad, 0x84, 0x50, 0x7f, 0xb3, 0x0c, 0x56, 0xc4, 0xd6, 0x05, 0xeb, 0x76, 0x90, 0x19, 0x69, 0xaf, 0xb4, 0x91, 0xd1, 0x35, 0x41, 0xbc, 0x4d, 0x8a, 0x13, 0x90,
0xb1, 0x2e, 0x69, 0x23, 0xb1, 0x31, 0xe3, 0x21, 0xd2, 0x88, 0x61, 0xa5, 0x54, 0x95, 0x32, 0x8f, 0x8f, 0x61, 0x51, 0xf5, 0x88, 0x91, 0x8b, 0x85, 0x54, 0x5b, 0x09, 0xa4, 0xfe, 0x66, 0x19, 0xac,
0xa5, 0xbe, 0xa6, 0xdd, 0xbf, 0xfa, 0xea, 0x62, 0x96, 0xab, 0x66, 0xb4, 0x7a, 0xd9, 0xd1, 0x2d, 0x88, 0xad, 0x0b, 0x62, 0x5d, 0xd2, 0x46, 0x62, 0x63, 0xc6, 0x43, 0xa4, 0x11, 0xc3, 0x4a, 0xa9,
0x08, 0xbf, 0x0d, 0x1d, 0xbb, 0x79, 0xd0, 0xe8, 0xec, 0x9a, 0x46, 0x43, 0xa3, 0xb3, 0xeb, 0xba, 0x8a, 0x65, 0x1e, 0x4b, 0x7d, 0x0d, 0xbc, 0x7f, 0xf5, 0xd5, 0xc5, 0x2f, 0x57, 0xcd, 0x68, 0xf5,
0x0d, 0xf5, 0xe5, 0x92, 0x8e, 0xbd, 0x0c, 0xf9, 0x36, 0xac, 0x58, 0xf5, 0xcf, 0xc3, 0x59, 0x3c, 0xb2, 0xa3, 0x5b, 0x16, 0x7e, 0x13, 0x3a, 0x76, 0x4b, 0xa2, 0xd1, 0xd9, 0x35, 0xed, 0x8b, 0x46,
0x34, 0xc2, 0x53, 0xed, 0x43, 0xe9, 0xd7, 0x19, 0x4f, 0xba, 0x25, 0x08, 0xaf, 0x51, 0x87, 0x30, 0x67, 0xd7, 0xf5, 0x30, 0xea, 0xcb, 0x25, 0x1d, 0x7b, 0x19, 0xf2, 0x2d, 0x58, 0xb1, 0xea, 0xa5,
0x0a, 0xce, 0x7d, 0x68, 0xdb, 0xb5, 0xd5, 0x57, 0xd0, 0xdd, 0xb2, 0xa6, 0xec, 0xe6, 0x8d, 0x5b, 0x87, 0xb3, 0x78, 0x68, 0x84, 0xa7, 0xda, 0xdd, 0xd2, 0xaf, 0xb3, 0xb4, 0x74, 0x4b, 0x10, 0x5e,
0x1e, 0xf9, 0x0b, 0x0f, 0x3a, 0x76, 0x87, 0x13, 0x71, 0xb2, 0x2a, 0x25, 0x3a, 0x3d, 0x7b, 0xce, 0xa3, 0x0e, 0x61, 0x14, 0x9c, 0xfb, 0xd0, 0xb6, 0x6b, 0xb1, 0xaf, 0xa0, 0xbb, 0x65, 0x4d, 0xd9,
0x26, 0x44, 0x9f, 0x88, 0x4d, 0xee, 0xdd, 0x78, 0xe4, 0x30, 0xf9, 0x85, 0xe3, 0x14, 0xdd, 0xb4, 0xcd, 0x1e, 0xb7, 0x3c, 0xf2, 0x67, 0x1e, 0x74, 0xec, 0xbe, 0x29, 0xe2, 0x64, 0x61, 0x4a, 0x74,
0xff, 0x93, 0xf1, 0xb2, 0x3c, 0x69, 0xf7, 0xe9, 0xbc, 0xbc, 0xe5, 0x91, 0x0f, 0xe5, 0x3f, 0x6f, 0x7a, 0xf6, 0x9c, 0x4d, 0x88, 0x3e, 0x11, 0x9b, 0xdc, 0xbb, 0xf1, 0xc8, 0x61, 0xf2, 0x0b, 0xc7,
0x74, 0x80, 0x42, 0x2c, 0xc5, 0x56, 0x66, 0x97, 0xfd, 0x77, 0x96, 0x6d, 0xef, 0x96, 0x47, 0x7e, 0x89, 0xba, 0x69, 0xff, 0xd3, 0xe3, 0x65, 0x79, 0xd2, 0xee, 0xfe, 0x79, 0x79, 0xcb, 0x23, 0x1f,
0x47, 0xfe, 0x0d, 0x43, 0x7d, 0x2b, 0xb8, 0xfe, 0xa6, 0xdf, 0xd3, 0xb7, 0xc5, 0x49, 0xae, 0xd2, 0xc8, 0xff, 0xf3, 0xe8, 0x80, 0x86, 0x58, 0x8a, 0xad, 0xcc, 0x2e, 0xfb, 0x4f, 0x32, 0xdb, 0xde,
0x4b, 0xce, 0x49, 0xca, 0x9a, 0xfd, 0x00, 0xa0, 0x88, 0x36, 0x49, 0x29, 0xf4, 0x32, 0x3a, 0xaf, 0x2d, 0x8f, 0xfc, 0x96, 0xfc, 0x73, 0x87, 0xfa, 0x56, 0x70, 0xfd, 0x4d, 0xbf, 0xa7, 0x6f, 0x89,
0x1a, 0x90, 0xba, 0xb7, 0xa9, 0x23, 0x34, 0xa9, 0x06, 0x3a, 0x56, 0x9c, 0x97, 0x9b, 0xeb, 0xac, 0x93, 0x5c, 0xa5, 0x97, 0x9c, 0x93, 0x94, 0x35, 0xfb, 0x01, 0x40, 0x11, 0x9d, 0x92, 0x52, 0xa8,
0x46, 0x8d, 0xfd, 0x7e, 0xdd, 0x94, 0xa2, 0xff, 0x55, 0x41, 0xff, 0x2d, 0x72, 0xd9, 0xa6, 0xbf, 0x66, 0x74, 0x5e, 0x35, 0x80, 0x75, 0x6f, 0x53, 0x47, 0x74, 0x52, 0x0d, 0x74, 0xac, 0xb8, 0x30,
0xf3, 0xc2, 0x8e, 0x32, 0x5f, 0x92, 0xcf, 0xa1, 0xbb, 0x9f, 0x24, 0xcf, 0xa6, 0xa9, 0x49, 0x68, 0x37, 0xd7, 0x59, 0x8d, 0x32, 0xfb, 0xfd, 0xba, 0x29, 0x45, 0xff, 0x2b, 0x82, 0xfe, 0x97, 0xc8,
0xb8, 0x71, 0x13, 0x46, 0xba, 0xfd, 0xd2, 0xa1, 0xe8, 0x75, 0x41, 0xf9, 0x32, 0xb9, 0xe4, 0x52, 0x65, 0x9b, 0xfe, 0xce, 0x0b, 0x3b, 0x2a, 0x7d, 0x49, 0x3e, 0x83, 0xee, 0x7e, 0x92, 0x3c, 0x9b,
0x2e, 0x62, 0xdf, 0x97, 0x24, 0x80, 0x35, 0x63, 0xef, 0xcc, 0x41, 0xfa, 0x2e, 0x1d, 0x3b, 0x04, 0xa6, 0x26, 0x01, 0xe2, 0xc6, 0x59, 0x18, 0x19, 0xf7, 0x4b, 0x87, 0xa2, 0xd7, 0x05, 0xe5, 0xcb,
0xad, 0xac, 0xe1, 0x78, 0x20, 0x66, 0x8d, 0x5c, 0xd3, 0xbc, 0xe5, 0x91, 0x03, 0xe8, 0x3c, 0x60, 0xe4, 0x92, 0x4b, 0xb9, 0x88, 0x95, 0x5f, 0x92, 0x00, 0xd6, 0x8c, 0xbd, 0x33, 0x07, 0xe9, 0xbb,
0xc3, 0x64, 0xc4, 0x54, 0xa8, 0xb3, 0x5e, 0xec, 0xdc, 0xc4, 0x48, 0xfd, 0xae, 0x03, 0x74, 0x35, 0x74, 0xec, 0x90, 0xb5, 0xb2, 0x86, 0xe3, 0x81, 0x98, 0x35, 0x72, 0x4d, 0xf3, 0x96, 0x47, 0x0e,
0x40, 0x1a, 0xcc, 0x32, 0xf6, 0xe5, 0xce, 0x0b, 0x15, 0x44, 0xbd, 0xd4, 0x1a, 0x40, 0x07, 0x7e, 0xa0, 0xf3, 0x80, 0x0d, 0x93, 0x11, 0x53, 0xa1, 0xd1, 0x7a, 0xb1, 0x73, 0x13, 0x53, 0xf5, 0xbb,
0x8e, 0x06, 0x28, 0x45, 0x8a, 0x8e, 0x06, 0xa8, 0x44, 0x8a, 0x8e, 0x06, 0xd0, 0x81, 0x27, 0x89, 0x0e, 0xd0, 0xd5, 0x00, 0x69, 0x30, 0xcb, 0xd8, 0x17, 0x3b, 0x2f, 0x54, 0xd0, 0xf5, 0x52, 0x6b,
0x30, 0x7e, 0x2c, 0x05, 0x97, 0xc6, 0x6a, 0x9e, 0x17, 0x92, 0xf6, 0xaf, 0x9d, 0x8f, 0xe0, 0xae, 0x00, 0x1d, 0x28, 0x3a, 0x1a, 0xa0, 0x14, 0x59, 0x3a, 0x1a, 0xa0, 0x12, 0x59, 0x3a, 0x1a, 0x40,
0x76, 0xc3, 0x5d, 0xed, 0x10, 0xba, 0x0f, 0x98, 0x64, 0x96, 0xac, 0x12, 0xf4, 0x5d, 0x95, 0x62, 0x07, 0xaa, 0x24, 0xc2, 0x78, 0xb3, 0x14, 0x8c, 0x1a, 0xab, 0x79, 0x5e, 0x08, 0xdb, 0xbf, 0x76,
0x57, 0x14, 0xca, 0xea, 0x46, 0xcc, 0xb9, 0x2a, 0x5e, 0xa4, 0xe8, 0xc9, 0x77, 0xa0, 0xfd, 0x98, 0x3e, 0x82, 0xbb, 0xda, 0x0d, 0x77, 0xb5, 0x43, 0xe8, 0x3e, 0x60, 0x92, 0x59, 0xb2, 0xaa, 0xd0,
0x71, 0x5d, 0x16, 0x30, 0xbe, 0x47, 0xa9, 0x4e, 0xd0, 0xaf, 0xa9, 0x2a, 0xd0, 0x6b, 0x82, 0x5a, 0x77, 0x55, 0x8a, 0x5d, 0x81, 0x28, 0xab, 0x1b, 0x31, 0xe7, 0xaa, 0x78, 0x91, 0xd2, 0x27, 0xdf,
0x9f, 0xf4, 0x0c, 0xb5, 0x1d, 0x36, 0x1a, 0x33, 0xf9, 0xf8, 0x07, 0xe1, 0xe8, 0x25, 0xf9, 0x4d, 0x86, 0xf6, 0x63, 0xc6, 0x75, 0x19, 0xc1, 0xf8, 0x1e, 0xa5, 0xba, 0x42, 0xbf, 0xa6, 0x0a, 0x41,
0x41, 0xdc, 0x54, 0x12, 0x37, 0xad, 0x6c, 0xb2, 0x4d, 0x7c, 0xa5, 0x04, 0xaf, 0xa3, 0x1c, 0x27, 0xaf, 0x09, 0x6a, 0x7d, 0xd2, 0x33, 0xd4, 0x76, 0xd8, 0x68, 0xcc, 0xe4, 0xe3, 0x1f, 0x84, 0xa3,
0x23, 0x66, 0x19, 0xbb, 0x18, 0xda, 0x56, 0xd9, 0xd8, 0x3c, 0xa8, 0x6a, 0x2d, 0xda, 0x3c, 0xa8, 0x97, 0xe4, 0xd7, 0x05, 0x71, 0x53, 0x79, 0xdc, 0xb4, 0xb2, 0xcf, 0x36, 0xf1, 0x95, 0x12, 0xbc,
0x9a, 0x2a, 0x33, 0xdd, 0x16, 0xeb, 0x50, 0x72, 0xad, 0x58, 0x47, 0x56, 0x96, 0x8b, 0x95, 0x76, 0x8e, 0x72, 0x9c, 0x8c, 0x98, 0x65, 0xec, 0x62, 0x68, 0x5b, 0x65, 0x66, 0xf3, 0xa0, 0xaa, 0xb5,
0x5e, 0x04, 0x13, 0xfe, 0x92, 0x7c, 0x21, 0x5a, 0x9d, 0xed, 0xd2, 0x47, 0xe1, 0xfb, 0x94, 0xab, 0x6b, 0xf3, 0xa0, 0x6a, 0xaa, 0xd2, 0x74, 0x5b, 0xac, 0x43, 0xc9, 0xb5, 0x62, 0x1d, 0x59, 0x89,
0x24, 0x86, 0x59, 0xd6, 0x94, 0xeb, 0x0f, 0xc9, 0xa5, 0x84, 0x4d, 0xfc, 0x3a, 0xc0, 0x21, 0x4f, 0x2e, 0x56, 0xda, 0x79, 0x11, 0x4c, 0xf8, 0x4b, 0xf2, 0xb9, 0x68, 0xa0, 0xb6, 0x4b, 0x25, 0x85,
0xd2, 0x07, 0x01, 0x9b, 0x24, 0x71, 0xa1, 0xc9, 0x8a, 0xf4, 0x7e, 0xa1, 0xc9, 0xac, 0x1c, 0x3f, 0xef, 0x53, 0xae, 0xaa, 0x18, 0x66, 0x59, 0x53, 0xae, 0x3f, 0x24, 0x97, 0x12, 0x36, 0xf1, 0x6b,
0xf9, 0xc2, 0xf2, 0x3e, 0x9d, 0xca, 0x91, 0x16, 0xae, 0x73, 0x2b, 0x00, 0x86, 0x21, 0x35, 0x55, 0x00, 0x87, 0x3c, 0x49, 0x1f, 0x04, 0x6c, 0x92, 0xc4, 0x85, 0x26, 0x2b, 0xca, 0x01, 0x85, 0x26,
0x80, 0x5b, 0x1e, 0xfa, 0x92, 0x45, 0x2a, 0xc3, 0xf8, 0x92, 0x95, 0x2c, 0x89, 0x51, 0x83, 0x35, 0xb3, 0x6a, 0x02, 0xe4, 0x73, 0xcb, 0xfb, 0x74, 0x2a, 0x4d, 0x5a, 0xb8, 0xce, 0xad, 0x18, 0x18,
0x79, 0x8f, 0x03, 0x68, 0x15, 0xf1, 0xb4, 0x36, 0x4f, 0xe5, 0xe8, 0xdb, 0xd8, 0x9b, 0x4a, 0x98, 0x86, 0xd4, 0x54, 0x0d, 0x6e, 0x79, 0xe8, 0x4b, 0x16, 0xa9, 0x0f, 0xe3, 0x4b, 0x56, 0xb2, 0x2a,
0x4b, 0x57, 0x05, 0xab, 0x80, 0x2c, 0x21, 0xab, 0x44, 0xe5, 0xfb, 0x29, 0x80, 0xdc, 0xe0, 0x23, 0x46, 0x0d, 0xd6, 0xe4, 0x49, 0x0e, 0xa0, 0x55, 0xc4, 0xdf, 0xda, 0x3c, 0x95, 0xa3, 0x75, 0x63,
0x1c, 0x59, 0x24, 0x9d, 0x68, 0xd6, 0x26, 0xe9, 0x86, 0x8d, 0xda, 0x1d, 0xa1, 0x86, 0xe4, 0x87, 0x6f, 0x2a, 0x61, 0x31, 0x5d, 0x15, 0xac, 0x02, 0xb2, 0x84, 0xac, 0x12, 0x95, 0xf2, 0x21, 0xac,
0xde, 0x8d, 0xa3, 0x05, 0xf1, 0x0f, 0xe1, 0xaf, 0xfd, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x6e, 0xcb, 0x0d, 0x1a, 0xe3, 0x29, 0x12, 0xdc, 0xfa, 0x24, 0x35, 0x61, 0xb0, 0x79, 0xcd, 0x75, 0x51,
0x8c, 0x0d, 0xd9, 0x53, 0x3c, 0x00, 0x00, 0xa4, 0xf6, 0x4e, 0xa8, 0x59, 0xe1, 0x03, 0xef, 0xc6, 0xd1, 0x82, 0xf8, 0x1b, 0xf2, 0x57, 0xff,
0x27, 0x00, 0x00, 0xff, 0xff, 0x33, 0xf0, 0xc8, 0xda, 0xb8, 0x3c, 0x00, 0x00,
} }

View File

@ -523,15 +523,15 @@ func request_Lightning_FeeReport_0(ctx context.Context, marshaler runtime.Marsha
} }
func request_Lightning_UpdateFees_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { func request_Lightning_UpdateChannelPolicy_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq FeeUpdateRequest var protoReq PolicyUpdateRequest
var metadata runtime.ServerMetadata var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil { if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
} }
msg, err := client.UpdateFees(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) msg, err := client.UpdateChannelPolicy(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err return msg, metadata, err
} }
@ -1452,7 +1452,7 @@ func RegisterLightningHandler(ctx context.Context, mux *runtime.ServeMux, conn *
}) })
mux.Handle("POST", pattern_Lightning_UpdateFees_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { mux.Handle("POST", pattern_Lightning_UpdateChannelPolicy_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
defer cancel() defer cancel()
if cn, ok := w.(http.CloseNotifier); ok { if cn, ok := w.(http.CloseNotifier); ok {
@ -1470,14 +1470,14 @@ func RegisterLightningHandler(ctx context.Context, mux *runtime.ServeMux, conn *
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
} }
resp, md, err := request_Lightning_UpdateFees_0(rctx, inboundMarshaler, client, req, pathParams) resp, md, err := request_Lightning_UpdateChannelPolicy_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md) ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil { if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return return
} }
forward_Lightning_UpdateFees_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) forward_Lightning_UpdateChannelPolicy_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
}) })
@ -1539,7 +1539,7 @@ var (
pattern_Lightning_FeeReport_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "fees"}, "")) pattern_Lightning_FeeReport_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "fees"}, ""))
pattern_Lightning_UpdateFees_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "fees"}, "")) pattern_Lightning_UpdateChannelPolicy_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "fees"}, ""))
) )
var ( var (
@ -1597,5 +1597,5 @@ var (
forward_Lightning_FeeReport_0 = runtime.ForwardResponseMessage forward_Lightning_FeeReport_0 = runtime.ForwardResponseMessage
forward_Lightning_UpdateFees_0 = runtime.ForwardResponseMessage forward_Lightning_UpdateChannelPolicy_0 = runtime.ForwardResponseMessage
) )

View File

@ -441,11 +441,11 @@ service Lightning {
}; };
} }
/** lncli: `updatefees` /** lncli: `updatechanpolicy`
UpdateFees allows the caller to update the fee schedule for all channels UpdateChannelPolicy allows the caller to update the fee schedule and
globally, or a particular channel. channel policies for all channels globally, or a particular channel.
*/ */
rpc UpdateFees(FeeUpdateRequest) returns (FeeUpdateResponse) { rpc UpdateChannelPolicy(PolicyUpdateRequest) returns (PolicyUpdateResponse) {
option (google.api.http) = { option (google.api.http) = {
post: "/v1/fees" post: "/v1/fees"
body: "*" body: "*"
@ -877,6 +877,9 @@ message OpenChannelRequest {
/// Whether this channel should be private, not announced to the greater network. /// Whether this channel should be private, not announced to the greater network.
bool private = 8 [json_name = "private"]; bool private = 8 [json_name = "private"];
/// The minimum value in millisatoshi we will require for incoming HTLCs on the channel.
int64 min_htlc_msat = 9 [json_name = "min_htlc_msat"];
} }
message OpenStatusUpdate { message OpenStatusUpdate {
oneof update { oneof update {
@ -1401,12 +1404,12 @@ message FeeReportResponse {
repeated ChannelFeeReport channel_fees = 1 [json_name = "channel_fees"]; repeated ChannelFeeReport channel_fees = 1 [json_name = "channel_fees"];
} }
message FeeUpdateRequest { message PolicyUpdateRequest {
oneof scope { oneof scope {
/// If set, then this fee update applies to all currently active channels. /// If set, then this update applies to all currently active channels.
bool global = 1 [json_name = "global"] ; bool global = 1 [json_name = "global"] ;
/// If set, this fee update will target a specific channel. /// If set, this update will target a specific channel.
ChannelPoint chan_point = 2 [json_name = "chan_point"]; ChannelPoint chan_point = 2 [json_name = "chan_point"];
} }
@ -1415,6 +1418,9 @@ message FeeUpdateRequest {
/// The effective fee rate in milli-satoshis. The precision of this value goes up to 6 decimal places, so 1e-6. /// The effective fee rate in milli-satoshis. The precision of this value goes up to 6 decimal places, so 1e-6.
double fee_rate = 4 [json_name = "fee_rate"]; double fee_rate = 4 [json_name = "fee_rate"];
/// The required timelock delta for HTLCs forwarded over the channel.
uint32 time_lock_delta = 5 [json_name = "time_lock_delta"];
} }
message FeeUpdateResponse { message PolicyUpdateResponse {
} }

View File

@ -222,13 +222,13 @@
] ]
}, },
"post": { "post": {
"summary": "* lncli: `updatefees`\nUpdateFees allows the caller to update the fee schedule for all channels\nglobally, or a particular channel.", "summary": "* lncli: `updatechanpolicy`\nUpdateChannelPolicy allows the caller to update the fee schedule and\nchannel policies for all channels globally, or a particular channel.",
"operationId": "UpdateFees", "operationId": "UpdateChannelPolicy",
"responses": { "responses": {
"200": { "200": {
"description": "", "description": "",
"schema": { "schema": {
"$ref": "#/definitions/lnrpcFeeUpdateResponse" "$ref": "#/definitions/lnrpcPolicyUpdateResponse"
} }
} }
}, },
@ -238,7 +238,7 @@
"in": "body", "in": "body",
"required": true, "required": true,
"schema": { "schema": {
"$ref": "#/definitions/lnrpcFeeUpdateRequest" "$ref": "#/definitions/lnrpcPolicyUpdateRequest"
} }
} }
], ],
@ -1166,33 +1166,6 @@
} }
} }
}, },
"lnrpcFeeUpdateRequest": {
"type": "object",
"properties": {
"global": {
"type": "boolean",
"format": "boolean",
"description": "/ If set, then this fee update applies to all currently active channels."
},
"chan_point": {
"$ref": "#/definitions/lnrpcChannelPoint",
"description": "/ If set, this fee update will target a specific channel."
},
"base_fee_msat": {
"type": "string",
"format": "int64",
"description": "/ The base fee charged regardless of the number of milli-satoshis sent."
},
"fee_rate": {
"type": "number",
"format": "double",
"description": "/ The effective fee rate in milli-satoshis. The precision of this value goes up to 6 decimal places, so 1e-6."
}
}
},
"lnrpcFeeUpdateResponse": {
"type": "object"
},
"lnrpcGetInfoResponse": { "lnrpcGetInfoResponse": {
"type": "object", "type": "object",
"properties": { "properties": {
@ -1616,6 +1589,11 @@
"type": "boolean", "type": "boolean",
"format": "boolean", "format": "boolean",
"description": "/ Whether this channel should be private, not announced to the greater network." "description": "/ Whether this channel should be private, not announced to the greater network."
},
"min_htlc_msat": {
"type": "string",
"format": "int64",
"description": "/ The minimum value in millisatoshi we will require for incoming HTLCs on the channel."
} }
} }
}, },
@ -1826,6 +1804,38 @@
} }
} }
}, },
"lnrpcPolicyUpdateRequest": {
"type": "object",
"properties": {
"global": {
"type": "boolean",
"format": "boolean",
"description": "/ If set, then this update applies to all currently active channels."
},
"chan_point": {
"$ref": "#/definitions/lnrpcChannelPoint",
"description": "/ If set, this update will target a specific channel."
},
"base_fee_msat": {
"type": "string",
"format": "int64",
"description": "/ The base fee charged regardless of the number of milli-satoshis sent."
},
"fee_rate": {
"type": "number",
"format": "double",
"description": "/ The effective fee rate in milli-satoshis. The precision of this value goes up to 6 decimal places, so 1e-6."
},
"time_lock_delta": {
"type": "integer",
"format": "int64",
"description": "/ The required timelock delta for HTLCs forwarded over the channel."
}
}
},
"lnrpcPolicyUpdateResponse": {
"type": "object"
},
"lnrpcQueryRoutesResponse": { "lnrpcQueryRoutesResponse": {
"type": "object", "type": "object",
"properties": { "properties": {

View File

@ -122,7 +122,8 @@ func (cfg nodeConfig) genArgs() []string {
args = append(args, "--nobootstrap") args = append(args, "--nobootstrap")
args = append(args, "--noencryptwallet") args = append(args, "--noencryptwallet")
args = append(args, "--debuglevel=debug") args = append(args, "--debuglevel=debug")
args = append(args, "--defaultchanconfs=1") args = append(args, "--bitcoin.defaultchanconfs=1")
args = append(args, "--bitcoin.defaultremotedelay=4")
args = append(args, fmt.Sprintf("--bitcoin.rpchost=%v", cfg.RPCConfig.Host)) args = append(args, fmt.Sprintf("--bitcoin.rpchost=%v", cfg.RPCConfig.Host))
args = append(args, fmt.Sprintf("--bitcoin.rpcuser=%v", cfg.RPCConfig.User)) args = append(args, fmt.Sprintf("--bitcoin.rpcuser=%v", cfg.RPCConfig.User))
args = append(args, fmt.Sprintf("--bitcoin.rpcpass=%v", cfg.RPCConfig.Pass)) args = append(args, fmt.Sprintf("--bitcoin.rpcpass=%v", cfg.RPCConfig.Pass))

View File

@ -89,8 +89,12 @@ func (c *chanController) OpenChannel(target *btcec.PublicKey,
if err != nil { if err != nil {
return err return err
} }
// TODO(halseth): make configurable?
minHtlc := lnwire.NewMSatFromSatoshis(1)
updateStream, errChan := c.server.OpenChannel(-1, target, amt, 0, updateStream, errChan := c.server.OpenChannel(-1, target, amt, 0,
feePerWeight, false) minHtlc, feePerWeight, false)
select { select {
case err := <-errChan: case err := <-errChan:

View File

@ -94,6 +94,18 @@ type FeeSchema struct {
FeeRate uint32 FeeRate uint32
} }
// ChannelPolicy holds the parameters that determine the policy we enforce
// when fowarding payments on a channel. These parameters are communicated
// to the rest of the network in ChannelUpdate messages.
type ChannelPolicy struct {
// FeeSchema holds the fee configuration for a channel.
FeeSchema
// TimeLockDelta is the required HTLC timelock delta to be used
// when forwarding payments.
TimeLockDelta uint32
}
// Config defines the configuration for the ChannelRouter. ALL elements within // Config defines the configuration for the ChannelRouter. ALL elements within
// the configuration MUST be non-nil for the ChannelRouter to carry out its // the configuration MUST be non-nil for the ChannelRouter to carry out its
// duties. // duties.

View File

@ -551,6 +551,7 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
localFundingAmt := btcutil.Amount(in.LocalFundingAmount) localFundingAmt := btcutil.Amount(in.LocalFundingAmount)
remoteInitialBalance := btcutil.Amount(in.PushSat) remoteInitialBalance := btcutil.Amount(in.PushSat)
minHtlc := lnwire.MilliSatoshi(in.MinHtlcMsat)
// Ensure that the initial balance of the remote party (if pushing // Ensure that the initial balance of the remote party (if pushing
// satoshis) does not exceed the amount the local party has requested // satoshis) does not exceed the amount the local party has requested
@ -627,7 +628,7 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
updateChan, errChan := r.server.OpenChannel( updateChan, errChan := r.server.OpenChannel(
in.TargetPeerId, nodePubKey, localFundingAmt, in.TargetPeerId, nodePubKey, localFundingAmt,
lnwire.NewMSatFromSatoshis(remoteInitialBalance), lnwire.NewMSatFromSatoshis(remoteInitialBalance),
feePerByte, in.Private, minHtlc, feePerByte, in.Private,
) )
var outpoint wire.OutPoint var outpoint wire.OutPoint
@ -722,6 +723,7 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context,
localFundingAmt := btcutil.Amount(in.LocalFundingAmount) localFundingAmt := btcutil.Amount(in.LocalFundingAmount)
remoteInitialBalance := btcutil.Amount(in.PushSat) remoteInitialBalance := btcutil.Amount(in.PushSat)
minHtlc := lnwire.MilliSatoshi(in.MinHtlcMsat)
// Ensure that the initial balance of the remote party (if pushing // Ensure that the initial balance of the remote party (if pushing
// satoshis) does not exceed the amount the local party has requested // satoshis) does not exceed the amount the local party has requested
@ -746,7 +748,7 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context,
updateChan, errChan := r.server.OpenChannel( updateChan, errChan := r.server.OpenChannel(
in.TargetPeerId, nodepubKey, localFundingAmt, in.TargetPeerId, nodepubKey, localFundingAmt,
lnwire.NewMSatFromSatoshis(remoteInitialBalance), lnwire.NewMSatFromSatoshis(remoteInitialBalance),
feePerByte, in.Private, minHtlc, feePerByte, in.Private,
) )
select { select {
@ -2083,7 +2085,7 @@ func (r *rpcServer) AddInvoice(ctx context.Context,
zpay32.CLTVExpiry(invoice.CltvExpiry)) zpay32.CLTVExpiry(invoice.CltvExpiry))
default: default:
// TODO(roasbeef): assumes set delta between versions // TODO(roasbeef): assumes set delta between versions
defaultDelta := defaultBitcoinForwardingPolicy.TimeLockDelta defaultDelta := cfg.Bitcoin.TimeLockDelta
options = append(options, zpay32.CLTVExpiry(uint64(defaultDelta))) options = append(options, zpay32.CLTVExpiry(uint64(defaultDelta)))
} }
@ -3192,13 +3194,13 @@ func (r *rpcServer) FeeReport(ctx context.Context,
// 0.000001, or 0.0001%. // 0.000001, or 0.0001%.
const minFeeRate = 1e-6 const minFeeRate = 1e-6
// UpdateFees allows the caller to update the fee schedule for all channels // UpdateChannelPolicy allows the caller to update the channel forwarding policy
// globally, or a particular channel. // for all channels globally, or a particular channel.
func (r *rpcServer) UpdateFees(ctx context.Context, func (r *rpcServer) UpdateChannelPolicy(ctx context.Context,
req *lnrpc.FeeUpdateRequest) (*lnrpc.FeeUpdateResponse, error) { req *lnrpc.PolicyUpdateRequest) (*lnrpc.PolicyUpdateResponse, error) {
if r.authSvc != nil { if r.authSvc != nil {
if err := macaroons.ValidateMacaroon(ctx, "udpatefees", if err := macaroons.ValidateMacaroon(ctx, "updatechannelpolicy",
r.authSvc); err != nil { r.authSvc); err != nil {
return nil, err return nil, err
} }
@ -3208,11 +3210,11 @@ func (r *rpcServer) UpdateFees(ctx context.Context,
switch scope := req.Scope.(type) { switch scope := req.Scope.(type) {
// If the request is targeting all active channels, then we don't need // If the request is targeting all active channels, then we don't need
// target any channels by their channel point. // target any channels by their channel point.
case *lnrpc.FeeUpdateRequest_Global: case *lnrpc.PolicyUpdateRequest_Global:
// Otherwise, we're targeting an individual channel by its channel // Otherwise, we're targeting an individual channel by its channel
// point. // point.
case *lnrpc.FeeUpdateRequest_ChanPoint: case *lnrpc.PolicyUpdateRequest_ChanPoint:
txid, err := chainhash.NewHash(scope.ChanPoint.FundingTxid) txid, err := chainhash.NewHash(scope.ChanPoint.FundingTxid)
if err != nil { if err != nil {
return nil, err return nil, err
@ -3226,12 +3228,19 @@ func (r *rpcServer) UpdateFees(ctx context.Context,
} }
// As a sanity check, we'll ensure that the passed fee rate is below // As a sanity check, we'll ensure that the passed fee rate is below
// 1e-6, or the lowest allowed fee rate. // 1e-6, or the lowest allowed fee rate, and that the passed timelock
// is large enough.
if req.FeeRate < minFeeRate { if req.FeeRate < minFeeRate {
return nil, fmt.Errorf("fee rate of %v is too small, min fee "+ return nil, fmt.Errorf("fee rate of %v is too small, min fee "+
"rate is %v", req.FeeRate, minFeeRate) "rate is %v", req.FeeRate, minFeeRate)
} }
if req.TimeLockDelta < minTimeLockDelta {
return nil, fmt.Errorf("time lock delta of %v is too small, "+
"minimum supported is %v", req.TimeLockDelta,
minTimeLockDelta)
}
// We'll also need to convert the floating point fee rate we accept // We'll also need to convert the floating point fee rate we accept
// over RPC to the fixed point rate that we use within the protocol. We // over RPC to the fixed point rate that we use within the protocol. We
// do this by multiplying the passed fee rate by the fee base. This // do this by multiplying the passed fee rate by the fee base. This
@ -3244,16 +3253,21 @@ func (r *rpcServer) UpdateFees(ctx context.Context,
FeeRate: feeRateFixed, FeeRate: feeRateFixed,
} }
rpcsLog.Tracef("[updatefees] updating fee schedule base_fee=%v, "+ chanPolicy := routing.ChannelPolicy{
"rate_float=%v, rate_fixed=%v, targets=%v", FeeSchema: feeSchema,
req.BaseFeeMsat, req.FeeRate, feeRateFixed, TimeLockDelta: req.TimeLockDelta,
}
rpcsLog.Tracef("[updatechanpolicy] updating channel policy base_fee=%v, "+
"rate_float=%v, rate_fixed=%v, time_lock_delta: %v, targets=%v",
req.BaseFeeMsat, req.FeeRate, feeRateFixed, req.TimeLockDelta,
spew.Sdump(targetChans)) spew.Sdump(targetChans))
// With the scope resolved, we'll now send this to the // With the scope resolved, we'll now send this to the
// AuthenticatedGossiper so it can propagate the new fee schema for out // AuthenticatedGossiper so it can propagate the new policy for our
// target channel(s). // target channel(s).
err := r.server.authGossiper.PropagateFeeUpdate( err := r.server.authGossiper.PropagateChanPolicyUpdate(
feeSchema, targetChans..., chanPolicy, targetChans...,
) )
if err != nil { if err != nil {
return nil, err return nil, err
@ -3265,8 +3279,9 @@ func (r *rpcServer) UpdateFees(ctx context.Context,
// We create a partially policy as the logic won't overwrite a valid // We create a partially policy as the logic won't overwrite a valid
// sub-policy with a "nil" one. // sub-policy with a "nil" one.
p := htlcswitch.ForwardingPolicy{ p := htlcswitch.ForwardingPolicy{
BaseFee: baseFeeMsat, BaseFee: baseFeeMsat,
FeeRate: lnwire.MilliSatoshi(feeRateFixed), FeeRate: lnwire.MilliSatoshi(feeRateFixed),
TimeLockDelta: req.TimeLockDelta,
} }
err = r.server.htlcSwitch.UpdateForwardingPolicies(p, targetChans...) err = r.server.htlcSwitch.UpdateForwardingPolicies(p, targetChans...)
if err != nil { if err != nil {
@ -3276,5 +3291,5 @@ func (r *rpcServer) UpdateFees(ctx context.Context,
rpcsLog.Warnf("Unable to update link fees: %v", err) rpcsLog.Warnf("Unable to update link fees: %v", err)
} }
return &lnrpc.FeeUpdateResponse{}, nil return &lnrpc.PolicyUpdateResponse{}, nil
} }

View File

@ -1495,6 +1495,8 @@ type openChanReq struct {
private bool private bool
minHtlc lnwire.MilliSatoshi
// TODO(roasbeef): add ability to specify channel constraints as well // TODO(roasbeef): add ability to specify channel constraints as well
updates chan *lnrpc.OpenStatusUpdate updates chan *lnrpc.OpenStatusUpdate
@ -1612,6 +1614,7 @@ func (s *server) DisconnectPeer(pubKey *btcec.PublicKey) error {
// NOTE: This function is safe for concurrent access. // NOTE: This function is safe for concurrent access.
func (s *server) OpenChannel(peerID int32, nodeKey *btcec.PublicKey, func (s *server) OpenChannel(peerID int32, nodeKey *btcec.PublicKey,
localAmt btcutil.Amount, pushAmt lnwire.MilliSatoshi, localAmt btcutil.Amount, pushAmt lnwire.MilliSatoshi,
minHtlc lnwire.MilliSatoshi,
fundingFeePerByte btcutil.Amount, fundingFeePerByte btcutil.Amount,
private bool) (chan *lnrpc.OpenStatusUpdate, chan error) { private bool) (chan *lnrpc.OpenStatusUpdate, chan error) {
@ -1674,6 +1677,7 @@ func (s *server) OpenChannel(peerID int32, nodeKey *btcec.PublicKey,
fundingFeePerWeight: fundingFeePerWeight, fundingFeePerWeight: fundingFeePerWeight,
pushAmt: pushAmt, pushAmt: pushAmt,
private: private, private: private,
minHtlc: minHtlc,
updates: updateChan, updates: updateChan,
err: errChan, err: errChan,
} }