Merge pull request #787 from Roasbeef/switch-revenue-stats

lnrpc+htlcswitch+channeldb: add new forwarding log time series and query interface
This commit is contained in:
Olaoluwa Osuntokun 2018-03-06 15:19:07 -05:00 committed by GitHub
commit 614e0b3816
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 2066 additions and 372 deletions

View File

@ -85,4 +85,8 @@ var (
// ErrNoClosedChannels is returned when a node is queries for all the
// channels it has closed, but it hasn't yet closed any channels.
ErrNoClosedChannels = fmt.Errorf("no channel have been closed yet")
// ErrNoForwardingEvents is returned in the case that a query fails due
// to the log not having any recorded events.
ErrNoForwardingEvents = fmt.Errorf("no recorded forwarding events")
)

274
channeldb/forwarding_log.go Normal file
View File

@ -0,0 +1,274 @@
package channeldb
import (
"bytes"
"io"
"sort"
"time"
"github.com/boltdb/bolt"
"github.com/lightningnetwork/lnd/lnwire"
)
var (
// forwardingLogBucket is the bucket that we'll use to store the
// forwarding log. The forwarding log contains a time series database
// of the forwarding history of a lightning daemon. Each key within the
// bucket is a timestamp (in nano seconds since the unix epoch), and
// the value a slice of a forwarding event for that timestamp.
forwardingLogBucket = []byte("circuit-fwd-log")
)
const (
// forwardingEventSize is the size of a forwarding event. The breakdown
// is as follows:
//
// * 8 byte incoming chan ID || 8 byte outgoing chan ID || 8 byte value in
// || 8 byte value out
//
// From the value in and value out, callers can easily compute the
// total fee extract from a forwarding event.
forwardingEventSize = 32
// MaxResponseEvents is the max number of forwarding events that will
// be returned by a single query response. This size was selected to
// safely remain under gRPC's 4MiB message size response limit. As each
// full forwarding event (including the timestamp) is 40 bytes, we can
// safely return 50k entries in a single response.
MaxResponseEvents = 50000
)
// ForwardingLog returns an instance of the ForwardingLog object backed by the
// target database instance.
func (d *DB) ForwardingLog() *ForwardingLog {
return &ForwardingLog{
db: d,
}
}
// ForwardingLog is a time series database that logs the fulfilment of payment
// circuits by a lightning network daemon. The log contains a series of
// forwarding events which map a timestamp to a forwarding event. A forwarding
// event describes which channels were used to create+settle a circuit, and the
// amount involved. Subtracting the outgoing amount from the incoming amount
// reveals the fee charged for the forwarding service.
type ForwardingLog struct {
db *DB
}
// ForwardingEvent is an event in the forwarding log's time series. Each
// forwarding event logs the creation and tear-down of a payment circuit. A
// circuit is created once an incoming HTLC has been fully forwarded, and
// destroyed once the payment has been settled.
type ForwardingEvent struct {
// Timestamp is the settlement time of this payment circuit.
Timestamp time.Time
// IncomingChanID is the incoming channel ID of the payment circuit.
IncomingChanID lnwire.ShortChannelID
// OutgoingChanID is the outgoing channel ID of the payment circuit.
OutgoingChanID lnwire.ShortChannelID
// AmtIn is the amount of the incoming HTLC. Subtracting this from the
// outgoing amount gives the total fees of this payment circuit.
AmtIn lnwire.MilliSatoshi
// AmtOut is the amount of the outgoing HTLC. Subtracting the incoming
// amount from this gives the total fees for this payment circuit.
AmtOut lnwire.MilliSatoshi
}
// encodeForwardingEvent writes out the target forwarding event to the passed
// io.Writer, using the expected DB format. Note that the timestamp isn't
// serialized as this will be the key value within the bucket.
func encodeForwardingEvent(w io.Writer, f *ForwardingEvent) error {
return writeElements(
w, f.IncomingChanID, f.OutgoingChanID, f.AmtIn, f.AmtOut,
)
}
// decodeForwardingEvent attempts to decode the raw bytes of a serialized
// forwarding event into the target ForwardingEvent. Note that the timestamp
// won't be decoded, as the caller is expected to set this due to the bucket
// structure of the forwarding log.
func decodeForwardingEvent(r io.Reader, f *ForwardingEvent) error {
return readElements(
r, &f.IncomingChanID, &f.OutgoingChanID, &f.AmtIn, &f.AmtOut,
)
}
// AddForwardingEvents adds a series of forwarding events to the database.
// Before inserting, the set of events will be sorted according to their
// timestamp. This ensures that all writes to disk are sequential.
func (f *ForwardingLog) AddForwardingEvents(events []ForwardingEvent) error {
// Before we create the database transaction, we'll ensure that the set
// of forwarding events are properly sorted according to their
// timestamp.
sort.Slice(events, func(i, j int) bool {
return events[i].Timestamp.Before(events[j].Timestamp)
})
var timestamp [8]byte
return f.db.Batch(func(tx *bolt.Tx) error {
// First, we'll fetch the bucket that stores our time series
// log.
logBucket, err := tx.CreateBucketIfNotExists(
forwardingLogBucket,
)
if err != nil {
return err
}
// With the bucket obtained, we can now begin to write out the
// series of events.
for _, event := range events {
var eventBytes [forwardingEventSize]byte
eventBuf := bytes.NewBuffer(eventBytes[0:0:forwardingEventSize])
// First, we'll serialize this timestamp into our
// timestamp buffer.
byteOrder.PutUint64(
timestamp[:], uint64(event.Timestamp.UnixNano()),
)
// With the key encoded, we'll then encode the event
// into our buffer, then write it out to disk.
err := encodeForwardingEvent(eventBuf, &event)
if err != nil {
return err
}
err = logBucket.Put(timestamp[:], eventBuf.Bytes())
if err != nil {
return err
}
}
return nil
})
}
// ForwardingEventQuery represents a query to the forwarding log payment
// circuit time series database. The query allows a caller to retrieve all
// records for a particular time slice, offset in that time slice, limiting the
// total number of responses returned.
type ForwardingEventQuery struct {
// StartTime is the start time of the time slice.
StartTime time.Time
// EndTime is the end time of the time slice.
EndTime time.Time
// IndexOffset is the offset within the time slice to start at. This
// can be used to start the response at a particular record.
IndexOffset uint32
// NumMaxEvents is the max number of events to return.
NumMaxEvents uint32
}
// ForwardingLogTimeSlice is the response to a forwarding query. It includes
// the original query, the set events that match the query, and an integer
// which represents the offset index of the last item in the set of retuned
// events. This integer allows callers to resume their query using this offset
// in the event that the query's response exceeds the max number of returnable
// events.
type ForwardingLogTimeSlice struct {
ForwardingEventQuery
// ForwardingEvents is the set of events in our time series that answer
// the query embedded above.
ForwardingEvents []ForwardingEvent
// LastIndexOffset is the index of the last element in the set of
// returned ForwardingEvents above. Callers can use this to resume
// their query in the event that the time slice has too many events to
// fit into a single response.
LastIndexOffset uint32
}
// Query allows a caller to query the forwarding event time series for a
// particular time slice. The caller can control the precise time as well as
// the number of events to be returned.
//
// TODO(roasbeef): rename?
func (f *ForwardingLog) Query(q ForwardingEventQuery) (ForwardingLogTimeSlice, error) {
resp := ForwardingLogTimeSlice{
ForwardingEventQuery: q,
}
// If the user provided an index offset, then we'll not know how many
// records we need to skip. We'll also keep track of the record offset
// as that's part of the final return value.
recordsToSkip := q.IndexOffset
recordOffset := q.IndexOffset
err := f.db.View(func(tx *bolt.Tx) error {
// If the bucket wasn't found, then there aren't any events to
// be returned.
logBucket := tx.Bucket(forwardingLogBucket)
if logBucket == nil {
return ErrNoForwardingEvents
}
// We'll be using a cursor to seek into the database, so we'll
// populate byte slices that represent the start of the key
// space we're interested in, and the end.
var startTime, endTime [8]byte
byteOrder.PutUint64(startTime[:], uint64(q.StartTime.UnixNano()))
byteOrder.PutUint64(endTime[:], uint64(q.EndTime.UnixNano()))
// If we know that a set of log events exists, then we'll begin
// our seek through the log in order to satisfy the query.
// We'll continue until either we reach the end of the range,
// or reach our max number of events.
logCursor := logBucket.Cursor()
timestamp, events := logCursor.Seek(startTime[:])
for ; timestamp != nil && bytes.Compare(timestamp, endTime[:]) <= 0; timestamp, events = logCursor.Next() {
// If our current return payload exceeds the max number
// of events, then we'll exit now.
if uint32(len(resp.ForwardingEvents)) >= q.NumMaxEvents {
return nil
}
// If we're not yet past the user defined offset, then
// we'll continue to seek forward.
if recordsToSkip > 0 {
recordsToSkip--
continue
}
currentTime := time.Unix(
0, int64(byteOrder.Uint64(timestamp)),
)
// At this point, we've skipped enough records to start
// to collate our query. For each record, we'll
// increment the final record offset so the querier can
// utilize pagination to seek further.
readBuf := bytes.NewReader(events)
for readBuf.Len() != 0 {
var event ForwardingEvent
err := decodeForwardingEvent(readBuf, &event)
if err != nil {
return err
}
event.Timestamp = currentTime
resp.ForwardingEvents = append(resp.ForwardingEvents, event)
recordOffset++
}
}
return nil
})
if err != nil && err != ErrNoForwardingEvents {
return ForwardingLogTimeSlice{}, err
}
resp.LastIndexOffset = recordOffset
return resp, nil
}

View File

@ -0,0 +1,265 @@
package channeldb
import (
"math/rand"
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/lightningnetwork/lnd/lnwire"
"time"
)
// TestForwardingLogBasicStorageAndQuery tests that we're able to store and
// then query for items that have previously been added to the event log.
func TestForwardingLogBasicStorageAndQuery(t *testing.T) {
t.Parallel()
// First, we'll set up a test database, and use that to instantiate the
// forwarding event log that we'll be using for the duration of the
// test.
db, cleanUp, err := makeTestDB()
defer cleanUp()
if err != nil {
t.Fatalf("unable to make test db: %v", err)
}
log := ForwardingLog{
db: db,
}
initialTime := time.Unix(1234, 0)
timestamp := time.Unix(1234, 0)
// We'll create 100 random events, which each event being spaced 10
// minutes after the prior event.
numEvents := 100
events := make([]ForwardingEvent, numEvents)
for i := 0; i < numEvents; i++ {
events[i] = ForwardingEvent{
Timestamp: timestamp,
IncomingChanID: lnwire.NewShortChanIDFromInt(uint64(rand.Int63())),
OutgoingChanID: lnwire.NewShortChanIDFromInt(uint64(rand.Int63())),
AmtIn: lnwire.MilliSatoshi(rand.Int63()),
AmtOut: lnwire.MilliSatoshi(rand.Int63()),
}
timestamp = timestamp.Add(time.Minute * 10)
}
// Now that all of our set of events constructed, we'll add them to the
// database in a batch manner.
if err := log.AddForwardingEvents(events); err != nil {
t.Fatalf("unable to add events: %v", err)
}
// With our events added we'll now construct a basic query to retrieve
// all of the events.
eventQuery := ForwardingEventQuery{
StartTime: initialTime,
EndTime: timestamp,
IndexOffset: 0,
NumMaxEvents: 1000,
}
timeSlice, err := log.Query(eventQuery)
if err != nil {
t.Fatalf("unable to query for events: %v", err)
}
// The set of returned events should match identically, as they should
// be returned in sorted order.
if !reflect.DeepEqual(events, timeSlice.ForwardingEvents) {
t.Fatalf("event mismatch: expected %v vs %v",
spew.Sdump(events), spew.Sdump(timeSlice.ForwardingEvents))
}
// The offset index of the final entry should be numEvents, so the
// number of total events we've written.
if timeSlice.LastIndexOffset != uint32(numEvents) {
t.Fatalf("wrong final offset: expected %v, got %v",
timeSlice.LastIndexOffset, numEvents)
}
}
// TestForwardingLogQueryOptions tests that the query offset works properly. So
// if we add a series of events, then we should be able to seek within the
// timeslice accordingly. This exercises the index offset and num max event
// field in the query, and also the last index offset field int he response.
func TestForwardingLogQueryOptions(t *testing.T) {
t.Parallel()
// First, we'll set up a test database, and use that to instantiate the
// forwarding event log that we'll be using for the duration of the
// test.
db, cleanUp, err := makeTestDB()
defer cleanUp()
if err != nil {
t.Fatalf("unable to make test db: %v", err)
}
log := ForwardingLog{
db: db,
}
initialTime := time.Unix(1234, 0)
endTime := time.Unix(1234, 0)
// We'll create 20 random events, which each event being spaced 10
// minutes after the prior event.
numEvents := 20
events := make([]ForwardingEvent, numEvents)
for i := 0; i < numEvents; i++ {
events[i] = ForwardingEvent{
Timestamp: endTime,
IncomingChanID: lnwire.NewShortChanIDFromInt(uint64(rand.Int63())),
OutgoingChanID: lnwire.NewShortChanIDFromInt(uint64(rand.Int63())),
AmtIn: lnwire.MilliSatoshi(rand.Int63()),
AmtOut: lnwire.MilliSatoshi(rand.Int63()),
}
endTime = endTime.Add(time.Minute * 10)
}
// Now that all of our set of events constructed, we'll add them to the
// database in a batch manner.
if err := log.AddForwardingEvents(events); err != nil {
t.Fatalf("unable to add events: %v", err)
}
// With all of our events added, we should be able to query for the
// first 10 events using the max event query field.
eventQuery := ForwardingEventQuery{
StartTime: initialTime,
EndTime: endTime,
IndexOffset: 0,
NumMaxEvents: 10,
}
timeSlice, err := log.Query(eventQuery)
if err != nil {
t.Fatalf("unable to query for events: %v", err)
}
// We should get exactly 10 events back.
if len(timeSlice.ForwardingEvents) != 10 {
t.Fatalf("wrong number of events: expected %v, got %v", 10,
len(timeSlice.ForwardingEvents))
}
// The set of events returned should be the first 10 events that we
// added.
if !reflect.DeepEqual(events[:10], timeSlice.ForwardingEvents) {
t.Fatalf("wrong response: expected %v, got %v",
spew.Sdump(events[:10]),
spew.Sdump(timeSlice.ForwardingEvents))
}
// The final offset should be the exact number of events returned.
if timeSlice.LastIndexOffset != 10 {
t.Fatalf("wrong index offset: expected %v, got %v", 10,
timeSlice.LastIndexOffset)
}
// If we use the final offset to query again, then we should get 10
// more events, that are the last 10 events we wrote.
eventQuery.IndexOffset = 10
timeSlice, err = log.Query(eventQuery)
if err != nil {
t.Fatalf("unable to query for events: %v", err)
}
// We should get exactly 10 events back once again.
if len(timeSlice.ForwardingEvents) != 10 {
t.Fatalf("wrong number of events: expected %v, got %v", 10,
len(timeSlice.ForwardingEvents))
}
// The events that we got back should be the last 10 events that we
// wrote out.
if !reflect.DeepEqual(events[10:], timeSlice.ForwardingEvents) {
t.Fatalf("wrong response: expected %v, got %v",
spew.Sdump(events[10:]),
spew.Sdump(timeSlice.ForwardingEvents))
}
// Finally, the last index offset should be 20, or the number of
// records we've written out.
if timeSlice.LastIndexOffset != 20 {
t.Fatalf("wrong index offset: expected %v, got %v", 20,
timeSlice.LastIndexOffset)
}
}
// TestForwardingLogQueryLimit tests that we're able to properly limit the
// number of events that are returned as part of a query.
func TestForwardingLogQueryLimit(t *testing.T) {
t.Parallel()
// First, we'll set up a test database, and use that to instantiate the
// forwarding event log that we'll be using for the duration of the
// test.
db, cleanUp, err := makeTestDB()
defer cleanUp()
if err != nil {
t.Fatalf("unable to make test db: %v", err)
}
log := ForwardingLog{
db: db,
}
initialTime := time.Unix(1234, 0)
endTime := time.Unix(1234, 0)
// We'll create 200 random events, which each event being spaced 10
// minutes after the prior event.
numEvents := 200
events := make([]ForwardingEvent, numEvents)
for i := 0; i < numEvents; i++ {
events[i] = ForwardingEvent{
Timestamp: endTime,
IncomingChanID: lnwire.NewShortChanIDFromInt(uint64(rand.Int63())),
OutgoingChanID: lnwire.NewShortChanIDFromInt(uint64(rand.Int63())),
AmtIn: lnwire.MilliSatoshi(rand.Int63()),
AmtOut: lnwire.MilliSatoshi(rand.Int63()),
}
endTime = endTime.Add(time.Minute * 10)
}
// Now that all of our set of events constructed, we'll add them to the
// database in a batch manner.
if err := log.AddForwardingEvents(events); err != nil {
t.Fatalf("unable to add events: %v", err)
}
// Once the events have been written out, we'll issue a query over the
// entire range, but restrict the number of events to the first 100.
eventQuery := ForwardingEventQuery{
StartTime: initialTime,
EndTime: endTime,
IndexOffset: 0,
NumMaxEvents: 100,
}
timeSlice, err := log.Query(eventQuery)
if err != nil {
t.Fatalf("unable to query for events: %v", err)
}
// We should get exactly 100 events back.
if len(timeSlice.ForwardingEvents) != 100 {
t.Fatalf("wrong number of events: expected %v, got %v", 10,
len(timeSlice.ForwardingEvents))
}
// The set of events returned should be the first 100 events that we
// added.
if !reflect.DeepEqual(events[:100], timeSlice.ForwardingEvents) {
t.Fatalf("wrong response: expected %v, got %v",
spew.Sdump(events[:100]),
spew.Sdump(timeSlice.ForwardingEvents))
}
// The final offset should be the exact number of events returned.
if timeSlice.LastIndexOffset != 100 {
t.Fatalf("wrong index offset: expected %v, got %v", 100,
timeSlice.LastIndexOffset)
}
}

View File

@ -2427,3 +2427,117 @@ func updateChannelPolicy(ctx *cli.Context) error {
printRespJSON(resp)
return nil
}
var forwardingHistoryCommand = cli.Command{
Name: "fwdinghistory",
Usage: "Query the history of all forwarded htlcs",
ArgsUsage: "start_time [end_time] [index_offset] [max_events]",
Description: `
Query the htlc switch's internal forwarding log for all completed
payment circuits (HTLCs) over a particular time range (--start_time and
--end_time). The start and end times are meant to be expressed in
seconds since the Unix epoch. If a start and end time aren't provided,
then events over the past 24 hours are queried for.
The max number of events returned is 50k. The default number is 100,
callers can use the --max_events param to modify this value.
Finally, callers can skip a series of events using the --index_offset
parameter. Each response will contain the offset index of the last
entry. Using this callers can manually paginate within a time slice.
`,
Flags: []cli.Flag{
cli.Int64Flag{
Name: "start_time",
Usage: "the starting time for the query, expressed in " +
"seconds since the unix epoch",
},
cli.Int64Flag{
Name: "end_time",
Usage: "the end time for the query, expressed in " +
"seconds since the unix epoch",
},
cli.Int64Flag{
Name: "index_offset",
Usage: "the number of events to skip",
},
cli.Int64Flag{
Name: "max_events",
Usage: "the max number of events to return",
},
},
Action: actionDecorator(forwardingHistory),
}
func forwardingHistory(ctx *cli.Context) error {
ctxb := context.Background()
client, cleanUp := getClient(ctx)
defer cleanUp()
var (
startTime, endTime uint64
indexOffset, maxEvents uint32
err error
)
args := ctx.Args()
switch {
case ctx.IsSet("start_time"):
startTime = ctx.Uint64("start_time")
case args.Present():
startTime, err = strconv.ParseUint(args.First(), 10, 64)
if err != nil {
return fmt.Errorf("unable to decode start_time %v", err)
}
args = args.Tail()
}
switch {
case ctx.IsSet("end_time"):
endTime = ctx.Uint64("end_time")
case args.Present():
endTime, err = strconv.ParseUint(args.First(), 10, 64)
if err != nil {
return fmt.Errorf("unable to decode end_time: %v", err)
}
args = args.Tail()
}
switch {
case ctx.IsSet("index_offset"):
indexOffset = uint32(ctx.Int64("index_offset"))
case args.Present():
i, err := strconv.ParseInt(args.First(), 10, 64)
if err != nil {
return fmt.Errorf("unable to decode index_offset: %v", err)
}
indexOffset = uint32(i)
args = args.Tail()
}
switch {
case ctx.IsSet("max_events"):
maxEvents = uint32(ctx.Int64("max_events"))
case args.Present():
m, err := strconv.ParseInt(args.First(), 10, 64)
if err != nil {
return fmt.Errorf("unable to decode max_events: %v", err)
}
maxEvents = uint32(m)
args = args.Tail()
}
req := &lnrpc.ForwardingHistoryRequest{
StartTime: startTime,
EndTime: endTime,
IndexOffset: indexOffset,
NumMaxEvents: maxEvents,
}
resp, err := client.ForwardingHistory(ctxb, req)
if err != nil {
return err
}
printRespJSON(resp)
return nil
}

View File

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

View File

@ -16,26 +16,36 @@ type PaymentCircuit struct {
// PaymentHash used as unique identifier of payment.
PaymentHash [32]byte
// IncomingChanID identifies the channel from which add HTLC request came
// and to which settle/fail HTLC request will be returned back. Once
// the switch forwards the settle/fail message to the src the circuit
// is considered to be completed.
// IncomingChanID identifies the channel from which add HTLC request
// came and to which settle/fail HTLC request will be returned back.
// Once the switch forwards the settle/fail message to the src the
// circuit is considered to be completed.
IncomingChanID lnwire.ShortChannelID
// IncomingHTLCID is the ID in the update_add_htlc message we received from
// the incoming channel, which will be included in any settle/fail messages
// we send back.
// IncomingHTLCID is the ID in the update_add_htlc message we received
// from the incoming channel, which will be included in any settle/fail
// messages we send back.
IncomingHTLCID uint64
// OutgoingChanID identifies the channel to which we propagate the HTLC add
// update and from which we are expecting to receive HTLC settle/fail
// request back.
// IncomingAmt is the value of the incoming HTLC. If we take this and
// subtract it from the OutgoingAmt, then we'll compute the total fee
// attached to this payment circuit.
IncomingAmt lnwire.MilliSatoshi
// OutgoingChanID identifies the channel to which we propagate the HTLC
// add update and from which we are expecting to receive HTLC
// settle/fail request back.
OutgoingChanID lnwire.ShortChannelID
// OutgoingHTLCID is the ID in the update_add_htlc message we sent to the
// outgoing channel.
// OutgoingHTLCID is the ID in the update_add_htlc message we sent to
// the outgoing channel.
OutgoingHTLCID uint64
// OutgoingAmt is the value of the outgoing HTLC. If we subtract this
// from the IncomingAmt, then we'll compute the total fee attached to
// this payment circuit.
OutgoingAmt lnwire.MilliSatoshi
// ErrorEncrypter is used to re-encrypt the onion failure before
// sending it back to the originator of the payment.
ErrorEncrypter ErrorEncrypter

View File

@ -121,3 +121,15 @@ type Peer interface {
// properly handle.
Disconnect(reason error)
}
// ForwardingLog is an interface that represents a time series database which
// keep track of all successfully completed payment circuits. Every few
// seconds, the switch will collate and flush out all the successful payment
// circuits during the last interval.
type ForwardingLog interface {
// AddForwardingEvents is a method that should write out the set of
// forwarding events in a batch to persistent storage. Outside
// sub-systems can then query the contents of the log for analysis,
// visualizations, etc.
AddForwardingEvents([]channeldb.ForwardingEvent) error
}

View File

@ -683,6 +683,8 @@ out:
// carried out by the remote peer. In the case of such an
// event, we'll wipe the channel state from the peer, and mark
// the contract as fully settled. Afterwards we can exit.
//
// TODO(roasbeef): add force closure? also breach?
case <-l.cfg.ChainEvents.UnilateralClosure:
log.Warnf("Remote peer has closed ChannelPoint(%v) on-chain",
l.channel.ChannelPoint())
@ -881,14 +883,16 @@ func (l *channelLink) handleDownStreamPkt(pkt *htlcPacket, isReProcess bool) {
"local_log_index=%v, batch_size=%v",
htlc.PaymentHash[:], index, l.batchCounter+1)
// Create circuit (remember the path) in order to forward settle/fail
// packet back.
// Create circuit (remember the path) in order to forward
// settle/fail packet back.
l.cfg.Switch.addCircuit(&PaymentCircuit{
PaymentHash: htlc.PaymentHash,
IncomingChanID: pkt.incomingChanID,
IncomingHTLCID: pkt.incomingHTLCID,
IncomingAmt: pkt.incomingHtlcAmt,
OutgoingChanID: l.ShortChanID(),
OutgoingHTLCID: index,
OutgoingAmt: htlc.Amount,
ErrorEncrypter: pkt.obfuscator,
})
@ -1393,8 +1397,8 @@ func (l *channelLink) processLockedInHtlcs(
switch pd.EntryType {
// A settle for an HTLC we previously forwarded HTLC has been
// received. So we'll forward the HTLC to the switch which
// will handle propagating the settle to the prior hop.
// received. So we'll forward the HTLC to the switch which will
// handle propagating the settle to the prior hop.
case lnwallet.Settle:
settlePacket := &htlcPacket{
outgoingChanID: l.ShortChanID(),
@ -1411,10 +1415,10 @@ func (l *channelLink) processLockedInHtlcs(
packetsToForward = append(packetsToForward, settlePacket)
l.overflowQueue.SignalFreeSlot()
// A failureCode message for a previously forwarded HTLC has been
// received. As a result a new slot will be freed up in our
// commitment state, so we'll forward this to the switch so the
// backwards undo can continue.
// A failureCode message for a previously forwarded HTLC has
// been received. As a result a new slot will be freed up in
// our commitment state, so we'll forward this to the switch so
// the backwards undo can continue.
case lnwallet.Fail:
// Fetch the reason the HTLC was cancelled so we can
// continue to propagate it.
@ -1818,12 +1822,13 @@ func (l *channelLink) processLockedInHtlcs(
}
updatePacket := &htlcPacket{
incomingChanID: l.ShortChanID(),
incomingHTLCID: pd.HtlcIndex,
outgoingChanID: fwdInfo.NextHop,
amount: addMsg.Amount,
htlc: addMsg,
obfuscator: obfuscator,
incomingChanID: l.ShortChanID(),
incomingHTLCID: pd.HtlcIndex,
outgoingChanID: fwdInfo.NextHop,
incomingHtlcAmt: pd.Amount,
amount: addMsg.Amount,
htlc: addMsg,
obfuscator: obfuscator,
}
packetsToForward = append(packetsToForward, updatePacket)
}

View File

@ -80,6 +80,23 @@ func (m *mockFeeEstimator) Stop() error {
var _ lnwallet.FeeEstimator = (*mockFeeEstimator)(nil)
type mockForwardingLog struct {
sync.Mutex
events map[time.Time]channeldb.ForwardingEvent
}
func (m *mockForwardingLog) AddForwardingEvents(events []channeldb.ForwardingEvent) error {
m.Lock()
defer m.Unlock()
for _, event := range events {
m.events[event.Timestamp] = event
}
return nil
}
type mockServer struct {
started int32
shutdown int32
@ -108,13 +125,17 @@ func newMockServer(t testing.TB, name string) *mockServer {
copy(id[:], h[:])
return &mockServer{
t: t,
id: id,
name: name,
messages: make(chan lnwire.Message, 3000),
quit: make(chan struct{}),
registry: newMockRegistry(),
htlcSwitch: New(Config{}),
t: t,
id: id,
name: name,
messages: make(chan lnwire.Message, 3000),
quit: make(chan struct{}),
registry: newMockRegistry(),
htlcSwitch: New(Config{
FwdingLog: &mockForwardingLog{
events: make(map[time.Time]channeldb.ForwardingEvent),
},
}),
interceptorFuncs: make([]messageInterceptor, 0),
}
}

View File

@ -23,6 +23,14 @@ type htlcPacket struct {
// on the incoming channel.
incomingHTLCID uint64
// incomingHtlcAmt is the value of the *incoming* HTLC. This will be
// set by the link when it receives an incoming HTLC to be forwarded
// through the switch. Then the outgoing link will use this once it
// creates a full circuit add. This allows us to properly populate the
// forwarding event for this circuit/packet in the case the payment
// circuit is successful.
incomingHtlcAmt lnwire.MilliSatoshi
// outgoingHTLCID is the ID of the HTLC that we offered to the peer on the
// outgoing channel.
outgoingHTLCID uint64

View File

@ -13,6 +13,7 @@ import (
"github.com/roasbeef/btcd/btcec"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/contractcourt"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnwallet"
@ -99,6 +100,12 @@ type Config struct {
// properly route around link./vertex failures.
SelfKey *btcec.PublicKey
// FwdingLog is an interface that will be used by the switch to log
// forwarding events. A forwarding event happens each time a payment
// circuit is successfully completed. So when we forward an HTLC, and a
// settle is eventually received.
FwdingLog ForwardingLog
// LocalChannelClose kicks-off the workflow to execute a cooperative or
// forced unilateral closure of the channel initiated by a local
// subsystem.
@ -169,6 +176,12 @@ type Switch struct {
// linkControl is a channel used to propagate add/remove/get htlc
// switch handler commands.
linkControl chan interface{}
// pendingFwdingEvents is the set of forwarding events which have been
// collected during the current interval, but hasn't yet been written
// to the forwarding log.
fwdEventMtx sync.Mutex
pendingFwdingEvents []channeldb.ForwardingEvent
}
// New creates the new instance of htlc switch.
@ -553,8 +566,8 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
// the ultimate settle message back latter.
case *lnwire.UpdateAddHTLC:
if packet.incomingChanID == (lnwire.ShortChannelID{}) {
// A blank incomingChanID indicates that this is a pending
// user-initiated payment.
// A blank incomingChanID indicates that this is a
// pending user-initiated payment.
return s.handleLocalDispatch(packet)
}
@ -606,8 +619,8 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
}
if link.Bandwidth() >= htlc.Amount {
destination = link
break
}
}
@ -665,9 +678,12 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
return err
}
// Remove circuit since we are about to complete the HTLC.
err := s.circuits.Remove(packet.outgoingChanID,
packet.outgoingHTLCID)
// Remove the circuit since we are about to complete
// the HTLC.
err := s.circuits.Remove(
packet.outgoingChanID,
packet.outgoingHTLCID,
)
if err != nil {
log.Warnf("Failed to close completed onion circuit for %x: "+
"(%s, %d) <-> (%s, %d)", circuit.PaymentHash,
@ -683,6 +699,29 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
packet.incomingChanID = circuit.IncomingChanID
packet.incomingHTLCID = circuit.IncomingHTLCID
// If this is an HTLC settle, and it wasn't from a
// locally initiated HTLC, then we'll log a forwarding
// event so we can flush it to disk later.
//
// TODO(roasbeef): only do this once link actually
// fully settles?
_, isSettle := packet.htlc.(*lnwire.UpdateFulfillHTLC)
localHTLC := packet.incomingChanID == (lnwire.ShortChannelID{})
if isSettle && !localHTLC {
s.fwdEventMtx.Lock()
s.pendingFwdingEvents = append(
s.pendingFwdingEvents,
channeldb.ForwardingEvent{
Timestamp: time.Now(),
IncomingChanID: circuit.IncomingChanID,
OutgoingChanID: circuit.OutgoingChanID,
AmtIn: circuit.IncomingAmt,
AmtOut: circuit.OutgoingAmt,
},
)
s.fwdEventMtx.Unlock()
}
// Obfuscate the error message for fail updates before
// sending back through the circuit unless the payment
// was generated locally.
@ -715,9 +754,11 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
}
}
// A blank IncomingChanID in a circuit indicates that it is a
// pending user-initiated payment.
if packet.incomingChanID == (lnwire.ShortChannelID{}) {
// For local HTLCs we'll dispatch the settle event back to the
// caller, rather than to the peer that sent us the HTLC
// originally.
localHTLC := packet.incomingChanID == (lnwire.ShortChannelID{})
if localHTLC {
return s.handleLocalDispatch(packet)
}
@ -790,6 +831,13 @@ func (s *Switch) htlcForwarder() {
"channel link on stop: %v", err)
}
}
// Before we exit fully, we'll attempt to flush out any
// forwarding events that may still be lingering since the last
// batch flush.
if err := s.FlushForwardingEvents(); err != nil {
log.Errorf("unable to flush forwarding events: %v", err)
}
}()
// TODO(roasbeef): cleared vs settled distinction
@ -801,6 +849,11 @@ func (s *Switch) htlcForwarder() {
logTicker := time.NewTicker(10 * time.Second)
defer logTicker.Stop()
// Every 15 seconds, we'll flush out the forwarding events that
// occurred during that period.
fwdEventTicker := time.NewTicker(15 * time.Second)
defer fwdEventTicker.Stop()
for {
select {
// A local close request has arrived, we'll forward this to the
@ -861,6 +914,17 @@ func (s *Switch) htlcForwarder() {
case cmd := <-s.htlcPlex:
cmd.err <- s.handlePacketForward(cmd.pkt)
// When this time ticks, then it indicates that we should
// collect all the forwarding events since the last internal,
// and write them out to our log.
case <-fwdEventTicker.C:
go func() {
if err := s.FlushForwardingEvents(); err != nil {
log.Errorf("unable to flush "+
"forwarding events: %v", err)
}
}()
// The log ticker has fired, so we'll calculate some forwarding
// stats for the last 10 seconds to display within the logs to
// users.
@ -1307,3 +1371,32 @@ func (s *Switch) numPendingPayments() int {
func (s *Switch) addCircuit(circuit *PaymentCircuit) {
s.circuits.Add(circuit)
}
// FlushForwardingEvents flushes out the set of pending forwarding events to
// the persistent log. This will be used by the switch to periodically flush
// out the set of forwarding events to disk. External callers can also use this
// method to ensure all data is flushed to dis before querying the log.
func (s *Switch) FlushForwardingEvents() error {
// First, we'll obtain a copy of the current set of pending forwarding
// events.
s.fwdEventMtx.Lock()
// If we won't have any forwarding events, then we can exit early.
if len(s.pendingFwdingEvents) == 0 {
s.fwdEventMtx.Unlock()
return nil
}
events := make([]channeldb.ForwardingEvent, len(s.pendingFwdingEvents))
copy(events[:], s.pendingFwdingEvents[:])
// With the copy obtained, we can now clear out the header pointer of
// the current slice. This way, we can re-use the underlying storage
// allocated for the slice.
s.pendingFwdingEvents = s.pendingFwdingEvents[:0]
s.fwdEventMtx.Unlock()
// Finally, we'll write out the copied events to the persistent
// forwarding log.
return s.cfg.FwdingLog.AddForwardingEvents(events)
}

View File

@ -7,10 +7,13 @@ import (
"time"
"github.com/btcsuite/fastsha256"
"github.com/davecgh/go-spew/spew"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil"
)
var (
@ -35,7 +38,11 @@ func TestSwitchForward(t *testing.T) {
alicePeer := newMockServer(t, "alice")
bobPeer := newMockServer(t, "bob")
s := New(Config{})
s := New(Config{
FwdingLog: &mockForwardingLog{
events: make(map[time.Time]channeldb.ForwardingEvent),
},
})
s.Start()
aliceChannelLink := newMockChannelLink(
@ -122,7 +129,11 @@ func TestSkipIneligibleLinksMultiHopForward(t *testing.T) {
alicePeer := newMockServer(t, "alice")
bobPeer := newMockServer(t, "bob")
s := New(Config{})
s := New(Config{
FwdingLog: &mockForwardingLog{
events: make(map[time.Time]channeldb.ForwardingEvent),
},
})
s.Start()
aliceChannelLink := newMockChannelLink(
@ -177,7 +188,11 @@ func TestSkipIneligibleLinksLocalForward(t *testing.T) {
// to forward form the get go.
alicePeer := newMockServer(t, "alice")
s := New(Config{})
s := New(Config{
FwdingLog: &mockForwardingLog{
events: make(map[time.Time]channeldb.ForwardingEvent),
},
})
s.Start()
aliceChannelLink := newMockChannelLink(
@ -216,7 +231,11 @@ func TestSwitchCancel(t *testing.T) {
alicePeer := newMockServer(t, "alice")
bobPeer := newMockServer(t, "bob")
s := New(Config{})
s := New(Config{
FwdingLog: &mockForwardingLog{
events: make(map[time.Time]channeldb.ForwardingEvent),
},
})
s.Start()
aliceChannelLink := newMockChannelLink(
@ -298,7 +317,11 @@ func TestSwitchAddSamePayment(t *testing.T) {
alicePeer := newMockServer(t, "alice")
bobPeer := newMockServer(t, "bob")
s := New(Config{})
s := New(Config{
FwdingLog: &mockForwardingLog{
events: make(map[time.Time]channeldb.ForwardingEvent),
},
})
s.Start()
aliceChannelLink := newMockChannelLink(
@ -422,7 +445,11 @@ func TestSwitchSendPayment(t *testing.T) {
alicePeer := newMockServer(t, "alice")
s := New(Config{})
s := New(Config{
FwdingLog: &mockForwardingLog{
events: make(map[time.Time]channeldb.ForwardingEvent),
},
})
s.Start()
aliceChannelLink := newMockChannelLink(
@ -542,3 +569,165 @@ func TestSwitchSendPayment(t *testing.T) {
t.Fatal("wrong amount of pending payments")
}
}
// TestLocalPaymentNoForwardingEvents tests that if we send a series of locally
// initiated payments, then they aren't reflected in the forwarding log.
func TestLocalPaymentNoForwardingEvents(t *testing.T) {
t.Parallel()
// First, we'll create our traditional three hop network. We'll only be
// interacting with and asserting the state of the first end point for
// this test.
channels, cleanUp, _, err := createClusterChannels(
btcutil.SatoshiPerBitcoin*3,
btcutil.SatoshiPerBitcoin*5)
if err != nil {
t.Fatalf("unable to create channel: %v", err)
}
defer cleanUp()
n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
channels.bobToCarol, channels.carolToBob, testStartingHeight)
if err := n.start(); err != nil {
t.Fatalf("unable to start three hop network: %v", err)
}
// We'll now craft and send a payment from Alice to Bob.
amount := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
htlcAmt, totalTimelock, hops := generateHops(
amount, testStartingHeight, n.firstBobChannelLink,
)
// With the payment crafted, we'll send it from Alice to Bob. We'll
// wait for Alice to receive the preimage for the payment before
// proceeding.
receiver := n.bobServer
_, err = n.makePayment(
n.aliceServer, receiver, n.bobServer.PubKey(), hops, amount,
htlcAmt, totalTimelock,
).Wait(30 * time.Second)
if err != nil {
t.Fatalf("unable to make the payment: %v", err)
}
// At this point, we'll forcibly stop the three hop network. Doing
// this will cause any pending forwarding events to be flushed by the
// various switches in the network.
n.stop()
// With all the switches stopped, we'll fetch Alice's mock forwarding
// event log.
log, ok := n.aliceServer.htlcSwitch.cfg.FwdingLog.(*mockForwardingLog)
if !ok {
t.Fatalf("mockForwardingLog assertion failed")
}
// If we examine the memory of the forwarding log, then it should be
// blank.
if len(log.events) != 0 {
t.Fatalf("log should have no events, instead has: %v",
spew.Sdump(log.events))
}
}
// TestMultiHopPaymentForwardingEvents tests that if we send a series of
// multi-hop payments via Alice->Bob->Carol. Then Bob properly logs forwarding
// events, while Alice and Carol don't.
func TestMultiHopPaymentForwardingEvents(t *testing.T) {
t.Parallel()
// First, we'll create our traditional three hop network.
channels, cleanUp, _, err := createClusterChannels(
btcutil.SatoshiPerBitcoin*3,
btcutil.SatoshiPerBitcoin*5)
if err != nil {
t.Fatalf("unable to create channel: %v", err)
}
defer cleanUp()
n := newThreeHopNetwork(t, channels.aliceToBob, channels.bobToAlice,
channels.bobToCarol, channels.carolToBob, testStartingHeight)
if err := n.start(); err != nil {
t.Fatalf("unable to start three hop network: %v", err)
}
// We'll make now 10 payments, of 100k satoshis each from Alice to
// Carol via Bob.
const numPayments = 10
finalAmt := lnwire.NewMSatFromSatoshis(100000)
htlcAmt, totalTimelock, hops := generateHops(
finalAmt, testStartingHeight, n.firstBobChannelLink,
n.carolChannelLink,
)
for i := 0; i < numPayments; i++ {
_, err := n.makePayment(
n.aliceServer, n.carolServer, n.bobServer.PubKey(),
hops, finalAmt, htlcAmt, totalTimelock,
).Wait(30 * time.Second)
if err != nil {
t.Fatalf("unable to send payment: %v", err)
}
}
time.Sleep(time.Millisecond * 200)
// With all 10 payments sent. We'll now manually stop each of the
// switches so we can examine their end state.
n.stop()
// Alice and Carol shouldn't have any recorded forwarding events, as
// they were the source and the sink for these payment flows.
aliceLog, ok := n.aliceServer.htlcSwitch.cfg.FwdingLog.(*mockForwardingLog)
if !ok {
t.Fatalf("mockForwardingLog assertion failed")
}
if len(aliceLog.events) != 0 {
t.Fatalf("log should have no events, instead has: %v",
spew.Sdump(aliceLog.events))
}
carolLog, ok := n.carolServer.htlcSwitch.cfg.FwdingLog.(*mockForwardingLog)
if !ok {
t.Fatalf("mockForwardingLog assertion failed")
}
if len(carolLog.events) != 0 {
t.Fatalf("log should have no events, instead has: %v",
spew.Sdump(carolLog.events))
}
// Bob on the other hand, should have 10 events.
bobLog, ok := n.bobServer.htlcSwitch.cfg.FwdingLog.(*mockForwardingLog)
if !ok {
t.Fatalf("mockForwardingLog assertion failed")
}
if len(bobLog.events) != 10 {
t.Fatalf("log should have 10 events, instead has: %v",
spew.Sdump(bobLog.events))
}
// Each of the 10 events should have had all fields set properly.
for _, event := range bobLog.events {
// The incoming and outgoing channels should properly be set for
// the event.
if event.IncomingChanID != n.aliceChannelLink.ShortChanID() {
t.Fatalf("chan id mismatch: expected %v, got %v",
event.IncomingChanID,
n.aliceChannelLink.ShortChanID())
}
if event.OutgoingChanID != n.carolChannelLink.ShortChanID() {
t.Fatalf("chan id mismatch: expected %v, got %v",
event.OutgoingChanID,
n.carolChannelLink.ShortChanID())
}
// Additionally, the incoming and outgoing amounts should also
// be properly set.
if event.AmtIn != htlcAmt {
t.Fatalf("incoming amt mismatch: expected %v, got %v",
event.AmtIn, htlcAmt)
}
if event.AmtOut != finalAmt {
t.Fatalf("outgoing amt mismatch: expected %v, got %v",
event.AmtOut, finalAmt)
}
}
}

View File

@ -117,9 +117,7 @@ func assertTxInBlock(t *harnessTest, block *wire.MsgBlock, txid *chainhash.Hash)
// mineBlocks mine 'num' of blocks and check that blocks are present in
// node blockchain.
func mineBlocks(t *harnessTest, net *lntest.NetworkHarness, num uint32,
) []*wire.MsgBlock {
func mineBlocks(t *harnessTest, net *lntest.NetworkHarness, num uint32) []*wire.MsgBlock {
blocks := make([]*wire.MsgBlock, num)
blockHashes, err := net.Miner.Node.Generate(num)
@ -2497,6 +2495,51 @@ func testMultiHopPayments(net *lntest.NetworkHarness, t *harnessTest) {
assertAmountPaid(t, ctxb, "Carol(local) => Dave(remote)", carol,
carolFundPoint, amountPaid+(baseFee*numPayments)*2, int64(0))
// Now that we know all the balances have been settled out properly,
// we'll ensure that our internal record keeping for completed circuits
// was properly updated.
// First, check that the FeeReport response shows the proper fees
// accrued over each time range. Dave should've earned 1 satoshi for
// each of the forwarded payments.
feeReport, err := dave.FeeReport(ctxb, &lnrpc.FeeReportRequest{})
if err != nil {
t.Fatalf("unable to query for fee report: %v", err)
}
const exectedFees = 5
if feeReport.DayFeeSum != exectedFees {
t.Fatalf("fee mismatch: expected %v, got %v", 5,
feeReport.DayFeeSum)
}
if feeReport.WeekFeeSum != exectedFees {
t.Fatalf("fee mismatch: expected %v, got %v", 5,
feeReport.WeekFeeSum)
}
if feeReport.MonthFeeSum != exectedFees {
t.Fatalf("fee mismatch: expected %v, got %v", 5,
feeReport.MonthFeeSum)
}
// Next, ensure that if we issue the vanilla query for the forwarding
// history, it returns 5 values, and each entry is formatted properly.
fwdingHistory, err := dave.ForwardingHistory(
ctxb, &lnrpc.ForwardingHistoryRequest{},
)
if err != nil {
t.Fatalf("unable to query for fee report: %v", err)
}
if len(fwdingHistory.ForwardingEvents) != 5 {
t.Fatalf("wrong number of forwarding event: expected %v, "+
"got %v", 5, len(fwdingHistory.ForwardingEvents))
}
for _, event := range fwdingHistory.ForwardingEvents {
// Each event should show a fee of 1 satoshi.
if event.Fee != 1 {
t.Fatalf("fee mismatch: expected %v, got %v", 1,
event.Fee)
}
}
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, net.Alice, chanPointAlice, false)
ctxt, _ = context.WithTimeout(ctxb, timeout)
@ -2504,9 +2547,9 @@ func testMultiHopPayments(net *lntest.NetworkHarness, t *harnessTest) {
ctxt, _ = context.WithTimeout(ctxb, timeout)
closeChannelAndAssert(ctxt, t, net, carol, chanPointCarol, false)
// Finally, shutdown the nodes we created for the duration of the tests,
// only leaving the two seed nodes (Alice and Bob) within our test
// network.
// Finally, shutdown the nodes we created for the duration of the
// tests, only leaving the two seed nodes (Alice and Bob) within our
// test network.
if err := net.ShutdownNode(carol); err != nil {
t.Fatalf("unable to shutdown carol: %v", err)
}

View File

@ -102,6 +102,9 @@ It has these top-level messages:
FeeReportResponse
PolicyUpdateRequest
PolicyUpdateResponse
ForwardingHistoryRequest
ForwardingEvent
ForwardingHistoryResponse
*/
package lnrpc
@ -3664,6 +3667,12 @@ func (m *ChannelFeeReport) GetFeeRate() float64 {
type FeeReportResponse struct {
// / An array of channel fee reports which describes the current fee schedule for each channel.
ChannelFees []*ChannelFeeReport `protobuf:"bytes,1,rep,name=channel_fees" json:"channel_fees,omitempty"`
// / The total amount of fee revenue (in satoshis) the switch has collected over the past 24 hrs.
DayFeeSum uint64 `protobuf:"varint,2,opt,name=day_fee_sum" json:"day_fee_sum,omitempty"`
// / The total amount of fee revenue (in satoshis) the switch has collected over the past 1 week.
WeekFeeSum uint64 `protobuf:"varint,3,opt,name=week_fee_sum" json:"week_fee_sum,omitempty"`
// / The total amount of fee revenue (in satoshis) the switch has collected over the past 1 month.
MonthFeeSum uint64 `protobuf:"varint,4,opt,name=month_fee_sum" json:"month_fee_sum,omitempty"`
}
func (m *FeeReportResponse) Reset() { *m = FeeReportResponse{} }
@ -3678,6 +3687,27 @@ func (m *FeeReportResponse) GetChannelFees() []*ChannelFeeReport {
return nil
}
func (m *FeeReportResponse) GetDayFeeSum() uint64 {
if m != nil {
return m.DayFeeSum
}
return 0
}
func (m *FeeReportResponse) GetWeekFeeSum() uint64 {
if m != nil {
return m.WeekFeeSum
}
return 0
}
func (m *FeeReportResponse) GetMonthFeeSum() uint64 {
if m != nil {
return m.MonthFeeSum
}
return 0
}
type PolicyUpdateRequest struct {
// Types that are valid to be assigned to Scope:
// *PolicyUpdateRequest_Global
@ -3833,6 +3863,138 @@ func (m *PolicyUpdateResponse) String() string { return proto.Compact
func (*PolicyUpdateResponse) ProtoMessage() {}
func (*PolicyUpdateResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{93} }
type ForwardingHistoryRequest struct {
// / Start time is the starting point of the forwarding history request. All records beyond this point will be included, respecting the end time, and the index offset.
StartTime uint64 `protobuf:"varint,1,opt,name=start_time" json:"start_time,omitempty"`
// / End time is the end point of the forwarding history request. The response will carry at most 50k records between the start time and the end time. The index offset can be used to implement pagination.
EndTime uint64 `protobuf:"varint,2,opt,name=end_time" json:"end_time,omitempty"`
// / Index offset is the offset in the time series to start at. As each response can only contain 50k records, callers can use this to skip around within a packed time series.
IndexOffset uint32 `protobuf:"varint,3,opt,name=index_offset" json:"index_offset,omitempty"`
// / The max number of events to return in the response to this query.
NumMaxEvents uint32 `protobuf:"varint,4,opt,name=num_max_events" json:"num_max_events,omitempty"`
}
func (m *ForwardingHistoryRequest) Reset() { *m = ForwardingHistoryRequest{} }
func (m *ForwardingHistoryRequest) String() string { return proto.CompactTextString(m) }
func (*ForwardingHistoryRequest) ProtoMessage() {}
func (*ForwardingHistoryRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{94} }
func (m *ForwardingHistoryRequest) GetStartTime() uint64 {
if m != nil {
return m.StartTime
}
return 0
}
func (m *ForwardingHistoryRequest) GetEndTime() uint64 {
if m != nil {
return m.EndTime
}
return 0
}
func (m *ForwardingHistoryRequest) GetIndexOffset() uint32 {
if m != nil {
return m.IndexOffset
}
return 0
}
func (m *ForwardingHistoryRequest) GetNumMaxEvents() uint32 {
if m != nil {
return m.NumMaxEvents
}
return 0
}
type ForwardingEvent struct {
// / Timestamp is the time (unix epoch offset) that this circuit was completed.
Timestamp uint64 `protobuf:"varint,1,opt,name=timestamp" json:"timestamp,omitempty"`
// / The incoming channel ID that carried the HTLC that created the circuit.
ChanIdIn uint64 `protobuf:"varint,2,opt,name=chan_id_in" json:"chan_id_in,omitempty"`
// / The outgoing channel ID that carried the preimage that completed the circuit.
ChanIdOut uint64 `protobuf:"varint,4,opt,name=chan_id_out" json:"chan_id_out,omitempty"`
// / The total amount of the incoming HTLC that created half the circuit.
AmtIn uint64 `protobuf:"varint,5,opt,name=amt_in" json:"amt_in,omitempty"`
// / The total amount of the outgoign HTLC that created the second half of the circuit.
AmtOut uint64 `protobuf:"varint,6,opt,name=amt_out" json:"amt_out,omitempty"`
// / The total fee that this payment circuit carried.
Fee uint64 `protobuf:"varint,7,opt,name=fee" json:"fee,omitempty"`
}
func (m *ForwardingEvent) Reset() { *m = ForwardingEvent{} }
func (m *ForwardingEvent) String() string { return proto.CompactTextString(m) }
func (*ForwardingEvent) ProtoMessage() {}
func (*ForwardingEvent) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{95} }
func (m *ForwardingEvent) GetTimestamp() uint64 {
if m != nil {
return m.Timestamp
}
return 0
}
func (m *ForwardingEvent) GetChanIdIn() uint64 {
if m != nil {
return m.ChanIdIn
}
return 0
}
func (m *ForwardingEvent) GetChanIdOut() uint64 {
if m != nil {
return m.ChanIdOut
}
return 0
}
func (m *ForwardingEvent) GetAmtIn() uint64 {
if m != nil {
return m.AmtIn
}
return 0
}
func (m *ForwardingEvent) GetAmtOut() uint64 {
if m != nil {
return m.AmtOut
}
return 0
}
func (m *ForwardingEvent) GetFee() uint64 {
if m != nil {
return m.Fee
}
return 0
}
type ForwardingHistoryResponse struct {
// / A list of forwarding events from the time slice of the time series specified in the request.
ForwardingEvents []*ForwardingEvent `protobuf:"bytes,1,rep,name=forwarding_events" json:"forwarding_events,omitempty"`
// / The index of the last time in the set of returned forwarding events. Can be used to seek further, pagination style.
LastOffsetIndex uint32 `protobuf:"varint,2,opt,name=last_offset_index" json:"last_offset_index,omitempty"`
}
func (m *ForwardingHistoryResponse) Reset() { *m = ForwardingHistoryResponse{} }
func (m *ForwardingHistoryResponse) String() string { return proto.CompactTextString(m) }
func (*ForwardingHistoryResponse) ProtoMessage() {}
func (*ForwardingHistoryResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{96} }
func (m *ForwardingHistoryResponse) GetForwardingEvents() []*ForwardingEvent {
if m != nil {
return m.ForwardingEvents
}
return nil
}
func (m *ForwardingHistoryResponse) GetLastOffsetIndex() uint32 {
if m != nil {
return m.LastOffsetIndex
}
return 0
}
func init() {
proto.RegisterType((*GenSeedRequest)(nil), "lnrpc.GenSeedRequest")
proto.RegisterType((*GenSeedResponse)(nil), "lnrpc.GenSeedResponse")
@ -3932,6 +4094,9 @@ func init() {
proto.RegisterType((*FeeReportResponse)(nil), "lnrpc.FeeReportResponse")
proto.RegisterType((*PolicyUpdateRequest)(nil), "lnrpc.PolicyUpdateRequest")
proto.RegisterType((*PolicyUpdateResponse)(nil), "lnrpc.PolicyUpdateResponse")
proto.RegisterType((*ForwardingHistoryRequest)(nil), "lnrpc.ForwardingHistoryRequest")
proto.RegisterType((*ForwardingEvent)(nil), "lnrpc.ForwardingEvent")
proto.RegisterType((*ForwardingHistoryResponse)(nil), "lnrpc.ForwardingHistoryResponse")
proto.RegisterEnum("lnrpc.NewAddressRequest_AddressType", NewAddressRequest_AddressType_name, NewAddressRequest_AddressType_value)
}
@ -4321,6 +4486,18 @@ type LightningClient interface {
// UpdateChannelPolicy allows the caller to update the fee schedule and
// channel policies for all channels globally, or a particular channel.
UpdateChannelPolicy(ctx context.Context, in *PolicyUpdateRequest, opts ...grpc.CallOption) (*PolicyUpdateResponse, error)
// * lncli: `forwardinghistory`
// ForwardingHistory allows the caller to query the htlcswitch for a record of
// all HTLC's forwarded within the target time range, and integer offset
// within that time range. If no time-range is specified, then the first chunk
// of the past 24 hrs of forwarding history are returned.
//
// A list of forwarding events are returned. The size of each forwarding event
// is 40 bytes, and the max message size able to be returned in gRPC is 4 MiB.
// As a result each message can only contain 50k entries. Each response has
// the index offset of the last entry. The index offset can be provided to the
// request to allow the caller to skip a series of records.
ForwardingHistory(ctx context.Context, in *ForwardingHistoryRequest, opts ...grpc.CallOption) (*ForwardingHistoryResponse, error)
}
type lightningClient struct {
@ -4810,6 +4987,15 @@ func (c *lightningClient) UpdateChannelPolicy(ctx context.Context, in *PolicyUpd
return out, nil
}
func (c *lightningClient) ForwardingHistory(ctx context.Context, in *ForwardingHistoryRequest, opts ...grpc.CallOption) (*ForwardingHistoryResponse, error) {
out := new(ForwardingHistoryResponse)
err := grpc.Invoke(ctx, "/lnrpc.Lightning/ForwardingHistory", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Lightning service
type LightningServer interface {
@ -5008,6 +5194,18 @@ type LightningServer interface {
// UpdateChannelPolicy allows the caller to update the fee schedule and
// channel policies for all channels globally, or a particular channel.
UpdateChannelPolicy(context.Context, *PolicyUpdateRequest) (*PolicyUpdateResponse, error)
// * lncli: `forwardinghistory`
// ForwardingHistory allows the caller to query the htlcswitch for a record of
// all HTLC's forwarded within the target time range, and integer offset
// within that time range. If no time-range is specified, then the first chunk
// of the past 24 hrs of forwarding history are returned.
//
// A list of forwarding events are returned. The size of each forwarding event
// is 40 bytes, and the max message size able to be returned in gRPC is 4 MiB.
// As a result each message can only contain 50k entries. Each response has
// the index offset of the last entry. The index offset can be provided to the
// request to allow the caller to skip a series of records.
ForwardingHistory(context.Context, *ForwardingHistoryRequest) (*ForwardingHistoryResponse, error)
}
func RegisterLightningServer(s *grpc.Server, srv LightningServer) {
@ -5721,6 +5919,24 @@ func _Lightning_UpdateChannelPolicy_Handler(srv interface{}, ctx context.Context
return interceptor(ctx, in, info, handler)
}
func _Lightning_ForwardingHistory_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(ForwardingHistoryRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(LightningServer).ForwardingHistory(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/lnrpc.Lightning/ForwardingHistory",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(LightningServer).ForwardingHistory(ctx, req.(*ForwardingHistoryRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Lightning_serviceDesc = grpc.ServiceDesc{
ServiceName: "lnrpc.Lightning",
HandlerType: (*LightningServer)(nil),
@ -5853,6 +6069,10 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
MethodName: "UpdateChannelPolicy",
Handler: _Lightning_UpdateChannelPolicy_Handler,
},
{
MethodName: "ForwardingHistory",
Handler: _Lightning_ForwardingHistory_Handler,
},
},
Streams: []grpc.StreamDesc{
{
@ -5893,322 +6113,337 @@ var _Lightning_serviceDesc = grpc.ServiceDesc{
func init() { proto.RegisterFile("rpc.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{
// 5069 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5b, 0xcd, 0x93, 0x1c, 0xc9,
0x55, 0x57, 0xf5, 0xf4, 0x7c, 0xf4, 0xeb, 0x8f, 0x99, 0xc9, 0x19, 0xcd, 0xb4, 0x7a, 0x65, 0x59,
0x5b, 0x6c, 0xac, 0x84, 0x58, 0x34, 0xda, 0xb1, 0xbd, 0xac, 0x57, 0x60, 0x87, 0xbe, 0x56, 0xb3,
0xf6, 0xac, 0x3c, 0xae, 0x91, 0xbc, 0xe0, 0x05, 0xda, 0x35, 0xdd, 0x39, 0x3d, 0x65, 0x55, 0x57,
0xd5, 0x56, 0x55, 0xcf, 0xa8, 0x77, 0x51, 0x04, 0x1f, 0x11, 0x9c, 0x20, 0x38, 0x40, 0x04, 0x61,
0x08, 0x73, 0xc0, 0x17, 0x38, 0xf0, 0x07, 0x10, 0x8e, 0x80, 0xbb, 0x23, 0x08, 0x0e, 0x3e, 0x11,
0x1c, 0xe1, 0x64, 0x8e, 0x04, 0x17, 0x4e, 0xc4, 0x7b, 0xf9, 0x51, 0x99, 0x55, 0x35, 0x92, 0x6c,
0x03, 0xb7, 0xce, 0x5f, 0x66, 0xbd, 0xcc, 0x7c, 0xf9, 0xf2, 0x7d, 0xe4, 0x7b, 0x0d, 0xad, 0x34,
0x19, 0xdd, 0x4c, 0xd2, 0x38, 0x8f, 0xd9, 0x62, 0x18, 0xa5, 0xc9, 0x68, 0x70, 0x79, 0x12, 0xc7,
0x93, 0x90, 0xef, 0xf8, 0x49, 0xb0, 0xe3, 0x47, 0x51, 0x9c, 0xfb, 0x79, 0x10, 0x47, 0x99, 0x18,
0xe4, 0x7e, 0x07, 0x7a, 0x0f, 0x79, 0x74, 0xc8, 0xf9, 0xd8, 0xe3, 0x9f, 0xcc, 0x78, 0x96, 0xb3,
0x5f, 0x82, 0x75, 0x9f, 0x7f, 0xca, 0xf9, 0x78, 0x98, 0xf8, 0x59, 0x96, 0x9c, 0xa4, 0x7e, 0xc6,
0xfb, 0xce, 0x55, 0xe7, 0x7a, 0xc7, 0x5b, 0x13, 0x1d, 0x07, 0x1a, 0x67, 0xaf, 0x43, 0x27, 0xc3,
0xa1, 0x3c, 0xca, 0xd3, 0x38, 0x99, 0xf7, 0x1b, 0x34, 0xae, 0x8d, 0xd8, 0x03, 0x01, 0xb9, 0x21,
0xac, 0xea, 0x19, 0xb2, 0x24, 0x8e, 0x32, 0xce, 0x6e, 0xc1, 0xe6, 0x28, 0x48, 0x4e, 0x78, 0x3a,
0xa4, 0x8f, 0xa7, 0x11, 0x9f, 0xc6, 0x51, 0x30, 0xea, 0x3b, 0x57, 0x17, 0xae, 0xb7, 0x3c, 0x26,
0xfa, 0xf0, 0x8b, 0x0f, 0x65, 0x0f, 0xbb, 0x06, 0xab, 0x3c, 0x12, 0x38, 0x1f, 0xd3, 0x57, 0x72,
0xaa, 0x5e, 0x01, 0xe3, 0x07, 0xee, 0x5f, 0x3a, 0xb0, 0xfe, 0x41, 0x14, 0xe4, 0x1f, 0xf9, 0x61,
0xc8, 0x73, 0xb5, 0xa7, 0x6b, 0xb0, 0x7a, 0x46, 0x00, 0xed, 0xe9, 0x2c, 0x4e, 0xc7, 0x72, 0x47,
0x3d, 0x01, 0x1f, 0x48, 0xf4, 0xdc, 0x95, 0x35, 0xce, 0x5d, 0x59, 0x2d, 0xbb, 0x16, 0xea, 0xd9,
0xe5, 0x6e, 0x02, 0x33, 0x17, 0x27, 0xd8, 0xe1, 0x7e, 0x05, 0x36, 0x9e, 0x44, 0x61, 0x3c, 0x7a,
0xfa, 0xb3, 0x2d, 0xda, 0xdd, 0x82, 0x4d, 0xfb, 0x7b, 0x49, 0xf7, 0x7b, 0x0d, 0x68, 0x3f, 0x4e,
0xfd, 0x28, 0xf3, 0x47, 0x78, 0xe4, 0xac, 0x0f, 0xcb, 0xf9, 0xb3, 0xe1, 0x89, 0x9f, 0x9d, 0x10,
0xa1, 0x96, 0xa7, 0x9a, 0x6c, 0x0b, 0x96, 0xfc, 0x69, 0x3c, 0x8b, 0x72, 0xe2, 0xea, 0x82, 0x27,
0x5b, 0xec, 0x2d, 0x58, 0x8f, 0x66, 0xd3, 0xe1, 0x28, 0x8e, 0x8e, 0x83, 0x74, 0x2a, 0x04, 0x87,
0x36, 0xb7, 0xe8, 0x55, 0x3b, 0xd8, 0x15, 0x80, 0x23, 0x5c, 0x86, 0x98, 0xa2, 0x49, 0x53, 0x18,
0x08, 0x73, 0xa1, 0x23, 0x5b, 0x3c, 0x98, 0x9c, 0xe4, 0xfd, 0x45, 0x22, 0x64, 0x61, 0x48, 0x23,
0x0f, 0xa6, 0x7c, 0x98, 0xe5, 0xfe, 0x34, 0xe9, 0x2f, 0xd1, 0x6a, 0x0c, 0x84, 0xfa, 0xe3, 0xdc,
0x0f, 0x87, 0xc7, 0x9c, 0x67, 0xfd, 0x65, 0xd9, 0xaf, 0x11, 0xf6, 0x26, 0xf4, 0xc6, 0x3c, 0xcb,
0x87, 0xfe, 0x78, 0x9c, 0xf2, 0x2c, 0xe3, 0x59, 0x7f, 0x85, 0x8e, 0xae, 0x84, 0xba, 0x7d, 0xd8,
0x7a, 0xc8, 0x73, 0x83, 0x3b, 0x99, 0x64, 0xbb, 0xbb, 0x0f, 0xcc, 0x80, 0xef, 0xf3, 0xdc, 0x0f,
0xc2, 0x8c, 0xbd, 0x03, 0x9d, 0xdc, 0x18, 0x4c, 0xa2, 0xda, 0xde, 0x65, 0x37, 0xe9, 0x8e, 0xdd,
0x34, 0x3e, 0xf0, 0xac, 0x71, 0xee, 0x7f, 0x3b, 0xd0, 0x3e, 0xe4, 0x91, 0xbe, 0x5d, 0x0c, 0x9a,
0xb8, 0x12, 0x79, 0x92, 0xf4, 0x9b, 0x7d, 0x1e, 0xda, 0xb4, 0xba, 0x2c, 0x4f, 0x83, 0x68, 0x42,
0x47, 0xd0, 0xf2, 0x00, 0xa1, 0x43, 0x42, 0xd8, 0x1a, 0x2c, 0xf8, 0xd3, 0x9c, 0x18, 0xbf, 0xe0,
0xe1, 0x4f, 0xbc, 0x77, 0x89, 0x3f, 0x9f, 0xf2, 0x28, 0x2f, 0x98, 0xdd, 0xf1, 0xda, 0x12, 0xdb,
0x43, 0x6e, 0xdf, 0x84, 0x0d, 0x73, 0x88, 0xa2, 0xbe, 0x48, 0xd4, 0xd7, 0x8d, 0x91, 0x72, 0x92,
0x6b, 0xb0, 0xaa, 0xc6, 0xa7, 0x62, 0xb1, 0xc4, 0xfe, 0x96, 0xd7, 0x93, 0xb0, 0xda, 0xc2, 0x75,
0x58, 0x3b, 0x0e, 0x22, 0x3f, 0x1c, 0x8e, 0xc2, 0xfc, 0x74, 0x38, 0xe6, 0x61, 0xee, 0xd3, 0x41,
0x2c, 0x7a, 0x3d, 0xc2, 0xef, 0x85, 0xf9, 0xe9, 0x7d, 0x44, 0xdd, 0x3f, 0x73, 0xa0, 0x23, 0x36,
0x2f, 0x2f, 0xfe, 0x1b, 0xd0, 0x55, 0x73, 0xf0, 0x34, 0x8d, 0x53, 0x29, 0x87, 0x36, 0xc8, 0x6e,
0xc0, 0x9a, 0x02, 0x92, 0x94, 0x07, 0x53, 0x7f, 0xc2, 0xe5, 0x6d, 0xaf, 0xe0, 0x6c, 0xb7, 0xa0,
0x98, 0xc6, 0xb3, 0x5c, 0x5c, 0xbd, 0xf6, 0x6e, 0x47, 0x1e, 0x8c, 0x87, 0x98, 0x67, 0x0f, 0x71,
0xff, 0xda, 0x81, 0xce, 0xbd, 0x13, 0x3f, 0x8a, 0x78, 0x78, 0x10, 0x07, 0x51, 0xce, 0x6e, 0x01,
0x3b, 0x9e, 0x45, 0xe3, 0x20, 0x9a, 0x0c, 0xf3, 0x67, 0xc1, 0x78, 0x78, 0x34, 0xcf, 0x79, 0x26,
0x8e, 0x68, 0xef, 0x82, 0x57, 0xd3, 0xc7, 0xde, 0x82, 0x35, 0x0b, 0xcd, 0xf2, 0x54, 0x9c, 0xdb,
0xde, 0x05, 0xaf, 0xd2, 0x83, 0x82, 0x1f, 0xcf, 0xf2, 0x64, 0x96, 0x0f, 0x83, 0x68, 0xcc, 0x9f,
0xd1, 0x1a, 0xbb, 0x9e, 0x85, 0xdd, 0xed, 0x41, 0xc7, 0xfc, 0xce, 0xfd, 0x0a, 0xac, 0xed, 0xe3,
0x8d, 0x88, 0x82, 0x68, 0x72, 0x47, 0x88, 0x2d, 0x5e, 0xd3, 0x64, 0x76, 0xf4, 0x94, 0xcf, 0x25,
0xdf, 0x64, 0x0b, 0x85, 0xea, 0x24, 0xce, 0x72, 0x29, 0x39, 0xf4, 0xdb, 0xfd, 0x37, 0x07, 0x56,
0x91, 0xf7, 0x1f, 0xfa, 0xd1, 0x5c, 0x9d, 0xdc, 0x3e, 0x74, 0x90, 0xd4, 0xe3, 0xf8, 0x8e, 0xb8,
0xec, 0x42, 0x88, 0xaf, 0x4b, 0x5e, 0x95, 0x46, 0xdf, 0x34, 0x87, 0xa2, 0x32, 0x9f, 0x7b, 0xd6,
0xd7, 0x28, 0xb6, 0xb9, 0x9f, 0x4e, 0x78, 0x4e, 0x6a, 0x40, 0xaa, 0x05, 0x10, 0xd0, 0xbd, 0x38,
0x3a, 0x66, 0x57, 0xa1, 0x93, 0xf9, 0xf9, 0x30, 0xe1, 0x29, 0x71, 0x8d, 0x44, 0x6f, 0xc1, 0x83,
0xcc, 0xcf, 0x0f, 0x78, 0x7a, 0x77, 0x9e, 0xf3, 0xc1, 0x57, 0x61, 0xbd, 0x32, 0x0b, 0x4a, 0x7b,
0xb1, 0x45, 0xfc, 0xc9, 0x36, 0x61, 0xf1, 0xd4, 0x0f, 0x67, 0x5c, 0x6a, 0x27, 0xd1, 0x78, 0xaf,
0xf1, 0xae, 0xe3, 0xbe, 0x09, 0x6b, 0xc5, 0xb2, 0xa5, 0x90, 0x31, 0x68, 0x22, 0x07, 0x25, 0x01,
0xfa, 0xed, 0xfe, 0x9e, 0x23, 0x06, 0xde, 0x8b, 0x03, 0x7d, 0xd3, 0x71, 0x20, 0x2a, 0x04, 0x35,
0x10, 0x7f, 0x9f, 0xab, 0x09, 0x7f, 0xfe, 0xcd, 0xba, 0xd7, 0x60, 0xdd, 0x58, 0xc2, 0x0b, 0x16,
0xfb, 0x57, 0x0e, 0xac, 0x3f, 0xe2, 0x67, 0xf2, 0xd4, 0xd5, 0x6a, 0xdf, 0x85, 0x66, 0x3e, 0x4f,
0x84, 0x29, 0xee, 0xed, 0xbe, 0x21, 0x0f, 0xad, 0x32, 0xee, 0xa6, 0x6c, 0x3e, 0x9e, 0x27, 0xdc,
0xa3, 0x2f, 0xdc, 0x6f, 0x40, 0xdb, 0x00, 0xd9, 0x36, 0x6c, 0x7c, 0xf4, 0xc1, 0xe3, 0x47, 0x0f,
0x0e, 0x0f, 0x87, 0x07, 0x4f, 0xee, 0x7e, 0xfd, 0xc1, 0x6f, 0x0c, 0xf7, 0xee, 0x1c, 0xee, 0xad,
0x5d, 0x60, 0x5b, 0xc0, 0x1e, 0x3d, 0x38, 0x7c, 0xfc, 0xe0, 0xbe, 0x85, 0x3b, 0x6c, 0x15, 0xda,
0x26, 0xd0, 0x70, 0x07, 0xd0, 0x7f, 0xc4, 0xcf, 0x3e, 0x0a, 0xf2, 0x88, 0x67, 0x99, 0x3d, 0xbd,
0x7b, 0x13, 0x98, 0xb9, 0x26, 0xb9, 0xcd, 0x3e, 0x2c, 0x4b, 0xdd, 0xab, 0x4c, 0x8f, 0x6c, 0xba,
0x6f, 0x02, 0x3b, 0x0c, 0x26, 0xd1, 0x87, 0x3c, 0xcb, 0xfc, 0x09, 0x57, 0x9b, 0x5d, 0x83, 0x85,
0x69, 0x36, 0x91, 0x5a, 0x12, 0x7f, 0xba, 0x5f, 0x80, 0x0d, 0x6b, 0x9c, 0x24, 0x7c, 0x19, 0x5a,
0x59, 0x30, 0x89, 0xfc, 0x7c, 0x96, 0x72, 0x49, 0xba, 0x00, 0xdc, 0xf7, 0x61, 0xf3, 0x5b, 0x3c,
0x0d, 0x8e, 0xe7, 0x2f, 0x23, 0x6f, 0xd3, 0x69, 0x94, 0xe9, 0x3c, 0x80, 0x8b, 0x25, 0x3a, 0x72,
0x7a, 0x21, 0x99, 0xf2, 0xfc, 0x56, 0x3c, 0xd1, 0x30, 0xee, 0x69, 0xc3, 0xbc, 0xa7, 0xee, 0x13,
0x60, 0xf7, 0xe2, 0x28, 0xe2, 0xa3, 0xfc, 0x80, 0xf3, 0xb4, 0x70, 0xb8, 0x0a, 0x31, 0x6c, 0xef,
0x6e, 0xcb, 0x83, 0x2d, 0x5f, 0x7e, 0x29, 0x9f, 0x0c, 0x9a, 0x09, 0x4f, 0xa7, 0x44, 0x78, 0xc5,
0xa3, 0xdf, 0xee, 0x45, 0xd8, 0xb0, 0xc8, 0x4a, 0xf3, 0xff, 0x36, 0x5c, 0xbc, 0x1f, 0x64, 0xa3,
0xea, 0x84, 0x7d, 0x58, 0x4e, 0x66, 0x47, 0xc3, 0xe2, 0x92, 0xa9, 0x26, 0x5a, 0xc5, 0xf2, 0x27,
0x92, 0xd8, 0x1f, 0x3a, 0xd0, 0xdc, 0x7b, 0xbc, 0x7f, 0x8f, 0x0d, 0x60, 0x25, 0x88, 0x46, 0xf1,
0x14, 0x6d, 0x89, 0xd8, 0xb4, 0x6e, 0x9f, 0x7b, 0x79, 0x2e, 0x43, 0x8b, 0x4c, 0x10, 0x1a, 0x7a,
0xe9, 0x1b, 0x15, 0x00, 0x3a, 0x19, 0xfc, 0x59, 0x12, 0xa4, 0xe4, 0x45, 0x28, 0xdf, 0xa0, 0x49,
0x2a, 0xb2, 0xda, 0xe1, 0xfe, 0xa4, 0x09, 0xdd, 0x3b, 0xa3, 0x3c, 0x38, 0xe5, 0x52, 0x85, 0xd3,
0xac, 0x04, 0xc8, 0xf5, 0xc8, 0x16, 0x1a, 0x9b, 0x94, 0x4f, 0xe3, 0x9c, 0x0f, 0xad, 0xc3, 0xb0,
0x41, 0x1c, 0x35, 0x12, 0x84, 0x86, 0x09, 0x1a, 0x03, 0x5a, 0x5f, 0xcb, 0xb3, 0x41, 0x64, 0x19,
0x02, 0xc3, 0x60, 0x4c, 0x2b, 0x6b, 0x7a, 0xaa, 0x89, 0xfc, 0x18, 0xf9, 0x89, 0x3f, 0x0a, 0xf2,
0xb9, 0xbc, 0xf3, 0xba, 0x8d, 0xb4, 0xc3, 0x78, 0xe4, 0x87, 0xc3, 0x23, 0x3f, 0xf4, 0xa3, 0x11,
0x97, 0xfe, 0x8c, 0x0d, 0xa2, 0xcb, 0x22, 0x97, 0xa4, 0x86, 0x09, 0xb7, 0xa6, 0x84, 0xa2, 0xeb,
0x33, 0x8a, 0xa7, 0xd3, 0x20, 0x47, 0x4f, 0xa7, 0xbf, 0x22, 0xf4, 0x4b, 0x81, 0xd0, 0x4e, 0x44,
0xeb, 0x4c, 0xf0, 0xb0, 0x25, 0x66, 0xb3, 0x40, 0xa4, 0x72, 0xcc, 0x39, 0xe9, 0xa9, 0xa7, 0x67,
0x7d, 0x10, 0x54, 0x0a, 0x04, 0x4f, 0x63, 0x16, 0x65, 0x3c, 0xcf, 0x43, 0x3e, 0xd6, 0x0b, 0x6a,
0xd3, 0xb0, 0x6a, 0x07, 0xbb, 0x05, 0x1b, 0xc2, 0xf9, 0xca, 0xfc, 0x3c, 0xce, 0x4e, 0x82, 0x6c,
0x98, 0xf1, 0x28, 0xef, 0x77, 0x68, 0x7c, 0x5d, 0x17, 0x7b, 0x17, 0xb6, 0x4b, 0x70, 0xca, 0x47,
0x3c, 0x38, 0xe5, 0xe3, 0x7e, 0x97, 0xbe, 0x3a, 0xaf, 0x9b, 0x5d, 0x85, 0x36, 0xfa, 0x9c, 0xb3,
0x64, 0xec, 0xa3, 0x79, 0xee, 0xd1, 0x39, 0x98, 0x10, 0x7b, 0x1b, 0xba, 0x09, 0x17, 0x36, 0xf4,
0x24, 0x0f, 0x47, 0x59, 0x7f, 0x95, 0x0c, 0x5c, 0x5b, 0x5e, 0x29, 0x94, 0x5f, 0xcf, 0x1e, 0x81,
0xa2, 0x39, 0xca, 0xc8, 0x8b, 0xf1, 0xe7, 0xfd, 0x35, 0x12, 0xba, 0x02, 0xc0, 0x9b, 0xb5, 0x1f,
0x64, 0xb9, 0x94, 0x34, 0xad, 0xe3, 0xf6, 0x60, 0xd3, 0x86, 0x75, 0x5c, 0xb3, 0x22, 0xc5, 0x26,
0xeb, 0xb7, 0x69, 0xea, 0x4d, 0x39, 0xb5, 0x25, 0xb1, 0x9e, 0x1e, 0xe5, 0xfe, 0xc4, 0x81, 0x26,
0xde, 0xb3, 0xf3, 0xef, 0xa4, 0xa9, 0x3a, 0x17, 0x2c, 0xd5, 0x49, 0xfe, 0x36, 0x7a, 0x23, 0x82,
0xe7, 0x42, 0x2e, 0x0d, 0xa4, 0xe8, 0x4f, 0xf9, 0xe8, 0x94, 0x84, 0x53, 0xf7, 0x23, 0x82, 0xa2,
0x8b, 0x26, 0x8b, 0xbe, 0x16, 0x92, 0xa9, 0xdb, 0xaa, 0x8f, 0xbe, 0x5c, 0x2e, 0xfa, 0xe8, 0xbb,
0x3e, 0x2c, 0x07, 0xd1, 0x51, 0x3c, 0x8b, 0xc6, 0x24, 0x85, 0x2b, 0x9e, 0x6a, 0x22, 0x37, 0x13,
0xf2, 0x60, 0x82, 0x29, 0x97, 0xe2, 0x57, 0x00, 0x2e, 0x43, 0x97, 0x26, 0x23, 0xbd, 0xa2, 0x59,
0xf9, 0x0e, 0xac, 0x1b, 0x98, 0xe4, 0xe3, 0xeb, 0xb0, 0x98, 0x20, 0x20, 0x1d, 0x14, 0x75, 0x7e,
0xa4, 0x90, 0x44, 0x8f, 0xbb, 0x86, 0x71, 0x6b, 0xfe, 0x41, 0x74, 0x1c, 0x2b, 0x4a, 0xff, 0xb8,
0x80, 0x81, 0xa6, 0x84, 0x24, 0xa1, 0xeb, 0xb0, 0x1a, 0x8c, 0x79, 0x94, 0x07, 0xf9, 0x7c, 0x68,
0x79, 0x4e, 0x65, 0x18, 0x15, 0xb9, 0x1f, 0x06, 0x7e, 0x26, 0x95, 0x84, 0x68, 0xb0, 0x5d, 0xd8,
0x44, 0xf9, 0x52, 0x22, 0xa3, 0x0f, 0x57, 0x38, 0x70, 0xb5, 0x7d, 0x78, 0x25, 0x10, 0x17, 0x4a,
0xa8, 0xf8, 0x44, 0x28, 0xb4, 0xba, 0x2e, 0xe4, 0x9a, 0xa0, 0x84, 0x5b, 0x5e, 0x14, 0x32, 0xa8,
0x81, 0x4a, 0xd4, 0xb4, 0x24, 0x9c, 0xc7, 0x72, 0xd4, 0x64, 0x44, 0x5e, 0x2b, 0x95, 0xc8, 0xeb,
0x3a, 0xac, 0x66, 0xf3, 0x68, 0xc4, 0xc7, 0xc3, 0x3c, 0xc6, 0x79, 0x83, 0x88, 0x4e, 0x67, 0xc5,
0x2b, 0xc3, 0x14, 0x23, 0xf2, 0x2c, 0x8f, 0x78, 0x4e, 0xba, 0x61, 0xc5, 0x53, 0x4d, 0x54, 0xb3,
0x34, 0x44, 0x88, 0x76, 0xcb, 0x93, 0x2d, 0xb4, 0x48, 0xb3, 0x34, 0xc8, 0xfa, 0x1d, 0x42, 0xe9,
0x37, 0xfb, 0x22, 0x5c, 0x3c, 0xc2, 0x88, 0xe6, 0x84, 0xfb, 0x63, 0x9e, 0xd2, 0xe9, 0x8b, 0x80,
0x4e, 0x5c, 0xf1, 0xfa, 0x4e, 0xf7, 0x53, 0x32, 0x8f, 0x3a, 0xa0, 0x7c, 0x42, 0xb7, 0x9a, 0xbd,
0x06, 0x2d, 0xb1, 0x93, 0xec, 0xc4, 0x97, 0x16, 0x7b, 0x85, 0x80, 0xc3, 0x13, 0x1f, 0xe3, 0x20,
0x8b, 0x39, 0x0d, 0xf2, 0xcb, 0xda, 0x84, 0xed, 0x09, 0xde, 0xbc, 0x01, 0x3d, 0x15, 0xaa, 0x66,
0xc3, 0x90, 0x1f, 0xe7, 0xca, 0xfd, 0x8e, 0x66, 0x53, 0x9c, 0x2e, 0xdb, 0xe7, 0xc7, 0xb9, 0xfb,
0x08, 0xd6, 0xe5, 0xed, 0xfc, 0x46, 0xc2, 0xd5, 0xd4, 0x5f, 0x2e, 0xdb, 0x06, 0x61, 0xa2, 0x37,
0xa4, 0x3c, 0x9a, 0x31, 0x44, 0xc9, 0x60, 0xb8, 0x1e, 0x30, 0xd9, 0x7d, 0x2f, 0x8c, 0x33, 0x2e,
0x09, 0xba, 0xd0, 0x19, 0x85, 0x71, 0xa6, 0x9c, 0x7c, 0xb9, 0x1d, 0x0b, 0xc3, 0x13, 0xc8, 0x66,
0xa3, 0x11, 0xde, 0x77, 0x61, 0xe4, 0x55, 0xd3, 0xfd, 0x1b, 0x07, 0x36, 0x88, 0x9a, 0xd2, 0x23,
0xda, 0x33, 0x7c, 0xf5, 0x65, 0x76, 0x46, 0x66, 0xe0, 0xb3, 0x09, 0x8b, 0xc7, 0x71, 0x3a, 0xe2,
0x72, 0x26, 0xd1, 0xf8, 0xe9, 0x7d, 0xdd, 0x66, 0xc5, 0xd7, 0xfd, 0x17, 0x07, 0xd6, 0x69, 0xa9,
0x87, 0xb9, 0x9f, 0xcf, 0x32, 0xb9, 0xfd, 0x5f, 0x85, 0x2e, 0x6e, 0x95, 0xab, 0x4b, 0x23, 0x17,
0xba, 0xa9, 0xef, 0x37, 0xa1, 0x62, 0xf0, 0xde, 0x05, 0xcf, 0x1e, 0xcc, 0xbe, 0x0a, 0x1d, 0xf3,
0xbd, 0x81, 0xd6, 0xdc, 0xde, 0xbd, 0xa4, 0x76, 0x59, 0x91, 0x9c, 0xbd, 0x0b, 0x9e, 0xf5, 0x01,
0xbb, 0x0d, 0x40, 0x56, 0x9b, 0xc8, 0xca, 0x40, 0xf1, 0x92, 0xcd, 0x24, 0xe3, 0xb0, 0xf6, 0x2e,
0x78, 0xc6, 0xf0, 0xbb, 0x2b, 0xb0, 0x24, 0xcc, 0x8c, 0xfb, 0x10, 0xba, 0xd6, 0x4a, 0x2d, 0x1f,
0xbe, 0x23, 0x7c, 0xf8, 0x4a, 0xc8, 0xd7, 0xa8, 0x86, 0x7c, 0xee, 0xdf, 0x37, 0x80, 0xa1, 0xb4,
0x95, 0x8e, 0x13, 0xed, 0x5c, 0x3c, 0xb6, 0xbc, 0x96, 0x8e, 0x67, 0x42, 0xec, 0x26, 0x30, 0xa3,
0xa9, 0x22, 0x7b, 0x61, 0x1d, 0x6a, 0x7a, 0x50, 0x8d, 0x09, 0x97, 0x43, 0x45, 0x98, 0xd2, 0x4b,
0x13, 0xe7, 0x56, 0xdb, 0x87, 0x06, 0x20, 0x99, 0x65, 0x27, 0x68, 0x87, 0x95, 0x5f, 0xa3, 0xda,
0x65, 0x01, 0x59, 0x7a, 0xa9, 0x80, 0x2c, 0x97, 0x05, 0x84, 0xec, 0x5d, 0x1a, 0x9c, 0xfa, 0x39,
0x57, 0x36, 0x44, 0x36, 0xd1, 0x8d, 0x99, 0x06, 0x11, 0x99, 0xe7, 0xe1, 0x14, 0x67, 0x97, 0x6e,
0x8c, 0x05, 0xba, 0x3f, 0x76, 0x60, 0x0d, 0x79, 0x67, 0xc9, 0xd7, 0x7b, 0x40, 0xe2, 0xfd, 0x8a,
0xe2, 0x65, 0x8d, 0xfd, 0xf9, 0xa5, 0xeb, 0x5d, 0x68, 0x11, 0xc1, 0x38, 0xe1, 0x91, 0x14, 0xae,
0xbe, 0x2d, 0x5c, 0x85, 0x66, 0xd9, 0xbb, 0xe0, 0x15, 0x83, 0x0d, 0xd1, 0xfa, 0x67, 0x07, 0xda,
0x72, 0x99, 0x3f, 0xb3, 0xb3, 0x3d, 0x80, 0x15, 0x94, 0x32, 0xc3, 0x97, 0xd5, 0x6d, 0xb4, 0x03,
0x53, 0x8c, 0x68, 0xd0, 0xf0, 0x59, 0x8e, 0x76, 0x19, 0x46, 0x2b, 0x46, 0x4a, 0x34, 0x1b, 0xe6,
0x41, 0x38, 0x54, 0xbd, 0xf2, 0xc9, 0xae, 0xae, 0x0b, 0x75, 0x49, 0x96, 0xfb, 0x13, 0x2e, 0x0d,
0x94, 0x68, 0x60, 0x44, 0x21, 0x37, 0x54, 0x76, 0xa2, 0x7e, 0x04, 0xb0, 0x5d, 0xe9, 0xd2, 0x8e,
0x94, 0xf4, 0x1d, 0xc3, 0x60, 0x7a, 0x14, 0x6b, 0x37, 0xd4, 0x31, 0xdd, 0x4a, 0xab, 0x8b, 0x4d,
0xe0, 0xa2, 0xb2, 0xc4, 0xc8, 0xd3, 0xc2, 0xee, 0x36, 0xc8, 0x85, 0x78, 0xdb, 0x96, 0x81, 0xf2,
0x84, 0x0a, 0x37, 0x6f, 0x63, 0x3d, 0x3d, 0x76, 0x02, 0x7d, 0x6d, 0xf2, 0xa5, 0xda, 0x36, 0xdc,
0x02, 0x9c, 0xeb, 0xad, 0x97, 0xcc, 0x45, 0x3a, 0x66, 0xac, 0xa6, 0x39, 0x97, 0x1a, 0x9b, 0xc3,
0x15, 0xd5, 0x47, 0x7a, 0xb9, 0x3a, 0x5f, 0xf3, 0x95, 0xf6, 0xf6, 0x3e, 0x7e, 0x6c, 0x4f, 0xfa,
0x12, 0xc2, 0x83, 0x1f, 0x39, 0xd0, 0xb3, 0xc9, 0xa1, 0xe8, 0xc8, 0x78, 0x44, 0x29, 0x18, 0xe5,
0x4a, 0x95, 0xe0, 0x6a, 0x44, 0xd5, 0xa8, 0x8b, 0xa8, 0xcc, 0xb8, 0x69, 0xe1, 0x65, 0x71, 0x53,
0xf3, 0xd5, 0xe2, 0xa6, 0xc5, 0xba, 0xb8, 0x69, 0xf0, 0x5f, 0x0e, 0xb0, 0xea, 0xf9, 0xb2, 0x87,
0x22, 0xa4, 0x8b, 0x78, 0x28, 0xf5, 0xc4, 0x2f, 0xbf, 0x9a, 0x8c, 0x28, 0x1e, 0xaa, 0xaf, 0x51,
0x58, 0x4d, 0x45, 0x60, 0xba, 0x22, 0x5d, 0xaf, 0xae, 0xab, 0x14, 0xc9, 0x35, 0x5f, 0x1e, 0xc9,
0x2d, 0xbe, 0x3c, 0x92, 0x5b, 0x2a, 0x47, 0x72, 0x83, 0xdf, 0x81, 0xae, 0x75, 0xea, 0xff, 0x7b,
0x3b, 0x2e, 0xbb, 0x31, 0xe2, 0x80, 0x2d, 0x6c, 0xf0, 0x1f, 0x0d, 0x60, 0x55, 0xc9, 0xfb, 0x7f,
0x5d, 0x03, 0xc9, 0x91, 0xa5, 0x40, 0x16, 0xa4, 0x1c, 0x59, 0xaa, 0xe3, 0xff, 0x52, 0x29, 0xbe,
0x05, 0xeb, 0x29, 0x1f, 0xc5, 0xa7, 0x94, 0xb6, 0xb2, 0x5f, 0x01, 0xaa, 0x1d, 0xe8, 0xc8, 0xd9,
0xf1, 0xeb, 0x8a, 0x95, 0x65, 0x30, 0x2c, 0x43, 0x29, 0x8c, 0x75, 0xbf, 0x0c, 0x9b, 0x22, 0xf9,
0x73, 0x57, 0x90, 0x52, 0xbe, 0xc4, 0xeb, 0xd0, 0x39, 0x13, 0xcf, 0x74, 0xc3, 0x38, 0x0a, 0xe7,
0xd2, 0x88, 0xb4, 0x25, 0xf6, 0x8d, 0x28, 0x9c, 0xbb, 0xdf, 0x77, 0xe0, 0x62, 0xe9, 0xdb, 0xe2,
0xb5, 0x5e, 0xa8, 0x5a, 0x5b, 0xff, 0xda, 0x20, 0x6e, 0x51, 0xca, 0xb8, 0xb1, 0x45, 0x61, 0x92,
0xaa, 0x1d, 0xc8, 0xc2, 0x59, 0x54, 0x1d, 0x2f, 0x0e, 0xa6, 0xae, 0xcb, 0xdd, 0x86, 0x8b, 0xf2,
0xf0, 0xed, 0xbd, 0xb9, 0xbb, 0xb0, 0x55, 0xee, 0x28, 0x5e, 0x1b, 0xed, 0x25, 0xab, 0xa6, 0xfb,
0xdb, 0xc0, 0xbe, 0x39, 0xe3, 0xe9, 0x9c, 0xf2, 0x02, 0xfa, 0x69, 0x75, 0xbb, 0x1c, 0x7c, 0x2f,
0x25, 0xb3, 0xa3, 0xaf, 0xf3, 0xb9, 0x4a, 0xbc, 0x34, 0x8a, 0xc4, 0xcb, 0xe7, 0x00, 0x30, 0x9a,
0xa0, 0x44, 0x82, 0x4a, 0x85, 0x61, 0xb0, 0x26, 0x08, 0xba, 0xb7, 0x61, 0xc3, 0xa2, 0xaf, 0x39,
0xb9, 0x24, 0xbf, 0x10, 0x11, 0xad, 0x9d, 0x9e, 0x90, 0x7d, 0xee, 0x9f, 0x3b, 0xb0, 0xb0, 0x17,
0x27, 0xe6, 0x63, 0x93, 0x63, 0x3f, 0x36, 0x49, 0xd5, 0x3a, 0xd4, 0x9a, 0xb3, 0x21, 0x15, 0x83,
0x09, 0xa2, 0x62, 0xf4, 0xa7, 0x39, 0xc6, 0x74, 0xc7, 0x71, 0x7a, 0xe6, 0xa7, 0x63, 0xc9, 0xde,
0x12, 0x8a, 0xbb, 0x2b, 0xf4, 0x0f, 0xfe, 0x44, 0x9f, 0x82, 0x5e, 0xdc, 0xe6, 0x32, 0x0c, 0x95,
0x2d, 0xf7, 0x4f, 0x1c, 0x58, 0xa4, 0xb5, 0xe2, 0x65, 0x11, 0xc7, 0x4f, 0x39, 0x39, 0x7a, 0xd0,
0x73, 0xc4, 0x65, 0x29, 0xc1, 0xa5, 0x4c, 0x5d, 0xa3, 0x92, 0xa9, 0xbb, 0x0c, 0x2d, 0xd1, 0x2a,
0x52, 0x5b, 0x05, 0xc0, 0xae, 0x40, 0xf3, 0x24, 0x4e, 0x94, 0x89, 0x03, 0xf5, 0x82, 0x13, 0x27,
0x1e, 0xe1, 0xee, 0x0d, 0x58, 0x7d, 0x14, 0x8f, 0xb9, 0xf1, 0x00, 0x70, 0xee, 0x29, 0xba, 0xbf,
0xeb, 0xc0, 0x8a, 0x1a, 0xcc, 0xae, 0x43, 0x13, 0x2d, 0x55, 0xc9, 0x37, 0xd4, 0xaf, 0xad, 0x38,
0xce, 0xa3, 0x11, 0xa8, 0x61, 0x28, 0x70, 0x2c, 0x3c, 0x09, 0x15, 0x36, 0x16, 0x36, 0xfa, 0x4d,
0xe8, 0x89, 0x35, 0x97, 0x6c, 0x59, 0x09, 0x75, 0xff, 0xd6, 0x81, 0xae, 0x35, 0x07, 0x7a, 0xf9,
0xa1, 0x9f, 0xe5, 0xf2, 0xed, 0x4a, 0x32, 0xd1, 0x84, 0xcc, 0x27, 0xa1, 0x86, 0xfd, 0x24, 0xa4,
0x1f, 0x2b, 0x16, 0xcc, 0xc7, 0x8a, 0x5b, 0xd0, 0x2a, 0xb2, 0x9e, 0x4d, 0x4b, 0x73, 0xe0, 0x8c,
0xea, 0x1d, 0xb9, 0x18, 0x84, 0x74, 0x46, 0x71, 0x18, 0xa7, 0x32, 0x29, 0x28, 0x1a, 0xee, 0x6d,
0x68, 0x1b, 0xe3, 0x71, 0x19, 0x11, 0xcf, 0xcf, 0xe2, 0xf4, 0xa9, 0x7a, 0x99, 0x92, 0x4d, 0x9d,
0x3f, 0x69, 0x14, 0xf9, 0x13, 0xf7, 0xef, 0x1c, 0xe8, 0xa2, 0xa4, 0x04, 0xd1, 0xe4, 0x20, 0x0e,
0x83, 0xd1, 0x9c, 0x24, 0x46, 0x09, 0x85, 0xcc, 0x16, 0x2a, 0x89, 0xb1, 0x61, 0x74, 0x09, 0x94,
0x93, 0x2f, 0xe5, 0x45, 0xb7, 0x51, 0xf2, 0xd1, 0xb4, 0x1d, 0xf9, 0x19, 0x17, 0x51, 0x81, 0x54,
0xe5, 0x16, 0x88, 0xda, 0x05, 0x81, 0xd4, 0xcf, 0xf9, 0x70, 0x1a, 0x84, 0x61, 0x20, 0xc6, 0x0a,
0x09, 0xaf, 0xeb, 0x72, 0x7f, 0xd8, 0x80, 0xb6, 0xd4, 0x22, 0x0f, 0xc6, 0x13, 0xf1, 0xc8, 0x2a,
0xfd, 0x14, 0x7d, 0xfd, 0x0c, 0x44, 0xf5, 0x5b, 0x9e, 0x8d, 0x81, 0x94, 0x8f, 0x75, 0xa1, 0x7a,
0xac, 0x97, 0xa1, 0x85, 0xe2, 0xf5, 0x36, 0xb9, 0x50, 0x22, 0x49, 0x5e, 0x00, 0xaa, 0x77, 0x97,
0x7a, 0x17, 0x8b, 0x5e, 0x02, 0x2c, 0xa7, 0x69, 0xa9, 0xe4, 0x34, 0xbd, 0x0b, 0x1d, 0x49, 0x86,
0xf8, 0x4e, 0x31, 0x57, 0x21, 0xe0, 0xd6, 0x99, 0x78, 0xd6, 0x48, 0xf5, 0xe5, 0xae, 0xfa, 0x72,
0xe5, 0x65, 0x5f, 0xaa, 0x91, 0x94, 0x79, 0x10, 0xbc, 0x79, 0x98, 0xfa, 0xc9, 0x89, 0xd2, 0xcc,
0x63, 0x9d, 0x5f, 0x25, 0x98, 0xdd, 0x80, 0x45, 0xfc, 0x4c, 0x69, 0xbf, 0xfa, 0x4b, 0x27, 0x86,
0xb0, 0xeb, 0xb0, 0xc8, 0xc7, 0x13, 0xae, 0x1c, 0x77, 0x66, 0x87, 0x50, 0x78, 0x46, 0x9e, 0x18,
0x80, 0x2a, 0x00, 0xd1, 0x92, 0x0a, 0xb0, 0x35, 0xe7, 0x12, 0x36, 0x3f, 0x18, 0xbb, 0x9b, 0xc0,
0x1e, 0x09, 0xa9, 0x35, 0x9f, 0x0c, 0xff, 0x60, 0x01, 0xda, 0x06, 0x8c, 0xb7, 0x79, 0x82, 0x0b,
0x1e, 0x8e, 0x03, 0x7f, 0xca, 0x73, 0x9e, 0x4a, 0x49, 0x2d, 0xa1, 0xa4, 0x60, 0x4f, 0x27, 0xc3,
0x78, 0x96, 0x0f, 0xc7, 0x7c, 0x92, 0x72, 0x61, 0xef, 0x1c, 0xaf, 0x84, 0xe2, 0xb8, 0xa9, 0xff,
0xcc, 0x1c, 0x27, 0xe4, 0xa1, 0x84, 0xaa, 0x07, 0x40, 0xc1, 0xa3, 0x66, 0xf1, 0x00, 0x28, 0x38,
0x52, 0xd6, 0x43, 0x8b, 0x35, 0x7a, 0xe8, 0x1d, 0xd8, 0x12, 0x1a, 0x47, 0xde, 0xcd, 0x61, 0x49,
0x4c, 0xce, 0xe9, 0x65, 0x37, 0x60, 0x0d, 0xd7, 0xac, 0x04, 0x3c, 0x0b, 0x3e, 0x15, 0xc1, 0xba,
0xe3, 0x55, 0x70, 0x1c, 0x8b, 0xd7, 0xd1, 0x1a, 0x2b, 0xb2, 0x10, 0x15, 0x9c, 0xc6, 0xfa, 0xcf,
0xec, 0xb1, 0x2d, 0x39, 0xb6, 0x84, 0xbb, 0x5d, 0x68, 0x1f, 0xe6, 0x71, 0xa2, 0x0e, 0xa5, 0x07,
0x1d, 0xd1, 0x94, 0x99, 0xa7, 0xd7, 0xe0, 0x12, 0x49, 0xd1, 0xe3, 0x38, 0x89, 0xc3, 0x78, 0x32,
0x3f, 0x9c, 0x1d, 0x65, 0xa3, 0x34, 0x48, 0xd0, 0xa1, 0x76, 0xff, 0xc9, 0x81, 0x0d, 0xab, 0x57,
0xbe, 0x04, 0x7c, 0x51, 0x88, 0xb4, 0x4e, 0x16, 0x08, 0xc1, 0x5b, 0x37, 0xd4, 0xa1, 0x18, 0x28,
0xde, 0x55, 0x9e, 0xc8, 0xfc, 0xc1, 0x1d, 0x58, 0x55, 0x2b, 0x53, 0x1f, 0x0a, 0x29, 0xec, 0x57,
0xa5, 0x50, 0x7e, 0xdf, 0x93, 0x1f, 0x28, 0x12, 0xbf, 0x26, 0xdc, 0x52, 0x3e, 0xa6, 0x3d, 0xaa,
0x90, 0x70, 0xa0, 0xbe, 0x37, 0x7d, 0x61, 0xb5, 0x82, 0x91, 0x06, 0x33, 0xf7, 0x8f, 0x1c, 0x80,
0x62, 0x75, 0x28, 0x18, 0x85, 0x4a, 0x17, 0xd5, 0x51, 0x86, 0xfa, 0x7e, 0x1d, 0x3a, 0xfa, 0x19,
0xbb, 0xb0, 0x12, 0x6d, 0x85, 0xa1, 0x03, 0x73, 0x0d, 0x56, 0x27, 0x61, 0x7c, 0x44, 0x36, 0x97,
0x52, 0x99, 0x99, 0xcc, 0xbf, 0xf5, 0x04, 0xfc, 0xbe, 0x44, 0x0b, 0x93, 0xd2, 0x34, 0x4c, 0x8a,
0xfb, 0xc7, 0x0d, 0xfd, 0x2c, 0x5a, 0xec, 0xf9, 0xdc, 0x5b, 0xc6, 0x76, 0x2b, 0xca, 0xf1, 0x9c,
0x57, 0x48, 0x7a, 0xfc, 0x38, 0x78, 0x69, 0x1c, 0x78, 0x1b, 0x7a, 0xa9, 0xd0, 0x3e, 0x4a, 0x35,
0x35, 0x5f, 0xa0, 0x9a, 0xba, 0xa9, 0x65, 0x77, 0x7e, 0x11, 0xd6, 0xfc, 0xf1, 0x29, 0x4f, 0xf3,
0x80, 0x02, 0x02, 0x32, 0xfa, 0x42, 0xa1, 0xae, 0x1a, 0x38, 0xd9, 0xe2, 0x6b, 0xb0, 0x2a, 0x73,
0x9e, 0x7a, 0xa4, 0x2c, 0x7d, 0x29, 0x60, 0x1c, 0xe8, 0xfe, 0x40, 0xbd, 0xc0, 0xda, 0x67, 0x78,
0x3e, 0x47, 0xcc, 0xdd, 0x35, 0x4a, 0xbb, 0xfb, 0x05, 0xf9, 0x1a, 0x3a, 0x56, 0x51, 0x87, 0x7c,
0x97, 0x16, 0xa0, 0x7c, 0xbd, 0xb6, 0x59, 0xda, 0x7c, 0x15, 0x96, 0xba, 0xdf, 0x5f, 0x80, 0xe5,
0x0f, 0xa2, 0xd3, 0x38, 0x18, 0xd1, 0xdb, 0xe4, 0x94, 0x4f, 0x63, 0x55, 0x5f, 0x80, 0xbf, 0xd1,
0xa2, 0x53, 0x52, 0x2d, 0xc9, 0xe5, 0xe3, 0xa2, 0x6a, 0xa2, 0x75, 0x4b, 0x8b, 0x9a, 0x1b, 0x21,
0x29, 0x06, 0x82, 0xfe, 0x61, 0x6a, 0x16, 0x1c, 0xc9, 0x56, 0x51, 0xa0, 0xb1, 0x68, 0x14, 0x68,
0xd0, 0x4b, 0xb6, 0xc8, 0x17, 0x12, 0x3b, 0x57, 0x3c, 0xd5, 0x24, 0x3f, 0x36, 0xe5, 0x22, 0x26,
0x26, 0x3b, 0xb9, 0x2c, 0xfd, 0x58, 0x13, 0x44, 0x5b, 0x2a, 0x3e, 0x10, 0x63, 0x84, 0xae, 0x31,
0x21, 0xf4, 0x2d, 0xca, 0x35, 0x4b, 0x2d, 0x71, 0xc4, 0x25, 0x18, 0x15, 0xd2, 0x98, 0x6b, 0xbd,
0x21, 0xf6, 0x00, 0xa2, 0xa6, 0xa8, 0x8c, 0x1b, 0x5e, 0xb0, 0xc8, 0x7b, 0xca, 0x16, 0xf9, 0x20,
0x7e, 0x18, 0x1e, 0xf9, 0xa3, 0xa7, 0x54, 0x49, 0x46, 0x69, 0xce, 0x96, 0x67, 0x83, 0xb8, 0x6a,
0x2a, 0x8c, 0x92, 0x24, 0xba, 0x22, 0x4d, 0x69, 0x40, 0xee, 0xb7, 0x80, 0xdd, 0x19, 0x8f, 0xe5,
0x09, 0xe9, 0x18, 0xa1, 0xe0, 0xad, 0x63, 0xf1, 0xb6, 0x66, 0x8f, 0x8d, 0xda, 0x3d, 0xba, 0x0f,
0xa0, 0x7d, 0x60, 0x14, 0x80, 0xd1, 0x61, 0xaa, 0xd2, 0x2f, 0x29, 0x00, 0x06, 0x62, 0x4c, 0xd8,
0x30, 0x27, 0x74, 0x7f, 0x05, 0xd8, 0x7e, 0x90, 0xe5, 0x7a, 0x7d, 0x3a, 0x92, 0xd4, 0x0f, 0x62,
0x46, 0x24, 0x29, 0x31, 0x8a, 0x24, 0xef, 0x88, 0x6c, 0x69, 0x79, 0x63, 0x37, 0x60, 0x25, 0x10,
0x90, 0xd2, 0xc3, 0x3d, 0x29, 0xc0, 0x6a, 0xa4, 0xee, 0x47, 0x87, 0x42, 0x82, 0x96, 0x9a, 0xff,
0xa1, 0x03, 0xcb, 0x72, 0x6b, 0x68, 0x0e, 0xad, 0xd2, 0x37, 0xb1, 0x31, 0x0b, 0xab, 0x2f, 0x18,
0xaa, 0x4a, 0xdd, 0x42, 0x9d, 0xd4, 0x31, 0x68, 0x26, 0x7e, 0x7e, 0x42, 0x1e, 0x74, 0xcb, 0xa3,
0xdf, 0x2a, 0x52, 0x5a, 0x2c, 0x22, 0xa5, 0xba, 0x1a, 0x35, 0xa1, 0x33, 0x2a, 0xb8, 0xca, 0x22,
0xcb, 0x0d, 0xe8, 0x07, 0xd0, 0xbb, 0x22, 0x8b, 0x5c, 0xc0, 0x05, 0xbf, 0x24, 0x89, 0x32, 0xbf,
0xe4, 0x50, 0x4f, 0xf7, 0xbb, 0x03, 0xe8, 0xdf, 0xe7, 0x21, 0xcf, 0xf9, 0x9d, 0x30, 0x2c, 0xd3,
0x7f, 0x0d, 0x2e, 0xd5, 0xf4, 0x49, 0xab, 0xfa, 0x3e, 0xac, 0xdf, 0xe7, 0x47, 0xb3, 0xc9, 0x3e,
0x3f, 0x2d, 0x32, 0x0f, 0x0c, 0x9a, 0xd9, 0x49, 0x7c, 0x26, 0xcf, 0x96, 0x7e, 0x63, 0xc0, 0x1b,
0xe2, 0x98, 0x61, 0x96, 0xf0, 0x91, 0xaa, 0x8c, 0x21, 0xe4, 0x30, 0xe1, 0x23, 0xf7, 0x1d, 0x60,
0x26, 0x1d, 0xb9, 0x05, 0xbc, 0xb9, 0xb3, 0xa3, 0x61, 0x36, 0xcf, 0x72, 0x3e, 0x55, 0x25, 0x3f,
0x26, 0xe4, 0x5e, 0x83, 0xce, 0x81, 0x3f, 0xf7, 0xf8, 0x27, 0xb2, 0xfa, 0x10, 0x83, 0x37, 0x7f,
0x8e, 0xa2, 0xac, 0x83, 0x37, 0xea, 0x76, 0xff, 0xa1, 0x01, 0x4b, 0x62, 0x24, 0x52, 0x1d, 0xf3,
0x2c, 0x0f, 0x22, 0xf1, 0x42, 0x2f, 0xa9, 0x1a, 0x50, 0x45, 0x36, 0x1a, 0x35, 0xb2, 0x21, 0xdd,
0x29, 0x55, 0x5f, 0x20, 0x85, 0xc0, 0xc2, 0x28, 0x36, 0xd5, 0x39, 0xcb, 0xa6, 0x8c, 0x4d, 0x15,
0x50, 0x8a, 0x92, 0x0b, 0xfd, 0x20, 0xd6, 0xa7, 0x84, 0x56, 0x8a, 0x83, 0x09, 0xd5, 0x6a, 0xa1,
0x65, 0x21, 0x35, 0x15, 0x2d, 0x54, 0xd1, 0x36, 0x2b, 0xaf, 0xa0, 0x6d, 0x84, 0x8f, 0x65, 0x69,
0x1b, 0x06, 0x6b, 0xef, 0x73, 0xee, 0xf1, 0x24, 0x4e, 0x55, 0x09, 0xa7, 0xfb, 0x3d, 0x07, 0xd6,
0xa4, 0xf5, 0xd0, 0x7d, 0xec, 0x75, 0xcb, 0xd4, 0x38, 0x75, 0x8f, 0xb6, 0x6f, 0x40, 0x97, 0x82,
0x2d, 0x8c, 0xa4, 0x28, 0xb2, 0x92, 0xef, 0x0f, 0x16, 0x88, 0x6b, 0x52, 0xcf, 0x90, 0xd3, 0x20,
0x94, 0x0c, 0x36, 0x21, 0x34, 0x8b, 0x2a, 0x18, 0x23, 0xf6, 0x3a, 0x9e, 0x6e, 0xbb, 0x07, 0xb0,
0x6e, 0xac, 0x57, 0x0a, 0xd4, 0x6d, 0x50, 0x89, 0x4b, 0xf1, 0x9c, 0x20, 0xee, 0xc5, 0xb6, 0x6d,
0x08, 0x8b, 0xcf, 0xac, 0xc1, 0xee, 0xbf, 0x3a, 0xb0, 0x21, 0x9c, 0x02, 0xe9, 0x72, 0xe9, 0x3a,
0xa8, 0x25, 0xe1, 0x05, 0x09, 0x81, 0xdf, 0xbb, 0xe0, 0xc9, 0x36, 0xfb, 0xd2, 0x2b, 0x3a, 0x32,
0x3a, 0x47, 0x78, 0x0e, 0x7b, 0x16, 0xea, 0xd8, 0xf3, 0x82, 0xcd, 0xd7, 0x05, 0xcb, 0x8b, 0xb5,
0xc1, 0xf2, 0xdd, 0x65, 0x58, 0xcc, 0x46, 0x71, 0xc2, 0xdd, 0x2d, 0xd8, 0xb4, 0x37, 0x27, 0x58,
0xb6, 0xfb, 0x83, 0x06, 0xf4, 0xc4, 0xbb, 0x9e, 0x28, 0x0e, 0xe7, 0x29, 0xfb, 0x10, 0x96, 0x65,
0x29, 0x3e, 0xbb, 0x28, 0x77, 0x63, 0x17, 0xff, 0x0f, 0xb6, 0xca, 0xb0, 0x54, 0x17, 0x1b, 0xbf,
0xff, 0xe3, 0x7f, 0xff, 0xd3, 0x46, 0x97, 0xb5, 0x77, 0x4e, 0xdf, 0xde, 0x99, 0xf0, 0x28, 0x43,
0x1a, 0xbf, 0x09, 0x50, 0x54, 0xb3, 0xb3, 0xbe, 0x56, 0xea, 0xa5, 0xea, 0xfb, 0xc1, 0xa5, 0x9a,
0x1e, 0x49, 0xf7, 0x12, 0xd1, 0xdd, 0x70, 0x7b, 0x48, 0x37, 0x88, 0x82, 0x5c, 0x94, 0xb6, 0xbf,
0xe7, 0xdc, 0x60, 0x63, 0xe8, 0x98, 0x55, 0xed, 0x4c, 0xf9, 0xd0, 0x35, 0xa5, 0xf2, 0x83, 0xd7,
0x6a, 0xfb, 0x54, 0x00, 0x41, 0x73, 0x5c, 0x74, 0xd7, 0x70, 0x8e, 0x19, 0x8d, 0xd0, 0xb3, 0xec,
0xfe, 0xe7, 0x00, 0x5a, 0x3a, 0x0e, 0x65, 0xdf, 0x85, 0xae, 0xf5, 0x14, 0xca, 0x14, 0xe1, 0xba,
0xc7, 0xd5, 0xc1, 0xe5, 0xfa, 0x4e, 0x39, 0xed, 0x15, 0x9a, 0xb6, 0xcf, 0xb6, 0x70, 0x5a, 0xf9,
0xfe, 0xb8, 0x43, 0x6f, 0xc4, 0xa2, 0xd2, 0xe2, 0x29, 0xf4, 0xec, 0xe7, 0x4b, 0x76, 0xd9, 0x96,
0xb0, 0xd2, 0x6c, 0x9f, 0x3b, 0xa7, 0x57, 0x4e, 0x77, 0x99, 0xa6, 0xdb, 0x62, 0x9b, 0xe6, 0x74,
0x3a, 0x3e, 0xe4, 0x54, 0x1b, 0x63, 0x96, 0xbb, 0xb3, 0xcf, 0xe9, 0xa3, 0xae, 0x2b, 0x83, 0xd7,
0x87, 0x56, 0xad, 0x85, 0x77, 0xfb, 0x34, 0x15, 0x63, 0xc4, 0x50, 0xb3, 0xda, 0x9d, 0x7d, 0x0c,
0x2d, 0x5d, 0xe2, 0xca, 0xb6, 0x8d, 0xba, 0x62, 0xb3, 0xee, 0x76, 0xd0, 0xaf, 0x76, 0xd4, 0x1d,
0x95, 0x49, 0x19, 0x05, 0x62, 0x1f, 0x2e, 0x4a, 0xa7, 0xe0, 0x88, 0xff, 0x34, 0x3b, 0xa9, 0x29,
0xd2, 0xbf, 0xe5, 0xb0, 0xdb, 0xb0, 0xa2, 0x2a, 0x87, 0xd9, 0x56, 0x7d, 0x05, 0xf4, 0x60, 0xbb,
0x82, 0x4b, 0x75, 0x74, 0x07, 0xa0, 0x28, 0x72, 0xd5, 0x92, 0x5f, 0xa9, 0xc5, 0xd5, 0x4c, 0xac,
0xa9, 0x88, 0x9d, 0x50, 0x8d, 0xaf, 0x5d, 0x43, 0xcb, 0x3e, 0x5f, 0x8c, 0xaf, 0xad, 0xae, 0x7d,
0x01, 0x41, 0x77, 0x8b, 0x78, 0xb7, 0xc6, 0xe8, 0x2a, 0x45, 0xfc, 0x4c, 0x55, 0x89, 0xdd, 0x87,
0xb6, 0x51, 0x38, 0xcb, 0x14, 0x85, 0x6a, 0xd1, 0xed, 0x60, 0x50, 0xd7, 0x25, 0x97, 0xfb, 0x35,
0xe8, 0x5a, 0x15, 0xb0, 0xfa, 0x66, 0xd4, 0xd5, 0xd7, 0xea, 0x9b, 0x51, 0x5f, 0x34, 0xfb, 0x6d,
0x68, 0x1b, 0xf5, 0xaa, 0xcc, 0xc8, 0xb1, 0x97, 0x2a, 0x55, 0xf5, 0x8a, 0xea, 0xca, 0x5b, 0x37,
0x69, 0xbf, 0x3d, 0xb7, 0x85, 0xfb, 0xa5, 0x52, 0x29, 0x14, 0x92, 0xef, 0x42, 0xcf, 0xae, 0x60,
0xd5, 0xb7, 0xaa, 0xb6, 0x16, 0x56, 0xdf, 0xaa, 0x73, 0xca, 0x5e, 0xa5, 0x40, 0xde, 0xd8, 0xd0,
0x93, 0xec, 0x7c, 0x26, 0x5f, 0x61, 0x9f, 0xb3, 0x6f, 0xa2, 0xea, 0x90, 0xb5, 0x6b, 0xac, 0xa8,
0xdb, 0xb5, 0x2b, 0xdc, 0xb4, 0xb4, 0x57, 0xca, 0xdc, 0xdc, 0x75, 0x22, 0xde, 0x66, 0xc5, 0x0e,
0x84, 0x86, 0xa6, 0x1a, 0x36, 0x43, 0x43, 0x9b, 0x65, 0x6e, 0x86, 0x86, 0xb6, 0x4a, 0xdd, 0xca,
0x1a, 0x3a, 0x0f, 0x90, 0x46, 0x04, 0xab, 0xa5, 0xbc, 0x9a, 0xbe, 0x2c, 0xf5, 0x59, 0xf9, 0xc1,
0x95, 0x17, 0xa7, 0xe3, 0x6c, 0x35, 0xa3, 0xd4, 0xcb, 0x8e, 0x2a, 0xa2, 0xf8, 0x2d, 0xe8, 0x98,
0x85, 0x91, 0x5a, 0x67, 0xd7, 0x14, 0x51, 0x6a, 0x9d, 0x5d, 0x57, 0x49, 0xa9, 0x0e, 0x97, 0x75,
0xcc, 0x69, 0xd8, 0xb7, 0x61, 0xd5, 0xc8, 0xe0, 0x1e, 0xce, 0xa3, 0x91, 0x16, 0x9e, 0x6a, 0x1d,
0xcd, 0xa0, 0xce, 0x60, 0xbb, 0xdb, 0x44, 0x78, 0xdd, 0xb5, 0x08, 0xa3, 0xe0, 0xdc, 0x83, 0xb6,
0x99, 0x1d, 0x7e, 0x01, 0xdd, 0x6d, 0xa3, 0xcb, 0x2c, 0x3f, 0xb9, 0xe5, 0xb0, 0xbf, 0x70, 0xa0,
0x63, 0x56, 0x68, 0x31, 0xeb, 0xe1, 0xa7, 0x44, 0xa7, 0x6f, 0xf6, 0x99, 0x84, 0x5c, 0x8f, 0x16,
0xb9, 0x7f, 0xe3, 0x6b, 0x16, 0x93, 0x3f, 0xb3, 0x7c, 0xb1, 0x9b, 0xe5, 0x7f, 0x99, 0x3c, 0x2f,
0x0f, 0x30, 0x6b, 0x8d, 0x9e, 0xdf, 0x72, 0xd8, 0x7b, 0xe2, 0x9f, 0x48, 0x2a, 0x8e, 0x62, 0x86,
0x72, 0x2b, 0xb3, 0xcc, 0xfc, 0xd3, 0xce, 0x75, 0xe7, 0x96, 0xc3, 0xbe, 0x23, 0xfe, 0x4c, 0x22,
0xbf, 0x25, 0xce, 0xbf, 0xea, 0xf7, 0xee, 0x1b, 0xb4, 0x9b, 0x2b, 0xee, 0x25, 0x6b, 0x37, 0x65,
0xed, 0x7e, 0x00, 0x50, 0x04, 0xc5, 0xac, 0x14, 0x21, 0x6a, 0xbd, 0x57, 0x8d, 0x9b, 0xed, 0x13,
0x55, 0x81, 0x24, 0x52, 0xfc, 0x58, 0x08, 0xa3, 0x1c, 0x9f, 0xe9, 0x23, 0xad, 0x06, 0xb7, 0x83,
0x41, 0x5d, 0x57, 0x9d, 0x28, 0x2a, 0xfa, 0xec, 0x09, 0x74, 0xf7, 0xe3, 0xf8, 0xe9, 0x2c, 0xd1,
0x0f, 0x2d, 0x76, 0x8c, 0x86, 0x11, 0xf8, 0xa0, 0xb4, 0x0b, 0xf7, 0x2a, 0x91, 0x1a, 0xb0, 0xbe,
0x41, 0x6a, 0xe7, 0xb3, 0x22, 0x24, 0x7f, 0xce, 0x7c, 0x58, 0xd7, 0x36, 0x4e, 0x2f, 0x7c, 0x60,
0x93, 0x31, 0x23, 0xe3, 0xca, 0x14, 0x96, 0xd7, 0xa1, 0x56, 0xbb, 0x93, 0x29, 0x9a, 0xb7, 0x1c,
0x76, 0x00, 0x9d, 0xfb, 0x7c, 0x14, 0x8f, 0xb9, 0x8c, 0xaa, 0x36, 0x8a, 0x85, 0xeb, 0x70, 0x6c,
0xd0, 0xb5, 0x40, 0xfb, 0xd6, 0x27, 0xfe, 0x3c, 0xe5, 0x9f, 0xec, 0x7c, 0x26, 0xe3, 0xb5, 0xe7,
0xea, 0xd6, 0xab, 0x18, 0xd3, 0xba, 0xf5, 0xa5, 0xa0, 0xd4, 0xba, 0xf5, 0x95, 0xa0, 0xd4, 0x62,
0xb5, 0x8a, 0x71, 0x59, 0x88, 0xa1, 0x6a, 0x29, 0x8e, 0xd5, 0x96, 0xf2, 0xbc, 0xe8, 0x77, 0x70,
0xf5, 0xfc, 0x01, 0xf6, 0x6c, 0x37, 0xec, 0xd9, 0x0e, 0xa1, 0x7b, 0x9f, 0x0b, 0x66, 0x89, 0xe4,
0xc5, 0xc0, 0x56, 0x23, 0x66, 0xa2, 0xa3, 0xac, 0x62, 0xa8, 0xcf, 0x56, 0xeb, 0x94, 0x39, 0x60,
0x1f, 0x43, 0xfb, 0x21, 0xcf, 0x55, 0xb6, 0x42, 0xfb, 0x1b, 0xa5, 0xf4, 0xc5, 0xa0, 0x26, 0xd9,
0x61, 0xcb, 0x0c, 0x51, 0xdb, 0xe1, 0xe3, 0x09, 0x17, 0x97, 0x7d, 0x18, 0x8c, 0x9f, 0xb3, 0x5f,
0x27, 0xe2, 0x3a, 0xc1, 0xb9, 0x65, 0x3c, 0x72, 0x9b, 0xc4, 0x57, 0x4b, 0x78, 0x1d, 0xe5, 0x28,
0x1e, 0x73, 0xc3, 0xc0, 0x45, 0xd0, 0x36, 0xb2, 0xd9, 0xfa, 0x02, 0x55, 0x33, 0xe8, 0xfa, 0x02,
0xd5, 0x24, 0xbf, 0xdd, 0xeb, 0x34, 0x8f, 0xcb, 0xae, 0x16, 0xf3, 0x88, 0x84, 0x77, 0x31, 0xd3,
0xce, 0x67, 0xfe, 0x34, 0x7f, 0xce, 0x3e, 0xa2, 0xa2, 0x6e, 0x33, 0x23, 0x53, 0xf8, 0x3b, 0xe5,
0xe4, 0x8d, 0x66, 0x96, 0xd1, 0x65, 0xfb, 0x40, 0x62, 0x2a, 0xb2, 0x83, 0x5f, 0x02, 0x38, 0xcc,
0xe3, 0xe4, 0xbe, 0xcf, 0xa7, 0x71, 0x54, 0x68, 0xae, 0x22, 0xeb, 0x50, 0x68, 0x2e, 0x23, 0xf5,
0xc0, 0x3e, 0x32, 0x3c, 0x4e, 0x2b, 0xa1, 0xa5, 0x84, 0xeb, 0xdc, 0xc4, 0x84, 0x66, 0x48, 0x4d,
0x72, 0xe2, 0x96, 0x83, 0xfe, 0x63, 0xf1, 0x6a, 0xa2, 0xfd, 0xc7, 0xca, 0x83, 0x8c, 0x56, 0x7b,
0x35, 0x4f, 0x2c, 0x07, 0xd0, 0x2a, 0x42, 0x77, 0x65, 0x92, 0xca, 0x81, 0xbe, 0xb6, 0x31, 0x95,
0x88, 0xda, 0x5d, 0x23, 0x56, 0x01, 0x5b, 0x41, 0x56, 0x51, 0x42, 0x3e, 0x80, 0x0d, 0xb1, 0x40,
0x6d, 0x30, 0xe9, 0x1d, 0x5d, 0xed, 0xa4, 0x26, 0x82, 0xd6, 0xb7, 0xb9, 0x2e, 0x00, 0xb5, 0x63,
0x3b, 0x94, 0x56, 0xf1, 0x86, 0xff, 0x9e, 0x73, 0xe3, 0x68, 0x89, 0xfe, 0x7c, 0xfe, 0x85, 0xff,
0x09, 0x00, 0x00, 0xff, 0xff, 0xce, 0xee, 0xff, 0xf6, 0xae, 0x3e, 0x00, 0x00,
// 5312 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x5c, 0x4b, 0x90, 0x1c, 0xc9,
0x59, 0x56, 0xf5, 0xf4, 0x3c, 0xfa, 0xef, 0x9e, 0x57, 0xce, 0x68, 0xd4, 0xea, 0x95, 0x65, 0x6d,
0xb1, 0xb1, 0x12, 0x62, 0xd1, 0x68, 0xc7, 0xf6, 0xb2, 0x5e, 0x81, 0x1d, 0x7a, 0xcf, 0xda, 0xb3,
0xf2, 0xb8, 0x46, 0xf2, 0x82, 0x17, 0x68, 0xd7, 0x74, 0xe7, 0xf4, 0x94, 0xd5, 0x5d, 0x55, 0x5b,
0x95, 0x3d, 0xa3, 0xde, 0x45, 0x11, 0x3c, 0x22, 0x38, 0x41, 0xf8, 0x00, 0x11, 0x84, 0x21, 0xcc,
0xc1, 0xbe, 0xc0, 0x81, 0x23, 0x07, 0xc2, 0x04, 0xdc, 0x1d, 0x41, 0x70, 0xf0, 0x89, 0xe0, 0x06,
0x9c, 0xcc, 0x99, 0x0b, 0x27, 0xe2, 0xff, 0xf3, 0x51, 0x99, 0x55, 0x35, 0x92, 0x6c, 0x03, 0xb7,
0xce, 0x2f, 0x33, 0xff, 0x7c, 0xfd, 0xf9, 0xbf, 0xf2, 0xaf, 0x86, 0x56, 0x96, 0x0e, 0x6e, 0xa4,
0x59, 0x22, 0x12, 0x36, 0x3f, 0x8e, 0xb3, 0x74, 0xd0, 0xbb, 0x34, 0x4a, 0x92, 0xd1, 0x98, 0x6f,
0x87, 0x69, 0xb4, 0x1d, 0xc6, 0x71, 0x22, 0x42, 0x11, 0x25, 0x71, 0x2e, 0x1b, 0xf9, 0xdf, 0x82,
0x95, 0x87, 0x3c, 0x3e, 0xe0, 0x7c, 0x18, 0xf0, 0x8f, 0xa7, 0x3c, 0x17, 0xec, 0x97, 0x60, 0x3d,
0xe4, 0x9f, 0x70, 0x3e, 0xec, 0xa7, 0x61, 0x9e, 0xa7, 0xc7, 0x59, 0x98, 0xf3, 0xae, 0x77, 0xc5,
0xbb, 0xd6, 0x09, 0xd6, 0x64, 0xc5, 0xbe, 0xc1, 0xd9, 0xeb, 0xd0, 0xc9, 0xb1, 0x29, 0x8f, 0x45,
0x96, 0xa4, 0xb3, 0x6e, 0x83, 0xda, 0xb5, 0x11, 0xbb, 0x2f, 0x21, 0x7f, 0x0c, 0xab, 0x66, 0x84,
0x3c, 0x4d, 0xe2, 0x9c, 0xb3, 0x9b, 0xb0, 0x39, 0x88, 0xd2, 0x63, 0x9e, 0xf5, 0xa9, 0xf3, 0x24,
0xe6, 0x93, 0x24, 0x8e, 0x06, 0x5d, 0xef, 0xca, 0xdc, 0xb5, 0x56, 0xc0, 0x64, 0x1d, 0xf6, 0xf8,
0x40, 0xd5, 0xb0, 0xab, 0xb0, 0xca, 0x63, 0x89, 0xf3, 0x21, 0xf5, 0x52, 0x43, 0xad, 0x14, 0x30,
0x76, 0xf0, 0xff, 0xc2, 0x83, 0xf5, 0xf7, 0xe3, 0x48, 0x7c, 0x18, 0x8e, 0xc7, 0x5c, 0xe8, 0x35,
0x5d, 0x85, 0xd5, 0x53, 0x02, 0x68, 0x4d, 0xa7, 0x49, 0x36, 0x54, 0x2b, 0x5a, 0x91, 0xf0, 0xbe,
0x42, 0xcf, 0x9c, 0x59, 0xe3, 0xcc, 0x99, 0xd5, 0x6e, 0xd7, 0x5c, 0xfd, 0x76, 0xf9, 0x9b, 0xc0,
0xec, 0xc9, 0xc9, 0xed, 0xf0, 0xbf, 0x04, 0x1b, 0x4f, 0xe2, 0x71, 0x32, 0x78, 0xfa, 0xb3, 0x4d,
0xda, 0xdf, 0x82, 0x4d, 0xb7, 0xbf, 0xa2, 0xfb, 0xdd, 0x06, 0xb4, 0x1f, 0x67, 0x61, 0x9c, 0x87,
0x03, 0x3c, 0x72, 0xd6, 0x85, 0x45, 0xf1, 0xac, 0x7f, 0x1c, 0xe6, 0xc7, 0x44, 0xa8, 0x15, 0xe8,
0x22, 0xdb, 0x82, 0x85, 0x70, 0x92, 0x4c, 0x63, 0x41, 0xbb, 0x3a, 0x17, 0xa8, 0x12, 0x7b, 0x0b,
0xd6, 0xe3, 0xe9, 0xa4, 0x3f, 0x48, 0xe2, 0xa3, 0x28, 0x9b, 0x48, 0xc6, 0xa1, 0xc5, 0xcd, 0x07,
0xd5, 0x0a, 0x76, 0x19, 0xe0, 0x10, 0xa7, 0x21, 0x87, 0x68, 0xd2, 0x10, 0x16, 0xc2, 0x7c, 0xe8,
0xa8, 0x12, 0x8f, 0x46, 0xc7, 0xa2, 0x3b, 0x4f, 0x84, 0x1c, 0x0c, 0x69, 0x88, 0x68, 0xc2, 0xfb,
0xb9, 0x08, 0x27, 0x69, 0x77, 0x81, 0x66, 0x63, 0x21, 0x54, 0x9f, 0x88, 0x70, 0xdc, 0x3f, 0xe2,
0x3c, 0xef, 0x2e, 0xaa, 0x7a, 0x83, 0xb0, 0x37, 0x61, 0x65, 0xc8, 0x73, 0xd1, 0x0f, 0x87, 0xc3,
0x8c, 0xe7, 0x39, 0xcf, 0xbb, 0x4b, 0x74, 0x74, 0x25, 0xd4, 0xef, 0xc2, 0xd6, 0x43, 0x2e, 0xac,
0xdd, 0xc9, 0xd5, 0xb6, 0xfb, 0x7b, 0xc0, 0x2c, 0xf8, 0x1e, 0x17, 0x61, 0x34, 0xce, 0xd9, 0x3b,
0xd0, 0x11, 0x56, 0x63, 0x62, 0xd5, 0xf6, 0x0e, 0xbb, 0x41, 0x77, 0xec, 0x86, 0xd5, 0x21, 0x70,
0xda, 0xf9, 0xff, 0xed, 0x41, 0xfb, 0x80, 0xc7, 0xe6, 0x76, 0x31, 0x68, 0xe2, 0x4c, 0xd4, 0x49,
0xd2, 0x6f, 0xf6, 0x59, 0x68, 0xd3, 0xec, 0x72, 0x91, 0x45, 0xf1, 0x88, 0x8e, 0xa0, 0x15, 0x00,
0x42, 0x07, 0x84, 0xb0, 0x35, 0x98, 0x0b, 0x27, 0x82, 0x36, 0x7e, 0x2e, 0xc0, 0x9f, 0x78, 0xef,
0xd2, 0x70, 0x36, 0xe1, 0xb1, 0x28, 0x36, 0xbb, 0x13, 0xb4, 0x15, 0xb6, 0x8b, 0xbb, 0x7d, 0x03,
0x36, 0xec, 0x26, 0x9a, 0xfa, 0x3c, 0x51, 0x5f, 0xb7, 0x5a, 0xaa, 0x41, 0xae, 0xc2, 0xaa, 0x6e,
0x9f, 0xc9, 0xc9, 0xd2, 0xf6, 0xb7, 0x82, 0x15, 0x05, 0xeb, 0x25, 0x5c, 0x83, 0xb5, 0xa3, 0x28,
0x0e, 0xc7, 0xfd, 0xc1, 0x58, 0x9c, 0xf4, 0x87, 0x7c, 0x2c, 0x42, 0x3a, 0x88, 0xf9, 0x60, 0x85,
0xf0, 0xbb, 0x63, 0x71, 0x72, 0x0f, 0x51, 0xff, 0x4f, 0x3d, 0xe8, 0xc8, 0xc5, 0xab, 0x8b, 0xff,
0x06, 0x2c, 0xeb, 0x31, 0x78, 0x96, 0x25, 0x99, 0xe2, 0x43, 0x17, 0x64, 0xd7, 0x61, 0x4d, 0x03,
0x69, 0xc6, 0xa3, 0x49, 0x38, 0xe2, 0xea, 0xb6, 0x57, 0x70, 0xb6, 0x53, 0x50, 0xcc, 0x92, 0xa9,
0x90, 0x57, 0xaf, 0xbd, 0xd3, 0x51, 0x07, 0x13, 0x20, 0x16, 0xb8, 0x4d, 0xfc, 0xef, 0x7b, 0xd0,
0xb9, 0x7b, 0x1c, 0xc6, 0x31, 0x1f, 0xef, 0x27, 0x51, 0x2c, 0xd8, 0x4d, 0x60, 0x47, 0xd3, 0x78,
0x18, 0xc5, 0xa3, 0xbe, 0x78, 0x16, 0x0d, 0xfb, 0x87, 0x33, 0xc1, 0x73, 0x79, 0x44, 0xbb, 0xe7,
0x82, 0x9a, 0x3a, 0xf6, 0x16, 0xac, 0x39, 0x68, 0x2e, 0x32, 0x79, 0x6e, 0xbb, 0xe7, 0x82, 0x4a,
0x0d, 0x32, 0x7e, 0x32, 0x15, 0xe9, 0x54, 0xf4, 0xa3, 0x78, 0xc8, 0x9f, 0xd1, 0x1c, 0x97, 0x03,
0x07, 0xbb, 0xb3, 0x02, 0x1d, 0xbb, 0x9f, 0xff, 0x25, 0x58, 0xdb, 0xc3, 0x1b, 0x11, 0x47, 0xf1,
0xe8, 0xb6, 0x64, 0x5b, 0xbc, 0xa6, 0xe9, 0xf4, 0xf0, 0x29, 0x9f, 0xa9, 0x7d, 0x53, 0x25, 0x64,
0xaa, 0xe3, 0x24, 0x17, 0x8a, 0x73, 0xe8, 0xb7, 0xff, 0xef, 0x1e, 0xac, 0xe2, 0xde, 0x7f, 0x10,
0xc6, 0x33, 0x7d, 0x72, 0x7b, 0xd0, 0x41, 0x52, 0x8f, 0x93, 0xdb, 0xf2, 0xb2, 0x4b, 0x26, 0xbe,
0xa6, 0xf6, 0xaa, 0xd4, 0xfa, 0x86, 0xdd, 0x14, 0x85, 0xf9, 0x2c, 0x70, 0x7a, 0x23, 0xdb, 0x8a,
0x30, 0x1b, 0x71, 0x41, 0x62, 0x40, 0x89, 0x05, 0x90, 0xd0, 0xdd, 0x24, 0x3e, 0x62, 0x57, 0xa0,
0x93, 0x87, 0xa2, 0x9f, 0xf2, 0x8c, 0x76, 0x8d, 0x58, 0x6f, 0x2e, 0x80, 0x3c, 0x14, 0xfb, 0x3c,
0xbb, 0x33, 0x13, 0xbc, 0xf7, 0x65, 0x58, 0xaf, 0x8c, 0x82, 0xdc, 0x5e, 0x2c, 0x11, 0x7f, 0xb2,
0x4d, 0x98, 0x3f, 0x09, 0xc7, 0x53, 0xae, 0xa4, 0x93, 0x2c, 0xbc, 0xd7, 0x78, 0xd7, 0xf3, 0xdf,
0x84, 0xb5, 0x62, 0xda, 0x8a, 0xc9, 0x18, 0x34, 0x71, 0x07, 0x15, 0x01, 0xfa, 0xed, 0xff, 0x9e,
0x27, 0x1b, 0xde, 0x4d, 0x22, 0x73, 0xd3, 0xb1, 0x21, 0x0a, 0x04, 0xdd, 0x10, 0x7f, 0x9f, 0x29,
0x09, 0x7f, 0xfe, 0xc5, 0xfa, 0x57, 0x61, 0xdd, 0x9a, 0xc2, 0x0b, 0x26, 0xfb, 0x97, 0x1e, 0xac,
0x3f, 0xe2, 0xa7, 0xea, 0xd4, 0xf5, 0x6c, 0xdf, 0x85, 0xa6, 0x98, 0xa5, 0x52, 0x15, 0xaf, 0xec,
0xbc, 0xa1, 0x0e, 0xad, 0xd2, 0xee, 0x86, 0x2a, 0x3e, 0x9e, 0xa5, 0x3c, 0xa0, 0x1e, 0xfe, 0xd7,
0xa0, 0x6d, 0x81, 0xec, 0x02, 0x6c, 0x7c, 0xf8, 0xfe, 0xe3, 0x47, 0xf7, 0x0f, 0x0e, 0xfa, 0xfb,
0x4f, 0xee, 0x7c, 0xf5, 0xfe, 0x6f, 0xf4, 0x77, 0x6f, 0x1f, 0xec, 0xae, 0x9d, 0x63, 0x5b, 0xc0,
0x1e, 0xdd, 0x3f, 0x78, 0x7c, 0xff, 0x9e, 0x83, 0x7b, 0x6c, 0x15, 0xda, 0x36, 0xd0, 0xf0, 0x7b,
0xd0, 0x7d, 0xc4, 0x4f, 0x3f, 0x8c, 0x44, 0xcc, 0xf3, 0xdc, 0x1d, 0xde, 0xbf, 0x01, 0xcc, 0x9e,
0x93, 0x5a, 0x66, 0x17, 0x16, 0x95, 0xec, 0xd5, 0xaa, 0x47, 0x15, 0xfd, 0x37, 0x81, 0x1d, 0x44,
0xa3, 0xf8, 0x03, 0x9e, 0xe7, 0xe1, 0x88, 0xeb, 0xc5, 0xae, 0xc1, 0xdc, 0x24, 0x1f, 0x29, 0x29,
0x89, 0x3f, 0xfd, 0xcf, 0xc1, 0x86, 0xd3, 0x4e, 0x11, 0xbe, 0x04, 0xad, 0x3c, 0x1a, 0xc5, 0xa1,
0x98, 0x66, 0x5c, 0x91, 0x2e, 0x00, 0xff, 0x01, 0x6c, 0x7e, 0x83, 0x67, 0xd1, 0xd1, 0xec, 0x65,
0xe4, 0x5d, 0x3a, 0x8d, 0x32, 0x9d, 0xfb, 0x70, 0xbe, 0x44, 0x47, 0x0d, 0x2f, 0x39, 0x53, 0x9d,
0xdf, 0x52, 0x20, 0x0b, 0xd6, 0x3d, 0x6d, 0xd8, 0xf7, 0xd4, 0x7f, 0x02, 0xec, 0x6e, 0x12, 0xc7,
0x7c, 0x20, 0xf6, 0x39, 0xcf, 0x0a, 0x83, 0xab, 0x60, 0xc3, 0xf6, 0xce, 0x05, 0x75, 0xb0, 0xe5,
0xcb, 0xaf, 0xf8, 0x93, 0x41, 0x33, 0xe5, 0xd9, 0x84, 0x08, 0x2f, 0x05, 0xf4, 0xdb, 0x3f, 0x0f,
0x1b, 0x0e, 0x59, 0xa5, 0xfe, 0xdf, 0x86, 0xf3, 0xf7, 0xa2, 0x7c, 0x50, 0x1d, 0xb0, 0x0b, 0x8b,
0xe9, 0xf4, 0xb0, 0x5f, 0x5c, 0x32, 0x5d, 0x44, 0xad, 0x58, 0xee, 0xa2, 0x88, 0xfd, 0xa1, 0x07,
0xcd, 0xdd, 0xc7, 0x7b, 0x77, 0x59, 0x0f, 0x96, 0xa2, 0x78, 0x90, 0x4c, 0x50, 0x97, 0xc8, 0x45,
0x9b, 0xf2, 0x99, 0x97, 0xe7, 0x12, 0xb4, 0x48, 0x05, 0xa1, 0xa2, 0x57, 0xb6, 0x51, 0x01, 0xa0,
0x91, 0xc1, 0x9f, 0xa5, 0x51, 0x46, 0x56, 0x84, 0xb6, 0x0d, 0x9a, 0x24, 0x22, 0xab, 0x15, 0xfe,
0x4f, 0x9a, 0xb0, 0x7c, 0x7b, 0x20, 0xa2, 0x13, 0xae, 0x44, 0x38, 0x8d, 0x4a, 0x80, 0x9a, 0x8f,
0x2a, 0xa1, 0xb2, 0xc9, 0xf8, 0x24, 0x11, 0xbc, 0xef, 0x1c, 0x86, 0x0b, 0x62, 0xab, 0x81, 0x24,
0xd4, 0x4f, 0x51, 0x19, 0xd0, 0xfc, 0x5a, 0x81, 0x0b, 0xe2, 0x96, 0x21, 0xd0, 0x8f, 0x86, 0x34,
0xb3, 0x66, 0xa0, 0x8b, 0xb8, 0x1f, 0x83, 0x30, 0x0d, 0x07, 0x91, 0x98, 0xa9, 0x3b, 0x6f, 0xca,
0x48, 0x7b, 0x9c, 0x0c, 0xc2, 0x71, 0xff, 0x30, 0x1c, 0x87, 0xf1, 0x80, 0x2b, 0x7b, 0xc6, 0x05,
0xd1, 0x64, 0x51, 0x53, 0xd2, 0xcd, 0xa4, 0x59, 0x53, 0x42, 0xd1, 0xf4, 0x19, 0x24, 0x93, 0x49,
0x24, 0xd0, 0xd2, 0xe9, 0x2e, 0x49, 0xf9, 0x52, 0x20, 0xb4, 0x12, 0x59, 0x3a, 0x95, 0x7b, 0xd8,
0x92, 0xa3, 0x39, 0x20, 0x52, 0x39, 0xe2, 0x9c, 0xe4, 0xd4, 0xd3, 0xd3, 0x2e, 0x48, 0x2a, 0x05,
0x82, 0xa7, 0x31, 0x8d, 0x73, 0x2e, 0xc4, 0x98, 0x0f, 0xcd, 0x84, 0xda, 0xd4, 0xac, 0x5a, 0xc1,
0x6e, 0xc2, 0x86, 0x34, 0xbe, 0xf2, 0x50, 0x24, 0xf9, 0x71, 0x94, 0xf7, 0x73, 0x1e, 0x8b, 0x6e,
0x87, 0xda, 0xd7, 0x55, 0xb1, 0x77, 0xe1, 0x42, 0x09, 0xce, 0xf8, 0x80, 0x47, 0x27, 0x7c, 0xd8,
0x5d, 0xa6, 0x5e, 0x67, 0x55, 0xb3, 0x2b, 0xd0, 0x46, 0x9b, 0x73, 0x9a, 0x0e, 0x43, 0x54, 0xcf,
0x2b, 0x74, 0x0e, 0x36, 0xc4, 0xde, 0x86, 0xe5, 0x94, 0x4b, 0x1d, 0x7a, 0x2c, 0xc6, 0x83, 0xbc,
0xbb, 0x4a, 0x0a, 0xae, 0xad, 0xae, 0x14, 0xf2, 0x6f, 0xe0, 0xb6, 0x40, 0xd6, 0x1c, 0xe4, 0x64,
0xc5, 0x84, 0xb3, 0xee, 0x1a, 0x31, 0x5d, 0x01, 0xe0, 0xcd, 0xda, 0x8b, 0x72, 0xa1, 0x38, 0xcd,
0xc8, 0xb8, 0x5d, 0xd8, 0x74, 0x61, 0xe3, 0xd7, 0x2c, 0x29, 0xb6, 0xc9, 0xbb, 0x6d, 0x1a, 0x7a,
0x53, 0x0d, 0xed, 0x70, 0x6c, 0x60, 0x5a, 0xf9, 0x3f, 0xf1, 0xa0, 0x89, 0xf7, 0xec, 0xec, 0x3b,
0x69, 0x8b, 0xce, 0x39, 0x47, 0x74, 0x92, 0xbd, 0x8d, 0xd6, 0x88, 0xdc, 0x73, 0xc9, 0x97, 0x16,
0x52, 0xd4, 0x67, 0x7c, 0x70, 0x42, 0xcc, 0x69, 0xea, 0x11, 0x41, 0xd6, 0x45, 0x95, 0x45, 0xbd,
0x25, 0x67, 0x9a, 0xb2, 0xae, 0xa3, 0x9e, 0x8b, 0x45, 0x1d, 0xf5, 0xeb, 0xc2, 0x62, 0x14, 0x1f,
0x26, 0xd3, 0x78, 0x48, 0x5c, 0xb8, 0x14, 0xe8, 0x22, 0xee, 0x66, 0x4a, 0x16, 0x4c, 0x34, 0xe1,
0x8a, 0xfd, 0x0a, 0xc0, 0x67, 0x68, 0xd2, 0xe4, 0x24, 0x57, 0xcc, 0x56, 0xbe, 0x03, 0xeb, 0x16,
0xa6, 0xf6, 0xf1, 0x75, 0x98, 0x4f, 0x11, 0x50, 0x06, 0x8a, 0x3e, 0x3f, 0x12, 0x48, 0xb2, 0xc6,
0x5f, 0x43, 0xbf, 0x55, 0xbc, 0x1f, 0x1f, 0x25, 0x9a, 0xd2, 0x3f, 0xce, 0xa1, 0xa3, 0xa9, 0x20,
0x45, 0xe8, 0x1a, 0xac, 0x46, 0x43, 0x1e, 0x8b, 0x48, 0xcc, 0xfa, 0x8e, 0xe5, 0x54, 0x86, 0x51,
0x90, 0x87, 0xe3, 0x28, 0xcc, 0x95, 0x90, 0x90, 0x05, 0xb6, 0x03, 0x9b, 0xc8, 0x5f, 0x9a, 0x65,
0xcc, 0xe1, 0x4a, 0x03, 0xae, 0xb6, 0x0e, 0xaf, 0x04, 0xe2, 0x52, 0x08, 0x15, 0x5d, 0xa4, 0x40,
0xab, 0xab, 0xc2, 0x5d, 0x93, 0x94, 0x70, 0xc9, 0xf3, 0x92, 0x07, 0x0d, 0x50, 0xf1, 0x9a, 0x16,
0xa4, 0xf1, 0x58, 0xf6, 0x9a, 0x2c, 0xcf, 0x6b, 0xa9, 0xe2, 0x79, 0x5d, 0x83, 0xd5, 0x7c, 0x16,
0x0f, 0xf8, 0xb0, 0x2f, 0x12, 0x1c, 0x37, 0x8a, 0xe9, 0x74, 0x96, 0x82, 0x32, 0x4c, 0x3e, 0x22,
0xcf, 0x45, 0xcc, 0x05, 0xc9, 0x86, 0xa5, 0x40, 0x17, 0x51, 0xcc, 0x52, 0x13, 0xc9, 0xda, 0xad,
0x40, 0x95, 0x50, 0x23, 0x4d, 0xb3, 0x28, 0xef, 0x76, 0x08, 0xa5, 0xdf, 0xec, 0xf3, 0x70, 0xfe,
0x10, 0x3d, 0x9a, 0x63, 0x1e, 0x0e, 0x79, 0x46, 0xa7, 0x2f, 0x1d, 0x3a, 0x79, 0xc5, 0xeb, 0x2b,
0xfd, 0x4f, 0x48, 0x3d, 0x1a, 0x87, 0xf2, 0x09, 0xdd, 0x6a, 0xf6, 0x1a, 0xb4, 0xe4, 0x4a, 0xf2,
0xe3, 0x50, 0x69, 0xec, 0x25, 0x02, 0x0e, 0x8e, 0x43, 0xf4, 0x83, 0x9c, 0xcd, 0x69, 0x90, 0x5d,
0xd6, 0x26, 0x6c, 0x57, 0xee, 0xcd, 0x1b, 0xb0, 0xa2, 0x5d, 0xd5, 0xbc, 0x3f, 0xe6, 0x47, 0x42,
0x9b, 0xdf, 0xf1, 0x74, 0x82, 0xc3, 0xe5, 0x7b, 0xfc, 0x48, 0xf8, 0x8f, 0x60, 0x5d, 0xdd, 0xce,
0xaf, 0xa5, 0x5c, 0x0f, 0xfd, 0xc5, 0xb2, 0x6e, 0x90, 0x2a, 0x7a, 0x43, 0xf1, 0xa3, 0xed, 0x43,
0x94, 0x14, 0x86, 0x1f, 0x00, 0x53, 0xd5, 0x77, 0xc7, 0x49, 0xce, 0x15, 0x41, 0x1f, 0x3a, 0x83,
0x71, 0x92, 0x6b, 0x23, 0x5f, 0x2d, 0xc7, 0xc1, 0xf0, 0x04, 0xf2, 0xe9, 0x60, 0x80, 0xf7, 0x5d,
0x2a, 0x79, 0x5d, 0xf4, 0xff, 0xca, 0x83, 0x0d, 0xa2, 0xa6, 0xe5, 0x88, 0xb1, 0x0c, 0x5f, 0x7d,
0x9a, 0x9d, 0x81, 0xed, 0xf8, 0x6c, 0xc2, 0xfc, 0x51, 0x92, 0x0d, 0xb8, 0x1a, 0x49, 0x16, 0x7e,
0x7a, 0x5b, 0xb7, 0x59, 0xb1, 0x75, 0xff, 0xc5, 0x83, 0x75, 0x9a, 0xea, 0x81, 0x08, 0xc5, 0x34,
0x57, 0xcb, 0xff, 0x55, 0x58, 0xc6, 0xa5, 0x72, 0x7d, 0x69, 0xd4, 0x44, 0x37, 0xcd, 0xfd, 0x26,
0x54, 0x36, 0xde, 0x3d, 0x17, 0xb8, 0x8d, 0xd9, 0x97, 0xa1, 0x63, 0xc7, 0x1b, 0x68, 0xce, 0xed,
0x9d, 0x8b, 0x7a, 0x95, 0x15, 0xce, 0xd9, 0x3d, 0x17, 0x38, 0x1d, 0xd8, 0x2d, 0x00, 0xd2, 0xda,
0x44, 0x56, 0x39, 0x8a, 0x17, 0xdd, 0x4d, 0xb2, 0x0e, 0x6b, 0xf7, 0x5c, 0x60, 0x35, 0xbf, 0xb3,
0x04, 0x0b, 0x52, 0xcd, 0xf8, 0x0f, 0x61, 0xd9, 0x99, 0xa9, 0x63, 0xc3, 0x77, 0xa4, 0x0d, 0x5f,
0x71, 0xf9, 0x1a, 0x55, 0x97, 0xcf, 0xff, 0xbb, 0x06, 0x30, 0xe4, 0xb6, 0xd2, 0x71, 0xa2, 0x9e,
0x4b, 0x86, 0x8e, 0xd5, 0xd2, 0x09, 0x6c, 0x88, 0xdd, 0x00, 0x66, 0x15, 0xb5, 0x67, 0x2f, 0xb5,
0x43, 0x4d, 0x0d, 0x8a, 0x31, 0x69, 0x72, 0x68, 0x0f, 0x53, 0x59, 0x69, 0xf2, 0xdc, 0x6a, 0xeb,
0x50, 0x01, 0xa4, 0xd3, 0xfc, 0x18, 0xf5, 0xb0, 0xb6, 0x6b, 0x74, 0xb9, 0xcc, 0x20, 0x0b, 0x2f,
0x65, 0x90, 0xc5, 0x32, 0x83, 0x90, 0xbe, 0xcb, 0xa2, 0x93, 0x50, 0x70, 0xad, 0x43, 0x54, 0x11,
0xcd, 0x98, 0x49, 0x14, 0x93, 0x7a, 0xee, 0x4f, 0x70, 0x74, 0x65, 0xc6, 0x38, 0xa0, 0xff, 0x63,
0x0f, 0xd6, 0x70, 0xef, 0x1c, 0xfe, 0x7a, 0x0f, 0x88, 0xbd, 0x5f, 0x91, 0xbd, 0x9c, 0xb6, 0x3f,
0x3f, 0x77, 0xbd, 0x0b, 0x2d, 0x22, 0x98, 0xa4, 0x3c, 0x56, 0xcc, 0xd5, 0x75, 0x99, 0xab, 0x90,
0x2c, 0xbb, 0xe7, 0x82, 0xa2, 0xb1, 0xc5, 0x5a, 0xff, 0xec, 0x41, 0x5b, 0x4d, 0xf3, 0x67, 0x36,
0xb6, 0x7b, 0xb0, 0x84, 0x5c, 0x66, 0xd9, 0xb2, 0xa6, 0x8c, 0x7a, 0x60, 0x82, 0x1e, 0x0d, 0x2a,
0x3e, 0xc7, 0xd0, 0x2e, 0xc3, 0xa8, 0xc5, 0x48, 0x88, 0xe6, 0x7d, 0x11, 0x8d, 0xfb, 0xba, 0x56,
0x85, 0xec, 0xea, 0xaa, 0x50, 0x96, 0xe4, 0x22, 0x1c, 0x71, 0xa5, 0xa0, 0x64, 0x01, 0x3d, 0x0a,
0xb5, 0xa0, 0xb2, 0x11, 0xf5, 0x23, 0x80, 0x0b, 0x95, 0x2a, 0x63, 0x48, 0x29, 0xdb, 0x71, 0x1c,
0x4d, 0x0e, 0x13, 0x63, 0x86, 0x7a, 0xb6, 0x59, 0xe9, 0x54, 0xb1, 0x11, 0x9c, 0xd7, 0x9a, 0x18,
0xf7, 0xb4, 0xd0, 0xbb, 0x0d, 0x32, 0x21, 0xde, 0x76, 0x79, 0xa0, 0x3c, 0xa0, 0xc6, 0xed, 0xdb,
0x58, 0x4f, 0x8f, 0x1d, 0x43, 0xd7, 0xa8, 0x7c, 0x25, 0xb6, 0x2d, 0xb3, 0x00, 0xc7, 0x7a, 0xeb,
0x25, 0x63, 0x91, 0x8c, 0x19, 0xea, 0x61, 0xce, 0xa4, 0xc6, 0x66, 0x70, 0x59, 0xd7, 0x91, 0x5c,
0xae, 0x8e, 0xd7, 0x7c, 0xa5, 0xb5, 0x3d, 0xc0, 0xce, 0xee, 0xa0, 0x2f, 0x21, 0xdc, 0xfb, 0x91,
0x07, 0x2b, 0x2e, 0x39, 0x64, 0x1d, 0xe5, 0x8f, 0x68, 0x01, 0xa3, 0x4d, 0xa9, 0x12, 0x5c, 0xf5,
0xa8, 0x1a, 0x75, 0x1e, 0x95, 0xed, 0x37, 0xcd, 0xbd, 0xcc, 0x6f, 0x6a, 0xbe, 0x9a, 0xdf, 0x34,
0x5f, 0xe7, 0x37, 0xf5, 0xfe, 0xcb, 0x03, 0x56, 0x3d, 0x5f, 0xf6, 0x50, 0xba, 0x74, 0x31, 0x1f,
0x2b, 0x39, 0xf1, 0xcb, 0xaf, 0xc6, 0x23, 0x7a, 0x0f, 0x75, 0x6f, 0x64, 0x56, 0x5b, 0x10, 0xd8,
0xa6, 0xc8, 0x72, 0x50, 0x57, 0x55, 0xf2, 0xe4, 0x9a, 0x2f, 0xf7, 0xe4, 0xe6, 0x5f, 0xee, 0xc9,
0x2d, 0x94, 0x3d, 0xb9, 0xde, 0xef, 0xc0, 0xb2, 0x73, 0xea, 0xff, 0x7b, 0x2b, 0x2e, 0x9b, 0x31,
0xf2, 0x80, 0x1d, 0xac, 0xf7, 0x9f, 0x0d, 0x60, 0x55, 0xce, 0xfb, 0x7f, 0x9d, 0x03, 0xf1, 0x91,
0x23, 0x40, 0xe6, 0x14, 0x1f, 0x39, 0xa2, 0xe3, 0xff, 0x52, 0x28, 0xbe, 0x05, 0xeb, 0x19, 0x1f,
0x24, 0x27, 0xf4, 0x6c, 0xe5, 0x46, 0x01, 0xaa, 0x15, 0x68, 0xc8, 0xb9, 0xfe, 0xeb, 0x92, 0xf3,
0xca, 0x60, 0x69, 0x86, 0x92, 0x1b, 0xeb, 0x7f, 0x11, 0x36, 0xe5, 0xe3, 0xcf, 0x1d, 0x49, 0x4a,
0xdb, 0x12, 0xaf, 0x43, 0xe7, 0x54, 0x86, 0xe9, 0xfa, 0x49, 0x3c, 0x9e, 0x29, 0x25, 0xd2, 0x56,
0xd8, 0xd7, 0xe2, 0xf1, 0xcc, 0xff, 0x9e, 0x07, 0xe7, 0x4b, 0x7d, 0x8b, 0x68, 0xbd, 0x14, 0xb5,
0xae, 0xfc, 0x75, 0x41, 0x5c, 0xa2, 0xe2, 0x71, 0x6b, 0x89, 0x52, 0x25, 0x55, 0x2b, 0x70, 0x0b,
0xa7, 0x71, 0xb5, 0xbd, 0x3c, 0x98, 0xba, 0x2a, 0xff, 0x02, 0x9c, 0x57, 0x87, 0xef, 0xae, 0xcd,
0xdf, 0x81, 0xad, 0x72, 0x45, 0x11, 0x6d, 0x74, 0xa7, 0xac, 0x8b, 0xfe, 0x6f, 0x03, 0xfb, 0xfa,
0x94, 0x67, 0x33, 0x7a, 0x17, 0x30, 0xa1, 0xd5, 0x0b, 0x65, 0xe7, 0x7b, 0x21, 0x9d, 0x1e, 0x7e,
0x95, 0xcf, 0xf4, 0xc3, 0x4b, 0xa3, 0x78, 0x78, 0xf9, 0x0c, 0x00, 0x7a, 0x13, 0xf4, 0x90, 0xa0,
0x9f, 0xc2, 0xd0, 0x59, 0x93, 0x04, 0xfd, 0x5b, 0xb0, 0xe1, 0xd0, 0x37, 0x3b, 0xb9, 0xa0, 0x7a,
0x48, 0x8f, 0xd6, 0x7d, 0x9e, 0x50, 0x75, 0xfe, 0x9f, 0x79, 0x30, 0xb7, 0x9b, 0xa4, 0x76, 0xb0,
0xc9, 0x73, 0x83, 0x4d, 0x4a, 0xb4, 0xf6, 0x8d, 0xe4, 0x6c, 0x28, 0xc1, 0x60, 0x83, 0x28, 0x18,
0xc3, 0x89, 0x40, 0x9f, 0xee, 0x28, 0xc9, 0x4e, 0xc3, 0x6c, 0xa8, 0xb6, 0xb7, 0x84, 0xe2, 0xea,
0x0a, 0xf9, 0x83, 0x3f, 0xd1, 0xa6, 0xa0, 0x88, 0xdb, 0x4c, 0xb9, 0xa1, 0xaa, 0xe4, 0x7f, 0xc7,
0x83, 0x79, 0x9a, 0x2b, 0x5e, 0x16, 0x79, 0xfc, 0xf4, 0x26, 0x47, 0x01, 0x3d, 0x4f, 0x5e, 0x96,
0x12, 0x5c, 0x7a, 0xa9, 0x6b, 0x54, 0x5e, 0xea, 0x2e, 0x41, 0x4b, 0x96, 0x8a, 0xa7, 0xad, 0x02,
0x60, 0x97, 0xa1, 0x79, 0x9c, 0xa4, 0x5a, 0xc5, 0x81, 0x8e, 0xe0, 0x24, 0x69, 0x40, 0xb8, 0x7f,
0x1d, 0x56, 0x1f, 0x25, 0x43, 0x6e, 0x05, 0x00, 0xce, 0x3c, 0x45, 0xff, 0x77, 0x3d, 0x58, 0xd2,
0x8d, 0xd9, 0x35, 0x68, 0xa2, 0xa6, 0x2a, 0xd9, 0x86, 0x26, 0xda, 0x8a, 0xed, 0x02, 0x6a, 0x81,
0x12, 0x86, 0x1c, 0xc7, 0xc2, 0x92, 0xd0, 0x6e, 0x63, 0xa1, 0xa3, 0xdf, 0x84, 0x15, 0x39, 0xe7,
0x92, 0x2e, 0x2b, 0xa1, 0xfe, 0x5f, 0x7b, 0xb0, 0xec, 0x8c, 0x81, 0x56, 0xfe, 0x38, 0xcc, 0x85,
0x8a, 0x5d, 0xa9, 0x4d, 0xb4, 0x21, 0x3b, 0x24, 0xd4, 0x70, 0x43, 0x42, 0x26, 0x58, 0x31, 0x67,
0x07, 0x2b, 0x6e, 0x42, 0xab, 0x78, 0xf5, 0x6c, 0x3a, 0x92, 0x03, 0x47, 0xd4, 0x71, 0xe4, 0xa2,
0x11, 0xd2, 0x19, 0x24, 0xe3, 0x24, 0x53, 0x8f, 0x82, 0xb2, 0xe0, 0xdf, 0x82, 0xb6, 0xd5, 0x1e,
0xa7, 0x11, 0x73, 0x71, 0x9a, 0x64, 0x4f, 0x75, 0x64, 0x4a, 0x15, 0xcd, 0xfb, 0x49, 0xa3, 0x78,
0x3f, 0xf1, 0xff, 0xc6, 0x83, 0x65, 0xe4, 0x94, 0x28, 0x1e, 0xed, 0x27, 0xe3, 0x68, 0x30, 0x23,
0x8e, 0xd1, 0x4c, 0xa1, 0x5e, 0x0b, 0x35, 0xc7, 0xb8, 0x30, 0x9a, 0x04, 0xda, 0xc8, 0x57, 0xfc,
0x62, 0xca, 0xc8, 0xf9, 0xa8, 0xda, 0x0e, 0xc3, 0x9c, 0x4b, 0xaf, 0x40, 0x89, 0x72, 0x07, 0x44,
0xe9, 0x82, 0x40, 0x16, 0x0a, 0xde, 0x9f, 0x44, 0xe3, 0x71, 0x24, 0xdb, 0x4a, 0x0e, 0xaf, 0xab,
0xf2, 0x7f, 0xd8, 0x80, 0xb6, 0x92, 0x22, 0xf7, 0x87, 0x23, 0x19, 0x64, 0x55, 0x76, 0x8a, 0xb9,
0x7e, 0x16, 0xa2, 0xeb, 0x1d, 0xcb, 0xc6, 0x42, 0xca, 0xc7, 0x3a, 0x57, 0x3d, 0xd6, 0x4b, 0xd0,
0x42, 0xf6, 0x7a, 0x9b, 0x4c, 0x28, 0xf9, 0x48, 0x5e, 0x00, 0xba, 0x76, 0x87, 0x6a, 0xe7, 0x8b,
0x5a, 0x02, 0x1c, 0xa3, 0x69, 0xa1, 0x64, 0x34, 0xbd, 0x0b, 0x1d, 0x45, 0x86, 0xf6, 0x9d, 0x7c,
0xae, 0x82, 0xc1, 0x9d, 0x33, 0x09, 0x9c, 0x96, 0xba, 0xe7, 0x8e, 0xee, 0xb9, 0xf4, 0xb2, 0x9e,
0xba, 0x25, 0xbd, 0x3c, 0xc8, 0xbd, 0x79, 0x98, 0x85, 0xe9, 0xb1, 0x96, 0xcc, 0x43, 0xf3, 0xbe,
0x4a, 0x30, 0xbb, 0x0e, 0xf3, 0xd8, 0x4d, 0x4b, 0xbf, 0xfa, 0x4b, 0x27, 0x9b, 0xb0, 0x6b, 0x30,
0xcf, 0x87, 0x23, 0xae, 0x0d, 0x77, 0xe6, 0xba, 0x50, 0x78, 0x46, 0x81, 0x6c, 0x80, 0x22, 0x00,
0xd1, 0x92, 0x08, 0x70, 0x25, 0xe7, 0x02, 0x16, 0xdf, 0x1f, 0xfa, 0x9b, 0xc0, 0x1e, 0x49, 0xae,
0xb5, 0x43, 0x86, 0x7f, 0x30, 0x07, 0x6d, 0x0b, 0xc6, 0xdb, 0x3c, 0xc2, 0x09, 0xf7, 0x87, 0x51,
0x38, 0xe1, 0x82, 0x67, 0x8a, 0x53, 0x4b, 0x28, 0x09, 0xd8, 0x93, 0x51, 0x3f, 0x99, 0x8a, 0xfe,
0x90, 0x8f, 0x32, 0x2e, 0xf5, 0x9d, 0x17, 0x94, 0x50, 0x6c, 0x37, 0x09, 0x9f, 0xd9, 0xed, 0x24,
0x3f, 0x94, 0x50, 0x1d, 0x00, 0x94, 0x7b, 0xd4, 0x2c, 0x02, 0x80, 0x72, 0x47, 0xca, 0x72, 0x68,
0xbe, 0x46, 0x0e, 0xbd, 0x03, 0x5b, 0x52, 0xe2, 0xa8, 0xbb, 0xd9, 0x2f, 0xb1, 0xc9, 0x19, 0xb5,
0xec, 0x3a, 0xac, 0xe1, 0x9c, 0x35, 0x83, 0xe7, 0xd1, 0x27, 0xd2, 0x59, 0xf7, 0x82, 0x0a, 0x8e,
0x6d, 0xf1, 0x3a, 0x3a, 0x6d, 0xe5, 0x2b, 0x44, 0x05, 0xa7, 0xb6, 0xe1, 0x33, 0xb7, 0x6d, 0x4b,
0xb5, 0x2d, 0xe1, 0xfe, 0x32, 0xb4, 0x0f, 0x44, 0x92, 0xea, 0x43, 0x59, 0x81, 0x8e, 0x2c, 0xaa,
0x97, 0xa7, 0xd7, 0xe0, 0x22, 0x71, 0xd1, 0xe3, 0x24, 0x4d, 0xc6, 0xc9, 0x68, 0x76, 0x30, 0x3d,
0xcc, 0x07, 0x59, 0x94, 0xa2, 0x41, 0xed, 0xff, 0x93, 0x07, 0x1b, 0x4e, 0xad, 0x8a, 0x04, 0x7c,
0x5e, 0xb2, 0xb4, 0x79, 0x2c, 0x90, 0x8c, 0xb7, 0x6e, 0x89, 0x43, 0xd9, 0x50, 0xc6, 0x55, 0x9e,
0xa8, 0xf7, 0x83, 0xdb, 0xb0, 0xaa, 0x67, 0xa6, 0x3b, 0x4a, 0x2e, 0xec, 0x56, 0xb9, 0x50, 0xf5,
0x5f, 0x51, 0x1d, 0x34, 0x89, 0x5f, 0x93, 0x66, 0x29, 0x1f, 0xd2, 0x1a, 0xb5, 0x4b, 0xd8, 0xd3,
0xfd, 0x6d, 0x5b, 0x58, 0xcf, 0x60, 0x60, 0xc0, 0xdc, 0xff, 0x23, 0x0f, 0xa0, 0x98, 0x1d, 0x32,
0x46, 0x21, 0xd2, 0x65, 0x76, 0x94, 0x25, 0xbe, 0x5f, 0x87, 0x8e, 0x09, 0x63, 0x17, 0x5a, 0xa2,
0xad, 0x31, 0x34, 0x60, 0xae, 0xc2, 0xea, 0x68, 0x9c, 0x1c, 0x92, 0xce, 0xa5, 0xa7, 0xcc, 0x5c,
0xbd, 0xbf, 0xad, 0x48, 0xf8, 0x81, 0x42, 0x0b, 0x95, 0xd2, 0xb4, 0x54, 0x8a, 0xff, 0xc7, 0x0d,
0x13, 0x16, 0x2d, 0xd6, 0x7c, 0xe6, 0x2d, 0x63, 0x3b, 0x15, 0xe1, 0x78, 0x46, 0x14, 0x92, 0x82,
0x1f, 0xfb, 0x2f, 0xf5, 0x03, 0x6f, 0xc1, 0x4a, 0x26, 0xa5, 0x8f, 0x16, 0x4d, 0xcd, 0x17, 0x88,
0xa6, 0xe5, 0xcc, 0xd1, 0x3b, 0xbf, 0x08, 0x6b, 0xe1, 0xf0, 0x84, 0x67, 0x22, 0x22, 0x87, 0x80,
0x94, 0xbe, 0x14, 0xa8, 0xab, 0x16, 0x4e, 0xba, 0xf8, 0x2a, 0xac, 0xaa, 0x37, 0x4f, 0xd3, 0x52,
0xa5, 0xbe, 0x14, 0x30, 0x36, 0xf4, 0x7f, 0xa0, 0x23, 0xb0, 0xee, 0x19, 0x9e, 0xbd, 0x23, 0xf6,
0xea, 0x1a, 0xa5, 0xd5, 0xfd, 0x82, 0x8a, 0x86, 0x0e, 0xb5, 0xd7, 0xa1, 0xe2, 0xd2, 0x12, 0x54,
0xd1, 0x6b, 0x77, 0x4b, 0x9b, 0xaf, 0xb2, 0xa5, 0xfe, 0xf7, 0xe6, 0x60, 0xf1, 0xfd, 0xf8, 0x24,
0x89, 0x06, 0x14, 0x9b, 0x9c, 0xf0, 0x49, 0xa2, 0xf3, 0x0b, 0xf0, 0x37, 0x6a, 0x74, 0x7a, 0x54,
0x4b, 0x85, 0x0a, 0x2e, 0xea, 0x22, 0x6a, 0xb7, 0xac, 0xc8, 0xb9, 0x91, 0x9c, 0x62, 0x21, 0x68,
0x1f, 0x66, 0x76, 0xc2, 0x91, 0x2a, 0x15, 0x09, 0x1a, 0xf3, 0x56, 0x82, 0x06, 0x45, 0xb2, 0xe5,
0x7b, 0x21, 0x6d, 0xe7, 0x52, 0xa0, 0x8b, 0x64, 0xc7, 0x66, 0x5c, 0xfa, 0xc4, 0xa4, 0x27, 0x17,
0x95, 0x1d, 0x6b, 0x83, 0xa8, 0x4b, 0x65, 0x07, 0xd9, 0x46, 0xca, 0x1a, 0x1b, 0x42, 0xdb, 0xa2,
0x9c, 0xb3, 0xd4, 0x92, 0x47, 0x5c, 0x82, 0x51, 0x20, 0x0d, 0xb9, 0x91, 0x1b, 0x72, 0x0d, 0x20,
0x73, 0x8a, 0xca, 0xb8, 0x65, 0x05, 0xcb, 0x77, 0x4f, 0x55, 0x22, 0x1b, 0x24, 0x1c, 0x8f, 0x0f,
0xc3, 0xc1, 0x53, 0xca, 0x24, 0xa3, 0x67, 0xce, 0x56, 0xe0, 0x82, 0x38, 0x6b, 0x4a, 0x8c, 0x52,
0x24, 0x96, 0xe5, 0x33, 0xa5, 0x05, 0xf9, 0xdf, 0x00, 0x76, 0x7b, 0x38, 0x54, 0x27, 0x64, 0x7c,
0x84, 0x62, 0x6f, 0x3d, 0x67, 0x6f, 0x6b, 0xd6, 0xd8, 0xa8, 0x5d, 0xa3, 0x7f, 0x1f, 0xda, 0xfb,
0x56, 0x02, 0x18, 0x1d, 0xa6, 0x4e, 0xfd, 0x52, 0x0c, 0x60, 0x21, 0xd6, 0x80, 0x0d, 0x7b, 0x40,
0xff, 0x57, 0x80, 0xed, 0x45, 0xb9, 0x30, 0xf3, 0x33, 0x9e, 0xa4, 0x09, 0x88, 0x59, 0x9e, 0xa4,
0xc2, 0xc8, 0x93, 0xbc, 0x2d, 0x5f, 0x4b, 0xcb, 0x0b, 0xbb, 0x0e, 0x4b, 0x91, 0x84, 0xb4, 0x1c,
0x5e, 0x51, 0x0c, 0xac, 0x5b, 0x9a, 0x7a, 0x34, 0x28, 0x14, 0xe8, 0x88, 0xf9, 0x1f, 0x7a, 0xb0,
0xa8, 0x96, 0x86, 0xea, 0xd0, 0x49, 0x7d, 0x93, 0x0b, 0x73, 0xb0, 0xfa, 0x84, 0xa1, 0x2a, 0xd7,
0xcd, 0xd5, 0x71, 0x1d, 0x83, 0x66, 0x1a, 0x8a, 0x63, 0xb2, 0xa0, 0x5b, 0x01, 0xfd, 0xd6, 0x9e,
0xd2, 0x7c, 0xe1, 0x29, 0xd5, 0xe5, 0xa8, 0x49, 0x99, 0x51, 0xc1, 0xf5, 0x2b, 0xb2, 0x5a, 0x80,
0x09, 0x80, 0xde, 0x91, 0xaf, 0xc8, 0x05, 0x5c, 0xec, 0x97, 0x22, 0x51, 0xde, 0x2f, 0xd5, 0x34,
0x30, 0xf5, 0x7e, 0x0f, 0xba, 0xf7, 0xf8, 0x98, 0x0b, 0x7e, 0x7b, 0x3c, 0x2e, 0xd3, 0x7f, 0x0d,
0x2e, 0xd6, 0xd4, 0x29, 0xad, 0xfa, 0x00, 0xd6, 0xef, 0xf1, 0xc3, 0xe9, 0x68, 0x8f, 0x9f, 0x14,
0x2f, 0x0f, 0x0c, 0x9a, 0xf9, 0x71, 0x72, 0xaa, 0xce, 0x96, 0x7e, 0xa3, 0xc3, 0x3b, 0xc6, 0x36,
0xfd, 0x3c, 0xe5, 0x03, 0x9d, 0x19, 0x43, 0xc8, 0x41, 0xca, 0x07, 0xfe, 0x3b, 0xc0, 0x6c, 0x3a,
0x6a, 0x09, 0x78, 0x73, 0xa7, 0x87, 0xfd, 0x7c, 0x96, 0x0b, 0x3e, 0xd1, 0x29, 0x3f, 0x36, 0xe4,
0x5f, 0x85, 0xce, 0x7e, 0x38, 0x0b, 0xf8, 0xc7, 0x2a, 0xfb, 0x10, 0x9d, 0xb7, 0x70, 0x86, 0xac,
0x6c, 0x9c, 0x37, 0xaa, 0xf6, 0xff, 0xa1, 0x01, 0x0b, 0xb2, 0x25, 0x52, 0x1d, 0xf2, 0x5c, 0x44,
0xb1, 0x8c, 0xd0, 0x2b, 0xaa, 0x16, 0x54, 0xe1, 0x8d, 0x46, 0x0d, 0x6f, 0x28, 0x73, 0x4a, 0xe7,
0x17, 0x28, 0x26, 0x70, 0x30, 0xf2, 0x4d, 0xcd, 0x9b, 0x65, 0x53, 0xf9, 0xa6, 0x1a, 0x28, 0x79,
0xc9, 0x85, 0x7c, 0x90, 0xf3, 0xd3, 0x4c, 0xab, 0xd8, 0xc1, 0x86, 0x6a, 0xa5, 0xd0, 0xa2, 0xe4,
0x9a, 0x8a, 0x14, 0xaa, 0x48, 0x9b, 0xa5, 0x57, 0x90, 0x36, 0xd2, 0xc6, 0x72, 0xa4, 0x0d, 0x83,
0xb5, 0x07, 0x9c, 0x07, 0x3c, 0x4d, 0x32, 0x9d, 0xc2, 0xe9, 0x7f, 0xd7, 0x83, 0x35, 0xa5, 0x3d,
0x4c, 0x1d, 0x7b, 0xdd, 0x51, 0x35, 0x5e, 0x5d, 0xd0, 0xf6, 0x0d, 0x58, 0x26, 0x67, 0x0b, 0x3d,
0x29, 0xf2, 0xac, 0x54, 0xfc, 0xc1, 0x01, 0x71, 0x4e, 0x3a, 0x0c, 0x39, 0x89, 0xc6, 0x6a, 0x83,
0x6d, 0x08, 0xd5, 0xa2, 0x76, 0xc6, 0x68, 0x7b, 0xbd, 0xc0, 0x94, 0xfd, 0xbf, 0xf7, 0x60, 0xdd,
0x9a, 0xb0, 0xe2, 0xa8, 0x5b, 0xa0, 0x5f, 0x2e, 0x65, 0x3c, 0x41, 0x5e, 0x8c, 0x0b, 0xae, 0x26,
0x2c, 0xba, 0x39, 0x8d, 0xe9, 0x60, 0xc2, 0x19, 0x4d, 0x30, 0x9f, 0xca, 0xdc, 0xa9, 0x66, 0x60,
0x43, 0xc8, 0x14, 0xa7, 0x9c, 0x3f, 0x35, 0x4d, 0xe6, 0xa8, 0x89, 0x83, 0xd1, 0xc3, 0x54, 0x12,
0x8b, 0x63, 0xd3, 0x48, 0x66, 0x5c, 0xb8, 0xa0, 0xff, 0xaf, 0x1e, 0x6c, 0x48, 0x0b, 0x44, 0xd9,
0x77, 0x26, 0xe9, 0x6a, 0x41, 0x9a, 0x5c, 0xf2, 0x76, 0xed, 0x9e, 0x0b, 0x54, 0x99, 0x7d, 0xe1,
0x15, 0xad, 0x26, 0xf3, 0x20, 0x79, 0xc6, 0x59, 0xcc, 0xd5, 0x9d, 0xc5, 0x0b, 0x76, 0xba, 0xce,
0x33, 0x9f, 0xaf, 0xf5, 0xcc, 0xef, 0x2c, 0xc2, 0x7c, 0x3e, 0x48, 0x52, 0xee, 0x6f, 0xc1, 0xa6,
0xbb, 0x38, 0x25, 0x4e, 0xbe, 0xef, 0x41, 0xf7, 0x81, 0x0c, 0x2b, 0x45, 0xf1, 0x68, 0x37, 0xca,
0x45, 0x92, 0x99, 0xb4, 0xd3, 0xcb, 0x00, 0xb9, 0x08, 0x33, 0x21, 0xd3, 0x42, 0x94, 0x4f, 0x5d,
0x20, 0x38, 0x47, 0x1e, 0x0f, 0x65, 0xad, 0x3c, 0x1b, 0x53, 0xc6, 0x83, 0xa1, 0xc7, 0xd2, 0x7e,
0x72, 0x74, 0x94, 0x73, 0x63, 0x23, 0xd9, 0x18, 0xba, 0x59, 0x78, 0x7b, 0xd1, 0xb1, 0xe0, 0x27,
0x24, 0x36, 0xa5, 0x0f, 0x55, 0x42, 0xfd, 0xbf, 0xf5, 0x60, 0xb5, 0x98, 0xe4, 0x7d, 0x04, 0xdd,
0x9b, 0x2e, 0xa7, 0x66, 0xdd, 0x74, 0xed, 0xed, 0x47, 0xc3, 0x7e, 0x14, 0xab, 0xb9, 0x59, 0x08,
0xdd, 0x3e, 0x55, 0x4a, 0xa6, 0x3a, 0x05, 0xc7, 0x86, 0xe4, 0x2b, 0x9d, 0xc0, 0xde, 0x32, 0xff,
0x46, 0x95, 0x28, 0xab, 0x67, 0x22, 0xa8, 0xd7, 0x82, 0x8c, 0xf1, 0xa9, 0xa2, 0xd6, 0x35, 0x8b,
0x84, 0xe2, 0x4f, 0xff, 0x3b, 0x1e, 0x5c, 0xac, 0xd9, 0x5c, 0x75, 0x33, 0xee, 0xc1, 0xfa, 0x91,
0xa9, 0xd4, 0x1b, 0x20, 0xaf, 0xc7, 0x96, 0xe2, 0xa2, 0xd2, 0xa2, 0x83, 0x6a, 0x07, 0xf6, 0x16,
0xac, 0x53, 0x90, 0x42, 0x6e, 0xa9, 0xf3, 0x68, 0x5d, 0xad, 0xd8, 0xf9, 0x41, 0x03, 0x56, 0x64,
0xcc, 0x58, 0x7e, 0x78, 0xc0, 0x33, 0xf6, 0x01, 0x2c, 0xaa, 0xcf, 0x3c, 0xd8, 0x79, 0x35, 0xac,
0xfb, 0x61, 0x49, 0x6f, 0xab, 0x0c, 0x2b, 0xde, 0xd9, 0xf8, 0xfd, 0x1f, 0xff, 0xc7, 0x9f, 0x34,
0x96, 0x59, 0x7b, 0xfb, 0xe4, 0xed, 0xed, 0x11, 0x8f, 0x73, 0xa4, 0xf1, 0x9b, 0x00, 0xc5, 0x97,
0x12, 0xac, 0x6b, 0x0c, 0x86, 0xd2, 0x97, 0x1d, 0xbd, 0x8b, 0x35, 0x35, 0x8a, 0xee, 0x45, 0xa2,
0xbb, 0xe1, 0xaf, 0x20, 0xdd, 0x28, 0x8e, 0x84, 0xfc, 0x6c, 0xe2, 0x3d, 0xef, 0x3a, 0x1b, 0x42,
0xc7, 0xfe, 0x62, 0x82, 0x69, 0xff, 0xac, 0xe6, 0x33, 0x8c, 0xde, 0x6b, 0xb5, 0x75, 0xda, 0x39,
0xa5, 0x31, 0xce, 0xfb, 0x6b, 0x38, 0xc6, 0x94, 0x5a, 0x98, 0x51, 0x76, 0xfe, 0xed, 0x35, 0x68,
0x99, 0x18, 0x07, 0xfb, 0x36, 0x2c, 0x3b, 0x61, 0x76, 0xa6, 0x09, 0xd7, 0x05, 0xee, 0x7b, 0x97,
0xea, 0x2b, 0xd5, 0xb0, 0x97, 0x69, 0xd8, 0x2e, 0xdb, 0xc2, 0x61, 0x55, 0x6c, 0x7b, 0x9b, 0xde,
0x1f, 0x64, 0x16, 0xcf, 0x53, 0x58, 0x71, 0x43, 0xe3, 0xec, 0x92, 0x2b, 0x50, 0x4a, 0xa3, 0x7d,
0xe6, 0x8c, 0x5a, 0x35, 0xdc, 0x25, 0x1a, 0x6e, 0x8b, 0x6d, 0xda, 0xc3, 0x99, 0xd8, 0x03, 0xa7,
0xbc, 0x2b, 0xfb, 0x53, 0x0a, 0xf6, 0x19, 0x73, 0xd4, 0x75, 0x9f, 0x58, 0x98, 0x43, 0xab, 0x7e,
0x67, 0xe1, 0x77, 0x69, 0x28, 0xc6, 0x68, 0x43, 0xed, 0x2f, 0x29, 0xd8, 0x47, 0xd0, 0x32, 0xe9,
0xd3, 0xec, 0x82, 0x95, 0xb3, 0x6e, 0xe7, 0x74, 0xf7, 0xba, 0xd5, 0x8a, 0xba, 0xa3, 0xb2, 0x29,
0x23, 0x43, 0xec, 0xc1, 0x79, 0x65, 0x70, 0x1e, 0xf2, 0x9f, 0x66, 0x25, 0x35, 0x1f, 0x80, 0xdc,
0xf4, 0xd8, 0x2d, 0x58, 0xd2, 0x59, 0xe9, 0x6c, 0xab, 0x3e, 0xbb, 0xbe, 0x77, 0xa1, 0x82, 0xab,
0xfb, 0x7c, 0x1b, 0xa0, 0x48, 0xa0, 0x36, 0x9c, 0x5f, 0xc9, 0xf3, 0x36, 0x9b, 0x58, 0x93, 0x6d,
0x3d, 0xa2, 0xfc, 0x71, 0x37, 0x3f, 0x9b, 0x7d, 0xb6, 0x68, 0x5f, 0x9b, 0xb9, 0xfd, 0x02, 0x82,
0xfe, 0x16, 0xed, 0xdd, 0x1a, 0xa3, 0xab, 0x14, 0xf3, 0x53, 0x9d, 0x81, 0x78, 0x0f, 0xda, 0x56,
0x52, 0x36, 0xd3, 0x14, 0xaa, 0x09, 0xdd, 0xbd, 0x5e, 0x5d, 0x95, 0x9a, 0xee, 0x57, 0x60, 0xd9,
0xc9, 0xae, 0x36, 0x37, 0xa3, 0x2e, 0x77, 0xdb, 0xdc, 0x8c, 0xfa, 0x84, 0xec, 0x6f, 0x42, 0xdb,
0xca, 0x85, 0x66, 0x56, 0xfe, 0x46, 0x29, 0x0b, 0xda, 0xcc, 0xa8, 0x2e, 0x75, 0x7a, 0x93, 0xd6,
0xbb, 0xe2, 0xb7, 0x70, 0xbd, 0x94, 0x86, 0x87, 0x4c, 0xf2, 0x6d, 0x58, 0x71, 0xb3, 0xa3, 0xcd,
0xad, 0xaa, 0xcd, 0xb3, 0x36, 0xb7, 0xea, 0x8c, 0x94, 0x6a, 0xc5, 0x90, 0xd7, 0x37, 0xcc, 0x20,
0xdb, 0x9f, 0xaa, 0x08, 0xff, 0x73, 0xf6, 0x75, 0x14, 0x1d, 0x2a, 0x2f, 0x92, 0x15, 0x39, 0xe1,
0x6e, 0xf6, 0xa4, 0xe1, 0xf6, 0x4a, 0x0a, 0xa5, 0xbf, 0x4e, 0xc4, 0xdb, 0xac, 0x58, 0x81, 0x94,
0xd0, 0x94, 0x1f, 0x69, 0x49, 0x68, 0x3b, 0x85, 0xd2, 0x92, 0xd0, 0x4e, 0x1a, 0x65, 0x59, 0x42,
0x8b, 0x08, 0x69, 0xc4, 0xb0, 0x5a, 0x7a, 0xb3, 0x35, 0x97, 0xa5, 0x3e, 0xe3, 0xa3, 0x77, 0xf9,
0xc5, 0x4f, 0xbd, 0xae, 0x98, 0xd1, 0xe2, 0x65, 0x5b, 0x27, 0xe8, 0xfc, 0x16, 0x74, 0xec, 0xa4,
0x5b, 0x23, 0xb3, 0x6b, 0x12, 0x74, 0x8d, 0xcc, 0xae, 0xcb, 0xd2, 0xd5, 0x87, 0xcb, 0x3a, 0xf6,
0x30, 0xec, 0x9b, 0xb0, 0x6a, 0x65, 0x07, 0x1c, 0xcc, 0xe2, 0x81, 0x61, 0x9e, 0x6a, 0x8e, 0x56,
0xaf, 0xce, 0x3e, 0xf3, 0x2f, 0x10, 0xe1, 0x75, 0xdf, 0x21, 0x8c, 0x8c, 0x73, 0x17, 0xda, 0x76,
0xe6, 0xc1, 0x0b, 0xe8, 0x5e, 0xb0, 0xaa, 0xec, 0xd4, 0xa6, 0x9b, 0x1e, 0xfb, 0x73, 0x0f, 0x3a,
0x76, 0xf6, 0x1f, 0x73, 0x82, 0x8a, 0x25, 0x3a, 0x5d, 0xbb, 0xce, 0x26, 0xe4, 0x07, 0x34, 0xc9,
0xbd, 0xeb, 0x5f, 0x71, 0x36, 0xf9, 0x53, 0xc7, 0xce, 0xbf, 0x51, 0xfe, 0x82, 0xe9, 0x79, 0xb9,
0x81, 0x9d, 0xc7, 0xf6, 0xfc, 0xa6, 0xc7, 0xde, 0x93, 0x5f, 0xb9, 0x69, 0x1f, 0x9d, 0x59, 0xc2,
0xad, 0xbc, 0x65, 0xf6, 0x07, 0x61, 0xd7, 0xbc, 0x9b, 0x1e, 0xfb, 0x96, 0xfc, 0x50, 0x49, 0xf5,
0xa5, 0x9d, 0x7f, 0xd5, 0xfe, 0xfe, 0x1b, 0xb4, 0x9a, 0xcb, 0xfe, 0x45, 0x67, 0x35, 0x65, 0xe9,
0xbe, 0x0f, 0x50, 0x04, 0x5c, 0x58, 0x29, 0xfa, 0x60, 0xe4, 0x5e, 0x35, 0x26, 0xe3, 0x9e, 0xa8,
0x0e, 0x52, 0x20, 0xc5, 0x8f, 0x24, 0x33, 0xaa, 0xf6, 0xb9, 0x39, 0xd2, 0x6a, 0xe0, 0xa4, 0xd7,
0xab, 0xab, 0xaa, 0x63, 0x45, 0x4d, 0x9f, 0x3d, 0x81, 0xe5, 0xbd, 0x24, 0x79, 0x3a, 0x4d, 0x4d,
0x10, 0xcf, 0xf5, 0xff, 0x77, 0xc3, 0xfc, 0xb8, 0x57, 0x5a, 0x85, 0x7f, 0x85, 0x48, 0xf5, 0x58,
0xd7, 0x22, 0xb5, 0xfd, 0x69, 0x11, 0xee, 0x79, 0xce, 0x42, 0x58, 0x37, 0x3a, 0xce, 0x4c, 0xbc,
0xe7, 0x92, 0xb1, 0xa3, 0x2e, 0x95, 0x21, 0x1c, 0xab, 0x43, 0xcf, 0x76, 0x3b, 0xd7, 0x34, 0x6f,
0x7a, 0x6c, 0x1f, 0x3a, 0xf7, 0xf8, 0x20, 0x19, 0x72, 0xe5, 0xb1, 0x6f, 0x14, 0x13, 0x37, 0xae,
0x7e, 0x6f, 0xd9, 0x01, 0xdd, 0x5b, 0x9f, 0x86, 0xb3, 0x8c, 0x7f, 0xbc, 0xfd, 0xa9, 0x8a, 0x05,
0x3c, 0xd7, 0xb7, 0x5e, 0xc7, 0x2f, 0x9c, 0x5b, 0x5f, 0x0a, 0x78, 0x38, 0xb7, 0xbe, 0x12, 0xf0,
0x70, 0xb6, 0x5a, 0xc7, 0x4f, 0xd8, 0x18, 0xd6, 0x2b, 0x31, 0x12, 0xa3, 0x29, 0xcf, 0x8a, 0xac,
0xf4, 0xae, 0x9c, 0xdd, 0xc0, 0x1d, 0xed, 0xba, 0x3b, 0xda, 0x01, 0x2c, 0xdf, 0xe3, 0x72, 0xb3,
0xe4, 0xc3, 0x58, 0xcf, 0x15, 0x23, 0xf6, 0x23, 0x5a, 0x59, 0xc4, 0x50, 0x9d, 0x2b, 0xd6, 0xe9,
0x55, 0x8a, 0x7d, 0x04, 0xed, 0x87, 0x5c, 0xe8, 0x97, 0x30, 0x63, 0x6f, 0x94, 0x9e, 0xc6, 0x7a,
0x35, 0x0f, 0x69, 0x2e, 0xcf, 0x10, 0xb5, 0x6d, 0x3e, 0x1c, 0x71, 0x79, 0xd9, 0xfb, 0xd1, 0xf0,
0x39, 0xfb, 0x75, 0x22, 0x6e, 0x1e, 0xcf, 0xb7, 0xac, 0x07, 0x14, 0x9b, 0xf8, 0x6a, 0x09, 0xaf,
0xa3, 0x1c, 0x27, 0x43, 0x6e, 0x29, 0xb8, 0x18, 0xda, 0x56, 0xa6, 0x84, 0xb9, 0x40, 0xd5, 0xec,
0x0c, 0x73, 0x81, 0x6a, 0x12, 0x2b, 0xfc, 0x6b, 0x34, 0x8e, 0xcf, 0xae, 0x14, 0xe3, 0xc8, 0x64,
0x8a, 0x62, 0xa4, 0xed, 0x4f, 0xc3, 0x89, 0x78, 0xce, 0x3e, 0xa4, 0x0f, 0x06, 0xec, 0xd7, 0xbe,
0xc2, 0xde, 0x29, 0x3f, 0x0c, 0x9a, 0xcd, 0xb2, 0xaa, 0x5c, 0x1b, 0x48, 0x0e, 0x45, 0x7a, 0xf0,
0x0b, 0x00, 0x07, 0x22, 0x49, 0xef, 0x85, 0x7c, 0x92, 0xc4, 0x85, 0xe4, 0x2a, 0x5e, 0xb4, 0x0a,
0xc9, 0x65, 0x3d, 0x6b, 0xb1, 0x0f, 0x2d, 0x8b, 0xd3, 0x79, 0x2c, 0xd5, 0xcc, 0x75, 0xe6, 0xa3,
0x97, 0xd9, 0x90, 0x9a, 0x87, 0xaf, 0x9b, 0x1e, 0xda, 0x8f, 0x45, 0x44, 0xce, 0xd8, 0x8f, 0x95,
0x60, 0x9f, 0x11, 0x7b, 0x35, 0xe1, 0xbb, 0x7d, 0x68, 0x15, 0x61, 0x21, 0xad, 0x92, 0xca, 0x41,
0x24, 0xa3, 0x63, 0x2a, 0xc1, 0x1a, 0x7f, 0x8d, 0xb6, 0x0a, 0xd8, 0x12, 0x6e, 0x15, 0x45, 0x60,
0x22, 0xd8, 0x90, 0x13, 0x34, 0x0a, 0x93, 0xde, 0x68, 0xf4, 0x4a, 0x6a, 0x02, 0x26, 0xe6, 0x36,
0xd7, 0xc6, 0x1b, 0x1c, 0xdf, 0x0e, 0xb9, 0x55, 0xbe, 0x0f, 0xa1, 0x68, 0x9e, 0xc0, 0x7a, 0xc5,
0x59, 0x36, 0x57, 0xfa, 0xac, 0x18, 0x85, 0xb9, 0xd2, 0x67, 0xfa, 0xd9, 0xfe, 0x79, 0x1a, 0x72,
0xd5, 0x07, 0x1c, 0x32, 0x3f, 0x8d, 0xc4, 0xe0, 0xf8, 0x3d, 0xef, 0xfa, 0xe1, 0x02, 0xfd, 0x8f,
0xc2, 0xe7, 0xfe, 0x27, 0x00, 0x00, 0xff, 0xff, 0x2a, 0xe2, 0xf7, 0xd2, 0x79, 0x41, 0x00, 0x00,
}

View File

@ -551,6 +551,19 @@ func request_Lightning_UpdateChannelPolicy_0(ctx context.Context, marshaler runt
}
func request_Lightning_ForwardingHistory_0(ctx context.Context, marshaler runtime.Marshaler, client LightningClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq ForwardingHistoryRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
msg, err := client.ForwardingHistory(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
// RegisterWalletUnlockerHandlerFromEndpoint is same as RegisterWalletUnlockerHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterWalletUnlockerHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
@ -1529,6 +1542,35 @@ func RegisterLightningHandler(ctx context.Context, mux *runtime.ServeMux, conn *
})
mux.Handle("POST", pattern_Lightning_ForwardingHistory_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()
if cn, ok := w.(http.CloseNotifier); ok {
go func(done <-chan struct{}, closed <-chan bool) {
select {
case <-done:
case <-closed:
cancel()
}
}(ctx.Done(), cn.CloseNotify())
}
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
rctx, err := runtime.AnnotateContext(ctx, mux, req)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Lightning_ForwardingHistory_0(rctx, inboundMarshaler, client, req, pathParams)
ctx = runtime.NewServerMetadataContext(ctx, md)
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
forward_Lightning_ForwardingHistory_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
@ -1588,6 +1630,8 @@ var (
pattern_Lightning_FeeReport_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", "chanpolicy"}, ""))
pattern_Lightning_ForwardingHistory_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1}, []string{"v1", "switch"}, ""))
)
var (
@ -1646,4 +1690,6 @@ var (
forward_Lightning_FeeReport_0 = runtime.ForwardResponseMessage
forward_Lightning_UpdateChannelPolicy_0 = runtime.ForwardResponseMessage
forward_Lightning_ForwardingHistory_0 = runtime.ForwardResponseMessage
)

View File

@ -531,6 +531,25 @@ service Lightning {
body: "*"
};
}
/** lncli: `forwardinghistory`
ForwardingHistory allows the caller to query the htlcswitch for a record of
all HTLC's forwarded within the target time range, and integer offset
within that time range. If no time-range is specified, then the first chunk
of the past 24 hrs of forwarding history are returned.
A list of forwarding events are returned. The size of each forwarding event
is 40 bytes, and the max message size able to be returned in gRPC is 4 MiB.
As a result each message can only contain 50k entries. Each response has
the index offset of the last entry. The index offset can be provided to the
request to allow the caller to skip a series of records.
*/
rpc ForwardingHistory(ForwardingHistoryRequest) returns (ForwardingHistoryResponse) {
option (google.api.http) = {
post: "/v1/switch"
body: "*"
};
};
}
message Transaction {
@ -1488,6 +1507,15 @@ message ChannelFeeReport {
message FeeReportResponse {
/// An array of channel fee reports which describes the current fee schedule for each channel.
repeated ChannelFeeReport channel_fees = 1 [json_name = "channel_fees"];
/// The total amount of fee revenue (in satoshis) the switch has collected over the past 24 hrs.
uint64 day_fee_sum = 2 [json_name = "day_fee_sum"];
/// The total amount of fee revenue (in satoshis) the switch has collected over the past 1 week.
uint64 week_fee_sum = 3 [json_name = "week_fee_sum"];
/// The total amount of fee revenue (in satoshis) the switch has collected over the past 1 month.
uint64 month_fee_sum = 4 [json_name = "month_fee_sum"];
}
message PolicyUpdateRequest {
@ -1510,3 +1538,47 @@ message PolicyUpdateRequest {
}
message PolicyUpdateResponse {
}
message ForwardingHistoryRequest {
/// Start time is the starting point of the forwarding history request. All records beyond this point will be included, respecting the end time, and the index offset.
uint64 start_time = 1 [json_name = "start_time"];
/// End time is the end point of the forwarding history request. The response will carry at most 50k records between the start time and the end time. The index offset can be used to implement pagination.
uint64 end_time = 2 [json_name = "end_time"];
/// Index offset is the offset in the time series to start at. As each response can only contain 50k records, callers can use this to skip around within a packed time series.
uint32 index_offset = 3 [json_name = "index_offset"];
/// The max number of events to return in the response to this query.
uint32 num_max_events = 4 [json_name = "num_max_events"];
}
message ForwardingEvent {
/// Timestamp is the time (unix epoch offset) that this circuit was completed.
uint64 timestamp = 1 [json_name = "timestamp"];
/// The incoming channel ID that carried the HTLC that created the circuit.
uint64 chan_id_in = 2 [json_name = "chan_id_in"];
/// The outgoing channel ID that carried the preimage that completed the circuit.
uint64 chan_id_out = 4 [json_name = "chan_id_out"];
/// The total amount of the incoming HTLC that created half the circuit.
uint64 amt_in = 5 [json_name = "amt_in"];
/// The total amount of the outgoign HTLC that created the second half of the circuit.
uint64 amt_out = 6 [json_name = "amt_out"];
/// The total fee that this payment circuit carried.
uint64 fee = 7 [json_name = "fee"];
// TODO(roasbeef): add settlement latency?
// * use FPE on the chan id?
// * also list failures?
}
message ForwardingHistoryResponse {
/// A list of forwarding events from the time slice of the time series specified in the request.
repeated ForwardingEvent forwarding_events = 1 [json_name = "forwarding_events"];
/// The index of the last time in the set of returned forwarding events. Can be used to seek further, pagination style.
uint32 last_offset_index = 2 [json_name = "last_offset_index"];
}

View File

@ -670,6 +670,34 @@
]
}
},
"/v1/switch": {
"post": {
"summary": "* lncli: `forwardinghistory`\nForwardingHistory allows the caller to query the htlcswitch for a record of\nall HTLC's forwarded within the target time range, and integer offset\nwithin that time range. If no time-range is specified, then the first chunk\nof the past 24 hrs of forwarding history are returned.",
"description": "A list of forwarding events are returned. The size of each forwarding event\nis 40 bytes, and the max message size able to be returned in gRPC is 4 MiB.\nAs a result each message can only contain 50k entries. Each response has\nthe index offset of the last entry. The index offset can be provided to the\nrequest to allow the caller to skip a series of records.",
"operationId": "ForwardingHistory",
"responses": {
"200": {
"description": "",
"schema": {
"$ref": "#/definitions/lnrpcForwardingHistoryResponse"
}
}
},
"parameters": [
{
"name": "body",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/lnrpcForwardingHistoryRequest"
}
}
],
"tags": [
"Lightning"
]
}
},
"/v1/transactions": {
"get": {
"summary": "* lncli: `listchaintxns`\nGetTransactions returns a list describing all the known transactions\nrelevant to the wallet.",
@ -1189,6 +1217,98 @@
"$ref": "#/definitions/lnrpcChannelFeeReport"
},
"description": "/ An array of channel fee reports which describes the current fee schedule for each channel."
},
"day_fee_sum": {
"type": "string",
"format": "uint64",
"description": "/ The total amount of fee revenue (in satoshis) the switch has collected over the past 24 hrs."
},
"week_fee_sum": {
"type": "string",
"format": "uint64",
"description": "/ The total amount of fee revenue (in satoshis) the switch has collected over the past 1 week."
},
"month_fee_sum": {
"type": "string",
"format": "uint64",
"description": "/ The total amount of fee revenue (in satoshis) the switch has collected over the past 1 month."
}
}
},
"lnrpcForwardingEvent": {
"type": "object",
"properties": {
"timestamp": {
"type": "string",
"format": "uint64",
"description": "/ Timestamp is the time (unix epoch offset) that this circuit was completed."
},
"chan_id_in": {
"type": "string",
"format": "uint64",
"description": "/ The incoming channel ID that carried the HTLC that created the circuit."
},
"chan_id_out": {
"type": "string",
"format": "uint64",
"description": "/ The outgoing channel ID that carried the preimage that completed the circuit."
},
"amt_in": {
"type": "string",
"format": "uint64",
"description": "/ The total amount of the incoming HTLC that created half the circuit."
},
"amt_out": {
"type": "string",
"format": "uint64",
"description": "/ The total amount of the outgoign HTLC that created the second half of the circuit."
},
"fee": {
"type": "string",
"format": "uint64",
"description": "/ The total fee that this payment circuit carried."
}
}
},
"lnrpcForwardingHistoryRequest": {
"type": "object",
"properties": {
"start_time": {
"type": "string",
"format": "uint64",
"description": "/ Start time is the starting point of the forwarding history request. All records beyond this point will be included, respecting the end time, and the index offset."
},
"end_time": {
"type": "string",
"format": "uint64",
"description": "/ End time is the end point of the forwarding history request. The response will carry at most 50k records between the start time and the end time. The index offset can be used to implement pagination."
},
"index_offset": {
"type": "integer",
"format": "int64",
"description": "/ Index offset is the offset in the time series to start at. As each response can only contain 50k records, callers can use this to skip around within a packed time series."
},
"num_max_events": {
"type": "integer",
"format": "int64",
"description": "/ The max number of events to return in the response to this query."
}
}
},
"lnrpcForwardingHistoryResponse": {
"type": "object",
"properties": {
"forwarding_events": {
"type": "array",
"items": {
"$ref": "#/definitions/lnrpcForwardingEvent"
},
"description": "/ A list of forwarding events from the time slice of the time series specified in the request."
},
"last_offset_index": {
"type": "integer",
"format": "int64",
"description": "/ The index of the last time in the set of returned forwarding events. Can be used to seek further, pagination style."
}
}
},

View File

@ -263,6 +263,10 @@ var (
Entity: "offchain",
Action: "write",
}},
"/lnrpc.Lightning/ForwardingHistory": {{
Entity: "offchain",
Action: "read",
}},
}
)
@ -3042,6 +3046,8 @@ func (r *rpcServer) FeeReport(ctx context.Context,
// TODO(roasbeef): use UnaryInterceptor to add automated logging
rpcsLog.Debugf("[feereport]")
channelGraph := r.server.chanDB.ChannelGraph()
selfNode, err := channelGraph.SourceNode()
if err != nil {
@ -3074,8 +3080,94 @@ func (r *rpcServer) FeeReport(ctx context.Context,
return nil, err
}
fwdEventLog := r.server.chanDB.ForwardingLog()
// computeFeeSum is a helper function that computes the total fees for
// a particular time slice described by a forwarding event query.
computeFeeSum := func(query channeldb.ForwardingEventQuery) (lnwire.MilliSatoshi, error) {
var totalFees lnwire.MilliSatoshi
// We'll continue to fetch the next query and accumulate the
// fees until the next query returns no events.
for {
timeSlice, err := fwdEventLog.Query(query)
if err != nil {
return 0, nil
}
// If the timeslice is empty, then we'll return as
// we've retrieved all the entries in this range.
if len(timeSlice.ForwardingEvents) == 0 {
break
}
// Otherwise, we'll tally up an accumulate the total
// fees for this time slice.
for _, event := range timeSlice.ForwardingEvents {
fee := event.AmtIn - event.AmtOut
totalFees += fee
}
// We'll now take the last offset index returned as
// part of this response, and modify our query to start
// at this index. This has a pagination effect in the
// case that our query bounds has more than 100k
// entries.
query.IndexOffset = timeSlice.LastIndexOffset
}
return totalFees, nil
}
now := time.Now()
// Before we perform the queries below, we'll instruct the switch to
// flush any pending events to disk. This ensure we get a complete
// snapshot at this particular time.
if r.server.htlcSwitch.FlushForwardingEvents(); err != nil {
return nil, fmt.Errorf("unable to flush forwarding "+
"events: %v", err)
}
// In addition to returning the current fee schedule for each channel.
// We'll also perform a series of queries to obtain the total fees
// earned over the past day, week, and month.
dayQuery := channeldb.ForwardingEventQuery{
StartTime: now.Add(-time.Hour * 24),
EndTime: now,
NumMaxEvents: 1000,
}
dayFees, err := computeFeeSum(dayQuery)
if err != nil {
return nil, fmt.Errorf("unable to retrieve day fees: %v", err)
}
weekQuery := channeldb.ForwardingEventQuery{
StartTime: now.Add(-time.Hour * 24 * 7),
EndTime: now,
NumMaxEvents: 1000,
}
weekFees, err := computeFeeSum(weekQuery)
if err != nil {
return nil, fmt.Errorf("unable to retrieve day fees: %v", err)
}
monthQuery := channeldb.ForwardingEventQuery{
StartTime: now.Add(-time.Hour * 24 * 30),
EndTime: now,
NumMaxEvents: 1000,
}
monthFees, err := computeFeeSum(monthQuery)
if err != nil {
return nil, fmt.Errorf("unable to retrieve day fees: %v", err)
}
return &lnrpc.FeeReportResponse{
ChannelFees: feeReports,
DayFeeSum: uint64(dayFees.ToSatoshis()),
WeekFeeSum: uint64(weekFees.ToSatoshis()),
MonthFeeSum: uint64(monthFees.ToSatoshis()),
}, nil
}
@ -3181,3 +3273,92 @@ func (r *rpcServer) UpdateChannelPolicy(ctx context.Context,
return &lnrpc.PolicyUpdateResponse{}, nil
}
// ForwardingHistory allows the caller to query the htlcswitch for a record of
// all HTLC's forwarded within the target time range, and integer offset within
// that time range. If no time-range is specified, then the first chunk of the
// past 24 hrs of forwarding history are returned.
// A list of forwarding events are returned. The size of each forwarding event
// is 40 bytes, and the max message size able to be returned in gRPC is 4 MiB.
// In order to safely stay under this max limit, we'll return 50k events per
// response. Each response has the index offset of the last entry. The index
// offset can be provided to the request to allow the caller to skip a series
// of records.
func (r *rpcServer) ForwardingHistory(ctx context.Context,
req *lnrpc.ForwardingHistoryRequest) (*lnrpc.ForwardingHistoryResponse, error) {
rpcsLog.Debugf("[forwardinghistory]")
// Before we perform the queries below, we'll instruct the switch to
// flush any pending events to disk. This ensure we get a complete
// snapshot at this particular time.
if err := r.server.htlcSwitch.FlushForwardingEvents(); err != nil {
return nil, fmt.Errorf("unable to flush forwarding "+
"events: %v", err)
}
var (
startTime, endTime time.Time
numEvents uint32
)
// If the start and end time were not set, then we'll just return the
// records over the past 24 hours.
if req.StartTime == 0 && req.EndTime == 0 {
now := time.Now()
startTime = now.Add(-time.Hour * 24)
endTime = now
} else {
startTime = time.Unix(int64(req.StartTime), 0)
endTime = time.Unix(int64(req.EndTime), 0)
}
// If the number of events wasn't specified, then we'll default to
// returning the last 100 events.
numEvents = req.NumMaxEvents
if numEvents == 0 {
numEvents = 100
}
// Next, we'll map the proto request into a format the is understood by
// the forwarding log.
eventQuery := channeldb.ForwardingEventQuery{
StartTime: startTime,
EndTime: endTime,
IndexOffset: req.IndexOffset,
NumMaxEvents: numEvents,
}
timeSlice, err := r.server.chanDB.ForwardingLog().Query(eventQuery)
if err != nil {
return nil, fmt.Errorf("unable to query forwarding log: %v", err)
}
// TODO(roasbeef): add settlement latency?
// * use FPE on all records?
// With the events retrieved, we'll now map them into the proper proto
// response.
//
// TODO(roasbeef): show in ns for the outside?
resp := &lnrpc.ForwardingHistoryResponse{
ForwardingEvents: make([]*lnrpc.ForwardingEvent, len(timeSlice.ForwardingEvents)),
LastOffsetIndex: timeSlice.LastIndexOffset,
}
for i, event := range timeSlice.ForwardingEvents {
amtInSat := event.AmtIn.ToSatoshis()
amtOutSat := event.AmtOut.ToSatoshis()
resp.ForwardingEvents[i] = &lnrpc.ForwardingEvent{
Timestamp: uint64(event.Timestamp.Unix()),
ChanIdIn: event.IncomingChanID.ToUint64(),
ChanIdOut: event.OutgoingChanID.ToUint64(),
AmtIn: uint64(amtInSat),
AmtOut: uint64(amtOutSat),
Fee: uint64(amtInSat - amtOutSat),
}
}
return resp, nil
}

View File

@ -221,6 +221,7 @@ func newServer(listenAddrs []string, chanDB *channeldb.DB, cc *chainControl,
pubKey[:], err)
}
},
FwdingLog: chanDB.ForwardingLog(),
})
// If external IP addresses have been specified, add those to the list