From 56fc80d66d5a1eb81f513574859530b5cc089dc7 Mon Sep 17 00:00:00 2001 From: Sergio Mena Date: Wed, 1 Jun 2022 18:53:10 +0200 Subject: [PATCH] abci: Move `app_hash` parameter from `Commit` to `FinalizeBlock` (#8664) * Removed from proto * make proto-gen * make build works * make some tests pass * Fix TestMempoolTxConcurrentWithCommit * Minor change * Update abci/types/types.go * Update internal/state/execution.go * Update test/e2e/app/state.go Co-authored-by: Callum Waters * Updated changelog and `UPGRADING.md` * Fixed abci-cli tests, and doc * Addressed @cmwaters' comments * Addressed @cmwaters' comments, part 2 Co-authored-by: Callum Waters --- CHANGELOG_PENDING.md | 3 + UPGRADING.md | 8 + abci/cmd/abci-cli/abci-cli.go | 89 ++++----- abci/example/kvstore/kvstore.go | 15 +- abci/tests/server/client.go | 18 +- abci/tests/test_cli/ex1.abci | 3 +- abci/tests/test_cli/ex1.abci.out | 21 +-- abci/tests/test_cli/ex2.abci | 3 + abci/tests/test_cli/ex2.abci.out | 15 ++ abci/types/types.pb.go | 290 ++++++++++------------------- docs/app-dev/abci-cli.md | 31 +-- internal/consensus/mempool_test.go | 40 +++- internal/consensus/replay_stubs.go | 2 +- internal/consensus/replay_test.go | 8 +- internal/state/execution.go | 48 +++-- proto/tendermint/abci/types.proto | 2 - test/e2e/app/app.go | 4 +- test/e2e/app/state.go | 15 +- 18 files changed, 297 insertions(+), 318 deletions(-) diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 797725bd7..c3e191e7e 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -30,6 +30,7 @@ Special thanks to external contributors on this release: - [tendermint/spec] \#7804 Migrate spec from [spec repo](https://github.com/tendermint/spec). - [abci] \#7984 Remove the locks preventing concurrent use of ABCI applications by Tendermint. (@tychoish) - [abci] \#8605 Remove info, log, events, gasUsed and mempoolError fields from ResponseCheckTx as they are not used by Tendermint. (@jmalicevic) + - [abci] \#8664 Move `app_hash` parameter from `Commit` to `FinalizeBlock`. (@sergio-mena) - P2P Protocol @@ -69,6 +70,8 @@ Special thanks to external contributors on this release: - [consensus] \#7711 Use the proposer timestamp for the first height instead of the genesis time. Chains will still start consensus at the genesis time. (@anca) - [cli] \#8281 Add a tool to update old config files to the latest version. (@creachadair) - [consenus] \#8514 move `RecheckTx` from the local node mempool config to a global `ConsensusParams` field in `BlockParams` (@cmwaters) +- [abci] ABCI++ [specified](https://github.com/tendermint/tendermint/tree/master/spec/abci%2B%2B). (@sergio-mena, @cmwaters, @josef-widder) +- [abci] ABCI++ [implemented](https://github.com/orgs/tendermint/projects/9). (@williambanfield, @thanethomson, @sergio-mena) ### IMPROVEMENTS diff --git a/UPGRADING.md b/UPGRADING.md index 44e589888..13582e75b 100644 --- a/UPGRADING.md +++ b/UPGRADING.md @@ -21,6 +21,14 @@ For information on how ABCI++ works, see the In particular, the simplest way to upgrade your application is described [here](https://github.com/tendermint/tendermint/blob/master/spec/abci%2B%2B/abci++_tmint_expected_behavior_002_draft.md#adapting-existing-applications-that-use-abci). +#### Moving the `app_hash` parameter + +The Application's hash (or any data representing the Application's current +state) is known by the time `FinalizeBlock` finishes its execution. +Accordingly, the `app_hash` parameter has been moved from `ResponseCommit` to +`ResponseFinalizeBlock`, since it makes sense for the Application to return +this value as soon as is it known. + #### ABCI Mutex In previous versions of ABCI, Tendermint was prevented from making diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go index b1b1b2c7e..97a5e815b 100644 --- a/abci/cmd/abci-cli/abci-cli.go +++ b/abci/cmd/abci-cli/abci-cli.go @@ -298,23 +298,23 @@ func cmdTest(cmd *cobra.Command, args []string) error { return compose( []func() error{ func() error { return servertest.InitChain(ctx, client) }, - func() error { return servertest.Commit(ctx, client, nil) }, + func() error { return servertest.Commit(ctx, client) }, func() error { return servertest.FinalizeBlock(ctx, client, [][]byte{ []byte("abc"), }, []uint32{ code.CodeTypeBadNonce, - }, nil) + }, nil, nil) }, - func() error { return servertest.Commit(ctx, client, nil) }, + func() error { return servertest.Commit(ctx, client) }, func() error { return servertest.FinalizeBlock(ctx, client, [][]byte{ {0x00}, }, []uint32{ code.CodeTypeOK, - }, nil) + }, nil, []byte{0, 0, 0, 0, 0, 0, 0, 1}) }, - func() error { return servertest.Commit(ctx, client, []byte{0, 0, 0, 0, 0, 0, 0, 1}) }, + func() error { return servertest.Commit(ctx, client) }, func() error { return servertest.FinalizeBlock(ctx, client, [][]byte{ {0x00}, @@ -330,9 +330,9 @@ func cmdTest(cmd *cobra.Command, args []string) error { code.CodeTypeOK, code.CodeTypeOK, code.CodeTypeBadNonce, - }, nil) + }, nil, []byte{0, 0, 0, 0, 0, 0, 0, 5}) }, - func() error { return servertest.Commit(ctx, client, []byte{0, 0, 0, 0, 0, 0, 0, 5}) }, + func() error { return servertest.Commit(ctx, client) }, }) } @@ -494,7 +494,7 @@ func cmdInfo(cmd *cobra.Command, args []string) error { const codeBad uint32 = 10 -// Append a new tx to application +// Append new txs to application func cmdFinalizeBlock(cmd *cobra.Command, args []string) error { if len(args) == 0 { printResponse(cmd, args, response{ @@ -515,14 +515,19 @@ func cmdFinalizeBlock(cmd *cobra.Command, args []string) error { if err != nil { return err } + resps := make([]response, 0, len(res.TxResults)+1) for _, tx := range res.TxResults { - printResponse(cmd, args, response{ + resps = append(resps, response{ Code: tx.Code, Data: tx.Data, Info: tx.Info, Log: tx.Log, }) } + resps = append(resps, response{ + Data: res.AppHash, + }) + printResponse(cmd, args, resps...) return nil } @@ -552,13 +557,11 @@ func cmdCheckTx(cmd *cobra.Command, args []string) error { // Get application Merkle root hash func cmdCommit(cmd *cobra.Command, args []string) error { - res, err := client.Commit(cmd.Context()) + _, err := client.Commit(cmd.Context()) if err != nil { return err } - printResponse(cmd, args, response{ - Data: res.Data, - }) + printResponse(cmd, args, response{}) return nil } @@ -632,44 +635,46 @@ func makeKVStoreCmd(logger log.Logger) func(*cobra.Command, []string) error { //-------------------------------------------------------------------------------- -func printResponse(cmd *cobra.Command, args []string, rsp response) { +func printResponse(cmd *cobra.Command, args []string, rsps ...response) { if flagVerbose { fmt.Println(">", cmd.Use, strings.Join(args, " ")) } - // Always print the status code. - if rsp.Code == types.CodeTypeOK { - fmt.Printf("-> code: OK\n") - } else { - fmt.Printf("-> code: %d\n", rsp.Code) + for _, rsp := range rsps { + // Always print the status code. + if rsp.Code == types.CodeTypeOK { + fmt.Printf("-> code: OK\n") + } else { + fmt.Printf("-> code: %d\n", rsp.Code) - } + } - if len(rsp.Data) != 0 { - // Do no print this line when using the commit command - // because the string comes out as gibberish - if cmd.Use != "commit" { - fmt.Printf("-> data: %s\n", rsp.Data) + if len(rsp.Data) != 0 { + // Do no print this line when using the finalize_block command + // because the string comes out as gibberish + if cmd.Use != "finalize_block" { + fmt.Printf("-> data: %s\n", rsp.Data) + } + fmt.Printf("-> data.hex: 0x%X\n", rsp.Data) + } + if rsp.Log != "" { + fmt.Printf("-> log: %s\n", rsp.Log) } - fmt.Printf("-> data.hex: 0x%X\n", rsp.Data) - } - if rsp.Log != "" { - fmt.Printf("-> log: %s\n", rsp.Log) - } - if rsp.Query != nil { - fmt.Printf("-> height: %d\n", rsp.Query.Height) - if rsp.Query.Key != nil { - fmt.Printf("-> key: %s\n", rsp.Query.Key) - fmt.Printf("-> key.hex: %X\n", rsp.Query.Key) - } - if rsp.Query.Value != nil { - fmt.Printf("-> value: %s\n", rsp.Query.Value) - fmt.Printf("-> value.hex: %X\n", rsp.Query.Value) - } - if rsp.Query.ProofOps != nil { - fmt.Printf("-> proof: %#v\n", rsp.Query.ProofOps) + if rsp.Query != nil { + fmt.Printf("-> height: %d\n", rsp.Query.Height) + if rsp.Query.Key != nil { + fmt.Printf("-> key: %s\n", rsp.Query.Key) + fmt.Printf("-> key.hex: %X\n", rsp.Query.Key) + } + if rsp.Query.Value != nil { + fmt.Printf("-> value: %s\n", rsp.Query.Value) + fmt.Printf("-> value.hex: %X\n", rsp.Query.Value) + } + if rsp.Query.ProofOps != nil { + fmt.Printf("-> proof: %#v\n", rsp.Query.ProofOps) + } } } } diff --git a/abci/example/kvstore/kvstore.go b/abci/example/kvstore/kvstore.go index 61d129239..bbb2fbe34 100644 --- a/abci/example/kvstore/kvstore.go +++ b/abci/example/kvstore/kvstore.go @@ -196,7 +196,13 @@ func (app *Application) FinalizeBlock(_ context.Context, req *types.RequestFinal respTxs[i] = app.handleTx(tx) } - return &types.ResponseFinalizeBlock{TxResults: respTxs, ValidatorUpdates: app.ValUpdates}, nil + // Using a memdb - just return the big endian size of the db + appHash := make([]byte, 8) + binary.PutVarint(appHash, app.state.Size) + app.state.AppHash = appHash + app.state.Height++ + + return &types.ResponseFinalizeBlock{TxResults: respTxs, ValidatorUpdates: app.ValUpdates, AppHash: appHash}, nil } func (*Application) CheckTx(_ context.Context, req *types.RequestCheckTx) (*types.ResponseCheckTx, error) { @@ -207,14 +213,9 @@ func (app *Application) Commit(_ context.Context) (*types.ResponseCommit, error) app.mu.Lock() defer app.mu.Unlock() - // Using a memdb - just return the big endian size of the db - appHash := make([]byte, 8) - binary.PutVarint(appHash, app.state.Size) - app.state.AppHash = appHash - app.state.Height++ saveState(app.state) - resp := &types.ResponseCommit{Data: appHash} + resp := &types.ResponseCommit{} if app.RetainBlocks > 0 && app.state.Height >= app.RetainBlocks { resp.RetainHeight = app.state.Height - app.RetainBlocks + 1 } diff --git a/abci/tests/server/client.go b/abci/tests/server/client.go index eeae74746..cddb42ec0 100644 --- a/abci/tests/server/client.go +++ b/abci/tests/server/client.go @@ -32,25 +32,20 @@ func InitChain(ctx context.Context, client abciclient.Client) error { return nil } -func Commit(ctx context.Context, client abciclient.Client, hashExp []byte) error { - res, err := client.Commit(ctx) - data := res.Data +func Commit(ctx context.Context, client abciclient.Client) error { + _, err := client.Commit(ctx) if err != nil { fmt.Println("Failed test: Commit") fmt.Printf("error while committing: %v\n", err) return err } - if !bytes.Equal(data, hashExp) { - fmt.Println("Failed test: Commit") - fmt.Printf("Commit hash was unexpected. Got %X expected %X\n", data, hashExp) - return errors.New("commitTx failed") - } fmt.Println("Passed test: Commit") return nil } -func FinalizeBlock(ctx context.Context, client abciclient.Client, txBytes [][]byte, codeExp []uint32, dataExp []byte) error { +func FinalizeBlock(ctx context.Context, client abciclient.Client, txBytes [][]byte, codeExp []uint32, dataExp []byte, hashExp []byte) error { res, _ := client.FinalizeBlock(ctx, &types.RequestFinalizeBlock{Txs: txBytes}) + appHash := res.AppHash for i, tx := range res.TxResults { code, data, log := tx.Code, tx.Data, tx.Log if code != codeExp[i] { @@ -66,6 +61,11 @@ func FinalizeBlock(ctx context.Context, client abciclient.Client, txBytes [][]by return errors.New("FinalizeBlock error") } } + if !bytes.Equal(appHash, hashExp) { + fmt.Println("Failed test: FinalizeBlock") + fmt.Printf("Application hash was unexpected. Got %X expected %X\n", appHash, hashExp) + return errors.New("FinalizeBlock error") + } fmt.Println("Passed test: FinalizeBlock") return nil } diff --git a/abci/tests/test_cli/ex1.abci b/abci/tests/test_cli/ex1.abci index 09457189e..56355dc94 100644 --- a/abci/tests/test_cli/ex1.abci +++ b/abci/tests/test_cli/ex1.abci @@ -1,9 +1,8 @@ echo hello info -commit finalize_block "abc" -info commit +info query "abc" finalize_block "def=xyz" "ghi=123" commit diff --git a/abci/tests/test_cli/ex1.abci.out b/abci/tests/test_cli/ex1.abci.out index c004ab059..9a35290b0 100644 --- a/abci/tests/test_cli/ex1.abci.out +++ b/abci/tests/test_cli/ex1.abci.out @@ -8,26 +8,23 @@ -> data: {"size":0} -> data.hex: 0x7B2273697A65223A307D -> commit --> code: OK --> data.hex: 0x0000000000000000 - > finalize_block "abc" -> code: OK +-> code: OK +-> data.hex: 0x0200000000000000 + +> commit +-> code: OK > info -> code: OK -> data: {"size":1} -> data.hex: 0x7B2273697A65223A317D -> commit --> code: OK --> data.hex: 0x0200000000000000 - > query "abc" -> code: OK -> log: exists --> height: 2 +-> height: 1 -> key: abc -> key.hex: 616263 -> value: abc @@ -35,17 +32,17 @@ > finalize_block "def=xyz" "ghi=123" -> code: OK -> finalize_block "def=xyz" "ghi=123" -> code: OK +-> code: OK +-> data.hex: 0x0600000000000000 > commit -> code: OK --> data.hex: 0x0600000000000000 > query "def" -> code: OK -> log: exists --> height: 3 +-> height: 2 -> key: def -> key.hex: 646566 -> value: xyz diff --git a/abci/tests/test_cli/ex2.abci b/abci/tests/test_cli/ex2.abci index 90e99c2f9..1cabba151 100644 --- a/abci/tests/test_cli/ex2.abci +++ b/abci/tests/test_cli/ex2.abci @@ -1,7 +1,10 @@ check_tx 0x00 check_tx 0xff finalize_block 0x00 +commit check_tx 0x00 finalize_block 0x01 +commit finalize_block 0x04 +commit info diff --git a/abci/tests/test_cli/ex2.abci.out b/abci/tests/test_cli/ex2.abci.out index aab0b1966..e29a35368 100644 --- a/abci/tests/test_cli/ex2.abci.out +++ b/abci/tests/test_cli/ex2.abci.out @@ -6,15 +6,30 @@ > finalize_block 0x00 -> code: OK +-> code: OK +-> data.hex: 0x0200000000000000 + +> commit +-> code: OK > check_tx 0x00 -> code: OK > finalize_block 0x01 -> code: OK +-> code: OK +-> data.hex: 0x0400000000000000 + +> commit +-> code: OK > finalize_block 0x04 -> code: OK +-> code: OK +-> data.hex: 0x0600000000000000 + +> commit +-> code: OK > info -> code: OK diff --git a/abci/types/types.pb.go b/abci/types/types.pb.go index 77d515bbe..946cfa6af 100644 --- a/abci/types/types.pb.go +++ b/abci/types/types.pb.go @@ -2382,8 +2382,7 @@ func (m *ResponseDeliverTx) GetCodespace() string { type ResponseCommit struct { // reserve 1 - Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` - RetainHeight int64 `protobuf:"varint,3,opt,name=retain_height,json=retainHeight,proto3" json:"retain_height,omitempty"` + RetainHeight int64 `protobuf:"varint,3,opt,name=retain_height,json=retainHeight,proto3" json:"retain_height,omitempty"` } func (m *ResponseCommit) Reset() { *m = ResponseCommit{} } @@ -2419,13 +2418,6 @@ func (m *ResponseCommit) XXX_DiscardUnknown() { var xxx_messageInfo_ResponseCommit proto.InternalMessageInfo -func (m *ResponseCommit) GetData() []byte { - if m != nil { - return m.Data - } - return nil -} - func (m *ResponseCommit) GetRetainHeight() int64 { if m != nil { return m.RetainHeight @@ -2871,7 +2863,6 @@ type ResponseFinalizeBlock struct { ValidatorUpdates []ValidatorUpdate `protobuf:"bytes,3,rep,name=validator_updates,json=validatorUpdates,proto3" json:"validator_updates"` ConsensusParamUpdates *types1.ConsensusParams `protobuf:"bytes,4,opt,name=consensus_param_updates,json=consensusParamUpdates,proto3" json:"consensus_param_updates,omitempty"` AppHash []byte `protobuf:"bytes,5,opt,name=app_hash,json=appHash,proto3" json:"app_hash,omitempty"` - RetainHeight int64 `protobuf:"varint,6,opt,name=retain_height,json=retainHeight,proto3" json:"retain_height,omitempty"` } func (m *ResponseFinalizeBlock) Reset() { *m = ResponseFinalizeBlock{} } @@ -2942,13 +2933,6 @@ func (m *ResponseFinalizeBlock) GetAppHash() []byte { return nil } -func (m *ResponseFinalizeBlock) GetRetainHeight() int64 { - if m != nil { - return m.RetainHeight - } - return 0 -} - type CommitInfo struct { Round int32 `protobuf:"varint,1,opt,name=round,proto3" json:"round,omitempty"` Votes []VoteInfo `protobuf:"bytes,2,rep,name=votes,proto3" json:"votes"` @@ -3845,13 +3829,13 @@ func init() { func init() { proto.RegisterFile("tendermint/abci/types.proto", fileDescriptor_252557cfdd89a31a) } var fileDescriptor_252557cfdd89a31a = []byte{ - // 3263 bytes of a gzipped FileDescriptorProto + // 3253 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5a, 0xcd, 0x73, 0x23, 0xd5, 0x11, 0xd7, 0xe8, 0x5b, 0xad, 0xaf, 0xf1, 0xb3, 0x59, 0xb4, 0x62, 0xd7, 0x36, 0x43, 0x01, 0xcb, 0x02, 0x36, 0xf1, 0x66, 0x61, 0xc9, 0x42, 0x28, 0x5b, 0xd6, 0x46, 0xf6, 0x7a, 0x6d, 0x33, 0x96, 0x4d, 0x91, 0x0f, 0x86, 0xb1, 0xf4, 0x6c, 0x0d, 0x2b, 0x69, 0x86, 0x99, 0x91, 0x91, 0x39, 0x26, - 0xc5, 0x85, 0x43, 0xc2, 0x25, 0x95, 0xa4, 0x2a, 0xdc, 0x92, 0xaa, 0xe4, 0x3f, 0x48, 0x2e, 0x39, - 0xe5, 0xc0, 0x21, 0x07, 0x4e, 0xa9, 0x9c, 0x48, 0x0a, 0x6e, 0xf9, 0x07, 0x72, 0x4b, 0xa5, 0xde, + 0xc5, 0x85, 0x43, 0x8a, 0x4b, 0x2a, 0x49, 0x55, 0xb8, 0x25, 0x55, 0xc9, 0x7f, 0x90, 0x5c, 0x72, + 0xca, 0x81, 0x43, 0x0e, 0x9c, 0x52, 0x39, 0x91, 0x14, 0xdc, 0xf8, 0x07, 0x72, 0x4b, 0xa5, 0xde, 0xc7, 0x7c, 0x49, 0x33, 0xfa, 0x00, 0x8a, 0xaa, 0x54, 0x71, 0x9b, 0xd7, 0xd3, 0xdd, 0xef, 0xab, 0x5f, 0x77, 0xff, 0xfa, 0x3d, 0x78, 0xcc, 0xc6, 0xfd, 0x36, 0x36, 0x7b, 0x5a, 0xdf, 0x5e, 0x57, 0x4f, 0x5b, 0xda, 0xba, 0x7d, 0x69, 0x60, 0x6b, 0xcd, 0x30, 0x75, 0x5b, 0x47, 0x65, 0xef, 0xe7, @@ -3859,7 +3843,7 @@ var fileDescriptor_252557cfdd89a31a = []byte{ 0x31, 0xfe, 0xea, 0xb5, 0xf1, 0xdf, 0x0f, 0xf1, 0x25, 0xd7, 0x16, 0x10, 0xa6, 0xbd, 0xac, 0x1b, 0xaa, 0xa9, 0xf6, 0x9c, 0xdf, 0x2b, 0xe7, 0xba, 0x7e, 0xde, 0xc5, 0xeb, 0xb4, 0x75, 0x3a, 0x38, 0x5b, 0xb7, 0xb5, 0x1e, 0xb6, 0x6c, 0xb5, 0x67, 0x70, 0x86, 0xa5, 0x73, 0xfd, 0x5c, 0xa7, 0x9f, - 0xeb, 0xe4, 0x8b, 0x51, 0xa5, 0x3f, 0xe7, 0x20, 0x23, 0xe3, 0x77, 0x07, 0xd8, 0xb2, 0xd1, 0x06, + 0xeb, 0xe4, 0x8b, 0x51, 0xa5, 0xbf, 0xe4, 0x20, 0x23, 0xe3, 0x77, 0x07, 0xd8, 0xb2, 0xd1, 0x06, 0x24, 0x71, 0xab, 0xa3, 0x57, 0x84, 0x55, 0xe1, 0x46, 0x7e, 0xe3, 0xda, 0xda, 0xc8, 0xf0, 0xd7, 0x38, 0x5f, 0xbd, 0xd5, 0xd1, 0x1b, 0x31, 0x99, 0xf2, 0xa2, 0xdb, 0x90, 0x3a, 0xeb, 0x0e, 0xac, 0x4e, 0x25, 0x4e, 0x85, 0xae, 0x47, 0x09, 0xdd, 0x23, 0x4c, 0x8d, 0x98, 0xcc, 0xb8, 0x49, 0x57, @@ -3872,7 +3856,7 @@ var fileDescriptor_252557cfdd89a31a = []byte{ 0x39, 0x52, 0x96, 0x72, 0x35, 0x62, 0x32, 0xe7, 0x47, 0xfb, 0x50, 0xea, 0x6a, 0x96, 0xad, 0x58, 0x7d, 0xd5, 0xb0, 0x3a, 0xba, 0x6d, 0x55, 0xf2, 0x54, 0xc3, 0x93, 0x51, 0x1a, 0xf6, 0x34, 0xcb, 0x3e, 0x72, 0x98, 0x1b, 0x31, 0xb9, 0xd8, 0xf5, 0x13, 0x88, 0x3e, 0xfd, 0xec, 0x0c, 0x9b, 0xae, - 0xc2, 0x4a, 0x61, 0xb2, 0xbe, 0x03, 0xc2, 0xed, 0xc8, 0x13, 0x7d, 0xba, 0x9f, 0x80, 0x7e, 0x04, + 0xc2, 0x4a, 0x61, 0xb2, 0xbe, 0x03, 0xc2, 0xed, 0xc8, 0x13, 0x7d, 0xba, 0x9f, 0x80, 0x7e, 0x02, 0x8b, 0x5d, 0x5d, 0x6d, 0xbb, 0xea, 0x94, 0x56, 0x67, 0xd0, 0x7f, 0x58, 0x29, 0x52, 0xa5, 0xcf, 0x44, 0x0e, 0x52, 0x57, 0xdb, 0x8e, 0x8a, 0x1a, 0x11, 0x68, 0xc4, 0xe4, 0x85, 0xee, 0x28, 0x11, 0xbd, 0x05, 0x4b, 0xaa, 0x61, 0x74, 0x2f, 0x47, 0xb5, 0x97, 0xa8, 0xf6, 0x9b, 0x51, 0xda, 0x37, @@ -3892,9 +3876,9 @@ var fileDescriptor_252557cfdd89a31a = []byte{ 0x2e, 0xc9, 0x9b, 0xe8, 0x09, 0x28, 0xd2, 0x49, 0x28, 0xce, 0x7f, 0xe2, 0xe6, 0x92, 0x72, 0x81, 0x12, 0x4f, 0x38, 0xd3, 0x0a, 0xe4, 0x8d, 0x0d, 0xc3, 0x65, 0x49, 0x50, 0x16, 0x30, 0x36, 0x0c, 0x87, 0xe1, 0x71, 0x28, 0x90, 0x19, 0xbb, 0x1c, 0x49, 0xda, 0x49, 0x9e, 0xd0, 0x38, 0x8b, 0xf4, - 0xb7, 0x38, 0x88, 0xa3, 0xae, 0x0b, 0xdd, 0x81, 0x24, 0xf1, 0xe2, 0xdc, 0x21, 0x57, 0xd7, 0x98, + 0xf7, 0x38, 0x88, 0xa3, 0xae, 0x0b, 0xdd, 0x81, 0x24, 0xf1, 0xe2, 0xdc, 0x21, 0x57, 0xd7, 0x98, 0x8b, 0x5f, 0x73, 0x5c, 0xfc, 0x5a, 0xd3, 0x71, 0xf1, 0x5b, 0xd9, 0x4f, 0x3e, 0x5b, 0x89, 0x7d, - 0xf4, 0xcf, 0x15, 0x41, 0xa6, 0x12, 0xe8, 0x2a, 0x71, 0x58, 0xaa, 0xd6, 0x57, 0xb4, 0x36, 0x1d, + 0xf4, 0xaf, 0x15, 0x41, 0xa6, 0x12, 0xe8, 0x2a, 0x71, 0x58, 0xaa, 0xd6, 0x57, 0xb4, 0x36, 0x1d, 0x72, 0x8e, 0x78, 0x23, 0x55, 0xeb, 0xef, 0xb4, 0xd1, 0x1e, 0x88, 0x2d, 0xbd, 0x6f, 0xe1, 0xbe, 0x35, 0xb0, 0x14, 0x16, 0x42, 0xb8, 0x1b, 0x0e, 0x38, 0x53, 0x16, 0xc8, 0x6a, 0x0e, 0xe7, 0x21, 0x65, 0x94, 0xcb, 0xad, 0x20, 0x01, 0xdd, 0x03, 0xb8, 0x50, 0xbb, 0x5a, 0x5b, 0xb5, 0x75, 0xd3, @@ -3914,7 +3898,7 @@ var fileDescriptor_252557cfdd89a31a = []byte{ 0xe7, 0xf6, 0x2d, 0x98, 0x40, 0xcd, 0xde, 0x59, 0xb0, 0x2b, 0x90, 0x3e, 0xd3, 0xcd, 0x9e, 0x6a, 0x53, 0x65, 0x45, 0x99, 0xb7, 0xc8, 0x42, 0x32, 0x2f, 0x9e, 0xa0, 0x64, 0xd6, 0x90, 0x14, 0xb8, 0x1a, 0xe9, 0xbd, 0x89, 0x88, 0xd6, 0x6f, 0x63, 0xb6, 0xac, 0x45, 0x99, 0x35, 0x3c, 0x45, 0x6c, - 0xb0, 0xac, 0x41, 0xba, 0xb5, 0xe8, 0x5c, 0xa9, 0xfe, 0x9c, 0xcc, 0x5b, 0xd2, 0x1f, 0x13, 0x70, + 0xb0, 0xac, 0x41, 0xba, 0xb5, 0xe8, 0x5c, 0xa9, 0xfe, 0x9c, 0xcc, 0x5b, 0xd2, 0x9f, 0x12, 0x70, 0x25, 0xdc, 0x87, 0xa3, 0x55, 0x28, 0xf4, 0xd4, 0xa1, 0x62, 0x0f, 0xb9, 0xd9, 0x09, 0x74, 0xe3, 0xa1, 0xa7, 0x0e, 0x9b, 0x43, 0x66, 0x73, 0x22, 0x24, 0xec, 0xa1, 0x55, 0x89, 0xaf, 0x26, 0x6e, 0x14, 0x64, 0xf2, 0x89, 0x8e, 0x61, 0xa1, 0xab, 0xb7, 0xd4, 0xae, 0xd2, 0x55, 0x2d, 0x5b, 0xe1, @@ -3925,22 +3909,22 @@ var fileDescriptor_252557cfdd89a31a = []byte{ 0xc7, 0xbd, 0xa4, 0xe7, 0x76, 0x2f, 0x2f, 0xc0, 0x52, 0x1f, 0x0f, 0x6d, 0xdf, 0x18, 0x99, 0xe1, 0x64, 0xe8, 0x5e, 0x20, 0xf2, 0xcf, 0xeb, 0x9f, 0xd8, 0x10, 0x7a, 0x86, 0x86, 0x45, 0x43, 0xb7, 0xb0, 0xa9, 0xa8, 0xed, 0xb6, 0x89, 0x2d, 0xab, 0x92, 0xa5, 0xdc, 0x65, 0x87, 0xbe, 0xc9, 0xc8, - 0xd2, 0x6f, 0xfc, 0x7b, 0x15, 0x0c, 0x83, 0x7c, 0x27, 0x04, 0x6f, 0x27, 0x8e, 0x60, 0x89, 0xcb, + 0xd2, 0x6f, 0xfd, 0x7b, 0x15, 0x0c, 0x83, 0x7c, 0x27, 0x04, 0x6f, 0x27, 0x8e, 0x60, 0x89, 0xcb, 0xb7, 0x03, 0x9b, 0xc1, 0xd2, 0xd1, 0xc7, 0xc6, 0x0f, 0xdc, 0xe8, 0x26, 0x20, 0x47, 0x7c, 0x86, - 0x7d, 0x48, 0x7c, 0xb5, 0x7d, 0x40, 0x90, 0xa4, 0xab, 0x94, 0x64, 0x4e, 0x88, 0x7c, 0xff, 0xbf, + 0x7d, 0x48, 0x7c, 0xbd, 0x7d, 0x40, 0x90, 0xa4, 0xab, 0x94, 0x64, 0x4e, 0x88, 0x7c, 0xff, 0xbf, 0xed, 0xcd, 0x6b, 0xb0, 0x30, 0x96, 0x63, 0xb8, 0xf3, 0x12, 0x42, 0xe7, 0x15, 0xf7, 0xcf, 0x4b, - 0xfa, 0xad, 0x00, 0xd5, 0xe8, 0xa4, 0x22, 0x54, 0xd5, 0xb3, 0xb0, 0xe0, 0xce, 0xc5, 0x1d, 0x1f, + 0xfa, 0x9d, 0x00, 0xd5, 0xe8, 0xa4, 0x22, 0x54, 0xd5, 0xb3, 0xb0, 0xe0, 0xce, 0xc5, 0x1d, 0x1f, 0x3b, 0xf5, 0xa2, 0xfb, 0x83, 0x0f, 0x30, 0xd2, 0x81, 0x3f, 0x09, 0xa5, 0x91, 0x94, 0x87, 0xed, - 0x42, 0xf1, 0xc2, 0xdf, 0xbf, 0xf4, 0xcb, 0x84, 0xeb, 0x55, 0x03, 0x79, 0x49, 0x88, 0xe5, 0xbd, - 0x0e, 0x8b, 0x6d, 0xdc, 0xd2, 0xda, 0x5f, 0xd6, 0xf0, 0x16, 0xb8, 0xf4, 0xb7, 0x76, 0x37, 0x83, - 0xdd, 0xfd, 0x1c, 0x20, 0x2b, 0x63, 0xcb, 0x20, 0xd9, 0x07, 0xda, 0x82, 0x1c, 0x1e, 0xb6, 0xb0, + 0x42, 0xf1, 0xc2, 0xdf, 0xbf, 0xf4, 0xab, 0x84, 0xeb, 0x55, 0x03, 0x79, 0x49, 0x88, 0xe5, 0xbd, + 0x0e, 0x8b, 0x6d, 0xdc, 0xd2, 0xda, 0x5f, 0xd5, 0xf0, 0x16, 0xb8, 0xf4, 0x77, 0x76, 0x37, 0x83, + 0xdd, 0xfd, 0x12, 0x20, 0x2b, 0x63, 0xcb, 0x20, 0xd9, 0x07, 0xda, 0x82, 0x1c, 0x1e, 0xb6, 0xb0, 0x61, 0x3b, 0x09, 0x5b, 0x78, 0x2a, 0xcc, 0xb8, 0xeb, 0x0e, 0x27, 0x01, 0x82, 0xae, 0x18, 0xba, 0xc5, 0xb1, 0x6e, 0x34, 0x6c, 0xe5, 0xe2, 0x7e, 0xb0, 0xfb, 0xa2, 0x03, 0x76, 0x13, 0x91, 0x38, 0x8e, 0x49, 0x8d, 0xa0, 0xdd, 0x5b, 0x1c, 0xed, 0x26, 0xa7, 0x74, 0x16, 0x80, 0xbb, 0xb5, 0x00, 0xdc, 0x4d, 0x4d, 0x99, 0x66, 0x04, 0xde, 0x7d, 0xd1, 0xc1, 0xbb, 0xe9, 0x29, 0x23, 0x1e, 0x01, 0xbc, 0xaf, 0xfa, 0x00, 0x6f, 0x96, 0x8a, 0xae, 0x46, 0x8a, 0x86, 0x20, 0xde, 0x97, 0x5d, 0xc4, 0x9b, 0x8f, 0x44, 0xcb, 0x5c, 0x78, 0x14, 0xf2, 0x1e, 0x8c, 0x41, 0x5e, 0x06, 0x51, 0x9f, 0x8a, - 0x54, 0x31, 0x05, 0xf3, 0x1e, 0x8c, 0x61, 0xde, 0xe2, 0x14, 0x85, 0x53, 0x40, 0xef, 0x8f, 0xc3, + 0x54, 0x31, 0x05, 0xf3, 0x1e, 0x8c, 0x61, 0xde, 0xe2, 0x14, 0x85, 0x53, 0x40, 0xef, 0x4f, 0xc3, 0x41, 0x6f, 0x34, 0x2c, 0xe5, 0xc3, 0x9c, 0x0d, 0xf5, 0x2a, 0x11, 0xa8, 0xb7, 0x1c, 0x89, 0xd0, 0x98, 0xfa, 0x99, 0x61, 0xef, 0x71, 0x08, 0xec, 0x65, 0x00, 0xf5, 0x46, 0xa4, 0xf2, 0x19, 0x70, 0xef, 0x71, 0x08, 0xee, 0x5d, 0x98, 0xaa, 0x76, 0x2a, 0xf0, 0xbd, 0x17, 0x04, 0xbe, 0x28, 0x22, @@ -3948,12 +3932,12 @@ var fileDescriptor_252557cfdd89a31a = []byte{ 0x0e, 0xe8, 0x7b, 0x30, 0x06, 0x7d, 0x97, 0xa6, 0x58, 0xda, 0xec, 0xd8, 0x37, 0x23, 0x66, 0x19, 0xea, 0xdd, 0x4d, 0x66, 0x41, 0xcc, 0x4b, 0xcf, 0x90, 0x40, 0x3c, 0xe2, 0xe1, 0x48, 0x4e, 0x8c, 0x4d, 0x53, 0x37, 0x39, 0x8a, 0x65, 0x0d, 0xe9, 0x06, 0xc1, 0x42, 0x9e, 0x37, 0x9b, 0x80, 0x93, - 0x29, 0xf6, 0xf0, 0x79, 0x30, 0xe9, 0x4f, 0x82, 0x27, 0x4b, 0x91, 0xb2, 0x1f, 0x47, 0xe5, 0x38, + 0x29, 0xf6, 0xf0, 0x79, 0x30, 0xe9, 0xcf, 0x82, 0x27, 0x4b, 0x91, 0xb2, 0x1f, 0x47, 0xe5, 0x38, 0x8e, 0xf2, 0xa1, 0xe7, 0x78, 0x10, 0x3d, 0xaf, 0x40, 0x9e, 0x60, 0x8a, 0x11, 0x60, 0xac, 0x1a, 0x2e, 0x30, 0xbe, 0x09, 0x0b, 0x34, 0x76, 0x32, 0x8c, 0xcd, 0x03, 0x52, 0x92, 0x06, 0xa4, 0x32, 0xf9, 0xc1, 0xd6, 0x85, 0x45, 0xa6, 0xe7, 0x61, 0xd1, 0xc7, 0xeb, 0x62, 0x15, 0x86, 0x12, 0x45, - 0x97, 0x7b, 0x93, 0x83, 0x96, 0xbf, 0x0a, 0xde, 0x0a, 0x79, 0x88, 0x3a, 0x0c, 0xfc, 0x0a, 0x5f, - 0x13, 0xf8, 0x8d, 0x7f, 0x69, 0xf0, 0xeb, 0xc7, 0x5e, 0x89, 0x20, 0xf6, 0xfa, 0x8f, 0xe0, 0xed, + 0x97, 0x7b, 0x93, 0x83, 0x96, 0xbf, 0x09, 0xde, 0x0a, 0x79, 0x88, 0x3a, 0x0c, 0xfc, 0x0a, 0xdf, + 0x10, 0xf8, 0x8d, 0x7f, 0x65, 0xf0, 0xeb, 0xc7, 0x5e, 0x89, 0x20, 0xf6, 0xfa, 0x8f, 0xe0, 0xed, 0x89, 0x0b, 0x65, 0x5b, 0x7a, 0x1b, 0x73, 0x34, 0x44, 0xbf, 0x49, 0x76, 0xd2, 0xd5, 0xcf, 0x39, 0xe6, 0x21, 0x9f, 0x84, 0xcb, 0x0d, 0x39, 0x39, 0x1e, 0x51, 0x5c, 0x20, 0xc5, 0x42, 0x3e, 0x07, 0x52, 0x22, 0x24, 0x1e, 0x62, 0x16, 0x20, 0x0a, 0x32, 0xf9, 0x24, 0x7c, 0xd4, 0xec, 0x78, 0xe8, @@ -3968,88 +3952,88 @@ var fileDescriptor_252557cfdd89a31a = []byte{ 0x67, 0x8b, 0xdb, 0xb8, 0xab, 0x5d, 0x60, 0x73, 0x8e, 0xc9, 0xcc, 0xb6, 0xb9, 0xcb, 0x21, 0x53, 0xf6, 0x51, 0xc8, 0xe8, 0x49, 0x6b, 0x60, 0xe1, 0x36, 0x2f, 0x99, 0xb8, 0x6d, 0xd4, 0x80, 0x34, 0xbe, 0xc0, 0x7d, 0xdb, 0xaa, 0x64, 0xa8, 0x0d, 0x5f, 0x19, 0xc7, 0xb0, 0xe4, 0xf7, 0x56, 0x85, - 0x58, 0xee, 0xbf, 0x3f, 0x5b, 0x11, 0x19, 0xf7, 0x73, 0x7a, 0x4f, 0xb3, 0x71, 0xcf, 0xb0, 0x2f, - 0x65, 0x2e, 0x3f, 0x79, 0x65, 0xa5, 0x1d, 0x28, 0x05, 0xe3, 0x7e, 0xe8, 0x7c, 0x9f, 0x80, 0xa2, - 0x89, 0x6d, 0x55, 0xeb, 0x2b, 0x81, 0x4c, 0xbe, 0xc0, 0x88, 0xbc, 0xc0, 0x73, 0x08, 0x8f, 0x84, - 0xc6, 0x7f, 0xf4, 0x12, 0xe4, 0xbc, 0xd4, 0x41, 0xa0, 0xd3, 0x99, 0x50, 0xff, 0xf0, 0x78, 0xa5, - 0xbf, 0x08, 0x9e, 0xca, 0x60, 0x45, 0xa5, 0x0e, 0x69, 0x13, 0x5b, 0x83, 0x2e, 0xab, 0x71, 0x94, - 0x36, 0x9e, 0x9f, 0x2d, 0x73, 0x20, 0xd4, 0x41, 0xd7, 0x96, 0xb9, 0xb0, 0xf4, 0x16, 0xa4, 0x19, - 0x05, 0xe5, 0x21, 0x73, 0xbc, 0x7f, 0x7f, 0xff, 0xe0, 0x8d, 0x7d, 0x31, 0x86, 0x00, 0xd2, 0x9b, - 0xb5, 0x5a, 0xfd, 0xb0, 0x29, 0x0a, 0x28, 0x07, 0xa9, 0xcd, 0xad, 0x03, 0xb9, 0x29, 0xc6, 0x09, - 0x59, 0xae, 0xef, 0xd6, 0x6b, 0x4d, 0x31, 0x81, 0x16, 0xa0, 0xc8, 0xbe, 0x95, 0x7b, 0x07, 0xf2, - 0x83, 0xcd, 0xa6, 0x98, 0xf4, 0x91, 0x8e, 0xea, 0xfb, 0xdb, 0x75, 0x59, 0x4c, 0x49, 0xdf, 0x81, - 0xab, 0x91, 0xb9, 0x86, 0x57, 0x2e, 0x11, 0x7c, 0xe5, 0x12, 0xe9, 0xd7, 0x71, 0x82, 0xc6, 0xa2, - 0x12, 0x08, 0xb4, 0x3b, 0x32, 0xf1, 0x8d, 0x39, 0xb2, 0x8f, 0x91, 0xd9, 0x13, 0x00, 0x66, 0xe2, - 0x33, 0x6c, 0xb7, 0x3a, 0x2c, 0xa1, 0x61, 0xfe, 0xb2, 0x28, 0x17, 0x39, 0x95, 0x0a, 0x59, 0x8c, - 0xed, 0x1d, 0xdc, 0xb2, 0x15, 0x76, 0xea, 0x18, 0xf8, 0xc9, 0x11, 0x36, 0x42, 0x3d, 0x62, 0x44, - 0xe9, 0xed, 0xb9, 0xd6, 0x32, 0x07, 0x29, 0xb9, 0xde, 0x94, 0xdf, 0x14, 0x13, 0x08, 0x41, 0x89, - 0x7e, 0x2a, 0x47, 0xfb, 0x9b, 0x87, 0x47, 0x8d, 0x03, 0xb2, 0x96, 0x8b, 0x50, 0x76, 0xd6, 0xd2, - 0x21, 0xa6, 0xa4, 0xbf, 0xc7, 0xe1, 0xd1, 0x88, 0xf4, 0x07, 0xdd, 0x01, 0xb0, 0x87, 0x8a, 0x89, - 0x5b, 0xba, 0xd9, 0x8e, 0x36, 0xb2, 0xe6, 0x50, 0xa6, 0x1c, 0x72, 0xce, 0xe6, 0x5f, 0xd6, 0x84, - 0x2a, 0x1b, 0x7a, 0x85, 0x2b, 0x25, 0xb3, 0x72, 0x20, 0xdf, 0xf5, 0x90, 0x62, 0x12, 0x6e, 0x11, - 0xc5, 0x74, 0x6d, 0xa9, 0x62, 0xca, 0x8f, 0x1e, 0xf8, 0x41, 0xf2, 0x80, 0x06, 0x9a, 0x99, 0xcb, - 0xb1, 0x3e, 0x18, 0xcd, 0x08, 0x16, 0x7a, 0x13, 0x1e, 0x1d, 0x89, 0x93, 0xae, 0xd2, 0xd4, 0xac, - 0xe1, 0xf2, 0x91, 0x60, 0xb8, 0xe4, 0xaa, 0xa5, 0xdf, 0x25, 0xfc, 0x0b, 0x1b, 0xcc, 0xf6, 0x0e, - 0x20, 0x6d, 0xd9, 0xaa, 0x3d, 0xb0, 0xb8, 0xc1, 0xbd, 0x34, 0x6b, 0xea, 0xb8, 0xe6, 0x7c, 0x1c, - 0x51, 0x71, 0x99, 0xab, 0xf9, 0x76, 0xbd, 0x2d, 0xe9, 0x36, 0x94, 0x82, 0x8b, 0x13, 0x7d, 0x64, - 0x3c, 0x9f, 0x13, 0x97, 0xee, 0x02, 0x1a, 0x4f, 0xaa, 0x43, 0xca, 0x28, 0x42, 0x58, 0x19, 0xe5, - 0xf7, 0x02, 0x3c, 0x36, 0x21, 0x81, 0x46, 0xaf, 0x8f, 0xec, 0xf3, 0xcb, 0xf3, 0xa4, 0xdf, 0x6b, - 0x8c, 0x16, 0xdc, 0x69, 0xe9, 0x16, 0x14, 0xfc, 0xf4, 0xd9, 0x26, 0xf9, 0x8b, 0x84, 0xe7, 0xf3, - 0x83, 0xf5, 0x1e, 0x2f, 0x24, 0x0a, 0x5f, 0x31, 0x24, 0x06, 0xed, 0x2c, 0x3e, 0xa7, 0x9d, 0x1d, - 0x85, 0xd9, 0x59, 0x62, 0xae, 0x4c, 0x73, 0x2e, 0x6b, 0x4b, 0x7e, 0x35, 0x6b, 0x0b, 0x1c, 0xb8, - 0x54, 0xf0, 0xc0, 0x8d, 0xc5, 0xf5, 0x74, 0x48, 0x5c, 0x7f, 0x13, 0xc0, 0xab, 0x94, 0x91, 0xa8, - 0x65, 0xea, 0x83, 0x7e, 0x9b, 0x9a, 0x49, 0x4a, 0x66, 0x0d, 0x74, 0x1b, 0x52, 0xc4, 0xdc, 0x9c, - 0xc5, 0x1c, 0xf7, 0xbc, 0xc4, 0x5c, 0x7c, 0x95, 0x36, 0xc6, 0x2d, 0x69, 0x80, 0xc6, 0x4b, 0xf1, - 0x11, 0x5d, 0xbc, 0x1a, 0xec, 0xe2, 0xf1, 0xc8, 0xa2, 0x7e, 0x78, 0x57, 0xef, 0x43, 0x8a, 0x9a, - 0x07, 0xc9, 0x6f, 0xe8, 0xfd, 0x0f, 0xc7, 0x4b, 0xe4, 0x1b, 0xfd, 0x04, 0x40, 0xb5, 0x6d, 0x53, - 0x3b, 0x1d, 0x78, 0x1d, 0xac, 0x84, 0x9b, 0xd7, 0xa6, 0xc3, 0xb7, 0x75, 0x8d, 0xdb, 0xd9, 0x92, - 0x27, 0xea, 0xb3, 0x35, 0x9f, 0x42, 0x69, 0x1f, 0x4a, 0x41, 0x59, 0x27, 0xc3, 0x67, 0x63, 0x08, - 0x66, 0xf8, 0x0c, 0xb0, 0xf1, 0x0c, 0xdf, 0xc5, 0x07, 0x09, 0x76, 0xc9, 0x45, 0x1b, 0xd2, 0x7f, - 0x05, 0x28, 0xf8, 0xad, 0xf3, 0x6b, 0xce, 0x5b, 0xa7, 0xa4, 0xea, 0x57, 0xc7, 0xd2, 0xd6, 0xcc, - 0xb9, 0x6a, 0x1d, 0x7f, 0x93, 0x59, 0xeb, 0x07, 0x02, 0x64, 0xdd, 0xc9, 0x07, 0xef, 0xbb, 0x02, - 0x17, 0x84, 0x6c, 0xed, 0xe2, 0xfe, 0x4b, 0x2a, 0x76, 0x1d, 0x98, 0x70, 0xaf, 0x03, 0xef, 0xba, - 0x09, 0x55, 0x54, 0x29, 0xd0, 0xbf, 0xd2, 0xdc, 0xa6, 0x9c, 0xfc, 0xf1, 0x57, 0x7c, 0x1c, 0x24, - 0x93, 0x40, 0xdf, 0x83, 0xb4, 0xda, 0x72, 0x0b, 0xa0, 0xa5, 0x90, 0xca, 0xa0, 0xc3, 0xba, 0xd6, - 0x1c, 0x6e, 0x52, 0x4e, 0x99, 0x4b, 0xf0, 0x51, 0xc5, 0x9d, 0x51, 0x49, 0xaf, 0x11, 0xbd, 0x8c, - 0x27, 0xe8, 0x36, 0x4b, 0x00, 0xc7, 0xfb, 0x0f, 0x0e, 0xb6, 0x77, 0xee, 0xed, 0xd4, 0xb7, 0x79, - 0x4a, 0xb5, 0xbd, 0x5d, 0xdf, 0x16, 0xe3, 0x84, 0x4f, 0xae, 0x3f, 0x38, 0x38, 0xa9, 0x6f, 0x8b, - 0x09, 0xe9, 0x2e, 0xe4, 0x5c, 0xd7, 0x83, 0x2a, 0x90, 0x71, 0x8a, 0xb9, 0x02, 0x77, 0x00, 0xbc, - 0x36, 0xbf, 0x04, 0x29, 0x43, 0x7f, 0x8f, 0xdf, 0xcd, 0x25, 0x64, 0xd6, 0x90, 0xda, 0x50, 0x1e, - 0xf1, 0x5b, 0xe8, 0x2e, 0x64, 0x8c, 0xc1, 0xa9, 0xe2, 0x18, 0xed, 0x48, 0xe9, 0xdb, 0x01, 0x9a, - 0x83, 0xd3, 0xae, 0xd6, 0xba, 0x8f, 0x2f, 0x9d, 0x65, 0x32, 0x06, 0xa7, 0xf7, 0x99, 0x6d, 0xb3, - 0x5e, 0xe2, 0xfe, 0x5e, 0x2e, 0x20, 0xeb, 0x1c, 0x55, 0xf4, 0x7d, 0xc8, 0xb9, 0x2e, 0xd1, 0xbd, - 0x5b, 0x8f, 0xf4, 0xa5, 0x5c, 0xbd, 0x27, 0x82, 0x6e, 0xc2, 0x82, 0xa5, 0x9d, 0xf7, 0x9d, 0xc2, - 0x3f, 0x2b, 0xf5, 0xc4, 0xe9, 0x99, 0x29, 0xb3, 0x1f, 0x7b, 0x4e, 0x35, 0x82, 0x44, 0x42, 0x71, - 0xd4, 0x57, 0x7c, 0x93, 0x03, 0x08, 0x89, 0xd8, 0x89, 0xb0, 0x88, 0xfd, 0xb3, 0x38, 0xe4, 0x7d, - 0xd7, 0x09, 0xe8, 0xbb, 0x3e, 0xc7, 0x55, 0x0a, 0x09, 0x35, 0x3e, 0x5e, 0xef, 0xf2, 0x3a, 0x38, - 0xb1, 0xf8, 0xfc, 0x13, 0x8b, 0xba, 0xbd, 0x71, 0x6e, 0x25, 0x92, 0x73, 0xdf, 0x4a, 0x3c, 0x07, - 0xc8, 0xd6, 0x6d, 0xb5, 0xab, 0x5c, 0xe8, 0xb6, 0xd6, 0x3f, 0x57, 0x98, 0x69, 0x30, 0x37, 0x23, - 0xd2, 0x3f, 0x27, 0xf4, 0xc7, 0x21, 0xb5, 0x92, 0x9f, 0x0a, 0x90, 0x75, 0x61, 0xdf, 0xbc, 0x57, - 0xdb, 0x57, 0x20, 0xcd, 0x91, 0x0d, 0xbb, 0xdb, 0xe6, 0xad, 0xd0, 0xeb, 0x97, 0x2a, 0x64, 0x7b, - 0xd8, 0x56, 0xa9, 0xcf, 0x64, 0x61, 0xd2, 0x6d, 0xdf, 0x7c, 0x19, 0xf2, 0xbe, 0x67, 0x01, 0xc4, - 0x8d, 0xee, 0xd7, 0xdf, 0x10, 0x63, 0xd5, 0xcc, 0x87, 0x1f, 0xaf, 0x26, 0xf6, 0xf1, 0x7b, 0xe4, - 0x84, 0xc9, 0xf5, 0x5a, 0xa3, 0x5e, 0xbb, 0x2f, 0x0a, 0xd5, 0xfc, 0x87, 0x1f, 0xaf, 0x66, 0x64, - 0x4c, 0x2b, 0xef, 0x37, 0xef, 0x43, 0x79, 0x64, 0x63, 0x82, 0x07, 0x1a, 0x41, 0x69, 0xfb, 0xf8, - 0x70, 0x6f, 0xa7, 0xb6, 0xd9, 0xac, 0x2b, 0x27, 0x07, 0xcd, 0xba, 0x28, 0xa0, 0x47, 0x61, 0x71, - 0x6f, 0xe7, 0x07, 0x8d, 0xa6, 0x52, 0xdb, 0xdb, 0xa9, 0xef, 0x37, 0x95, 0xcd, 0x66, 0x73, 0xb3, - 0x76, 0x5f, 0x8c, 0x6f, 0xfc, 0x21, 0x0f, 0xe5, 0xcd, 0xad, 0xda, 0x0e, 0xc1, 0x76, 0x5a, 0x4b, - 0xa5, 0xee, 0xa1, 0x06, 0x49, 0x5a, 0x43, 0x9c, 0xf8, 0x38, 0xb0, 0x3a, 0xf9, 0x3a, 0x05, 0xdd, - 0x83, 0x14, 0x2d, 0x2f, 0xa2, 0xc9, 0xaf, 0x05, 0xab, 0x53, 0xee, 0x57, 0xc8, 0x60, 0xe8, 0x71, - 0x9a, 0xf8, 0x7c, 0xb0, 0x3a, 0xf9, 0xba, 0x05, 0xed, 0x41, 0xc6, 0xa9, 0x2e, 0x4d, 0x7b, 0xd3, - 0x57, 0x9d, 0x7a, 0x07, 0x42, 0xa6, 0xc6, 0xaa, 0x74, 0x93, 0x5f, 0x16, 0x56, 0xa7, 0x5c, 0xc4, - 0xa0, 0x1d, 0x48, 0xf3, 0x0a, 0xc9, 0x94, 0xc7, 0x82, 0xd5, 0x69, 0x57, 0x2b, 0x48, 0x86, 0x9c, - 0x57, 0xff, 0x9c, 0xfe, 0x5e, 0xb2, 0x3a, 0xc3, 0x1d, 0x13, 0x7a, 0x0b, 0x8a, 0xc1, 0xaa, 0xcb, - 0x6c, 0x0f, 0x12, 0xab, 0x33, 0x5e, 0xe2, 0x10, 0xfd, 0xc1, 0x12, 0xcc, 0x6c, 0x0f, 0x14, 0xab, - 0x33, 0xde, 0xe9, 0xa0, 0x77, 0x60, 0x61, 0xbc, 0x44, 0x32, 0xfb, 0x7b, 0xc5, 0xea, 0x1c, 0xb7, - 0x3c, 0xa8, 0x07, 0x28, 0xa4, 0xb4, 0x32, 0xc7, 0xf3, 0xc5, 0xea, 0x3c, 0x97, 0x3e, 0xa8, 0x0d, - 0xe5, 0xd1, 0x72, 0xc5, 0xac, 0xcf, 0x19, 0xab, 0x33, 0x5f, 0x00, 0xb1, 0x5e, 0x82, 0xd8, 0x7d, - 0xd6, 0xe7, 0x8d, 0xd5, 0x99, 0xef, 0x83, 0xd0, 0x31, 0x80, 0x0f, 0x7b, 0xce, 0xf0, 0xdc, 0xb1, - 0x3a, 0xcb, 0xcd, 0x10, 0x32, 0x60, 0x31, 0x0c, 0x94, 0xce, 0xf3, 0xfa, 0xb1, 0x3a, 0xd7, 0x85, - 0x11, 0xb1, 0xe7, 0x20, 0xbc, 0x9c, 0xed, 0x35, 0x64, 0x75, 0xc6, 0x9b, 0xa3, 0xad, 0xfa, 0x27, - 0x9f, 0x2f, 0x0b, 0x9f, 0x7e, 0xbe, 0x2c, 0xfc, 0xeb, 0xf3, 0x65, 0xe1, 0xa3, 0x2f, 0x96, 0x63, - 0x9f, 0x7e, 0xb1, 0x1c, 0xfb, 0xc7, 0x17, 0xcb, 0xb1, 0x1f, 0x3e, 0x7b, 0xae, 0xd9, 0x9d, 0xc1, - 0xe9, 0x5a, 0x4b, 0xef, 0xad, 0xfb, 0x1f, 0x90, 0x87, 0x3d, 0x5b, 0x3f, 0x4d, 0xd3, 0x80, 0x7a, - 0xeb, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x14, 0x5f, 0x7b, 0xc4, 0xd6, 0x2e, 0x00, 0x00, + 0x58, 0xee, 0x97, 0x9f, 0xad, 0x88, 0x8c, 0xfb, 0x39, 0xbd, 0xa7, 0xd9, 0xb8, 0x67, 0xd8, 0x97, + 0x32, 0x97, 0x9f, 0xbc, 0xb2, 0xd2, 0x6d, 0x28, 0x05, 0xe3, 0x3e, 0x7a, 0x02, 0x8a, 0x26, 0xb6, + 0x55, 0xad, 0xaf, 0x04, 0xb2, 0xf6, 0x02, 0x23, 0xf2, 0x62, 0xce, 0x21, 0x3c, 0x12, 0x1a, 0xeb, + 0xd1, 0x4b, 0x90, 0xf3, 0xd2, 0x04, 0x81, 0x0e, 0x7d, 0x42, 0xad, 0xc3, 0xe3, 0x95, 0xfe, 0x2a, + 0x78, 0x2a, 0x83, 0xd5, 0x93, 0x3a, 0xa4, 0x4d, 0x6c, 0x0d, 0xba, 0xac, 0x9e, 0x51, 0xda, 0x78, + 0x7e, 0xb6, 0x2c, 0x81, 0x50, 0x07, 0x5d, 0x5b, 0xe6, 0xc2, 0xd2, 0x5b, 0x90, 0x66, 0x14, 0x94, + 0x87, 0xcc, 0xf1, 0xfe, 0xfd, 0xfd, 0x83, 0x37, 0xf6, 0xc5, 0x18, 0x02, 0x48, 0x6f, 0xd6, 0x6a, + 0xf5, 0xc3, 0xa6, 0x28, 0xa0, 0x1c, 0xa4, 0x36, 0xb7, 0x0e, 0xe4, 0xa6, 0x18, 0x27, 0x64, 0xb9, + 0xbe, 0x5b, 0xaf, 0x35, 0xc5, 0x04, 0x5a, 0x80, 0x22, 0xfb, 0x56, 0xee, 0x1d, 0xc8, 0x0f, 0x36, + 0x9b, 0x62, 0xd2, 0x47, 0x3a, 0xaa, 0xef, 0x6f, 0xd7, 0x65, 0x31, 0x25, 0x7d, 0x0f, 0xae, 0x46, + 0xe6, 0x15, 0x5e, 0x69, 0x44, 0xf0, 0x95, 0x46, 0xa4, 0xdf, 0xc4, 0x09, 0xf2, 0x8a, 0x4a, 0x16, + 0xd0, 0xee, 0xc8, 0xc4, 0x37, 0xe6, 0xc8, 0x34, 0x46, 0x66, 0x4f, 0xc0, 0x96, 0x89, 0xcf, 0xb0, + 0xdd, 0xea, 0xb0, 0xe4, 0x85, 0xf9, 0xc6, 0xa2, 0x5c, 0xe4, 0x54, 0x2a, 0x64, 0x31, 0xb6, 0x77, + 0x70, 0xcb, 0x56, 0xd8, 0x09, 0x63, 0x40, 0x27, 0x47, 0xd8, 0x08, 0xf5, 0x88, 0x11, 0xa5, 0xb7, + 0xe7, 0x5a, 0xcb, 0x1c, 0xa4, 0xe4, 0x7a, 0x53, 0x7e, 0x53, 0x4c, 0x20, 0x04, 0x25, 0xfa, 0xa9, + 0x1c, 0xed, 0x6f, 0x1e, 0x1e, 0x35, 0x0e, 0xc8, 0x5a, 0x2e, 0x42, 0xd9, 0x59, 0x4b, 0x87, 0x98, + 0x92, 0xfe, 0x11, 0x87, 0x47, 0x23, 0x52, 0x1d, 0x74, 0x07, 0xc0, 0x1e, 0x2a, 0x26, 0x6e, 0xe9, + 0x66, 0x3b, 0xda, 0xc8, 0x9a, 0x43, 0x99, 0x72, 0xc8, 0x39, 0x9b, 0x7f, 0x59, 0x13, 0x2a, 0x6a, + 0xe8, 0x15, 0xae, 0x94, 0xcc, 0xca, 0x81, 0x77, 0xd7, 0x43, 0x0a, 0x47, 0xb8, 0x45, 0x14, 0xd3, + 0xb5, 0xa5, 0x8a, 0x29, 0x3f, 0x7a, 0xe0, 0x07, 0xc4, 0x03, 0x1a, 0x54, 0x66, 0x2e, 0xbd, 0xfa, + 0x20, 0x33, 0x23, 0x58, 0xe8, 0x4d, 0x78, 0x74, 0x24, 0x26, 0xba, 0x4a, 0x53, 0xb3, 0x86, 0xc6, + 0x47, 0x82, 0xa1, 0x91, 0xab, 0x96, 0x7e, 0x9f, 0xf0, 0x2f, 0x6c, 0x30, 0xb3, 0x3b, 0x80, 0xb4, + 0x65, 0xab, 0xf6, 0xc0, 0xe2, 0x06, 0xf7, 0xd2, 0xac, 0x69, 0xe2, 0x9a, 0xf3, 0x71, 0x44, 0xc5, + 0x65, 0xae, 0xe6, 0xbb, 0xf5, 0xb6, 0x88, 0x83, 0x0d, 0x2e, 0x4e, 0xf4, 0x91, 0xf1, 0x7c, 0x4e, + 0x5c, 0xba, 0x0b, 0x68, 0x3c, 0x81, 0x0e, 0x29, 0x99, 0x08, 0x61, 0x25, 0x93, 0x3f, 0x08, 0xf0, + 0xd8, 0x84, 0x64, 0x19, 0xbd, 0x3e, 0xb2, 0xcf, 0x2f, 0xcf, 0x93, 0x6a, 0xaf, 0x31, 0x5a, 0x70, + 0xa7, 0xa5, 0x5b, 0x50, 0xf0, 0xd3, 0x67, 0x9b, 0xe4, 0x97, 0x71, 0xcf, 0xe7, 0x07, 0x6b, 0x3b, + 0x5e, 0xf8, 0x13, 0xbe, 0x66, 0xf8, 0x0b, 0xda, 0x59, 0x7c, 0x4e, 0x3b, 0x3b, 0x0a, 0xb3, 0xb3, + 0xc4, 0x5c, 0x59, 0xe5, 0x5c, 0xd6, 0x96, 0xfc, 0x7a, 0xd6, 0x16, 0x38, 0x70, 0xa9, 0x60, 0xda, + 0xfa, 0x26, 0x80, 0x57, 0xf0, 0x22, 0x01, 0xc9, 0xd4, 0x07, 0xfd, 0x36, 0xb5, 0x80, 0x94, 0xcc, + 0x1a, 0xe8, 0x36, 0xa4, 0x88, 0x25, 0x39, 0xeb, 0x34, 0xee, 0x54, 0x89, 0x25, 0xf8, 0x0a, 0x66, + 0x8c, 0x5b, 0xd2, 0x00, 0x8d, 0x57, 0xd4, 0x23, 0xba, 0x78, 0x35, 0xd8, 0xc5, 0xe3, 0x91, 0xb5, + 0xf9, 0xf0, 0xae, 0xde, 0x87, 0x14, 0xdd, 0x79, 0x92, 0x70, 0xd1, 0x6b, 0x1c, 0x0e, 0x7b, 0xc8, + 0x37, 0xfa, 0x19, 0x80, 0x6a, 0xdb, 0xa6, 0x76, 0x3a, 0xf0, 0x3a, 0x58, 0x09, 0xb7, 0x9c, 0x4d, + 0x87, 0x6f, 0xeb, 0x1a, 0x37, 0xa1, 0x25, 0x4f, 0xd4, 0x67, 0x46, 0x3e, 0x85, 0xd2, 0x3e, 0x94, + 0x82, 0xb2, 0x4e, 0xa2, 0xce, 0xc6, 0x10, 0x4c, 0xd4, 0x19, 0xee, 0xe2, 0x89, 0xba, 0x9b, 0xe6, + 0x27, 0xd8, 0x5d, 0x15, 0x6d, 0x48, 0xff, 0x15, 0xa0, 0xe0, 0x37, 0xbc, 0x6f, 0x38, 0xfd, 0x9c, + 0x92, 0x71, 0x5f, 0x1d, 0xcb, 0x3e, 0x33, 0xe7, 0xaa, 0x75, 0xfc, 0x6d, 0x26, 0x9f, 0x1f, 0x08, + 0x90, 0x75, 0x27, 0x1f, 0xbc, 0xb6, 0x0a, 0xdc, 0xf3, 0xb1, 0xb5, 0x8b, 0xfb, 0xef, 0x9a, 0xd8, + 0xad, 0x5e, 0xc2, 0xbd, 0xd5, 0xbb, 0xeb, 0xe6, 0x4a, 0x51, 0x15, 0x3d, 0xff, 0x4a, 0x73, 0x9b, + 0x72, 0x52, 0xc3, 0x5f, 0xf3, 0x71, 0x90, 0x24, 0x01, 0xfd, 0x00, 0xd2, 0x6a, 0xcb, 0xad, 0x63, + 0x96, 0x42, 0x0a, 0x7c, 0x0e, 0xeb, 0x5a, 0x73, 0xb8, 0x49, 0x39, 0x65, 0x2e, 0xc1, 0x47, 0x15, + 0x77, 0x46, 0x25, 0xbd, 0x46, 0xf4, 0x32, 0x9e, 0xa0, 0x47, 0x2c, 0x01, 0x1c, 0xef, 0x3f, 0x38, + 0xd8, 0xde, 0xb9, 0xb7, 0x53, 0xdf, 0xe6, 0xd9, 0xd2, 0xf6, 0x76, 0x7d, 0x5b, 0x8c, 0x13, 0x3e, + 0xb9, 0xfe, 0xe0, 0xe0, 0xa4, 0xbe, 0x2d, 0x26, 0xa4, 0xbb, 0x90, 0x73, 0xbd, 0x0a, 0x41, 0xf5, + 0x4e, 0x4d, 0x56, 0xe0, 0x67, 0x9b, 0x97, 0xd8, 0x97, 0x20, 0x65, 0xe8, 0xef, 0xf1, 0x2b, 0xb6, + 0x84, 0xcc, 0x1a, 0x52, 0x1b, 0xca, 0x23, 0x2e, 0x09, 0xdd, 0x85, 0x8c, 0x31, 0x38, 0x55, 0x1c, + 0xa3, 0x1d, 0xa9, 0x60, 0x3b, 0x78, 0x71, 0x70, 0xda, 0xd5, 0x5a, 0xf7, 0xf1, 0xa5, 0xb3, 0x4c, + 0xc6, 0xe0, 0xf4, 0x3e, 0xb3, 0x6d, 0xd6, 0x4b, 0xdc, 0xdf, 0xcb, 0x05, 0x64, 0x9d, 0xa3, 0x8a, + 0x7e, 0x08, 0x39, 0xd7, 0xdb, 0xb9, 0x57, 0xe4, 0x91, 0x6e, 0x92, 0xab, 0xf7, 0x44, 0xd0, 0x4d, + 0x58, 0xb0, 0xb4, 0xf3, 0xbe, 0x53, 0xbf, 0x67, 0x15, 0x9b, 0x38, 0x3d, 0x33, 0x65, 0xf6, 0x63, + 0xcf, 0x29, 0x2a, 0x90, 0x20, 0x27, 0x8e, 0xfa, 0x8a, 0x6f, 0x73, 0x00, 0x21, 0xc1, 0x38, 0x11, + 0x16, 0x8c, 0x7f, 0x11, 0x87, 0xbc, 0xef, 0x56, 0x00, 0x7d, 0xdf, 0xe7, 0xb8, 0x4a, 0x21, 0x51, + 0xc4, 0xc7, 0xeb, 0xdd, 0x41, 0x07, 0x27, 0x16, 0x9f, 0x7f, 0x62, 0x51, 0x97, 0x30, 0xce, 0xe5, + 0x42, 0x72, 0xee, 0xcb, 0x85, 0xe7, 0x00, 0xd9, 0xba, 0xad, 0x76, 0x95, 0x0b, 0xdd, 0xd6, 0xfa, + 0xe7, 0x0a, 0x33, 0x0d, 0xe6, 0x66, 0x44, 0xfa, 0xe7, 0x84, 0xfe, 0x38, 0xa4, 0x56, 0xf2, 0x73, + 0x01, 0xb2, 0x2e, 0xa2, 0x9b, 0xf7, 0x86, 0xfa, 0x0a, 0xa4, 0x39, 0x68, 0x61, 0x57, 0xd4, 0xbc, + 0x15, 0x7a, 0x8b, 0x52, 0x85, 0x6c, 0x0f, 0xdb, 0x2a, 0xf5, 0x99, 0x2c, 0x02, 0xba, 0xed, 0x9b, + 0x2f, 0x43, 0xde, 0x77, 0xbb, 0x4f, 0xdc, 0xe8, 0x7e, 0xfd, 0x0d, 0x31, 0x56, 0xcd, 0x7c, 0xf8, + 0xf1, 0x6a, 0x62, 0x1f, 0xbf, 0x47, 0x4e, 0x98, 0x5c, 0xaf, 0x35, 0xea, 0xb5, 0xfb, 0xa2, 0x50, + 0xcd, 0x7f, 0xf8, 0xf1, 0x6a, 0x46, 0xc6, 0xb4, 0x80, 0x7e, 0xf3, 0x3e, 0x94, 0x47, 0x36, 0x26, + 0x78, 0xa0, 0x11, 0x94, 0xb6, 0x8f, 0x0f, 0xf7, 0x76, 0x6a, 0x9b, 0xcd, 0xba, 0x72, 0x72, 0xd0, + 0xac, 0x8b, 0x02, 0x7a, 0x14, 0x16, 0xf7, 0x76, 0x7e, 0xd4, 0x68, 0x2a, 0xb5, 0xbd, 0x9d, 0xfa, + 0x7e, 0x53, 0xd9, 0x6c, 0x36, 0x37, 0x6b, 0xf7, 0xc5, 0xf8, 0xc6, 0x1f, 0xf3, 0x50, 0xde, 0xdc, + 0xaa, 0xed, 0x10, 0xd8, 0xa6, 0xb5, 0x54, 0xea, 0x1e, 0x6a, 0x90, 0xa4, 0xa5, 0xc0, 0x89, 0x6f, + 0xfc, 0xaa, 0x93, 0x6f, 0x45, 0xd0, 0x3d, 0x48, 0xd1, 0x2a, 0x21, 0x9a, 0xfc, 0xe8, 0xaf, 0x3a, + 0xe5, 0x9a, 0x84, 0x0c, 0x86, 0x1e, 0xa7, 0x89, 0xaf, 0x00, 0xab, 0x93, 0x6f, 0x4d, 0xd0, 0x1e, + 0x64, 0x9c, 0x22, 0xd1, 0xb4, 0xa7, 0x79, 0xd5, 0xa9, 0x57, 0x19, 0x64, 0x6a, 0xac, 0xd8, 0x36, + 0xf9, 0x81, 0x60, 0x75, 0xca, 0x7d, 0x0a, 0xda, 0x81, 0x34, 0x2f, 0x74, 0x4c, 0x79, 0xf3, 0x57, + 0x9d, 0x76, 0x43, 0x82, 0x64, 0xc8, 0x79, 0x65, 0xcc, 0xe9, 0xcf, 0x1e, 0xab, 0x33, 0x5c, 0x15, + 0xa1, 0xb7, 0xa0, 0x18, 0x2c, 0xa8, 0xcc, 0xf6, 0xae, 0xb0, 0x3a, 0xe3, 0x5d, 0x0c, 0xd1, 0x1f, + 0xac, 0xae, 0xcc, 0xf6, 0xce, 0xb0, 0x3a, 0xe3, 0xd5, 0x0c, 0x7a, 0x07, 0x16, 0xc6, 0xab, 0x1f, + 0xb3, 0x3f, 0x3b, 0xac, 0xce, 0x71, 0x59, 0x83, 0x7a, 0x80, 0x42, 0xaa, 0x26, 0x73, 0xbc, 0x42, + 0xac, 0xce, 0x73, 0x77, 0x83, 0xda, 0x50, 0x1e, 0xad, 0x44, 0xcc, 0xfa, 0x2a, 0xb1, 0x3a, 0xf3, + 0x3d, 0x0e, 0xeb, 0x25, 0x08, 0xcb, 0x67, 0x7d, 0xa5, 0x58, 0x9d, 0xf9, 0x5a, 0x07, 0x1d, 0x03, + 0xf8, 0x60, 0xe5, 0x0c, 0xaf, 0x16, 0xab, 0xb3, 0x5c, 0xf0, 0x20, 0x03, 0x16, 0xc3, 0xf0, 0xe6, + 0x3c, 0x8f, 0x18, 0xab, 0x73, 0xdd, 0xfb, 0x10, 0x7b, 0x0e, 0x22, 0xc7, 0xd9, 0x1e, 0x35, 0x56, + 0x67, 0xbc, 0x00, 0xda, 0xaa, 0x7f, 0xf2, 0xf9, 0xb2, 0xf0, 0xe9, 0xe7, 0xcb, 0xc2, 0xbf, 0x3f, + 0x5f, 0x16, 0x3e, 0xfa, 0x62, 0x39, 0xf6, 0xe9, 0x17, 0xcb, 0xb1, 0x7f, 0x7e, 0xb1, 0x1c, 0xfb, + 0xf1, 0xb3, 0xe7, 0x9a, 0xdd, 0x19, 0x9c, 0xae, 0xb5, 0xf4, 0xde, 0xba, 0xff, 0x1d, 0x78, 0xd8, + 0xeb, 0xf3, 0xd3, 0x34, 0x0d, 0xa8, 0xb7, 0xfe, 0x17, 0x00, 0x00, 0xff, 0xff, 0x4c, 0xb4, 0x7f, + 0x53, 0x9d, 0x2e, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -6672,13 +6656,6 @@ func (m *ResponseCommit) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x18 } - if len(m.Data) > 0 { - i -= len(m.Data) - copy(dAtA[i:], m.Data) - i = encodeVarintTypes(dAtA, i, uint64(len(m.Data))) - i-- - dAtA[i] = 0x12 - } return len(dAtA) - i, nil } @@ -7069,11 +7046,6 @@ func (m *ResponseFinalizeBlock) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - if m.RetainHeight != 0 { - i = encodeVarintTypes(dAtA, i, uint64(m.RetainHeight)) - i-- - dAtA[i] = 0x30 - } if len(m.AppHash) > 0 { i -= len(m.AppHash) copy(dAtA[i:], m.AppHash) @@ -8734,10 +8706,6 @@ func (m *ResponseCommit) Size() (n int) { } var l int _ = l - l = len(m.Data) - if l > 0 { - n += 1 + l + sovTypes(uint64(l)) - } if m.RetainHeight != 0 { n += 1 + sovTypes(uint64(m.RetainHeight)) } @@ -8933,9 +8901,6 @@ func (m *ResponseFinalizeBlock) Size() (n int) { if l > 0 { n += 1 + l + sovTypes(uint64(l)) } - if m.RetainHeight != 0 { - n += 1 + sovTypes(uint64(m.RetainHeight)) - } return n } @@ -14238,40 +14203,6 @@ func (m *ResponseCommit) Unmarshal(dAtA []byte) error { return fmt.Errorf("proto: ResponseCommit: illegal tag %d (wire type %d)", fieldNum, wire) } switch fieldNum { - case 2: - if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Data", 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.Data = append(m.Data[:0], dAtA[iNdEx:postIndex]...) - if m.Data == nil { - m.Data = []byte{} - } - iNdEx = postIndex case 3: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field RetainHeight", wireType) @@ -15509,25 +15440,6 @@ func (m *ResponseFinalizeBlock) Unmarshal(dAtA []byte) error { m.AppHash = []byte{} } iNdEx = postIndex - case 6: - if wireType != 0 { - return fmt.Errorf("proto: wrong wireType = %d for field RetainHeight", wireType) - } - m.RetainHeight = 0 - for shift := uint(0); ; shift += 7 { - if shift >= 64 { - return ErrIntOverflowTypes - } - if iNdEx >= l { - return io.ErrUnexpectedEOF - } - b := dAtA[iNdEx] - iNdEx++ - m.RetainHeight |= int64(b&0x7F) << shift - if b < 0x80 { - break - } - } default: iNdEx = preIndex skippy, err := skipTypes(dAtA[iNdEx:]) diff --git a/docs/app-dev/abci-cli.md b/docs/app-dev/abci-cli.md index 8f916427d..109e3b9fb 100644 --- a/docs/app-dev/abci-cli.md +++ b/docs/app-dev/abci-cli.md @@ -169,46 +169,49 @@ Try running these commands: -> data: {"size":0} -> data.hex: 0x7B2273697A65223A307D -> commit --> code: OK --> data.hex: 0x0000000000000000 - > finalize_block "abc" -> code: OK +-> code: OK +-> data.hex: 0x0200000000000000 + +> commit +-> code: OK > info -> code: OK -> data: {"size":1} -> data.hex: 0x7B2273697A65223A317D -> commit --> code: OK --> data.hex: 0x0200000000000000 - > query "abc" -> code: OK -> log: exists --> height: 2 +-> height: 1 +-> key: abc +-> key.hex: 616263 -> value: abc -> value.hex: 616263 -> finalize_block "def=xyz" +> finalize_block "def=xyz" "ghi=123" -> code: OK +-> code: OK +-> code: OK +-> data.hex: 0x0600000000000000 > commit -> code: OK --> data.hex: 0x0400000000000000 > query "def" -> code: OK -> log: exists --> height: 3 +-> height: 2 +-> key: def +-> key.hex: 646566 -> value: xyz -> value.hex: 78797A ``` -Note that if we do `finalize_block "abc"` it will store `(abc, abc)`, but if -we do `finalize_block "abc=efg"` it will store `(abc, efg)`. +Note that if we do `finalize_block "abc" ...` it will store `(abc, abc)`, but if +we do `finalize_block "abc=efg" ...` it will store `(abc, efg)`. Similarly, you could put the commands in a file and run `abci-cli --verbose batch < myfile`. diff --git a/internal/consensus/mempool_test.go b/internal/consensus/mempool_test.go index 747d95750..ed11f6840 100644 --- a/internal/consensus/mempool_test.go +++ b/internal/consensus/mempool_test.go @@ -5,6 +5,7 @@ import ( "encoding/binary" "fmt" "os" + "sync" "testing" "time" @@ -136,8 +137,10 @@ func checkTxsRange(ctx context.Context, t *testing.T, cs *State, start, end int) for i := start; i < end; i++ { txBytes := make([]byte, 8) binary.BigEndian.PutUint64(txBytes, uint64(i)) - err := assertMempool(t, cs.txNotifier).CheckTx(ctx, txBytes, nil, mempool.TxInfo{}) + var rCode uint32 + err := assertMempool(t, cs.txNotifier).CheckTx(ctx, txBytes, func(r *abci.ResponseCheckTx) { rCode = r.Code }, mempool.TxInfo{}) require.NoError(t, err, "error after checkTx") + require.Equal(t, code.CodeTypeOK, rCode, "checkTx code is error, txBytes %X", txBytes) } } @@ -173,6 +176,7 @@ func TestMempoolTxConcurrentWithCommit(t *testing.T) { case msg := <-newBlockHeaderCh: headerEvent := msg.Data().(types.EventDataNewBlockHeader) n += headerEvent.NumTxs + logger.Info("new transactions", "nTxs", headerEvent.NumTxs, "total", n) case <-time.After(30 * time.Second): t.Fatal("Timed out waiting 30s to commit blocks with transactions") } @@ -202,10 +206,10 @@ func TestMempoolRmBadTx(t *testing.T) { resFinalize, err := app.FinalizeBlock(ctx, &abci.RequestFinalizeBlock{Txs: [][]byte{txBytes}}) require.NoError(t, err) assert.False(t, resFinalize.TxResults[0].IsErr(), fmt.Sprintf("expected no error. got %v", resFinalize)) + assert.True(t, len(resFinalize.AppHash) > 0) - resCommit, err := app.Commit(ctx) + _, err = app.Commit(ctx) require.NoError(t, err) - assert.True(t, len(resCommit.Data) > 0) emptyMempoolCh := make(chan struct{}) checkTxRespCh := make(chan struct{}) @@ -263,6 +267,7 @@ type CounterApplication struct { txCount int mempoolTxCount int + mu sync.Mutex } func NewCounterApplication() *CounterApplication { @@ -270,10 +275,16 @@ func NewCounterApplication() *CounterApplication { } func (app *CounterApplication) Info(_ context.Context, req *abci.RequestInfo) (*abci.ResponseInfo, error) { + app.mu.Lock() + defer app.mu.Unlock() + return &abci.ResponseInfo{Data: fmt.Sprintf("txs:%v", app.txCount)}, nil } func (app *CounterApplication) FinalizeBlock(_ context.Context, req *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) { + app.mu.Lock() + defer app.mu.Unlock() + respTxs := make([]*abci.ExecTxResult, len(req.Txs)) for i, tx := range req.Txs { txValue := txAsUint64(tx) @@ -287,10 +298,21 @@ func (app *CounterApplication) FinalizeBlock(_ context.Context, req *abci.Reques app.txCount++ respTxs[i] = &abci.ExecTxResult{Code: code.CodeTypeOK} } - return &abci.ResponseFinalizeBlock{TxResults: respTxs}, nil + + res := &abci.ResponseFinalizeBlock{TxResults: respTxs} + + if app.txCount > 0 { + res.AppHash = make([]byte, 8) + binary.BigEndian.PutUint64(res.AppHash, uint64(app.txCount)) + } + + return res, nil } func (app *CounterApplication) CheckTx(_ context.Context, req *abci.RequestCheckTx) (*abci.ResponseCheckTx, error) { + app.mu.Lock() + defer app.mu.Unlock() + txValue := txAsUint64(req.Tx) if txValue != uint64(app.mempoolTxCount) { return &abci.ResponseCheckTx{ @@ -308,13 +330,11 @@ func txAsUint64(tx []byte) uint64 { } func (app *CounterApplication) Commit(context.Context) (*abci.ResponseCommit, error) { + app.mu.Lock() + defer app.mu.Unlock() + app.mempoolTxCount = app.txCount - if app.txCount == 0 { - return &abci.ResponseCommit{}, nil - } - hash := make([]byte, 8) - binary.BigEndian.PutUint64(hash, uint64(app.txCount)) - return &abci.ResponseCommit{Data: hash}, nil + return &abci.ResponseCommit{}, nil } func (app *CounterApplication) PrepareProposal(_ context.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { diff --git a/internal/consensus/replay_stubs.go b/internal/consensus/replay_stubs.go index 407ec925e..ec5f70773 100644 --- a/internal/consensus/replay_stubs.go +++ b/internal/consensus/replay_stubs.go @@ -86,5 +86,5 @@ func (mock *mockProxyApp) FinalizeBlock(_ context.Context, req *abci.RequestFina } func (mock *mockProxyApp) Commit(context.Context) (*abci.ResponseCommit, error) { - return &abci.ResponseCommit{Data: mock.appHash}, nil + return &abci.ResponseCommit{}, nil } diff --git a/internal/consensus/replay_test.go b/internal/consensus/replay_test.go index 99d3c17a1..328dba040 100644 --- a/internal/consensus/replay_test.go +++ b/internal/consensus/replay_test.go @@ -1017,15 +1017,15 @@ type badApp struct { onlyLastHashIsWrong bool } -func (app *badApp) Commit(context.Context) (*abci.ResponseCommit, error) { +func (app *badApp) FinalizeBlock(_ context.Context, _ *abci.RequestFinalizeBlock) (*abci.ResponseFinalizeBlock, error) { app.height++ if app.onlyLastHashIsWrong { if app.height == app.numBlocks { - return &abci.ResponseCommit{Data: tmrand.Bytes(8)}, nil + return &abci.ResponseFinalizeBlock{AppHash: tmrand.Bytes(8)}, nil } - return &abci.ResponseCommit{Data: []byte{app.height}}, nil + return &abci.ResponseFinalizeBlock{AppHash: []byte{app.height}}, nil } else if app.allHashesAreWrong { - return &abci.ResponseCommit{Data: tmrand.Bytes(8)}, nil + return &abci.ResponseFinalizeBlock{AppHash: tmrand.Bytes(8)}, nil } panic("either allHashesAreWrong or onlyLastHashIsWrong must be set") diff --git a/internal/state/execution.go b/internal/state/execution.go index cc3e63d7a..5c46f4353 100644 --- a/internal/state/execution.go +++ b/internal/state/execution.go @@ -206,7 +206,7 @@ func (blockExec *BlockExecutor) ApplyBlock( return state, ErrInvalidBlock(err) } startTime := time.Now().UnixNano() - finalizeBlockResponse, err := blockExec.appClient.FinalizeBlock( + fBlockRes, err := blockExec.appClient.FinalizeBlock( ctx, &abci.RequestFinalizeBlock{ Hash: block.Hash(), @@ -225,8 +225,16 @@ func (blockExec *BlockExecutor) ApplyBlock( return state, ErrProxyAppConn(err) } + blockExec.logger.Info( + "finalized block", + "height", block.Height, + "num_txs_res", len(fBlockRes.TxResults), + "num_val_updates", len(fBlockRes.ValidatorUpdates), + "block_app_hash", fmt.Sprintf("%X", fBlockRes.AppHash), + ) + abciResponses := &tmstate.ABCIResponses{ - FinalizeBlock: finalizeBlockResponse, + FinalizeBlock: fBlockRes, } // Save the results before we commit. @@ -235,12 +243,12 @@ func (blockExec *BlockExecutor) ApplyBlock( } // validate the validator updates and convert to tendermint types - err = validateValidatorUpdates(finalizeBlockResponse.ValidatorUpdates, state.ConsensusParams.Validator) + err = validateValidatorUpdates(fBlockRes.ValidatorUpdates, state.ConsensusParams.Validator) if err != nil { return state, fmt.Errorf("error in validator updates: %w", err) } - validatorUpdates, err := types.PB2TM.ValidatorUpdates(finalizeBlockResponse.ValidatorUpdates) + validatorUpdates, err := types.PB2TM.ValidatorUpdates(fBlockRes.ValidatorUpdates) if err != nil { return state, err } @@ -248,23 +256,23 @@ func (blockExec *BlockExecutor) ApplyBlock( blockExec.logger.Debug("updates to validators", "updates", types.ValidatorListString(validatorUpdates)) blockExec.metrics.ValidatorSetUpdates.Add(1) } - if finalizeBlockResponse.ConsensusParamUpdates != nil { + if fBlockRes.ConsensusParamUpdates != nil { blockExec.metrics.ConsensusParamUpdates.Add(1) } // Update the state with the block and responses. - rs, err := abci.MarshalTxResults(finalizeBlockResponse.TxResults) + rs, err := abci.MarshalTxResults(fBlockRes.TxResults) if err != nil { return state, fmt.Errorf("marshaling TxResults: %w", err) } h := merkle.HashFromByteSlices(rs) - state, err = state.Update(blockID, &block.Header, h, finalizeBlockResponse.ConsensusParamUpdates, validatorUpdates) + state, err = state.Update(blockID, &block.Header, h, fBlockRes.ConsensusParamUpdates, validatorUpdates) if err != nil { return state, fmt.Errorf("commit failed for application: %w", err) } // Lock mempool, commit app state, update mempoool. - appHash, retainHeight, err := blockExec.Commit(ctx, state, block, finalizeBlockResponse.TxResults) + retainHeight, err := blockExec.Commit(ctx, state, block, fBlockRes.TxResults) if err != nil { return state, fmt.Errorf("commit failed for application: %w", err) } @@ -273,7 +281,7 @@ func (blockExec *BlockExecutor) ApplyBlock( blockExec.evpool.Update(ctx, state, block.Evidence) // Update the app hash and save the state. - state.AppHash = appHash + state.AppHash = fBlockRes.AppHash if err := blockExec.store.Save(state); err != nil { return state, err } @@ -293,7 +301,7 @@ func (blockExec *BlockExecutor) ApplyBlock( // Events are fired after everything else. // NOTE: if we crash between Commit and Save, events wont be fired during replay - fireEvents(blockExec.logger, blockExec.eventBus, block, blockID, finalizeBlockResponse, validatorUpdates) + fireEvents(blockExec.logger, blockExec.eventBus, block, blockID, fBlockRes, validatorUpdates) return state, nil } @@ -338,7 +346,7 @@ func (blockExec *BlockExecutor) Commit( state State, block *types.Block, txResults []*abci.ExecTxResult, -) ([]byte, int64, error) { +) (int64, error) { blockExec.mempool.Lock() defer blockExec.mempool.Unlock() @@ -347,14 +355,14 @@ func (blockExec *BlockExecutor) Commit( err := blockExec.mempool.FlushAppConn(ctx) if err != nil { blockExec.logger.Error("client error during mempool.FlushAppConn", "err", err) - return nil, 0, err + return 0, err } // Commit block, get hash back res, err := blockExec.appClient.Commit(ctx) if err != nil { blockExec.logger.Error("client error during proxyAppConn.Commit", "err", err) - return nil, 0, err + return 0, err } // ResponseCommit has no error code - just data @@ -362,7 +370,7 @@ func (blockExec *BlockExecutor) Commit( "committed state", "height", block.Height, "num_txs", len(block.Txs), - "app_hash", fmt.Sprintf("%X", res.Data), + "block_app_hash", fmt.Sprintf("%X", block.AppHash), ) // Update mempool. @@ -376,7 +384,7 @@ func (blockExec *BlockExecutor) Commit( state.ConsensusParams.ABCI.RecheckTx, ) - return res.Data, res.RetainHeight, err + return res.RetainHeight, err } func buildLastCommitInfo(block *types.Block, store Store, initialHeight int64) abci.CommitInfo { @@ -708,15 +716,15 @@ func ExecCommitBlock( fireEvents(be.logger, be.eventBus, block, blockID, finalizeBlockResponse, validatorUpdates) } - // Commit block, get hash back - res, err := appConn.Commit(ctx) + // Commit block + _, err = appConn.Commit(ctx) if err != nil { - logger.Error("client error during proxyAppConn.Commit", "err", res) + logger.Error("client error during proxyAppConn.Commit", "err", err) return nil, err } - // ResponseCommit has no error or log, just data - return res.Data, nil + // ResponseCommit has no error or log + return finalizeBlockResponse.AppHash, nil } func (blockExec *BlockExecutor) pruneBlocks(retainHeight int64) (uint64, error) { diff --git a/proto/tendermint/abci/types.proto b/proto/tendermint/abci/types.proto index 771c56a8f..aa9e3fcbe 100644 --- a/proto/tendermint/abci/types.proto +++ b/proto/tendermint/abci/types.proto @@ -249,7 +249,6 @@ message ResponseDeliverTx { message ResponseCommit { // reserve 1 - bytes data = 2; int64 retain_height = 3; } @@ -332,7 +331,6 @@ message ResponseFinalizeBlock { repeated ValidatorUpdate validator_updates = 3 [(gogoproto.nullable) = false]; tendermint.types.ConsensusParams consensus_param_updates = 4; bytes app_hash = 5; - int64 retain_height = 6; } //---------------------------------------- diff --git a/test/e2e/app/app.go b/test/e2e/app/app.go index 788f296b7..771b72643 100644 --- a/test/e2e/app/app.go +++ b/test/e2e/app/app.go @@ -210,6 +210,7 @@ func (app *Application) FinalizeBlock(_ context.Context, req *abci.RequestFinali return &abci.ResponseFinalizeBlock{ TxResults: txs, ValidatorUpdates: valUpdates, + AppHash: app.state.Finalize(), Events: []abci.Event{ { Type: "val_updates", @@ -233,7 +234,7 @@ func (app *Application) Commit(_ context.Context) (*abci.ResponseCommit, error) app.mu.Lock() defer app.mu.Unlock() - height, hash, err := app.state.Commit() + height, err := app.state.Commit() if err != nil { panic(err) } @@ -253,7 +254,6 @@ func (app *Application) Commit(_ context.Context) (*abci.ResponseCommit, error) retainHeight = int64(height - app.cfg.RetainBlocks + 1) } return &abci.ResponseCommit{ - Data: hash, RetainHeight: retainHeight, }, nil } diff --git a/test/e2e/app/state.go b/test/e2e/app/state.go index 17d8cd75f..91b1a32d5 100644 --- a/test/e2e/app/state.go +++ b/test/e2e/app/state.go @@ -137,8 +137,8 @@ func (s *State) Set(key, value string) { } } -// Commit commits the current state. -func (s *State) Commit() (uint64, []byte, error) { +// Finalize is called after applying a block, updating the height and returning the new app_hash +func (s *State) Finalize() []byte { s.Lock() defer s.Unlock() switch { @@ -150,13 +150,20 @@ func (s *State) Commit() (uint64, []byte, error) { s.Height = 1 } s.Hash = hashItems(s.Values, s.Height) + return s.Hash +} + +// Commit commits the current state. +func (s *State) Commit() (uint64, error) { + s.Lock() + defer s.Unlock() if s.persistInterval > 0 && s.Height%s.persistInterval == 0 { err := s.save() if err != nil { - return 0, nil, err + return 0, err } } - return s.Height, s.Hash, nil + return s.Height, nil } func (s *State) Rollback() error {