Compare commits

...

11 Commits

Author SHA1 Message Date
William Banfield
44e9f77ce3 make linter happy 2022-08-18 16:19:38 -04:00
William Banfield
4fa4ababa4 fixup abci clie 2022-08-18 16:16:47 -04:00
William Banfield
06eb1c5a4b fix stack overflow 2022-08-18 15:24:38 -04:00
William Banfield
78a3a3dd56 move new to begin block 2022-08-18 15:19:22 -04:00
William Banfield
8bcfc626ca update kvstore to mark transactions as replacements 2022-08-18 14:05:11 -04:00
William Banfield
b3a507bb08 Merge branch 'feature/abci++ppp' into wb/option-3 2022-08-18 13:43:04 -04:00
William Banfield
5852743a16 make linter happy 2022-08-18 13:42:45 -04:00
William Banfield
d2a8d2952a update test 2022-08-18 13:42:34 -04:00
William Banfield
6dbc09ce2c tests pass 2022-08-17 11:59:04 -04:00
William Banfield
e0663e1837 code builds 2022-08-17 11:48:44 -04:00
William Banfield
1655bf34fc regen protos 2022-08-17 11:29:28 -04:00
16 changed files with 314 additions and 1062 deletions

View File

@@ -2,7 +2,6 @@ package main
import (
"bufio"
"bytes"
"encoding/hex"
"errors"
"fmt"
@@ -332,9 +331,7 @@ func cmdTest(cmd *cobra.Command, args []string) error {
func() error {
return servertest.PrepareProposal(client, [][]byte{
{0x01},
}, []types.TxRecord_TxAction{
types.TxRecord_UNMODIFIED,
}, nil)
}, [][]byte{{0x01}}, nil)
},
func() error {
return servertest.ProcessProposal(client, [][]byte{
@@ -609,16 +606,6 @@ func cmdQuery(cmd *cobra.Command, args []string) error {
return nil
}
func inTxArray(txByteArray [][]byte, tx []byte) bool {
for _, txTmp := range txByteArray {
if bytes.Equal(txTmp, tx) {
return true
}
}
return false
}
func cmdPrepareProposal(cmd *cobra.Command, args []string) error {
txsBytesArray := make([][]byte, len(args))
@@ -638,22 +625,12 @@ func cmdPrepareProposal(cmd *cobra.Command, args []string) error {
if err != nil {
return err
}
resps := make([]response, 0, len(res.TxRecords)+1)
for _, tx := range res.TxRecords {
existingTx := inTxArray(txsBytesArray, tx.Tx)
if tx.Action == types.TxRecord_UNKNOWN ||
(existingTx && tx.Action == types.TxRecord_ADDED) ||
(!existingTx && (tx.Action == types.TxRecord_UNMODIFIED || tx.Action == types.TxRecord_REMOVED)) {
resps = append(resps, response{
Code: codeBad,
Log: "Failed. Tx: " + string(tx.GetTx()) + " action: " + tx.Action.String(),
})
} else {
resps = append(resps, response{
Code: code.CodeTypeOK,
Log: "Succeeded. Tx: " + string(tx.Tx) + " action: " + tx.Action.String(),
})
}
resps := make([]response, 0, len(res.Txs))
for _, tx := range res.Txs {
resps = append(resps, response{
Code: code.CodeTypeOK,
Log: "Succeeded. Tx: " + string(tx),
})
}
printResponse(cmd, args, resps...)

View File

@@ -7,4 +7,5 @@ const (
CodeTypeBadNonce uint32 = 2
CodeTypeUnauthorized uint32 = 3
CodeTypeUnknownError uint32 = 4
CodeTypeExecuted uint32 = 5
)

View File

@@ -68,6 +68,7 @@ type Application struct {
state State
RetainBlocks int64 // blocks to retain after commit (via ResponseCommit.RetainHeight)
txToRemove map[string]struct{}
}
func NewApplication() *Application {
@@ -87,6 +88,9 @@ func (app *Application) Info(req types.RequestInfo) (resInfo types.ResponseInfo)
// tx is either "key=value" or just arbitrary bytes
func (app *Application) DeliverTx(req types.RequestDeliverTx) types.ResponseDeliverTx {
if isReplacedTx(req.Tx) {
app.txToRemove[string(req.Tx)] = struct{}{}
}
var key, value []byte
parts := bytes.Split(req.Tx, []byte("="))
if len(parts) == 2 {
@@ -117,6 +121,11 @@ func (app *Application) DeliverTx(req types.RequestDeliverTx) types.ResponseDeli
}
func (app *Application) CheckTx(req types.RequestCheckTx) types.ResponseCheckTx {
if req.Type == types.CheckTxType_Recheck {
if _, ok := app.txToRemove[string(req.Tx)]; ok {
return types.ResponseCheckTx{Code: code.CodeTypeExecuted, GasWanted: 1}
}
}
return types.ResponseCheckTx{Code: code.CodeTypeOK, GasWanted: 1}
}
@@ -126,6 +135,8 @@ func (app *Application) Commit() types.ResponseCommit {
binary.PutVarint(appHash, app.state.Size)
app.state.AppHash = appHash
app.state.Height++
// empty out the set of transactions to remove via rechecktx
saveState(app.state)
resp := types.ResponseCommit{Data: appHash}
@@ -171,6 +182,11 @@ func (app *Application) Query(reqQuery types.RequestQuery) (resQuery types.Respo
return resQuery
}
func (app *Application) BeginBlock(req types.RequestBeginBlock) types.ResponseBeginBlock {
app.txToRemove = map[string]struct{}{}
return types.ResponseBeginBlock{}
}
func (app *Application) ProcessProposal(
req types.RequestProcessProposal) types.ResponseProcessProposal {
for _, tx := range req.Txs {

View File

@@ -45,7 +45,10 @@ func NewPersistentKVStoreApplication(dbDir string) *PersistentKVStoreApplication
state := loadState(db)
return &PersistentKVStoreApplication{
app: &Application{state: state},
app: &Application{
state: state,
txToRemove: map[string]struct{}{},
},
valAddrToPubKeyMap: make(map[string]pc.PublicKey),
logger: log.NewNopLogger(),
}
@@ -142,7 +145,7 @@ func (app *PersistentKVStoreApplication) BeginBlock(req types.RequestBeginBlock)
}
}
return types.ResponseBeginBlock{}
return app.app.BeginBlock(req)
}
// Update the validator set
@@ -172,7 +175,7 @@ func (app *PersistentKVStoreApplication) ApplySnapshotChunk(
func (app *PersistentKVStoreApplication) PrepareProposal(
req types.RequestPrepareProposal) types.ResponsePrepareProposal {
return types.ResponsePrepareProposal{TxRecords: app.substPrepareTx(req.Txs, req.MaxTxBytes)}
return types.ResponsePrepareProposal{Txs: app.substPrepareTx(req.Txs, req.MaxTxBytes)}
}
func (app *PersistentKVStoreApplication) ProcessProposal(
@@ -302,10 +305,15 @@ func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate
// -----------------------------
const PreparePrefix = "prepare"
const (
PreparePrefix = "prepare"
ReplacePrefix = "replace"
)
func isPrepareTx(tx []byte) bool {
return bytes.HasPrefix(tx, []byte(PreparePrefix))
func isPrepareTx(tx []byte) bool { return bytes.HasPrefix(tx, []byte(PreparePrefix)) }
func isReplacedTx(tx []byte) bool {
return bytes.HasPrefix(tx, []byte(ReplacePrefix))
}
// execPrepareTx is noop. tx data is considered as placeholder
@@ -317,32 +325,19 @@ func (app *PersistentKVStoreApplication) execPrepareTx(tx []byte) types.Response
// substPrepareTx substitutes all the transactions prefixed with 'prepare' in the
// proposal for transactions with the prefix stripped.
// It marks all of the original transactions as 'REMOVED' so that
// Tendermint will remove them from its mempool.
func (app *PersistentKVStoreApplication) substPrepareTx(blockData [][]byte, maxTxBytes int64) []*types.TxRecord {
trs := make([]*types.TxRecord, 0, len(blockData))
var removed []*types.TxRecord
func (app *PersistentKVStoreApplication) substPrepareTx(blockData [][]byte, maxTxBytes int64) [][]byte {
txs := make([][]byte, 0, len(blockData))
var totalBytes int64
for _, tx := range blockData {
txMod := tx
action := types.TxRecord_UNMODIFIED
if isPrepareTx(tx) {
removed = append(removed, &types.TxRecord{
Tx: tx,
Action: types.TxRecord_REMOVED,
})
txMod = bytes.TrimPrefix(tx, []byte(PreparePrefix))
action = types.TxRecord_ADDED
txMod = bytes.Replace(tx, []byte(PreparePrefix), []byte(ReplacePrefix), 1)
}
totalBytes += int64(len(txMod))
if totalBytes > maxTxBytes {
break
}
trs = append(trs, &types.TxRecord{
Tx: txMod,
Action: action,
})
txs = append(txs, txMod)
}
return append(trs, removed...)
return txs
}

View File

@@ -65,13 +65,13 @@ func DeliverTx(client abcicli.Client, txBytes []byte, codeExp uint32, dataExp []
return nil
}
func PrepareProposal(client abcicli.Client, txBytes [][]byte, codeExp []types.TxRecord_TxAction, dataExp []byte) error {
func PrepareProposal(client abcicli.Client, txBytes [][]byte, txExpected [][]byte, dataExp []byte) error {
res, _ := client.PrepareProposalSync(types.RequestPrepareProposal{Txs: txBytes})
for i, tx := range res.TxRecords {
if tx.Action != codeExp[i] {
for i, tx := range res.Txs {
if !bytes.Equal(tx, txExpected[i]) {
fmt.Println("Failed test: PrepareProposal")
fmt.Printf("PrepareProposal response code was unexpected. Got %v expected %v.",
tx.Action, codeExp)
fmt.Printf("PrepareProposal transaction was unexpected. Got %x expected %x.",
tx, txExpected[i])
return errors.New("PrepareProposal error")
}
}

View File

@@ -11,7 +11,7 @@ deliver_tx "def=xyz"
commit
query "def"
prepare_proposal "preparedef"
process_proposal "def"
process_proposal "replacedef"
process_proposal "preparedef"
prepare_proposal
process_proposal

View File

@@ -10,7 +10,7 @@
> prepare_proposal "abc"
-> code: OK
-> log: Succeeded. Tx: abc action: UNMODIFIED
-> log: Succeeded. Tx: abc
> process_proposal "abc"
-> code: OK
@@ -59,11 +59,9 @@
> prepare_proposal "preparedef"
-> code: OK
-> log: Succeeded. Tx: def action: ADDED
-> code: OK
-> log: Succeeded. Tx: preparedef action: REMOVED
-> log: Succeeded. Tx: replacedef
> process_proposal "def"
> process_proposal "replacedef"
-> code: OK
-> status: ACCEPT

View File

@@ -95,19 +95,16 @@ func (BaseApplication) ApplySnapshotChunk(req RequestApplySnapshotChunk) Respons
}
func (BaseApplication) PrepareProposal(req RequestPrepareProposal) ResponsePrepareProposal {
trs := make([]*TxRecord, 0, len(req.Txs))
txs := make([][]byte, 0, len(req.Txs))
var totalBytes int64
for _, tx := range req.Txs {
totalBytes += int64(len(tx))
if totalBytes > req.MaxTxBytes {
break
}
trs = append(trs, &TxRecord{
Action: TxRecord_UNMODIFIED,
Tx: tx,
})
txs = append(txs, tx)
}
return ResponsePrepareProposal{TxRecords: trs}
return ResponsePrepareProposal{Txs: txs}
}
func (BaseApplication) ProcessProposal(req RequestProcessProposal) ResponseProcessProposal {

View File

@@ -188,38 +188,6 @@ func (ResponseProcessProposal_ProposalStatus) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_252557cfdd89a31a, []int{34, 0}
}
// TxAction contains App-provided information on what to do with a transaction that is part of a raw proposal
type TxRecord_TxAction int32
const (
TxRecord_UNKNOWN TxRecord_TxAction = 0
TxRecord_UNMODIFIED TxRecord_TxAction = 1
TxRecord_ADDED TxRecord_TxAction = 2
TxRecord_REMOVED TxRecord_TxAction = 3
)
var TxRecord_TxAction_name = map[int32]string{
0: "UNKNOWN",
1: "UNMODIFIED",
2: "ADDED",
3: "REMOVED",
}
var TxRecord_TxAction_value = map[string]int32{
"UNKNOWN": 0,
"UNMODIFIED": 1,
"ADDED": 2,
"REMOVED": 3,
}
func (x TxRecord_TxAction) String() string {
return proto.EnumName(TxRecord_TxAction_name, int32(x))
}
func (TxRecord_TxAction) EnumDescriptor() ([]byte, []int) {
return fileDescriptor_252557cfdd89a31a, []int{42, 0}
}
type Request struct {
// Types that are valid to be assigned to Value:
// *Request_Echo
@@ -2675,7 +2643,7 @@ func (m *ResponseApplySnapshotChunk) GetRejectSenders() []string {
}
type ResponsePrepareProposal struct {
TxRecords []*TxRecord `protobuf:"bytes,1,rep,name=tx_records,json=txRecords,proto3" json:"tx_records,omitempty"`
Txs [][]byte `protobuf:"bytes,1,rep,name=txs,proto3" json:"txs,omitempty"`
}
func (m *ResponsePrepareProposal) Reset() { *m = ResponsePrepareProposal{} }
@@ -2711,9 +2679,9 @@ func (m *ResponsePrepareProposal) XXX_DiscardUnknown() {
var xxx_messageInfo_ResponsePrepareProposal proto.InternalMessageInfo
func (m *ResponsePrepareProposal) GetTxRecords() []*TxRecord {
func (m *ResponsePrepareProposal) GetTxs() [][]byte {
if m != nil {
return m.TxRecords
return m.Txs
}
return nil
}
@@ -3181,58 +3149,6 @@ func (m *TxResult) GetResult() ResponseDeliverTx {
return ResponseDeliverTx{}
}
type TxRecord struct {
Action TxRecord_TxAction `protobuf:"varint,1,opt,name=action,proto3,enum=tendermint.abci.TxRecord_TxAction" json:"action,omitempty"`
Tx []byte `protobuf:"bytes,2,opt,name=tx,proto3" json:"tx,omitempty"`
}
func (m *TxRecord) Reset() { *m = TxRecord{} }
func (m *TxRecord) String() string { return proto.CompactTextString(m) }
func (*TxRecord) ProtoMessage() {}
func (*TxRecord) Descriptor() ([]byte, []int) {
return fileDescriptor_252557cfdd89a31a, []int{42}
}
func (m *TxRecord) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
}
func (m *TxRecord) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
if deterministic {
return xxx_messageInfo_TxRecord.Marshal(b, m, deterministic)
} else {
b = b[:cap(b)]
n, err := m.MarshalToSizedBuffer(b)
if err != nil {
return nil, err
}
return b[:n], nil
}
}
func (m *TxRecord) XXX_Merge(src proto.Message) {
xxx_messageInfo_TxRecord.Merge(m, src)
}
func (m *TxRecord) XXX_Size() int {
return m.Size()
}
func (m *TxRecord) XXX_DiscardUnknown() {
xxx_messageInfo_TxRecord.DiscardUnknown(m)
}
var xxx_messageInfo_TxRecord proto.InternalMessageInfo
func (m *TxRecord) GetAction() TxRecord_TxAction {
if m != nil {
return m.Action
}
return TxRecord_UNKNOWN
}
func (m *TxRecord) GetTx() []byte {
if m != nil {
return m.Tx
}
return nil
}
// Validator
type Validator struct {
Address []byte `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty"`
@@ -3244,7 +3160,7 @@ func (m *Validator) Reset() { *m = Validator{} }
func (m *Validator) String() string { return proto.CompactTextString(m) }
func (*Validator) ProtoMessage() {}
func (*Validator) Descriptor() ([]byte, []int) {
return fileDescriptor_252557cfdd89a31a, []int{43}
return fileDescriptor_252557cfdd89a31a, []int{42}
}
func (m *Validator) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -3297,7 +3213,7 @@ func (m *ValidatorUpdate) Reset() { *m = ValidatorUpdate{} }
func (m *ValidatorUpdate) String() string { return proto.CompactTextString(m) }
func (*ValidatorUpdate) ProtoMessage() {}
func (*ValidatorUpdate) Descriptor() ([]byte, []int) {
return fileDescriptor_252557cfdd89a31a, []int{44}
return fileDescriptor_252557cfdd89a31a, []int{43}
}
func (m *ValidatorUpdate) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -3350,7 +3266,7 @@ func (m *VoteInfo) Reset() { *m = VoteInfo{} }
func (m *VoteInfo) String() string { return proto.CompactTextString(m) }
func (*VoteInfo) ProtoMessage() {}
func (*VoteInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_252557cfdd89a31a, []int{45}
return fileDescriptor_252557cfdd89a31a, []int{44}
}
func (m *VoteInfo) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -3403,7 +3319,7 @@ func (m *ExtendedVoteInfo) Reset() { *m = ExtendedVoteInfo{} }
func (m *ExtendedVoteInfo) String() string { return proto.CompactTextString(m) }
func (*ExtendedVoteInfo) ProtoMessage() {}
func (*ExtendedVoteInfo) Descriptor() ([]byte, []int) {
return fileDescriptor_252557cfdd89a31a, []int{46}
return fileDescriptor_252557cfdd89a31a, []int{45}
}
func (m *ExtendedVoteInfo) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -3471,7 +3387,7 @@ func (m *Misbehavior) Reset() { *m = Misbehavior{} }
func (m *Misbehavior) String() string { return proto.CompactTextString(m) }
func (*Misbehavior) ProtoMessage() {}
func (*Misbehavior) Descriptor() ([]byte, []int) {
return fileDescriptor_252557cfdd89a31a, []int{47}
return fileDescriptor_252557cfdd89a31a, []int{46}
}
func (m *Misbehavior) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -3547,7 +3463,7 @@ func (m *Snapshot) Reset() { *m = Snapshot{} }
func (m *Snapshot) String() string { return proto.CompactTextString(m) }
func (*Snapshot) ProtoMessage() {}
func (*Snapshot) Descriptor() ([]byte, []int) {
return fileDescriptor_252557cfdd89a31a, []int{48}
return fileDescriptor_252557cfdd89a31a, []int{47}
}
func (m *Snapshot) XXX_Unmarshal(b []byte) error {
return m.Unmarshal(b)
@@ -3617,7 +3533,6 @@ func init() {
proto.RegisterEnum("tendermint.abci.ResponseOfferSnapshot_Result", ResponseOfferSnapshot_Result_name, ResponseOfferSnapshot_Result_value)
proto.RegisterEnum("tendermint.abci.ResponseApplySnapshotChunk_Result", ResponseApplySnapshotChunk_Result_name, ResponseApplySnapshotChunk_Result_value)
proto.RegisterEnum("tendermint.abci.ResponseProcessProposal_ProposalStatus", ResponseProcessProposal_ProposalStatus_name, ResponseProcessProposal_ProposalStatus_value)
proto.RegisterEnum("tendermint.abci.TxRecord_TxAction", TxRecord_TxAction_name, TxRecord_TxAction_value)
proto.RegisterType((*Request)(nil), "tendermint.abci.Request")
proto.RegisterType((*RequestEcho)(nil), "tendermint.abci.RequestEcho")
proto.RegisterType((*RequestFlush)(nil), "tendermint.abci.RequestFlush")
@@ -3660,7 +3575,6 @@ func init() {
proto.RegisterType((*Event)(nil), "tendermint.abci.Event")
proto.RegisterType((*EventAttribute)(nil), "tendermint.abci.EventAttribute")
proto.RegisterType((*TxResult)(nil), "tendermint.abci.TxResult")
proto.RegisterType((*TxRecord)(nil), "tendermint.abci.TxRecord")
proto.RegisterType((*Validator)(nil), "tendermint.abci.Validator")
proto.RegisterType((*ValidatorUpdate)(nil), "tendermint.abci.ValidatorUpdate")
proto.RegisterType((*VoteInfo)(nil), "tendermint.abci.VoteInfo")
@@ -3672,207 +3586,202 @@ func init() {
func init() { proto.RegisterFile("tendermint/abci/types.proto", fileDescriptor_252557cfdd89a31a) }
var fileDescriptor_252557cfdd89a31a = []byte{
// 3199 bytes of a gzipped FileDescriptorProto
// 3108 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5a, 0xcd, 0x73, 0x23, 0xc5,
0x15, 0xd7, 0xf7, 0xc7, 0xd3, 0xa7, 0x7b, 0xcd, 0xae, 0x56, 0x2c, 0xb6, 0x77, 0xb6, 0x80, 0xdd,
0x05, 0x6c, 0x62, 0xb2, 0xb0, 0x04, 0x08, 0xd8, 0xb2, 0x16, 0x19, 0x7b, 0x6d, 0x67, 0x2c, 0x2f,
0x21, 0x1f, 0x3b, 0xb4, 0x34, 0x6d, 0x6b, 0x58, 0x69, 0x66, 0x98, 0x19, 0x19, 0x99, 0x63, 0xa8,
0x54, 0xa5, 0xc8, 0x85, 0x5b, 0xb8, 0x70, 0xc8, 0x21, 0xff, 0x43, 0x4e, 0xb9, 0xe4, 0x42, 0x55,
0x2e, 0x1c, 0x72, 0xc8, 0x21, 0x45, 0x52, 0x70, 0xcb, 0x3f, 0x90, 0x43, 0x0e, 0xa4, 0xfa, 0x63,
0xbe, 0x24, 0x8d, 0x25, 0x43, 0x2a, 0x55, 0xa9, 0xdc, 0xba, 0x5f, 0xbf, 0xf7, 0xa6, 0xfb, 0x75,
0xf7, 0x7b, 0xef, 0xf7, 0xa6, 0xe1, 0x71, 0x87, 0xe8, 0x2a, 0xb1, 0x06, 0x9a, 0xee, 0xac, 0xe1,
0x4e, 0x57, 0x5b, 0x73, 0xce, 0x4c, 0x62, 0xaf, 0x9a, 0x96, 0xe1, 0x18, 0xa8, 0xe2, 0x0f, 0xae,
0xd2, 0xc1, 0xfa, 0x13, 0x01, 0xee, 0xae, 0x75, 0x66, 0x3a, 0xc6, 0x9a, 0x69, 0x19, 0xc6, 0x31,
0xe7, 0xaf, 0x5f, 0x0b, 0x0c, 0x33, 0x3d, 0x41, 0x6d, 0xa1, 0x51, 0x21, 0xfc, 0x88, 0x9c, 0xb9,
0xa3, 0x4f, 0x4c, 0xc8, 0x9a, 0xd8, 0xc2, 0x03, 0x77, 0x78, 0xf9, 0xc4, 0x30, 0x4e, 0xfa, 0x64,
0x8d, 0xf5, 0x3a, 0xc3, 0xe3, 0x35, 0x47, 0x1b, 0x10, 0xdb, 0xc1, 0x03, 0x53, 0x30, 0x2c, 0x9e,
0x18, 0x27, 0x06, 0x6b, 0xae, 0xd1, 0x16, 0xa7, 0x4a, 0xdf, 0xe4, 0x20, 0x2b, 0x93, 0xf7, 0x87,
0xc4, 0x76, 0xd0, 0x3a, 0xa4, 0x48, 0xb7, 0x67, 0xd4, 0xe2, 0x2b, 0xf1, 0x9b, 0x85, 0xf5, 0x6b,
0xab, 0x63, 0x8b, 0x5b, 0x15, 0x7c, 0xcd, 0x6e, 0xcf, 0x68, 0xc5, 0x64, 0xc6, 0x8b, 0xee, 0x40,
0xfa, 0xb8, 0x3f, 0xb4, 0x7b, 0xb5, 0x04, 0x13, 0x7a, 0x22, 0x4a, 0xe8, 0x1e, 0x65, 0x6a, 0xc5,
0x64, 0xce, 0x4d, 0x3f, 0xa5, 0xe9, 0xc7, 0x46, 0x2d, 0x79, 0xfe, 0xa7, 0xb6, 0xf5, 0x63, 0xf6,
0x29, 0xca, 0x8b, 0x36, 0x01, 0x34, 0x5d, 0x73, 0x94, 0x6e, 0x0f, 0x6b, 0x7a, 0x2d, 0xcd, 0x24,
0xaf, 0x47, 0x4b, 0x6a, 0x4e, 0x83, 0x32, 0xb6, 0x62, 0x72, 0x5e, 0x73, 0x3b, 0x74, 0xba, 0xef,
0x0f, 0x89, 0x75, 0x56, 0xcb, 0x9c, 0x3f, 0xdd, 0x1f, 0x51, 0x26, 0x3a, 0x5d, 0xc6, 0x8d, 0x9a,
0x50, 0xe8, 0x90, 0x13, 0x4d, 0x57, 0x3a, 0x7d, 0xa3, 0xfb, 0xa8, 0x96, 0x65, 0xc2, 0x52, 0x94,
0xf0, 0x26, 0x65, 0xdd, 0xa4, 0x9c, 0xad, 0x98, 0x0c, 0x1d, 0xaf, 0x87, 0x5e, 0x85, 0x5c, 0xb7,
0x47, 0xba, 0x8f, 0x14, 0x67, 0x54, 0xcb, 0x31, 0x1d, 0xcb, 0x51, 0x3a, 0x1a, 0x94, 0xaf, 0x3d,
0x6a, 0xc5, 0xe4, 0x6c, 0x97, 0x37, 0xe9, 0xfa, 0x55, 0xd2, 0xd7, 0x4e, 0x89, 0x45, 0xe5, 0xf3,
0xe7, 0xaf, 0x7f, 0x8b, 0x73, 0x32, 0x0d, 0x79, 0xd5, 0xed, 0xa0, 0xd7, 0x21, 0x4f, 0x74, 0x55,
0x2c, 0x03, 0x98, 0x8a, 0x95, 0xc8, 0x7d, 0xd6, 0x55, 0x77, 0x11, 0x39, 0x22, 0xda, 0xe8, 0x2e,
0x64, 0xba, 0xc6, 0x60, 0xa0, 0x39, 0xb5, 0x02, 0x93, 0x5e, 0x8a, 0x5c, 0x00, 0xe3, 0x6a, 0xc5,
0x64, 0xc1, 0x8f, 0xf6, 0xa0, 0xdc, 0xd7, 0x6c, 0x47, 0xb1, 0x75, 0x6c, 0xda, 0x3d, 0xc3, 0xb1,
0x6b, 0x45, 0xa6, 0xe1, 0xc9, 0x28, 0x0d, 0xbb, 0x9a, 0xed, 0x1c, 0xba, 0xcc, 0xad, 0x98, 0x5c,
0xea, 0x07, 0x09, 0x54, 0x9f, 0x71, 0x7c, 0x4c, 0x2c, 0x4f, 0x61, 0xad, 0x74, 0xbe, 0xbe, 0x7d,
0xca, 0xed, 0xca, 0x53, 0x7d, 0x46, 0x90, 0x80, 0x7e, 0x0a, 0x97, 0xfa, 0x06, 0x56, 0x3d, 0x75,
0x4a, 0xb7, 0x37, 0xd4, 0x1f, 0xd5, 0xca, 0x4c, 0xe9, 0xad, 0xc8, 0x49, 0x1a, 0x58, 0x75, 0x55,
0x34, 0xa8, 0x40, 0x2b, 0x26, 0x2f, 0xf4, 0xc7, 0x89, 0xe8, 0x21, 0x2c, 0x62, 0xd3, 0xec, 0x9f,
0x8d, 0x6b, 0xaf, 0x30, 0xed, 0xb7, 0xa3, 0xb4, 0x6f, 0x50, 0x99, 0x71, 0xf5, 0x08, 0x4f, 0x50,
0x51, 0x1b, 0xaa, 0xa6, 0x45, 0x4c, 0x6c, 0x11, 0xc5, 0xb4, 0x0c, 0xd3, 0xb0, 0x71, 0xbf, 0x56,
0x65, 0xba, 0x9f, 0x8e, 0xd2, 0x7d, 0xc0, 0xf9, 0x0f, 0x04, 0x7b, 0x2b, 0x26, 0x57, 0xcc, 0x30,
0x89, 0x6b, 0x35, 0xba, 0xc4, 0xb6, 0x7d, 0xad, 0x0b, 0xb3, 0xb4, 0x32, 0xfe, 0xb0, 0xd6, 0x10,
0x69, 0x33, 0x0b, 0xe9, 0x53, 0xdc, 0x1f, 0x92, 0xb7, 0x52, 0xb9, 0x54, 0x35, 0x2d, 0x3d, 0x0d,
0x85, 0x80, 0x63, 0x41, 0x35, 0xc8, 0x0e, 0x88, 0x6d, 0xe3, 0x13, 0xc2, 0xfc, 0x50, 0x5e, 0x76,
0xbb, 0x52, 0x19, 0x8a, 0x41, 0x67, 0x22, 0x7d, 0x12, 0xf7, 0x24, 0xa9, 0x9f, 0xa0, 0x92, 0xa7,
0xc4, 0xb2, 0x35, 0x43, 0x77, 0x25, 0x45, 0x17, 0xdd, 0x80, 0x12, 0x3b, 0xf1, 0x8a, 0x3b, 0x4e,
0x9d, 0x55, 0x4a, 0x2e, 0x32, 0xe2, 0x03, 0xc1, 0xb4, 0x0c, 0x05, 0x73, 0xdd, 0xf4, 0x58, 0x92,
0x8c, 0x05, 0xcc, 0x75, 0xd3, 0x65, 0xb8, 0x0e, 0x45, 0xba, 0x52, 0x8f, 0x23, 0xc5, 0x3e, 0x52,
0xa0, 0x34, 0xc1, 0x22, 0xfd, 0x29, 0x01, 0xd5, 0x71, 0x07, 0x84, 0xee, 0x42, 0x8a, 0xfa, 0x62,
0xe1, 0x56, 0xeb, 0xab, 0xdc, 0x51, 0xaf, 0xba, 0x8e, 0x7a, 0xb5, 0xed, 0x3a, 0xea, 0xcd, 0xdc,
0xe7, 0x5f, 0x2e, 0xc7, 0x3e, 0xf9, 0xdb, 0x72, 0x5c, 0x66, 0x12, 0xe8, 0x2a, 0xf5, 0x17, 0x58,
0xd3, 0x15, 0x4d, 0x65, 0x53, 0xce, 0x53, 0x67, 0x80, 0x35, 0x7d, 0x5b, 0x45, 0x3b, 0x50, 0xed,
0x1a, 0xba, 0x4d, 0x74, 0x7b, 0x68, 0x2b, 0x3c, 0x10, 0x08, 0x67, 0x3a, 0x79, 0x9f, 0x1b, 0x2e,
0xe3, 0x01, 0xe3, 0x93, 0x2b, 0xdd, 0x30, 0x01, 0xdd, 0x03, 0x38, 0xc5, 0x7d, 0x4d, 0xc5, 0x8e,
0x61, 0xd9, 0xb5, 0xd4, 0x4a, 0x72, 0xaa, 0x9a, 0x07, 0x2e, 0xcb, 0x91, 0xa9, 0x62, 0x87, 0x6c,
0xa6, 0xe8, 0x6c, 0xe5, 0x80, 0x24, 0x7a, 0x0a, 0x2a, 0xd8, 0x34, 0x15, 0xdb, 0xc1, 0x0e, 0x51,
0x3a, 0x67, 0x0e, 0xb1, 0x99, 0x9b, 0x2e, 0xca, 0x25, 0x6c, 0x9a, 0x87, 0x94, 0xba, 0x49, 0x89,
0xe8, 0x49, 0x28, 0x53, 0x97, 0xac, 0xe1, 0xbe, 0xd2, 0x23, 0xda, 0x49, 0xcf, 0x61, 0xee, 0x38,
0x29, 0x97, 0x04, 0xb5, 0xc5, 0x88, 0x92, 0xea, 0x6d, 0x38, 0x73, 0xc7, 0x08, 0x41, 0x4a, 0xc5,
0x0e, 0x66, 0x86, 0x2c, 0xca, 0xac, 0x4d, 0x69, 0x26, 0x76, 0x7a, 0xc2, 0x3c, 0xac, 0x8d, 0x2e,
0x43, 0x46, 0xa8, 0x4d, 0x32, 0xb5, 0xa2, 0x87, 0x16, 0x21, 0x6d, 0x5a, 0xc6, 0x29, 0x61, 0x3b,
0x97, 0x93, 0x79, 0x47, 0xfa, 0x28, 0x01, 0x0b, 0x13, 0x8e, 0x9b, 0xea, 0xed, 0x61, 0xbb, 0xe7,
0x7e, 0x8b, 0xb6, 0xd1, 0x8b, 0x54, 0x2f, 0x56, 0x89, 0x25, 0x82, 0x5d, 0x2d, 0x68, 0x22, 0x1e,
0xc8, 0x5b, 0x6c, 0x5c, 0x98, 0x46, 0x70, 0xd3, 0xbd, 0xea, 0x63, 0xdb, 0x51, 0xb8, 0x23, 0x54,
0x02, 0x81, 0xef, 0xf1, 0x29, 0x7b, 0x45, 0x79, 0xe8, 0x79, 0x16, 0x4a, 0xca, 0x54, 0xd4, 0xa7,
0xa2, 0x23, 0x58, 0xec, 0x9c, 0x7d, 0x88, 0x75, 0x47, 0xd3, 0x89, 0x32, 0xb1, 0x6b, 0x93, 0x91,
0xf4, 0xbe, 0x66, 0x77, 0x48, 0x0f, 0x9f, 0x6a, 0x86, 0x3b, 0xad, 0x4b, 0x9e, 0xbc, 0xb7, 0xa3,
0xb6, 0x24, 0x43, 0x39, 0x1c, 0x79, 0x50, 0x19, 0x12, 0xce, 0x48, 0xac, 0x3f, 0xe1, 0x8c, 0xd0,
0xf3, 0x90, 0xa2, 0x6b, 0x64, 0x6b, 0x2f, 0x4f, 0xf9, 0x90, 0x90, 0x6b, 0x9f, 0x99, 0x44, 0x66,
0x9c, 0x92, 0xe4, 0x5d, 0x06, 0x2f, 0x1a, 0x8d, 0x6b, 0x95, 0x6e, 0x41, 0x65, 0x2c, 0xdc, 0x04,
0xb6, 0x2f, 0x1e, 0xdc, 0x3e, 0xa9, 0x02, 0xa5, 0x50, 0x6c, 0x91, 0x2e, 0xc3, 0xe2, 0xb4, 0x50,
0x21, 0xf5, 0x3c, 0x7a, 0xc8, 0xe5, 0xa3, 0x3b, 0x90, 0xf3, 0x62, 0x05, 0xbf, 0x8c, 0x57, 0x27,
0x56, 0xe1, 0x32, 0xcb, 0x1e, 0x2b, 0xbd, 0x85, 0xf4, 0x54, 0xb3, 0xe3, 0x90, 0x60, 0x13, 0xcf,
0x62, 0xd3, 0x6c, 0x61, 0xbb, 0x27, 0xbd, 0x0b, 0xb5, 0xa8, 0x38, 0x30, 0xb6, 0x8c, 0x94, 0x77,
0x0a, 0x2f, 0x43, 0xe6, 0xd8, 0xb0, 0x06, 0xd8, 0x61, 0xca, 0x4a, 0xb2, 0xe8, 0xd1, 0xd3, 0xc9,
0x63, 0x42, 0x92, 0x91, 0x79, 0x47, 0x52, 0xe0, 0x6a, 0x64, 0x2c, 0xa0, 0x22, 0x9a, 0xae, 0x12,
0x6e, 0xcf, 0x92, 0xcc, 0x3b, 0xbe, 0x22, 0x3e, 0x59, 0xde, 0xa1, 0x9f, 0xb5, 0xd9, 0x5a, 0x99,
0xfe, 0xbc, 0x2c, 0x7a, 0xd2, 0xa7, 0x49, 0xb8, 0x3c, 0x3d, 0x22, 0xa0, 0x15, 0x28, 0x0e, 0xf0,
0x48, 0x71, 0x46, 0xe2, 0x2e, 0xf3, 0xed, 0x80, 0x01, 0x1e, 0xb5, 0x47, 0xfc, 0x22, 0x57, 0x21,
0xe9, 0x8c, 0xec, 0x5a, 0x62, 0x25, 0x79, 0xb3, 0x28, 0xd3, 0x26, 0x3a, 0x82, 0x85, 0xbe, 0xd1,
0xc5, 0x7d, 0x25, 0x70, 0xe2, 0xc5, 0x61, 0xbf, 0x31, 0x61, 0xec, 0xe6, 0x88, 0x51, 0xd4, 0x89,
0x43, 0x5f, 0x61, 0x3a, 0x76, 0xbd, 0x93, 0x8f, 0xb6, 0xa0, 0x30, 0xf0, 0x0f, 0xf2, 0x05, 0x0e,
0x7b, 0x50, 0x2c, 0xb0, 0x25, 0xe9, 0x90, 0x63, 0x70, 0x3d, 0x74, 0xe6, 0xc2, 0x1e, 0xfa, 0x79,
0x58, 0xd4, 0xc9, 0xc8, 0x09, 0x5c, 0x44, 0x7e, 0x4e, 0xb2, 0xcc, 0xf4, 0x88, 0x8e, 0xf9, 0x97,
0x8c, 0x1e, 0x19, 0x74, 0x8b, 0xc5, 0x54, 0xd3, 0xb0, 0x89, 0xa5, 0x60, 0x55, 0xb5, 0x88, 0x6d,
0xb3, 0x5c, 0xb0, 0xc8, 0x02, 0x25, 0xa3, 0x6f, 0x70, 0xb2, 0xf4, 0xab, 0xe0, 0xd6, 0x84, 0x62,
0xa8, 0x6b, 0xf8, 0xb8, 0x6f, 0xf8, 0x43, 0x58, 0x14, 0xf2, 0x6a, 0xc8, 0xf6, 0x89, 0x79, 0x1d,
0x0d, 0x72, 0xc5, 0xa3, 0xcd, 0x9e, 0xfc, 0x76, 0x66, 0x77, 0x7d, 0x69, 0x2a, 0xe0, 0x4b, 0xff,
0xc7, 0xb6, 0xe2, 0xcf, 0x79, 0xc8, 0xc9, 0xc4, 0x36, 0x69, 0xe0, 0x44, 0x9b, 0x90, 0x27, 0xa3,
0x2e, 0x31, 0x1d, 0x37, 0xd5, 0x98, 0x8e, 0x05, 0x38, 0x77, 0xd3, 0xe5, 0xa4, 0x89, 0xb8, 0x27,
0x86, 0x5e, 0x10, 0x58, 0x2b, 0x1a, 0x36, 0x09, 0xf1, 0x20, 0xd8, 0x7a, 0xd1, 0x05, 0x5b, 0xc9,
0xc8, 0xdc, 0x9b, 0x4b, 0x8d, 0xa1, 0xad, 0x17, 0x04, 0xda, 0x4a, 0xcd, 0xf8, 0x58, 0x08, 0x6e,
0x35, 0x42, 0x70, 0x2b, 0x33, 0x63, 0x99, 0x11, 0x78, 0xeb, 0x45, 0x17, 0x6f, 0x65, 0x67, 0xcc,
0x78, 0x0c, 0x70, 0xdd, 0x0b, 0x03, 0xae, 0x5c, 0x84, 0x03, 0x71, 0xa5, 0x23, 0x11, 0xd7, 0x6b,
0x01, 0xc4, 0x95, 0x8f, 0x84, 0x3b, 0x5c, 0xc9, 0x14, 0xc8, 0xd5, 0x08, 0x41, 0x2e, 0x98, 0x61,
0x83, 0x08, 0xcc, 0xf5, 0x46, 0x10, 0x73, 0x15, 0x22, 0x61, 0x9b, 0xd8, 0xef, 0x69, 0xa0, 0xeb,
0x65, 0x0f, 0x74, 0x15, 0x23, 0x51, 0xa3, 0x58, 0xc3, 0x38, 0xea, 0xda, 0x9f, 0x40, 0x5d, 0x1c,
0x25, 0x3d, 0x15, 0xa9, 0x62, 0x06, 0xec, 0xda, 0x9f, 0x80, 0x5d, 0xe5, 0x19, 0x0a, 0x67, 0xe0,
0xae, 0x9f, 0x4d, 0xc7, 0x5d, 0xd1, 0xc8, 0x48, 0x4c, 0x73, 0x3e, 0xe0, 0xa5, 0x44, 0x00, 0x2f,
0x0e, 0x8e, 0x9e, 0x89, 0x54, 0x3f, 0x37, 0xf2, 0x3a, 0x9a, 0x82, 0xbc, 0x38, 0x46, 0xba, 0x19,
0xa9, 0x7c, 0x0e, 0xe8, 0x75, 0x34, 0x05, 0x7a, 0xa1, 0x99, 0x6a, 0x2f, 0x82, 0xbd, 0xd2, 0xd5,
0x8c, 0x74, 0x8b, 0xa6, 0xbe, 0x63, 0x7e, 0x8a, 0xe6, 0x0f, 0xc4, 0xb2, 0x0c, 0x4b, 0xa0, 0x28,
0xde, 0x91, 0x6e, 0xd2, 0x64, 0xdc, 0xf7, 0x49, 0xe7, 0xe0, 0x34, 0x96, 0xa7, 0x05, 0xfc, 0x90,
0xf4, 0xfb, 0xb8, 0x2f, 0xcb, 0x72, 0xd8, 0x60, 0x22, 0x9f, 0x17, 0x89, 0x7c, 0x00, 0xbd, 0x25,
0xc2, 0xe8, 0x6d, 0x19, 0x0a, 0x34, 0xff, 0x1a, 0x03, 0x66, 0xd8, 0xf4, 0x80, 0xd9, 0x6d, 0x58,
0x60, 0x11, 0x8f, 0x63, 0x3c, 0x11, 0x56, 0x52, 0x2c, 0xac, 0x54, 0xe8, 0x00, 0xbf, 0x50, 0x3c,
0xbe, 0x3c, 0x07, 0x97, 0x02, 0xbc, 0x5e, 0x5e, 0xc7, 0x61, 0x4a, 0xd5, 0xe3, 0xde, 0x10, 0x09,
0xde, 0x1f, 0xe3, 0xbe, 0x85, 0x7c, 0x44, 0x37, 0x0d, 0x7c, 0xc5, 0xff, 0x33, 0xe0, 0x2b, 0xf1,
0xad, 0xc1, 0x57, 0x30, 0x4d, 0x4d, 0x86, 0xd3, 0xd4, 0x7f, 0xc6, 0xfd, 0x2d, 0xf1, 0xa0, 0x54,
0xd7, 0x50, 0x89, 0x48, 0x1c, 0x59, 0x9b, 0xe6, 0x14, 0x7d, 0xe3, 0x44, 0xa4, 0x87, 0xb4, 0x49,
0xb9, 0xbc, 0xb8, 0x91, 0x17, 0x61, 0xc1, 0xcb, 0x39, 0x79, 0xdc, 0x16, 0x39, 0x67, 0x15, 0x92,
0x8f, 0x08, 0xaf, 0xaa, 0x15, 0x65, 0xda, 0xa4, 0x7c, 0xec, 0xa4, 0x89, 0xf8, 0xcb, 0x3b, 0xe8,
0x2e, 0xe4, 0x59, 0x3d, 0x54, 0x31, 0x4c, 0x5b, 0x78, 0xf5, 0x50, 0x6a, 0xc2, 0xcb, 0x9e, 0xab,
0x07, 0x94, 0x67, 0xdf, 0xb4, 0xe5, 0x9c, 0x29, 0x5a, 0x81, 0x84, 0x21, 0x1f, 0x4a, 0x18, 0xae,
0x41, 0x9e, 0xce, 0xde, 0x36, 0x71, 0x97, 0x30, 0x0f, 0x9d, 0x97, 0x7d, 0x82, 0xf4, 0x10, 0xd0,
0x64, 0x8c, 0x40, 0x2d, 0xc8, 0x90, 0x53, 0xa2, 0x3b, 0x3c, 0x81, 0x2a, 0xac, 0x5f, 0x9e, 0xcc,
0x4c, 0xe9, 0xf0, 0x66, 0x8d, 0x1a, 0xf9, 0x1f, 0x5f, 0x2e, 0x57, 0x39, 0xf7, 0xb3, 0xc6, 0x40,
0x73, 0xc8, 0xc0, 0x74, 0xce, 0x64, 0x21, 0x2f, 0xfd, 0x35, 0x41, 0xf1, 0x4b, 0x28, 0x7e, 0x4c,
0xb5, 0xad, 0x7b, 0xe2, 0x13, 0x01, 0xe8, 0x3a, 0x9f, 0xbd, 0x97, 0x00, 0x4e, 0xb0, 0xad, 0x7c,
0x80, 0x75, 0x87, 0xa8, 0xc2, 0xe8, 0x01, 0x0a, 0xaa, 0x43, 0x8e, 0xf6, 0x86, 0x36, 0x51, 0x05,
0x8a, 0xf6, 0xfa, 0x81, 0x75, 0x66, 0xbf, 0xdb, 0x3a, 0xc3, 0x56, 0xce, 0x8d, 0x59, 0x39, 0x80,
0x2d, 0xf2, 0x41, 0x6c, 0x41, 0xe7, 0x66, 0x5a, 0x9a, 0x61, 0x69, 0xce, 0x19, 0xdb, 0x9a, 0xa4,
0xec, 0xf5, 0xd1, 0x0d, 0x28, 0x0d, 0xc8, 0xc0, 0x34, 0x8c, 0xbe, 0xc2, 0xbd, 0x4d, 0x81, 0x89,
0x16, 0x05, 0xb1, 0xc9, 0x9c, 0xce, 0x2f, 0x13, 0xfe, 0xf5, 0xf3, 0x31, 0xe4, 0xff, 0x9d, 0x81,
0xa5, 0x5f, 0xb3, 0xba, 0x52, 0x38, 0x43, 0x40, 0x87, 0xb0, 0xe0, 0x5d, 0x7f, 0x65, 0xc8, 0xdc,
0x82, 0x7b, 0xa0, 0xe7, 0xf5, 0x1f, 0xd5, 0xd3, 0x30, 0xd9, 0x46, 0x3f, 0x86, 0x2b, 0x63, 0xae,
0xcd, 0x53, 0x9d, 0x98, 0xd3, 0xc3, 0x3d, 0x16, 0xf6, 0x70, 0xae, 0x66, 0xdf, 0x56, 0xc9, 0xef,
0x78, 0xe9, 0xb6, 0xa1, 0x1c, 0xce, 0x77, 0xa6, 0xee, 0xfe, 0x0d, 0x28, 0x59, 0xc4, 0xc1, 0x9a,
0xae, 0x84, 0x8a, 0x41, 0x45, 0x4e, 0x14, 0x25, 0xa6, 0x03, 0x78, 0x6c, 0x6a, 0xde, 0x83, 0x5e,
0x82, 0xbc, 0x9f, 0x32, 0x71, 0xa3, 0x9e, 0x53, 0x2c, 0xf0, 0x79, 0xa5, 0x3f, 0xc4, 0x7d, 0x95,
0xe1, 0xf2, 0x43, 0x13, 0x32, 0x16, 0xb1, 0x87, 0x7d, 0x5e, 0x10, 0x28, 0xaf, 0x3f, 0x37, 0x5f,
0xc6, 0x44, 0xa9, 0xc3, 0xbe, 0x23, 0x0b, 0x61, 0xe9, 0x21, 0x64, 0x38, 0x05, 0x15, 0x20, 0x7b,
0xb4, 0xb7, 0xb3, 0xb7, 0xff, 0xf6, 0x5e, 0x35, 0x86, 0x00, 0x32, 0x1b, 0x8d, 0x46, 0xf3, 0xa0,
0x5d, 0x8d, 0xa3, 0x3c, 0xa4, 0x37, 0x36, 0xf7, 0xe5, 0x76, 0x35, 0x41, 0xc9, 0x72, 0xf3, 0xad,
0x66, 0xa3, 0x5d, 0x4d, 0xa2, 0x05, 0x28, 0xf1, 0xb6, 0x72, 0x6f, 0x5f, 0xbe, 0xbf, 0xd1, 0xae,
0xa6, 0x02, 0xa4, 0xc3, 0xe6, 0xde, 0x56, 0x53, 0xae, 0xa6, 0xa5, 0xef, 0xc1, 0xd5, 0xc8, 0x1c,
0xcb, 0xaf, 0x2d, 0xc4, 0x03, 0xb5, 0x05, 0xe9, 0xd3, 0x04, 0xd4, 0xa3, 0x13, 0x27, 0xf4, 0xd6,
0xd8, 0xc2, 0xd7, 0x2f, 0x90, 0x75, 0x8d, 0xad, 0x1e, 0x3d, 0x09, 0x65, 0x8b, 0x1c, 0x13, 0xa7,
0xdb, 0xe3, 0x89, 0x1c, 0x8f, 0x98, 0x25, 0xb9, 0x24, 0xa8, 0x4c, 0xc8, 0xe6, 0x6c, 0xef, 0x91,
0xae, 0xa3, 0x70, 0x57, 0xc4, 0x0f, 0x5d, 0x9e, 0xb2, 0x51, 0xea, 0x21, 0x27, 0x4a, 0xef, 0x5e,
0xc8, 0x96, 0x79, 0x48, 0xcb, 0xcd, 0xb6, 0xfc, 0x4e, 0x35, 0x89, 0x10, 0x94, 0x59, 0x53, 0x39,
0xdc, 0xdb, 0x38, 0x38, 0x6c, 0xed, 0x53, 0x5b, 0x5e, 0x82, 0x8a, 0x6b, 0x4b, 0x97, 0x98, 0x96,
0x0e, 0xe1, 0x4a, 0x44, 0xd6, 0x87, 0xee, 0x02, 0x38, 0x23, 0xc5, 0x22, 0x5d, 0xc3, 0x52, 0xa3,
0xcf, 0x58, 0x7b, 0x24, 0x33, 0x0e, 0x39, 0xef, 0x88, 0x96, 0x2d, 0xfd, 0x36, 0x1e, 0xd4, 0x1a,
0xae, 0x0c, 0xec, 0x43, 0xc6, 0x76, 0xb0, 0x33, 0xb4, 0x85, 0xb1, 0x5f, 0x9a, 0x37, 0x5d, 0x5c,
0x75, 0x1b, 0x87, 0x4c, 0x5c, 0x16, 0x6a, 0xa4, 0x3b, 0x50, 0x0e, 0x8f, 0x44, 0xdb, 0xca, 0x3f,
0x6c, 0x09, 0xe9, 0x9b, 0x38, 0x54, 0xc6, 0x3c, 0x03, 0x5a, 0x87, 0x34, 0x47, 0x41, 0x51, 0x7f,
0x18, 0x99, 0x63, 0x13, 0x6e, 0x84, 0xb3, 0xa2, 0x57, 0x21, 0x47, 0x4e, 0x35, 0x95, 0xe8, 0x5d,
0x32, 0xcd, 0x03, 0xf1, 0xb2, 0x6b, 0x53, 0x70, 0x08, 0x51, 0x4f, 0x02, 0xbd, 0x0e, 0x79, 0xcf,
0xc5, 0x09, 0xd4, 0x7c, 0x7d, 0x52, 0xdc, 0x73, 0x8e, 0x42, 0xde, 0x97, 0x41, 0x2f, 0xfb, 0x69,
0x69, 0x6a, 0x12, 0x7b, 0x09, 0x71, 0xce, 0x20, 0x84, 0x5d, 0x7e, 0xa9, 0x01, 0x85, 0xc0, 0x7a,
0xd0, 0xe3, 0x90, 0x1f, 0xe0, 0x70, 0x29, 0x2d, 0x37, 0xc0, 0xa2, 0x90, 0x76, 0x05, 0xb2, 0x74,
0xf0, 0x04, 0x73, 0x37, 0x9b, 0x94, 0x33, 0x03, 0x3c, 0x7a, 0x13, 0xdb, 0xd2, 0x3b, 0x00, 0x81,
0xe2, 0xef, 0x22, 0xa4, 0x2d, 0x63, 0xa8, 0xab, 0x4c, 0x3e, 0x2d, 0xf3, 0x0e, 0xba, 0x03, 0xe9,
0x53, 0x83, 0x7b, 0xe8, 0xe9, 0x67, 0xe8, 0x81, 0xe1, 0x90, 0x40, 0xa5, 0x87, 0x73, 0x4b, 0x1a,
0xa0, 0xc9, 0x02, 0x5c, 0xc4, 0x27, 0x5e, 0x0b, 0x7f, 0xe2, 0x7a, 0x64, 0x29, 0x6f, 0xfa, 0xa7,
0x3e, 0x84, 0x34, 0x73, 0xee, 0xd4, 0x51, 0xb3, 0x22, 0xb2, 0xc8, 0xfc, 0x69, 0x1b, 0xfd, 0x1c,
0x00, 0x3b, 0x8e, 0xa5, 0x75, 0x86, 0xfe, 0x07, 0x96, 0xa7, 0x07, 0x87, 0x0d, 0x97, 0x6f, 0xf3,
0x9a, 0x88, 0x12, 0x8b, 0xbe, 0x68, 0x20, 0x52, 0x04, 0x14, 0x4a, 0x7b, 0x50, 0x0e, 0xcb, 0xba,
0xc9, 0x6a, 0x7c, 0x4a, 0xb2, 0x9a, 0x08, 0x26, 0xab, 0x5e, 0xaa, 0x9b, 0xe4, 0xff, 0x0b, 0x58,
0x47, 0xfa, 0x38, 0x0e, 0x39, 0x7a, 0x29, 0x99, 0xdb, 0x88, 0xa8, 0x55, 0xfb, 0xa2, 0x89, 0x60,
0x65, 0x96, 0x17, 0xbf, 0x93, 0x5e, 0x49, 0xfd, 0x0d, 0xcf, 0x31, 0xa6, 0xe6, 0x2d, 0x2d, 0xb8,
0xbf, 0x16, 0x44, 0x30, 0xf8, 0x8d, 0x98, 0x0c, 0xf5, 0x0b, 0xe8, 0x07, 0x90, 0xc1, 0x5d, 0xaf,
0x28, 0x55, 0x9e, 0xa2, 0xce, 0x65, 0x5d, 0x6d, 0x8f, 0x36, 0x18, 0xa7, 0x2c, 0x24, 0xc4, 0xd4,
0x12, 0x5e, 0x5d, 0xfe, 0x75, 0xaa, 0x97, 0xf3, 0x84, 0xef, 0x7b, 0x19, 0xe0, 0x68, 0xef, 0xfe,
0xfe, 0xd6, 0xf6, 0xbd, 0xed, 0xe6, 0x96, 0xf0, 0x8f, 0x5b, 0x5b, 0xcd, 0xad, 0x6a, 0x82, 0xf2,
0xc9, 0xcd, 0xfb, 0xfb, 0x0f, 0x9a, 0x5b, 0xd5, 0xa4, 0xf4, 0x0a, 0xe4, 0xbd, 0x6b, 0x45, 0xc1,
0x9d, 0x5b, 0x60, 0x8b, 0x0b, 0x68, 0xc2, 0xbb, 0xec, 0x9f, 0x8c, 0xf1, 0x81, 0xa8, 0x4a, 0x27,
0x65, 0xde, 0x91, 0x54, 0xa8, 0x8c, 0x25, 0x2c, 0xe8, 0x15, 0xc8, 0x9a, 0xc3, 0x8e, 0xe2, 0x6e,
0xdc, 0x98, 0xf7, 0x70, 0x71, 0xc3, 0xb0, 0xd3, 0xd7, 0xba, 0x3b, 0xe4, 0xcc, 0x35, 0x93, 0x39,
0xec, 0xec, 0xf0, 0xfd, 0xe5, 0x5f, 0x49, 0x04, 0xbf, 0x72, 0x0a, 0x39, 0xf7, 0xb8, 0xa2, 0x1f,
0x06, 0x1d, 0x85, 0xfb, 0xa7, 0x2e, 0x32, 0x89, 0x12, 0xea, 0x03, 0x7e, 0xe2, 0x36, 0x2c, 0xd8,
0xda, 0x89, 0xee, 0x16, 0x5f, 0xb9, 0x9b, 0x4b, 0xb0, 0x73, 0x53, 0xe1, 0x03, 0xbb, 0x2e, 0xb6,
0x94, 0x7e, 0x17, 0x87, 0xea, 0xf8, 0x7d, 0xf9, 0x6f, 0x4e, 0x80, 0x46, 0x47, 0x7a, 0x2f, 0x15,
0x42, 0x27, 0xe1, 0x81, 0xea, 0xa2, 0x5c, 0xa2, 0xd4, 0xa6, 0x4b, 0x94, 0x3e, 0x4a, 0x40, 0x21,
0x50, 0xda, 0x45, 0xdf, 0x0f, 0x5c, 0xde, 0xf2, 0x94, 0x44, 0x30, 0xc0, 0xeb, 0xff, 0x05, 0x0a,
0x2f, 0x2c, 0x71, 0xf1, 0x85, 0x45, 0xfd, 0xcd, 0x73, 0x2b, 0xc5, 0xa9, 0x0b, 0x57, 0x8a, 0x9f,
0x05, 0xe4, 0x18, 0x0e, 0xee, 0x2b, 0xa7, 0x86, 0xa3, 0xe9, 0x27, 0x0a, 0x3f, 0x1a, 0x3c, 0xf3,
0xaf, 0xb2, 0x91, 0x07, 0x6c, 0xe0, 0x80, 0x9d, 0x92, 0x5f, 0xc4, 0x21, 0xe7, 0xe5, 0x70, 0x17,
0xfd, 0xa9, 0x73, 0x19, 0x32, 0x22, 0x4d, 0xe1, 0x7f, 0x75, 0x44, 0x6f, 0x6a, 0x49, 0xbc, 0x0e,
0xb9, 0x01, 0x71, 0x30, 0x4b, 0x64, 0x79, 0x3d, 0xc2, 0xeb, 0xdf, 0x7e, 0x19, 0x0a, 0x81, 0xff,
0x6b, 0xd4, 0x83, 0xed, 0x35, 0xdf, 0xae, 0xc6, 0xea, 0xd9, 0x8f, 0x3f, 0x5b, 0x49, 0xee, 0x91,
0x0f, 0xe8, 0x0d, 0x93, 0x9b, 0x8d, 0x56, 0xb3, 0xb1, 0x53, 0x8d, 0xd7, 0x0b, 0x1f, 0x7f, 0xb6,
0x92, 0x95, 0x09, 0xab, 0x62, 0xde, 0xde, 0x81, 0xca, 0xd8, 0xc6, 0x84, 0x2f, 0x34, 0x82, 0xf2,
0xd6, 0xd1, 0xc1, 0xee, 0x76, 0x63, 0xa3, 0xdd, 0x54, 0x1e, 0xec, 0xb7, 0x9b, 0xd5, 0x38, 0xba,
0x02, 0x97, 0x76, 0xb7, 0xdf, 0x6c, 0xb5, 0x95, 0xc6, 0xee, 0x76, 0x73, 0xaf, 0xad, 0x6c, 0xb4,
0xdb, 0x1b, 0x8d, 0x9d, 0x6a, 0x62, 0xfd, 0x5f, 0x00, 0x95, 0x8d, 0xcd, 0xc6, 0x36, 0x4d, 0xd4,
0xb4, 0x2e, 0x66, 0xee, 0xa1, 0x01, 0x29, 0x56, 0x11, 0x3a, 0xf7, 0xc1, 0x50, 0xfd, 0xfc, 0x12,
0x37, 0xba, 0x07, 0x69, 0x56, 0x2c, 0x42, 0xe7, 0xbf, 0x20, 0xaa, 0xcf, 0xa8, 0x79, 0xd3, 0xc9,
0xb0, 0xeb, 0x74, 0xee, 0x93, 0xa2, 0xfa, 0xf9, 0x25, 0x70, 0x24, 0x43, 0xde, 0x47, 0x9b, 0xb3,
0x9f, 0xd8, 0xd4, 0xe7, 0xf0, 0xdb, 0x68, 0x17, 0xb2, 0x6e, 0x81, 0x60, 0xd6, 0xa3, 0x9f, 0xfa,
0xcc, 0x1a, 0x35, 0x35, 0x17, 0x2f, 0xe4, 0x9c, 0xff, 0x82, 0xa9, 0x3e, 0xa3, 0xe0, 0x8e, 0xb6,
0x21, 0x23, 0x20, 0xd4, 0x8c, 0x87, 0x3c, 0xf5, 0x59, 0x35, 0x67, 0x6a, 0x34, 0xbf, 0x42, 0x36,
0xfb, 0x5d, 0x56, 0x7d, 0x8e, 0x7f, 0x09, 0xe8, 0x08, 0x20, 0x50, 0xb6, 0x99, 0xe3, 0xc1, 0x55,
0x7d, 0x9e, 0x7f, 0x04, 0x68, 0x1f, 0x72, 0x1e, 0x8a, 0x9e, 0xf9, 0xfc, 0xa9, 0x3e, 0xbb, 0x58,
0x8f, 0x1e, 0x42, 0x29, 0x0c, 0x1f, 0xe7, 0x7b, 0xd4, 0x54, 0x9f, 0xb3, 0x0a, 0x4f, 0xf5, 0x87,
0xb1, 0xe4, 0x7c, 0x8f, 0x9c, 0xea, 0x73, 0x16, 0xe5, 0xd1, 0x7b, 0xb0, 0x30, 0x89, 0xf5, 0xe6,
0x7f, 0xf3, 0x54, 0xbf, 0x40, 0x99, 0x1e, 0x0d, 0x00, 0x4d, 0xc1, 0x88, 0x17, 0x78, 0x02, 0x55,
0xbf, 0x48, 0xd5, 0x1e, 0xa9, 0x50, 0x19, 0x07, 0x5e, 0xf3, 0x3e, 0x89, 0xaa, 0xcf, 0x5d, 0xc1,
0xe7, 0x5f, 0x09, 0x03, 0xb1, 0x79, 0x9f, 0x48, 0xd5, 0xe7, 0x2e, 0xe8, 0x6f, 0x36, 0x3f, 0xff,
0x6a, 0x29, 0xfe, 0xc5, 0x57, 0x4b, 0xf1, 0xbf, 0x7f, 0xb5, 0x14, 0xff, 0xe4, 0xeb, 0xa5, 0xd8,
0x17, 0x5f, 0x2f, 0xc5, 0xfe, 0xf2, 0xf5, 0x52, 0xec, 0x27, 0xcf, 0x9c, 0x68, 0x4e, 0x6f, 0xd8,
0x59, 0xed, 0x1a, 0x83, 0xb5, 0xe0, 0x3b, 0xd1, 0x69, 0x6f, 0x57, 0x3b, 0x19, 0x16, 0x23, 0x5f,
0xf8, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x33, 0x3e, 0x69, 0x24, 0xdb, 0x2a, 0x00, 0x00,
0x05, 0x6c, 0x62, 0xb2, 0x7c, 0x14, 0x10, 0xb0, 0xb5, 0x5a, 0x64, 0x6c, 0x6c, 0x67, 0x2c, 0x2f,
0x21, 0x1f, 0x3b, 0xb4, 0x34, 0x6d, 0x69, 0x58, 0x69, 0x66, 0x98, 0x19, 0x19, 0x99, 0x63, 0xa8,
0x54, 0xa5, 0xc8, 0x85, 0x23, 0x17, 0x0e, 0x39, 0xe4, 0x7f, 0xc8, 0x29, 0x97, 0x5c, 0xa8, 0xca,
0x85, 0x43, 0x0e, 0x39, 0xa4, 0x48, 0x8a, 0xbd, 0xe5, 0x1f, 0xc8, 0x21, 0x07, 0x52, 0xfd, 0x31,
0x5f, 0x92, 0xc6, 0x92, 0x21, 0x95, 0xaa, 0x54, 0x6e, 0xdd, 0xaf, 0xdf, 0x7b, 0xd3, 0xfd, 0xba,
0xfb, 0xbd, 0xf7, 0x7b, 0xd3, 0xf0, 0xb8, 0x43, 0x74, 0x95, 0x58, 0x43, 0x4d, 0x77, 0x36, 0x70,
0xa7, 0xab, 0x6d, 0x38, 0x67, 0x26, 0xb1, 0xd7, 0x4d, 0xcb, 0x70, 0x0c, 0x54, 0xf1, 0x07, 0xd7,
0xe9, 0x60, 0xfd, 0x89, 0x00, 0x77, 0xd7, 0x3a, 0x33, 0x1d, 0x63, 0xc3, 0xb4, 0x0c, 0xe3, 0x84,
0xf3, 0xd7, 0xaf, 0x05, 0x86, 0x99, 0x9e, 0xa0, 0xb6, 0xd0, 0xa8, 0x10, 0x7e, 0x48, 0xce, 0xdc,
0xd1, 0x27, 0xa6, 0x64, 0x4d, 0x6c, 0xe1, 0xa1, 0x3b, 0xbc, 0xda, 0x33, 0x8c, 0xde, 0x80, 0x6c,
0xb0, 0x5e, 0x67, 0x74, 0xb2, 0xe1, 0x68, 0x43, 0x62, 0x3b, 0x78, 0x68, 0x0a, 0x86, 0xe5, 0x9e,
0xd1, 0x33, 0x58, 0x73, 0x83, 0xb6, 0x38, 0x55, 0xfa, 0x36, 0x07, 0x59, 0x99, 0x7c, 0x38, 0x22,
0xb6, 0x83, 0x36, 0x21, 0x45, 0xba, 0x7d, 0xa3, 0x16, 0x5f, 0x8b, 0xdf, 0x2c, 0x6c, 0x5e, 0x5b,
0x9f, 0x58, 0xdc, 0xba, 0xe0, 0x6b, 0x76, 0xfb, 0x46, 0x2b, 0x26, 0x33, 0x5e, 0x74, 0x07, 0xd2,
0x27, 0x83, 0x91, 0xdd, 0xaf, 0x25, 0x98, 0xd0, 0x13, 0x51, 0x42, 0xf7, 0x28, 0x53, 0x2b, 0x26,
0x73, 0x6e, 0xfa, 0x29, 0x4d, 0x3f, 0x31, 0x6a, 0xc9, 0xf3, 0x3f, 0xb5, 0xa3, 0x9f, 0xb0, 0x4f,
0x51, 0x5e, 0xb4, 0x0d, 0xa0, 0xe9, 0x9a, 0xa3, 0x74, 0xfb, 0x58, 0xd3, 0x6b, 0x69, 0x26, 0x79,
0x3d, 0x5a, 0x52, 0x73, 0x1a, 0x94, 0xb1, 0x15, 0x93, 0xf3, 0x9a, 0xdb, 0xa1, 0xd3, 0xfd, 0x70,
0x44, 0xac, 0xb3, 0x5a, 0xe6, 0xfc, 0xe9, 0xfe, 0x98, 0x32, 0xd1, 0xe9, 0x32, 0x6e, 0xd4, 0x84,
0x42, 0x87, 0xf4, 0x34, 0x5d, 0xe9, 0x0c, 0x8c, 0xee, 0xc3, 0x5a, 0x96, 0x09, 0x4b, 0x51, 0xc2,
0xdb, 0x94, 0x75, 0x9b, 0x72, 0xb6, 0x62, 0x32, 0x74, 0xbc, 0x1e, 0x7a, 0x0d, 0x72, 0xdd, 0x3e,
0xe9, 0x3e, 0x54, 0x9c, 0x71, 0x2d, 0xc7, 0x74, 0xac, 0x46, 0xe9, 0x68, 0x50, 0xbe, 0xf6, 0xb8,
0x15, 0x93, 0xb3, 0x5d, 0xde, 0xa4, 0xeb, 0x57, 0xc9, 0x40, 0x3b, 0x25, 0x16, 0x95, 0xcf, 0x9f,
0xbf, 0xfe, 0xbb, 0x9c, 0x93, 0x69, 0xc8, 0xab, 0x6e, 0x07, 0xbd, 0x01, 0x79, 0xa2, 0xab, 0x62,
0x19, 0xc0, 0x54, 0xac, 0x45, 0xee, 0xb3, 0xae, 0xba, 0x8b, 0xc8, 0x11, 0xd1, 0x46, 0x2f, 0x43,
0xa6, 0x6b, 0x0c, 0x87, 0x9a, 0x53, 0x2b, 0x30, 0xe9, 0x95, 0xc8, 0x05, 0x30, 0xae, 0x56, 0x4c,
0x16, 0xfc, 0x68, 0x1f, 0xca, 0x03, 0xcd, 0x76, 0x14, 0x5b, 0xc7, 0xa6, 0xdd, 0x37, 0x1c, 0xbb,
0x56, 0x64, 0x1a, 0x9e, 0x8c, 0xd2, 0xb0, 0xa7, 0xd9, 0xce, 0x91, 0xcb, 0xdc, 0x8a, 0xc9, 0xa5,
0x41, 0x90, 0x40, 0xf5, 0x19, 0x27, 0x27, 0xc4, 0xf2, 0x14, 0xd6, 0x4a, 0xe7, 0xeb, 0x3b, 0xa0,
0xdc, 0xae, 0x3c, 0xd5, 0x67, 0x04, 0x09, 0xe8, 0x67, 0x70, 0x69, 0x60, 0x60, 0xd5, 0x53, 0xa7,
0x74, 0xfb, 0x23, 0xfd, 0x61, 0xad, 0xcc, 0x94, 0xde, 0x8a, 0x9c, 0xa4, 0x81, 0x55, 0x57, 0x45,
0x83, 0x0a, 0xb4, 0x62, 0xf2, 0xd2, 0x60, 0x92, 0x88, 0x1e, 0xc0, 0x32, 0x36, 0xcd, 0xc1, 0xd9,
0xa4, 0xf6, 0x0a, 0xd3, 0x7e, 0x3b, 0x4a, 0xfb, 0x16, 0x95, 0x99, 0x54, 0x8f, 0xf0, 0x14, 0x15,
0xb5, 0xa1, 0x6a, 0x5a, 0xc4, 0xc4, 0x16, 0x51, 0x4c, 0xcb, 0x30, 0x0d, 0x1b, 0x0f, 0x6a, 0x55,
0xa6, 0xfb, 0xe9, 0x28, 0xdd, 0x87, 0x9c, 0xff, 0x50, 0xb0, 0xb7, 0x62, 0x72, 0xc5, 0x0c, 0x93,
0xb8, 0x56, 0xa3, 0x4b, 0x6c, 0xdb, 0xd7, 0xba, 0x34, 0x4f, 0x2b, 0xe3, 0x0f, 0x6b, 0x0d, 0x91,
0xb6, 0xb3, 0x90, 0x3e, 0xc5, 0x83, 0x11, 0x79, 0x3b, 0x95, 0x4b, 0x55, 0xd3, 0xd2, 0xd3, 0x50,
0x08, 0x38, 0x16, 0x54, 0x83, 0xec, 0x90, 0xd8, 0x36, 0xee, 0x11, 0xe6, 0x87, 0xf2, 0xb2, 0xdb,
0x95, 0xca, 0x50, 0x0c, 0x3a, 0x13, 0xe9, 0xb3, 0xb8, 0x27, 0x49, 0xfd, 0x04, 0x95, 0x3c, 0x25,
0x96, 0xad, 0x19, 0xba, 0x2b, 0x29, 0xba, 0xe8, 0x06, 0x94, 0xd8, 0x89, 0x57, 0xdc, 0x71, 0xea,
0xac, 0x52, 0x72, 0x91, 0x11, 0xef, 0x0b, 0xa6, 0x55, 0x28, 0x98, 0x9b, 0xa6, 0xc7, 0x92, 0x64,
0x2c, 0x60, 0x6e, 0x9a, 0x2e, 0xc3, 0x75, 0x28, 0xd2, 0x95, 0x7a, 0x1c, 0x29, 0xf6, 0x91, 0x02,
0xa5, 0x09, 0x16, 0xe9, 0x4f, 0x09, 0xa8, 0x4e, 0x3a, 0x20, 0xf4, 0x32, 0xa4, 0xa8, 0x2f, 0x16,
0x6e, 0xb5, 0xbe, 0xce, 0x1d, 0xf5, 0xba, 0xeb, 0xa8, 0xd7, 0xdb, 0xae, 0xa3, 0xde, 0xce, 0x7d,
0xf9, 0xf5, 0x6a, 0xec, 0xb3, 0xbf, 0xad, 0xc6, 0x65, 0x26, 0x81, 0xae, 0x52, 0x7f, 0x81, 0x35,
0x5d, 0xd1, 0x54, 0x36, 0xe5, 0x3c, 0x75, 0x06, 0x58, 0xd3, 0x77, 0x54, 0xb4, 0x0b, 0xd5, 0xae,
0xa1, 0xdb, 0x44, 0xb7, 0x47, 0xb6, 0xc2, 0x03, 0x81, 0x70, 0xa6, 0xd3, 0xf7, 0xb9, 0xe1, 0x32,
0x1e, 0x32, 0x3e, 0xb9, 0xd2, 0x0d, 0x13, 0xd0, 0x3d, 0x80, 0x53, 0x3c, 0xd0, 0x54, 0xec, 0x18,
0x96, 0x5d, 0x4b, 0xad, 0x25, 0x67, 0xaa, 0xb9, 0xef, 0xb2, 0x1c, 0x9b, 0x2a, 0x76, 0xc8, 0x76,
0x8a, 0xce, 0x56, 0x0e, 0x48, 0xa2, 0xa7, 0xa0, 0x82, 0x4d, 0x53, 0xb1, 0x1d, 0xec, 0x10, 0xa5,
0x73, 0xe6, 0x10, 0x9b, 0xb9, 0xe9, 0xa2, 0x5c, 0xc2, 0xa6, 0x79, 0x44, 0xa9, 0xdb, 0x94, 0x88,
0x9e, 0x84, 0x32, 0x75, 0xc9, 0x1a, 0x1e, 0x28, 0x7d, 0xa2, 0xf5, 0xfa, 0x0e, 0x73, 0xc7, 0x49,
0xb9, 0x24, 0xa8, 0x2d, 0x46, 0x94, 0x54, 0x6f, 0xc3, 0x99, 0x3b, 0x46, 0x08, 0x52, 0x2a, 0x76,
0x30, 0x33, 0x64, 0x51, 0x66, 0x6d, 0x4a, 0x33, 0xb1, 0xd3, 0x17, 0xe6, 0x61, 0x6d, 0x74, 0x19,
0x32, 0x42, 0x6d, 0x92, 0xa9, 0x15, 0x3d, 0xb4, 0x0c, 0x69, 0xd3, 0x32, 0x4e, 0x09, 0xdb, 0xb9,
0x9c, 0xcc, 0x3b, 0xd2, 0x27, 0x09, 0x58, 0x9a, 0x72, 0xdc, 0x54, 0x6f, 0x1f, 0xdb, 0x7d, 0xf7,
0x5b, 0xb4, 0x8d, 0x5e, 0xa4, 0x7a, 0xb1, 0x4a, 0x2c, 0x11, 0xec, 0x6a, 0x41, 0x13, 0xf1, 0x40,
0xde, 0x62, 0xe3, 0xc2, 0x34, 0x82, 0x9b, 0xee, 0xd5, 0x00, 0xdb, 0x8e, 0xc2, 0x1d, 0xa1, 0x12,
0x08, 0x7c, 0x8f, 0xcf, 0xd8, 0x2b, 0xca, 0x43, 0xcf, 0xb3, 0x50, 0x52, 0xa6, 0xa2, 0x3e, 0x15,
0x1d, 0xc3, 0x72, 0xe7, 0xec, 0x63, 0xac, 0x3b, 0x9a, 0x4e, 0x94, 0xa9, 0x5d, 0x9b, 0x8e, 0xa4,
0xef, 0x68, 0x76, 0x87, 0xf4, 0xf1, 0xa9, 0x66, 0xb8, 0xd3, 0xba, 0xe4, 0xc9, 0x7b, 0x3b, 0x6a,
0x4b, 0x32, 0x94, 0xc3, 0x91, 0x07, 0x95, 0x21, 0xe1, 0x8c, 0xc5, 0xfa, 0x13, 0xce, 0x18, 0x3d,
0x0f, 0x29, 0xba, 0x46, 0xb6, 0xf6, 0xf2, 0x8c, 0x0f, 0x09, 0xb9, 0xf6, 0x99, 0x49, 0x64, 0xc6,
0x29, 0x49, 0xde, 0x65, 0xf0, 0xa2, 0xd1, 0xa4, 0x56, 0xe9, 0x16, 0x54, 0x26, 0xc2, 0x4d, 0x60,
0xfb, 0xe2, 0xc1, 0xed, 0x93, 0x2a, 0x50, 0x0a, 0xc5, 0x16, 0xe9, 0x32, 0x2c, 0xcf, 0x0a, 0x15,
0x52, 0xdf, 0xa3, 0x87, 0x5c, 0x3e, 0xba, 0x03, 0x39, 0x2f, 0x56, 0xf0, 0xcb, 0x78, 0x75, 0x6a,
0x15, 0x2e, 0xb3, 0xec, 0xb1, 0xd2, 0x5b, 0x48, 0x4f, 0x35, 0x3b, 0x0e, 0x09, 0x36, 0xf1, 0x2c,
0x36, 0xcd, 0x16, 0xb6, 0xfb, 0xd2, 0xfb, 0x50, 0x8b, 0x8a, 0x03, 0x13, 0xcb, 0x48, 0x79, 0xa7,
0xf0, 0x32, 0x64, 0x4e, 0x0c, 0x6b, 0x88, 0x1d, 0xa6, 0xac, 0x24, 0x8b, 0x1e, 0x3d, 0x9d, 0x3c,
0x26, 0x24, 0x19, 0x99, 0x77, 0x24, 0x05, 0xae, 0x46, 0xc6, 0x02, 0x2a, 0xa2, 0xe9, 0x2a, 0xe1,
0xf6, 0x2c, 0xc9, 0xbc, 0xe3, 0x2b, 0xe2, 0x93, 0xe5, 0x1d, 0xfa, 0x59, 0x9b, 0xad, 0x95, 0xe9,
0xcf, 0xcb, 0xa2, 0x27, 0x7d, 0x9e, 0x84, 0xcb, 0xb3, 0x23, 0x02, 0x5a, 0x83, 0xe2, 0x10, 0x8f,
0x15, 0x67, 0x2c, 0xee, 0x32, 0xdf, 0x0e, 0x18, 0xe2, 0x71, 0x7b, 0xcc, 0x2f, 0x72, 0x15, 0x92,
0xce, 0xd8, 0xae, 0x25, 0xd6, 0x92, 0x37, 0x8b, 0x32, 0x6d, 0xa2, 0x63, 0x58, 0x1a, 0x18, 0x5d,
0x3c, 0x50, 0x02, 0x27, 0x5e, 0x1c, 0xf6, 0x1b, 0x53, 0xc6, 0x6e, 0x8e, 0x19, 0x45, 0x9d, 0x3a,
0xf4, 0x15, 0xa6, 0x63, 0xcf, 0x3b, 0xf9, 0xe8, 0x2e, 0x14, 0x86, 0xfe, 0x41, 0xbe, 0xc0, 0x61,
0x0f, 0x8a, 0x05, 0xb6, 0x24, 0x1d, 0x72, 0x0c, 0xae, 0x87, 0xce, 0x5c, 0xd8, 0x43, 0x3f, 0x0f,
0xcb, 0x3a, 0x19, 0x3b, 0x81, 0x8b, 0xc8, 0xcf, 0x49, 0x96, 0x99, 0x1e, 0xd1, 0x31, 0xff, 0x92,
0xd1, 0x23, 0x83, 0x6e, 0xb1, 0x98, 0x6a, 0x1a, 0x36, 0xb1, 0x14, 0xac, 0xaa, 0x16, 0xb1, 0x6d,
0x96, 0x0b, 0x16, 0x59, 0xa0, 0x64, 0xf4, 0x2d, 0x4e, 0x96, 0x7e, 0x1d, 0xdc, 0x9a, 0x50, 0x0c,
0x75, 0x0d, 0x1f, 0xf7, 0x0d, 0x7f, 0x04, 0xcb, 0x42, 0x5e, 0x0d, 0xd9, 0x3e, 0xb1, 0xa8, 0xa3,
0x41, 0xae, 0x78, 0xb4, 0xd9, 0x93, 0xdf, 0xcd, 0xec, 0xae, 0x2f, 0x4d, 0x05, 0x7c, 0xe9, 0xff,
0xd8, 0x56, 0xfc, 0x39, 0x0f, 0x39, 0x99, 0xd8, 0x26, 0x0d, 0x9c, 0x68, 0x1b, 0xf2, 0x64, 0xdc,
0x25, 0xa6, 0xe3, 0xa6, 0x1a, 0xb3, 0xb1, 0x00, 0xe7, 0x6e, 0xba, 0x9c, 0x34, 0x11, 0xf7, 0xc4,
0xd0, 0x0b, 0x02, 0x6b, 0x45, 0xc3, 0x26, 0x21, 0x1e, 0x04, 0x5b, 0x2f, 0xba, 0x60, 0x2b, 0x19,
0x99, 0x7b, 0x73, 0xa9, 0x09, 0xb4, 0xf5, 0x82, 0x40, 0x5b, 0xa9, 0x39, 0x1f, 0x0b, 0xc1, 0xad,
0x46, 0x08, 0x6e, 0x65, 0xe6, 0x2c, 0x33, 0x02, 0x6f, 0xbd, 0xe8, 0xe2, 0xad, 0xec, 0x9c, 0x19,
0x4f, 0x00, 0xae, 0x7b, 0x61, 0xc0, 0x95, 0x8b, 0x70, 0x20, 0xae, 0x74, 0x24, 0xe2, 0x7a, 0x3d,
0x80, 0xb8, 0xf2, 0x91, 0x70, 0x87, 0x2b, 0x99, 0x01, 0xb9, 0x1a, 0x21, 0xc8, 0x05, 0x73, 0x6c,
0x10, 0x81, 0xb9, 0xde, 0x0c, 0x62, 0xae, 0x42, 0x24, 0x6c, 0x13, 0xfb, 0x3d, 0x0b, 0x74, 0xbd,
0xe2, 0x81, 0xae, 0x62, 0x24, 0x6a, 0x14, 0x6b, 0x98, 0x44, 0x5d, 0x07, 0x53, 0xa8, 0x8b, 0xa3,
0xa4, 0xa7, 0x22, 0x55, 0xcc, 0x81, 0x5d, 0x07, 0x53, 0xb0, 0xab, 0x3c, 0x47, 0xe1, 0x1c, 0xdc,
0xf5, 0xf3, 0xd9, 0xb8, 0x2b, 0x1a, 0x19, 0x89, 0x69, 0x2e, 0x06, 0xbc, 0x94, 0x08, 0xe0, 0xc5,
0xc1, 0xd1, 0x33, 0x91, 0xea, 0x17, 0x46, 0x5e, 0xc7, 0x33, 0x90, 0x17, 0xc7, 0x48, 0x37, 0x23,
0x95, 0x2f, 0x00, 0xbd, 0x8e, 0x67, 0x40, 0x2f, 0x34, 0x57, 0xed, 0x45, 0xb0, 0x57, 0xba, 0x9a,
0x91, 0x6e, 0xd1, 0xd4, 0x77, 0xc2, 0x4f, 0xd1, 0xfc, 0x81, 0x58, 0x96, 0x61, 0x09, 0x14, 0xc5,
0x3b, 0xd2, 0x4d, 0x9a, 0x8c, 0xfb, 0x3e, 0xe9, 0x1c, 0x9c, 0xc6, 0xf2, 0xb4, 0x80, 0x1f, 0x92,
0x7e, 0x1f, 0xf7, 0x65, 0x59, 0x0e, 0x1b, 0x4c, 0xe4, 0xf3, 0x22, 0x91, 0x0f, 0xa0, 0xb7, 0x44,
0x18, 0xbd, 0xad, 0x42, 0x81, 0xe6, 0x5f, 0x13, 0xc0, 0x0c, 0x9b, 0x1e, 0x30, 0xbb, 0x0d, 0x4b,
0x2c, 0xe2, 0x71, 0x8c, 0x27, 0xc2, 0x4a, 0x8a, 0x85, 0x95, 0x0a, 0x1d, 0xe0, 0x17, 0x8a, 0xc7,
0x97, 0xe7, 0xe0, 0x52, 0x80, 0xd7, 0xcb, 0xeb, 0x38, 0x4c, 0xa9, 0x7a, 0xdc, 0x5b, 0x22, 0xc1,
0xfb, 0x63, 0xdc, 0xb7, 0x90, 0x8f, 0xe8, 0x66, 0x81, 0xaf, 0xf8, 0x7f, 0x06, 0x7c, 0x25, 0xbe,
0x33, 0xf8, 0x0a, 0xa6, 0xa9, 0xc9, 0x70, 0x9a, 0xfa, 0xcf, 0xb8, 0xbf, 0x25, 0x1e, 0x94, 0xea,
0x1a, 0x2a, 0x11, 0x89, 0x23, 0x6b, 0xd3, 0x9c, 0x62, 0x60, 0xf4, 0x44, 0x7a, 0x48, 0x9b, 0x94,
0xcb, 0x8b, 0x1b, 0x79, 0x11, 0x16, 0xbc, 0x9c, 0x93, 0xc7, 0x6d, 0x91, 0x73, 0x56, 0x21, 0xf9,
0x90, 0xf0, 0xaa, 0x5a, 0x51, 0xa6, 0x4d, 0xca, 0xc7, 0x4e, 0x9a, 0x88, 0xbf, 0xbc, 0x83, 0x5e,
0x86, 0x3c, 0xab, 0x87, 0x2a, 0x86, 0x69, 0x0b, 0xaf, 0x1e, 0x4a, 0x4d, 0x78, 0xd9, 0x73, 0xfd,
0x90, 0xf2, 0x1c, 0x98, 0xb6, 0x9c, 0x33, 0x45, 0x2b, 0x90, 0x30, 0xe4, 0x43, 0x09, 0xc3, 0x35,
0xc8, 0xd3, 0xd9, 0xdb, 0x26, 0xee, 0x12, 0xe6, 0xa1, 0xf3, 0xb2, 0x4f, 0x90, 0x1e, 0x00, 0x9a,
0x8e, 0x11, 0xa8, 0x05, 0x19, 0x72, 0x4a, 0x74, 0x87, 0x27, 0x50, 0x85, 0xcd, 0xcb, 0xd3, 0x99,
0x29, 0x1d, 0xde, 0xae, 0x51, 0x23, 0xff, 0xe3, 0xeb, 0xd5, 0x2a, 0xe7, 0x7e, 0xd6, 0x18, 0x6a,
0x0e, 0x19, 0x9a, 0xce, 0x99, 0x2c, 0xe4, 0xa5, 0xbf, 0x26, 0x28, 0x7e, 0x09, 0xc5, 0x8f, 0x99,
0xb6, 0x75, 0x4f, 0x7c, 0x22, 0x00, 0x5d, 0x17, 0xb3, 0xf7, 0x0a, 0x40, 0x0f, 0xdb, 0xca, 0x47,
0x58, 0x77, 0x88, 0x2a, 0x8c, 0x1e, 0xa0, 0xa0, 0x3a, 0xe4, 0x68, 0x6f, 0x64, 0x13, 0x55, 0xa0,
0x68, 0xaf, 0x1f, 0x58, 0x67, 0xf6, 0xfb, 0xad, 0x33, 0x6c, 0xe5, 0xdc, 0x84, 0x95, 0x03, 0xd8,
0x22, 0x1f, 0xc4, 0x16, 0x74, 0x6e, 0xa6, 0xa5, 0x19, 0x96, 0xe6, 0x9c, 0xb1, 0xad, 0x49, 0xca,
0x5e, 0x1f, 0xdd, 0x80, 0xd2, 0x90, 0x0c, 0x4d, 0xc3, 0x18, 0x28, 0xdc, 0xdb, 0x14, 0x98, 0x68,
0x51, 0x10, 0x9b, 0xcc, 0xe9, 0xfc, 0x2a, 0xe1, 0x5f, 0x3f, 0x1f, 0x43, 0xfe, 0xdf, 0x19, 0x58,
0xfa, 0x0d, 0xab, 0x2b, 0x85, 0x33, 0x04, 0x74, 0x04, 0x4b, 0xde, 0xf5, 0x57, 0x46, 0xcc, 0x2d,
0xb8, 0x07, 0x7a, 0x51, 0xff, 0x51, 0x3d, 0x0d, 0x93, 0x6d, 0xf4, 0x13, 0xb8, 0x32, 0xe1, 0xda,
0x3c, 0xd5, 0x89, 0x05, 0x3d, 0xdc, 0x63, 0x61, 0x0f, 0xe7, 0x6a, 0xf6, 0x6d, 0x95, 0xfc, 0x9e,
0x97, 0x6e, 0x07, 0xca, 0xe1, 0x7c, 0x67, 0xe6, 0xee, 0xdf, 0x80, 0x92, 0x45, 0x1c, 0xac, 0xe9,
0x4a, 0xa8, 0x18, 0x54, 0xe4, 0x44, 0x51, 0x62, 0x3a, 0x84, 0xc7, 0x66, 0xe6, 0x3d, 0xe8, 0x25,
0xc8, 0xfb, 0x29, 0x13, 0x37, 0xea, 0x39, 0xc5, 0x02, 0x9f, 0x57, 0xfa, 0x43, 0xdc, 0x57, 0x19,
0x2e, 0x3f, 0x34, 0x21, 0x63, 0x11, 0x7b, 0x34, 0xe0, 0x05, 0x81, 0xf2, 0xe6, 0x73, 0x8b, 0x65,
0x4c, 0x94, 0x3a, 0x1a, 0x38, 0xb2, 0x10, 0x96, 0x1e, 0x40, 0x86, 0x53, 0x50, 0x01, 0xb2, 0xc7,
0xfb, 0xbb, 0xfb, 0x07, 0xef, 0xee, 0x57, 0x63, 0x08, 0x20, 0xb3, 0xd5, 0x68, 0x34, 0x0f, 0xdb,
0xd5, 0x38, 0xca, 0x43, 0x7a, 0x6b, 0xfb, 0x40, 0x6e, 0x57, 0x13, 0x94, 0x2c, 0x37, 0xdf, 0x6e,
0x36, 0xda, 0xd5, 0x24, 0x5a, 0x82, 0x12, 0x6f, 0x2b, 0xf7, 0x0e, 0xe4, 0x77, 0xb6, 0xda, 0xd5,
0x54, 0x80, 0x74, 0xd4, 0xdc, 0xbf, 0xdb, 0x94, 0xab, 0x69, 0xe9, 0x07, 0x70, 0x35, 0x32, 0xc7,
0xf2, 0x6b, 0x0b, 0xf1, 0x40, 0x6d, 0x41, 0xfa, 0x3c, 0x01, 0xf5, 0xe8, 0xc4, 0x09, 0xbd, 0x3d,
0xb1, 0xf0, 0xcd, 0x0b, 0x64, 0x5d, 0x13, 0xab, 0x47, 0x4f, 0x42, 0xd9, 0x22, 0x27, 0xc4, 0xe9,
0xf6, 0x79, 0x22, 0xc7, 0x23, 0x66, 0x49, 0x2e, 0x09, 0x2a, 0x13, 0xb2, 0x39, 0xdb, 0x07, 0xa4,
0xeb, 0x28, 0xdc, 0x15, 0xf1, 0x43, 0x97, 0xa7, 0x6c, 0x94, 0x7a, 0xc4, 0x89, 0xd2, 0xfb, 0x17,
0xb2, 0x65, 0x1e, 0xd2, 0x72, 0xb3, 0x2d, 0xbf, 0x57, 0x4d, 0x22, 0x04, 0x65, 0xd6, 0x54, 0x8e,
0xf6, 0xb7, 0x0e, 0x8f, 0x5a, 0x07, 0xd4, 0x96, 0x97, 0xa0, 0xe2, 0xda, 0xd2, 0x25, 0xa6, 0xa5,
0x67, 0xe0, 0x4a, 0x44, 0xd6, 0x37, 0x8d, 0xe1, 0xa5, 0xdf, 0xc6, 0x83, 0xdc, 0x61, 0xc4, 0x7f,
0x00, 0x19, 0xdb, 0xc1, 0xce, 0xc8, 0x16, 0x46, 0x7c, 0x69, 0xd1, 0x34, 0x70, 0xdd, 0x6d, 0x1c,
0x31, 0x71, 0x59, 0xa8, 0x91, 0xee, 0x40, 0x39, 0x3c, 0x12, 0x6d, 0x03, 0xff, 0x10, 0x25, 0xa4,
0x6f, 0xe3, 0x50, 0x99, 0xb8, 0xf1, 0x68, 0x13, 0xd2, 0x1c, 0xdd, 0x44, 0xfd, 0x39, 0x64, 0x0e,
0x4b, 0xb8, 0x07, 0xce, 0x8a, 0x5e, 0x83, 0x1c, 0x39, 0xd5, 0x54, 0xa2, 0x77, 0xc9, 0x2c, 0xcf,
0xc2, 0xcb, 0xa9, 0x4d, 0xc1, 0x21, 0x44, 0x3d, 0x09, 0xf4, 0x06, 0xe4, 0x3d, 0xd7, 0x25, 0xd0,
0xf0, 0xf5, 0x69, 0x71, 0xcf, 0xe9, 0x09, 0x79, 0x5f, 0x06, 0xbd, 0xe2, 0xa7, 0x9b, 0xa9, 0x69,
0x4c, 0x25, 0xc4, 0x39, 0x83, 0x10, 0x76, 0xf9, 0xa5, 0x06, 0x14, 0x02, 0xeb, 0x41, 0x8f, 0x43,
0x7e, 0x88, 0xc3, 0x25, 0xb2, 0xdc, 0x10, 0x8b, 0x02, 0xd9, 0x15, 0xc8, 0xd2, 0xc1, 0x1e, 0xe6,
0xee, 0x33, 0x29, 0x67, 0x86, 0x78, 0xfc, 0x16, 0xb6, 0xa5, 0xf7, 0x00, 0x02, 0x45, 0xdd, 0x65,
0x48, 0x5b, 0xc6, 0x48, 0x57, 0x99, 0x7c, 0x5a, 0xe6, 0x1d, 0x74, 0x07, 0xd2, 0xa7, 0x06, 0xf7,
0xbc, 0xb3, 0xfd, 0xcf, 0x7d, 0xc3, 0x21, 0x81, 0x0a, 0x0e, 0xe7, 0x96, 0x34, 0x40, 0xd3, 0x85,
0xb5, 0x88, 0x4f, 0xbc, 0x1e, 0xfe, 0xc4, 0xf5, 0xc8, 0x12, 0xdd, 0xec, 0x4f, 0x7d, 0x0c, 0x69,
0xe6, 0xb4, 0xa9, 0x03, 0x66, 0xc5, 0x61, 0x91, 0xd1, 0xd3, 0x36, 0xfa, 0x05, 0x00, 0x76, 0x1c,
0x4b, 0xeb, 0x8c, 0xfc, 0x0f, 0xac, 0xce, 0x76, 0xfa, 0x5b, 0x2e, 0xdf, 0xf6, 0x35, 0xe1, 0xfd,
0x97, 0x7d, 0xd1, 0x40, 0x04, 0x08, 0x28, 0x94, 0xf6, 0xa1, 0x1c, 0x96, 0x75, 0x93, 0xd0, 0xf8,
0x8c, 0x24, 0x34, 0x11, 0x4c, 0x42, 0xbd, 0x14, 0x36, 0xc9, 0xff, 0x03, 0xb0, 0x8e, 0xf4, 0x69,
0x1c, 0x72, 0xed, 0xb1, 0x70, 0x07, 0x11, 0x35, 0x68, 0x5f, 0x34, 0x11, 0xac, 0xb8, 0xf2, 0xa2,
0x76, 0xd2, 0x2b, 0x95, 0xbf, 0xe9, 0x39, 0xbc, 0xd4, 0xa2, 0x25, 0x03, 0xf7, 0x97, 0x81, 0x70,
0xf2, 0xaf, 0x42, 0xde, 0x3b, 0xbc, 0x14, 0x1a, 0xb9, 0xe5, 0xa9, 0xb8, 0x48, 0xec, 0x79, 0x97,
0xfd, 0xd1, 0x30, 0x3e, 0x12, 0x35, 0xdd, 0xa4, 0xcc, 0x3b, 0x92, 0x0a, 0x95, 0x89, 0x70, 0x8f,
0x5e, 0x85, 0xac, 0x39, 0xea, 0x28, 0xae, 0x79, 0x26, 0xee, 0xa8, 0x9b, 0x75, 0x8f, 0x3a, 0x03,
0xad, 0xbb, 0x4b, 0xce, 0xdc, 0xc9, 0x98, 0xa3, 0xce, 0x2e, 0xb7, 0x22, 0xff, 0x4a, 0x22, 0xf8,
0x95, 0x53, 0xc8, 0xb9, 0x87, 0x02, 0xfd, 0x28, 0x78, 0x1d, 0xdd, 0xff, 0x5c, 0x91, 0x29, 0x88,
0x50, 0x1f, 0xb8, 0x8d, 0xb7, 0x61, 0xc9, 0xd6, 0x7a, 0xba, 0x5b, 0xba, 0xe4, 0xce, 0x24, 0xc1,
0x76, 0xa7, 0xc2, 0x07, 0xf6, 0x5c, 0x64, 0x26, 0xfd, 0x2e, 0x0e, 0xd5, 0xc9, 0x53, 0xf9, 0xdf,
0x9c, 0x00, 0x8d, 0x2d, 0xf4, 0xf4, 0x2b, 0x84, 0x4e, 0xc2, 0x83, 0xa4, 0x45, 0xb9, 0x44, 0xa9,
0x4d, 0x97, 0x28, 0x7d, 0x92, 0x80, 0x42, 0xa0, 0x30, 0x8a, 0x7e, 0x18, 0xb8, 0x22, 0xe5, 0x19,
0x69, 0x54, 0x80, 0xd7, 0xff, 0x87, 0x12, 0x5e, 0x58, 0xe2, 0xe2, 0x0b, 0x8b, 0xfa, 0x17, 0xe6,
0xd6, 0x59, 0x53, 0x17, 0xae, 0xb3, 0x3e, 0x0b, 0xc8, 0x31, 0x1c, 0x3c, 0x50, 0x4e, 0x0d, 0x47,
0xd3, 0x7b, 0x0a, 0x3f, 0x1a, 0x3c, 0x6f, 0xae, 0xb2, 0x91, 0xfb, 0x6c, 0xe0, 0x90, 0x9d, 0x92,
0x5f, 0xc6, 0x21, 0xe7, 0x65, 0x40, 0x17, 0xfd, 0x25, 0x72, 0x19, 0x32, 0x22, 0xc8, 0xf3, 0x7f,
0x22, 0xa2, 0x37, 0xb3, 0xa0, 0x5c, 0x87, 0xdc, 0x90, 0x38, 0x98, 0xa5, 0x81, 0x1c, 0xcd, 0x7b,
0xfd, 0xdb, 0xaf, 0x40, 0x21, 0xf0, 0x77, 0x8a, 0xfa, 0x89, 0xfd, 0xe6, 0xbb, 0xd5, 0x58, 0x3d,
0xfb, 0xe9, 0x17, 0x6b, 0xc9, 0x7d, 0xf2, 0x11, 0xbd, 0x61, 0x72, 0xb3, 0xd1, 0x6a, 0x36, 0x76,
0xab, 0xf1, 0x7a, 0xe1, 0xd3, 0x2f, 0xd6, 0xb2, 0x32, 0x61, 0x35, 0xc0, 0xdb, 0xbb, 0x50, 0x99,
0xd8, 0x98, 0x70, 0x98, 0x44, 0x50, 0xbe, 0x7b, 0x7c, 0xb8, 0xb7, 0xd3, 0xd8, 0x6a, 0x37, 0x95,
0xfb, 0x07, 0xed, 0x66, 0x35, 0x8e, 0xae, 0xc0, 0xa5, 0xbd, 0x9d, 0xb7, 0x5a, 0x6d, 0xa5, 0xb1,
0xb7, 0xd3, 0xdc, 0x6f, 0x2b, 0x5b, 0xed, 0xf6, 0x56, 0x63, 0xb7, 0x9a, 0xd8, 0xfc, 0x17, 0x40,
0x65, 0x6b, 0xbb, 0xb1, 0x43, 0xd3, 0x1c, 0xad, 0x8b, 0x59, 0xb5, 0xa5, 0x01, 0x29, 0x56, 0x4f,
0x39, 0xf7, 0xb9, 0x4d, 0xfd, 0xfc, 0x02, 0x31, 0xba, 0x07, 0x69, 0x56, 0x6a, 0x41, 0xe7, 0xbf,
0xbf, 0xa9, 0xcf, 0xa9, 0x18, 0xd3, 0xc9, 0xb0, 0xeb, 0x74, 0xee, 0x83, 0x9c, 0xfa, 0xf9, 0x05,
0x64, 0x24, 0x43, 0xde, 0xc7, 0x6a, 0xf3, 0x1f, 0xa8, 0xd4, 0x17, 0xf0, 0x8e, 0x68, 0x0f, 0xb2,
0x2e, 0xbc, 0x9e, 0xf7, 0x64, 0xa6, 0x3e, 0xb7, 0xc2, 0x4b, 0xcd, 0xc5, 0xcb, 0x20, 0xe7, 0xbf,
0xff, 0xa9, 0xcf, 0x29, 0x57, 0xa3, 0x1d, 0xc8, 0x08, 0x00, 0x32, 0xe7, 0x19, 0x4c, 0x7d, 0x5e,
0xc5, 0x96, 0x1a, 0xcd, 0xaf, 0x2f, 0xcd, 0x7f, 0xd5, 0x54, 0x5f, 0xa0, 0x12, 0x8f, 0x8e, 0x01,
0x02, 0x45, 0x8f, 0x05, 0x9e, 0x2b, 0xd5, 0x17, 0xa9, 0xb0, 0xa3, 0x03, 0xc8, 0x79, 0x18, 0x74,
0xee, 0xe3, 0xa1, 0xfa, 0xfc, 0x52, 0x37, 0x7a, 0x00, 0xa5, 0x30, 0xf8, 0x5a, 0xec, 0x49, 0x50,
0x7d, 0xc1, 0x1a, 0x36, 0xd5, 0x1f, 0x46, 0x62, 0x8b, 0x3d, 0x11, 0xaa, 0x2f, 0x58, 0xd2, 0x46,
0x1f, 0xc0, 0xd2, 0x34, 0x52, 0x5a, 0xfc, 0xc5, 0x50, 0xfd, 0x02, 0x45, 0x6e, 0x34, 0x04, 0x34,
0x03, 0x61, 0x5d, 0xe0, 0x01, 0x51, 0xfd, 0x22, 0x35, 0x6f, 0xa4, 0x42, 0x65, 0x12, 0xb6, 0x2c,
0xfa, 0xa0, 0xa8, 0xbe, 0x70, 0xfd, 0x9b, 0x7f, 0x25, 0x0c, 0x77, 0x16, 0x7d, 0x60, 0x54, 0x5f,
0xb8, 0x1c, 0xbe, 0xdd, 0xfc, 0xf2, 0x9b, 0x95, 0xf8, 0x57, 0xdf, 0xac, 0xc4, 0xff, 0xfe, 0xcd,
0x4a, 0xfc, 0xb3, 0x47, 0x2b, 0xb1, 0xaf, 0x1e, 0xad, 0xc4, 0xfe, 0xf2, 0x68, 0x25, 0xf6, 0xd3,
0x67, 0x7a, 0x9a, 0xd3, 0x1f, 0x75, 0xd6, 0xbb, 0xc6, 0x70, 0x23, 0xf8, 0xca, 0x72, 0xd6, 0xcb,
0xcf, 0x4e, 0x86, 0xc5, 0xc8, 0x17, 0xfe, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x7a, 0xfb, 0x93, 0x3d,
0x19, 0x2a, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
@@ -6755,16 +6664,11 @@ func (m *ResponsePrepareProposal) MarshalToSizedBuffer(dAtA []byte) (int, error)
_ = i
var l int
_ = l
if len(m.TxRecords) > 0 {
for iNdEx := len(m.TxRecords) - 1; iNdEx >= 0; iNdEx-- {
{
size, err := m.TxRecords[iNdEx].MarshalToSizedBuffer(dAtA[:i])
if err != nil {
return 0, err
}
i -= size
i = encodeVarintTypes(dAtA, i, uint64(size))
}
if len(m.Txs) > 0 {
for iNdEx := len(m.Txs) - 1; iNdEx >= 0; iNdEx-- {
i -= len(m.Txs[iNdEx])
copy(dAtA[i:], m.Txs[iNdEx])
i = encodeVarintTypes(dAtA, i, uint64(len(m.Txs[iNdEx])))
i--
dAtA[i] = 0xa
}
@@ -7129,41 +7033,6 @@ func (m *TxResult) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
func (m *TxRecord) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalToSizedBuffer(dAtA[:size])
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *TxRecord) MarshalTo(dAtA []byte) (int, error) {
size := m.Size()
return m.MarshalToSizedBuffer(dAtA[:size])
}
func (m *TxRecord) MarshalToSizedBuffer(dAtA []byte) (int, error) {
i := len(dAtA)
_ = i
var l int
_ = l
if len(m.Tx) > 0 {
i -= len(m.Tx)
copy(dAtA[i:], m.Tx)
i = encodeVarintTypes(dAtA, i, uint64(len(m.Tx)))
i--
dAtA[i] = 0x12
}
if m.Action != 0 {
i = encodeVarintTypes(dAtA, i, uint64(m.Action))
i--
dAtA[i] = 0x8
}
return len(dAtA) - i, nil
}
func (m *Validator) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
@@ -8530,9 +8399,9 @@ func (m *ResponsePrepareProposal) Size() (n int) {
}
var l int
_ = l
if len(m.TxRecords) > 0 {
for _, e := range m.TxRecords {
l = e.Size()
if len(m.Txs) > 0 {
for _, b := range m.Txs {
l = len(b)
n += 1 + l + sovTypes(uint64(l))
}
}
@@ -8687,22 +8556,6 @@ func (m *TxResult) Size() (n int) {
return n
}
func (m *TxRecord) Size() (n int) {
if m == nil {
return 0
}
var l int
_ = l
if m.Action != 0 {
n += 1 + sovTypes(uint64(m.Action))
}
l = len(m.Tx)
if l > 0 {
n += 1 + l + sovTypes(uint64(l))
}
return n
}
func (m *Validator) Size() (n int) {
if m == nil {
return 0
@@ -14516,9 +14369,9 @@ func (m *ResponsePrepareProposal) Unmarshal(dAtA []byte) error {
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field TxRecords", wireType)
return fmt.Errorf("proto: wrong wireType = %d for field Txs", wireType)
}
var msglen int
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTypes
@@ -14528,25 +14381,23 @@ func (m *ResponsePrepareProposal) Unmarshal(dAtA []byte) error {
}
b := dAtA[iNdEx]
iNdEx++
msglen |= int(b&0x7F) << shift
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
if byteLen < 0 {
return ErrInvalidLengthTypes
}
postIndex := iNdEx + msglen
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthTypes
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.TxRecords = append(m.TxRecords, &TxRecord{})
if err := m.TxRecords[len(m.TxRecords)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
m.Txs = append(m.Txs, make([]byte, postIndex-iNdEx))
copy(m.Txs[len(m.Txs)-1], dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
@@ -15535,109 +15386,6 @@ func (m *TxResult) Unmarshal(dAtA []byte) error {
}
return nil
}
func (m *TxRecord) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTypes
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= uint64(b&0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: TxRecord: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: TxRecord: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Action", wireType)
}
m.Action = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTypes
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.Action |= TxRecord_TxAction(b&0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Tx", wireType)
}
var byteLen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTypes
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
byteLen |= int(b&0x7F) << shift
if b < 0x80 {
break
}
}
if byteLen < 0 {
return ErrInvalidLengthTypes
}
postIndex := iNdEx + byteLen
if postIndex < 0 {
return ErrInvalidLengthTypes
}
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Tx = append(m.Tx[:0], dAtA[iNdEx:postIndex]...)
if m.Tx == nil {
m.Tx = []byte{}
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipTypes(dAtA[iNdEx:])
if err != nil {
return err
}
if (skippy < 0) || (iNdEx+skippy) < 0 {
return ErrInvalidLengthTypes
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *Validator) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0

View File

@@ -259,20 +259,16 @@ func (app *CounterApplication) Commit() abci.ResponseCommit {
func (app *CounterApplication) PrepareProposal(
req abci.RequestPrepareProposal) abci.ResponsePrepareProposal {
trs := make([]*abci.TxRecord, 0, len(req.Txs))
txs := make([][]byte, 0, len(req.Txs))
var totalBytes int64
for _, tx := range req.Txs {
totalBytes += int64(len(tx))
if totalBytes > req.MaxTxBytes {
break
}
trs = append(trs, &abci.TxRecord{
Action: abci.TxRecord_UNMODIFIED,
Tx: tx,
})
txs = append(txs, tx)
}
return abci.ResponsePrepareProposal{TxRecords: trs}
return abci.ResponsePrepareProposal{Txs: txs}
}
func (app *CounterApplication) ProcessProposal(

View File

@@ -302,7 +302,7 @@ message ResponseApplySnapshotChunk {
}
message ResponsePrepareProposal {
repeated TxRecord tx_records = 1;
repeated bytes txs = 1;
}
message ResponseProcessProposal {
@@ -376,19 +376,6 @@ message TxResult {
ResponseDeliverTx result = 4 [(gogoproto.nullable) = false];
}
message TxRecord {
TxAction action = 1;
bytes tx = 2;
// TxAction contains App-provided information on what to do with a transaction that is part of a raw proposal
enum TxAction {
UNKNOWN = 0; // Unknown action
UNMODIFIED = 1; // The Application did not modify this transaction.
ADDED = 2; // The Application added this transaction.
REMOVED = 3; // The Application wants this transaction removed from the proposal and the mempool.
}
}
//----------------------------------------
// Blockchain Types

View File

@@ -136,19 +136,13 @@ func (blockExec *BlockExecutor) CreateProposalBlock(
// error for now (the production code calling this function is expected to panic).
return nil, err
}
txrSet := types.NewTxRecordSet(rpp.TxRecords)
if err := txrSet.Validate(maxDataBytes, block.Txs); err != nil {
txl := types.ToTxs(rpp.Txs)
if err := txl.Validate(maxDataBytes); err != nil {
return nil, err
}
for _, rtx := range txrSet.RemovedTxs() {
if err := blockExec.mempool.RemoveTxByKey(rtx.Key()); err != nil {
blockExec.logger.Debug("error removing transaction from the mempool", "error", err, "tx hash", rtx.Hash())
}
}
itxs := txrSet.IncludedTxs()
return state.MakeBlock(height, itxs, commit, evidence, proposerAddr), nil
return state.MakeBlock(height, txl, commit, evidence, proposerAddr), nil
}
func (blockExec *BlockExecutor) ProcessProposal(

View File

@@ -614,113 +614,9 @@ func TestEmptyPrepareProposal(t *testing.T) {
require.NoError(t, err)
}
// TestPrepareProposalErrorOnNonExistingRemoved tests that the block creation logic returns
// an error if the ResponsePrepareProposal returned from the application marks
// a transaction as REMOVED that was not present in the original proposal.
func TestPrepareProposalErrorOnNonExistingRemoved(t *testing.T) {
const height = 2
state, stateDB, privVals := makeState(1, height)
stateStore := sm.NewStore(stateDB)
evpool := &mocks.EvidencePool{}
evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0))
mp := &mpmocks.Mempool{}
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs{})
// create an invalid ResponsePrepareProposal
rpp := abci.ResponsePrepareProposal{
TxRecords: []*abci.TxRecord{
{
Action: abci.TxRecord_REMOVED,
Tx: []byte("new tx"),
},
},
}
app := abcimocks.NewBaseMock()
app.On("PrepareProposal", mock.Anything).Return(rpp)
cc := proxy.NewLocalClientCreator(app)
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
err := proxyApp.Start()
require.NoError(t, err)
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
blockExec := sm.NewBlockExecutor(
stateStore,
log.TestingLogger(),
proxyApp.Consensus(),
mp,
evpool,
)
pa, _ := state.Validators.GetByIndex(0)
commit, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
require.NoError(t, err)
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, nil)
require.Nil(t, block)
require.ErrorContains(t, err, "new transaction incorrectly marked as removed")
mp.AssertExpectations(t)
}
// TestPrepareProposalRemoveTxs tests that any transactions marked as REMOVED
// are not included in the block produced by CreateProposalBlock. The test also
// ensures that any transactions removed are also removed from the mempool.
func TestPrepareProposalRemoveTxs(t *testing.T) {
const height = 2
state, stateDB, privVals := makeState(1, height)
stateStore := sm.NewStore(stateDB)
evpool := &mocks.EvidencePool{}
evpool.On("PendingEvidence", mock.Anything).Return([]types.Evidence{}, int64(0))
txs := factory.MakeNTxs(height, 10)
mp := &mpmocks.Mempool{}
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs))
trs := txsToTxRecords(types.Txs(txs))
trs[0].Action = abci.TxRecord_REMOVED
trs[1].Action = abci.TxRecord_REMOVED
mp.On("RemoveTxByKey", mock.Anything).Return(nil).Twice()
app := abcimocks.NewBaseMock()
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
TxRecords: trs,
})
cc := proxy.NewLocalClientCreator(app)
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
err := proxyApp.Start()
require.NoError(t, err)
defer proxyApp.Stop() //nolint:errcheck // ignore for tests
blockExec := sm.NewBlockExecutor(
stateStore,
log.TestingLogger(),
proxyApp.Consensus(),
mp,
evpool,
)
pa, _ := state.Validators.GetByIndex(0)
commit, err := makeValidCommit(height, types.BlockID{}, state.Validators, privVals)
require.NoError(t, err)
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, nil)
require.NoError(t, err)
require.Len(t, block.Data.Txs.ToSliceOfBytes(), len(trs)-2)
require.Equal(t, -1, block.Data.Txs.Index(types.Tx(trs[0].Tx)))
require.Equal(t, -1, block.Data.Txs.Index(types.Tx(trs[1].Tx)))
mp.AssertCalled(t, "RemoveTxByKey", types.Tx(trs[0].Tx).Key())
mp.AssertCalled(t, "RemoveTxByKey", types.Tx(trs[1].Tx).Key())
mp.AssertExpectations(t)
}
// TestPrepareProposalAddedTxsIncluded tests that any transactions marked as ADDED
// in the prepare proposal response are included in the block.
func TestPrepareProposalAddedTxsIncluded(t *testing.T) {
// TestPrepareProposalTxsAllIncluded tests that any transactions included in
// the prepare proposal response are included in the block.
func TestPrepareProposalTxsAllIncluded(t *testing.T) {
const height = 2
state, stateDB, privVals := makeState(1, height)
@@ -733,13 +629,9 @@ func TestPrepareProposalAddedTxsIncluded(t *testing.T) {
mp := &mpmocks.Mempool{}
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs[2:]))
trs := txsToTxRecords(types.Txs(txs))
trs[0].Action = abci.TxRecord_ADDED
trs[1].Action = abci.TxRecord_ADDED
app := abcimocks.NewBaseMock()
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
TxRecords: trs,
Txs: types.Txs(txs).ToSliceOfBytes(),
})
cc := proxy.NewLocalClientCreator(app)
proxyApp := proxy.NewAppConns(cc, proxy.NopMetrics())
@@ -760,8 +652,9 @@ func TestPrepareProposalAddedTxsIncluded(t *testing.T) {
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, nil)
require.NoError(t, err)
require.Equal(t, txs[0], block.Data.Txs[0])
require.Equal(t, txs[1], block.Data.Txs[1])
for i, tx := range block.Data.Txs {
require.Equal(t, txs[i], tx)
}
mp.AssertExpectations(t)
}
@@ -781,13 +674,12 @@ func TestPrepareProposalReorderTxs(t *testing.T) {
mp := &mpmocks.Mempool{}
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs))
trs := txsToTxRecords(types.Txs(txs))
trs = trs[2:]
trs = append(trs[len(trs)/2:], trs[:len(trs)/2]...)
txs = txs[2:]
txs = append(txs[len(txs)/2:], txs[:len(txs)/2]...)
app := abcimocks.NewBaseMock()
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
TxRecords: trs,
Txs: types.Txs(txs).ToSliceOfBytes(),
})
cc := proxy.NewLocalClientCreator(app)
@@ -809,7 +701,7 @@ func TestPrepareProposalReorderTxs(t *testing.T) {
block, err := blockExec.CreateProposalBlock(height, state, commit, pa, nil)
require.NoError(t, err)
for i, tx := range block.Data.Txs {
require.Equal(t, types.Tx(trs[i].Tx), tx)
require.Equal(t, txs[i], tx)
}
mp.AssertExpectations(t)
@@ -836,11 +728,9 @@ func TestPrepareProposalErrorOnTooManyTxs(t *testing.T) {
mp := &mpmocks.Mempool{}
mp.On("ReapMaxBytesMaxGas", mock.Anything, mock.Anything).Return(types.Txs(txs))
trs := txsToTxRecords(types.Txs(txs))
app := abcimocks.NewBaseMock()
app.On("PrepareProposal", mock.Anything).Return(abci.ResponsePrepareProposal{
TxRecords: trs,
Txs: types.Txs(txs).ToSliceOfBytes(),
})
cc := proxy.NewLocalClientCreator(app)
@@ -928,14 +818,3 @@ func makeBlockID(hash []byte, partSetSize uint32, partSetHash []byte) types.Bloc
},
}
}
func txsToTxRecords(txs []types.Tx) []*abci.TxRecord {
trs := make([]*abci.TxRecord, len(txs))
for i, tx := range txs {
trs[i] = &abci.TxRecord{
Action: abci.TxRecord_UNMODIFIED,
Tx: tx,
}
}
return trs
}

View File

@@ -259,20 +259,16 @@ func (app *Application) ApplySnapshotChunk(req abci.RequestApplySnapshotChunk) a
func (app *Application) PrepareProposal(
req abci.RequestPrepareProposal) abci.ResponsePrepareProposal {
// None of the transactions are modified by this application.
trs := make([]*abci.TxRecord, 0, len(req.Txs))
txs := make([][]byte, 0, len(req.Txs))
var totalBytes int64
for _, tx := range req.Txs {
totalBytes += int64(len(tx))
if totalBytes > req.MaxTxBytes {
break
}
trs = append(trs, &abci.TxRecord{
Action: abci.TxRecord_UNMODIFIED,
Tx: tx,
})
txs = append(txs, tx)
}
return abci.ResponsePrepareProposal{TxRecords: trs}
return abci.ResponsePrepareProposal{Txs: txs}
}
// ProcessProposal implements part of the Application interface.

View File

@@ -5,9 +5,7 @@ import (
"crypto/sha256"
"errors"
"fmt"
"sort"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto/merkle"
"github.com/tendermint/tendermint/crypto/tmhash"
tmbytes "github.com/tendermint/tendermint/libs/bytes"
@@ -98,6 +96,25 @@ func (txs Txs) Less(i, j int) bool {
return bytes.Compare(txs[i], txs[j]) == -1
}
func ToTxs(txl [][]byte) Txs {
txs := make([]Tx, 0, len(txl))
for _, tx := range txl {
txs = append(txs, tx)
}
return txs
}
func (txs Txs) Validate(maxSizeBytes int64) error {
var size int64
for _, tx := range txs {
size += int64(len(tx))
if size > maxSizeBytes {
return fmt.Errorf("transaction data size exceeds maximum %d", maxSizeBytes)
}
}
return nil
}
// ToSliceOfBytes converts a Txs to slice of byte slices.
func (txs Txs) ToSliceOfBytes() [][]byte {
txBzs := make([][]byte, len(txs))
@@ -107,181 +124,6 @@ func (txs Txs) ToSliceOfBytes() [][]byte {
return txBzs
}
// TxRecordSet contains indexes into an underlying set of transactions.
// These indexes are useful for validating and working with a list of TxRecords
// from the PrepareProposal response.
//
// Only one copy of the original data is referenced by all of the indexes but a
// transaction may appear in multiple indexes.
type TxRecordSet struct {
// all holds the complete list of all transactions from the original list of
// TxRecords.
all Txs
// included is an index of the transactions that will be included in the block
// and is constructed from the list of both added and unmodified transactions.
// included maintains the original order that the transactions were present
// in the list of TxRecords.
included Txs
// added, unmodified, removed, and unknown are indexes for each of the actions
// that may be supplied with a transaction.
//
// Because each transaction only has one action, it can be referenced by
// at most 3 indexes in this data structure: the action-specific index, the
// included index, and the all index.
added Txs
unmodified Txs
removed Txs
unknown Txs
}
// NewTxRecordSet constructs a new set from the given transaction records.
// The contents of the input transactions are shared by the set, and must not
// be modified during the lifetime of the set.
func NewTxRecordSet(trs []*abci.TxRecord) TxRecordSet {
txrSet := TxRecordSet{
all: make([]Tx, len(trs)),
}
for i, tr := range trs {
txrSet.all[i] = Tx(tr.Tx)
// The following set of assignments do not allocate new []byte, they create
// pointers to the already allocated slice.
switch tr.GetAction() {
case abci.TxRecord_UNKNOWN:
txrSet.unknown = append(txrSet.unknown, txrSet.all[i])
case abci.TxRecord_UNMODIFIED:
txrSet.unmodified = append(txrSet.unmodified, txrSet.all[i])
txrSet.included = append(txrSet.included, txrSet.all[i])
case abci.TxRecord_ADDED:
txrSet.added = append(txrSet.added, txrSet.all[i])
txrSet.included = append(txrSet.included, txrSet.all[i])
case abci.TxRecord_REMOVED:
txrSet.removed = append(txrSet.removed, txrSet.all[i])
}
}
return txrSet
}
// IncludedTxs returns the transactions marked for inclusion in a block. This
// list maintains the order that the transactions were included in the list of
// TxRecords that were used to construct the TxRecordSet.
func (t TxRecordSet) IncludedTxs() []Tx {
return t.included
}
// RemovedTxs returns the transactions marked for removal by the application.
func (t TxRecordSet) RemovedTxs() []Tx {
return t.removed
}
// Validate checks that the record set was correctly constructed from the original
// list of transactions.
func (t TxRecordSet) Validate(maxSizeBytes int64, otxs Txs) error {
if len(t.unknown) > 0 {
return fmt.Errorf("%d transactions marked unknown (first unknown hash: %x)", len(t.unknown), t.unknown[0].Hash())
}
// The following validation logic performs a set of sorts on the data in the TxRecordSet indexes.
// It sorts the original transaction list, otxs, once.
// It sorts the new transaction list twice: once when sorting 'all', the total list,
// and once by sorting the set of the added, removed, and unmodified transactions indexes,
// which, when combined, comprise the complete list of modified transactions.
//
// Each of the added, removed, and unmodified indices is then iterated and once
// and each value index is checked against the sorted original list for containment.
// Asymptotically, this yields a total runtime of O(N*log(N) + 2*M*log(M) + M*log(N)).
// in the input size of the original list, N, and the input size of the new list, M, respectively.
// Performance gains are likely possible, but this was preferred for readability and maintainability.
// Sort a copy of the complete transaction slice so we can check for
// duplication. The copy is so we do not change the original ordering.
// Only the slices are copied, the transaction contents are shared.
allCopy := sortedCopy(t.all)
for i, cur := range allCopy {
// allCopy is sorted, so any duplicated data will be adjacent.
if i+1 < len(allCopy) && bytes.Equal(cur, allCopy[i+1]) {
return fmt.Errorf("found duplicate transaction with hash: %x", cur.Hash())
}
}
// create copies of each of the action-specific indexes so that order of the original
// indexes can be preserved.
addedCopy := sortedCopy(t.added)
removedCopy := sortedCopy(t.removed)
unmodifiedCopy := sortedCopy(t.unmodified)
var size int64
for _, cur := range append(unmodifiedCopy, addedCopy...) {
size += int64(len(cur))
if size > maxSizeBytes {
return fmt.Errorf("transaction data size exceeds maximum %d", maxSizeBytes)
}
}
// make a defensive copy of otxs so that the order of
// the caller's data is not altered.
otxsCopy := sortedCopy(otxs)
if ix, ok := containsAll(otxsCopy, unmodifiedCopy); !ok {
return fmt.Errorf("new transaction incorrectly marked as removed, transaction hash: %x", unmodifiedCopy[ix].Hash())
}
if ix, ok := containsAll(otxsCopy, removedCopy); !ok {
return fmt.Errorf("new transaction incorrectly marked as removed, transaction hash: %x", removedCopy[ix].Hash())
}
if ix, ok := containsAny(otxsCopy, addedCopy); ok {
return fmt.Errorf("existing transaction incorrectly marked as added, transaction hash: %x", addedCopy[ix].Hash())
}
return nil
}
func sortedCopy(txs Txs) Txs {
cp := make(Txs, len(txs))
copy(cp, txs)
sort.Sort(cp)
return cp
}
// containsAny checks that list a contains one of the transactions in list
// b. If a match is found, the index in b of the matching transaction is returned.
// Both lists must be sorted.
func containsAny(a, b []Tx) (int, bool) {
for i, cur := range b {
if _, ok := contains(a, cur); ok {
return i, true
}
}
return -1, false
}
// containsAll checks that super contains all of the transactions in the sub
// list. If not all values in sub are present in super, the index in sub of the
// first Tx absent from super is returned.
func containsAll(super, sub Txs) (int, bool) {
for i, cur := range sub {
if _, ok := contains(super, cur); !ok {
return i, false
}
}
return -1, true
}
// contains checks that the sorted list, set contains elem. If set does contain elem, then the
// index in set of elem is returned.
func contains(set []Tx, elem Tx) (int, bool) {
n := sort.Search(len(set), func(i int) bool {
return bytes.Compare(elem, set[i]) <= 0
})
if n == len(set) || !bytes.Equal(elem, set[n]) {
return -1, false
}
return n, true
}
// TxProof represents a Merkle proof of the presence of a transaction in the Merkle tree.
type TxProof struct {
RootHash tmbytes.HexBytes `json:"root_hash"`

View File

@@ -8,7 +8,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
tmrand "github.com/tendermint/tendermint/libs/rand"
ctest "github.com/tendermint/tendermint/libs/test"
tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
@@ -48,179 +47,6 @@ func TestTxIndexByHash(t *testing.T) {
}
}
func TestValidateTxRecordSet(t *testing.T) {
t.Run("should error on total transaction size exceeding max data size", func(t *testing.T) {
trs := []*abci.TxRecord{
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{1, 2, 3, 4, 5}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{6, 7, 8, 9, 10}),
},
}
txrSet := NewTxRecordSet(trs)
err := txrSet.Validate(9, []Tx{})
require.Error(t, err)
})
t.Run("should not error on removed transaction size exceeding max data size", func(t *testing.T) {
trs := []*abci.TxRecord{
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{1, 2, 3, 4, 5}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{6, 7, 8, 9}),
},
{
Action: abci.TxRecord_REMOVED,
Tx: Tx([]byte{10}),
},
}
txrSet := NewTxRecordSet(trs)
err := txrSet.Validate(9, []Tx{[]byte{10}})
require.NoError(t, err)
})
t.Run("should error on duplicate transactions with the same action", func(t *testing.T) {
trs := []*abci.TxRecord{
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{1, 2, 3, 4, 5}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{100}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{1, 2, 3, 4, 5}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{200}),
},
}
txrSet := NewTxRecordSet(trs)
err := txrSet.Validate(100, []Tx{})
require.Error(t, err)
})
t.Run("should error on duplicate transactions with mixed actions", func(t *testing.T) {
trs := []*abci.TxRecord{
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{1, 2, 3, 4, 5}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{100}),
},
{
Action: abci.TxRecord_REMOVED,
Tx: Tx([]byte{1, 2, 3, 4, 5}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{200}),
},
}
txrSet := NewTxRecordSet(trs)
err := txrSet.Validate(100, []Tx{})
require.Error(t, err)
})
t.Run("should error on new transactions marked UNMODIFIED", func(t *testing.T) {
trs := []*abci.TxRecord{
{
Action: abci.TxRecord_UNMODIFIED,
Tx: Tx([]byte{1, 2, 3, 4, 5}),
},
}
txrSet := NewTxRecordSet(trs)
err := txrSet.Validate(100, []Tx{})
require.Error(t, err)
})
t.Run("should error on new transactions marked REMOVED", func(t *testing.T) {
trs := []*abci.TxRecord{
{
Action: abci.TxRecord_REMOVED,
Tx: Tx([]byte{1, 2, 3, 4, 5}),
},
}
txrSet := NewTxRecordSet(trs)
err := txrSet.Validate(100, []Tx{})
require.Error(t, err)
})
t.Run("should error on existing transaction marked as ADDED", func(t *testing.T) {
trs := []*abci.TxRecord{
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{5, 4, 3, 2, 1}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{6}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{1, 2, 3, 4, 5}),
},
}
txrSet := NewTxRecordSet(trs)
err := txrSet.Validate(100, []Tx{{0}, {1, 2, 3, 4, 5}})
require.Error(t, err)
})
t.Run("should error if any transaction marked as UNKNOWN", func(t *testing.T) {
trs := []*abci.TxRecord{
{
Action: abci.TxRecord_UNKNOWN,
Tx: Tx([]byte{1, 2, 3, 4, 5}),
},
}
txrSet := NewTxRecordSet(trs)
err := txrSet.Validate(100, []Tx{})
require.Error(t, err)
})
t.Run("TxRecordSet preserves order", func(t *testing.T) {
trs := []*abci.TxRecord{
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{100}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{99}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{55}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{12}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{66}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{9}),
},
{
Action: abci.TxRecord_ADDED,
Tx: Tx([]byte{17}),
},
}
txrSet := NewTxRecordSet(trs)
err := txrSet.Validate(100, []Tx{})
require.NoError(t, err)
for i, tx := range txrSet.IncludedTxs() {
require.Equal(t, Tx(trs[i].Tx), tx)
}
})
}
func TestValidTxProof(t *testing.T) {
cases := []struct {
txs Txs