diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go index 3888b3637..e8de62d10 100644 --- a/abci/cmd/abci-cli/abci-cli.go +++ b/abci/cmd/abci-cli/abci-cli.go @@ -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...) diff --git a/abci/example/code/code.go b/abci/example/code/code.go index 988b2a93e..6d011ed9d 100644 --- a/abci/example/code/code.go +++ b/abci/example/code/code.go @@ -7,4 +7,5 @@ const ( CodeTypeBadNonce uint32 = 2 CodeTypeUnauthorized uint32 = 3 CodeTypeUnknownError uint32 = 4 + CodeTypeExecuted uint32 = 5 ) diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index 8f28e2852..aa05f2fa9 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -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 { diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index 5e2d35d3a..500d4c5c9 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -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 } diff --git a/abci/tests/server/client.go b/abci/tests/server/client.go index 0de84471a..76d315216 100644 --- a/abci/tests/server/client.go +++ b/abci/tests/server/client.go @@ -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") } } diff --git a/abci/tests/test_cli/ex1.abci b/abci/tests/test_cli/ex1.abci index f9817928a..e772593cc 100644 --- a/abci/tests/test_cli/ex1.abci +++ b/abci/tests/test_cli/ex1.abci @@ -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 diff --git a/abci/tests/test_cli/ex1.abci.out b/abci/tests/test_cli/ex1.abci.out index 2cc1ad7c1..dbc818855 100644 --- a/abci/tests/test_cli/ex1.abci.out +++ b/abci/tests/test_cli/ex1.abci.out @@ -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 diff --git a/abci/types/application.go b/abci/types/application.go index 2bc107091..0913ea463 100644 --- a/abci/types/application.go +++ b/abci/types/application.go @@ -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 { diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index 33151b17d..4f42bea2c 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -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 diff --git a/consensus/mempool_test.go b/consensus/mempool_test.go index 87321cdda..fdb0045e3 100644 --- a/consensus/mempool_test.go +++ b/consensus/mempool_test.go @@ -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( diff --git a/proto/tendermint/abci/types.proto b/proto/tendermint/abci/types.proto index 5910d5a91..bc178d370 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -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 diff --git a/state/execution.go b/state/execution.go index 0cd53e5bb..b8e328d84 100644 --- a/state/execution.go +++ b/state/execution.go @@ -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( diff --git a/state/execution_test.go b/state/execution_test.go index 1e7feaed9..c866a5372 100644 --- a/state/execution_test.go +++ b/state/execution_test.go @@ -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 -} diff --git a/test/e2e/app/app.go b/test/e2e/app/app.go index ad34fd7e4..b48242ffc 100644 --- a/test/e2e/app/app.go +++ b/test/e2e/app/app.go @@ -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. diff --git a/types/tx.go b/types/tx.go index ade9c94e4..8ed067ff7 100644 --- a/types/tx.go +++ b/types/tx.go @@ -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"` diff --git a/types/tx_test.go b/types/tx_test.go index a84b0bf36..bb53e0ea5 100644 --- a/types/tx_test.go +++ b/types/tx_test.go @@ -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