From 4e561f2cfd9bc78b7d930a2fe617f7d6335f5bf8 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Mon, 26 Apr 2021 11:31:34 -0600 Subject: [PATCH 01/31] add darksidewallet support for GetAddressUtxos Add a new darksidewallet gRPC to add a utxo that can be returned by the production GetAddressUtxos, for example: grpcurl -plaintext -d '{"address":"t1g1HQJuwDoStGrYYLj8VhLu1J5igd8TNXV","txid":"1zjB42Z7FtwRZOBNMlTubCgx9l3dsZSqXxmWpuZXJto=","script": "dqkU8saQsCVS4mNwcByoGCtfOaHFaCiIrA==","valueZat": "3010000","height": "686773"}' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/AddAddressUtxo Then the following returns this entry: grpcurl -plaintext -d '{"startHeight":0,"maxEntries":2,"addresses":["t1g1HQJuwDoStGrYYLj8VhLu1J5igd8TNXV"]}' localhost:9067 cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxos You can also clear the list of entries: grpcurl -plaintext localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/ClearAddressUtxo --- common/common.go | 2 +- common/darkside.go | 30 ++++++++++++++ frontend/service.go | 22 +++++++++- walletrpc/darkside.pb.go | 70 +++++++++++++++++++------------ walletrpc/darkside.proto | 7 ++++ walletrpc/darkside_grpc.pb.go | 78 +++++++++++++++++++++++++++++++++++ 6 files changed, 180 insertions(+), 29 deletions(-) diff --git a/common/common.go b/common/common.go index 2299a12..9d33d03 100644 --- a/common/common.go +++ b/common/common.go @@ -125,7 +125,7 @@ type ( ZcashdRpcRequestGetaddressutxos struct { Addresses []string `json:"addresses"` } - ZcashdRpcReplyGetaddressutxos []struct { + ZcashdRpcReplyGetaddressutxos struct { Address string Txid string OutputIndex int64 diff --git a/common/darkside.go b/common/darkside.go index 50919d5..535d9ae 100644 --- a/common/darkside.go +++ b/common/darkside.go @@ -46,6 +46,9 @@ type darksideState struct { // These transactions come from StageTransactions(); they will be merged into // activeBlocks by ApplyStaged() (and this list then cleared). stagedTransactions []stagedTx + + // Unordered list of replies + getAddressUtxos []ZcashdRpcReplyGetaddressutxos } var state darksideState @@ -459,6 +462,23 @@ func darksideRawRequest(method string, params []json.RawMessage) (json.RawMessag } return json.Marshal(reply) + case "getaddressutxos": + var req ZcashdRpcRequestGetaddressutxos + err := json.Unmarshal(params[0], &req) + if err != nil { + return nil, errors.New("failed to parse getaddressutxos JSON") + } + utxosReply := make([]ZcashdRpcReplyGetaddressutxos, 0) + for _, utxo := range state.getAddressUtxos { + for _, a := range req.Addresses { + if a == utxo.Address { + utxosReply = append(utxosReply, utxo) + break + } + } + } + return json.Marshal(utxosReply) + default: return nil, errors.New("there was an attempt to call an unsupported RPC") } @@ -585,3 +605,13 @@ func DarksideStageTransactionsURL(height int, url string) error { return scan.Err() } + +func DarksideAddAddressUtxo(arg ZcashdRpcReplyGetaddressutxos) error { + state.getAddressUtxos = append(state.getAddressUtxos, arg) + return nil +} + +func DarksideClearAddressUtxos() error { + state.getAddressUtxos = nil + return nil +} diff --git a/frontend/service.go b/frontend/service.go index c42727e..89251c6 100644 --- a/frontend/service.go +++ b/frontend/service.go @@ -527,7 +527,7 @@ func getAddressUtxos(arg *walletrpc.GetAddressUtxosArg, f func(*walletrpc.GetAdd if rpcErr != nil { return rpcErr } - var utxosReply common.ZcashdRpcReplyGetaddressutxos + var utxosReply []common.ZcashdRpcReplyGetaddressutxos err = json.Unmarshal(result, &utxosReply) if err != nil { return err @@ -706,3 +706,23 @@ func (s *DarksideStreamer) ClearIncomingTransactions(ctx context.Context, e *wal common.DarksideClearIncomingTransactions() return &walletrpc.Empty{}, nil } + +// AddAddressUtxo adds a UTXO which will be returned by GetAddressUtxos() (above) +func (s *DarksideStreamer) AddAddressUtxo(ctx context.Context, arg *walletrpc.GetAddressUtxosReply) (*walletrpc.Empty, error) { + utxosReply := common.ZcashdRpcReplyGetaddressutxos{ + Address: arg.Address, + Txid: hex.EncodeToString(parser.Reverse(arg.Txid)), + OutputIndex: int64(arg.Index), + Script: hex.EncodeToString(arg.Script), + Satoshis: uint64(arg.ValueZat), + Height: int(arg.Height), + } + err := common.DarksideAddAddressUtxo(utxosReply) + return &walletrpc.Empty{}, err +} + +// ClearAddressUtxo removes the list of cached utxo entries +func (s *DarksideStreamer) ClearAddressUtxo(ctx context.Context, arg *walletrpc.Empty) (*walletrpc.Empty, error) { + err := common.DarksideClearAddressUtxos() + return &walletrpc.Empty{}, err +} diff --git a/walletrpc/darkside.pb.go b/walletrpc/darkside.pb.go index 0ffa24e..77a2f1c 100644 --- a/walletrpc/darkside.pb.go +++ b/walletrpc/darkside.pb.go @@ -383,7 +383,7 @@ var file_darkside_proto_rawDesc = []byte{ 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x6e, 0x6f, 0x6e, 0x63, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0xda, 0x06, 0x0a, 0x10, + 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x32, 0x8b, 0x08, 0x0a, 0x10, 0x44, 0x61, 0x72, 0x6b, 0x73, 0x69, 0x64, 0x65, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x72, 0x12, 0x51, 0x0a, 0x05, 0x52, 0x65, 0x73, 0x65, 0x74, 0x12, 0x28, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, @@ -437,9 +437,20 @@ var file_darkside_proto_rawDesc = []byte{ 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, - 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, 0x67, 0x68, - 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, - 0x70, 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x5d, 0x0a, 0x0e, 0x41, 0x64, 0x64, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x12, 0x2b, 0x2e, 0x63, 0x61, 0x73, + 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, + 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, + 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x1a, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, + 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, + 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x12, 0x50, 0x0a, 0x10, 0x43, 0x6c, 0x65, 0x61, 0x72, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x12, 0x1c, 0x2e, 0x63, 0x61, + 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, + 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, + 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, + 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, 0x67, + 0x68, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x72, 0x70, 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -464,31 +475,36 @@ var file_darkside_proto_goTypes = []interface{}{ (*DarksideEmptyBlocks)(nil), // 5: cash.z.wallet.sdk.rpc.DarksideEmptyBlocks (*RawTransaction)(nil), // 6: cash.z.wallet.sdk.rpc.RawTransaction (*Empty)(nil), // 7: cash.z.wallet.sdk.rpc.Empty + (*GetAddressUtxosReply)(nil), // 8: cash.z.wallet.sdk.rpc.GetAddressUtxosReply } var file_darkside_proto_depIdxs = []int32{ - 0, // 0: cash.z.wallet.sdk.rpc.DarksideStreamer.Reset:input_type -> cash.z.wallet.sdk.rpc.DarksideMetaState - 1, // 1: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocksStream:input_type -> cash.z.wallet.sdk.rpc.DarksideBlock - 2, // 2: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocks:input_type -> cash.z.wallet.sdk.rpc.DarksideBlocksURL - 5, // 3: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocksCreate:input_type -> cash.z.wallet.sdk.rpc.DarksideEmptyBlocks - 6, // 4: cash.z.wallet.sdk.rpc.DarksideStreamer.StageTransactionsStream:input_type -> cash.z.wallet.sdk.rpc.RawTransaction - 3, // 5: cash.z.wallet.sdk.rpc.DarksideStreamer.StageTransactions:input_type -> cash.z.wallet.sdk.rpc.DarksideTransactionsURL - 4, // 6: cash.z.wallet.sdk.rpc.DarksideStreamer.ApplyStaged:input_type -> cash.z.wallet.sdk.rpc.DarksideHeight - 7, // 7: cash.z.wallet.sdk.rpc.DarksideStreamer.GetIncomingTransactions:input_type -> cash.z.wallet.sdk.rpc.Empty - 7, // 8: cash.z.wallet.sdk.rpc.DarksideStreamer.ClearIncomingTransactions:input_type -> cash.z.wallet.sdk.rpc.Empty - 7, // 9: cash.z.wallet.sdk.rpc.DarksideStreamer.Reset:output_type -> cash.z.wallet.sdk.rpc.Empty - 7, // 10: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocksStream:output_type -> cash.z.wallet.sdk.rpc.Empty - 7, // 11: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocks:output_type -> cash.z.wallet.sdk.rpc.Empty - 7, // 12: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocksCreate:output_type -> cash.z.wallet.sdk.rpc.Empty - 7, // 13: cash.z.wallet.sdk.rpc.DarksideStreamer.StageTransactionsStream:output_type -> cash.z.wallet.sdk.rpc.Empty - 7, // 14: cash.z.wallet.sdk.rpc.DarksideStreamer.StageTransactions:output_type -> cash.z.wallet.sdk.rpc.Empty - 7, // 15: cash.z.wallet.sdk.rpc.DarksideStreamer.ApplyStaged:output_type -> cash.z.wallet.sdk.rpc.Empty - 6, // 16: cash.z.wallet.sdk.rpc.DarksideStreamer.GetIncomingTransactions:output_type -> cash.z.wallet.sdk.rpc.RawTransaction - 7, // 17: cash.z.wallet.sdk.rpc.DarksideStreamer.ClearIncomingTransactions:output_type -> cash.z.wallet.sdk.rpc.Empty - 9, // [9:18] is the sub-list for method output_type - 0, // [0:9] is the sub-list for method input_type - 0, // [0:0] is the sub-list for extension type_name - 0, // [0:0] is the sub-list for extension extendee - 0, // [0:0] is the sub-list for field type_name + 0, // 0: cash.z.wallet.sdk.rpc.DarksideStreamer.Reset:input_type -> cash.z.wallet.sdk.rpc.DarksideMetaState + 1, // 1: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocksStream:input_type -> cash.z.wallet.sdk.rpc.DarksideBlock + 2, // 2: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocks:input_type -> cash.z.wallet.sdk.rpc.DarksideBlocksURL + 5, // 3: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocksCreate:input_type -> cash.z.wallet.sdk.rpc.DarksideEmptyBlocks + 6, // 4: cash.z.wallet.sdk.rpc.DarksideStreamer.StageTransactionsStream:input_type -> cash.z.wallet.sdk.rpc.RawTransaction + 3, // 5: cash.z.wallet.sdk.rpc.DarksideStreamer.StageTransactions:input_type -> cash.z.wallet.sdk.rpc.DarksideTransactionsURL + 4, // 6: cash.z.wallet.sdk.rpc.DarksideStreamer.ApplyStaged:input_type -> cash.z.wallet.sdk.rpc.DarksideHeight + 7, // 7: cash.z.wallet.sdk.rpc.DarksideStreamer.GetIncomingTransactions:input_type -> cash.z.wallet.sdk.rpc.Empty + 7, // 8: cash.z.wallet.sdk.rpc.DarksideStreamer.ClearIncomingTransactions:input_type -> cash.z.wallet.sdk.rpc.Empty + 8, // 9: cash.z.wallet.sdk.rpc.DarksideStreamer.AddAddressUtxo:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply + 7, // 10: cash.z.wallet.sdk.rpc.DarksideStreamer.ClearAddressUtxo:input_type -> cash.z.wallet.sdk.rpc.Empty + 7, // 11: cash.z.wallet.sdk.rpc.DarksideStreamer.Reset:output_type -> cash.z.wallet.sdk.rpc.Empty + 7, // 12: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocksStream:output_type -> cash.z.wallet.sdk.rpc.Empty + 7, // 13: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocks:output_type -> cash.z.wallet.sdk.rpc.Empty + 7, // 14: cash.z.wallet.sdk.rpc.DarksideStreamer.StageBlocksCreate:output_type -> cash.z.wallet.sdk.rpc.Empty + 7, // 15: cash.z.wallet.sdk.rpc.DarksideStreamer.StageTransactionsStream:output_type -> cash.z.wallet.sdk.rpc.Empty + 7, // 16: cash.z.wallet.sdk.rpc.DarksideStreamer.StageTransactions:output_type -> cash.z.wallet.sdk.rpc.Empty + 7, // 17: cash.z.wallet.sdk.rpc.DarksideStreamer.ApplyStaged:output_type -> cash.z.wallet.sdk.rpc.Empty + 6, // 18: cash.z.wallet.sdk.rpc.DarksideStreamer.GetIncomingTransactions:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 7, // 19: cash.z.wallet.sdk.rpc.DarksideStreamer.ClearIncomingTransactions:output_type -> cash.z.wallet.sdk.rpc.Empty + 7, // 20: cash.z.wallet.sdk.rpc.DarksideStreamer.AddAddressUtxo:output_type -> cash.z.wallet.sdk.rpc.Empty + 7, // 21: cash.z.wallet.sdk.rpc.DarksideStreamer.ClearAddressUtxo:output_type -> cash.z.wallet.sdk.rpc.Empty + 11, // [11:22] is the sub-list for method output_type + 0, // [0:11] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name } func init() { file_darkside_proto_init() } diff --git a/walletrpc/darkside.proto b/walletrpc/darkside.proto index e2e0341..f84e615 100644 --- a/walletrpc/darkside.proto +++ b/walletrpc/darkside.proto @@ -114,4 +114,11 @@ service DarksideStreamer { // Clear the incoming transaction pool. rpc ClearIncomingTransactions(Empty) returns (Empty) {} + + // Add a GetAddressUtxosReply entry to be returned by GetAddressUtxos(). + // There is no staging or applying for these, very simple. + rpc AddAddressUtxo(GetAddressUtxosReply) returns (Empty) {} + + // Clear the list of GetAddressUtxos entries (can't fail) + rpc ClearAddressUtxo(Empty) returns (Empty) {} } diff --git a/walletrpc/darkside_grpc.pb.go b/walletrpc/darkside_grpc.pb.go index 4ebd58f..ad65f01 100644 --- a/walletrpc/darkside_grpc.pb.go +++ b/walletrpc/darkside_grpc.pb.go @@ -76,6 +76,11 @@ type DarksideStreamerClient interface { GetIncomingTransactions(ctx context.Context, in *Empty, opts ...grpc.CallOption) (DarksideStreamer_GetIncomingTransactionsClient, error) // Clear the incoming transaction pool. ClearIncomingTransactions(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) + // Add a GetAddressUtxosReply entry to be returned by GetAddressUtxos(). + // There is no staging or applying for these, very simple. + AddAddressUtxo(ctx context.Context, in *GetAddressUtxosReply, opts ...grpc.CallOption) (*Empty, error) + // Clear the list of GetAddressUtxos entries (can't fail) + ClearAddressUtxo(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) } type darksideStreamerClient struct { @@ -240,6 +245,24 @@ func (c *darksideStreamerClient) ClearIncomingTransactions(ctx context.Context, return out, nil } +func (c *darksideStreamerClient) AddAddressUtxo(ctx context.Context, in *GetAddressUtxosReply, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, "/cash.z.wallet.sdk.rpc.DarksideStreamer/AddAddressUtxo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *darksideStreamerClient) ClearAddressUtxo(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) { + out := new(Empty) + err := c.cc.Invoke(ctx, "/cash.z.wallet.sdk.rpc.DarksideStreamer/ClearAddressUtxo", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // DarksideStreamerServer is the server API for DarksideStreamer service. // All implementations must embed UnimplementedDarksideStreamerServer // for forward compatibility @@ -302,6 +325,11 @@ type DarksideStreamerServer interface { GetIncomingTransactions(*Empty, DarksideStreamer_GetIncomingTransactionsServer) error // Clear the incoming transaction pool. ClearIncomingTransactions(context.Context, *Empty) (*Empty, error) + // Add a GetAddressUtxosReply entry to be returned by GetAddressUtxos(). + // There is no staging or applying for these, very simple. + AddAddressUtxo(context.Context, *GetAddressUtxosReply) (*Empty, error) + // Clear the list of GetAddressUtxos entries (can't fail) + ClearAddressUtxo(context.Context, *Empty) (*Empty, error) mustEmbedUnimplementedDarksideStreamerServer() } @@ -336,6 +364,12 @@ func (UnimplementedDarksideStreamerServer) GetIncomingTransactions(*Empty, Darks func (UnimplementedDarksideStreamerServer) ClearIncomingTransactions(context.Context, *Empty) (*Empty, error) { return nil, status.Errorf(codes.Unimplemented, "method ClearIncomingTransactions not implemented") } +func (UnimplementedDarksideStreamerServer) AddAddressUtxo(context.Context, *GetAddressUtxosReply) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddAddressUtxo not implemented") +} +func (UnimplementedDarksideStreamerServer) ClearAddressUtxo(context.Context, *Empty) (*Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method ClearAddressUtxo not implemented") +} func (UnimplementedDarksideStreamerServer) mustEmbedUnimplementedDarksideStreamerServer() {} // UnsafeDarksideStreamerServer may be embedded to opt out of forward compatibility for this service. @@ -530,6 +564,42 @@ func _DarksideStreamer_ClearIncomingTransactions_Handler(srv interface{}, ctx co return interceptor(ctx, in, info, handler) } +func _DarksideStreamer_AddAddressUtxo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetAddressUtxosReply) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DarksideStreamerServer).AddAddressUtxo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cash.z.wallet.sdk.rpc.DarksideStreamer/AddAddressUtxo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DarksideStreamerServer).AddAddressUtxo(ctx, req.(*GetAddressUtxosReply)) + } + return interceptor(ctx, in, info, handler) +} + +func _DarksideStreamer_ClearAddressUtxo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(Empty) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(DarksideStreamerServer).ClearAddressUtxo(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/cash.z.wallet.sdk.rpc.DarksideStreamer/ClearAddressUtxo", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(DarksideStreamerServer).ClearAddressUtxo(ctx, req.(*Empty)) + } + return interceptor(ctx, in, info, handler) +} + // DarksideStreamer_ServiceDesc is the grpc.ServiceDesc for DarksideStreamer service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -561,6 +631,14 @@ var DarksideStreamer_ServiceDesc = grpc.ServiceDesc{ MethodName: "ClearIncomingTransactions", Handler: _DarksideStreamer_ClearIncomingTransactions_Handler, }, + { + MethodName: "AddAddressUtxo", + Handler: _DarksideStreamer_AddAddressUtxo_Handler, + }, + { + MethodName: "ClearAddressUtxo", + Handler: _DarksideStreamer_ClearAddressUtxo_Handler, + }, }, Streams: []grpc.StreamDesc{ { From a4cc08b89eddd88befc36931422c53e4d4190185 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Wed, 5 May 2021 21:50:14 -0600 Subject: [PATCH 02/31] GetBlockRange: allow start < end, return blocks in reverse order --- common/common.go | 15 ++++++-- common/common_test.go | 83 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/common/common.go b/common/common.go index 9d33d03..7ded94e 100644 --- a/common/common.go +++ b/common/common.go @@ -406,8 +406,19 @@ func GetBlock(cache *BlockCache, height int) (*walletrpc.CompactBlock, error) { // GetBlockRange returns a sequence of consecutive blocks in the given range. func GetBlockRange(cache *BlockCache, blockOut chan<- *walletrpc.CompactBlock, errOut chan<- error, start, end int) { // Go over [start, end] inclusive - for i := start; i <= end; i++ { - block, err := GetBlock(cache, i) + low := start + high := end + if start > end { + // reverse the order + low, high = end, start + } + for i := low; i <= high; i++ { + j := i + if start > end { + // reverse the order + j = high - (i - low) + } + block, err := GetBlock(cache, j) if err != nil { errOut <- err return diff --git a/common/common_test.go b/common/common_test.go index aea0c97..a664862 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -325,12 +325,87 @@ func TestGetBlockRange(t *testing.T) { t.Fatal("reading height 22 should have failed") } - // check goroutine GetBlockRange() reaching the end of the range (and exiting) - go GetBlockRange(testcache, blockChan, errChan, 1, 0) - err := <-errChan + step = 0 + os.RemoveAll(unitTestPath) +} + +// There are four test blocks, 0..3 +func getblockStubReverse(method string, params []json.RawMessage) (json.RawMessage, error) { + var height string + err := json.Unmarshal(params[0], &height) if err != nil { - t.Fatal("unexpected err return") + testT.Fatal("could not unmarshal height") } + + step++ + switch step { + case 1: + if height != "380642" { + testT.Error("unexpected height") + } + // Sunny-day + return blocks[2], nil + case 2: + if height != "380641" { + testT.Error("unexpected height") + } + // Sunny-day + return blocks[1], nil + case 3: + if height != "380640" { + testT.Error("unexpected height") + } + // Sunny-day + return blocks[0], nil + } + testT.Error("getblockStub called too many times") + return nil, nil +} + +func TestGetBlockRangeReverse(t *testing.T) { + testT = t + RawRequest = getblockStubReverse + os.RemoveAll(unitTestPath) + testcache := NewBlockCache(unitTestPath, unitTestChain, 380640, true) + blockChan := make(chan *walletrpc.CompactBlock) + errChan := make(chan error) + + // Request the blocks in reverse order by specifying start greater than end + go GetBlockRange(testcache, blockChan, errChan, 380642, 380640) + + // read in block 380642 + select { + case err := <-errChan: + // this will also catch context.DeadlineExceeded from the timeout + t.Fatal("unexpected error:", err) + case cBlock := <-blockChan: + if cBlock.Height != 380642 { + t.Fatal("unexpected Height:", cBlock.Height) + } + } + + // read in block 380641 + select { + case err := <-errChan: + // this will also catch context.DeadlineExceeded from the timeout + t.Fatal("unexpected error:", err) + case cBlock := <-blockChan: + if cBlock.Height != 380641 { + t.Fatal("unexpected Height:", cBlock.Height) + } + } + + // read in block 380640 + select { + case err := <-errChan: + // this will also catch context.DeadlineExceeded from the timeout + t.Fatal("unexpected error:", err) + case cBlock := <-blockChan: + if cBlock.Height != 380640 { + t.Fatal("unexpected Height:", cBlock.Height) + } + } + step = 0 os.RemoveAll(unitTestPath) } From 39179a972176e3f6a6d84a37127b5f79ad1486ae Mon Sep 17 00:00:00 2001 From: Aditya Kulkarni Date: Mon, 26 Jul 2021 13:13:25 -0700 Subject: [PATCH 03/31] Return hashes from getlatestblock --- frontend/service.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/service.go b/frontend/service.go index 89251c6..8744172 100644 --- a/frontend/service.go +++ b/frontend/service.go @@ -58,13 +58,13 @@ func checkTaddress(taddr string) error { // GetLatestBlock returns the height of the best chain, according to zcashd. func (s *lwdStreamer) GetLatestBlock(ctx context.Context, placeholder *walletrpc.ChainSpec) (*walletrpc.BlockID, error) { latestBlock := s.cache.GetLatestHeight() + latestHash := s.cache.GetLatestHash() if latestBlock == -1 { return nil, errors.New("Cache is empty. Server is probably not yet ready") } - // TODO: also return block hashes here - return &walletrpc.BlockID{Height: uint64(latestBlock)}, nil + return &walletrpc.BlockID{Height: uint64(latestBlock), Hash: latestHash}, nil } // GetTaddressTxids is a streaming RPC that returns transaction IDs that have From 3f669c3d19705d104ca1dda380f874c0027eafe9 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Mon, 26 Jul 2021 23:14:05 -0600 Subject: [PATCH 04/31] added unit test --- .gitignore | 1 + frontend/frontend_test.go | 3 +++ 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index e3d8393..a93329e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ lwd-api.html *.orig __debug_bin .vscode +frontend/unittestcache/ diff --git a/frontend/frontend_test.go b/frontend/frontend_test.go index ccc8851..65a2c26 100644 --- a/frontend/frontend_test.go +++ b/frontend/frontend_test.go @@ -191,6 +191,9 @@ func TestGetLatestBlock(t *testing.T) { if blockID.Height != 380640 { t.Fatal("unexpected blockID.height") } + if string(blockID.Hash) != string(block.Hash) { + t.Fatal("unexpected blockID.hash") + } step = 0 } From c3e1b98d4f031fb45f9bbe37cde604895ca39ff7 Mon Sep 17 00:00:00 2001 From: Aditya Kulkarni Date: Wed, 14 Jul 2021 14:48:57 -0700 Subject: [PATCH 05/31] Mempool Streaming API --- cmd/root.go | 5 ++ common/mempool.go | 177 ++++++++++++++++++++++++++++++++++++++++ frontend/service.go | 21 +++++ walletrpc/service.proto | 4 + 4 files changed, 207 insertions(+) create mode 100644 common/mempool.go diff --git a/cmd/root.go b/cmd/root.go index 48ab97c..3a61e4a 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -267,6 +267,10 @@ func startServer(opts *common.Options) error { walletrpc.RegisterDarksideStreamerServer(server, service) } + // Initialize mempool monitor + exitMempool := make(chan bool) + common.StartMempoolMonitor(cache, exitMempool) + // Start listening listener, err := net.Listen("tcp", opts.GRPCBindAddr) if err != nil { @@ -282,6 +286,7 @@ func startServer(opts *common.Options) error { go func() { s := <-signals cache.Sync() + exitMempool <- true common.Log.WithFields(logrus.Fields{ "signal": s.String(), }).Info("caught signal, stopping gRPC server") diff --git a/common/mempool.go b/common/mempool.go new file mode 100644 index 0000000..7cdccbf --- /dev/null +++ b/common/mempool.go @@ -0,0 +1,177 @@ +package common + +import ( + "encoding/hex" + "encoding/json" + "sync" + "sync/atomic" + "time" + + "github.com/zcash/lightwalletd/walletrpc" +) + +var ( + // List of all mempool transactions + txns map[string]*walletrpc.RawTransaction = make(map[string]*walletrpc.RawTransaction) + + // List of all clients waiting to recieve mempool txns + clients []chan<- *walletrpc.RawTransaction + + // Last height of the blocks. If this changes, then close all the clients and flush the mempool + lastHeight int + + // A pointer to the blockcache + blockcache *BlockCache + + // Mutex to lock the above 2 structs + lock sync.Mutex + + // Since the mutex doesn't have a "try_lock" method, we'll have to improvize with this + refreshing int32 = 0 +) + +// AddNewClient adds a new client to the list of clients to notify for mempool txns +func AddNewClient(client chan<- *walletrpc.RawTransaction) { + lock.Lock() + defer lock.Unlock() + + //Log.Infoln("Adding new client, sending ", len(txns), " transactions") + + // Also send all pending mempool txns + for _, rtx := range txns { + if client != nil { + client <- rtx + } + } + + if client != nil { + clients = append(clients, client) + } +} + +// RefreshMempoolTxns gets all new mempool txns and sends any new ones to waiting clients +func refreshMempoolTxns() error { + Log.Infoln("Refreshing mempool") + + // First check if another refresh is running, if it is, just return + if !atomic.CompareAndSwapInt32(&refreshing, 0, 1) { + Log.Warnln("Another refresh in progress, returning") + return nil + } + + // Set refreshing to 0 when we exit + defer func() { + refreshing = 0 + }() + + // Check if the blockchain has changed, and if it has, then clear everything + + lock.Lock() + defer lock.Unlock() + + if lastHeight < blockcache.GetLatestHeight() { + Log.Infoln("Block height changed, clearing everything") + + // Flush all the clients + for _, client := range clients { + if client != nil { + close(client) + } + } + + clients = make([]chan<- *walletrpc.RawTransaction, 0) + + // Clear txns + txns = make(map[string]*walletrpc.RawTransaction) + + lastHeight = blockcache.GetLatestHeight() + } + + var mempoolList []string + params := make([]json.RawMessage, 0) + result, rpcErr := RawRequest("getrawmempool", params) + if rpcErr != nil { + return rpcErr + } + err := json.Unmarshal(result, &mempoolList) + if err != nil { + return err + } + + //println("getrawmempool size ", len(mempoolList)) + + // Fetch all new mempool txns and add them into `newTxns` + for _, txidstr := range mempoolList { + if _, ok := txns[txidstr]; !ok { + txidJSON, err := json.Marshal(txidstr) + if err != nil { + return err + } + // The "0" is because we only need the raw hex, which is returned as + // just a hex string, and not even a json string (with quotes). + params := []json.RawMessage{txidJSON, json.RawMessage("0")} + result, rpcErr := RawRequest("getrawtransaction", params) + if rpcErr != nil { + // Not an error; mempool transactions can disappear + continue + } + // strip the quotes + var txStr string + err = json.Unmarshal(result, &txStr) + if err != nil { + return err + } + + // conver to binary + txBytes, err := hex.DecodeString(txStr) + if err != nil { + return err + } + + newRtx := &walletrpc.RawTransaction{ + Data: txBytes, + Height: uint64(lastHeight), + } + + // Notify waiting clients + for _, client := range clients { + if client != nil { + client <- newRtx + } + } + + Log.Infoln("Adding new mempool txid", txidstr, " sending to ", len(clients), " clients") + txns[txidstr] = newRtx + } + } + + return nil +} + +// StartMempoolMonitor starts monitoring the mempool +func StartMempoolMonitor(cache *BlockCache, done <-chan bool) { + go func() { + ticker := time.NewTicker(2 * time.Second) + blockcache = cache + lastHeight = blockcache.GetLatestHeight() + + for { + select { + case <-ticker.C: + go func() { + //Log.Infoln("Ticker triggered") + err := refreshMempoolTxns() + if err != nil { + Log.Errorln("Mempool refresh error:", err.Error()) + } + }() + + case <-done: + for _, client := range clients { + close(client) + } + return + } + } + }() +} diff --git a/frontend/service.go b/frontend/service.go index 8744172..a22c8de 100644 --- a/frontend/service.go +++ b/frontend/service.go @@ -380,6 +380,27 @@ func (s *lwdStreamer) GetTaddressBalanceStream(addresses walletrpc.CompactTxStre return nil } +func (s *lwdStreamer) GetMempoolStream(_empty *walletrpc.Empty, resp walletrpc.CompactTxStreamer_GetMempoolStreamServer) error { + ch := make(chan *walletrpc.RawTransaction, 200) + go common.AddNewClient(ch) + + for { + select { + case rtx, more := <-ch: + if !more || rtx == nil { + return nil + } + + if resp.Send(rtx) != nil { + return nil + } + // Timeout after 5 mins + case <-time.After(5 * time.Minute): + return nil + } + } +} + // Key is 32-byte txid (as a 64-character string), data is pointer to compact tx. var mempoolMap *map[string]*walletrpc.CompactTx var mempoolList []string diff --git a/walletrpc/service.proto b/walletrpc/service.proto index 8021e3f..e43ec66 100644 --- a/walletrpc/service.proto +++ b/walletrpc/service.proto @@ -164,6 +164,10 @@ service CompactTxStreamer { // in the exclude list that don't exist in the mempool are ignored. rpc GetMempoolTx(Exclude) returns (stream CompactTx) {} + // Return a stream of current Mempool transactions. This will keep the output stream open while + // there are mempool transactions. It will close the returned stream when a new block is mined. + rpc GetMempoolStream(Empty) returns (stream RawTransaction) {} + // GetTreeState returns the note commitment tree state corresponding to the given block. // See section 3.7 of the Zcash protocol specification. It returns several other useful // values also (even though they can be obtained using GetBlock). From 503b9b256bcb0c36c6182b76be3ab7c659c45dae Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Mon, 19 Jul 2021 11:04:09 -0600 Subject: [PATCH 06/31] modifications to adityapk00 mempool streaming API This commit is based on adityapk00 streaming mempool interface but avoids using goroutines, which are difficult to reason about. Co-authored-by: Aditya Kulkarni --- cmd/root.go | 5 - common/common.go | 1 + common/mempool.go | 244 ++++++++++++++++------------------- frontend/service.go | 22 +--- walletrpc/service.pb.go | 117 +++++++++-------- walletrpc/service.proto | 3 +- walletrpc/service_grpc.pb.go | 69 +++++++++- 7 files changed, 249 insertions(+), 212 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 3a61e4a..48ab97c 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -267,10 +267,6 @@ func startServer(opts *common.Options) error { walletrpc.RegisterDarksideStreamerServer(server, service) } - // Initialize mempool monitor - exitMempool := make(chan bool) - common.StartMempoolMonitor(cache, exitMempool) - // Start listening listener, err := net.Listen("tcp", opts.GRPCBindAddr) if err != nil { @@ -286,7 +282,6 @@ func startServer(opts *common.Options) error { go func() { s := <-signals cache.Sync() - exitMempool <- true common.Log.WithFields(logrus.Fields{ "signal": s.String(), }).Info("caught signal, stopping gRPC server") diff --git a/common/common.go b/common/common.go index 7ded94e..cca4cc7 100644 --- a/common/common.go +++ b/common/common.go @@ -77,6 +77,7 @@ type ( Chain string Upgrades map[string]Upgradeinfo Blocks int + BestBlockHash string Consensus ConsensusInfo EstimatedHeight int } diff --git a/common/mempool.go b/common/mempool.go index 7cdccbf..4e7126c 100644 --- a/common/mempool.go +++ b/common/mempool.go @@ -4,174 +4,152 @@ import ( "encoding/hex" "encoding/json" "sync" - "sync/atomic" "time" "github.com/zcash/lightwalletd/walletrpc" ) +type txid string + var ( - // List of all mempool transactions - txns map[string]*walletrpc.RawTransaction = make(map[string]*walletrpc.RawTransaction) + // Set of mempool txids that have been seen during the current block interval. + // The zcashd RPC `getrawmempool` returns the entire mempool each time, so + // this allows us to ignore the txids that we've already seen. + g_txidSeen map[txid]struct{} = map[txid]struct{}{} - // List of all clients waiting to recieve mempool txns - clients []chan<- *walletrpc.RawTransaction + // List of transactions during current block interval, in order received. Each + // client thread can keep an index into this slice to record which transactions + // it's sent back to the client (everything before that index). The g_txidSeen + // map allows this list to not contain duplicates. + g_txList []*walletrpc.RawTransaction - // Last height of the blocks. If this changes, then close all the clients and flush the mempool - lastHeight int + // The most recent absolute time that we fetched the mempool and the latest + // (tip) block hash (so we know when a new block has been mined). + g_lastTime time.Time - // A pointer to the blockcache - blockcache *BlockCache + // The most recent zcashd getblockchaininfo reply, for height and best block + // hash (tip) which is used to detect when a new block arrives. + g_lastBlockChainInfo *ZcashdRpcReplyGetblockchaininfo = &ZcashdRpcReplyGetblockchaininfo{} - // Mutex to lock the above 2 structs - lock sync.Mutex - - // Since the mutex doesn't have a "try_lock" method, we'll have to improvize with this - refreshing int32 = 0 + // Mutex to protect the above variables. + g_lock sync.Mutex ) -// AddNewClient adds a new client to the list of clients to notify for mempool txns -func AddNewClient(client chan<- *walletrpc.RawTransaction) { - lock.Lock() - defer lock.Unlock() +func GetMempool(sendToClient func(*walletrpc.RawTransaction) error) error { + g_lock.Lock() + index := 0 + // Stay in this function until the tip block hash changes. + stayHash := g_lastBlockChainInfo.BestBlockHash - //Log.Infoln("Adding new client, sending ", len(txns), " transactions") - - // Also send all pending mempool txns - for _, rtx := range txns { - if client != nil { - client <- rtx + // Wait for more transactions to be added to the list + for { + // Don't fetch the mempool more often than every 2 seconds. + if time.Since(g_lastTime) > 2*time.Second { + blockChainInfo, err := getLatestBlockChainInfo() + if err != nil { + g_lock.Unlock() + return err + } + if g_lastBlockChainInfo.BestBlockHash != blockChainInfo.BestBlockHash { + // A new block has arrived + g_lastBlockChainInfo = blockChainInfo + Log.Infoln("Latest Block changed, clearing everything") + // We're the first thread to notice, clear cached state. + g_txidSeen = map[txid]struct{}{} + g_txList = []*walletrpc.RawTransaction{} + g_lastTime = time.Time{} + break + } + if err = refreshMempoolTxns(); err != nil { + g_lock.Unlock() + return err + } + g_lastTime = time.Now() + } + // Send transactions we haven't sent yet, best to not do so while + // holding the mutex, since this call may get flow-controlled. + toSend := g_txList[index:] + index = len(g_txList) + g_lock.Unlock() + for _, tx := range toSend { + if err := sendToClient(tx); err != nil { + return err + } + } + time.Sleep(200 * time.Millisecond) + g_lock.Lock() + if g_lastBlockChainInfo.BestBlockHash != stayHash { + break } } - - if client != nil { - clients = append(clients, client) - } + g_lock.Unlock() + return nil } // RefreshMempoolTxns gets all new mempool txns and sends any new ones to waiting clients func refreshMempoolTxns() error { Log.Infoln("Refreshing mempool") - // First check if another refresh is running, if it is, just return - if !atomic.CompareAndSwapInt32(&refreshing, 0, 1) { - Log.Warnln("Another refresh in progress, returning") - return nil - } - - // Set refreshing to 0 when we exit - defer func() { - refreshing = 0 - }() - - // Check if the blockchain has changed, and if it has, then clear everything - - lock.Lock() - defer lock.Unlock() - - if lastHeight < blockcache.GetLatestHeight() { - Log.Infoln("Block height changed, clearing everything") - - // Flush all the clients - for _, client := range clients { - if client != nil { - close(client) - } - } - - clients = make([]chan<- *walletrpc.RawTransaction, 0) - - // Clear txns - txns = make(map[string]*walletrpc.RawTransaction) - - lastHeight = blockcache.GetLatestHeight() - } - - var mempoolList []string - params := make([]json.RawMessage, 0) + params := []json.RawMessage{} result, rpcErr := RawRequest("getrawmempool", params) if rpcErr != nil { return rpcErr } + var mempoolList []string err := json.Unmarshal(result, &mempoolList) if err != nil { return err } - //println("getrawmempool size ", len(mempoolList)) - // Fetch all new mempool txns and add them into `newTxns` for _, txidstr := range mempoolList { - if _, ok := txns[txidstr]; !ok { - txidJSON, err := json.Marshal(txidstr) - if err != nil { - return err - } - // The "0" is because we only need the raw hex, which is returned as - // just a hex string, and not even a json string (with quotes). - params := []json.RawMessage{txidJSON, json.RawMessage("0")} - result, rpcErr := RawRequest("getrawtransaction", params) - if rpcErr != nil { - // Not an error; mempool transactions can disappear - continue - } - // strip the quotes - var txStr string - err = json.Unmarshal(result, &txStr) - if err != nil { - return err - } - - // conver to binary - txBytes, err := hex.DecodeString(txStr) - if err != nil { - return err - } - - newRtx := &walletrpc.RawTransaction{ - Data: txBytes, - Height: uint64(lastHeight), - } - - // Notify waiting clients - for _, client := range clients { - if client != nil { - client <- newRtx - } - } - - Log.Infoln("Adding new mempool txid", txidstr, " sending to ", len(clients), " clients") - txns[txidstr] = newRtx + if _, ok := g_txidSeen[txid(txidstr)]; ok { + // We've already fetched this transaction + continue } + g_txidSeen[txid(txidstr)] = struct{}{} + // We haven't fetched this transaction already. + txidJSON, err := json.Marshal(txidstr) + if err != nil { + return err + } + // The "0" is because we only need the raw hex, which is returned as + // just a hex string, and not even a json string (with quotes). + params := []json.RawMessage{txidJSON, json.RawMessage("0")} + result, rpcErr := RawRequest("getrawtransaction", params) + if rpcErr != nil { + // Not an error; mempool transactions can disappear + continue + } + // strip the quotes + var txStr string + err = json.Unmarshal(result, &txStr) + if err != nil { + return err + } + txBytes, err := hex.DecodeString(txStr) + if err != nil { + return err + } + Log.Infoln("appending", txidstr) + newRtx := &walletrpc.RawTransaction{ + Data: txBytes, + Height: uint64(g_lastBlockChainInfo.Blocks), + } + g_txList = append(g_txList, newRtx) } - return nil } -// StartMempoolMonitor starts monitoring the mempool -func StartMempoolMonitor(cache *BlockCache, done <-chan bool) { - go func() { - ticker := time.NewTicker(2 * time.Second) - blockcache = cache - lastHeight = blockcache.GetLatestHeight() - - for { - select { - case <-ticker.C: - go func() { - //Log.Infoln("Ticker triggered") - err := refreshMempoolTxns() - if err != nil { - Log.Errorln("Mempool refresh error:", err.Error()) - } - }() - - case <-done: - for _, client := range clients { - close(client) - } - return - } - } - }() +func getLatestBlockChainInfo() (*ZcashdRpcReplyGetblockchaininfo, error) { + result, rpcErr := RawRequest("getblockchaininfo", []json.RawMessage{}) + if rpcErr != nil { + return nil, rpcErr + } + var getblockchaininfoReply ZcashdRpcReplyGetblockchaininfo + err := json.Unmarshal(result, &getblockchaininfoReply) + if err != nil { + return nil, err + } + return &getblockchaininfoReply, nil } diff --git a/frontend/service.go b/frontend/service.go index a22c8de..d00ba6c 100644 --- a/frontend/service.go +++ b/frontend/service.go @@ -381,24 +381,10 @@ func (s *lwdStreamer) GetTaddressBalanceStream(addresses walletrpc.CompactTxStre } func (s *lwdStreamer) GetMempoolStream(_empty *walletrpc.Empty, resp walletrpc.CompactTxStreamer_GetMempoolStreamServer) error { - ch := make(chan *walletrpc.RawTransaction, 200) - go common.AddNewClient(ch) - - for { - select { - case rtx, more := <-ch: - if !more || rtx == nil { - return nil - } - - if resp.Send(rtx) != nil { - return nil - } - // Timeout after 5 mins - case <-time.After(5 * time.Minute): - return nil - } - } + err := common.GetMempool(func(tx *walletrpc.RawTransaction) error { + return resp.Send(tx) + }) + return err } // Key is 32-byte txid (as a 64-character string), data is pointer to compact tx. diff --git a/walletrpc/service.pb.go b/walletrpc/service.pb.go index 4b13421..8333790 100644 --- a/walletrpc/service.pb.go +++ b/walletrpc/service.pb.go @@ -205,7 +205,8 @@ func (x *TxFilter) GetHash() []byte { } // RawTransaction contains the complete transaction data. It also optionally includes -// the block height in which the transaction was included. +// the block height in which the transaction was included, or, when returned +// by GetMempoolStream(), the latest block height. type RawTransaction struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1303,7 +1304,7 @@ var file_service_proto_rawDesc = []byte{ 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x52, 0x0c, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x32, 0xbb, 0x0a, 0x0a, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x32, 0x98, 0x0b, 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, @@ -1357,39 +1358,45 @@ var file_service_proto_rawDesc = []byte{ 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0c, - 0x47, 0x65, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, - 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, - 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x20, 0x2e, 0x63, - 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, - 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x00, - 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, - 0x78, 0x6f, 0x73, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2f, + 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x10, + 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, - 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x22, - 0x00, 0x12, 0x73, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, - 0x74, 0x78, 0x6f, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, - 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, - 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, - 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, - 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, - 0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x67, - 0x68, 0x74, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, + 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0c, 0x47, 0x65, 0x74, + 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, + 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, + 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, + 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, + 0x63, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, + 0x0f, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, + 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2f, 0x2e, 0x63, 0x61, + 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, + 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, + 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x73, + 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, + 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, - 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, - 0x67, 0x68, 0x74, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x04, 0x50, 0x69, - 0x6e, 0x67, 0x12, 0x1f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x69, 0x6e, 0x67, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, - 0x67, 0x68, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x72, 0x70, 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, + 0x72, 0x67, 0x1a, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x64, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, + 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, + 0x1f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, 0x67, 0x68, 0x74, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, + 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1443,27 +1450,29 @@ var file_service_proto_depIdxs = []int32{ 12, // 11: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:input_type -> cash.z.wallet.sdk.rpc.AddressList 11, // 12: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:input_type -> cash.z.wallet.sdk.rpc.Address 14, // 13: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolTx:input_type -> cash.z.wallet.sdk.rpc.Exclude - 0, // 14: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:input_type -> cash.z.wallet.sdk.rpc.BlockID - 16, // 15: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg - 16, // 16: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg - 6, // 17: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:input_type -> cash.z.wallet.sdk.rpc.Empty - 9, // 18: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:input_type -> cash.z.wallet.sdk.rpc.Duration - 0, // 19: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLatestBlock:output_type -> cash.z.wallet.sdk.rpc.BlockID - 19, // 20: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlock:output_type -> cash.z.wallet.sdk.rpc.CompactBlock - 19, // 21: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlockRange:output_type -> cash.z.wallet.sdk.rpc.CompactBlock - 3, // 22: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTransaction:output_type -> cash.z.wallet.sdk.rpc.RawTransaction - 4, // 23: cash.z.wallet.sdk.rpc.CompactTxStreamer.SendTransaction:output_type -> cash.z.wallet.sdk.rpc.SendResponse - 3, // 24: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:output_type -> cash.z.wallet.sdk.rpc.RawTransaction - 13, // 25: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:output_type -> cash.z.wallet.sdk.rpc.Balance - 13, // 26: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:output_type -> cash.z.wallet.sdk.rpc.Balance - 20, // 27: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolTx:output_type -> cash.z.wallet.sdk.rpc.CompactTx - 15, // 28: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:output_type -> cash.z.wallet.sdk.rpc.TreeState - 18, // 29: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList - 17, // 30: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply - 7, // 31: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:output_type -> cash.z.wallet.sdk.rpc.LightdInfo - 10, // 32: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:output_type -> cash.z.wallet.sdk.rpc.PingResponse - 19, // [19:33] is the sub-list for method output_type - 5, // [5:19] is the sub-list for method input_type + 6, // 14: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolStream:input_type -> cash.z.wallet.sdk.rpc.Empty + 0, // 15: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:input_type -> cash.z.wallet.sdk.rpc.BlockID + 16, // 16: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg + 16, // 17: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg + 6, // 18: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:input_type -> cash.z.wallet.sdk.rpc.Empty + 9, // 19: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:input_type -> cash.z.wallet.sdk.rpc.Duration + 0, // 20: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLatestBlock:output_type -> cash.z.wallet.sdk.rpc.BlockID + 19, // 21: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlock:output_type -> cash.z.wallet.sdk.rpc.CompactBlock + 19, // 22: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlockRange:output_type -> cash.z.wallet.sdk.rpc.CompactBlock + 3, // 23: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTransaction:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 4, // 24: cash.z.wallet.sdk.rpc.CompactTxStreamer.SendTransaction:output_type -> cash.z.wallet.sdk.rpc.SendResponse + 3, // 25: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 13, // 26: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:output_type -> cash.z.wallet.sdk.rpc.Balance + 13, // 27: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:output_type -> cash.z.wallet.sdk.rpc.Balance + 20, // 28: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolTx:output_type -> cash.z.wallet.sdk.rpc.CompactTx + 3, // 29: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolStream:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 15, // 30: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:output_type -> cash.z.wallet.sdk.rpc.TreeState + 18, // 31: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList + 17, // 32: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply + 7, // 33: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:output_type -> cash.z.wallet.sdk.rpc.LightdInfo + 10, // 34: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:output_type -> cash.z.wallet.sdk.rpc.PingResponse + 20, // [20:35] is the sub-list for method output_type + 5, // [5:20] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name diff --git a/walletrpc/service.proto b/walletrpc/service.proto index e43ec66..91d0ada 100644 --- a/walletrpc/service.proto +++ b/walletrpc/service.proto @@ -32,7 +32,8 @@ message TxFilter { } // RawTransaction contains the complete transaction data. It also optionally includes -// the block height in which the transaction was included. +// the block height in which the transaction was included, or, when returned +// by GetMempoolStream(), the latest block height. message RawTransaction { bytes data = 1; // exact data returned by Zcash 'getrawtransaction' uint64 height = 2; // height that the transaction was mined (or -1) diff --git a/walletrpc/service_grpc.pb.go b/walletrpc/service_grpc.pb.go index da227dd..b23cc80 100644 --- a/walletrpc/service_grpc.pb.go +++ b/walletrpc/service_grpc.pb.go @@ -42,6 +42,9 @@ type CompactTxStreamerClient interface { // match a shortened txid, they are all sent (none is excluded). Transactions // in the exclude list that don't exist in the mempool are ignored. GetMempoolTx(ctx context.Context, in *Exclude, opts ...grpc.CallOption) (CompactTxStreamer_GetMempoolTxClient, error) + // Return a stream of current Mempool transactions. This will keep the output stream open while + // there are mempool transactions. It will close the returned stream when a new block is mined. + GetMempoolStream(ctx context.Context, in *Empty, opts ...grpc.CallOption) (CompactTxStreamer_GetMempoolStreamClient, error) // GetTreeState returns the note commitment tree state corresponding to the given block. // See section 3.7 of the Zcash protocol specification. It returns several other useful // values also (even though they can be obtained using GetBlock). @@ -238,6 +241,38 @@ func (x *compactTxStreamerGetMempoolTxClient) Recv() (*CompactTx, error) { return m, nil } +func (c *compactTxStreamerClient) GetMempoolStream(ctx context.Context, in *Empty, opts ...grpc.CallOption) (CompactTxStreamer_GetMempoolStreamClient, error) { + stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[4], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolStream", opts...) + if err != nil { + return nil, err + } + x := &compactTxStreamerGetMempoolStreamClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type CompactTxStreamer_GetMempoolStreamClient interface { + Recv() (*RawTransaction, error) + grpc.ClientStream +} + +type compactTxStreamerGetMempoolStreamClient struct { + grpc.ClientStream +} + +func (x *compactTxStreamerGetMempoolStreamClient) Recv() (*RawTransaction, error) { + m := new(RawTransaction) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func (c *compactTxStreamerClient) GetTreeState(ctx context.Context, in *BlockID, opts ...grpc.CallOption) (*TreeState, error) { out := new(TreeState) err := c.cc.Invoke(ctx, "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetTreeState", in, out, opts...) @@ -257,7 +292,7 @@ func (c *compactTxStreamerClient) GetAddressUtxos(ctx context.Context, in *GetAd } func (c *compactTxStreamerClient) GetAddressUtxosStream(ctx context.Context, in *GetAddressUtxosArg, opts ...grpc.CallOption) (CompactTxStreamer_GetAddressUtxosStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[4], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxosStream", opts...) + stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[5], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxosStream", opts...) if err != nil { return nil, err } @@ -334,6 +369,9 @@ type CompactTxStreamerServer interface { // match a shortened txid, they are all sent (none is excluded). Transactions // in the exclude list that don't exist in the mempool are ignored. GetMempoolTx(*Exclude, CompactTxStreamer_GetMempoolTxServer) error + // Return a stream of current Mempool transactions. This will keep the output stream open while + // there are mempool transactions. It will close the returned stream when a new block is mined. + GetMempoolStream(*Empty, CompactTxStreamer_GetMempoolStreamServer) error // GetTreeState returns the note commitment tree state corresponding to the given block. // See section 3.7 of the Zcash protocol specification. It returns several other useful // values also (even though they can be obtained using GetBlock). @@ -379,6 +417,9 @@ func (UnimplementedCompactTxStreamerServer) GetTaddressBalanceStream(CompactTxSt func (UnimplementedCompactTxStreamerServer) GetMempoolTx(*Exclude, CompactTxStreamer_GetMempoolTxServer) error { return status.Errorf(codes.Unimplemented, "method GetMempoolTx not implemented") } +func (UnimplementedCompactTxStreamerServer) GetMempoolStream(*Empty, CompactTxStreamer_GetMempoolStreamServer) error { + return status.Errorf(codes.Unimplemented, "method GetMempoolStream not implemented") +} func (UnimplementedCompactTxStreamerServer) GetTreeState(context.Context, *BlockID) (*TreeState, error) { return nil, status.Errorf(codes.Unimplemented, "method GetTreeState not implemented") } @@ -586,6 +627,27 @@ func (x *compactTxStreamerGetMempoolTxServer) Send(m *CompactTx) error { return x.ServerStream.SendMsg(m) } +func _CompactTxStreamer_GetMempoolStream_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(Empty) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(CompactTxStreamerServer).GetMempoolStream(m, &compactTxStreamerGetMempoolStreamServer{stream}) +} + +type CompactTxStreamer_GetMempoolStreamServer interface { + Send(*RawTransaction) error + grpc.ServerStream +} + +type compactTxStreamerGetMempoolStreamServer struct { + grpc.ServerStream +} + +func (x *compactTxStreamerGetMempoolStreamServer) Send(m *RawTransaction) error { + return x.ServerStream.SendMsg(m) +} + func _CompactTxStreamer_GetTreeState_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(BlockID) if err := dec(in); err != nil { @@ -744,6 +806,11 @@ var CompactTxStreamer_ServiceDesc = grpc.ServiceDesc{ Handler: _CompactTxStreamer_GetMempoolTx_Handler, ServerStreams: true, }, + { + StreamName: "GetMempoolStream", + Handler: _CompactTxStreamer_GetMempoolStream_Handler, + ServerStreams: true, + }, { StreamName: "GetAddressUtxosStream", Handler: _CompactTxStreamer_GetAddressUtxosStream_Handler, From 4f00783f48baacd3d25ab9820e9eef811d4577e9 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Fri, 23 Jul 2021 13:31:42 -0600 Subject: [PATCH 07/31] remove gRPC GetMempoolTx This RPC has never been used, and is now superceded by GetMempoolStream. --- frontend/frontend_test.go | 55 ----- frontend/service.go | 131 ------------ walletrpc/service.pb.go | 390 ++++++++++++++--------------------- walletrpc/service.proto | 15 -- walletrpc/service_grpc.pb.go | 85 +------- 5 files changed, 162 insertions(+), 514 deletions(-) diff --git a/frontend/frontend_test.go b/frontend/frontend_test.go index 65a2c26..d1af75a 100644 --- a/frontend/frontend_test.go +++ b/frontend/frontend_test.go @@ -540,58 +540,3 @@ func TestNewZRPCFromConf(t *testing.T) { t.Fatal("NewZRPCFromClient unexpected success") } } - -func TestMempoolFilter(t *testing.T) { - txidlist := []string{ - "2e819d0bab5c819dc7d5f92d1bfb4127ce321daf847f6602", - "29e594c312eee49bc2c9ad37367ba58f857c4a7387ec9715", - "d4d090e60bf9141c6573f0598b84cc1f9817543e55a4d84d", - "d4714779c6dd32a72077bd79d4a70cb2153b552d7addec15", - "9839c1d4deca000656caff57c1f720f4fbd114b52239edde", - "ce5a28854a509ab309faa433542e73414fef6e903a3d52f5", - } - exclude := []string{ - "98aa", // common prefix (98) but no match - "19", // no match - "29", // one match (should not appear) - "d4", // 2 matches (both should appear in result) - "ce5a28854a509ab309faa433542e73414fef6e903a3d52f5", // exact match - "ce5a28854a509ab309faa433542e73414fef6e903a3d52f500", // extra stuff ignored - } - expected := []string{ - "2e819d0bab5c819dc7d5f92d1bfb4127ce321daf847f6602", - "9839c1d4deca000656caff57c1f720f4fbd114b52239edde", - "d4714779c6dd32a72077bd79d4a70cb2153b552d7addec15", - "d4d090e60bf9141c6573f0598b84cc1f9817543e55a4d84d", - } - actual := MempoolFilter(txidlist, exclude) - if len(actual) != len(expected) { - t.Fatal("mempool: wrong number of filter results") - } - for i := 0; i < len(actual); i++ { - if actual[i] != expected[i] { - t.Fatal(fmt.Sprintf("mempool: expected: %s actual: %s", - expected[i], actual[i])) - } - } - // If the exclude list is empty, return the entire mempool. - actual = MempoolFilter(txidlist, []string{}) - expected = []string{ - "29e594c312eee49bc2c9ad37367ba58f857c4a7387ec9715", - "2e819d0bab5c819dc7d5f92d1bfb4127ce321daf847f6602", - "9839c1d4deca000656caff57c1f720f4fbd114b52239edde", - "ce5a28854a509ab309faa433542e73414fef6e903a3d52f5", - "d4714779c6dd32a72077bd79d4a70cb2153b552d7addec15", - "d4d090e60bf9141c6573f0598b84cc1f9817543e55a4d84d", - } - if len(actual) != len(expected) { - t.Fatal("mempool: wrong number of filter results") - } - for i := 0; i < len(actual); i++ { - if actual[i] != expected[i] { - t.Fatal(fmt.Sprintf("mempool: expected: %s actual: %s", - expected[i], actual[i])) - } - } - -} diff --git a/frontend/service.go b/frontend/service.go index d00ba6c..0c9cc08 100644 --- a/frontend/service.go +++ b/frontend/service.go @@ -12,7 +12,6 @@ import ( "errors" "io" "regexp" - "sort" "strconv" "strings" "sync/atomic" @@ -387,134 +386,6 @@ func (s *lwdStreamer) GetMempoolStream(_empty *walletrpc.Empty, resp walletrpc.C return err } -// Key is 32-byte txid (as a 64-character string), data is pointer to compact tx. -var mempoolMap *map[string]*walletrpc.CompactTx -var mempoolList []string - -// Last time we pulled a copy of the mempool from zcashd. -var lastMempool time.Time - -func (s *lwdStreamer) GetMempoolTx(exclude *walletrpc.Exclude, resp walletrpc.CompactTxStreamer_GetMempoolTxServer) error { - if time.Now().Sub(lastMempool).Seconds() >= 2 { - lastMempool = time.Now() - // Refresh our copy of the mempool. - params := make([]json.RawMessage, 0) - result, rpcErr := common.RawRequest("getrawmempool", params) - if rpcErr != nil { - return rpcErr - } - err := json.Unmarshal(result, &mempoolList) - if err != nil { - return err - } - newmempoolMap := make(map[string]*walletrpc.CompactTx) - if mempoolMap == nil { - mempoolMap = &newmempoolMap - } - for _, txidstr := range mempoolList { - if ctx, ok := (*mempoolMap)[txidstr]; ok { - // This ctx has already been fetched, copy pointer to it. - newmempoolMap[txidstr] = ctx - continue - } - txidJSON, err := json.Marshal(txidstr) - if err != nil { - return err - } - // The "0" is because we only need the raw hex, which is returned as - // just a hex string, and not even a json string (with quotes). - params := []json.RawMessage{txidJSON, json.RawMessage("0")} - result, rpcErr := common.RawRequest("getrawtransaction", params) - if rpcErr != nil { - // Not an error; mempool transactions can disappear - continue - } - // strip the quotes - var txStr string - err = json.Unmarshal(result, &txStr) - if err != nil { - return err - } - - // conver to binary - txBytes, err := hex.DecodeString(txStr) - if err != nil { - return err - } - tx := parser.NewTransaction() - txdata, err := tx.ParseFromSlice(txBytes) - if len(txdata) > 0 { - return errors.New("extra data deserializing transaction") - } - newmempoolMap[txidstr] = &walletrpc.CompactTx{} - if tx.HasSaplingElements() { - newmempoolMap[txidstr] = tx.ToCompact( /* height */ 0) - } - } - mempoolMap = &newmempoolMap - } - excludeHex := make([]string, len(exclude.Txid)) - for i := 0; i < len(exclude.Txid); i++ { - excludeHex[i] = hex.EncodeToString(parser.Reverse(exclude.Txid[i])) - } - for _, txid := range MempoolFilter(mempoolList, excludeHex) { - tx := (*mempoolMap)[txid] - if len(tx.Hash) > 0 { - err := resp.Send(tx) - if err != nil { - return err - } - } - } - return nil -} - -// Return the subset of items that aren't excluded, but -// if more than one item matches an exclude entry, return -// all those items. -func MempoolFilter(items, exclude []string) []string { - sort.Slice(items, func(i, j int) bool { - return items[i] < items[j] - }) - sort.Slice(exclude, func(i, j int) bool { - return exclude[i] < exclude[j] - }) - // Determine how many items match each exclude item. - nmatches := make([]int, len(exclude)) - // is the exclude string less than the item string? - lessthan := func(e, i string) bool { - l := len(e) - if l > len(i) { - l = len(i) - } - return e < i[0:l] - } - ei := 0 - for _, item := range items { - for ei < len(exclude) && lessthan(exclude[ei], item) { - ei++ - } - match := ei < len(exclude) && strings.HasPrefix(item, exclude[ei]) - if match { - nmatches[ei]++ - } - } - - // Add each item that isn't uniquely excluded to the results. - tosend := make([]string, 0) - ei = 0 - for _, item := range items { - for ei < len(exclude) && lessthan(exclude[ei], item) { - ei++ - } - match := ei < len(exclude) && strings.HasPrefix(item, exclude[ei]) - if !match || nmatches[ei] > 1 { - tosend = append(tosend, item) - } - } - return tosend -} - func getAddressUtxos(arg *walletrpc.GetAddressUtxosArg, f func(*walletrpc.GetAddressUtxosReply) error) error { for _, a := range arg.Addresses { if err := checkTaddress(a); err != nil { @@ -625,8 +496,6 @@ func (s *DarksideStreamer) Reset(ctx context.Context, ms *walletrpc.DarksideMeta if err != nil { return nil, err } - mempoolMap = nil - mempoolList = nil return &walletrpc.Empty{}, nil } diff --git a/walletrpc/service.pb.go b/walletrpc/service.pb.go index 8333790..9b3e228 100644 --- a/walletrpc/service.pb.go +++ b/walletrpc/service.pb.go @@ -857,53 +857,6 @@ func (x *Balance) GetValueZat() int64 { return 0 } -type Exclude struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Txid [][]byte `protobuf:"bytes,1,rep,name=txid,proto3" json:"txid,omitempty"` -} - -func (x *Exclude) Reset() { - *x = Exclude{} - if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[14] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Exclude) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Exclude) ProtoMessage() {} - -func (x *Exclude) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[14] - if protoimpl.UnsafeEnabled && x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use Exclude.ProtoReflect.Descriptor instead. -func (*Exclude) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{14} -} - -func (x *Exclude) GetTxid() [][]byte { - if x != nil { - return x.Txid - } - return nil -} - // The TreeState is derived from the Zcash z_gettreestate rpc. type TreeState struct { state protoimpl.MessageState @@ -920,7 +873,7 @@ type TreeState struct { func (x *TreeState) Reset() { *x = TreeState{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[15] + mi := &file_service_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -933,7 +886,7 @@ func (x *TreeState) String() string { func (*TreeState) ProtoMessage() {} func (x *TreeState) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[15] + mi := &file_service_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -946,7 +899,7 @@ func (x *TreeState) ProtoReflect() protoreflect.Message { // Deprecated: Use TreeState.ProtoReflect.Descriptor instead. func (*TreeState) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{15} + return file_service_proto_rawDescGZIP(), []int{14} } func (x *TreeState) GetNetwork() string { @@ -999,7 +952,7 @@ type GetAddressUtxosArg struct { func (x *GetAddressUtxosArg) Reset() { *x = GetAddressUtxosArg{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[16] + mi := &file_service_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1012,7 +965,7 @@ func (x *GetAddressUtxosArg) String() string { func (*GetAddressUtxosArg) ProtoMessage() {} func (x *GetAddressUtxosArg) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[16] + mi := &file_service_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1025,7 +978,7 @@ func (x *GetAddressUtxosArg) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAddressUtxosArg.ProtoReflect.Descriptor instead. func (*GetAddressUtxosArg) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{16} + return file_service_proto_rawDescGZIP(), []int{15} } func (x *GetAddressUtxosArg) GetAddresses() []string { @@ -1065,7 +1018,7 @@ type GetAddressUtxosReply struct { func (x *GetAddressUtxosReply) Reset() { *x = GetAddressUtxosReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[17] + mi := &file_service_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1078,7 +1031,7 @@ func (x *GetAddressUtxosReply) String() string { func (*GetAddressUtxosReply) ProtoMessage() {} func (x *GetAddressUtxosReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[17] + mi := &file_service_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1091,7 +1044,7 @@ func (x *GetAddressUtxosReply) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAddressUtxosReply.ProtoReflect.Descriptor instead. func (*GetAddressUtxosReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{17} + return file_service_proto_rawDescGZIP(), []int{16} } func (x *GetAddressUtxosReply) GetAddress() string { @@ -1147,7 +1100,7 @@ type GetAddressUtxosReplyList struct { func (x *GetAddressUtxosReplyList) Reset() { *x = GetAddressUtxosReplyList{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[18] + mi := &file_service_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1160,7 +1113,7 @@ func (x *GetAddressUtxosReplyList) String() string { func (*GetAddressUtxosReplyList) ProtoMessage() {} func (x *GetAddressUtxosReplyList) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[18] + mi := &file_service_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1173,7 +1126,7 @@ func (x *GetAddressUtxosReplyList) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAddressUtxosReplyList.ProtoReflect.Descriptor instead. func (*GetAddressUtxosReplyList) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{18} + return file_service_proto_rawDescGZIP(), []int{17} } func (x *GetAddressUtxosReplyList) GetAddressUtxos() []*GetAddressUtxosReply { @@ -1270,133 +1223,126 @@ var file_service_proto_rawDesc = []byte{ 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x25, 0x0a, 0x07, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x22, 0x1d, 0x0a, 0x07, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, - 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, - 0x04, 0x74, 0x78, 0x69, 0x64, 0x22, 0x79, 0x0a, 0x09, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x0a, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, - 0x22, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, - 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x45, 0x6e, 0x74, - 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x45, - 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, - 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x14, 0x0a, - 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, - 0x6b, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, - 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0c, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x52, 0x0c, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x32, 0x98, 0x0b, 0x0a, - 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0d, 0x47, - 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x21, 0x2e, 0x63, - 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, - 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x1a, - 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, - 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x63, 0x61, 0x73, - 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, - 0x70, 0x63, 0x2e, 0x54, 0x78, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0x25, 0x2e, 0x63, 0x61, - 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, + 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x22, 0x79, 0x0a, 0x09, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x0a, + 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, + 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x72, 0x65, + 0x65, 0x22, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, + 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x45, 0x6e, + 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x78, + 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x14, + 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x22, 0x6b, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, + 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0c, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x52, + 0x0c, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x32, 0xc2, 0x0a, + 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x23, - 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, - 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x54, 0x78, 0x69, 0x64, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x61, 0x73, 0x68, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x08, 0x47, 0x65, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, + 0x6d, 0x70, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x21, 0x2e, + 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, + 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x63, 0x61, + 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, + 0x72, 0x70, 0x63, 0x2e, 0x54, 0x78, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0x25, 0x2e, 0x63, + 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, + 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, + 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, + 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x78, 0x69, 0x64, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x61, 0x73, + 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, + 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x1a, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x12, 0x47, + 0x65, 0x74, 0x54, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x12, 0x22, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x54, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x5b, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x65, + 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1c, 0x2e, 0x63, 0x61, + 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, + 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, - 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, - 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x12, 0x47, 0x65, - 0x74, 0x54, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x12, 0x22, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x54, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, - 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x78, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, - 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x10, - 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x25, - 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, - 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0c, 0x47, 0x65, 0x74, - 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, - 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, - 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, - 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, - 0x0f, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, - 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2f, 0x2e, 0x63, 0x61, + 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x65, + 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, - 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x73, - 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, - 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, - 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, - 0x72, 0x67, 0x1a, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, + 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, + 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x15, 0x47, 0x65, 0x74, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, - 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x64, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, - 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, - 0x1f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, - 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, 0x67, 0x68, 0x74, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2b, 0x2e, + 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, + 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, + 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, + 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x21, 0x2e, + 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, + 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x64, 0x49, 0x6e, 0x66, 0x6f, + 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x2e, 0x63, 0x61, 0x73, + 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, + 0x70, 0x63, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x23, 0x2e, 0x63, 0x61, + 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, + 0x72, 0x70, 0x63, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0xba, 0x02, 0x00, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1411,7 +1357,7 @@ func file_service_proto_rawDescGZIP() []byte { return file_service_proto_rawDescData } -var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 19) +var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 18) var file_service_proto_goTypes = []interface{}{ (*BlockID)(nil), // 0: cash.z.wallet.sdk.rpc.BlockID (*BlockRange)(nil), // 1: cash.z.wallet.sdk.rpc.BlockRange @@ -1427,20 +1373,18 @@ var file_service_proto_goTypes = []interface{}{ (*Address)(nil), // 11: cash.z.wallet.sdk.rpc.Address (*AddressList)(nil), // 12: cash.z.wallet.sdk.rpc.AddressList (*Balance)(nil), // 13: cash.z.wallet.sdk.rpc.Balance - (*Exclude)(nil), // 14: cash.z.wallet.sdk.rpc.Exclude - (*TreeState)(nil), // 15: cash.z.wallet.sdk.rpc.TreeState - (*GetAddressUtxosArg)(nil), // 16: cash.z.wallet.sdk.rpc.GetAddressUtxosArg - (*GetAddressUtxosReply)(nil), // 17: cash.z.wallet.sdk.rpc.GetAddressUtxosReply - (*GetAddressUtxosReplyList)(nil), // 18: cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList - (*CompactBlock)(nil), // 19: cash.z.wallet.sdk.rpc.CompactBlock - (*CompactTx)(nil), // 20: cash.z.wallet.sdk.rpc.CompactTx + (*TreeState)(nil), // 14: cash.z.wallet.sdk.rpc.TreeState + (*GetAddressUtxosArg)(nil), // 15: cash.z.wallet.sdk.rpc.GetAddressUtxosArg + (*GetAddressUtxosReply)(nil), // 16: cash.z.wallet.sdk.rpc.GetAddressUtxosReply + (*GetAddressUtxosReplyList)(nil), // 17: cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList + (*CompactBlock)(nil), // 18: cash.z.wallet.sdk.rpc.CompactBlock } var file_service_proto_depIdxs = []int32{ 0, // 0: cash.z.wallet.sdk.rpc.BlockRange.start:type_name -> cash.z.wallet.sdk.rpc.BlockID 0, // 1: cash.z.wallet.sdk.rpc.BlockRange.end:type_name -> cash.z.wallet.sdk.rpc.BlockID 0, // 2: cash.z.wallet.sdk.rpc.TxFilter.block:type_name -> cash.z.wallet.sdk.rpc.BlockID 1, // 3: cash.z.wallet.sdk.rpc.TransparentAddressBlockFilter.range:type_name -> cash.z.wallet.sdk.rpc.BlockRange - 17, // 4: cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList.addressUtxos:type_name -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply + 16, // 4: cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList.addressUtxos:type_name -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply 5, // 5: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLatestBlock:input_type -> cash.z.wallet.sdk.rpc.ChainSpec 0, // 6: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlock:input_type -> cash.z.wallet.sdk.rpc.BlockID 1, // 7: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlockRange:input_type -> cash.z.wallet.sdk.rpc.BlockRange @@ -1449,30 +1393,28 @@ var file_service_proto_depIdxs = []int32{ 8, // 10: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:input_type -> cash.z.wallet.sdk.rpc.TransparentAddressBlockFilter 12, // 11: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:input_type -> cash.z.wallet.sdk.rpc.AddressList 11, // 12: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:input_type -> cash.z.wallet.sdk.rpc.Address - 14, // 13: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolTx:input_type -> cash.z.wallet.sdk.rpc.Exclude - 6, // 14: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolStream:input_type -> cash.z.wallet.sdk.rpc.Empty - 0, // 15: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:input_type -> cash.z.wallet.sdk.rpc.BlockID - 16, // 16: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg - 16, // 17: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg - 6, // 18: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:input_type -> cash.z.wallet.sdk.rpc.Empty - 9, // 19: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:input_type -> cash.z.wallet.sdk.rpc.Duration - 0, // 20: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLatestBlock:output_type -> cash.z.wallet.sdk.rpc.BlockID - 19, // 21: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlock:output_type -> cash.z.wallet.sdk.rpc.CompactBlock - 19, // 22: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlockRange:output_type -> cash.z.wallet.sdk.rpc.CompactBlock - 3, // 23: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTransaction:output_type -> cash.z.wallet.sdk.rpc.RawTransaction - 4, // 24: cash.z.wallet.sdk.rpc.CompactTxStreamer.SendTransaction:output_type -> cash.z.wallet.sdk.rpc.SendResponse - 3, // 25: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:output_type -> cash.z.wallet.sdk.rpc.RawTransaction - 13, // 26: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:output_type -> cash.z.wallet.sdk.rpc.Balance - 13, // 27: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:output_type -> cash.z.wallet.sdk.rpc.Balance - 20, // 28: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolTx:output_type -> cash.z.wallet.sdk.rpc.CompactTx - 3, // 29: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolStream:output_type -> cash.z.wallet.sdk.rpc.RawTransaction - 15, // 30: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:output_type -> cash.z.wallet.sdk.rpc.TreeState - 18, // 31: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList - 17, // 32: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply - 7, // 33: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:output_type -> cash.z.wallet.sdk.rpc.LightdInfo - 10, // 34: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:output_type -> cash.z.wallet.sdk.rpc.PingResponse - 20, // [20:35] is the sub-list for method output_type - 5, // [5:20] is the sub-list for method input_type + 6, // 13: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolStream:input_type -> cash.z.wallet.sdk.rpc.Empty + 0, // 14: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:input_type -> cash.z.wallet.sdk.rpc.BlockID + 15, // 15: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg + 15, // 16: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg + 6, // 17: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:input_type -> cash.z.wallet.sdk.rpc.Empty + 9, // 18: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:input_type -> cash.z.wallet.sdk.rpc.Duration + 0, // 19: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLatestBlock:output_type -> cash.z.wallet.sdk.rpc.BlockID + 18, // 20: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlock:output_type -> cash.z.wallet.sdk.rpc.CompactBlock + 18, // 21: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlockRange:output_type -> cash.z.wallet.sdk.rpc.CompactBlock + 3, // 22: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTransaction:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 4, // 23: cash.z.wallet.sdk.rpc.CompactTxStreamer.SendTransaction:output_type -> cash.z.wallet.sdk.rpc.SendResponse + 3, // 24: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 13, // 25: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:output_type -> cash.z.wallet.sdk.rpc.Balance + 13, // 26: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:output_type -> cash.z.wallet.sdk.rpc.Balance + 3, // 27: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolStream:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 14, // 28: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:output_type -> cash.z.wallet.sdk.rpc.TreeState + 17, // 29: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList + 16, // 30: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply + 7, // 31: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:output_type -> cash.z.wallet.sdk.rpc.LightdInfo + 10, // 32: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:output_type -> cash.z.wallet.sdk.rpc.PingResponse + 19, // [19:33] is the sub-list for method output_type + 5, // [5:19] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name @@ -1654,18 +1596,6 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Exclude); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_service_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*TreeState); i { case 0: return &v.state @@ -1677,7 +1607,7 @@ func file_service_proto_init() { return nil } } - file_service_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + file_service_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAddressUtxosArg); i { case 0: return &v.state @@ -1689,7 +1619,7 @@ func file_service_proto_init() { return nil } } - file_service_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + file_service_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAddressUtxosReply); i { case 0: return &v.state @@ -1701,7 +1631,7 @@ func file_service_proto_init() { return nil } } - file_service_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + file_service_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAddressUtxosReplyList); i { case 0: return &v.state @@ -1720,7 +1650,7 @@ func file_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_service_proto_rawDesc, NumEnums: 0, - NumMessages: 19, + NumMessages: 18, NumExtensions: 0, NumServices: 1, }, diff --git a/walletrpc/service.proto b/walletrpc/service.proto index 91d0ada..e55d62c 100644 --- a/walletrpc/service.proto +++ b/walletrpc/service.proto @@ -104,10 +104,6 @@ message Balance { int64 valueZat = 1; } -message Exclude { - repeated bytes txid = 1; -} - // The TreeState is derived from the Zcash z_gettreestate rpc. message TreeState { string network = 1; // "main" or "test" @@ -154,17 +150,6 @@ service CompactTxStreamer { rpc GetTaddressBalance(AddressList) returns (Balance) {} rpc GetTaddressBalanceStream(stream Address) returns (Balance) {} - // Return the compact transactions currently in the mempool; the results - // can be a few seconds out of date. If the Exclude list is empty, return - // all transactions; otherwise return all *except* those in the Exclude list - // (if any); this allows the client to avoid receiving transactions that it - // already has (from an earlier call to this rpc). The transaction IDs in the - // Exclude list can be shortened to any number of bytes to make the request - // more bandwidth-efficient; if two or more transactions in the mempool - // match a shortened txid, they are all sent (none is excluded). Transactions - // in the exclude list that don't exist in the mempool are ignored. - rpc GetMempoolTx(Exclude) returns (stream CompactTx) {} - // Return a stream of current Mempool transactions. This will keep the output stream open while // there are mempool transactions. It will close the returned stream when a new block is mined. rpc GetMempoolStream(Empty) returns (stream RawTransaction) {} diff --git a/walletrpc/service_grpc.pb.go b/walletrpc/service_grpc.pb.go index b23cc80..a0d6cb6 100644 --- a/walletrpc/service_grpc.pb.go +++ b/walletrpc/service_grpc.pb.go @@ -32,16 +32,6 @@ type CompactTxStreamerClient interface { GetTaddressTxids(ctx context.Context, in *TransparentAddressBlockFilter, opts ...grpc.CallOption) (CompactTxStreamer_GetTaddressTxidsClient, error) GetTaddressBalance(ctx context.Context, in *AddressList, opts ...grpc.CallOption) (*Balance, error) GetTaddressBalanceStream(ctx context.Context, opts ...grpc.CallOption) (CompactTxStreamer_GetTaddressBalanceStreamClient, error) - // Return the compact transactions currently in the mempool; the results - // can be a few seconds out of date. If the Exclude list is empty, return - // all transactions; otherwise return all *except* those in the Exclude list - // (if any); this allows the client to avoid receiving transactions that it - // already has (from an earlier call to this rpc). The transaction IDs in the - // Exclude list can be shortened to any number of bytes to make the request - // more bandwidth-efficient; if two or more transactions in the mempool - // match a shortened txid, they are all sent (none is excluded). Transactions - // in the exclude list that don't exist in the mempool are ignored. - GetMempoolTx(ctx context.Context, in *Exclude, opts ...grpc.CallOption) (CompactTxStreamer_GetMempoolTxClient, error) // Return a stream of current Mempool transactions. This will keep the output stream open while // there are mempool transactions. It will close the returned stream when a new block is mined. GetMempoolStream(ctx context.Context, in *Empty, opts ...grpc.CallOption) (CompactTxStreamer_GetMempoolStreamClient, error) @@ -209,40 +199,8 @@ func (x *compactTxStreamerGetTaddressBalanceStreamClient) CloseAndRecv() (*Balan return m, nil } -func (c *compactTxStreamerClient) GetMempoolTx(ctx context.Context, in *Exclude, opts ...grpc.CallOption) (CompactTxStreamer_GetMempoolTxClient, error) { - stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[3], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolTx", opts...) - if err != nil { - return nil, err - } - x := &compactTxStreamerGetMempoolTxClient{stream} - if err := x.ClientStream.SendMsg(in); err != nil { - return nil, err - } - if err := x.ClientStream.CloseSend(); err != nil { - return nil, err - } - return x, nil -} - -type CompactTxStreamer_GetMempoolTxClient interface { - Recv() (*CompactTx, error) - grpc.ClientStream -} - -type compactTxStreamerGetMempoolTxClient struct { - grpc.ClientStream -} - -func (x *compactTxStreamerGetMempoolTxClient) Recv() (*CompactTx, error) { - m := new(CompactTx) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - func (c *compactTxStreamerClient) GetMempoolStream(ctx context.Context, in *Empty, opts ...grpc.CallOption) (CompactTxStreamer_GetMempoolStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[4], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolStream", opts...) + stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[3], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolStream", opts...) if err != nil { return nil, err } @@ -292,7 +250,7 @@ func (c *compactTxStreamerClient) GetAddressUtxos(ctx context.Context, in *GetAd } func (c *compactTxStreamerClient) GetAddressUtxosStream(ctx context.Context, in *GetAddressUtxosArg, opts ...grpc.CallOption) (CompactTxStreamer_GetAddressUtxosStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[5], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxosStream", opts...) + stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[4], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxosStream", opts...) if err != nil { return nil, err } @@ -359,16 +317,6 @@ type CompactTxStreamerServer interface { GetTaddressTxids(*TransparentAddressBlockFilter, CompactTxStreamer_GetTaddressTxidsServer) error GetTaddressBalance(context.Context, *AddressList) (*Balance, error) GetTaddressBalanceStream(CompactTxStreamer_GetTaddressBalanceStreamServer) error - // Return the compact transactions currently in the mempool; the results - // can be a few seconds out of date. If the Exclude list is empty, return - // all transactions; otherwise return all *except* those in the Exclude list - // (if any); this allows the client to avoid receiving transactions that it - // already has (from an earlier call to this rpc). The transaction IDs in the - // Exclude list can be shortened to any number of bytes to make the request - // more bandwidth-efficient; if two or more transactions in the mempool - // match a shortened txid, they are all sent (none is excluded). Transactions - // in the exclude list that don't exist in the mempool are ignored. - GetMempoolTx(*Exclude, CompactTxStreamer_GetMempoolTxServer) error // Return a stream of current Mempool transactions. This will keep the output stream open while // there are mempool transactions. It will close the returned stream when a new block is mined. GetMempoolStream(*Empty, CompactTxStreamer_GetMempoolStreamServer) error @@ -414,9 +362,6 @@ func (UnimplementedCompactTxStreamerServer) GetTaddressBalance(context.Context, func (UnimplementedCompactTxStreamerServer) GetTaddressBalanceStream(CompactTxStreamer_GetTaddressBalanceStreamServer) error { return status.Errorf(codes.Unimplemented, "method GetTaddressBalanceStream not implemented") } -func (UnimplementedCompactTxStreamerServer) GetMempoolTx(*Exclude, CompactTxStreamer_GetMempoolTxServer) error { - return status.Errorf(codes.Unimplemented, "method GetMempoolTx not implemented") -} func (UnimplementedCompactTxStreamerServer) GetMempoolStream(*Empty, CompactTxStreamer_GetMempoolStreamServer) error { return status.Errorf(codes.Unimplemented, "method GetMempoolStream not implemented") } @@ -606,27 +551,6 @@ func (x *compactTxStreamerGetTaddressBalanceStreamServer) Recv() (*Address, erro return m, nil } -func _CompactTxStreamer_GetMempoolTx_Handler(srv interface{}, stream grpc.ServerStream) error { - m := new(Exclude) - if err := stream.RecvMsg(m); err != nil { - return err - } - return srv.(CompactTxStreamerServer).GetMempoolTx(m, &compactTxStreamerGetMempoolTxServer{stream}) -} - -type CompactTxStreamer_GetMempoolTxServer interface { - Send(*CompactTx) error - grpc.ServerStream -} - -type compactTxStreamerGetMempoolTxServer struct { - grpc.ServerStream -} - -func (x *compactTxStreamerGetMempoolTxServer) Send(m *CompactTx) error { - return x.ServerStream.SendMsg(m) -} - func _CompactTxStreamer_GetMempoolStream_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(Empty) if err := stream.RecvMsg(m); err != nil { @@ -801,11 +725,6 @@ var CompactTxStreamer_ServiceDesc = grpc.ServiceDesc{ Handler: _CompactTxStreamer_GetTaddressBalanceStream_Handler, ClientStreams: true, }, - { - StreamName: "GetMempoolTx", - Handler: _CompactTxStreamer_GetMempoolTx_Handler, - ServerStreams: true, - }, { StreamName: "GetMempoolStream", Handler: _CompactTxStreamer_GetMempoolStream_Handler, From 6357f7f15b9ab1a0a52c76961a5a805e558f3b81 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Sun, 25 Jul 2021 07:47:07 -0600 Subject: [PATCH 08/31] Refactor mock Sleep, add mock Now time function Instead of the Sleep function pointer being a standalong global variable, move it into a new Time struct, and add a Now function pointer, so that time.Now() can be mocked. Time.Now() isn't used yet. This will be cleaner if we need to mock more time-related functions in the future. --- cmd/root.go | 5 +++-- common/common.go | 22 ++++++++++++++-------- common/common_test.go | 8 ++++++-- common/mempool.go | 4 ++-- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 48ab97c..e049db0 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -381,8 +381,9 @@ func init() { logrus.RegisterExitHandler(onexit) - // Indirect function for test mocking (so unit tests can talk to stub functions) - common.Sleep = time.Sleep + // Indirect functions for test mocking (so unit tests can talk to stub functions) + common.Time.Sleep = time.Sleep + common.Time.Now = time.Now } // initConfig reads in config file and ENV variables if set. diff --git a/common/common.go b/common/common.go index cca4cc7..9607cf8 100644 --- a/common/common.go +++ b/common/common.go @@ -53,10 +53,16 @@ type Options struct { // in unit tests it points to a function to mock RPCs to zcashd. var RawRequest func(method string, params []json.RawMessage) (json.RawMessage, error) -// Sleep allows a request to time.Sleep() to be mocked for testing; -// in production, it points to the standard library time.Sleep(); -// in unit tests it points to a mock function. -var Sleep func(d time.Duration) +// Time allows time-related functions to be mocked for testing, +// so that tests can be deterministic and so they don't require +// real time to elapse. In production, these point to the standard +// library `time` functions; in unit tests they point to mock +// functions (set by the specific test as required). +// More functions can be added later. +var Time struct { + Sleep func(d time.Duration) + Now func() time.Time +} // Log as a global variable simplifies logging var Log *logrus.Entry @@ -163,7 +169,7 @@ func FirstRPC() { "error": rpcErr.Error(), "retry": retryCount, }).Warn("error with getblockchaininfo rpc, retrying...") - Sleep(time.Duration(10+retryCount*5) * time.Second) // backoff + Time.Sleep(time.Duration(10+retryCount*5) * time.Second) // backoff } } @@ -312,7 +318,7 @@ func BlockIngestor(c *BlockCache, rep int) { } // Delay then retry the same height. c.Sync() - Sleep(10 * time.Second) + Time.Sleep(10 * time.Second) wait = true continue } @@ -323,7 +329,7 @@ func BlockIngestor(c *BlockCache, rep int) { Log.Info("Waiting for zcashd height to reach Sapling activation height ", "(", c.GetFirstHeight(), ")...") reorgCount = 0 - Sleep(20 * time.Second) + Time.Sleep(20 * time.Second) continue } if wait { @@ -333,7 +339,7 @@ func BlockIngestor(c *BlockCache, rep int) { Log.Info("Ingestor waiting for block: ", height) lastHeightLogged = height - 1 } - Sleep(2 * time.Second) + Time.Sleep(2 * time.Second) wait = false continue } diff --git a/common/common_test.go b/common/common_test.go index a664862..04e9ce7 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -77,6 +77,10 @@ func sleepStub(d time.Duration) { sleepCount++ sleepDuration += d } +func nowStub() time.Time { + start := time.Time{} + return start.Add(sleepDuration) +} // ------------------------------------------ GetLightdInfo() @@ -110,7 +114,7 @@ func getLightdInfoStub(method string, params []json.RawMessage) (json.RawMessage func TestGetLightdInfo(t *testing.T) { testT = t RawRequest = getLightdInfoStub - Sleep = sleepStub + Time.Sleep = sleepStub // This calls the getblockchaininfo rpc just to establish connectivity with zcashd FirstRPC() @@ -270,7 +274,7 @@ func getblockStub(method string, params []json.RawMessage) (json.RawMessage, err func TestBlockIngestor(t *testing.T) { testT = t RawRequest = getblockStub - Sleep = sleepStub + Time.Sleep = sleepStub os.RemoveAll(unitTestPath) testcache := NewBlockCache(unitTestPath, unitTestChain, 380640, false) BlockIngestor(testcache, 11) diff --git a/common/mempool.go b/common/mempool.go index 4e7126c..3674fea 100644 --- a/common/mempool.go +++ b/common/mempool.go @@ -64,7 +64,7 @@ func GetMempool(sendToClient func(*walletrpc.RawTransaction) error) error { g_lock.Unlock() return err } - g_lastTime = time.Now() + g_lastTime = Time.Now() } // Send transactions we haven't sent yet, best to not do so while // holding the mutex, since this call may get flow-controlled. @@ -76,7 +76,7 @@ func GetMempool(sendToClient func(*walletrpc.RawTransaction) error) error { return err } } - time.Sleep(200 * time.Millisecond) + Time.Sleep(200 * time.Millisecond) g_lock.Lock() if g_lastBlockChainInfo.BestBlockHash != stayHash { break From b1f3687d83c5f87ed669a454f98ecc7252fc2b17 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Mon, 26 Jul 2021 08:45:12 -0600 Subject: [PATCH 09/31] add MempoolStream unit test --- common/common.go | 3 +- common/common_test.go | 156 ++++++++++++++++++++++++++++++++++++++++++ common/mempool.go | 5 +- 3 files changed, 161 insertions(+), 3 deletions(-) diff --git a/common/common.go b/common/common.go index 9607cf8..2fe8323 100644 --- a/common/common.go +++ b/common/common.go @@ -114,7 +114,8 @@ type ( } } - // zcashd rpc "getrawtransaction" + // zcashd rpc "getrawtransaction txid 1" (1 means verbose), there are + // many more fields but these are the only ones we current need. ZcashdRpcReplyGetrawtransaction struct { Hex string Height int diff --git a/common/common_test.go b/common/common_test.go index 04e9ce7..5006806 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -5,6 +5,7 @@ package common import ( "bufio" + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -418,3 +419,158 @@ func TestGenerateCerts(t *testing.T) { t.Fatal("GenerateCerts returned nil") } } + +// ------------------------------------------ GetMempoolStream + +// Note that in mocking zcashd's RPC replies here, we don't really need +// actual txids or transactions, or even strings with the correct format +// for those, except that a transaction must be a hex string. +func mempoolStub(method string, params []json.RawMessage) (json.RawMessage, error) { + step++ + switch step { + case 1: + // This will be a getblockchaininfo request + if method != "getblockchaininfo" { + testT.Fatal("expecting blockchaininfo") + } + r, _ := json.Marshal(&ZcashdRpcReplyGetblockchaininfo{ + BestBlockHash: "010203", + Blocks: 200, + }) + return r, nil + case 2: + // No new block has arrived. + if method != "getblockchaininfo" { + testT.Fatal("expecting blockchaininfo") + } + r, _ := json.Marshal(&ZcashdRpcReplyGetblockchaininfo{ + BestBlockHash: "010203", + Blocks: 200, + }) + return r, nil + case 3: + // Expect a getrawmempool next. + if method != "getrawmempool" { + testT.Fatal("expecting getrawmempool") + } + // In reality, this would be a hex txid + r, _ := json.Marshal([]string{ + "mempooltxid-1", + }) + return r, nil + case 4: + // Next, it should ask for this transaction (non-verbose). + if method != "getrawtransaction" { + testT.Fatal("expecting getrawtransaction") + } + var txid string + json.Unmarshal(params[0], &txid) + if txid != "mempooltxid-1" { + testT.Fatal("unexpected txid") + } + r, _ := json.Marshal("aabb") + return r, nil + case 5: + // Simulate that still no new block has arrived ... + if method != "getblockchaininfo" { + testT.Fatal("expecting blockchaininfo") + } + r, _ := json.Marshal(&ZcashdRpcReplyGetblockchaininfo{ + BestBlockHash: "010203", + Blocks: 200, + }) + return r, nil + case 6: + // ... but there a second tx has arrived in the mempool + if method != "getrawmempool" { + testT.Fatal("expecting getrawmempool") + } + // In reality, this would be a hex txid + r, _ := json.Marshal([]string{ + "mempooltxid-2", + "mempooltxid-1"}) + return r, nil + case 7: + // The new mempool tx (and only that one) gets fetched + if method != "getrawtransaction" { + testT.Fatal("expecting getrawtransaction") + } + var txid string + json.Unmarshal(params[0], &txid) + if txid != "mempooltxid-2" { + testT.Fatal("unexpected txid") + } + r, _ := json.Marshal("ccdd") + return r, nil + case 8: + // A new block arrives, this will cause these two tx to be returned + if method != "getblockchaininfo" { + testT.Fatal("expecting blockchaininfo") + } + r, _ := json.Marshal(&ZcashdRpcReplyGetblockchaininfo{ + BestBlockHash: "d1d2d3", + Blocks: 201, + }) + return r, nil + } + testT.Fatal("ran out of cases") + return nil, nil +} + +func TestMempoolStream(t *testing.T) { + testT = t + RawRequest = mempoolStub + Time.Sleep = sleepStub + Time.Now = nowStub + // In real life, wall time is not close to zero, simulate that. + sleepDuration = 1000 * time.Second + + var replies []*walletrpc.RawTransaction + // The first request after startup immediately returns an empty list. + err := GetMempool(func(tx *walletrpc.RawTransaction) error { + t.Fatal("send to client function called on initial GetMempool call") + return nil + }) + if err != nil { + t.Fatal("GetMempool failed") + } + + // This should return two transactions. + err = GetMempool(func(tx *walletrpc.RawTransaction) error { + replies = append(replies, tx) + return nil + }) + if err != nil { + t.Fatal("GetMempool failed") + } + if len(replies) != 2 { + t.Fatal("unexpected number of tx") + } + // The interface guarantees that the transactions will be returned + // in the order they entered the mempool. + if !bytes.Equal([]byte(replies[0].GetData()), []byte{0xaa, 0xbb}) { + t.Fatal("unexpected tx contents") + } + if replies[0].GetHeight() != 200 { + t.Fatal("unexpected tx height") + } + if !bytes.Equal([]byte(replies[1].GetData()), []byte{0xcc, 0xdd}) { + t.Fatal("unexpected tx contents") + } + if replies[1].GetHeight() != 200 { + t.Fatal("unexpected tx height") + } + + // Time started at 1000 seconds (since 1970), and just over 4 seconds + // should have elapsed. The units here are nanoseconds. + if sleepDuration != 1004400000000 { + t.Fatal("unexpected end time") + } + if step != 8 { + t.Fatal("unexpected number of zcashd RPCs") + } + + step = 0 + sleepCount = 0 + sleepDuration = 0 +} diff --git a/common/mempool.go b/common/mempool.go index 3674fea..4ee9dc2 100644 --- a/common/mempool.go +++ b/common/mempool.go @@ -44,7 +44,8 @@ func GetMempool(sendToClient func(*walletrpc.RawTransaction) error) error { // Wait for more transactions to be added to the list for { // Don't fetch the mempool more often than every 2 seconds. - if time.Since(g_lastTime) > 2*time.Second { + now := Time.Now() + if now.After(g_lastTime.Add(2 * time.Second)) { blockChainInfo, err := getLatestBlockChainInfo() if err != nil { g_lock.Unlock() @@ -64,7 +65,7 @@ func GetMempool(sendToClient func(*walletrpc.RawTransaction) error) error { g_lock.Unlock() return err } - g_lastTime = Time.Now() + g_lastTime = now } // Send transactions we haven't sent yet, best to not do so while // holding the mutex, since this call may get flow-controlled. From bdaac63f3ee0dbef62bde04f6817a9f90d483b00 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Tue, 10 Aug 2021 23:29:48 -0600 Subject: [PATCH 10/31] improve reorg by using getbestblockhash --- common/cache.go | 6 +- common/common.go | 121 ++++++++------------- common/common_test.go | 232 ++++++++++++++++++++++++++++++++++++---- common/darkside.go | 34 ++++-- docs/darksidewalletd.md | 10 ++ 5 files changed, 297 insertions(+), 106 deletions(-) diff --git a/common/cache.go b/common/cache.go index 9cc7d5e..8b5ffb8 100644 --- a/common/cache.go +++ b/common/cache.go @@ -51,12 +51,12 @@ func (c *BlockCache) GetLatestHash() []byte { return c.latestHash } -// HashMismatch indicates if the given prev-hash doesn't match the most recent block's hash +// HashMatch indicates if the given prev-hash matches the most recent block's hash // so reorgs can be detected. -func (c *BlockCache) HashMismatch(prevhash []byte) bool { +func (c *BlockCache) HashMatch(prevhash []byte) bool { c.mutex.RLock() defer c.mutex.RUnlock() - return c.latestHash != nil && !bytes.Equal(c.latestHash, prevhash) + return c.latestHash == nil || bytes.Equal(c.latestHash, prevhash) } // Make the block at the given height the lowest height that we don't have. diff --git a/common/common.go b/common/common.go index 2fe8323..a3a1e22 100644 --- a/common/common.go +++ b/common/common.go @@ -226,7 +226,7 @@ func getBlockFromRPC(height int) (*walletrpc.CompactBlock, error) { params := make([]json.RawMessage, 2) heightJSON, err := json.Marshal(strconv.Itoa(height)) if err != nil { - return nil, errors.Wrap(err, "error marshaling height") + Log.Fatal("getBlockFromRPC bad height argument", height, err) } params[0] = heightJSON params[1] = json.RawMessage("0") // non-verbose (raw hex) @@ -289,11 +289,8 @@ func stopIngestor() { // BlockIngestor runs as a goroutine and polls zcashd for new blocks, adding them // to the cache. The repetition count, rep, is nonzero only for unit-testing. func BlockIngestor(c *BlockCache, rep int) { - lastLog := time.Now() - reorgCount := 0 + lastLog := Time.Now() lastHeightLogged := 0 - retryCount := 0 - wait := true // Start listening for new blocks for i := 0; rep == 0 || i < rep; i++ { @@ -304,88 +301,60 @@ func BlockIngestor(c *BlockCache, rep int) { default: } - height := c.GetNextHeight() - block, err := getBlockFromRPC(height) + result, err := RawRequest("getbestblockhash", []json.RawMessage{}) if err != nil { Log.WithFields(logrus.Fields{ - "height": height, - "error": err, - }).Warn("error zcashd getblock rpc") - retryCount++ - if retryCount > 10 { - Log.WithFields(logrus.Fields{ - "timeouts": retryCount, - }).Fatal("unable to issue RPC call to zcashd node") - } - // Delay then retry the same height. + "error": err, + }).Fatal("error zcashd getbestblockhash rpc") + } + var hashHex string + err = json.Unmarshal(result, &hashHex) + if err != nil { + Log.Fatal("bad getbestblockhash return:", err, result) + } + lastBestBlockHash := []byte{} + lastBestBlockHash, err = hex.DecodeString(hashHex) + if err != nil { + Log.Fatal("error decoding getbestblockhash", err, hashHex) + } + + height := c.GetNextHeight() + if string(lastBestBlockHash) == string(parser.Reverse(c.GetLatestHash())) { + // Synced c.Sync() - Time.Sleep(10 * time.Second) - wait = true + if lastHeightLogged != height-1 { + lastHeightLogged = height - 1 + Log.Info("Waiting for block: ", height) + } + Time.Sleep(2 * time.Second) + lastLog = Time.Now() continue } - retryCount = 0 - if block == nil { - // No block at this height. - if height == c.GetFirstHeight() { - Log.Info("Waiting for zcashd height to reach Sapling activation height ", - "(", c.GetFirstHeight(), ")...") - reorgCount = 0 - Time.Sleep(20 * time.Second) - continue - } - if wait { - // Wait a bit then retry the same height. - c.Sync() - if lastHeightLogged+1 != height { - Log.Info("Ingestor waiting for block: ", height) - lastHeightLogged = height - 1 - } - Time.Sleep(2 * time.Second) - wait = false - continue - } + var block *walletrpc.CompactBlock + block, err = getBlockFromRPC(height) + if err != nil { + Log.Fatal("getblock failed, will retry", err) } - if block == nil || c.HashMismatch(block.PrevHash) { - // This may not be a reorg; it may be we're at the tip - // and there's no new block yet, but we want to back up - // so we detect a reorg in which the new chain is the - // same length or shorter. - reorgCount++ - if reorgCount > 100 { - Log.Fatal("Reorg exceeded max of 100 blocks! Help!") + if block != nil && c.HashMatch(block.PrevHash) { + if err = c.Add(height, block); err != nil { + Log.Fatal("Cache add failed:", err) } - // Print the hash of the block that is getting reorg-ed away - // as 'phash', not the prevhash of the block we just received. - if block != nil { - Log.WithFields(logrus.Fields{ - "height": height, - "hash": displayHash(block.Hash), - "phash": displayHash(c.GetLatestHash()), - "reorg": reorgCount, - }).Warn("REORG") - } else if reorgCount > 1 { - Log.WithFields(logrus.Fields{ - "height": height, - "phash": displayHash(c.GetLatestHash()), - "reorg": reorgCount, - }).Warn("REORG") + // Don't log these too often. + if DarksideEnabled || Time.Now().Sub(lastLog).Seconds() >= 4 { + lastLog = Time.Now() + Log.Info("Adding block to cache ", height, " ", displayHash(block.Hash)) } - // Try backing up - c.Reorg(height - 1) continue } - // We have a valid block to add. - wait = true - reorgCount = 0 - if err := c.Add(height, block); err != nil { - Log.Fatal("Cache add failed:", err) - } - // Don't log these too often. - if time.Now().Sub(lastLog).Seconds() >= 4 && c.GetNextHeight() == height+1 && height != lastHeightLogged { - lastLog = time.Now() - lastHeightLogged = height - Log.Info("Ingestor adding block to cache: ", height) + if height == c.GetFirstHeight() { + c.Sync() + Log.Info("Waiting for zcashd height to reach Sapling activation height ", + "(", c.GetFirstHeight(), ")...") + Time.Sleep(20 * time.Second) + return } + Log.Info("REORG: dropping block ", height-1, " ", displayHash(c.GetLatestHash())) + c.Reorg(height - 1) } } diff --git a/common/common_test.go b/common/common_test.go index 5006806..af2b3aa 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -34,6 +34,8 @@ var ( logger = logrus.New() blocks [][]byte // four test blocks + + testcache *BlockCache ) // TestMain does common setup that's shared across multiple tests @@ -60,6 +62,7 @@ func TestMain(m *testing.M) { blockJSON, _ := json.Marshal(scan.Text()) blocks = append(blocks, blockJSON) } + testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, true) // Setup is done; run all tests. exitcode := m.Run() @@ -160,8 +163,217 @@ func TestGetLightdInfo(t *testing.T) { // ------------------------------------------ BlockIngestor() +func checkSleepMethod(count int, duration time.Duration, expected string, method string) { + if sleepCount != count { + testT.Fatal("unexpected sleep count") + } + if sleepDuration != duration*time.Second { + testT.Fatal("unexpected sleep duration") + } + if method != expected { + testT.Error("unexpected method") + } +} + // There are four test blocks, 0..3 +func blockIngestorStub(method string, params []json.RawMessage) (json.RawMessage, error) { + step++ + // request the first two blocks very quickly (syncing), + // then next block isn't yet available + switch step { + case 1: + checkSleepMethod(0, 0, "getbestblockhash", method) + // This hash doesn't matter, won't match anything + r, _ := json.Marshal("010101") + return r, nil + case 2: + checkSleepMethod(0, 0, "getblock", method) + var height string + err := json.Unmarshal(params[0], &height) + if err != nil { + testT.Fatal("could not unmarshal height") + } + if height != "380640" { + testT.Fatal("incorrect height requested") + } + // height 380640 + return blocks[0], nil + case 3: + checkSleepMethod(0, 0, "getbestblockhash", method) + // This hash doesn't matter, won't match anything + r, _ := json.Marshal("010101") + return r, nil + case 4: + checkSleepMethod(0, 0, "getblock", method) + var height string + err := json.Unmarshal(params[0], &height) + if err != nil { + testT.Fatal("could not unmarshal height") + } + if height != "380641" { + testT.Fatal("incorrect height requested") + } + // height 380641 + return blocks[1], nil + case 5: + // Return the expected block hash, so we're synced, should + // then sleep for 2 seconds, then another getbestblockhash + checkSleepMethod(0, 0, "getbestblockhash", method) + r, _ := json.Marshal(displayHash(testcache.GetLatestHash())) + return r, nil + case 6: + // Simulate still no new block, still synced, should + // sleep for 2 seconds, then another getbestblockhash + checkSleepMethod(1, 2, "getbestblockhash", method) + r, _ := json.Marshal(displayHash(testcache.GetLatestHash())) + return r, nil + case 7: + // Simulate new block (any non-matching hash will do) + checkSleepMethod(2, 4, "getbestblockhash", method) + r, _ := json.Marshal("aabb") + return r, nil + case 8: + checkSleepMethod(2, 4, "getblock", method) + var height string + err := json.Unmarshal(params[0], &height) + if err != nil { + testT.Fatal("could not unmarshal height") + } + if height != "380642" { + testT.Fatal("incorrect height requested") + } + // height 380642 + return blocks[2], nil + case 9: + // Simulate still no new block, still synced, should + // sleep for 2 seconds, then another getbestblockhash + checkSleepMethod(2, 4, "getbestblockhash", method) + r, _ := json.Marshal(displayHash(testcache.GetLatestHash())) + return r, nil + case 10: + // There are 3 blocks in the cache (380640-642), so let's + // simulate a 1-block reorg, new version (replacement) of 380642 + checkSleepMethod(3, 6, "getbestblockhash", method) + // hash doesn't matter, just something that doesn't match + r, _ := json.Marshal("4545") + return r, nil + case 11: + // It thinks there may simply be a new block, but we'll say + // there is no block at this height (380642 was replaced). + checkSleepMethod(3, 6, "getblock", method) + var height string + err := json.Unmarshal(params[0], &height) + if err != nil { + testT.Fatal("could not unmarshal height") + } + if height != "380643" { + testT.Fatal("incorrect height requested") + } + return nil, errors.New("-8: Block height out of range") + case 12: + // It will re-ask the best hash (let's make no change) + checkSleepMethod(3, 6, "getbestblockhash", method) + // hash doesn't matter, just something that doesn't match + r, _ := json.Marshal("4545") + return r, nil + case 13: + // It should have backed up one block + checkSleepMethod(3, 6, "getblock", method) + var height string + err := json.Unmarshal(params[0], &height) + if err != nil { + testT.Fatal("could not unmarshal height") + } + if height != "380642" { + testT.Fatal("incorrect height requested") + } + // height 380642 + return blocks[2], nil + case 14: + // We're back to the same state as case 9, and this time + // we'll make it back up 2 blocks (rather than one) + checkSleepMethod(3, 6, "getbestblockhash", method) // XXXXXXXXXXXXXXXXXXXXXXXXXXXXX XXX + // hash doesn't matter, just something that doesn't match + r, _ := json.Marshal("5656") + return r, nil + case 15: + // It thinks there may simply be a new block, but we'll say + // there is no block at this height (380642 was replaced). + checkSleepMethod(3, 6, "getblock", method) + var height string + err := json.Unmarshal(params[0], &height) + if err != nil { + testT.Fatal("could not unmarshal height") + } + if height != "380643" { + testT.Fatal("incorrect height requested") + } + return nil, errors.New("-8: Block height out of range") + case 16: + checkSleepMethod(3, 6, "getbestblockhash", method) + // hash doesn't matter, just something that doesn't match + r, _ := json.Marshal("5656") + return r, nil + case 17: + // Like case 13, it should have backed up one block, but + // this time we'll make it back up one more + checkSleepMethod(3, 6, "getblock", method) + var height string + err := json.Unmarshal(params[0], &height) + if err != nil { + testT.Fatal("could not unmarshal height") + } + if height != "380642" { + testT.Fatal("incorrect height requested") + } + return nil, errors.New("-8: Block height out of range") + case 18: + checkSleepMethod(3, 6, "getbestblockhash", method) + // hash doesn't matter, just something that doesn't match + r, _ := json.Marshal("5656") + return r, nil + case 19: + // It should have backed up one more + checkSleepMethod(3, 6, "getblock", method) + var height string + err := json.Unmarshal(params[0], &height) + if err != nil { + testT.Fatal("could not unmarshal height") + } + if height != "380641" { + testT.Fatal("incorrect height requested") + } + return blocks[1], nil + } + testT.Error("blockIngestorStub called too many times") + return nil, nil +} + +func TestBlockIngestor(t *testing.T) { + testT = t + RawRequest = blockIngestorStub + Time.Sleep = sleepStub + Time.Now = nowStub + os.RemoveAll(unitTestPath) + testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, false) + BlockIngestor(testcache, 11) + if step != 19 { + t.Error("unexpected final step", step) + } + step = 0 + sleepCount = 0 + sleepDuration = 0 + os.RemoveAll(unitTestPath) +} + +// ------------------------------------------ GetBlockRange() + +// There are four test blocks, 0..3 +// (probably don't need all these cases) func getblockStub(method string, params []json.RawMessage) (json.RawMessage, error) { + if method != "getblock" { + testT.Error("unexpected method") + } var height string err := json.Unmarshal(params[0], &height) if err != nil { @@ -272,27 +484,11 @@ func getblockStub(method string, params []json.RawMessage) (json.RawMessage, err return nil, nil } -func TestBlockIngestor(t *testing.T) { - testT = t - RawRequest = getblockStub - Time.Sleep = sleepStub - os.RemoveAll(unitTestPath) - testcache := NewBlockCache(unitTestPath, unitTestChain, 380640, false) - BlockIngestor(testcache, 11) - if step != 11 { - t.Error("unexpected final step", step) - } - step = 0 - sleepCount = 0 - sleepDuration = 0 - os.RemoveAll(unitTestPath) -} - func TestGetBlockRange(t *testing.T) { testT = t RawRequest = getblockStub os.RemoveAll(unitTestPath) - testcache := NewBlockCache(unitTestPath, unitTestChain, 380640, true) + testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, true) blockChan := make(chan *walletrpc.CompactBlock) errChan := make(chan error) go GetBlockRange(testcache, blockChan, errChan, 380640, 380642) @@ -371,7 +567,7 @@ func TestGetBlockRangeReverse(t *testing.T) { testT = t RawRequest = getblockStubReverse os.RemoveAll(unitTestPath) - testcache := NewBlockCache(unitTestPath, unitTestChain, 380640, true) + testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, true) blockChan := make(chan *walletrpc.CompactBlock) errChan := make(chan error) diff --git a/common/darkside.go b/common/darkside.go index 535d9ae..70608d3 100644 --- a/common/darkside.go +++ b/common/darkside.go @@ -77,7 +77,7 @@ func DarksideInit(c *BlockCache, timeout int) { // DarksideReset allows the wallet test code to specify values // that are returned by GetLightdInfo(). func DarksideReset(sa int, bi, cn string) error { - Log.Info("Reset(saplingActivation=", sa, ")") + Log.Info("DarksideReset(saplingActivation=", sa, ")") stopIngestor() state = darksideState{ resetted: true, @@ -138,7 +138,7 @@ func setPrevhash() { copy(blockBytes[4:4+32], prevhash) } prevhash = block.GetEncodableHash() - Log.Info("active block height ", block.GetHeight(), " hash ", + Log.Info("Darkside active block height ", block.GetHeight(), " hash ", hex.EncodeToString(block.GetDisplayHash()), " txcount ", block.GetTxCount()) } @@ -153,7 +153,7 @@ func DarksideApplyStaged(height int) error { if !state.resetted { return errors.New("please call Reset first") } - Log.Info("ApplyStaged(height=", height, ")") + Log.Info("DarksideApplyStaged(height=", height, ")") if height < state.startHeight { return errors.New(fmt.Sprint("height ", height, " is less than sapling activation height ", state.startHeight)) @@ -212,9 +212,13 @@ func DarksideApplyStaged(height int) error { block = append(block, tx.bytes...) state.activeBlocks[tx.height-state.startHeight] = block } + maxHeight := state.startHeight + len(state.activeBlocks) - 1 + if height > maxHeight { + height = maxHeight + } setPrevhash() state.latestHeight = height - Log.Info("active blocks from ", state.startHeight, + Log.Info("darkside: active blocks from ", state.startHeight, " to ", state.startHeight+len(state.activeBlocks)-1, ", latest presented height ", state.latestHeight) @@ -244,7 +248,7 @@ func darksideStageBlock(caller string, b []byte) error { if len(rest) != 0 { return errors.New("block serialization is too long") } - Log.Info(caller, "(height=", block.GetHeight(), ")") + Log.Info(caller, "DarksideStageBlock(height=", block.GetHeight(), ")") if block.GetHeight() < state.startHeight { return errors.New(fmt.Sprint("block height ", block.GetHeight(), " is less than sapling activation height ", state.startHeight)) @@ -259,7 +263,7 @@ func DarksideStageBlocks(url string) error { if !state.resetted { return errors.New("please call Reset first") } - Log.Info("StageBlocks(url=", url, ")") + Log.Info("DarksideStageBlocks(url=", url, ")") resp, err := http.Get(url) if err != nil { return err @@ -292,7 +296,7 @@ func DarksideStageBlockStream(blockHex string) error { if !state.resetted { return errors.New("please call Reset first") } - Log.Info("StageBlocksStream()") + Log.Info("DarksideStageBlocksStream()") blockBytes, err := hex.DecodeString(blockHex) if err != nil { return err @@ -308,7 +312,7 @@ func DarksideStageBlocksCreate(height int32, nonce int32, count int32) error { if !state.resetted { return errors.New("please call Reset first") } - Log.Info("StageBlocksCreate(height=", height, ", nonce=", nonce, ", count=", count, ")") + Log.Info("DarksideStageBlocksCreate(height=", height, ", nonce=", nonce, ", count=", count, ")") for i := 0; i < int(count); i++ { fakeCoinbase := "0400008085202f890100000000000000000000000000000000000000000000000000" + @@ -413,6 +417,18 @@ func darksideRawRequest(method string, params []json.RawMessage) (json.RawMessag } return json.Marshal(hex.EncodeToString(state.activeBlocks[index])) + case "getbestblockhash": + state.mutex.RLock() + defer state.mutex.RUnlock() + if len(state.activeBlocks) == 0 { + Log.Fatal("getbestblockhash: no blocks") + } + index := state.latestHeight - state.startHeight + block := parser.NewBlock() + block.ParseFromSlice(state.activeBlocks[index]) + hash := hex.EncodeToString(block.GetDisplayHash()) + return json.Marshal(hash) + case "getaddresstxids": // Not required for minimal reorg testing. return nil, errors.New("not implemented yet") @@ -577,7 +593,7 @@ func DarksideStageTransactionsURL(height int, url string) error { if !state.resetted { return errors.New("please call Reset first") } - Log.Info("StageTransactionsURL(height=", height, ", url=", url, ")") + Log.Info("DarksideStageTransactionsURL(height=", height, ", url=", url, ")") resp, err := http.Get(url) if err != nil { return err diff --git a/docs/darksidewalletd.md b/docs/darksidewalletd.md index ec44ae2..afccdfb 100644 --- a/docs/darksidewalletd.md +++ b/docs/darksidewalletd.md @@ -94,6 +94,16 @@ block height to another. This happens in two parts, first we create and apply the "before reorg" state. Then we create the "after reorg" stage and apply it, which makes the reorg happen. +Here's a quick-start guide to simulating a reorg: +``` +grpcurl -plaintext -d '{"saplingActivation": 663150,"branchID": "bad", "chainName":"x"}' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/Reset +grpcurl -plaintext -d '{"url": "https://raw.githubusercontent.com/zcash-hackworks/darksidewalletd-test-data/master/basic-reorg/663150.txt"}' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/StageBlocks +grpcurl -plaintext -d '{"height":663151,"count":10}' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/StageBlocksCreate +grpcurl -plaintext -d '{"height":663160}' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/ApplyStaged +grpcurl -plaintext -d '{"height":663155,"count":10,"nonce":44}' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/StageBlocksCreate +grpcurl -plaintext -d '{"height":663164}' localhost:9067 cash.z.wallet.sdk.rpc.DarksideStreamer/ApplyStaged +``` + #### Creating the Before-Reorg State If you haven't already started darksidewalletd, please start it: From 33d3d53d347ddafce2eb9ca7e7a7ec1dfcbc29d3 Mon Sep 17 00:00:00 2001 From: adityapk00 Date: Mon, 19 Apr 2021 13:48:05 -0700 Subject: [PATCH 11/31] Verify rawtx --- frontend/service.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/frontend/service.go b/frontend/service.go index 0c9cc08..0b6e095 100644 --- a/frontend/service.go +++ b/frontend/service.go @@ -288,6 +288,11 @@ func (s *lwdStreamer) SendTransaction(ctx context.Context, rawtx *walletrpc.RawT // Result: // "hex" (string) The transaction hash in hex + // Verify rawtx + if rawtx == nil || rawtx.Data == nil { + return nil, errors.New("Bad transaction data") + } + // Construct raw JSON-RPC params params := make([]json.RawMessage, 1) txJSON, err := json.Marshal(hex.EncodeToString(rawtx.Data)) From ca4905a6a27ec84df95376f8f032bab495de2b0a Mon Sep 17 00:00:00 2001 From: Sasha <2592730+superbaud@users.noreply.github.com> Date: Tue, 28 Sep 2021 18:55:45 -0500 Subject: [PATCH 12/31] correct typos --- docs/docker-compose-setup.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docker-compose-setup.md b/docs/docker-compose-setup.md index a58c2a0..9d0aaa2 100644 --- a/docs/docker-compose-setup.md +++ b/docs/docker-compose-setup.md @@ -18,8 +18,8 @@ Copy `.env.example` to `.env` and change any required paramaters. | `ZCASHD_RPCUSER` | zcashd rpc user | | `ZCASHD_RPCPORT` | zcashd rpc port | |`ZCASHD_ALLOWIP`| zcashd rpc allowed IPs (don't |change unless you know what you're doing)| -|`ZCASHD_DATADIR`| local location of zcasd data directory. `uid` 2001 needs write access| -|`ZCASHD_PARMDIR`| local location of zcasd data directory. `uid` 2001 needs read access| +|`ZCASHD_DATADIR`| local location of zcashd data directory. `uid` 2001 needs write access| +|`ZCASHD_PARMDIR`| local location of zcashd data directory. `uid` 2001 needs read access| |`ZCASHD_NETWORK`| zcashd network to use, `testnet` or `mainnet`| |`ZCASHD_GEN`| should zcashd mine? `0` or `1` |`LWD_PORT`| port for lightwalletd to bind to| From f84af8fe71b0d3c9a3c0ac291458c2584ba437d7 Mon Sep 17 00:00:00 2001 From: Sasha <2592730+superbaud@users.noreply.github.com> Date: Tue, 28 Sep 2021 19:13:35 -0500 Subject: [PATCH 13/31] added notes on the multiple zcashd.conf files --- docs/docker-compose-setup.md | 37 +++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/docs/docker-compose-setup.md b/docs/docker-compose-setup.md index 9d0aaa2..e64e3ae 100644 --- a/docs/docker-compose-setup.md +++ b/docs/docker-compose-setup.md @@ -16,8 +16,9 @@ Copy `.env.example` to `.env` and change any required paramaters. | ------------- |:-------------:| | `GF_SECURITY_ADMIN_USER` | Grafana admin user name | | `ZCASHD_RPCUSER` | zcashd rpc user | +| `ZCASHD_RPCPASSWORD` | zcashd rpc password | | `ZCASHD_RPCPORT` | zcashd rpc port | -|`ZCASHD_ALLOWIP`| zcashd rpc allowed IPs (don't |change unless you know what you're doing)| +|`ZCASHD_ALLOWIP`| zcashd rpc allowed IPs (don't change unless you know what you're doing)| |`ZCASHD_DATADIR`| local location of zcashd data directory. `uid` 2001 needs write access| |`ZCASHD_PARMDIR`| local location of zcashd data directory. `uid` 2001 needs read access| |`ZCASHD_NETWORK`| zcashd network to use, `testnet` or `mainnet`| @@ -32,6 +33,25 @@ Copy `.env.example` to `.env` and change any required paramaters. ./buildenv.sh | tee .env ``` +## Edit the two zcash.conf files +There are two zcash.conf files; one read by zcashd, one read by lightwalletd. + +### `$ZCASHD_DATADIR/zcash.conf` -- read by zcashd +The zcashd's `zcash.conf` needs to look like: +``` +rpcuser=zcashrpc +rpcpassword=Z7V7IoXt6I0OVuKr7TpHjxAnE6NvB/ZBRdM6x9w/OAU= +experimentalfeatures=1 +lightwalletd=1 +``` +`rpcuser` and `rpcpassword` must be set, as lightwalletd doesn't work with RPC cookies (see the [rpcpassword](https://zcash.readthedocs.io/en/latest/rtd_pages/zcash_conf_guide.html) documentation) for authentication. + +`rpcuser` and `rpcpassword` in `.env` are only used by zcashd_exporter, but they also must be the same values as in `$ZCASHD_DATADIR/zcash.conf` + +### `lightwalletd/docker/zcash.conf` -- read by lightwalletd +The other `zcashd.conf` -- the one read by lightwalletd -- needs to have `rpcbind` (the address of the zcashd it will connect to) set to `zcashd` -- docker-compose networking will make it resolve to the right IP address. Also, it needs to have the same `rpcuser` and `rpcpassword` values that are listed in `$ZCASHD_DATADIR/zcash.conf` to be able to authenticate. + + ## Build initial local docker image `docker-compose build` @@ -98,3 +118,18 @@ Loki as a rich query syntax to help with log in many ways, for example combine 2 ![grafana-explore4](./images/grafana-explore-4.png) See more here: https://github.com/grafana/loki/blob/master/docs/logql.md + + +# Exposing to the network + +Edit `docker-compose.yml` to look like + +``` + ports: + #- "127.0.0.1:$LWD_GRPC_PORT:$LWD_GRPC_PORT" + #- "127.0.0.1:$LWD_HTTP_PORT:$LWD_HTTP_PORT" + - "0.0.0.0:$LWD_GRPC_PORT:$LWD_GRPC_PORT" + - "0.0.0.0:$LWD_HTTP_PORT:$LWD_HTTP_PORT" +``` + +When you edit these lines in `docker-compose.yml`, stopping/starting the individual `lightwalletd` container doesn't actually make the changes happen -- you have to stop/start the whole `docker-compose` ensemble of containers because the ports/network config stuff lives at that level and doesn't seem to be affected by individual container stop/starts. Also if you want to expose `lightwalletd` to the whole internet, you don't need to specify an IP address, `0.0.0.0` works as it should. From e150fa7790261fd00ecb051ffddd567f93f33968 Mon Sep 17 00:00:00 2001 From: Sasha <2592730+superbaud@users.noreply.github.com> Date: Tue, 28 Sep 2021 19:16:43 -0500 Subject: [PATCH 14/31] clarify that we're exposing lwd to the network --- docs/docker-compose-setup.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docker-compose-setup.md b/docs/docker-compose-setup.md index e64e3ae..9931a9b 100644 --- a/docs/docker-compose-setup.md +++ b/docs/docker-compose-setup.md @@ -120,7 +120,7 @@ Loki as a rich query syntax to help with log in many ways, for example combine 2 See more here: https://github.com/grafana/loki/blob/master/docs/logql.md -# Exposing to the network +# Exposing `lightwalletd` to the network Edit `docker-compose.yml` to look like From 2defc2501d7837d33bcb7d7b2ac0300f659f2ea9 Mon Sep 17 00:00:00 2001 From: Taylor Hornby Date: Tue, 28 Sep 2021 18:32:19 -0600 Subject: [PATCH 15/31] Fix em-dashes and add instructions to use a random password --- docs/docker-compose-setup.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/docker-compose-setup.md b/docs/docker-compose-setup.md index 9931a9b..7b090cc 100644 --- a/docs/docker-compose-setup.md +++ b/docs/docker-compose-setup.md @@ -36,20 +36,23 @@ Copy `.env.example` to `.env` and change any required paramaters. ## Edit the two zcash.conf files There are two zcash.conf files; one read by zcashd, one read by lightwalletd. -### `$ZCASHD_DATADIR/zcash.conf` -- read by zcashd +### `$ZCASHD_DATADIR/zcash.conf`—read by zcashd The zcashd's `zcash.conf` needs to look like: ``` rpcuser=zcashrpc -rpcpassword=Z7V7IoXt6I0OVuKr7TpHjxAnE6NvB/ZBRdM6x9w/OAU= +rpcpassword=TODO INSERT A RANDOM PASSWORD HERE experimentalfeatures=1 lightwalletd=1 ``` + +Replace `TODO INSERT A RANDOM PASSWORD HERE` with a random password, e.g. the output of `head -c 16 /dev/urandom | base64`. + `rpcuser` and `rpcpassword` must be set, as lightwalletd doesn't work with RPC cookies (see the [rpcpassword](https://zcash.readthedocs.io/en/latest/rtd_pages/zcash_conf_guide.html) documentation) for authentication. `rpcuser` and `rpcpassword` in `.env` are only used by zcashd_exporter, but they also must be the same values as in `$ZCASHD_DATADIR/zcash.conf` -### `lightwalletd/docker/zcash.conf` -- read by lightwalletd -The other `zcashd.conf` -- the one read by lightwalletd -- needs to have `rpcbind` (the address of the zcashd it will connect to) set to `zcashd` -- docker-compose networking will make it resolve to the right IP address. Also, it needs to have the same `rpcuser` and `rpcpassword` values that are listed in `$ZCASHD_DATADIR/zcash.conf` to be able to authenticate. +### `lightwalletd/docker/zcash.conf`—read by lightwalletd +The other `zcashd.conf`—the one read by lightwalletd—needs to have `rpcbind` (the address of the zcashd it will connect to) set to `zcashd`, and then docker-compose networking will make it resolve to the right IP address. Also, it needs to have the same `rpcuser` and `rpcpassword` values that are listed in `$ZCASHD_DATADIR/zcash.conf` to be able to authenticate. ## Build initial local docker image @@ -132,4 +135,4 @@ Edit `docker-compose.yml` to look like - "0.0.0.0:$LWD_HTTP_PORT:$LWD_HTTP_PORT" ``` -When you edit these lines in `docker-compose.yml`, stopping/starting the individual `lightwalletd` container doesn't actually make the changes happen -- you have to stop/start the whole `docker-compose` ensemble of containers because the ports/network config stuff lives at that level and doesn't seem to be affected by individual container stop/starts. Also if you want to expose `lightwalletd` to the whole internet, you don't need to specify an IP address, `0.0.0.0` works as it should. +When you edit these lines in `docker-compose.yml`, stopping/starting the individual `lightwalletd` container doesn't actually make the changes happen—you have to stop/start the whole `docker-compose` ensemble of containers because the ports/network config stuff lives at that level and doesn't seem to be affected by individual container stop/starts. Also if you want to expose `lightwalletd` to the whole internet, you don't need to specify an IP address, `0.0.0.0` works as it should. From ddf3781a60ecda546e8cdb4974a2556e0266b182 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Thu, 7 Oct 2021 14:57:26 -0600 Subject: [PATCH 16/31] parse V5 (nu5) transactions TODO: - store, instead of just skip over, nu5 transaction fields - add relevant nu5 fields to CompactBlock - restore disabled V4 unit tests - add V5 test vectors to unit tests The reason most of the V4 transaction and block unit tests are removed is that they used V3 transactions, which lightwalletd never sees in production, since lightwalletd starts at Sapling activation (which has V4 transactions). So these tests were always wrong, in a way. This commit simplifies the parsing code by removing support for V3 (since it was never needed). The tests need to be updated to V4, but we'll do that in a later PR. --- parser/block_header_test.go | 2 +- parser/block_test.go | 196 -------- parser/transaction.go | 428 +++++++++++------- parser/transaction_test.go | 879 ------------------------------------ testdata/blocks | 8 +- 5 files changed, 272 insertions(+), 1241 deletions(-) diff --git a/parser/block_header_test.go b/parser/block_header_test.go index 0909da3..e0dbfc6 100644 --- a/parser/block_header_test.go +++ b/parser/block_header_test.go @@ -134,7 +134,7 @@ func TestBlockHeader(t *testing.T) { } // This is not necessarily true for anything but our current test cases. - for _, b := range hash[:4] { + for _, b := range hash[:1] { if b != 0 { t.Errorf("Hash lacked leading zeros: %x", hash) } diff --git a/parser/block_test.go b/parser/block_test.go index 99f6338..affb8c6 100644 --- a/parser/block_test.go +++ b/parser/block_test.go @@ -4,13 +4,11 @@ package parser import ( - "bufio" "bytes" "encoding/hex" "encoding/json" "fmt" "io/ioutil" - "os" "testing" "github.com/pkg/errors" @@ -18,200 +16,6 @@ import ( protobuf "github.com/golang/protobuf/proto" ) -func TestBlockParser(t *testing.T) { - // These (valid on testnet) correspond to the transactions in testdata/blocks; - // for each block, the hashes for the tx within that block. - var txhashes = [][]string{ - { - "81096ff101a4f01d25ffd34a446bee4368bd46c233a59ac0faf101e1861c6b22", - }, { - "921dc41bef3a0d887c615abac60a29979efc8b4bbd3d887caeb6bb93501bde8e", - }, { - "d8e4c336ffa69dacaa4e0b4eaf8e3ae46897f1930a573c10b53837a03318c980", - "4d5ccbfc6984680c481ff5ce145b8a93d59dfea90c150dfa45c938ab076ee5b2", - }, { - "df2b03619d441ce3d347e9278d87618e975079d0e235dfb3b3d8271510f707aa", - "8d2593edfc328fa637b4ac91c7d569ee922bb9a6fda7cea230e92deb3ae4b634", - }, - } - testBlocks, err := os.Open("../testdata/blocks") - if err != nil { - t.Fatal(err) - } - defer testBlocks.Close() - - scan := bufio.NewScanner(testBlocks) - for blockindex := 0; scan.Scan(); blockindex++ { - blockDataHex := scan.Text() - blockData, err := hex.DecodeString(blockDataHex) - if err != nil { - t.Error(err) - continue - } - - // This is just a sanity check of the test: - if int(blockData[1487]) != len(txhashes[blockindex]) { - t.Error("wrong number of transactions, test broken?") - } - - // Make a copy of just the transactions alone, which, - // for these blocks, start just beyond the header and - // the one-byte nTx value, which is offset 1488. - transactions := make([]byte, len(blockData[1488:])) - copy(transactions, blockData[1488:]) - - // Each iteration of this loop appends the block's original - // transactions, so we build an ever-larger block. The loop - // limit is arbitrary, but make sure we get into double-digit - // transaction counts (compact integer). - for i := 0; i < 264; i++ { - b := blockData - block := NewBlock() - b, err = block.ParseFromSlice(b) - if err != nil { - t.Error(errors.Wrap(err, fmt.Sprintf("parsing block %d", i))) - continue - } - if len(b) > 0 { - t.Error("Extra data remaining") - } - - // Some basic sanity checks - if block.hdr.Version != 4 { - t.Error("Read wrong version in a test block.") - break - } - if block.GetVersion() != 4 { - t.Error("Read wrong version in a test block.") - break - } - if block.GetTxCount() < 1 { - t.Error("No transactions in block") - break - } - if len(block.Transactions()) != block.GetTxCount() { - t.Error("Number of transactions mismatch") - break - } - if block.GetTxCount() != len(txhashes[blockindex])*(i+1) { - t.Error("Unexpected number of transactions") - } - if block.HasSaplingTransactions() { - t.Error("Unexpected Sapling tx") - break - } - for txindex, tx := range block.Transactions() { - if tx.HasSaplingElements() { - t.Error("Unexpected Sapling tx") - break - } - expectedHash := txhashes[blockindex][txindex%len(txhashes[blockindex])] - if hex.EncodeToString(tx.GetDisplayHash()) != expectedHash { - t.Error("incorrect tx hash") - } - } - // Keep appending the original transactions, which is unrealistic - // because the coinbase is being replicated, but it works; first do - // some surgery to the transaction count (see DarksideApplyStaged()). - for j := 0; j < len(txhashes[blockindex]); j++ { - nTxFirstByte := blockData[1487] - switch { - case nTxFirstByte < 252: - blockData[1487]++ - case nTxFirstByte == 252: - // incrementing to 253, requires "253" followed by 2-byte length, - // extend the block by two bytes, shift existing transaction bytes - blockData = append(blockData, 0, 0) - copy(blockData[1490:], blockData[1488:len(blockData)-2]) - blockData[1487] = 253 - blockData[1488] = 253 - blockData[1489] = 0 - case nTxFirstByte == 253: - blockData[1488]++ - if blockData[1488] == 0 { - // wrapped around - blockData[1489]++ - } - } - } - blockData = append(blockData, transactions...) - } - } -} - -func TestBlockParserFail(t *testing.T) { - testBlocks, err := os.Open("../testdata/badblocks") - if err != nil { - t.Fatal(err) - } - defer testBlocks.Close() - - scan := bufio.NewScanner(testBlocks) - - // the first "block" contains an illegal hex character - { - scan.Scan() - blockDataHex := scan.Text() - _, err := hex.DecodeString(blockDataHex) - if err == nil { - t.Error("unexpected success parsing illegal hex bad block") - } - } - for i := 0; scan.Scan(); i++ { - blockDataHex := scan.Text() - blockData, err := hex.DecodeString(blockDataHex) - if err != nil { - t.Error(err) - continue - } - - block := NewBlock() - blockData, err = block.ParseFromSlice(blockData) - if err == nil { - t.Error("unexpected success parsing bad block") - } - } -} - -// Checks on the first 20 blocks from mainnet genesis. -func TestGenesisBlockParser(t *testing.T) { - blockFile, err := os.Open("../testdata/mainnet_genesis") - if err != nil { - t.Fatal(err) - } - defer blockFile.Close() - - scan := bufio.NewScanner(blockFile) - for i := 0; scan.Scan(); i++ { - blockDataHex := scan.Text() - blockData, err := hex.DecodeString(blockDataHex) - if err != nil { - t.Error(err) - continue - } - - block := NewBlock() - blockData, err = block.ParseFromSlice(blockData) - if err != nil { - t.Error(err) - continue - } - if len(blockData) > 0 { - t.Error("Extra data remaining") - } - - // Some basic sanity checks - if block.hdr.Version != 4 { - t.Error("Read wrong version in genesis block.") - break - } - - if block.GetHeight() != i { - t.Errorf("Got wrong height for block %d: %d", i, block.GetHeight()) - } - } -} - func TestCompactBlocks(t *testing.T) { type compactTest struct { BlockHeight int `json:"block"` diff --git a/parser/transaction.go b/parser/transaction.go index add99d1..a93b34e 100644 --- a/parser/transaction.go +++ b/parser/transaction.go @@ -7,6 +7,7 @@ package parser import ( "crypto/sha256" + "fmt" "github.com/pkg/errors" "github.com/zcash/lightwalletd/parser/internal/bytestring" @@ -14,20 +15,21 @@ import ( ) type rawTransaction struct { - fOverwintered bool - version uint32 - nVersionGroupID uint32 - transparentInputs []*txIn - transparentOutputs []*txOut - nLockTime uint32 - nExpiryHeight uint32 - valueBalance int64 - shieldedSpends []*spend - shieldedOutputs []*output - joinSplits []*joinSplit - joinSplitPubKey []byte - joinSplitSig []byte - bindingSig []byte + fOverwintered bool + version uint32 + nVersionGroupID uint32 + consensusBranchID uint32 + transparentInputs []*txIn + transparentOutputs []*txOut + nLockTime uint32 + nExpiryHeight uint32 + valueBalanceSapling int64 + shieldedSpends []*spend + shieldedOutputs []*output + joinSplits []*joinSplit + joinSplitPubKey []byte + joinSplitSig []byte + bindingSigSapling []byte } // Txin format as described in https://en.bitcoin.it/wiki/Transaction @@ -90,8 +92,45 @@ func (tx *txOut) ParseFromSlice(data []byte) ([]byte, error) { return []byte(s), nil } +// parse the transparent parts of the transaction +func (tx *Transaction) ParseTransparent(data []byte) ([]byte, error) { + s := bytestring.String(data) + var txInCount int + if !s.ReadCompactSize(&txInCount) { + return nil, errors.New("could not read tx_in_count") + } + var err error + // TODO: Duplicate/otherwise-too-many transactions are a possible DoS + // TODO: vector. At the moment we're assuming trusted input. + // See https://nvd.nist.gov/vuln/detail/CVE-2018-17144 for an example. + tx.transparentInputs = make([]*txIn, txInCount) + for i := 0; i < txInCount; i++ { + ti := &txIn{} + s, err = ti.ParseFromSlice([]byte(s)) + if err != nil { + return nil, errors.Wrap(err, "while parsing transparent input") + } + tx.transparentInputs[i] = ti + } + + var txOutCount int + if !s.ReadCompactSize(&txOutCount) { + return nil, errors.New("could not read tx_out_count") + } + tx.transparentOutputs = make([]*txOut, txOutCount) + for i := 0; i < txOutCount; i++ { + to := &txOut{} + s, err = to.ParseFromSlice([]byte(s)) + if err != nil { + return nil, errors.Wrap(err, "while parsing transparent output") + } + tx.transparentOutputs[i] = to + } + return []byte(s), nil +} + // spend is a Sapling Spend Description as described in 7.3 of the Zcash -// protocol spec. Total size is 384 bytes. +// protocol specification. type spend struct { cv []byte // 32 anchor []byte // 32 @@ -101,14 +140,14 @@ type spend struct { spendAuthSig []byte // 64 } -func (p *spend) ParseFromSlice(data []byte) ([]byte, error) { +func (p *spend) ParseFromSlice(data []byte, version uint32) ([]byte, error) { s := bytestring.String(data) if !s.ReadBytes(&p.cv, 32) { return nil, errors.New("could not read cv") } - if !s.ReadBytes(&p.anchor, 32) { + if version <= 4 && !s.ReadBytes(&p.anchor, 32) { return nil, errors.New("could not read anchor") } @@ -120,11 +159,11 @@ func (p *spend) ParseFromSlice(data []byte) ([]byte, error) { return nil, errors.New("could not read rk") } - if !s.ReadBytes(&p.zkproof, 192) { + if version <= 4 && !s.ReadBytes(&p.zkproof, 192) { return nil, errors.New("could not read zkproof") } - if !s.ReadBytes(&p.spendAuthSig, 64) { + if version <= 4 && !s.ReadBytes(&p.spendAuthSig, 64) { return nil, errors.New("could not read spendAuthSig") } @@ -138,7 +177,7 @@ func (p *spend) ToCompact() *walletrpc.CompactSpend { } // output is a Sapling Output Description as described in section 7.4 of the -// Zcash protocol spec. Total size is 948. +// Zcash protocol spec. type output struct { cv []byte // 32 cmu []byte // 32 @@ -148,7 +187,7 @@ type output struct { zkproof []byte // 192 } -func (p *output) ParseFromSlice(data []byte) ([]byte, error) { +func (p *output) ParseFromSlice(data []byte, version uint32) ([]byte, error) { s := bytestring.String(data) if !s.ReadBytes(&p.cv, 32) { @@ -171,7 +210,7 @@ func (p *output) ParseFromSlice(data []byte) ([]byte, error) { return nil, errors.New("could not read outCiphertext") } - if !s.ReadBytes(&p.zkproof, 192) { + if version <= 4 && !s.ReadBytes(&p.zkproof, 192) { return nil, errors.New("could not read zkproof") } @@ -188,7 +227,7 @@ func (p *output) ToCompact() *walletrpc.CompactOutput { // joinSplit is a JoinSplit description as described in 7.2 of the Zcash // protocol spec. Its exact contents differ by transaction version and network -// upgrade level. +// upgrade level. Only version 4 is supported, no need for proofPHGR13. type joinSplit struct { vpubOld uint64 vpubNew uint64 @@ -198,12 +237,8 @@ type joinSplit struct { ephemeralKey []byte // 32 randomSeed []byte // 32 vmacs [2][]byte // 64 [N_old][32]byte - proofPHGR13 []byte // 296 - proofGroth16 []byte // 192 + proofGroth16 []byte // 192 (version 4 only) encCiphertexts [2][]byte // 1202 [N_new][601]byte - - // not actually in the format, but needed for parsing - version uint32 } func (p *joinSplit) ParseFromSlice(data []byte) ([]byte, error) { @@ -247,16 +282,8 @@ func (p *joinSplit) ParseFromSlice(data []byte) ([]byte, error) { } } - if p.version == 2 || p.version == 3 { - if !s.ReadBytes(&p.proofPHGR13, 296) { - return nil, errors.New("could not read PHGR13 proof") - } - } else if p.version >= 4 { - if !s.ReadBytes(&p.proofGroth16, 192) { - return nil, errors.New("could not read Groth16 proof") - } - } else { - return nil, errors.New("unexpected transaction version") + if !s.ReadBytes(&p.proofGroth16, 192) { + return nil, errors.New("could not read Groth16 proof") } for i := 0; i < 2; i++ { @@ -325,6 +352,194 @@ func (tx *Transaction) ToCompact(index int) *walletrpc.CompactTx { return ctx } +// parse version 4 transaction data after the nVersionGroupId field. +func (tx *Transaction) parseV4(data []byte) ([]byte, error) { + s := bytestring.String(data) + var err error + if tx.nVersionGroupID != 0x892F2085 { + return nil, errors.New(fmt.Sprintf("version group ID %x must be 0x892F2085", tx.nVersionGroupID)) + } + s, err = tx.ParseTransparent([]byte(s)) + if err != nil { + return nil, err + } + if !s.ReadUint32(&tx.nLockTime) { + return nil, errors.New("could not read nLockTime") + } + + if !s.ReadUint32(&tx.nExpiryHeight) { + return nil, errors.New("could not read nExpiryHeight") + } + + var spendCount, outputCount int + + if !s.ReadInt64(&tx.valueBalanceSapling) { + return nil, errors.New("could not read valueBalance") + } + if !s.ReadCompactSize(&spendCount) { + return nil, errors.New("could not read nShieldedSpend") + } + tx.shieldedSpends = make([]*spend, spendCount) + for i := 0; i < spendCount; i++ { + newSpend := &spend{} + s, err = newSpend.ParseFromSlice([]byte(s), 4) + if err != nil { + return nil, errors.Wrap(err, "while parsing shielded Spend") + } + tx.shieldedSpends[i] = newSpend + } + if !s.ReadCompactSize(&outputCount) { + return nil, errors.New("could not read nShieldedOutput") + } + tx.shieldedOutputs = make([]*output, outputCount) + for i := 0; i < outputCount; i++ { + newOutput := &output{} + s, err = newOutput.ParseFromSlice([]byte(s), 4) + if err != nil { + return nil, errors.Wrap(err, "while parsing shielded Output") + } + tx.shieldedOutputs[i] = newOutput + } + var joinSplitCount int + if !s.ReadCompactSize(&joinSplitCount) { + return nil, errors.New("could not read nJoinSplit") + } + + tx.joinSplits = make([]*joinSplit, joinSplitCount) + if joinSplitCount > 0 { + for i := 0; i < joinSplitCount; i++ { + js := &joinSplit{} + s, err = js.ParseFromSlice([]byte(s)) + if err != nil { + return nil, errors.Wrap(err, "while parsing JoinSplit") + } + tx.joinSplits[i] = js + } + + if !s.ReadBytes(&tx.joinSplitPubKey, 32) { + return nil, errors.New("could not read joinSplitPubKey") + } + + if !s.ReadBytes(&tx.joinSplitSig, 64) { + return nil, errors.New("could not read joinSplitSig") + } + } + if spendCount+outputCount > 0 && !s.ReadBytes(&tx.bindingSigSapling, 64) { + return nil, errors.New("could not read bindingSigSapling") + } + return s, nil +} + +// parse version 5 transaction data after the nVersionGroupId field. +func (tx *Transaction) parseV5(data []byte) ([]byte, error) { + s := bytestring.String(data) + var err error + if !s.ReadUint32(&tx.consensusBranchID) { + return nil, errors.New("could not read nVersionGroupId") + } + if tx.nVersionGroupID != 0x26A7270A { + return nil, errors.New(fmt.Sprintf("version group ID %d must be 0x26A7270A", tx.nVersionGroupID)) + } + if tx.consensusBranchID != 0x37519621 { + return nil, errors.New("unknown consensusBranchID") + } + if !s.ReadUint32(&tx.nLockTime) { + return nil, errors.New("could not read nLockTime") + } + if !s.ReadUint32(&tx.nExpiryHeight) { + return nil, errors.New("could not read nExpiryHeight") + } + s, err = tx.ParseTransparent([]byte(s)) + if err != nil { + return nil, err + } + + var spendCount, outputCount int + if !s.ReadCompactSize(&spendCount) { + return nil, errors.New("could not read nShieldedSpend") + } + if spendCount >= (1 << 16) { + return nil, errors.New(fmt.Sprintf("spentCount (%d) must be less than 2^16", spendCount)) + } + tx.shieldedSpends = make([]*spend, spendCount) + for i := 0; i < spendCount; i++ { + newSpend := &spend{} + s, err = newSpend.ParseFromSlice([]byte(s), tx.version) + if err != nil { + return nil, errors.Wrap(err, "while parsing shielded Spend") + } + tx.shieldedSpends[i] = newSpend + } + if !s.ReadCompactSize(&outputCount) { + return nil, errors.New("could not read nShieldedOutput") + } + if outputCount >= (1 << 16) { + return nil, errors.New(fmt.Sprintf("outputCount (%d) must be less than 2^16", outputCount)) + } + tx.shieldedOutputs = make([]*output, outputCount) + for i := 0; i < outputCount; i++ { + newOutput := &output{} + s, err = newOutput.ParseFromSlice([]byte(s), tx.version) + if err != nil { + return nil, errors.Wrap(err, "while parsing shielded Output") + } + tx.shieldedOutputs[i] = newOutput + } + if spendCount+outputCount > 0 && !s.ReadInt64(&tx.valueBalanceSapling) { + return nil, errors.New("could not read valueBalance") + } + if spendCount > 0 && !s.Skip(32) { + return nil, errors.New("could not skip anchorSapling") + } + if !s.Skip(192 * spendCount) { + return nil, errors.New("could not skip vSpendProofsSapling") + } + if !s.Skip(64 * spendCount) { + return nil, errors.New("could not skip vSpendAuthSigsSapling") + } + if !s.Skip(192 * outputCount) { + return nil, errors.New("could not skip vOutputProofsSapling") + } + if spendCount+outputCount > 0 && !s.ReadBytes(&tx.bindingSigSapling, 64) { + return nil, errors.New("could not read bindingSigSapling") + } + var actionsCount int + if !s.ReadCompactSize(&actionsCount) { + return nil, errors.New("could not read nActionsOrchard") + } + if actionsCount >= (1 << 16) { + return nil, errors.New(fmt.Sprintf("actionsCount (%d) must be less than 2^16", actionsCount)) + } + if !s.Skip(820 * actionsCount) { + return nil, errors.New("could not skip vActionsOrchard") + } + if actionsCount > 0 { + if !s.Skip(1) { + return nil, errors.New("could not skip flagsOrchard") + } + if !s.Skip(8) { + return nil, errors.New("could not skip valueBalanceOrchard") + } + if !s.Skip(32) { + return nil, errors.New("could not skip anchorOrchard") + } + var proofsCount int + if !s.ReadCompactSize(&proofsCount) { + return nil, errors.New("could not read sizeProofsOrchard") + } + if !s.Skip(proofsCount) { + return nil, errors.New("could not skip proofsOrchard") + } + if !s.Skip(64 * actionsCount) { + return nil, errors.New("could not skip vSpendAuthSigsOrchard") + } + if !s.Skip(64) { + return nil, errors.New("could not skip bindingSigOrchard") + } + } + return s, nil +} + // ParseFromSlice deserializes a single transaction from the given data. func (tx *Transaction) ParseFromSlice(data []byte) ([]byte, error) { s := bytestring.String(data) @@ -338,135 +553,26 @@ func (tx *Transaction) ParseFromSlice(data []byte) ([]byte, error) { } tx.fOverwintered = (header >> 31) == 1 + if !tx.fOverwintered { + return nil, errors.New("fOverwinter flag must be set") + } tx.version = header & 0x7FFFFFFF - - if tx.version >= 3 { - if !s.ReadUint32(&tx.nVersionGroupID) { - return nil, errors.New("could not read nVersionGroupId") - } + if tx.version < 4 { + return nil, errors.New(fmt.Sprintf("version number %d must be greater or equal to 4", tx.version)) } - var txInCount int - if !s.ReadCompactSize(&txInCount) { - return nil, errors.New("could not read tx_in_count") + if !s.ReadUint32(&tx.nVersionGroupID) { + return nil, errors.New("could not read nVersionGroupId") } - - // TODO: Duplicate/otherwise-too-many transactions are a possible DoS - // TODO: vector. At the moment we're assuming trusted input. - // See https://nvd.nist.gov/vuln/detail/CVE-2018-17144 for an example. - - if txInCount > 0 { - tx.transparentInputs = make([]*txIn, txInCount) - for i := 0; i < txInCount; i++ { - ti := &txIn{} - s, err = ti.ParseFromSlice([]byte(s)) - if err != nil { - return nil, errors.Wrap(err, "while parsing transparent input") - } - tx.transparentInputs[i] = ti - } + // parse the main part of the transaction + if tx.version <= 4 { + s, err = tx.parseV4([]byte(s)) + } else { + s, err = tx.parseV5([]byte(s)) } - - var txOutCount int - if !s.ReadCompactSize(&txOutCount) { - return nil, errors.New("could not read tx_out_count") + if err != nil { + return nil, err } - - if txOutCount > 0 { - tx.transparentOutputs = make([]*txOut, txOutCount) - for i := 0; i < txOutCount; i++ { - to := &txOut{} - s, err = to.ParseFromSlice([]byte(s)) - if err != nil { - return nil, errors.Wrap(err, "while parsing transparent output") - } - tx.transparentOutputs[i] = to - } - } - - if !s.ReadUint32(&tx.nLockTime) { - return nil, errors.New("could not read nLockTime") - } - - if tx.fOverwintered { - if !s.ReadUint32(&tx.nExpiryHeight) { - return nil, errors.New("could not read nExpiryHeight") - } - } - - var spendCount, outputCount int - - if tx.version >= 4 { - if !s.ReadInt64(&tx.valueBalance) { - return nil, errors.New("could not read valueBalance") - } - - if !s.ReadCompactSize(&spendCount) { - return nil, errors.New("could not read nShieldedSpend") - } - - if spendCount > 0 { - tx.shieldedSpends = make([]*spend, spendCount) - for i := 0; i < spendCount; i++ { - newSpend := &spend{} - s, err = newSpend.ParseFromSlice([]byte(s)) - if err != nil { - return nil, errors.Wrap(err, "while parsing shielded Spend") - } - tx.shieldedSpends[i] = newSpend - } - } - - if !s.ReadCompactSize(&outputCount) { - return nil, errors.New("could not read nShieldedOutput") - } - - if outputCount > 0 { - tx.shieldedOutputs = make([]*output, outputCount) - for i := 0; i < outputCount; i++ { - newOutput := &output{} - s, err = newOutput.ParseFromSlice([]byte(s)) - if err != nil { - return nil, errors.Wrap(err, "while parsing shielded Output") - } - tx.shieldedOutputs[i] = newOutput - } - } - } - - if tx.version >= 2 { - var joinSplitCount int - if !s.ReadCompactSize(&joinSplitCount) { - return nil, errors.New("could not read nJoinSplit") - } - - if joinSplitCount > 0 { - tx.joinSplits = make([]*joinSplit, joinSplitCount) - for i := 0; i < joinSplitCount; i++ { - js := &joinSplit{version: tx.version} - s, err = js.ParseFromSlice([]byte(s)) - if err != nil { - return nil, errors.Wrap(err, "while parsing JoinSplit") - } - tx.joinSplits[i] = js - } - - if !s.ReadBytes(&tx.joinSplitPubKey, 32) { - return nil, errors.New("could not read joinSplitPubKey") - } - - if !s.ReadBytes(&tx.joinSplitSig, 64) { - return nil, errors.New("could not read joinSplitSig") - } - } - } - - if tx.version >= 4 && (spendCount+outputCount > 0) { - if !s.ReadBytes(&tx.bindingSig, 64) { - return nil, errors.New("could not read bindingSig") - } - } - // TODO: implement rawBytes with MarshalBinary() instead txLen := len(data) - len(s) tx.rawBytes = data[:txLen] diff --git a/parser/transaction_test.go b/parser/transaction_test.go index 6067113..4b6a372 100644 --- a/parser/transaction_test.go +++ b/parser/transaction_test.go @@ -2,882 +2,3 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . package parser - -import ( - "bufio" - "bytes" - "encoding/binary" - "encoding/hex" - "os" - "strings" - "testing" - - "github.com/zcash/lightwalletd/parser/internal/bytestring" -) - -// "Human-readable" version of joinSplit struct defined in transaction.go. -// Remember to update this if the format ever changes. -type joinSplitTestVector struct { - vpubOld uint64 - vpubNew uint64 - anchor string // 32 - nullifiers []string // 64 [N_old][32]byte - commitments []string // 64 [N_new][32]byte - ephemeralKey string // 32 - randomSeed string // 32 - vmacs []string // 64 [N_old][32]byte - proofPHGR13 string // 296 - proofGroth16 string // 192 - encCiphertexts []string // 1202 [N_new][601]byte -} - -type spendTestVector struct { - cv string // 32 - anchor string // 32 - nullifier string // 32 - rk string // 32 - zkproof string // 192 - spendAuthSig string // 64 -} - -type outputTestVector struct { - cv string // 32 - cmu string // 32 - ephemeralKey string // 32 - encCiphertext string // 580 - outCiphertext string // 80 - zkproof string // 192 -} - -type txTestVector struct { - // Sprout and Sapling - txid, header, nVersionGroupID, nLockTime, nExpiryHeight string - vin, vout [][]string - vJoinSplits []joinSplitTestVector - joinSplitPubKey, joinSplitSig string - - // Sapling-only - valueBalance string // encoded int64 - spends []spendTestVector - outputs []outputTestVector - bindingSig string -} - -// https://github.com/zcash/zips/blob/master/zip-0143.rst -var zip143tests = []txTestVector{ - { - // Test vector 1 - txid: "f0b22277ac851b5f4df590fe6a128aad9d0ce8063235eb2b328c2dc6a23c1ec5", - header: "03000080", - nVersionGroupID: "7082c403", - nLockTime: "481cdd86", - nExpiryHeight: "b3cc4318", - vin: nil, - vout: [][]string{ - {"8f739811893e0000", "095200ac6551ac636565"}, - {"b1a45a0805750200", "025151"}, - }, - }, - { - // Test vector 2 - //raw: "we have some raw data for this tx, which this comment is too small to contain", - txid: "39fe585a56b005f568c3171d22afa916e946e2a8aff5971d58ee8a6fc1482059", - header: "03000080", - nVersionGroupID: "7082c403", - nLockTime: "97b0e4e4", - nExpiryHeight: "c705fc05", - vin: [][]string{ - {"4201cfb1cd8dbf69b8250c18ef41294ca97993db546c1fe01f7e9c8e36d6a5e2", "9d4e30a7", "03ac6a00", "98421c69"}, - {"378af1e40f64e125946f62c2fa7b2fecbcb64b6968912a6381ce3dc166d56a1d", "62f5a8d7", "056363635353", "e8c7203d"}, - }, - vout: [][]string{ - {"6af786387ae60100", "080063656a63ac5200"}, - {"23752997f4ff0400", "0751510053536565"}, - }, - vJoinSplits: []joinSplitTestVector{ - { - vpubOld: uint64(0), - vpubNew: uint64(0), - anchor: "76495c222f7fba1e31defa3d5a57efc2e1e9b01a035587d5fb1a38e01d94903d", - nullifiers: []string{ - "3c3e0ad3360c1d3710acd20b183e31d49f25c9a138f49b1a537edcf04be34a98", - "51a7af9db6990ed83dd64af3597c04323ea51b0052ad8084a8b9da948d320dad", - }, - commitments: []string{ - "d64f5431e61ddf658d24ae67c22c8d1309131fc00fe7f235734276d38d47f1e1", - "91e00c7a1d48af046827591e9733a97fa6b679f3dc601d008285edcbdae69ce8", - }, - ephemeralKey: "fc1be4aac00ff2711ebd931de518856878f73476f21a482ec9378365c8f7393c", - randomSeed: "94e2885315eb4671098b79535e790fe53e29fef2b3766697ac32b4f473f468a0", - vmacs: []string{ - "08e72389fc03880d780cb07fcfaabe3f1a84b27db59a4a153d882d2b21035965", - "55ed9494c6ac893c49723833ec8926c1039586a7afcf4a0d9c731e985d99589c", - }, - proofPHGR13: "03b838e8aaf745533ed9e8ae3a1cd074a51a20da8aba18d1dbebbc862ded42435e02476930d069896cff30eb414f727b89e001afa2fb8dc3436d75a4a6f26572504b0b2232ecb9f0c02411e52596bc5e90457e745939ffedbd12863ce71a02af117d417adb3d15cc54dcb1fce467500c6b8fb86b12b56da9c382857deecc40a98d5f2903395ee4762dd21afdbb5d47fa9a6dd984d567db2857b927b7fae2db587105415d0242789d38f50b8dbcc129cab3d17d19f3355bcf73cecb8cb8a5da01307152f13902a270572670dc82d39026c6cb4cd4b0f7f5aa2a4f5a5341ec5dd715406f2fdd2a02733f5f641c8c21862a1bafce2609d9eecfa158cfb5cd79f88008e315dc7d8388036c1782fd2795d18a763624c25fa959cc97489ce75745824b77868c53239cfbdf", - encCiphertexts: []string{ - "73caec65604037314faaceb56218c6bd30f8374ac13386793f21a9fb80ad03bc0cda4a44946c00e1b1a1df0e5b87b5bece477a709649e950060591394812951e1fe3895b8cc3d14d2cf6556df6ed4b4ddd3d9a69f53357d7767f4f5ccbdbc596631277f8fecd08cb056b95e3025b9792fff7f244fc716269b926d62e9596fa825c6bf21aff9e68625a192440ea06828123d97884806f15fa08da52754a1095e3ff1abd5ce4fddfccfc3a6128aef784a64610a89d1a7099216d0814d3a2d452431c32d411ac1cce82ad0229407bbc48985675e3f874a4533f1d63a84dfa3e0f460fe2f57e34fbc75423c3737f5b2a0615f5722db041a3ef66fa483afd3c2e19e59444a64add6df1d963f5dd5b5010d3d025f0287c4cf19c75f33d51ddddba5d657b43ee8da645443814cc7329f3e9b4e54c236c29af3923101756d9fa4bd0f7d2ddaacb6b0f86a2658e0a07a05ac5b950051cd24c47a88d13d659ba2a46ca1830816d09cd7646f76f716abec5de07fe9b523410806ea6f288f8736c23357c85f45791e1708029d9824d90704607f387a03e49bf9836574431345a7877efaa8a08e73081ef8d62cb780ab6883a50a0d470190dfba10a857f82842d3825b3d6da0573d316eb160dc0b716c48fbd467f75b780149ae8808f4e68f50c0536acddf6f1aeab016b6bc1ec144b4e553acfd670f77e755fc88e0677e31ba459b44e307768958fe3789d41c2b1ff434cb30e15914f01bc6bc2307b488d2556d7b7380ea4ffd712f6b02fe806b94569cd4059f396bf29b99d0a40e5e1711ca944f72d436a102fca4b97693da0b086fe9d2e7162470d02e0f05d4bec9512bf", - "b3f38327296efaa74328b118c27402c70c3a90b49ad4bbc68e37c0aa7d9b3fe17799d73b841e751713a02943905aae0803fd69442eb7681ec2a05600054e92eed555028f21b6a155268a2dd6640a69301a52a38d4d9f9f957ae35af7167118141ce4c9be0a6a492fe79f1581a155fa3a2b9dafd82e650b386ad3a08cb6b83131ac300b0846354a7eef9c410e4b62c47c5426907dfc6685c5c99b7141ac626ab4761fd3f41e728e1a28f89db89ffdeca364dd2f0f0739f0534556483199c71f189341ac9b78a269164206a0ea1ce73bfb2a942e7370b247c046f8e75ef8e3f8bd821cf577491864e20e6d08fd2e32b555c92c661f19588b72a89599710a88061253ca285b6304b37da2b5294f5cb354a894322848ccbdc7c2545b7da568afac87ffa005c312241c2d57f4b45d6419f0d2e2c5af33ae243785b325cdab95404fc7aed70525cddb41872cfcc214b13232edc78609753dbff930eb0dc156612b9cb434bc4b693392deb87c530435312edcedc6a961133338d786c4a3e103f60110a16b1337129704bf4754ff6ba9fbe65951e610620f71cda8fc877625f2c5bb04cbe1228b1e886f4050afd8fe94e97d2e9e85c6bb748c0042d3249abb1342bb0eebf62058bf3de080d94611a3750915b5dc6c0b3899d41222bace760ee9c8818ded599e34c56d7372af1eb86852f2a732104bdb750739de6c2c6e0f9eb7cb17f1942bfc9f4fd6ebb6b4cdd4da2bca26fac4578e9f543405acc7d86ff59158bd0cba3aef6f4a8472d144d99f8b8d1dedaa9077d4f01d4bb27bbe31d88fbefac3dcd4797563a26b1d61fcd9a464ab21ed550fe6fa09695ba0b2f10e", - }, - }, - { - vpubOld: uint64(0), - vpubNew: uint64(0), - anchor: "ea6468cc6e20a66f826e3d14c5006f0563887f5e1289be1b2004caca8d3f34d6", - nullifiers: []string{ - "e84bf59c1e04619a7c23a996941d889e4622a9b9b1d59d5e319094318cd405ba", - "27b7e2c084762d31453ec4549a4d97729d033460fcf89d6494f2ffd789e98082", - }, - commitments: []string{ - "ea5ce9534b3acd60fe49e37e4f666931677319ed89f85588741b3128901a93bd", - "78e4be0225a9e2692c77c969ed0176bdf9555948cbd5a332d045de6ba6bf4490", - }, - ephemeralKey: "adfe7444cd467a09075417fcc0062e49f008c51ad4227439c1b4476ccd8e9786", - randomSeed: "2dab7be1e8d399c05ef27c6e22ee273e15786e394c8f1be31682a30147963ac8", - vmacs: []string{ - "da8d41d804258426a3f70289b8ad19d8de13be4eebe3bd4c8a6f55d6e0c373d4", - "56851879f5fbc282db9e134806bff71e11bc33ab75dd6ca067fb73a043b646a7", - }, - proofPHGR13: "0339cab4928386786d2f24141ee120fdc34d6764eafc66880ee0204f53cc1167ed02b43a52dea3ca7cff8ef35cd8e6d7c111a68ef44bcd0c1513ad47ca61c659cc5d0a5b440f6b9f59aff66879bb6688fd2859362b182f207b3175961f6411a493bffd048e7d0d87d82fe6f990a2b0a25f5aa0111a6e68f37bf6f3ac2d26b84686e569038d99c1383597fad81193c4c1b16e6a90e2d507cdfe6fbdaa86163e9cf5de310003ca7e8da047b090db9f37952fbfee76af61668190bd52ed490e677b515d0143840307219c7c0ee7fc7bfc79f325644e4df4c0d7db08e9f0bd024943c705abff899403a605cfbc7ed746a7d3f7c37d9e8bdc433b7d79e08a12f738a8f0dbddfef2f26502f3e47d1b0fd11e6a13311fb799c79c641d9da43b33e7ad012e28255398789262", - encCiphertexts: []string{ - "275f1175be8462c01491c4d842406d0ec4282c9526174a09878fe8fdde33a29604e5e5e7b2a025d6650b97dbb52befb59b1d30a57433b0a351474444099daa371046613260cf3354cfcdada663ece824ffd7e44393886a86165ddddf2b4c41773554c86995269408b11e6737a4c447586f69173446d8e48bf84cbc000a807899973eb93c5e819aad669413f8387933ad1584aa35e43f4ecd1e2d0407c0b1b89920ffdfdb9bea51ac95b557af71b89f903f5d9848f14fcbeb1837570f544d6359eb23faf38a0822da36ce426c4a2fbeffeb0a8a2e297a9d19ba15024590e3329d9fa9261f9938a4032dd34606c9cf9f3dd33e576f05cd1dd6811c6298757d77d9e810abdb226afcaa4346a6560f8932b3181fd355d5d391976183f8d99388839632d6354f666d09d3e5629ea19737388613d38a34fd0f6e50ee5a0cc9677177f50028c141378187bd2819403fc534f80076e9380cb4964d3b6b45819d3b8e9caf54f051852d671bf8c1ffde2d1510756418cb4810936aa57e6965d6fb656a760b7f19adf96c173488552193b147ee58858033dac7cd0eb204c06490bbdedf5f7571acb2ebe76acef3f2a01ee987486dfe6c3f0a5e234c127258f97a28fb5d164a8176be946b8097d0e317287f33bf9c16f9a545409ce29b1f4273725fc0df02a04ebae178b3414fb0a82d50deb09fcf4e6ee9d180ff4f56ff3bc1d3601fc2dc90d814c3256f4967d3a8d64c83fea339c51f5a8e5801fbb97835581b602465dee04b5922c2761b54245bec0c9eef2db97d22b2b3556cc969fbb13d06509765a52b3fac54b93f421bf08e18d52ddd52cc1c8ca8adfaccab7e5cc2", - "f4573fbbf8239bb0b8aedbf8dad16282da5c9125dba1c059d0df8abf621078f02d6c4bc86d40845ac1d59710c45f07d585eb48b32fc0167ba256e73ca3b9311c62d109497957d8dbe10aa3e866b40c0baa2bc492c19ad1e6372d9622bf163fbffeaeee796a3cd9b6fbbfa4d792f34d7fd6e763cd5859dd26833d21d9bc5452bd19515dff9f4995b35bc0c1f876e6ad11f2452dc9ae85aec01fc56f8cbfda75a7727b75ebbd6bbffb43b63a3b1b671e40feb0db002974a3c3b1a788567231bf6399ff89236981149d423802d2341a3bedb9ddcbac1fe7b6435e1479c72e7089d029e7fbbaf3cf37e9b9a6b776791e4c5e6fda57e8d5f14c8c35a2d270846b9dbe005cda16af4408f3ab06a916eeeb9c9594b70424a4c1d171295b6763b22f47f80b53ccbb904bd68fd65fbd3fbdea1035e98c21a7dbc91a9b5bc7690f05ec317c97f8764eb48e911d428ec8d861b708e8298acb62155145155ae95f0a1d1501034753146e22d05f586d7f6b4fe12dad9a17f5db70b1db96b8d9a83edadc966c8a5466b61fc998c31f1070d9a5c9a6d268d304fe6b8fd3b4010348611abdcbd49fe4f85b623c7828c71382e1034ea67bc8ae97404b0c50b2a04f559e49950afcb0ef462a2ae024b0f0224dfd73684b88c7fbe92d02b68f759c4752663cd7b97a14943649305521326bde085630864629291bae25ff8822a14c4b666a9259ad0dc42a8290ac7bc7f53a16f379f758e5de750f04fd7cad47701c8597f97888bea6fa0bf2999956fbfd0ee68ec36e4688809ae231eb8bc4369f5fe1573f57e099d9c09901bf39caac48dc11956a8ae905ead86954547c448ae43d31", - }, - }, - }, - - joinSplitPubKey: "5e669c4242da565938f417bf43ce7b2b30b1cd4018388e1a910f0fc41fb0877a", - // This joinSplitSig is (intentionally) invalid random data. - joinSplitSig: "5925e466819d375b0a912d4fe843b76ef6f223f0f7c894f38f7ab780dfd75f669c8c06cffa43eb47565a50e3b1fa45ad61ce9a1c4727b7aaa53562f523e73952", - }, -} - -func TestSproutTransactionParser(t *testing.T) { - // The raw data are stored in a separate file because they're large enough - // to make the test table difficult to scroll through. They are in the same - // order as the test table above. If you update the test table without - // adding a line to the raw file, this test will panic due to index - // misalignment. - testData, err := os.Open("../testdata/zip143_raw_tx") - if err != nil { - t.Fatal(err) - } - defer testData.Close() - - // Parse the raw transactions file - rawTxData := [][]byte{} - scan := bufio.NewScanner(testData) - for scan.Scan() { - dataLine := scan.Text() - // Skip the comments - if strings.HasPrefix(dataLine, "#") { - continue - } - - txData, err := hex.DecodeString(dataLine) - if err != nil { - t.Fatal(err) - } - rawTxData = append(rawTxData, txData) - } - - for i, tt := range zip143tests { - tx := NewTransaction() - - rest, err := tx.ParseFromSlice(rawTxData[i]) - if err != nil { - t.Errorf("Test %d: %v", i, err) - continue - } - - if len(rest) != 0 { - t.Errorf("Test %d: did not consume entire buffer", i) - continue - } - - // Transaction metadata - if !subTestCommonBlockMeta(&tt, tx, t, i) { - continue - } - - // Transparent inputs and outputs - if !subTestTransparentInputs(tt.vin, tx.transparentInputs, t, i) { - continue - } - - if !subTestTransparentOutputs(tt.vout, tx.transparentOutputs, t, i) { - continue - } - - // JoinSplits - if !subTestJoinSplits(tt.vJoinSplits, tx.joinSplits, t, i) { - continue - } - - testJSPubKey, _ := hex.DecodeString(tt.joinSplitPubKey) - if !bytes.Equal(testJSPubKey, tx.joinSplitPubKey) { - t.Errorf("Test %d: jsPubKey mismatch %x %x", i, testJSPubKey, tx.joinSplitPubKey) - continue - } - - testJSSig, _ := hex.DecodeString(tt.joinSplitSig) - if !bytes.Equal(testJSSig, tx.joinSplitSig) { - t.Errorf("Test %d: jsSig mismatch %x %x", i, testJSSig, tx.joinSplitSig) - continue - } - if hex.EncodeToString(tx.GetDisplayHash()) != tt.txid { - t.Errorf("Test %d: incorrect txid", i) - } - } -} - -func subTestCommonBlockMeta(tt *txTestVector, tx *Transaction, t *testing.T, caseNum int) bool { - headerBytes, _ := hex.DecodeString(tt.header) - header := binary.LittleEndian.Uint32(headerBytes) - if (header >> 31) == 1 != tx.fOverwintered { - t.Errorf("Test %d: unexpected fOverwintered", caseNum) - return false - } - if (header & 0x7FFFFFFF) != tx.version { - t.Errorf("Test %d: unexpected tx version", caseNum) - return false - } - - versionGroupBytes, _ := hex.DecodeString(tt.nVersionGroupID) - versionGroup := binary.LittleEndian.Uint32(versionGroupBytes) - if versionGroup != tx.nVersionGroupID { - t.Errorf("Test %d: unexpected versionGroupId", caseNum) - return false - } - - lockTimeBytes, _ := hex.DecodeString(tt.nLockTime) - lockTime := binary.LittleEndian.Uint32(lockTimeBytes) - if lockTime != tx.nLockTime { - t.Errorf("Test %d: unexpected nLockTime", caseNum) - return false - } - - expiryHeightBytes, _ := hex.DecodeString(tt.nExpiryHeight) - expiryHeight := binary.LittleEndian.Uint32(expiryHeightBytes) - if expiryHeight != tx.nExpiryHeight { - t.Errorf("Test %d: unexpected nExpiryHeight", caseNum) - return false - } - - return true -} - -func subTestJoinSplits(testJoinSplits []joinSplitTestVector, txJoinSplits []*joinSplit, t *testing.T, caseNum int) bool { - if testJoinSplits == nil && txJoinSplits != nil { - t.Errorf("Test %d: non-zero joinSplits when expected empty vector", caseNum) - return false - } - if len(testJoinSplits) != len(txJoinSplits) { - t.Errorf("Test %d: joinSplit vector lengths mismatch", caseNum) - return false - } - - success := true - -JoinSplitLoop: - for idx, test := range testJoinSplits { - tx := txJoinSplits[idx] - - if test.vpubOld != tx.vpubOld { - t.Errorf("Test %d js %d: vpubOld %d %d", caseNum, idx, test.vpubOld, tx.vpubOld) - success = false - continue - } - if test.vpubNew != tx.vpubNew { - t.Errorf("Test %d js %d: vpubNew %d %d", caseNum, idx, test.vpubNew, tx.vpubNew) - success = false - continue - } - - anchor, _ := hex.DecodeString(test.anchor) - if !bytes.Equal(anchor, tx.anchor) { - t.Errorf("Test %d js %d: anchor %x %x", caseNum, idx, anchor, tx.anchor) - success = false - continue - } - - if len(test.nullifiers) != len(tx.nullifiers) { - t.Errorf("Test %d js %d: nf len mismatch %d %d", caseNum, idx, len(test.nullifiers), len(tx.nullifiers)) - success = false - continue - } - - for j := 0; j < len(test.nullifiers); j++ { - nf, _ := hex.DecodeString(test.nullifiers[j]) - if !bytes.Equal(nf, tx.nullifiers[j]) { - t.Errorf("Test %d js %d: nf mismatch %x %x", caseNum, idx, nf, tx.nullifiers[j]) - success = false - continue JoinSplitLoop - } - } - - if len(test.commitments) != len(tx.commitments) { - t.Errorf("Test %d js %d: cm len mismatch %d %d", caseNum, idx, len(test.commitments), len(tx.commitments)) - success = false - continue - } - - for j := 0; j < len(test.commitments); j++ { - cm, _ := hex.DecodeString(test.commitments[j]) - if !bytes.Equal(cm, tx.commitments[j]) { - t.Errorf("Test %d js %d: commit mismatch %x %x", caseNum, idx, cm, tx.commitments[j]) - success = false - continue JoinSplitLoop - } - } - - ephemeralKey, _ := hex.DecodeString(test.ephemeralKey) - if !bytes.Equal(ephemeralKey, tx.ephemeralKey) { - t.Errorf("Test %d js %d: ephemeralKey %x %x", caseNum, idx, ephemeralKey, tx.ephemeralKey) - success = false - continue - } - - randomSeed, _ := hex.DecodeString(test.randomSeed) - if !bytes.Equal(randomSeed, tx.randomSeed) { - t.Errorf("Test %d js %d: randomSeed %x %x", caseNum, idx, randomSeed, tx.randomSeed) - success = false - continue - } - - if len(test.vmacs) != len(tx.vmacs) { - t.Errorf("Test %d js %d: mac len mismatch %d %d", caseNum, idx, len(test.vmacs), len(tx.vmacs)) - success = false - continue - } - - for j := 0; j < len(test.vmacs); j++ { - mac, _ := hex.DecodeString(test.vmacs[j]) - if !bytes.Equal(mac, tx.vmacs[j]) { - t.Errorf("Test %d js %d: mac mismatch %x %x", caseNum, idx, mac, tx.vmacs[j]) - success = false - continue JoinSplitLoop - } - } - - // This should not be possible. - if tx.proofPHGR13 != nil && tx.proofGroth16 != nil { - t.Errorf("Test %d js %d: parsed tx had both PHGR and Groth proofs defined", caseNum, idx) - success = false - continue - } - - if test.proofPHGR13 != "" { - zkproof, _ := hex.DecodeString(test.proofPHGR13) - if !bytes.Equal(zkproof, tx.proofPHGR13) { - t.Errorf("Test %d js %d: zkproof %x %x", caseNum, idx, zkproof, tx.proofPHGR13) - success = false - continue - } - } - - if test.proofGroth16 != "" { - zkproof, _ := hex.DecodeString(test.proofGroth16) - if !bytes.Equal(zkproof, tx.proofGroth16) { - t.Errorf("Test %d js %d: zkproof %x %x", caseNum, idx, zkproof, tx.proofGroth16) - success = false - continue - } - } - - if len(test.encCiphertexts) != len(tx.encCiphertexts) { - t.Errorf("Test %d js %d: enc len mismatch %d %d", caseNum, idx, len(test.encCiphertexts), len(tx.encCiphertexts)) - success = false - continue - } - - for j := 0; j < len(test.encCiphertexts); j++ { - ct, _ := hex.DecodeString(test.encCiphertexts[j]) - if !bytes.Equal(ct, tx.encCiphertexts[j]) { - t.Errorf("Test %d js %d: ct mismatch %x %x", caseNum, idx, ct, tx.encCiphertexts[j]) - success = false - continue JoinSplitLoop - } - } - } - - return success -} - -func subTestTransparentInputs(testInputs [][]string, txInputs []*txIn, t *testing.T, caseNum int) bool { - if testInputs == nil && txInputs != nil { - t.Errorf("Test %d: non-zero vin when expected zero", caseNum) - return false - } - - if len(testInputs) != len(txInputs) { - t.Errorf("Test %d: vins have mismatched lengths", caseNum) - return false - } - - success := true - le := binary.LittleEndian - - // 4201cfb1cd8dbf69b8250c18ef41294ca97993db546c1fe01f7e9c8e36d6a5e2 9d4e30a7 03ac6a00 98421c69 - for idx, ti := range testInputs { - txInput := txInputs[idx] - - testPrevTxHash, _ := hex.DecodeString(ti[0]) - if eq := bytes.Equal(testPrevTxHash, txInput.PrevTxHash); !eq { - t.Errorf("Test %d tin %d: prevhash mismatch %x %x", caseNum, idx, testPrevTxHash, txInput.PrevTxHash) - success = false - continue - } - - testPrevTxOutIndexBytes, _ := hex.DecodeString(ti[1]) - testPrevTxOutIndex := le.Uint32(testPrevTxOutIndexBytes) - if testPrevTxOutIndex != txInput.PrevTxOutIndex { - t.Errorf("Test %d tin %d: prevout index mismatch %d %d", caseNum, idx, testPrevTxOutIndex, txInput.PrevTxOutIndex) - success = false - continue - } - - // Decode scriptSig and correctly consume own CompactSize field - testScriptSig, _ := hex.DecodeString(ti[2]) - ok := (*bytestring.String)(&testScriptSig).ReadCompactLengthPrefixed((*bytestring.String)(&testScriptSig)) - if !ok { - t.Errorf("Test %d, tin %d: couldn't strip size from script", caseNum, idx) - success = false - continue - } - - if eq := bytes.Equal(testScriptSig, txInput.ScriptSig); !eq { - t.Errorf("Test %d tin %d: scriptsig mismatch %x %x", caseNum, idx, testScriptSig, txInput.ScriptSig) - success = false - continue - } - - testSeqNumBytes, _ := hex.DecodeString(ti[3]) - testSeqNum := le.Uint32(testSeqNumBytes) - if testSeqNum != txInput.SequenceNumber { - t.Errorf("Test %d tin %d: seq mismatch %d %d", caseNum, idx, testSeqNum, txInput.SequenceNumber) - success = false - continue - } - } - return success -} - -func subTestTransparentOutputs(testOutputs [][]string, txOutputs []*txOut, t *testing.T, caseNum int) bool { - if testOutputs == nil && txOutputs != nil { - t.Errorf("Test %d: non-zero vout when expected zero", caseNum) - return false - } - - if len(testOutputs) != len(txOutputs) { - t.Errorf("Test %d: vout have mismatched lengths", caseNum) - return false - } - - success := true - le := binary.LittleEndian - - for idx, testOutput := range testOutputs { - txOutput := txOutputs[idx] - - // Parse tx out value from test - testValueBytes, _ := hex.DecodeString(testOutput[0]) - testValue := le.Uint64(testValueBytes) - - if testValue != txOutput.Value { - t.Errorf("Test %d, tout %d: value mismatch %d %d", caseNum, idx, testValue, txOutput.Value) - success = false - continue - } - - // Parse script from test - testScript, _ := hex.DecodeString(testOutput[1]) - // Correctly consume own CompactSize field - ok := (*bytestring.String)(&testScript).ReadCompactLengthPrefixed((*bytestring.String)(&testScript)) - if !ok { - t.Errorf("Test %d, tout %d: couldn't strip size from script", caseNum, idx) - success = false - continue - } - - if !bytes.Equal(testScript, txOutput.Script) { - t.Errorf("Test %d, tout %d: script mismatch %x %x", caseNum, idx, testScript, txOutput.Script) - success = false - continue - } - } - return success -} - -// https://github.com/zcash/zips/blob/master/zip-0243.rst -var zip243tests = []txTestVector{ - // Test vector 1 - { - txid: "5fc4867a1b8bd5ab709799adf322a85d10607e053726d5f5ab4b1c9ab897e6bc", - header: "04000080", - nVersionGroupID: "85202f89", - vin: nil, - vout: [][]string{ - {"e7719811893e0000", "095200ac6551ac636565"}, - {"b2835a0805750200", "025151"}, - }, - nLockTime: "481cdd86", - nExpiryHeight: "b3cc4318", - valueBalance: "442117623ceb0500", - spends: []spendTestVector{ - { - cv: "1b3d1a027c2c40590958b7eb13d742a997738c46a458965baf276ba92f272c72", - anchor: "1fe01f7e9c8e36d6a5e29d4e30a73594bf5098421c69378af1e40f64e125946f", - nullifier: "62c2fa7b2fecbcb64b6968912a6381ce3dc166d56a1d62f5a8d7551db5fd9313", - rk: "e8c7203d996af7d477083756d59af80d06a745f44ab023752cb5b406ed8985e1", - zkproof: "8130ab33362697b0e4e4c763ccb8f676495c222f7fba1e31defa3d5a57efc2e1e9b01a035587d5fb1a38e01d94903d3c3e0ad3360c1d3710acd20b183e31d49f25c9a138f49b1a537edcf04be34a9851a7af9db6990ed83dd64af3597c04323ea51b0052ad8084a8b9da948d320dadd64f5431e61ddf658d24ae67c22c8d1309131fc00fe7f235734276d38d47f1e191e00c7a1d48af046827591e9733a97fa6b679f3dc601d008285edcbdae69ce8fc1be4aac00ff2711ebd931de518856878", - spendAuthSig: "f73476f21a482ec9378365c8f7393c94e2885315eb4671098b79535e790fe53e29fef2b3766697ac32b4f473f468a008e72389fc03880d780cb07fcfaabe3f1a", - }, - { - cv: "15825b7acb4d6b57a61bc68f242b52e4fbf85cf1a09cc45b6d6bb3a391578f49", - anchor: "9486a7afd04a0d9c74c2995d96b4de37b36046a1ef6d190b916b1111c9288731", - nullifier: "1a20da8aba18d1dbebbc862ded42435e92476930d069896cff30eb414f727b89", - rk: "e001afa2fb8dc3436d75a4a6f26572504b192232ecb9f0c02411e52596bc5e90", - zkproof: "457e745939ffedbd12863ce71a02af117d417adb3d15cc54dcb1fce467500c6b8fb86b12b56da9c382857deecc40a98d5f2935395ee4762dd21afdbb5d47fa9a6dd984d567db2857b927b7fae2db587105415d4642789d38f50b8dbcc129cab3d17d19f3355bcf73cecb8cb8a5da01307152f13936a270572670dc82d39026c6cb4cd4b0f7f5aa2a4f5a5341ec5dd715406f2fdd2afa733f5f641c8c21862a1bafce2609d9eecfa158cfb5cd79f88008e315dc7d8388e76c1782fd2795d18a76", - spendAuthSig: "3624c25fa959cc97489ce75745824b77868c53239cfbdf73caec65604037314faaceb56218c6bd30f8374ac13386793f21a9fb80ad03bc0cda4a44946c00e1b1", - }, - { - cv: "02c78f11876b7065212183199fb5979ca77d2c24c738fe5145f02602053bb4c2", - anchor: "f6556df6ed4b4ddd3d9a69f53357d7767f4f5ccbdbc596631277f8fecd08cb05", - nullifier: "6b95e3025b9792fff7f244fc716269b926d62e9596fa825c6bf21aff9e68625a", - rk: "192440ea06828123d97884806f15fa08da52754a1095e3ff1abd5ce4fddfccfc", - zkproof: "3a6128aef784a64610a89d1a7099216d0814d3a2d452431c32d411ac1cce82ad0229407bbc48985675e3f874a4533f1d63a84dfa3e0f460fe2f57e34fbc75423c3737f5b2a0615f5722db041a3ef66fa483afd3c2e19e59444a64add6df1d963f5dd5b5010d3d025f0287c4cf19c75f33d51ddddba5d657b43ee8da645443814cc7329f3e9b4e54c236c29af3923101756d9fa4bd0f7d2ddaacb6b0f86a2658e0a07a05ac5b950051cd24c47a88d13d659ba2a46ca1830816d09cd7646f76f71", - spendAuthSig: "6abec5de07fe9b523410806ea6f288f8736c23357c85f45791e1708029d9824d90704607f387a03e49bf9836574431345a7877efaa8a08e73081ef8d62cb780a", - }, - }, - outputs: []outputTestVector{ - { - cv: "0fa3207ee2f0408097d563da1b2146819edf88d33e7753664fb71d122a6e3699", - cmu: "8fbd467f75b780149ae8808f4e68f50c0536acddf6f1aeab016b6bc1ec144b4e", - ephemeralKey: "59aeb77eef49d00e5fbb67101cdd41e6bc9cf641a52fca98be915f8440a410d7", - encCiphertext: "4cb30e15914f01bc6bc2307b488d2556d7b7380ea4ffd712f6b02fe806b94569cd4059f396bf29b99d0a40e5e1711ca944f72d436a102fca4b97693da0b086fe9d2e7162470d02e0f05d4bec9512bfb3f38327296efaa74328b118c27402c70c3a90b49ad4bbc68e37c0aa7d9b3fe17799d73b841e751713a02943905aae0803fd69442eb7681ec2a05600054e92eed555028f21b6a155268a2dd6640a69301a52a38d4d9f9f957ae35af7167118141ce4c9be0a6a492fe79f1581a155fa3a2b9dafd82e650b386ad3a08cb6b83131ac300b0846354a7eef9c410e4b62c47c5426907dfc6685c5c99b7141ac626ab4761fd3f41e728e1a28f89db89ffdeca364dd2f0f0739f0534556483199c71f189341ac9b78a269164206a0ea1ce73bfb2a942e7370b247c046f8e75ef8e3f8bd821cf577491864e20e6d08fd2e32b555c92c661f19588b72a89599710a88061253ca285b6304b37da2b5294f5cb354a894322848ccbdc7c2545b7da568afac87ffa005c312241c2d57f4b45d6419f0d2e2c5af33ae243785b325cdab95404fc7aed70525cddb41872cfcc214b13232edc78609753dbff930eb0dc156612b9cb434bc4b693392deb87c530435312edcedc6a961133338d786c4a3e103f60110a16b1337129704bf4754ff6ba9fbe65951e610620f71cda8fc877625f2c5bb04cbe1228b1e886f4050afd8fe94e97d2e9e85c6bb748c0042d3249abb1342bb0eebf62058bf3de080d94611a3750915b5dc6c0b3899d41222bace760ee9c8818ded599e34c56d7372af1eb86852f2a732104bdb750739", - outCiphertext: "de6c2c6e0f9eb7cb17f1942bfc9f4fd6ebb6b4cdd4da2bca26fac4578e9f543405acc7d86ff59158bd0cba3aef6f4a8472d144d99f8b8d1dedaa9077d4f01d4bb27bbe31d88fbefac3dcd4797563a26b", - zkproof: "1d61fcd9a464ab21ed550fe6fa09695ba0b2f10eea6468cc6e20a66f826e3d14c5006f0563887f5e1289be1b2004caca8d3f34d6e84bf59c1e04619a7c23a996941d889e4622a9b9b1d59d5e319094318cd405ba27b7e2c084762d31453ec4549a4d97729d033460fcf89d6494f2ffd789e98082ea5ce9534b3acd60fe49e37e4f666931677319ed89f85588741b3128901a93bd78e4be0225a9e2692c77c969ed0176bdf9555948cbd5a332d045de6ba6bf4490adfe7444cd467a09075417fc", - }, - }, - vJoinSplits: []joinSplitTestVector{ - { - vpubOld: uint64(0), - vpubNew: uint64(0), - anchor: "062e49f008c51ad4227439c1b4476ccd8e97862dab7be1e8d399c05ef27c6e22", - nullifiers: []string{ - "ee273e15786e394c8f1be31682a30147963ac8da8d41d804258426a3f70289b8", - "ad19d8de13be4eebe3bd4c8a6f55d6e0c373d456851879f5fbc282db9e134806", - }, - commitments: []string{ - "bff71e11bc33ab75dd6ca067fb73a043b646a7cf39cab4928386786d2f24141e", - "e120fdc34d6764eafc66880ee0204f53cc1167ed20b43a52dea3ca7cff8ef35c", - }, - ephemeralKey: "d8e6d7c111a68ef44bcd0c1513ad47ca61c659cc5d325b440f6b9f59aff66879", - randomSeed: "bb6688fd2859362b182f207b3175961f6411a493bffd048e7d0d87d82fe6f990", - vmacs: []string{ - "a2b0a25f5aa0111a6e68f37bf6f3ac2d26b84686e569d58d99c1383597fad811", - "93c4c1b16e6a90e2d507cdfe6fbdaa86163e9cf5de3100fbca7e8da047b090db", - }, - proofGroth16: "9f37952fbfee76af61668190bd52ed490e677b515d014384af07219c7c0ee7fc7bfc79f325644e4df4c0d7db08e9f0bd024943c705abff8994bfa605cfbc7ed746a7d3f7c37d9e8bdc433b7d79e08a12f738a8f0dbddfef2f2657ef3e47d1b0fd11e6a13311fb799c79c641d9da43b33e7ad012e28255398789262275f1175be8462c01491c4d842406d0ec4282c9526174a09878fe8fdde33a29604e5e5e7b2a025d6650b97dbb52befb59b1d30a57433b0a351474444099daa371046613260", - encCiphertexts: []string{ - "cf3354cfcdada663ece824ffd7e44393886a86165ddddf2b4c41773554c86995269408b11e6737a4c447586f69173446d8e48bf84cbc000a807899973eb93c5e819aad669413f8387933ad1584aa35e43f4ecd1e2d0407c0b1b89920ffdfdb9bea51ac95b557af71b89f903f5d9848f14fcbeb1837570f544d6359eb23faf38a0822da36ce426c4a2fbeffeb0a8a2e297a9d19ba15024590e3329d9fa9261f9938a4032dd34606c9cf9f3dd33e576f05cd1dd6811c6298757d77d9e810abdb226afcaa4346a6560f8932b3181fd355d5d391976183f8d99388839632d6354f666d09d3e5629ea19737388613d38a34fd0f6e50ee5a0cc9677177f50028c141378187bd2819403fc534f80076e9380cb4964d3b6b45819d3b8e9caf54f051852d671bf8c1ffde2d1510756418cb4810936aa57e6965d6fb656a760b7f19adf96c173488552193b147ee58858033dac7cd0eb204c06490bbdedf5f7571acb2ebe76acef3f2a01ee987486dfe6c3f0a5e234c127258f97a28fb5d164a8176be946b8097d0e317287f33bf9c16f9a545409ce29b1f4273725fc0df02a04ebae178b3414fb0a82d50deb09fcf4e6ee9d180ff4f56ff3bc1d3601fc2dc90d814c3256f4967d3a8d64c83fea339c51f5a8e5801fbb97835581b602465dee04b5922c2761b54245bec0c9eef2db97d22b2b3556cc969fbb13d06509765a52b3fac54b93f421bf08e18d52ddd52cc1c8ca8adfaccab7e5cc2f4573fbbf8239bb0b8aedbf8dad16282da5c9125dba1c059d0df8abf621078f02d6c4bc86d40845ac1d59710c45f07d585eb48b32fc0167ba256e73ca3b9311c62d1094979", - "57d8dbe10aa3e866b40c0baa2bc492c19ad1e6372d9622bf163fbffeaeee796a3cd9b6fbbfa4d792f34d7fd6e763cd5859dd26833d21d9bc5452bd19515dff9f4995b35bc0c1f876e6ad11f2452dc9ae85aec01fc56f8cbfda75a7727b75ebbd6bbffb43b63a3b1b671e40feb0db002974a3c3b1a788567231bf6399ff89236981149d423802d2341a3bedb9ddcbac1fe7b6435e1479c72e7089d029e7fbbaf3cf37e9b9a6b776791e4c5e6fda57e8d5f14c8c35a2d270846b9dbe005cda16af4408f3ab06a916eeeb9c9594b70424a4c1d171295b6763b22f47f80b53ccbb904bd68fd65fbd3fbdea1035e98c21a7dbc91a9b5bc7690f05ec317c97f8764eb48e911d428ec8d861b708e8298acb62155145155ae95f0a1d1501034753146e22d05f586d7f6b4fe12dad9a17f5db70b1db96b8d9a83edadc966c8a5466b61fc998c31f1070d9a5c9a6d268d304fe6b8fd3b4010348611abdcbd49fe4f85b623c7828c71382e1034ea67bc8ae97404b0c50b2a04f559e49950afcb0ef462a2ae024b0f0224dfd73684b88c7fbe92d02b68f759c4752663cd7b97a14943649305521326bde085630864629291bae25ff8822a14c4b666a9259ad0dc42a8290ac7bc7f53a16f379f758e5de750f04fd7cad47701c8597f97888bea6fa0bf2999956fbfd0ee68ec36e4688809ae231eb8bc4369f5fe1573f57e099d9c09901bf39caac48dc11956a8ae905ead86954547c448ae43d315e669c4242da565938f417bf43ce7b2b30b1cd4018388e1a910f0fc41fb0877a5925e466819d375b0a912d4fe843b76ef6f223f0f7c894f38f7ab780dfd75f669c8c06cffa", - }, - }, - { - vpubOld: uint64(0), - vpubNew: uint64(0), - anchor: "43eb47565a50e3b1fa45ad61ce9a1c4727b7aaa53562f523e73952bbf33d8a41", - nullifiers: []string{ - "04078ade3eaaa49699a69fdf1c5ac7732146ee5e1d6b6ca9b9180f964cc9d087", - "8ae1373524d7d510e58227df6de9d30d271867640177b0f1856e28d5c8afb095", - }, - commitments: []string{ - "ef6184fed651589022eeaea4c0ce1fa6f085092b04979489172b3ef8194a798d", - "f5724d6b05f1ae000013a08d612bca8a8c31443c10346dbf61de8475c0bbec51", - }, - ephemeralKey: "04b47556af3d514458e2321d146071789d2335934a680614e83562f82dfd405b", - randomSeed: "54a45eb32c165448d4d5d61ca2859585369f53f1a137e9e82b67b8fdaf01bda5", - vmacs: []string{ - "4a317311896ae10280a032440c420a421e944d1e952b70d5826cd3b08b7db963", - "0fe4fd5f22125de840fcc40b98038af11d55be25432597b4b65b9ec1c7a8bbfd", - }, - proofGroth16: "052cbf7e1c1785314934b262d5853754f1f17771cfb7503072655753fa3f54ecc587e9f83b581916092df26e63e18994cb0db91a0bbdc7b6119b32222adf5e61d8d8ae89dae4954b54813bb33f08d562ba513fee1b09c0fcd516055419474dd7fda038a89c84ea7b9468287f0eb0c10c4b132520194d3d8d5351fc10d09c15c8cc101aa1663bbf17b84111f38bb439f07353bdea3596d15e713e1e2e7d3f1c383135b47fa7f81f46df7a902a404699ec912f5656c35b85763e4de583aecaa1df", - encCiphertexts: []string{ - "d5d2677d9c8ffee877f63f40a5ca0d67f6e554124739f805af876aeede53aa8b0f8e5604a73c30cbd09dad963d6f8a5dcc40def40797342113ba206fae8ebe4f3bc3caf69259e462eff9ba8b3f4bfaa1300c26925a8729cd32915bfc966086f0d5560bbe32a598c22adfb48cef72ba5d4287c0cefbacfd8ce195b4963c34a94bba7a175dae4bbe3ef4863d53708915090f47a068e227433f9e49d3aa09e356d8d66d0c0121e91a3c4aa3f27fa1b63396e2b41db908fdab8b18cc7304e94e970568f9421c0dbbbaf84598d972b0534f48a5e52670436aaa776ed2482ad703430201e53443c36dcfd34a0cb6637876105e79bf3bd58ec148cb64970e3223a91f71dfcfd5a04b667fbaf3d4b3b908b9828820dfecdd753750b5f9d2216e56c615272f854464c0ca4b1e85aedd038292c4e1a57744ebba010b9ebfbb011bd6f0b78805025d27f3c17746bae116c15d9f471f0f6288a150647b2afe9df7cccf01f5cde5f04680bbfed87f6cf429fb27ad6babe791766611cf5bc20e48bef119259b9b8a0e39c3df28cb9582ea338601cdc481b32fb82adeebb3dade25d1a3df20c37e712506b5d996c49a9f0f30ddcb91fe9004e1e83294a6c9203d94e8dc2cbb449de4155032604e47997016b304fd437d8235045e255a19b743a0a9f2e336b44cae307bb3987bd3e4e777fbb34c0ab8cc3d67466c0a88dd4ccad18a07a8d1068df5b629e5718d0f6df5c957cf71bb00a5178f175caca944e635c5159f738e2402a2d21aa081e10e456afb00b9f62416c8b9c0f7228f510729e0be3f305313d77f7379dc2af24869c6c74ee4471498861d192f0ff0f508285dab6b", - "6a36ccf7d12256cc76b95503720ac672d08268d2cf7773b6ba2a5f664847bf707f2fc10c98f2f006ec22ccb5a8c8b7c40c7c2d49a6639b9f2ce33c25c04bc461e744dfa536b00d94baddf4f4d14044c695a33881477df124f0fcf206a9fb2e65e304cdbf0c4d2390170c130ab849c2f22b5cdd3921640c8cf1976ae1010b0dfd9cb2543e45f99749cc4d61f2e8aabfe98bd905fa39951b33ea769c45ab9531c57209862ad12fd76ba4807e65417b6cd12fa8ec916f013ebb8706a96effeda06c4be24b04846392e9d1e6930eae01fa21fbd700583fb598b92c8f4eb8a61aa6235db60f2841cf3a1c6ab54c67066844711d091eb931a1bd6281aedf2a0e8fab18817202a9be06402ed9cc720c16bfe881e4df4255e87afb7fc62f38116bbe03cd8a3cb11a27d568414782f47b1a44c97c680467694bc9709d32916c97e8006cbb07ba0e4180a3738038c374c4cce8f32959afb25f303f5815c4533124acf9d18940e77522ac5dc4b9570aae8f47b7f57fd8767bea1a24ae7bed65b4afdc8f1278c30e2db98fd172730ac6bbed4f1127cd32b04a95b205526cfcb4c4e1cc955175b3e8de1f5d81b18669692350aaa1a1d797617582e54d7a5b57a683b32fb1098062dad7b0c2eb518f6862e83db25e3dbaf7aed504de932acb99d735992ce62bae9ef893ff6acc0ffcf8e3483e146b9d49dd8c7835f43a37dca0787e3ec9f6605223d5ba7ae0ab9025b73bc03f7fac36c009a56d4d95d1e81d3b3ebca7e54cc1a12d127b57c8138976e791013b015f06a624f521b6ee04ec980893c7e5e01a336203594094f82833d7445fe2d09130f63511da54832de9136b39", - }, - }, - }, - joinSplitPubKey: "f4599f5aa5dfbb45da60cdceab7eefde89be63f3f7c0d2324847cce1405def7c", - joinSplitSig: "469b0e272494e5df54f568656cb9c8818d92b72b8bc34db7bb3112487e746eefe4e808bbb287d99bf07d00dabededc5e5f074ffeae0cba7da3a516c173be1c51", - bindingSig: "3323e119f635e8209a074b216b7023fadc2d25949c90037e71e3e550726d210a2c688342e52440635e9cc14afe10102621a9c9accb782e9e4a5fa87f0a956f5b", - }, - // Test vector 2 - { - txid: "6732cf8d67aac5b82a2a0f0217a7d4aa245b2adb0b97fd2d923dfc674415e221", - header: "04000080", - nVersionGroupID: "85202f89", - vin: [][]string{ - {"56e551406a7ee8355656a21e43e38ce129fdadb759eddfa08f00fc8e567cef93", "c6792d01", "0763656300ac63ac", "8df04245"}, - {"1a33590d3e8cf49b2627218f0c292fa66ada945fa55bb23548e33a83a562957a", "3149a993", "086a5352516a65006a", "78d97ce4"}, - }, - vout: [][]string{ - {"e91cb65a63b70100", "09516a6a656aac636565"}, - {"5cc7c9aae5bd0300", "02636a"}, - }, - nLockTime: "675cb83e", - nExpiryHeight: "43e29c17", - valueBalance: "44b8b5b99ce30500", - spends: []spendTestVector{ - { - cv: "b0f5b874a6ecabe6c56ee58b67d02f5d47db8cc3458435d5088d69b2240c28f3", - anchor: "71c012c415d2382a6eebc8b3db07ea1cbf28288daaa91538de4552eeeef72c24", - nullifier: "c85d83db20efad48be8996fb1bff591efff360fe1199056c56e5feec61a7b8b9", - rk: "f699d6012c2849232f329fef95c7af370098ffe4918e0ca1df47f275867b739e", - zkproof: "0a514d3209325e217045927b479c1ce2e5d54f25488cad1513e3f44a21266cfd841633327dee6cf810fbf7393e317d9e53d1be1d5ae7839b66b943b9ed18f2c530e975422332c3439cce49a29f2a336a4851263c5e9bd13d731109e844b7f8c392a5c1dcaa2ae5f50ff63fab9765e016702c35a67cd7364d3fab552fb349e35c15c50250453fd18f7b855992632e2c76c0fbf1ef963ea80e3223de3277bc559251725829ec03f213ba8955cab2822ff21a9b0a4904d668fcd77224bde3dd01f6", - spendAuthSig: "ffc4828f6b64230b35c6a049873494276ea1d7ed5e92cb4f90ba83a9e49601b194042f2900d99d312d7b70508cf176066d154dbe96ef9d4367e4c840e4a17b5e", - }, - { - - cv: "26bca7fdd7cc43201c56f468fadc42cff0d81a966417ad8f097ebf3b25879e55", - anchor: "c23e34da91c816d8d1790dfe34bdce040db1727af24d59ef78d3f4aac2b59822", - nullifier: "d6f12f24fd364496b3be0871ca3dd9625348a614b59bde45885649bae36de34d", - rk: "ef8fcec85343475d976ae1e9b27829ce2ac5efd0b399a8b448be6504294ee6b3", - zkproof: "c1c6a5342d7c01ae9d8ad3070c2b1a91573af5e0c5e4cbbf4acdc6b54c9272200d9970250c17c1036f06085c41858ed3a0c48150bc697e4a695fef335f7ad07e1a46dc767ff822db70e6669080b9816b2232c81a4c66cc586abfe1eaa8ca6cf41fc3c3e6c7b886fb6dac9f4822b4fc6fff9d0513d61a21c80a377671d135a668a0ae2bb934c82c4142da69d12ca7de9a7df706400ec79878d868e17e8f71ea31495af819a016cc419e07c501aa8309b2e6c85b79b2763733a37bbc0420d42537", - spendAuthSig: "b871b4294a65d3e055ff718dd9dc8c75e7e5b2efe442637371b7c48f6ee99e3ea38a4b0f2f67fc2b908cda657eae754e037e262e9a9f9bd7ec4267ed8e96930e", - }, - { - cv: "eb89a85980f97d7faaed78d8f38beb624b774c73a46ced614be219b3d94873b6", - anchor: "0df7fc90b579abf62037975edd6aacc442190a0ba55b15f81f86bade794ace2a", - nullifier: "9d9a816baf728a955b960b7701fa626687dc3c9cba646337b53e29816e9482dd", - rk: "f5578a8768aae477fce410ac2d5de6095861c111d7feb3e6bb4fbb5a54955495", - zkproof: "972798350a253f05f66c2ecfcbc0ed43f5ec2e6d8dba15a51254d97b1821107c07dd9a16ef8406f943e282b95d4b362530c913d6ba421df6027de5af1e4745d5868106954be6c1962780a2941072e95131b1679df0637625042c37d48ffb152e5ebc185c8a2b7d4385f1c95af937df78dfd8757fab434968b0b57c66574468f160b447ac8221e5060676a842a1c6b7172dd3340f764070ab1fe091c5c74c95a5dc043390723a4c127da14cdde1dc2675a62340b3e6afd0522a31de26e7d1ec3a", - spendAuthSig: "9c8a091ffdc75b7ecfdc7c12995a5e37ce3488bd29f8629d68f696492448dd526697476dc061346ebe3f677217ff9c60efce943af28dfd3f9e59692598a6047c", - }, - }, - outputs: nil, - vJoinSplits: nil, - joinSplitPubKey: "", - joinSplitSig: "", - bindingSig: "c01400f1ab5730eac0ae8d5843d5051c376240172af218d7a1ecfe65b4f75100638983c14de4974755dade8018c9b8f4543fb095961513e67c61dbc59c607f9b", - }, -} - -func TestSaplingTransactionParser(t *testing.T) { - testData, err := os.Open("../testdata/zip243_raw_tx") - if err != nil { - t.Fatal(err) - } - defer testData.Close() - - // Parse the raw transactions file - rawTxData := [][]byte{} - scan := bufio.NewScanner(testData) - for scan.Scan() { - dataLine := scan.Text() - // Skip the comments - if strings.HasPrefix(dataLine, "#") { - continue - } - - txData, err := hex.DecodeString(dataLine) - if err != nil { - t.Fatal(err) - } - rawTxData = append(rawTxData, txData) - } - - for i, tt := range zip243tests { - tx := NewTransaction() - - rest, err := tx.ParseFromSlice(rawTxData[i]) - if err != nil { - t.Errorf("Test %d: %v", i, err) - continue - } - - if len(rest) != 0 { - t.Errorf("Test %d: did not consume entire buffer", i) - continue - } - - // If the transaction is shorter than it should be, parsing - // should fail gracefully - for j := 0; j < len(rawTxData[i]); j++ { - _, err := tx.ParseFromSlice(rawTxData[i][0:j]) - if err == nil { - t.Errorf("Test %d: Parsing transaction unexpected succeeded", i) - break - } - if len(rest) > 0 { - t.Errorf("Test %d: Parsing transaction unexpected rest", i) - break - } - } - - // Transaction metadata - if !subTestCommonBlockMeta(&tt, tx, t, i) { - continue - } - - // Transparent inputs and outputs - if !subTestTransparentInputs(tt.vin, tx.transparentInputs, t, i) { - continue - } - if !subTestTransparentOutputs(tt.vout, tx.transparentOutputs, t, i) { - continue - } - - // JoinSplits - if !subTestJoinSplits(tt.vJoinSplits, tx.joinSplits, t, i) { - continue - } - - testJSPubKey, _ := hex.DecodeString(tt.joinSplitPubKey) - if !bytes.Equal(testJSPubKey, tx.joinSplitPubKey) { - t.Errorf("Test %d: jsPubKey mismatch %x %x", i, testJSPubKey, tx.joinSplitPubKey) - continue - } - - testJSSig, _ := hex.DecodeString(tt.joinSplitSig) - if !bytes.Equal(testJSSig, tx.joinSplitSig) { - t.Errorf("Test %d: jsSig mismatch %x %x", i, testJSSig, tx.joinSplitSig) - continue - } - - // Begin Sapling-specific tests - testValueBalanceBytes, _ := hex.DecodeString(tt.valueBalance) - testValueBalance := int64(binary.LittleEndian.Uint64(testValueBalanceBytes)) - if testValueBalance != tx.valueBalance { - t.Errorf("Test %d: valueBalance mismatch %d %d", i, testValueBalance, tx.valueBalance) - continue - } - - if !subTestShieldedSpends(tt.spends, tx.shieldedSpends, t, i) { - continue - } - - if !subTestShieldedOutputs(tt.outputs, tx.shieldedOutputs, t, i) { - continue - } - - testBinding, _ := hex.DecodeString(tt.bindingSig) - if !bytes.Equal(testBinding, tx.bindingSig) { - t.Errorf("Test %d: bindingSig %x %x", i, testBinding, tx.bindingSig) - continue - } - - if hex.EncodeToString(tx.GetDisplayHash()) != tt.txid { - t.Errorf("Test %d: incorrect txid", i) - } - // test caching - if hex.EncodeToString(tx.GetDisplayHash()) != tt.txid { - t.Errorf("Test %d: incorrect cached txid", i) - } - } -} - -func subTestShieldedSpends(testSpends []spendTestVector, txSpends []*spend, t *testing.T, caseNum int) bool { - if testSpends == nil && txSpends != nil { - t.Errorf("Test %d: non-zero Spends when expected empty vector", caseNum) - return false - } - if len(testSpends) != len(txSpends) { - t.Errorf("Test %d: Spend vector lengths mismatch", caseNum) - return false - } - - success := true - for j, tt := range testSpends { - tx := txSpends[j] - - testCV, _ := hex.DecodeString(tt.cv) - if !bytes.Equal(testCV, tx.cv) { - t.Errorf("Test %d spend %d: cv %x %x", caseNum, j, testCV, tx.cv) - success = false - continue - } - testAnchor, _ := hex.DecodeString(tt.anchor) - if !bytes.Equal(testAnchor, tx.anchor) { - t.Errorf("Test %d spend %d: anchor %x %x", caseNum, j, testAnchor, tx.anchor) - success = false - continue - } - testNullifier, _ := hex.DecodeString(tt.nullifier) - if !bytes.Equal(testNullifier, tx.nullifier) { - t.Errorf("Test %d spend %d: nullifier %x %x", caseNum, j, testNullifier, tx.nullifier) - success = false - continue - } - testrk, _ := hex.DecodeString(tt.rk) - if !bytes.Equal(testrk, tx.rk) { - t.Errorf("Test %d spend %d: rk %x %x", caseNum, j, testrk, tx.rk) - success = false - continue - } - testzkproof, _ := hex.DecodeString(tt.zkproof) - if !bytes.Equal(testzkproof, tx.zkproof) { - t.Errorf("Test %d spend %d: zkproof %x %x", caseNum, j, testzkproof, tx.zkproof) - success = false - continue - } - testspendAuthSig, _ := hex.DecodeString(tt.spendAuthSig) - if !bytes.Equal(testspendAuthSig, tx.spendAuthSig) { - t.Errorf("Test %d spend %d: spendAuthSig %x %x", caseNum, j, testspendAuthSig, tx.spendAuthSig) - success = false - continue - } - } - - return success -} - -func subTestShieldedOutputs(testOutputs []outputTestVector, txOutputs []*output, t *testing.T, caseNum int) bool { - if testOutputs == nil && txOutputs != nil { - t.Errorf("Test %d: non-zero Outputs when expected empty vector", caseNum) - return false - } - if len(testOutputs) != len(txOutputs) { - t.Errorf("Test %d: Output vector lengths mismatch", caseNum) - return false - } - - success := true - for j, tt := range testOutputs { - tx := txOutputs[j] - - testCV, _ := hex.DecodeString(tt.cv) - if !bytes.Equal(testCV, tx.cv) { - t.Errorf("Test %d output %d: cv %x %x", caseNum, j, testCV, tx.cv) - success = false - continue - } - testcmu, _ := hex.DecodeString(tt.cmu) - if !bytes.Equal(testcmu, tx.cmu) { - t.Errorf("Test %d output %d: cmu %x %x", caseNum, j, testcmu, tx.cmu) - success = false - continue - } - testEphemeralKey, _ := hex.DecodeString(tt.ephemeralKey) - if !bytes.Equal(testEphemeralKey, tx.ephemeralKey) { - t.Errorf("Test %d output %d: ephemeralKey %x %x", caseNum, j, testEphemeralKey, tx.ephemeralKey) - success = false - continue - } - testencCiphertext, _ := hex.DecodeString(tt.encCiphertext) - if !bytes.Equal(testencCiphertext, tx.encCiphertext) { - t.Errorf("Test %d output %d: encCiphertext %x %x", caseNum, j, testencCiphertext, tx.encCiphertext) - success = false - continue - } - testzkproof, _ := hex.DecodeString(tt.zkproof) - if !bytes.Equal(testzkproof, tx.zkproof) { - t.Errorf("Test %d output %d: zkproof %x %x", caseNum, j, testzkproof, tx.zkproof) - success = false - continue - } - } - - return success -} diff --git a/testdata/blocks b/testdata/blocks index e3af9b8..4c8f068 100644 --- a/testdata/blocks +++ b/testdata/blocks @@ -1,4 +1,4 @@ -040000008a024cebb99e30ff83d5b9f50cc5303351923da95a8dc7fda3e0160900000000226b1c86e101f1fac09aa533c246bd6843ee6b444ad3ff251df0a401f16f0981000000000000000000000000000000000000000000000000000000000000000069cb7d5b0f1d0a1c0000000000000000004d6cdd939a4900000000000000000000000000310c50b3fd4005008f78a11b4b81126dec31cba6ede2e131ab376a8611237b04d20cd8bb4253f1e8721e0941970e0f58c206f09c8c86c70b2f73b3b5994571c389a90fadf6b812cebba5afe622b3e4aab20a88c805e1f3925e31300df11c09e3d4b7acebbfb156b75e213e36f65d88f023feae5d37ab2f13a1215299155b7d9a1f4cbb333212e8ef9959785adfcd2506d33ac1021207fc32584022080f4a7ba67157ea99f6fc645737a2a5c6f5d99700ff0c1a92f2845dba3b022d24de26691c0b56977b13e94d5f93ebe423d3d422a478f2e6d9f0b113b3fa0434fe432c173013b1a641e884f74922a3e6fc6cde4dedcd8c2458be13fb91f5285ffbf8a5dd207dad3b011b832444a85e27da43c1f94864ad7b066559d5210bf495565005f9297f6204ad7b4c395d5faa19c2530427ad4889555aefd272185f825564525f6d3e0bb62b18cce8ca120a6f636ee36f18612fede045bae81403752f8154e7c3bf4fef7134591379389155360cd15328c731c1ed3b57f439994b0350799e91f91f53e30ce9bed6ea9ce01b3f74f6041b739d85965d789b9c43ba65cf4f99808ef03c2ac46461ba0eedc05e77400815af9d8049027bb7fef6646142d86ef35adcca060e8a656c6c6cc811f65231b4ac531fa2083ddffbab26d19409cc357b89ed7da3d25476cb9d9a9c939c343d23fa35a09c52d593fb03f609dafcc28579fdd35e044168e33e747757bfdf5123080b6799d2527368f90de7f126304610765670214b6e9b6f497a491650db139129da19964461c368e2524aa1524248ed92561b3e94aec38d5ff4adcab5f73565dc7626477b1d56620ad1bb49000d1cd915a260c0913960c493edb9770d2fefae76dae63963e147331a51b1c66d5ff3ecf87f141306d575e60ca3b34fd26cb0b1d735dbbb1977db519a7a9d345ccc77121b688c7470975ee9dbfc489800f25a41d406ccbcbe1f01c629dfcbd59ea5dcd00334cc6af8718e08631c3a83d5e6395b4acc3afab48b145b73a064904176c30c2cd9a876ebab5f333b6ebd17c10e31ce6f009daad792c31fce13dd6d401120eb08d66348c13735ffc667e653ccf80bda54d0773473177433406a0c2001ec9f534c54e667c5a3cff3bca72625b5fee94a51dfc2b5b3fc73353b2b2f3a9e708932003e771dd21441e9cd075765e33ab5fe0db05b4b087bb04608d5fe5bd32f8272752cb1e59f856eab0e366a935a9651be4584f8886649c66bb6bff29ac82e0bf749c94e46cc6a42988196c15d3b2a6376185b990e653cbc8a77e56fd3e74378bcd54f4540afdf39650950fd18192198bd743708fcd948ec57aab07d71211157758ca83509cbc0094158b75bd840810fb266f91565df4f4013aaa4e414b3a56bf8442938189441ae24f8e2417b7bb729f9cf27a2ee5da291fed793afcf09a1c63f14af0dab7aa6b34b1be3d545356653f14a2d31173cf50fa6aed549933a5743cd46809bf6e64824111cb5032130536943f359ffe5cc7ad5b4dcce3f2d078f7ca751fb08f8a5d9ac3970cb9f2cf31ed1a713cec3e8bac8586ce10c74e215c3d691c58e176b39382da1fe4ff0edc7f23373f594ab00c987c772f92317575d4d199e79f5b95fe1986ff0dd4d4a339a797b3b366e737bf8befbf80fe58f0d5b6f35c831d00ebd0c47ddcec914cf879ca685c0724b9036f98abacd96102657148a55e6e7896f33d51daa97190a233a4d68278b51c2bd26dd23916c54e605b6578a8e3657359c36a9ee2e4c0f650a95a3f4b61c4133ddaa901154d20c807a6ab85211bda537f26a4fe4b53a4a64f5522d13eb75cd2bab7669ef47d1c7fd43416182bcde9370105383fa2c0c574d4895d2fea59889068e66f83c8f15118c7ce7adb41fd8de61c2afd7277d6ac31346aa01030000807082c403010000000000000000000000000000000000000000000000000000000000000000ffffffff0503e0ce0500ffffffff0200ca9a3b000000001976a914361683f47d7dfc6a8d17f8f7b9413ff1a27ec62988ac80b2e60e0000000017a91414c884180589f6d0fdc7c400181afb1bb10c9fe487000000000000000000 -0400000015bea7ff6272a28e0dd187b5160d8fae6f2ad6316d652fdd26b92204000000008ede1b5093bbb6ae7c883dbd4b8bfc9e97290ac6ba5a617c880d3aef1bc41d9200000000000000000000000000000000000000000000000000000000000000009bcb7d5ba51e0a1c0000000000000000002c476f1d2c08000000000000000000000000002c27173bfd4005001a40fe9c1795f9617212aa1ade76cd93f85db2e5254a8ffd5aab68218adb36065f6947477a423c26c719803ba9c01555a1d81ef1cbd4ab0e7a7c9c7aeba622c7c1614f16409f5b0f757a93d12923027fbe875e01d4003525150ba313f43148bf39e7836a3d5fae611de2de0eebd8a3d1b163e4ad7b3ad0bbc00a3f4b8d029ee0e23f6354cbc381711eec9df770f31149ac1e0eb55229d10d7ba106edb12834296cfd241f5a62a6014ac0726dc4543f05a88138ad2d7654b707aa799115886e467957d5f195fda7a0a0d7908e084b974a1e0565e222acc9670f4f7191b4052f84595457315647468f532773a0755b0b3c7649aad0ce1e9da9597819022f7c951ddf47978c5985bb3f4350b1b91f2eff7f149ba3992316712d0bb2a3b6fbb3dee54b516d22b21f60c23f87e3513d2ae3447191b55b17f3671f9e3629da0eb3b8261cb7eccc675761f95452435bf6b6f800a4e5a2deeee86b8ae3d026a11eaed463373c378b079a68b51eed25b596fd519a2530f9414684d71dfa427c461ab170137fcd343af2fef3122bb037df784e647b6386823340abffa60668eb7a5729fdda35d42602729b2fa822d11da41c304ce27530bc17e717fdd10a2f221cf793e4db842ee2f6f04b2a74d3945214ec0d1819005f0e42f68c8183e9a0a87011180ed87eb3205e7f01d7a760d95b874ae2d17e915b267cbd0e4e06e99982744e7473f74af2ff766730ee01f7528478116633f7d2eebd87d8e134429e4d29b92144fd0ffb1628add40f98c4df2856632055a3d2c70a2abf69851929b6aa5a9de2af2148ea564a7520f6d95e5724b40c2a3c9d39c995b2f3e131795f2299029c215b07cd14bce9e10fd05296a8a646302ff4c45e85a4bc3bec12fedc1d1f60da8f6aac22f746d43a11bd08be8e52153c60ab71061d35284872c427e3aaff0d8a3a82af016db8d42937f04be1ccb021e7152aa82a068b0a1b04cbe51b904ffc00f3bd46cedc3c58ba378355360e11982c9981c6a5ae7c6d21fa3b2e54b8e01d49a0f618df191ffbd3274b7ac3c5f066623a15c1e07eb64e1db18f37cf4d4452c441c63b8075ca220d0131a2d022da9305700b8e0d236ab4dfd17c2f453c3f17fd722925de3cdfac190bdec92340db74ad46ff693f79c92bad961d021e3a01dcf16445d66713e53bc37b89720c887088d26831e5a4a7011366eb3b6920df10a3316eb41dfbe9ef6f697bfb673be8bce2a64c16baa1ed1cce0a0bc32595bbc22f42ba8d2dbdba3f6097cf7b27cf4717d7187bab684e364a17c69e29c49ff3aab10d8f9d3002149c72d90cf115723ca3aee3c8b8014918896bf63a2746d7a486a496b274b21765dcdeb83b387252b876129b65ce91e613783f55aa1ca85c07a448b4eb36d95681c1851f7532a47e8056eacbd8c45d01b61dda489c744f267111294fef34a98c5391d6ff31491f974fdcbee1cfcd740c714abd9a616878d604076dc376afd53826da95c4b81ad0f991642a6bd3d518feec5a021e20a73be5924147203586c75b3fa9c51111119bd39061fdeedbc1a1a92aeafe834dff67071438f21b247a5713e66bf156ab70f04995ba70f4f0129da42ee3d5ca8da38c35c2b8d33d19e79618a0061ac8fdae6a58250725ab51dbda4a6541d7fe1cd31403ad8d6cfde6badbb85ff162bc1f08b109866f8aaa1688b2de068fa272a3a6371f3876101a37191dc9c308eff5bbe41512b7a749668f2a35f43e2dc67c91ed13e39bc4ce5783b795d1a16ba03886ca053dbd6a4006e0f5091e6955d7abdc154373d10d4d60ecee597c0a8d71dfbb2e3b4fa83554d459c0a68d655c9b28ff0cdd2ace655bdd8dc2322394a34255b1d5373dc39c310244fa0fa1c557d7caa3d029ad36966d0d7606e301030000807082c403010000000000000000000000000000000000000000000000000000000000000000ffffffff0503e1ce0500ffffffff0200ca9a3b000000001976a9146d17c0ca63e020d639f5fba181ca4f5da48b8f7088ac80b2e60e0000000017a91414c884180589f6d0fdc7c400181afb1bb10c9fe487000000000000000000 -040000003723935953489b860d1de1b8fc8240ced04625f0ffd23f16519b8e0700000000f066ce1a8f2b97d05dc15ee51e1eedac93ba6057a953eb81d3f856e9f4b7ae270000000000000000000000000000000000000000000000000000000000000000c8cb7d5b13520a1c00000000020ff5b32c230200000000000000000000000000000000000d5e39cefd4005003eed282b472cfdcb0540b4f4245ed479c5357ff11989ef70be9700eff5987318f727dafa0b467302aa016e5e956c5a5d23f0f9e4a5623b9dda30e0f1b3c47e8c3d151e686cabd4ac89fc627e5cded736d707780c3dc341370bf7af4cb291878a1c4236cc207f39f081ef26b50fe099bdb90c8a9462663d1aaf079fefa010c2a8c29211f08b8debf2f90e4afade29bc53d74253c3fbdf265759fee75e1ab46adc90f38bba5f73e80e518def9b61e8b714d6e6a1dd585539ebd1f5f2040e8e73498455bec4fd333747f96823c66fb859177d13f3ba05868918255bb022cc32dd0a099438fdb1b31b15e2de9acc341066d6b20e4c1d18f56397dfe97919a2eb988e114e3cc015c1c209d906e494c46b9dac34a99ad0595868b4f84a1474ff532202149ef12acc1db1f9307213f49efcb0a408fcc1cc6124e637adb22ee4368da8d0a78b696956e7e4c9feea5d2e198f0e00bdd16ca49956c32ff793e3ff6e635efe57788fbf11a8b2a771d1981aeed6462fdd74a6a9f5d813d86616923f5add19a41d507ba22b69a0caf4a608f8b7673bd605d7f2b022b3d8436448aeb34e491c12addc3911564ecf5de8b1355f1762dc402e37915c35cb69de2a9d575a765737ff92c653eed1eb871270589c2dff15f827ba45b19ab3d25da28fe3176895a8cc32a943391276625d991492ed315ab22a7192fbcc5f5eb01611640c96585ac4db590777a87444c3dadc653bc2bb279474ad76ac00d5972b92e026d45fdd0ed84b099a13d08c00eea952c5a2a78218e06ff989f85b52801d203f82d395310f67e80f4646f3f29c5b0c2f9c3ce31530b766c12f3253a6f3e2aed5b66efd9dcdb6f1de1e601a36b00fa2b7c4d6241e46afc746cfb73f1b2f19c60f81472bd15bdaff21afb0c44514915bfbdb5a20172209c4c9c281ba14950b07b22d097bc0cce5fa017ef344aec83bca60db20f4241ab3e1d29f7f200a24b801aa1e2c638d77ac13c0732da3561e3e1bcc6f2ac78fcf21e7f113b56d83c0343a1cb6d8b69eb12975891c1bb27885f1efc8c97a0af71176f716fe36b61262e23b48e7266f6551821948e4c44d48153df60a1a921faa5766e4a99239c2f36231bdadc93e749db715ff7e8ced9ef6cb28e7a5a8aa7603de56e73ea5fd1d79be8974a4c071ed1b969382da51d68b11f4722303d74f82849cb05d07c0c313d9a0d5eccbb853f85f0c7002c67eeb93737060180a3f557582d3849b73bf13807ff3f4065faacd7562487fc96a20fcf2cc892e16ad74b813caed4f72f11293243912c976a9f2035b099b9b9debc968aec54031bb9f4eb7815ddb3bffc128d6be34814fbec58b17c6dfab6c812a5eb3fd15d60fe3fa55128466cb80bae5579b71025e6a9c34b7f818eb11cdb029eff94fa465e419b9aef2e8679f12ce02b780aba2c17313bb375033beda50e90be5fd949002fbb85634d42598b62d21e346d13c99e4b6364354136f828f972a7b2fd8c4464defb82629ffdddea6355afcf76a5b6a1f797b2a46b51cee7f95f812d926d00436f98711c6f6a1e374a5f8a6f73f39b70150ae7315988b1a2c88174e9bad58c2e0f82476bb1c9ebd03079970441810e5c7c1b8d3a5b8eb21750f29eb92f348d5ccd2dd13fc68b063c754564f490e33ba1bc71b111eb14500d1dfcd883be26a8952aed912f4a999831b80a94d1f994c69558334e165513fbe129a9348922d6205d0bdb3fed3d0fec41a8f7dac2a46f2f9bdd837d6fa791bd25ea3bedf454b7fcf8907000a78d4e911bc798c6d261817400572cea0a26e9e2f5b16cd7129e92738be52b4832c2a072e2c7db3c2b889fa089b1284a3ae2ec654da817e15e0b26856be1caa1c7c0b3446158365a0992bc01fc67c19d5444ad8c6be044902030000807082c403010000000000000000000000000000000000000000000000000000000000000000ffffffff4903e2ce050004c8cb7d5b598c2ba5c7f0b4587e7d60e62e08985aee803a179dda187d2f0c5284df7da7482f4f76657277696e7465722f5361706c696e672f706f6f6c696e2e636f6d2fffffffff02d0eb9a3b000000001976a914de2757e612dee78a681ace9b8e623570dce776f588ac80b2e60e0000000017a91414c884180589f6d0fdc7c400181afb1bb10c9fe487000000000000000000030000807082c403012263eb14eca399ec05b64be85325e2c5ab93e5d70d7dc49b4e8b8dcb52ff0f7b680200006a47304402207ade36a404000f4d5cebed5c28e28c57a539b7985b66aa4c014e0c59a43fde3a02201bd61f0a998301241d83fc9370654e0acda7cc6602258b954af6bf06605dffaf0121026e88bf2ad6aa4fdc21b2636583184a589fcddb3c1ed40b4e3613cf61601fad54feffffff01bc131600000000001976a9142f592f81575c361e91397dccc60772b9335f432588accfce0500f4ce050000 -04000000c848f5a041f4406004243ecf645ef91f303f6766f5615e4cd2c7ab08000000002c46ffced042e210ec98759ee1bf5792b62c3a3c56f24667422350dcf3c84e070000000000000000000000000000000000000000000000000000000000000000d4cb7d5b7beb091cf18a4fa59e6d1400000000000000000000000000000000000000000038d0a613fd400500572cbab31dea47dc52529819b5b80cce7fa6ff4f0c1e8f09d50bee7b5012813826948828c3d5e6f14505a4f7ab54884cb7b8f1a49675b99f820d219567d72695fc14f74a26554ad3c346123852669bd29e50eb056cda5e176bd065ccbbf2663f3a12a11f7773a0847c09059fddb09a37908e1a8240f29dd6eaec78f92b1f67e2a1cd27c2b1d6f9c5c7d0c8545dedb6f949c93507838d4525606b73d176ed8ddfa2a20d93ba4d4303512f8fa4c96efcc084329bad15f9997edef03fc90edcd1e7f943c95b5c7123d66a4d0ec9012ab2578e1d9bbcb47bdf56e9eb72e3c82dcfb14ed9785c860522791bb3bde200f76db5e907555c27ef91489cc4410b4129e4aa08b800c00952b451cf3640c326b690e719d92cd2a87799d1dd63b36984c30a6582bdb1fc6d16b323c2ef33b23deaf2325763f8b1fe7b21dd7c801ffc7df2c4d68511aff3b2059e681bb1afaa19090d03592df27c452350d040718758902afcaee5fa48a00e9fffbd3ec4222973c2686fb3e7beee6c5ebe0ecc130b8981d00adfcd464832b625d7cd82adb99e02f71c4b3d9ec6ce5812cb5075cd70cffd967e73b9f08c04c97c728e02efa88b18f318423ce212fca69c05b743289eb18912bdbcde1bc5fb507286baccb1b8937438092c5418a86fe56bdf441637ac4a49e172f491f743a694bc0516e02ebb1e36ec3b4a460e5f45bf6d2c055924c396c9546f9839b696785fdc09f99ddc7e701a3f0f61b7cb90d6de292400b8423f45bdb6d11d921711cc0a5316d200c220f70e80cda935f6b911e34d1aed9769d40ab973a6aaf1bbbc33ecf57f60d93a490d5835583ca4b70de9218510784caaf2d28c1b3dfe1af1e47ea3c96ddd46e27343ea5b80d175c3ef69b5387d2bfae8d026c765d2f5ec7ec7dacdca63dfbae85974e5a57e636edd68754664b56b9421a009ed09d7007b72db75980945b9f8e0da56d03cdcd8cdb9ce8a6d21336a8ab946fdeb59472585584a85e59e31152f0a9e03da924877854e93d5f6fbcff21a4165bbc4b60c8a195bd46761499fe1a28eb464c0957203d337cd0781d1e385308017999260c8da0933ee3b751d3ce92015b3e67393dc98d883d70113d24f72c4b73a885f1cea47062e88aba3983f192c69dc10db68b95fb3e021363ba87ad938bf97077263a7aeca4f769dfd54370194582d31d7c632c60833fa9346814b26ce1d7c451d6fbd6c309de603780185f499b963a7310abc42450445a2f2840f25350d38f2140b3904fd636ff5016334e2c234d72eff0d8096588c71ffdcde3569b3914f02acbe1938957135acc6d10f2d5bf56885c177e6ca778f7ce431225fd535a15949fdfd2e8ab0c47fd8251e294c02cf98d91340afb5812950c46d9b8dadba902fc70ea73c2c26b5a4d0d59410da46018486d9ec5a0a9b89422190afb7e72b7150f97b359d70841170ae23bacf915f10a019c601f48017472dd2b7c7b89bf0128dfd4a01e450adf17071ded02a3ae13eb7f8fec54510955a305256b2be2d97994140f36222d5ba55ec0ea35749fc894289af4454e944c1dbb1d7d40f1e72276651c4e3542bfd9f145500d9295eab83c6fc92362875620cd0159b7b53ff530d66b25adde2187438045c5fc4b8bdaff93dfb65b85c71753846bfa91f6ac60c9db561423b6109feef932486ece2cdc3c539112318c79ecc7e08ddda73ce663c3d4e9b9a7c743955330f1ca702b8a5673f82328b5f637dfa3743bcbe07583bfecf731cab93abc8d4a5dbfcdbf3536db25f1fff0d71175b744d561eecd1a89fe0484d466d2c5a68d61abba2dd9e8786211482b2b50218eec505f94c67971242a67a93872681ac81e1f541ecbf95a8bd7f5d8c1e60d95a76138bab40da3455b5a36549920d7f4d0102030000807082c403010000000000000000000000000000000000000000000000000000000000000000ffffffff1a03e3ce05152f5669614254432f48656c6c6f20776f726c64212fffffffff0288f19a3b000000001976a914fb8a6a4c11cb216ce21f9f371dfc9271a469bd6d88ac80b2e60e0000000017a91414c884180589f6d0fdc7c400181afb1bb10c9fe487000000000000000000030000807082c403016f4029430669c709d041c579c8e9469446f1159064613e6cd47514691b93b8ff000000006a47304402204ff60fca23a36026d374850bc018e7c714de78d8560d4188fa247b1a4ee4dcee02206538b475265173915662465b4a8554a111003701371d553134047bf185c98ba20121029ba2dc5033c7c17f06274b85c0f8e51ad518f34235713ffee4705867162819fafeffffff0208672302000000001976a9140ed9a32475fa7214939a6705fd790a74976bab0388ac3e926c01000000001976a914d752bedc09529fb3719004407dd6b5f29000e3e688acd7ce0500f6ce050000 +0400000006785624481f381e68b4506ba5d6cc53ddd6dc876cbc97e45e3c7c4a626a19008c450aa9112b2e900eba30c2e3b8428c23fb2a30bb7fa34a853c97f02fb1a2200d86a7943df2dc92fb8b131b0698db24dd01a12d19696c20443f5f639df3ab5aecdc295c172c191f870000790c975f8c9f0a77f12ef1e021c6045c3c9acb7c432c2aa7ed42ad0000fd4005005db35c547608bbf32bd1b90369e6e0bc1e275a0d064f092b235835cb6dfc9158c6146a0e5cfbdf2d2f04f71478f7d9212bdeeb938029a5cc217ebf1f517711dbc7cb35d084328b8f7613e878f69e27b2b40e8103292cb87845006943b761bbbe43d94cfbf971011617965a7de1e846a586ad81f2ad138eb0fd74729efc04720badc95a1f1383cc70e890db91c143591ab96526263ec6b8a6186568dda29b9015078d19e87cbb2c00b26edee3e4b227ad6fa090db2d290191ef1043e2037fc0bd225e6a1364d16223b0234311e8c5ff455a0cc2e0c1f84944dfab49864f57e5ec2a2b5e91df5229449ee399b4c721abab63c534602abdf64d37f60905bea71cd59be759bae5967f6f3e1bf732753d4d9c1c11dcb5b6949bc4f63a06a3b0db5c2deebab040c70fc55fc648bb2559fe2da2b4a4bd53ccbca7aaf03262cdf31c5bab90cdc151b686d3e5f8a29103b9346602b5394b245dc9655116d33c2ed9d31563bb74950d0927895a024c119d30d4f2c760c346b11108ab29db2931123aa36937cbd152756758e3d341ec40db1d1b539fcbefa2e76af9827f575025c58569fbec1f806607e03a6eabc228a900a18144e310ab608a930ee6a10d5e6fc7ecc9934fb42b51f0a6552e2a1ba1d627b51df2d27889e14b87f093452b79bce3e2a74398f4b748eda48495b30437bef13527a231ce724f24d5bcf4040cc77f56dab50ff30ce12039ca968eef3c5ab44b2cdd8f7fecb36ed39bc624807a4da49d5b2f53abeb1a027676fb500fb89d7092eef849f254dfd3a7325e4b810687b8567d7db8e796480836bbeab2efff44bb0421f4e3d5d1493bedc2c331f75b7ba931e93495dc379fa22404a2e60919fb2644ee4e5c3bc2025fb17540dce5dede2fdc4fc6b87494ae26ae89bbe954ee8458740601a665126129c9962052683661efcfffce8303211fe3bf0adac14936927ce2f4b30a22227c39b4128616dfbfcaeae8f4a3b30255d215d1b9ccd9bff2173b5a94ffa5473bc49097030e782d81c1dc4f8c4a25803c6dd9a9e775646084ed2bbbf7457386ece058032128bc87c7b7772be513f79176241de701580b5c04f88ee81e231583528b514f0c478382e60d5741bd0b5582c2ece2767931f538d050498886814c5b27541764bda9734d5c46f2b3c80f2649d40374e45c053c050e33e83ed578f6d5c19435b7f495261e7e522d5225eaead374497fed7c30e322d6acbf512751b58417120bf8d97fcfdaa5e3be842fc7beab85235ff7e9ee459b03ef2ae01ceff42e0621ddce403e65521505ed0b25fca1a0747263e44c72c5e1e95d85119a7956852c629fcb903b9589e768a6bcbec5358a5332368a6913ffb9b9880e8d3701c6a366bec102bab8c6d9e57b2287ede4774d69fe81af83dbd423c905f666170bfc03f360314edf3098fe2036634ad90f326a71faa5b70f245acbdf8f7d4481283819d8dae7823ac31bafef129b94822a4d94b8ea5773b7477269be9dbe3bc8c11886a2d2a8176658fe30e1ba1bdcf320873d1d2325096223af6e142818a2c370b8447db8e6e865fef2074596bc9e679b8c37c51e99c4f35ac8431303fac507193d1d162d309a3d8d6e57cf4cd429b34ce69f69ef2f1e728d7b9347051b46d4ba5a7e7f9d43963e960d0bbaf63eeb69396dc75b325f08e9243db0c678aa192eb3db32d9cf65a0a38bd5726e31380f2de23ea9a019d8496ab511df2d955978fcee6e331aeea5df0a1826639323180b87bbbf6a039e34f482a5e4eabc6d9a139c40e2840b40eb53dea3d549b54405ba7e1b392f308df2abfd6e97a3eb44f540b272a6d97c085be2142445ea27349bc7a5493397ceee4c8016a38457480f4de263cd2c2416296a2d86c19ded93cf431bfdd1e3010400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e0ce050103ffffffff0200ca9a3b000000001976a914f4fbba801ad64c6539e64a1478392c57c9a5e7a688ac80b2e60e0000000017a91466a04fe5f6f1b5bb1149e6cd0079167f570880148700000000000000000000000000000000000000 +04000000913b07faeb835a29bd3a1727876fe1c65aaeb10c7cde36ccd038b2b3445e0a001e1ecc75a33e350c7763ccf055402a3082f28209c643810a2ef17df4b14486cb0d86a7943df2dc92fb8b131b0698db24dd01a12d19696c20443f5f639df3ab5a15dd295c21c8181f15000813d02ae5e889354eb9b7b62cca4f139ca6364fb63646ff32f2b4700000fd400500356a828c88fb46eaaa21d759e99109375d79b61557348f830c71fd4dabb126ae794d403af814bfd3fc01b76c2fc110525507f6728bec686ea9a432eda0ef1c8fc97d912923a9ab0d06f8aeef2ba272a3b7125e0e5079f131e4706d59c4f34a2775cfed3fca2cba4a23795351a38abfacf67ae7772e6fd14326efde46192105f2c3b0989a15aedfd22063529005723c8c8ea756ea876e0ae7846d4556e57fdb4b7ae1d2125456130653604eff1a6e632e20350a88b0fece49fa57079818d28c0064764979ed2e75766767a7ddce8578a23712274fac056571619b9ef4371f6924068fd5dcf25112da1ffe4255fe60b851e50b704eef0577967a867a0fbd7ed06f0d93f0f0ae31830ab5eaee5b20757e96233c21f96d6eb8f5b5ff03e88d7a31ced8037a52d94ce82fb719764841c06016ba82e29acb9d521cf9d74e1a1a90175c19ff365b763db1d3fbb6332499a5f90903d26af753812bca3eb2b142992bbd87140c3a0c29732d648c650e6bdfda63a56c6184570bf2bb4d700ba30d7800cecac92f67647bbb282d85228fedbdae1e1e3d56e9cb7743a58f02df45f658cb3b627f6bb311618d1697a217ede3f763d8fbf1fba96f697a62b321d397d492594b477029b23565f8ee80e3df1a74d81be2eaf203e3c9215c72c5fa6ec5f0d20cff3f151d7131ff6a3021f489f87819245d6af34f12dc5d1bd70d2a692be0da4827ed3f81a4b63ee58223bcb53474471a4bfcab99ca08d7dfc6c1cefda57f836d1e58731b4eccbea60c0606dc42945b26b17a3599be385b0b64519f6160adb65fd1da795cf27ea25e9884783834130be24ec10d549eb21ca32188f4221d3ee358f2894099930d165ca4d78985e6c13ceb9ea340ad5c116118c6a2d7851781e554a0e1b968540151e3715513f5476ff6618a5de0498e4f5632a6d5ffea8579ddd0be010b10204499c82540d521397b9a39a079acccaaae32add4d7991db22dcfcb04f86730cbef5b8abb0fa31137abab148457325eb161fd0ebe033094c324f6da261fc31476619753fe95c63a326fd14dbe6e7f83c501b77d4ce5602877b5164973dd55c04f7a891e6f3b423ee425b992d73bdf7e2735e4f52cbedceed9afde086f923208dc1abf1ddce86f9e7c467a8b0b79740510900bef0351faffdda005bd35e5a819b96c1622ca06399cc62f01d08d64c4236970e6c119380df5c48d11d57a8908e76b6d8ac3517f459ec83af139986abf135ea4be1886054eeb0087996aff4ace796b765175166f46af49d6ba10f8b5a455f7bc04ad82a53ac8d71529a6d0dd138642b68a518b61be13761b33bf60133cf97c9c2a0fab6cbfaf3601c0642b6d918b142caf1da2da75d52523739a62984f34f3016ed17cfe03522e71e332079920eb33c0b41de3c3f41e92c0382f7804195e251c0db4d2b6db83566cb4a767a9a85d4d674b226732bbe298ab5df3d5fe2239b82dca3fef254d1a7e3fa9c3545a23eb49136fcd21308280715c7d26601eb40fc49e025b33a6f676b16ae215bce5d6281e0bee2d779549ca7fe32811335e404c10e142ec50c5442afbb03ce0f179848765998ef1a3b60062fd187733919a34999d8bc5724d354421e699e25c39d416c74eb3e2c79229656b54af9a3cb0e71a52e8a9fefd08043f69ad8a8e536b0728e2a039ee80c999d4bd86340c97dea3cb2be33ff333e11dddf0ddd482d99844df08f7bc2ac351987b71e802aec4266b372f2a1e86780b89be04b444801e52c2a4cbf0cc2049e2b3d2c6180e7a28ba0e459de1ddbd016e0c3538f137dc98e2ee1729d5d5b0879073eb421183da7b55866ba8b79c911792a4a6b80b9ed15dfc4249d4c88cf229bc16491f5b8f1afcf272622bf782396aa55fe527a5a67d6327010400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e1ce050103ffffffff0200ca9a3b000000001976a91403e105ea5dbb243cddada1ec31cebd92266cd22588ac80b2e60e0000000017a91466a04fe5f6f1b5bb1149e6cd0079167f570880148700000000000000000000000000000000000000 +040000003893091e180992ed3ddbdca17b69bb32636fa24e139463cc3fcc390f72f00100152337b21d88dbabe7993aeda34e575edc0a10d8425ebf675ebc4a83b3ebc20c0d86a7943df2dc92fb8b131b0698db24dd01a12d19696c20443f5f639df3ab5a39dd295c4699181f1e007d867f82f93d39b4107a1265900058b7565dd1b2945241a4ef63bc690000fd4005004b556303cc38ed6fccb33b93a81c0f8f05dd0e8006d64e34868ac9fe5c22863eff3928f6f2831b502400bed03b98c17215ca8a50612273461d43f857d192154f4ad5db69f067aa48d19503523a2f0602fbccbc01bceeeeb256d5690efa33a583efbf397481afaa5745976f397bd7c0cd270674f2de48ded99778fb0da11068c770ac303637c1ecb1b53e6f1be4f89130a7b45a49edcf081959f37121190d42d668b6bd5c1d353609479c1a598c2c43dd0661c9d51222bda25437f3fd1e952aea271a02c0f19a92f4a11faa20ccef67046f1a4036f38b299e73692e61aa67d526f1b7251da0914ac33d1cddd7a4bfeefada21c8f38ceedc1557d0710bbba0f4932c839177bac34abcf402e168303ed8021dc449b3ca0940448bff3217c0bc2730b19190c6811c420629a1878f6e7021171e9359002e4bc636a5322b8056733e3299bdf0c0f4545aba5372ca4a5b337300f3e8c43e89819a563df0e385229405479e1c12a50b57cc1b8e8e2f8b1db9b3aeca20724244f21c2997270e316e37528f7f302d95d9a2b71d7f0319fb4afc4cb25ea17f1a6fc3b7fbf68d71b65139fbee9997c404c2c76611cbb97750d7e5aae23d2ac26de83ec81d2774caf7279e40737f7b1502a4e8ad15f88a7bcc660a148b1c8c68fe4d891514a7a0d7b6b9e9e4561cf510a9c10bdd586fa57a9683afc9db809d93777c435e0780404f5e05a91ab84d6a63abd77e33e1881f2e341c6a7cae0b8d0507dd6bab56ba688cef5910ff18a816a3bb38fa5c6307eea5221569cdc90cde855083b12e189c39cc9052c328d1e654a3ea11371a6f5ad6ad07d396227464e23b4ae1e1aa1956567e000558473a29f34c53dcd8d4b8d3df0410d8af663de3f177565409f30a2ee236d6edbffc355609b4a60643f6f7db0b30a111d9269d6df96d1d487a6ae2d672ce37fbe0ef02ef64ea5793778f67af238fe45ff15ad1609bdead8fe39d445b2644254cf0acc5effc84ff8786fcbd041b7d3a42085d5c2f1cea86f2817d9b1e21c252e6f02404c7699ed8c6c14b979371d1a726451599fe312b10653fa69fc6c132e30bc299af2e92f9fb4495229f1e49ce927fadcbb19ebee2cb5ceae56981201d7b73168997bde9b287abb25d86d43562839a0b30be80f02c4ee3d2021afb990364264e78cf5b07097dfe860a047191ece343378f63d88136a6509c511f4b785b48187e1bde4f8fcabec22451c19eb9af4a6b743c289426cbc2b508bd4bf9ebb382fe131928292ed0d5113f3e86c3dd8656638f4c0ee5b500ef57d19aaa4e00ab17dcf7950954d017f648aa09bbd5a87aab8c9f42a01c810bc93790ed6791d3a2462a571320bae636632b2d3d77611c6cdff96afff421a534df351910f3ffe92eaae18fc6d4a50908be160ffc5dca626d2777625205d07f18eae60d9fe1b9e32f56fd3e92f98a1aa5c728130249a24cb47556d2c40f2029b17217807e3b3a11afbf8d981df73122d61199854f2414b9ec3f687a2a021fec118b38e6756e67aec56eb726fcab9b69ae09d77d3b55559c10d8afd242836bab39f8a93d1a5f16ba0e0c76565a2731a467af0a490a8e3baa3b3bcd36fba75c7d5c3985355dc38f2ff73f9728903f02274126e39301b1ee8599ccb9fd9955696f08f79a035f09d9ac570225ad05666a2900b56c97cfaa985f3a5814c46ad2a5d998c7f18be2dc1e23c540cd208b3a11124de8965dd8b645746cf351544d0371cc798ec6552d329980050c2a04812f66219677507591470d9a5d103d011b901f2a234395f363a59cc8d18561f7807f1d26e0fe5d2bd38365c174b2886758eeccb73f958c1550df9016863c87aa5a94daabb03dc34208bd682854ebaebe90954ddfc2a4974a02566c15fc5976d38c010400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e2ce050101ffffffff0200ca9a3b000000001976a9141fb09ecb821dd7ce9cc8ee1ed54df0d1863f824188ac80b2e60e0000000017a91466a04fe5f6f1b5bb1149e6cd0079167f570880148700000000000000000000000000000000000000 +04000000d91995f491c208a7a8c1d358417120e91451158db34ab4ef30a1c026e2d6070094eda3d052afd42b6d45e541a1d97184636bb767ccbb7d8f91584693a80233250d86a7943df2dc92fb8b131b0698db24dd01a12d19696c20443f5f639df3ab5ab4dd295ca942181f1a0006ea1556b0c6c73177cd09700bb72e6b2457fed3174f1d69d4e6e3190000fd4005002f434e5adb994d86d3d45d4fc9584f3d70dba6ee34005bbd47169aa751d4c4d06bf9476a26cfb2fdc905c663c77186e370765413a5d0d25be15e4d50746c232c0fde2d09e01c5cf1e53b70db14c9d889fcd1ad128add562edc91339d81d1a4a78e3eed31d4d16d5b3209ae0571569d590114f68e85fd37b26b0c7724531d14a72bbacd02a309cf039521ce4d5537069326fd1fec07eda692ff09f941a8f8c0f543372ec8fa85c20416161dd719e3f4d3f0e663eeefc4da0feb31eace0e54f9df1fd5714aebb4d306636d0b61357e343e9c04c7f60402a9066bdf11b6817ced3f39e539ff98e4282b63ead466a6e15d5c85b76affa63a4e8e935f060a074f30cf9bcc89ef3872f80268ffb4cdc5aa67fb10b745e3b856fbb4b95d439fe8c8933b50eb5dc0091ec9c41c5ddc998b477c5302c0aede64eecf70b35c1f7d7983e1e052039dea18529752ea7e5ae67f4f51009908fd530968726e0fc9dd79d71022d2eb5749f1055e96fa98b35b79e7e32a92de750036c29bbaf38c0d30587b1f87653bcbb979142b658396d4cb7c2496566154d160ba44dbe97165f909c0c5c2e99077bb71041313603d852e22f0c560ab1f0738ddf74f105aa13e26ad8baa139f98f29ed71b85c80085f324f7db0b0a6e9e1d4584af6c592483254378654903adf6b4b70b77be24e817b053ac55016f29ac78a218095509e902936166f7923ceaabda692442ce1da6fd8e3a6b860cf8ecf7770ad2d77afbb0d15de5fc6084ff491a370baeb547171415e1be7f38e5edcc1283bc573f5522286f3d92208d568896dcf51c1566cdb583f1f0d87902e8fc27c6d1c43b1b3bb051cf568e426b551608cc1d27aa72fc9ff34f2479e609a8fc68a6899598f2d713065789f95983f78a3b746798bb19a989381ae0a531178d735c11e1cfa856b40d05b1f959115608e66800e95c067be6ebbd6ac008193ced38e24bc6db67a210d9a6ed0eddbbcdfb0f8439ab66cd4e27ca32f01a095212f36cd6b1c13c17c4ad3327c255b770dc66e03c797b1d39177365c08af59e723825596f024c7dba0a6a71657f84ab7ef5b345c11ad19191bd25d725cd1660d4e0e793c453bf9aac0494f4942f75481f577d0fe5860ffe6c776fbbb801bc6794c11c9d81ec91d32a2e162c69651c8deb52d2bd4bcdf9e0f18254549c041230266cadaf43cb09f113967492286faa3ce2c50a261ec8e847956ddce763e1df446b4164f2594bc40a1c6a756a451022381fd618bb432b9dbfbcb92d79865e3e9beee8a7277359188b6ef0956b59b47e023c06cd3f99c002fed0fb9a7cb63578a4ab4be39d24981caf13d1d4d580e4d48183aca7ae8a3230011b83d1097b33d5b7a3cf89f61d6702f5ec79e66fcd93e5bd4a5183189fae5123cb56791bcf64b9425e473cfdd2073b30e551703823d848e4adcf32673270b7d4b91511b38efd3a60937f2b90c7accaf7102e0b3c5e12c907b62acf098fe2abe7d073f4a4ba248623757adcd12f27ec2cedb749c7e280452b7f2cc085beceb495040cdb00ef434bc9ab78f8b0f79e8bb90cfaf7f8577743dd5a96c8264aa9b19ca5b7e46ed1a74b3d3fa1f02eb0164b62297b61e6bd951bea4562fdf8b3355ea95ad9a72ad0a35affe08f683bf77f781212cb53f9130b672e7e6a5ed3b31bf0a1089f2e694ed7875b9b5d11bc2c6ce1db7dc770d3b6a4e872a572285bfaff5c1f7a2202d8891b3ee2f6bbe4bd633ab700815c6ed9229e19b49856d72da66f865b9bcda32e5af81fd54c122c79ca1fd7a666ea4ef38b5a668b9532e675d8c0561aeba9c5f29a8f9cfc66918bda81e63ecf792705298699e526a10d4bdcc293e93b21513b8cb7dd486030145c81942a84038001873665f93d8ef80bf942f0020400008085202f89010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e3ce050104ffffffff0208d99a3b000000001976a9142943b34e1bbdefc6f6a5c501264404eac228ba5b88ac80b2e60e0000000017a91466a04fe5f6f1b5bb1149e6cd0079167f5708801487000000000000000000000000000000000000000400008085202f8901bd114c7474dc9bddc943f99e3cd8af97332b4c899fbdebc44f105fe6b760bf41010000006b483045022100b73c1baacf61e8b4bd915ee1c007a51366a7cc718c5469de9f035aec7f92fdda0220595ad4cadd2617be7ce65ca5aa10b6d86ae27fb86c960686d7e1c668469afe12012103f9e72f0713a4d4a980309a14a2ba563e0b1125ad067818e77553a1eefbfc5be7ffffffff02f0ba0400000000001976a914d78f41784821c3d9929fa56e85267eae0bb09ffd88ac486e1500000000001976a9144faeeb51bcd0b49f238b323e5f1c6c8bf11ae02a88ac00000000e6ce05000000000000000000000000 From 6d7d9f11a600199f76f37d09615fe768d072d422 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Mon, 29 Nov 2021 18:14:52 -0700 Subject: [PATCH 17/31] improve tx parser memory behavior This is a non-functional change. Instead of `rawTransaction` (which carries the result of parsing a full transaction from zcashd) containing slices of _pointers_ to separately-allocated memory (one allocation for each spend, for example), allocate a slice of spends (one contiguous range of memory). A quick benchmark shows sync improves by about 8%. --- parser/transaction.go | 45 ++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/parser/transaction.go b/parser/transaction.go index a93b34e..a5cfcc5 100644 --- a/parser/transaction.go +++ b/parser/transaction.go @@ -19,14 +19,14 @@ type rawTransaction struct { version uint32 nVersionGroupID uint32 consensusBranchID uint32 - transparentInputs []*txIn - transparentOutputs []*txOut + transparentInputs []txIn + transparentOutputs []txOut nLockTime uint32 nExpiryHeight uint32 valueBalanceSapling int64 - shieldedSpends []*spend - shieldedOutputs []*output - joinSplits []*joinSplit + shieldedSpends []spend + shieldedOutputs []output + joinSplits []joinSplit joinSplitPubKey []byte joinSplitSig []byte bindingSigSapling []byte @@ -103,28 +103,26 @@ func (tx *Transaction) ParseTransparent(data []byte) ([]byte, error) { // TODO: Duplicate/otherwise-too-many transactions are a possible DoS // TODO: vector. At the moment we're assuming trusted input. // See https://nvd.nist.gov/vuln/detail/CVE-2018-17144 for an example. - tx.transparentInputs = make([]*txIn, txInCount) + tx.transparentInputs = make([]txIn, txInCount) for i := 0; i < txInCount; i++ { - ti := &txIn{} + ti := &tx.transparentInputs[i] s, err = ti.ParseFromSlice([]byte(s)) if err != nil { return nil, errors.Wrap(err, "while parsing transparent input") } - tx.transparentInputs[i] = ti } var txOutCount int if !s.ReadCompactSize(&txOutCount) { return nil, errors.New("could not read tx_out_count") } - tx.transparentOutputs = make([]*txOut, txOutCount) + tx.transparentOutputs = make([]txOut, txOutCount) for i := 0; i < txOutCount; i++ { - to := &txOut{} + to := &tx.transparentOutputs[i] s, err = to.ParseFromSlice([]byte(s)) if err != nil { return nil, errors.Wrap(err, "while parsing transparent output") } - tx.transparentOutputs[i] = to } return []byte(s), nil } @@ -379,41 +377,38 @@ func (tx *Transaction) parseV4(data []byte) ([]byte, error) { if !s.ReadCompactSize(&spendCount) { return nil, errors.New("could not read nShieldedSpend") } - tx.shieldedSpends = make([]*spend, spendCount) + tx.shieldedSpends = make([]spend, spendCount) for i := 0; i < spendCount; i++ { - newSpend := &spend{} + newSpend := &tx.shieldedSpends[i] s, err = newSpend.ParseFromSlice([]byte(s), 4) if err != nil { return nil, errors.Wrap(err, "while parsing shielded Spend") } - tx.shieldedSpends[i] = newSpend } if !s.ReadCompactSize(&outputCount) { return nil, errors.New("could not read nShieldedOutput") } - tx.shieldedOutputs = make([]*output, outputCount) + tx.shieldedOutputs = make([]output, outputCount) for i := 0; i < outputCount; i++ { - newOutput := &output{} + newOutput := &tx.shieldedOutputs[i] s, err = newOutput.ParseFromSlice([]byte(s), 4) if err != nil { return nil, errors.Wrap(err, "while parsing shielded Output") } - tx.shieldedOutputs[i] = newOutput } var joinSplitCount int if !s.ReadCompactSize(&joinSplitCount) { return nil, errors.New("could not read nJoinSplit") } - tx.joinSplits = make([]*joinSplit, joinSplitCount) + tx.joinSplits = make([]joinSplit, joinSplitCount) if joinSplitCount > 0 { for i := 0; i < joinSplitCount; i++ { - js := &joinSplit{} + js := &tx.joinSplits[i] s, err = js.ParseFromSlice([]byte(s)) if err != nil { return nil, errors.Wrap(err, "while parsing JoinSplit") } - tx.joinSplits[i] = js } if !s.ReadBytes(&tx.joinSplitPubKey, 32) { @@ -461,14 +456,13 @@ func (tx *Transaction) parseV5(data []byte) ([]byte, error) { if spendCount >= (1 << 16) { return nil, errors.New(fmt.Sprintf("spentCount (%d) must be less than 2^16", spendCount)) } - tx.shieldedSpends = make([]*spend, spendCount) + tx.shieldedSpends = make([]spend, spendCount) for i := 0; i < spendCount; i++ { - newSpend := &spend{} + newSpend := &tx.shieldedSpends[i] s, err = newSpend.ParseFromSlice([]byte(s), tx.version) if err != nil { return nil, errors.Wrap(err, "while parsing shielded Spend") } - tx.shieldedSpends[i] = newSpend } if !s.ReadCompactSize(&outputCount) { return nil, errors.New("could not read nShieldedOutput") @@ -476,14 +470,13 @@ func (tx *Transaction) parseV5(data []byte) ([]byte, error) { if outputCount >= (1 << 16) { return nil, errors.New(fmt.Sprintf("outputCount (%d) must be less than 2^16", outputCount)) } - tx.shieldedOutputs = make([]*output, outputCount) + tx.shieldedOutputs = make([]output, outputCount) for i := 0; i < outputCount; i++ { - newOutput := &output{} + newOutput := &tx.shieldedOutputs[i] s, err = newOutput.ParseFromSlice([]byte(s), tx.version) if err != nil { return nil, errors.Wrap(err, "while parsing shielded Output") } - tx.shieldedOutputs[i] = newOutput } if spendCount+outputCount > 0 && !s.ReadInt64(&tx.valueBalanceSapling) { return nil, errors.New("could not read valueBalance") From fdc54babf2fad6f1d60470c74ab7a2003b89ff39 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Tue, 30 Nov 2021 08:30:08 -0700 Subject: [PATCH 18/31] performance: don't deserialize unused tx fields --- parser/internal/bytestring/bytestring.go | 15 ++ parser/internal/bytestring/bytestring_test.go | 32 +++ parser/transaction.go | 209 +++++++++--------- 3 files changed, 150 insertions(+), 106 deletions(-) diff --git a/parser/internal/bytestring/bytestring.go b/parser/internal/bytestring/bytestring.go index 98f75c1..9f6dfa7 100644 --- a/parser/internal/bytestring/bytestring.go +++ b/parser/internal/bytestring/bytestring.go @@ -137,6 +137,11 @@ func (s *String) ReadCompactSize(size *int) bool { return true } +func (s *String) SkipCompactSize() bool { + var unused int + return s.ReadCompactSize(&unused) +} + // ReadCompactLengthPrefixed reads data prefixed by a CompactSize-encoded // length field into out. It reports whether the read was successful. func (s *String) ReadCompactLengthPrefixed(out *String) bool { @@ -154,6 +159,16 @@ func (s *String) ReadCompactLengthPrefixed(out *String) bool { return true } +// SkipCompactLengthPrefixed reads a CompactSize-encoded +// length field, then skips that many bytes. +func (s *String) SkipCompactLengthPrefixed() bool { + var length int + if !s.ReadCompactSize(&length) { + return false + } + return s.Skip(length) +} + // ReadInt32 decodes a little-endian 32-bit value into out, treating it as // signed, and advances over it. It reports whether the read was successful. func (s *String) ReadInt32(out *int32) bool { diff --git a/parser/internal/bytestring/bytestring_test.go b/parser/internal/bytestring/bytestring_test.go index 1de8117..97e957e 100644 --- a/parser/internal/bytestring/bytestring_test.go +++ b/parser/internal/bytestring/bytestring_test.go @@ -261,6 +261,38 @@ func TestString_ReadCompactLengthPrefixed(t *testing.T) { } } +func TestString_SkipCompactLengthPrefixed(t *testing.T) { + // a stream of 3 bytes followed by 2 bytes into the value variable, v + s := String{3, 55, 66, 77, 2, 88, 99} + + // read the 3 and thus the following 3 bytes + if !s.SkipCompactLengthPrefixed() { + t.Fatalf("SkipCompactLengthPrefix failed") + } + if len(s) != 3 { + t.Fatalf("SkipCompactLengthPrefix incorrect remaining length") + } + + // read the 2 and then two bytes + if !s.SkipCompactLengthPrefixed() { + t.Fatalf("SkipCompactLengthPrefix failed") + } + if len(s) != 0 { + t.Fatalf("SkipCompactLengthPrefix incorrect remaining length") + } + + // at the end of the String, another read should return false + if s.SkipCompactLengthPrefixed() { + t.Fatalf("SkipCompactLengthPrefix unexpected success") + } + + // this string is too short (less than 2 bytes of data) + s = String{3, 55, 66} + if s.SkipCompactLengthPrefixed() { + t.Fatalf("SkipdCompactLengthPrefix unexpected success") + } +} + var readInt32Tests = []struct { s String expected int32 diff --git a/parser/transaction.go b/parser/transaction.go index a5cfcc5..44f7d0d 100644 --- a/parser/transaction.go +++ b/parser/transaction.go @@ -15,55 +15,55 @@ import ( ) type rawTransaction struct { - fOverwintered bool - version uint32 - nVersionGroupID uint32 - consensusBranchID uint32 - transparentInputs []txIn - transparentOutputs []txOut - nLockTime uint32 - nExpiryHeight uint32 - valueBalanceSapling int64 - shieldedSpends []spend - shieldedOutputs []output - joinSplits []joinSplit - joinSplitPubKey []byte - joinSplitSig []byte - bindingSigSapling []byte + fOverwintered bool + version uint32 + nVersionGroupID uint32 + consensusBranchID uint32 + transparentInputs []txIn + transparentOutputs []txOut + //nLockTime uint32 + //nExpiryHeight uint32 + //valueBalanceSapling int64 + shieldedSpends []spend + shieldedOutputs []output + joinSplits []joinSplit + //joinSplitPubKey []byte + //joinSplitSig []byte + //bindingSigSapling []byte } // Txin format as described in https://en.bitcoin.it/wiki/Transaction type txIn struct { // SHA256d of a previous (to-be-used) transaction - PrevTxHash []byte + //PrevTxHash []byte // Index of the to-be-used output in the previous tx - PrevTxOutIndex uint32 + //PrevTxOutIndex uint32 // CompactSize-prefixed, could be a pubkey or a script ScriptSig []byte // Bitcoin: "normally 0xFFFFFFFF; irrelevant unless transaction's lock_time > 0" - SequenceNumber uint32 + //SequenceNumber uint32 } func (tx *txIn) ParseFromSlice(data []byte) ([]byte, error) { s := bytestring.String(data) - if !s.ReadBytes(&tx.PrevTxHash, 32) { - return nil, errors.New("could not read PrevTxHash") + if !s.Skip(32) { + return nil, errors.New("could not skip PrevTxHash") } - if !s.ReadUint32(&tx.PrevTxOutIndex) { - return nil, errors.New("could not read PrevTxOutIndex") + if !s.Skip(4) { + return nil, errors.New("could not skip PrevTxOutIndex") } if !s.ReadCompactLengthPrefixed((*bytestring.String)(&tx.ScriptSig)) { return nil, errors.New("could not read ScriptSig") } - if !s.ReadUint32(&tx.SequenceNumber) { - return nil, errors.New("could not read SequenceNumber") + if !s.Skip(4) { + return nil, errors.New("could not skip SequenceNumber") } return []byte(s), nil @@ -75,18 +75,18 @@ type txOut struct { Value uint64 // Script. CompactSize-prefixed. - Script []byte + //Script []byte } func (tx *txOut) ParseFromSlice(data []byte) ([]byte, error) { s := bytestring.String(data) - if !s.ReadUint64(&tx.Value) { - return nil, errors.New("could not read txOut value") + if !s.Skip(8) { + return nil, errors.New("could not skip txOut value") } - if !s.ReadCompactLengthPrefixed((*bytestring.String)(&tx.Script)) { - return nil, errors.New("could not read txOut script") + if !s.SkipCompactLengthPrefixed() { + return nil, errors.New("could not skip txOut script") } return []byte(s), nil @@ -100,9 +100,6 @@ func (tx *Transaction) ParseTransparent(data []byte) ([]byte, error) { return nil, errors.New("could not read tx_in_count") } var err error - // TODO: Duplicate/otherwise-too-many transactions are a possible DoS - // TODO: vector. At the moment we're assuming trusted input. - // See https://nvd.nist.gov/vuln/detail/CVE-2018-17144 for an example. tx.transparentInputs = make([]txIn, txInCount) for i := 0; i < txInCount; i++ { ti := &tx.transparentInputs[i] @@ -130,39 +127,39 @@ func (tx *Transaction) ParseTransparent(data []byte) ([]byte, error) { // spend is a Sapling Spend Description as described in 7.3 of the Zcash // protocol specification. type spend struct { - cv []byte // 32 - anchor []byte // 32 - nullifier []byte // 32 - rk []byte // 32 - zkproof []byte // 192 - spendAuthSig []byte // 64 + //cv []byte // 32 + //anchor []byte // 32 + nullifier []byte // 32 + //rk []byte // 32 + //zkproof []byte // 192 + //spendAuthSig []byte // 64 } func (p *spend) ParseFromSlice(data []byte, version uint32) ([]byte, error) { s := bytestring.String(data) - if !s.ReadBytes(&p.cv, 32) { - return nil, errors.New("could not read cv") + if !s.Skip(32) { + return nil, errors.New("could not skip cv") } - if version <= 4 && !s.ReadBytes(&p.anchor, 32) { - return nil, errors.New("could not read anchor") + if version <= 4 && !s.Skip(32) { + return nil, errors.New("could not skip anchor") } if !s.ReadBytes(&p.nullifier, 32) { return nil, errors.New("could not read nullifier") } - if !s.ReadBytes(&p.rk, 32) { - return nil, errors.New("could not read rk") + if !s.Skip(32) { + return nil, errors.New("could not skip rk") } - if version <= 4 && !s.ReadBytes(&p.zkproof, 192) { - return nil, errors.New("could not read zkproof") + if version <= 4 && !s.Skip(192) { + return nil, errors.New("could not skip zkproof") } - if version <= 4 && !s.ReadBytes(&p.spendAuthSig, 64) { - return nil, errors.New("could not read spendAuthSig") + if version <= 4 && !s.Skip(64) { + return nil, errors.New("could not skip spendAuthSig") } return []byte(s), nil @@ -177,19 +174,19 @@ func (p *spend) ToCompact() *walletrpc.CompactSpend { // output is a Sapling Output Description as described in section 7.4 of the // Zcash protocol spec. type output struct { - cv []byte // 32 + //cv []byte // 32 cmu []byte // 32 ephemeralKey []byte // 32 encCiphertext []byte // 580 - outCiphertext []byte // 80 - zkproof []byte // 192 + //outCiphertext []byte // 80 + //zkproof []byte // 192 } func (p *output) ParseFromSlice(data []byte, version uint32) ([]byte, error) { s := bytestring.String(data) - if !s.ReadBytes(&p.cv, 32) { - return nil, errors.New("could not read cv") + if !s.Skip(32) { + return nil, errors.New("could not skip cv") } if !s.ReadBytes(&p.cmu, 32) { @@ -204,12 +201,12 @@ func (p *output) ParseFromSlice(data []byte, version uint32) ([]byte, error) { return nil, errors.New("could not read encCiphertext") } - if !s.ReadBytes(&p.outCiphertext, 80) { - return nil, errors.New("could not read outCiphertext") + if !s.Skip(80) { + return nil, errors.New("could not skip outCiphertext") } - if version <= 4 && !s.ReadBytes(&p.zkproof, 192) { - return nil, errors.New("could not read zkproof") + if version <= 4 && !s.Skip(192) { + return nil, errors.New("could not skip zkproof") } return []byte(s), nil @@ -227,66 +224,66 @@ func (p *output) ToCompact() *walletrpc.CompactOutput { // protocol spec. Its exact contents differ by transaction version and network // upgrade level. Only version 4 is supported, no need for proofPHGR13. type joinSplit struct { - vpubOld uint64 - vpubNew uint64 - anchor []byte // 32 - nullifiers [2][]byte // 64 [N_old][32]byte - commitments [2][]byte // 64 [N_new][32]byte - ephemeralKey []byte // 32 - randomSeed []byte // 32 - vmacs [2][]byte // 64 [N_old][32]byte - proofGroth16 []byte // 192 (version 4 only) - encCiphertexts [2][]byte // 1202 [N_new][601]byte + //vpubOld uint64 + //vpubNew uint64 + //anchor []byte // 32 + //nullifiers [2][]byte // 64 [N_old][32]byte + //commitments [2][]byte // 64 [N_new][32]byte + //ephemeralKey []byte // 32 + //randomSeed []byte // 32 + //vmacs [2][]byte // 64 [N_old][32]byte + //proofGroth16 []byte // 192 (version 4 only) + //encCiphertexts [2][]byte // 1202 [N_new][601]byte } func (p *joinSplit) ParseFromSlice(data []byte) ([]byte, error) { s := bytestring.String(data) - if !s.ReadUint64(&p.vpubOld) { - return nil, errors.New("could not read vpubOld") + if !s.Skip(8) { + return nil, errors.New("could not skip vpubOld") } - if !s.ReadUint64(&p.vpubNew) { - return nil, errors.New("could not read vpubNew") + if !s.Skip(8) { + return nil, errors.New("could not skip vpubNew") } - if !s.ReadBytes(&p.anchor, 32) { - return nil, errors.New("could not read anchor") + if !s.Skip(32) { + return nil, errors.New("could not skip anchor") } for i := 0; i < 2; i++ { - if !s.ReadBytes(&p.nullifiers[i], 32) { - return nil, errors.New("could not read a nullifier") + if !s.Skip(32) { + return nil, errors.New("could not skip a nullifier") } } for i := 0; i < 2; i++ { - if !s.ReadBytes(&p.commitments[i], 32) { - return nil, errors.New("could not read a commitment") + if !s.Skip(32) { + return nil, errors.New("could not skip a commitment") } } - if !s.ReadBytes(&p.ephemeralKey, 32) { - return nil, errors.New("could not read ephemeralKey") + if !s.Skip(32) { + return nil, errors.New("could not skip ephemeralKey") } - if !s.ReadBytes(&p.randomSeed, 32) { - return nil, errors.New("could not read randomSeed") + if !s.Skip(32) { + return nil, errors.New("could not skip randomSeed") } for i := 0; i < 2; i++ { - if !s.ReadBytes(&p.vmacs[i], 32) { - return nil, errors.New("could not read a vmac") + if !s.Skip(32) { + return nil, errors.New("could not skip a vmac") } } - if !s.ReadBytes(&p.proofGroth16, 192) { - return nil, errors.New("could not read Groth16 proof") + if !s.Skip(192) { + return nil, errors.New("could not skip Groth16 proof") } for i := 0; i < 2; i++ { - if !s.ReadBytes(&p.encCiphertexts[i], 601) { - return nil, errors.New("could not read an encCiphertext") + if !s.Skip(601) { + return nil, errors.New("could not skip an encCiphertext") } } @@ -361,18 +358,18 @@ func (tx *Transaction) parseV4(data []byte) ([]byte, error) { if err != nil { return nil, err } - if !s.ReadUint32(&tx.nLockTime) { - return nil, errors.New("could not read nLockTime") + if !s.Skip(4) { + return nil, errors.New("could not skip nLockTime") } - if !s.ReadUint32(&tx.nExpiryHeight) { - return nil, errors.New("could not read nExpiryHeight") + if !s.Skip(4) { + return nil, errors.New("could not skip nExpiryHeight") } var spendCount, outputCount int - if !s.ReadInt64(&tx.valueBalanceSapling) { - return nil, errors.New("could not read valueBalance") + if !s.Skip(8) { + return nil, errors.New("could not skip valueBalance") } if !s.ReadCompactSize(&spendCount) { return nil, errors.New("could not read nShieldedSpend") @@ -411,16 +408,16 @@ func (tx *Transaction) parseV4(data []byte) ([]byte, error) { } } - if !s.ReadBytes(&tx.joinSplitPubKey, 32) { - return nil, errors.New("could not read joinSplitPubKey") + if !s.Skip(32) { + return nil, errors.New("could not skip joinSplitPubKey") } - if !s.ReadBytes(&tx.joinSplitSig, 64) { - return nil, errors.New("could not read joinSplitSig") + if !s.Skip(64) { + return nil, errors.New("could not skip joinSplitSig") } } - if spendCount+outputCount > 0 && !s.ReadBytes(&tx.bindingSigSapling, 64) { - return nil, errors.New("could not read bindingSigSapling") + if spendCount+outputCount > 0 && !s.Skip(64) { + return nil, errors.New("could not skip bindingSigSapling") } return s, nil } @@ -438,11 +435,11 @@ func (tx *Transaction) parseV5(data []byte) ([]byte, error) { if tx.consensusBranchID != 0x37519621 { return nil, errors.New("unknown consensusBranchID") } - if !s.ReadUint32(&tx.nLockTime) { - return nil, errors.New("could not read nLockTime") + if !s.Skip(4) { + return nil, errors.New("could not skip nLockTime") } - if !s.ReadUint32(&tx.nExpiryHeight) { - return nil, errors.New("could not read nExpiryHeight") + if !s.Skip(4) { + return nil, errors.New("could not skip nExpiryHeight") } s, err = tx.ParseTransparent([]byte(s)) if err != nil { @@ -478,7 +475,7 @@ func (tx *Transaction) parseV5(data []byte) ([]byte, error) { return nil, errors.Wrap(err, "while parsing shielded Output") } } - if spendCount+outputCount > 0 && !s.ReadInt64(&tx.valueBalanceSapling) { + if spendCount+outputCount > 0 && !s.Skip(8) { return nil, errors.New("could not read valueBalance") } if spendCount > 0 && !s.Skip(32) { @@ -493,8 +490,8 @@ func (tx *Transaction) parseV5(data []byte) ([]byte, error) { if !s.Skip(192 * outputCount) { return nil, errors.New("could not skip vOutputProofsSapling") } - if spendCount+outputCount > 0 && !s.ReadBytes(&tx.bindingSigSapling, 64) { - return nil, errors.New("could not read bindingSigSapling") + if spendCount+outputCount > 0 && !s.Skip(64) { + return nil, errors.New("could not skip bindingSigSapling") } var actionsCount int if !s.ReadCompactSize(&actionsCount) { From 4e1910aeea661f8340d803cd4d61259925474dee Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Tue, 30 Nov 2021 10:08:21 -0700 Subject: [PATCH 19/31] add NU5 Orchard transaction data to CompactBlock --- parser/internal/bytestring/bytestring.go | 9 +- parser/transaction.go | 71 ++++++- walletrpc/compact_formats.pb.go | 233 +++++++++++++++++------ walletrpc/compact_formats.proto | 22 ++- walletrpc/darkside.pb.go | 2 +- walletrpc/service.pb.go | 2 +- 6 files changed, 255 insertions(+), 84 deletions(-) diff --git a/parser/internal/bytestring/bytestring.go b/parser/internal/bytestring/bytestring.go index 9f6dfa7..0c980f9 100644 --- a/parser/internal/bytestring/bytestring.go +++ b/parser/internal/bytestring/bytestring.go @@ -137,11 +137,6 @@ func (s *String) ReadCompactSize(size *int) bool { return true } -func (s *String) SkipCompactSize() bool { - var unused int - return s.ReadCompactSize(&unused) -} - // ReadCompactLengthPrefixed reads data prefixed by a CompactSize-encoded // length field into out. It reports whether the read was successful. func (s *String) ReadCompactLengthPrefixed(out *String) bool { @@ -159,8 +154,8 @@ func (s *String) ReadCompactLengthPrefixed(out *String) bool { return true } -// SkipCompactLengthPrefixed reads a CompactSize-encoded -// length field, then skips that many bytes. +// SkipCompactLengthPrefixed skips a CompactSize-encoded +// length field. func (s *String) SkipCompactLengthPrefixed() bool { var length int if !s.ReadCompactSize(&length) { diff --git a/parser/transaction.go b/parser/transaction.go index 44f7d0d..b5aef01 100644 --- a/parser/transaction.go +++ b/parser/transaction.go @@ -30,6 +30,7 @@ type rawTransaction struct { //joinSplitPubKey []byte //joinSplitSig []byte //bindingSigSapling []byte + orchardActions []action } // Txin format as described in https://en.bitcoin.it/wiki/Transaction @@ -165,8 +166,8 @@ func (p *spend) ParseFromSlice(data []byte, version uint32) ([]byte, error) { return []byte(s), nil } -func (p *spend) ToCompact() *walletrpc.CompactSpend { - return &walletrpc.CompactSpend{ +func (p *spend) ToCompact() *walletrpc.CompactSaplingSpend { + return &walletrpc.CompactSaplingSpend{ Nf: p.nullifier, } } @@ -212,8 +213,8 @@ func (p *output) ParseFromSlice(data []byte, version uint32) ([]byte, error) { return []byte(s), nil } -func (p *output) ToCompact() *walletrpc.CompactOutput { - return &walletrpc.CompactOutput{ +func (p *output) ToCompact() *walletrpc.CompactSaplingOutput { + return &walletrpc.CompactSaplingOutput{ Cmu: p.cmu, Epk: p.ephemeralKey, Ciphertext: p.encCiphertext[:52], @@ -290,6 +291,51 @@ func (p *joinSplit) ParseFromSlice(data []byte) ([]byte, error) { return []byte(s), nil } +type action struct { + //cv []byte // 32 + nullifier []byte // 32 + //rk []byte // 32 + cmx []byte // 32 + ephemeralKey []byte // 32 + encCiphertext []byte // 580 + //outCiphertext []byte // 80 +} + +func (a *action) ParseFromSlice(data []byte) ([]byte, error) { + s := bytestring.String(data) + if !s.Skip(32) { + return nil, errors.New("could not read action cv") + } + if !s.ReadBytes(&a.nullifier, 32) { + return nil, errors.New("could not read action nullifier") + } + if !s.Skip(32) { + return nil, errors.New("could not read action rk") + } + if !s.ReadBytes(&a.cmx, 32) { + return nil, errors.New("could not read action cmx") + } + if !s.ReadBytes(&a.ephemeralKey, 32) { + return nil, errors.New("could not read action ephemeralKey") + } + if !s.ReadBytes(&a.encCiphertext, 580) { + return nil, errors.New("could not read action encCiphertext") + } + if !s.Skip(80) { + return nil, errors.New("could not read action outCiphertext") + } + return []byte(s), nil +} + +func (p *action) ToCompact() *walletrpc.CompactOrchardAction { + return &walletrpc.CompactOrchardAction{ + Nullifier: p.nullifier, + Cmx: p.cmx, + EphemeralKey: p.ephemeralKey, + EncCiphertext: p.encCiphertext[:52], + } +} + // Transaction encodes a full (zcashd) transaction. type Transaction struct { *rawTransaction @@ -335,8 +381,9 @@ func (tx *Transaction) ToCompact(index int) *walletrpc.CompactTx { Index: uint64(index), // index is contextual Hash: tx.GetEncodableHash(), //Fee: 0, // TODO: calculate fees - Spends: make([]*walletrpc.CompactSpend, len(tx.shieldedSpends)), - Outputs: make([]*walletrpc.CompactOutput, len(tx.shieldedOutputs)), + Spends: make([]*walletrpc.CompactSaplingSpend, len(tx.shieldedSpends)), + Outputs: make([]*walletrpc.CompactSaplingOutput, len(tx.shieldedOutputs)), + Actions: make([]*walletrpc.CompactOrchardAction, len(tx.orchardActions)), } for i, spend := range tx.shieldedSpends { ctx.Spends[i] = spend.ToCompact() @@ -344,6 +391,9 @@ func (tx *Transaction) ToCompact(index int) *walletrpc.CompactTx { for i, output := range tx.shieldedOutputs { ctx.Outputs[i] = output.ToCompact() } + for i, a := range tx.orchardActions { + ctx.Actions[i] = a.ToCompact() + } return ctx } @@ -500,8 +550,13 @@ func (tx *Transaction) parseV5(data []byte) ([]byte, error) { if actionsCount >= (1 << 16) { return nil, errors.New(fmt.Sprintf("actionsCount (%d) must be less than 2^16", actionsCount)) } - if !s.Skip(820 * actionsCount) { - return nil, errors.New("could not skip vActionsOrchard") + tx.orchardActions = make([]action, actionsCount) + for i := 0; i < actionsCount; i++ { + a := &tx.orchardActions[i] + s, err = a.ParseFromSlice([]byte(s)) + if err != nil { + return nil, errors.Wrap(err, "while parsing orchard action") + } } if actionsCount > 0 { if !s.Skip(1) { diff --git a/walletrpc/compact_formats.pb.go b/walletrpc/compact_formats.pb.go index 3ddb38d..98f22b2 100644 --- a/walletrpc/compact_formats.pb.go +++ b/walletrpc/compact_formats.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 +// protoc-gen-go v1.27.1 // protoc v3.15.7 // source: compact_formats.proto @@ -138,9 +138,10 @@ type CompactTx struct { // unset because the calculation requires reference to prior transactions. // in a pure-Sapling context, the fee will be calculable as: // valueBalance + (sum(vPubNew) - sum(vPubOld) - sum(tOut)) - Fee uint32 `protobuf:"varint,3,opt,name=fee,proto3" json:"fee,omitempty"` - Spends []*CompactSpend `protobuf:"bytes,4,rep,name=spends,proto3" json:"spends,omitempty"` // inputs - Outputs []*CompactOutput `protobuf:"bytes,5,rep,name=outputs,proto3" json:"outputs,omitempty"` // outputs + Fee uint32 `protobuf:"varint,3,opt,name=fee,proto3" json:"fee,omitempty"` + Spends []*CompactSaplingSpend `protobuf:"bytes,4,rep,name=spends,proto3" json:"spends,omitempty"` // inputs + Outputs []*CompactSaplingOutput `protobuf:"bytes,5,rep,name=outputs,proto3" json:"outputs,omitempty"` // outputs + Actions []*CompactOrchardAction `protobuf:"bytes,6,rep,name=actions,proto3" json:"actions,omitempty"` } func (x *CompactTx) Reset() { @@ -196,23 +197,30 @@ func (x *CompactTx) GetFee() uint32 { return 0 } -func (x *CompactTx) GetSpends() []*CompactSpend { +func (x *CompactTx) GetSpends() []*CompactSaplingSpend { if x != nil { return x.Spends } return nil } -func (x *CompactTx) GetOutputs() []*CompactOutput { +func (x *CompactTx) GetOutputs() []*CompactSaplingOutput { if x != nil { return x.Outputs } return nil } -// CompactSpend is a Sapling Spend Description as described in 7.3 of the Zcash +func (x *CompactTx) GetActions() []*CompactOrchardAction { + if x != nil { + return x.Actions + } + return nil +} + +// CompactSaplingSpend is a Sapling Spend Description as described in 7.3 of the Zcash // protocol specification. -type CompactSpend struct { +type CompactSaplingSpend struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -220,8 +228,8 @@ type CompactSpend struct { Nf []byte `protobuf:"bytes,1,opt,name=nf,proto3" json:"nf,omitempty"` // nullifier (see the Zcash protocol specification) } -func (x *CompactSpend) Reset() { - *x = CompactSpend{} +func (x *CompactSaplingSpend) Reset() { + *x = CompactSaplingSpend{} if protoimpl.UnsafeEnabled { mi := &file_compact_formats_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -229,13 +237,13 @@ func (x *CompactSpend) Reset() { } } -func (x *CompactSpend) String() string { +func (x *CompactSaplingSpend) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CompactSpend) ProtoMessage() {} +func (*CompactSaplingSpend) ProtoMessage() {} -func (x *CompactSpend) ProtoReflect() protoreflect.Message { +func (x *CompactSaplingSpend) ProtoReflect() protoreflect.Message { mi := &file_compact_formats_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -247,12 +255,12 @@ func (x *CompactSpend) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CompactSpend.ProtoReflect.Descriptor instead. -func (*CompactSpend) Descriptor() ([]byte, []int) { +// Deprecated: Use CompactSaplingSpend.ProtoReflect.Descriptor instead. +func (*CompactSaplingSpend) Descriptor() ([]byte, []int) { return file_compact_formats_proto_rawDescGZIP(), []int{2} } -func (x *CompactSpend) GetNf() []byte { +func (x *CompactSaplingSpend) GetNf() []byte { if x != nil { return x.Nf } @@ -261,18 +269,18 @@ func (x *CompactSpend) GetNf() []byte { // output is a Sapling Output Description as described in section 7.4 of the // Zcash protocol spec. Total size is 948. -type CompactOutput struct { +type CompactSaplingOutput struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Cmu []byte `protobuf:"bytes,1,opt,name=cmu,proto3" json:"cmu,omitempty"` // note commitment u-coordinate Epk []byte `protobuf:"bytes,2,opt,name=epk,proto3" json:"epk,omitempty"` // ephemeral public key - Ciphertext []byte `protobuf:"bytes,3,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"` // ciphertext and zkproof + Ciphertext []byte `protobuf:"bytes,3,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"` // first 52 bytes of ciphertext } -func (x *CompactOutput) Reset() { - *x = CompactOutput{} +func (x *CompactSaplingOutput) Reset() { + *x = CompactSaplingOutput{} if protoimpl.UnsafeEnabled { mi := &file_compact_formats_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -280,13 +288,13 @@ func (x *CompactOutput) Reset() { } } -func (x *CompactOutput) String() string { +func (x *CompactSaplingOutput) String() string { return protoimpl.X.MessageStringOf(x) } -func (*CompactOutput) ProtoMessage() {} +func (*CompactSaplingOutput) ProtoMessage() {} -func (x *CompactOutput) ProtoReflect() protoreflect.Message { +func (x *CompactSaplingOutput) ProtoReflect() protoreflect.Message { mi := &file_compact_formats_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -298,32 +306,105 @@ func (x *CompactOutput) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use CompactOutput.ProtoReflect.Descriptor instead. -func (*CompactOutput) Descriptor() ([]byte, []int) { +// Deprecated: Use CompactSaplingOutput.ProtoReflect.Descriptor instead. +func (*CompactSaplingOutput) Descriptor() ([]byte, []int) { return file_compact_formats_proto_rawDescGZIP(), []int{3} } -func (x *CompactOutput) GetCmu() []byte { +func (x *CompactSaplingOutput) GetCmu() []byte { if x != nil { return x.Cmu } return nil } -func (x *CompactOutput) GetEpk() []byte { +func (x *CompactSaplingOutput) GetEpk() []byte { if x != nil { return x.Epk } return nil } -func (x *CompactOutput) GetCiphertext() []byte { +func (x *CompactSaplingOutput) GetCiphertext() []byte { if x != nil { return x.Ciphertext } return nil } +// https://github.com/zcash/zips/blob/main/zip-0225.rst#orchard-action-description-orchardaction +// (but not all fields are needed) +type CompactOrchardAction struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Nullifier []byte `protobuf:"bytes,1,opt,name=nullifier,proto3" json:"nullifier,omitempty"` // [32] The nullifier of the input note + Cmx []byte `protobuf:"bytes,2,opt,name=cmx,proto3" json:"cmx,omitempty"` // [32] The x-coordinate of the note commitment for the output note + EphemeralKey []byte `protobuf:"bytes,3,opt,name=ephemeralKey,proto3" json:"ephemeralKey,omitempty"` // [32] An encoding of an ephemeral Pallas public key + EncCiphertext []byte `protobuf:"bytes,4,opt,name=encCiphertext,proto3" json:"encCiphertext,omitempty"` // [580] The encrypted contents of the note plaintext +} + +func (x *CompactOrchardAction) Reset() { + *x = CompactOrchardAction{} + if protoimpl.UnsafeEnabled { + mi := &file_compact_formats_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CompactOrchardAction) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CompactOrchardAction) ProtoMessage() {} + +func (x *CompactOrchardAction) ProtoReflect() protoreflect.Message { + mi := &file_compact_formats_proto_msgTypes[4] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CompactOrchardAction.ProtoReflect.Descriptor instead. +func (*CompactOrchardAction) Descriptor() ([]byte, []int) { + return file_compact_formats_proto_rawDescGZIP(), []int{4} +} + +func (x *CompactOrchardAction) GetNullifier() []byte { + if x != nil { + return x.Nullifier + } + return nil +} + +func (x *CompactOrchardAction) GetCmx() []byte { + if x != nil { + return x.Cmx + } + return nil +} + +func (x *CompactOrchardAction) GetEphemeralKey() []byte { + if x != nil { + return x.EphemeralKey + } + return nil +} + +func (x *CompactOrchardAction) GetEncCiphertext() []byte { + if x != nil { + return x.EncCiphertext + } + return nil +} + var File_compact_formats_proto protoreflect.FileDescriptor var file_compact_formats_proto_rawDesc = []byte{ @@ -343,29 +424,45 @@ var file_compact_formats_proto_rawDesc = []byte{ 0x06, 0x68, 0x65, 0x61, 0x64, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x03, 0x76, 0x74, 0x78, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x52, 0x03, 0x76, 0x74, 0x78, 0x22, 0xc4, 0x01, 0x0a, 0x09, + 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x52, 0x03, 0x76, 0x74, 0x78, 0x22, 0x99, 0x02, 0x0a, 0x09, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x10, 0x0a, 0x03, 0x66, 0x65, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x3b, 0x0a, 0x06, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x52, 0x03, 0x66, 0x65, 0x65, 0x12, 0x42, 0x0a, 0x06, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2a, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, - 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x52, 0x06, 0x73, 0x70, 0x65, 0x6e, - 0x64, 0x73, 0x12, 0x3e, 0x0a, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, 0x18, 0x05, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, - 0x61, 0x63, 0x74, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, - 0x74, 0x73, 0x22, 0x1e, 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x70, 0x65, - 0x6e, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x6e, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, - 0x6e, 0x66, 0x22, 0x53, 0x0a, 0x0d, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x4f, 0x75, 0x74, - 0x70, 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x6d, 0x75, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, - 0x52, 0x03, 0x63, 0x6d, 0x75, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0c, 0x52, 0x03, 0x65, 0x70, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, - 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, - 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, 0x67, 0x68, 0x74, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x61, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x70, 0x65, 0x6e, + 0x64, 0x52, 0x06, 0x73, 0x70, 0x65, 0x6e, 0x64, 0x73, 0x12, 0x45, 0x0a, 0x07, 0x6f, 0x75, 0x74, + 0x70, 0x75, 0x74, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x61, 0x73, + 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, + 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x61, 0x70, 0x6c, 0x69, 0x6e, + 0x67, 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x52, 0x07, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x73, + 0x12, 0x45, 0x0a, 0x07, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, + 0x74, 0x4f, 0x72, 0x63, 0x68, 0x61, 0x72, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x25, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x70, 0x61, + 0x63, 0x74, 0x53, 0x61, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x53, 0x70, 0x65, 0x6e, 0x64, 0x12, 0x0e, + 0x0a, 0x02, 0x6e, 0x66, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x02, 0x6e, 0x66, 0x22, 0x5a, + 0x0a, 0x14, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x53, 0x61, 0x70, 0x6c, 0x69, 0x6e, 0x67, + 0x4f, 0x75, 0x74, 0x70, 0x75, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x6d, 0x75, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x6d, 0x75, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x70, 0x6b, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x65, 0x70, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, + 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, + 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x90, 0x01, 0x0a, 0x14, 0x43, + 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x4f, 0x72, 0x63, 0x68, 0x61, 0x72, 0x64, 0x41, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x75, 0x6c, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6e, 0x75, 0x6c, 0x6c, 0x69, 0x66, 0x69, 0x65, + 0x72, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x6d, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, + 0x63, 0x6d, 0x78, 0x12, 0x22, 0x0a, 0x0c, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, + 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x65, 0x70, 0x68, 0x65, 0x6d, + 0x65, 0x72, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0d, 0x65, 0x6e, 0x63, 0x43, 0x69, + 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, + 0x65, 0x6e, 0x63, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x42, 0x1b, 0x5a, + 0x16, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -380,22 +477,24 @@ func file_compact_formats_proto_rawDescGZIP() []byte { return file_compact_formats_proto_rawDescData } -var file_compact_formats_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_compact_formats_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_compact_formats_proto_goTypes = []interface{}{ - (*CompactBlock)(nil), // 0: cash.z.wallet.sdk.rpc.CompactBlock - (*CompactTx)(nil), // 1: cash.z.wallet.sdk.rpc.CompactTx - (*CompactSpend)(nil), // 2: cash.z.wallet.sdk.rpc.CompactSpend - (*CompactOutput)(nil), // 3: cash.z.wallet.sdk.rpc.CompactOutput + (*CompactBlock)(nil), // 0: cash.z.wallet.sdk.rpc.CompactBlock + (*CompactTx)(nil), // 1: cash.z.wallet.sdk.rpc.CompactTx + (*CompactSaplingSpend)(nil), // 2: cash.z.wallet.sdk.rpc.CompactSaplingSpend + (*CompactSaplingOutput)(nil), // 3: cash.z.wallet.sdk.rpc.CompactSaplingOutput + (*CompactOrchardAction)(nil), // 4: cash.z.wallet.sdk.rpc.CompactOrchardAction } var file_compact_formats_proto_depIdxs = []int32{ 1, // 0: cash.z.wallet.sdk.rpc.CompactBlock.vtx:type_name -> cash.z.wallet.sdk.rpc.CompactTx - 2, // 1: cash.z.wallet.sdk.rpc.CompactTx.spends:type_name -> cash.z.wallet.sdk.rpc.CompactSpend - 3, // 2: cash.z.wallet.sdk.rpc.CompactTx.outputs:type_name -> cash.z.wallet.sdk.rpc.CompactOutput - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 2, // 1: cash.z.wallet.sdk.rpc.CompactTx.spends:type_name -> cash.z.wallet.sdk.rpc.CompactSaplingSpend + 3, // 2: cash.z.wallet.sdk.rpc.CompactTx.outputs:type_name -> cash.z.wallet.sdk.rpc.CompactSaplingOutput + 4, // 3: cash.z.wallet.sdk.rpc.CompactTx.actions:type_name -> cash.z.wallet.sdk.rpc.CompactOrchardAction + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_compact_formats_proto_init() } @@ -429,7 +528,7 @@ func file_compact_formats_proto_init() { } } file_compact_formats_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CompactSpend); i { + switch v := v.(*CompactSaplingSpend); i { case 0: return &v.state case 1: @@ -441,7 +540,19 @@ func file_compact_formats_proto_init() { } } file_compact_formats_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*CompactOutput); i { + switch v := v.(*CompactSaplingOutput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_compact_formats_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CompactOrchardAction); i { case 0: return &v.state case 1: @@ -459,7 +570,7 @@ func file_compact_formats_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_compact_formats_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/walletrpc/compact_formats.proto b/walletrpc/compact_formats.proto index bf51ebb..566545f 100644 --- a/walletrpc/compact_formats.proto +++ b/walletrpc/compact_formats.proto @@ -37,20 +37,30 @@ message CompactTx { // valueBalance + (sum(vPubNew) - sum(vPubOld) - sum(tOut)) uint32 fee = 3; - repeated CompactSpend spends = 4; // inputs - repeated CompactOutput outputs = 5; // outputs + repeated CompactSaplingSpend spends = 4; // inputs + repeated CompactSaplingOutput outputs = 5; // outputs + repeated CompactOrchardAction actions = 6; } -// CompactSpend is a Sapling Spend Description as described in 7.3 of the Zcash +// CompactSaplingSpend is a Sapling Spend Description as described in 7.3 of the Zcash // protocol specification. -message CompactSpend { +message CompactSaplingSpend { bytes nf = 1; // nullifier (see the Zcash protocol specification) } // output is a Sapling Output Description as described in section 7.4 of the // Zcash protocol spec. Total size is 948. -message CompactOutput { +message CompactSaplingOutput { bytes cmu = 1; // note commitment u-coordinate bytes epk = 2; // ephemeral public key - bytes ciphertext = 3; // ciphertext and zkproof + bytes ciphertext = 3; // first 52 bytes of ciphertext +} + +// https://github.com/zcash/zips/blob/main/zip-0225.rst#orchard-action-description-orchardaction +// (but not all fields are needed) +message CompactOrchardAction { + bytes nullifier = 1; // [32] The nullifier of the input note + bytes cmx = 2; // [32] The x-coordinate of the note commitment for the output note + bytes ephemeralKey = 3; // [32] An encoding of an ephemeral Pallas public key + bytes encCiphertext = 4; // [580] The encrypted contents of the note plaintext } diff --git a/walletrpc/darkside.pb.go b/walletrpc/darkside.pb.go index 77a2f1c..0fa8b61 100644 --- a/walletrpc/darkside.pb.go +++ b/walletrpc/darkside.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 +// protoc-gen-go v1.27.1 // protoc v3.15.7 // source: darkside.proto diff --git a/walletrpc/service.pb.go b/walletrpc/service.pb.go index 9b3e228..55a3d56 100644 --- a/walletrpc/service.pb.go +++ b/walletrpc/service.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.26.0 +// protoc-gen-go v1.27.1 // protoc v3.15.7 // source: service.proto From c006cfe5fbae2bfdfb396ffdb489371f31100c4d Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Tue, 30 Nov 2021 14:12:47 -0700 Subject: [PATCH 20/31] compact blocks should include Orchard transactions In previous commit, I forgot to include Orchard transactions in compact blocks (so they will now include transactions with either Sapling or Orchard transactions). --- parser/block.go | 4 ++-- parser/transaction.go | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/parser/block.go b/parser/block.go index 5aea760..ebbfba7 100644 --- a/parser/block.go +++ b/parser/block.go @@ -62,7 +62,7 @@ func (b *Block) GetDisplayPrevHash() []byte { // HasSaplingTransactions indicates if the block contains any Sapling tx. func (b *Block) HasSaplingTransactions() bool { for _, tx := range b.vtx { - if tx.HasSaplingElements() { + if tx.HasShieldedElements() { return true } } @@ -118,7 +118,7 @@ func (b *Block) ToCompact() *walletrpc.CompactBlock { // Only Sapling transactions have a meaningful compact encoding saplingTxns := make([]*walletrpc.CompactTx, 0, len(b.vtx)) for idx, tx := range b.vtx { - if tx.HasSaplingElements() { + if tx.HasShieldedElements() { saplingTxns = append(saplingTxns, tx.ToCompact(idx)) } } diff --git a/parser/transaction.go b/parser/transaction.go index b5aef01..42c9b8f 100644 --- a/parser/transaction.go +++ b/parser/transaction.go @@ -369,10 +369,11 @@ func (tx *Transaction) Bytes() []byte { return tx.rawBytes } -// HasSaplingElements indicates whether a transaction has +// HasShieldedElements indicates whether a transaction has // at least one shielded input or output. -func (tx *Transaction) HasSaplingElements() bool { - return tx.version >= 4 && (len(tx.shieldedSpends)+len(tx.shieldedOutputs)) > 0 +func (tx *Transaction) HasShieldedElements() bool { + nshielded := len(tx.shieldedSpends) + len(tx.shieldedOutputs) + len(tx.orchardActions) + return tx.version >= 4 && nshielded > 0 } // ToCompact converts the given (full) transaction to compact format. From 13de3232b347673de0ca479b18b4f73a9f3896cf Mon Sep 17 00:00:00 2001 From: Dimitris Apostolou Date: Fri, 5 Nov 2021 10:22:29 +0200 Subject: [PATCH 21/31] Fix typos --- cmd/root.go | 2 +- docs/docker-compose-setup.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index e049db0..461a8c7 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -394,7 +394,7 @@ func initConfig() { } else { // Look in the current directory for a configuration file viper.AddConfigPath(".") - // Viper auto appends extention to this config name + // Viper auto appends extension to this config name // For example, lightwalletd.yml viper.SetConfigName("lightwalletd") } diff --git a/docs/docker-compose-setup.md b/docs/docker-compose-setup.md index 7b090cc..4592041 100644 --- a/docs/docker-compose-setup.md +++ b/docs/docker-compose-setup.md @@ -10,7 +10,7 @@ docker plugin install grafana/loki-docker-driver:latest --alias loki --grant-all ## Setup .env file -Copy `.env.example` to `.env` and change any required paramaters. +Copy `.env.example` to `.env` and change any required parameters. | Variable | Usage | | ------------- |:-------------:| @@ -100,7 +100,7 @@ This should then be taken to the `Zcashd node exporter` dashboard. If all goes as planned, the dashboard should start populating data from the container services. -If there are an issues, you can view all the `docker-compose` services under the `Explore` section. +If there are any issues, you can view all the `docker-compose` services under the `Explore` section. # Viewing container logs From c7b1be9fd29266479455a629380a92b495cae295 Mon Sep 17 00:00:00 2001 From: teor Date: Mon, 6 Dec 2021 13:29:57 +1000 Subject: [PATCH 22/31] Update list of RPC methods in README --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c93f06a..ee1fdc9 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,17 @@ The `zcashd` can be configured to run `mainnet` or `testnet` (or `regtest`). If It's necessary to run `zcashd --reindex` one time for these options to take effect. This typically takes several hours, and requires more space in the `.zcash` data directory. Lightwalletd uses the following `zcashd` RPCs: +- `getinfo` - `getblockchaininfo` +- `getbestblockhash` +- `z_gettreestate` - `getblock` - `getrawtransaction` -- `getaddresstxids` - `sendrawtransaction` +- `getrawmempool` +- `getaddresstxids` +- `getaddressbalance` +- `getaddressutxos` ## Lightwalletd From 83bb19918fb36a5f6d5013fba7938daf3d73db1d Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Thu, 9 Dec 2021 15:22:06 -0700 Subject: [PATCH 23/31] restore gRPC GetMempoolTx This was removed as part of PR #358 (commit "remove gRPC GetMempoolTx") but should not have been, so it's being restored. --- frontend/frontend_test.go | 55 +++++ frontend/service.go | 131 ++++++++++++ walletrpc/service.pb.go | 392 +++++++++++++++++++++-------------- walletrpc/service.proto | 15 ++ walletrpc/service_grpc.pb.go | 85 +++++++- 5 files changed, 515 insertions(+), 163 deletions(-) diff --git a/frontend/frontend_test.go b/frontend/frontend_test.go index d1af75a..65a2c26 100644 --- a/frontend/frontend_test.go +++ b/frontend/frontend_test.go @@ -540,3 +540,58 @@ func TestNewZRPCFromConf(t *testing.T) { t.Fatal("NewZRPCFromClient unexpected success") } } + +func TestMempoolFilter(t *testing.T) { + txidlist := []string{ + "2e819d0bab5c819dc7d5f92d1bfb4127ce321daf847f6602", + "29e594c312eee49bc2c9ad37367ba58f857c4a7387ec9715", + "d4d090e60bf9141c6573f0598b84cc1f9817543e55a4d84d", + "d4714779c6dd32a72077bd79d4a70cb2153b552d7addec15", + "9839c1d4deca000656caff57c1f720f4fbd114b52239edde", + "ce5a28854a509ab309faa433542e73414fef6e903a3d52f5", + } + exclude := []string{ + "98aa", // common prefix (98) but no match + "19", // no match + "29", // one match (should not appear) + "d4", // 2 matches (both should appear in result) + "ce5a28854a509ab309faa433542e73414fef6e903a3d52f5", // exact match + "ce5a28854a509ab309faa433542e73414fef6e903a3d52f500", // extra stuff ignored + } + expected := []string{ + "2e819d0bab5c819dc7d5f92d1bfb4127ce321daf847f6602", + "9839c1d4deca000656caff57c1f720f4fbd114b52239edde", + "d4714779c6dd32a72077bd79d4a70cb2153b552d7addec15", + "d4d090e60bf9141c6573f0598b84cc1f9817543e55a4d84d", + } + actual := MempoolFilter(txidlist, exclude) + if len(actual) != len(expected) { + t.Fatal("mempool: wrong number of filter results") + } + for i := 0; i < len(actual); i++ { + if actual[i] != expected[i] { + t.Fatal(fmt.Sprintf("mempool: expected: %s actual: %s", + expected[i], actual[i])) + } + } + // If the exclude list is empty, return the entire mempool. + actual = MempoolFilter(txidlist, []string{}) + expected = []string{ + "29e594c312eee49bc2c9ad37367ba58f857c4a7387ec9715", + "2e819d0bab5c819dc7d5f92d1bfb4127ce321daf847f6602", + "9839c1d4deca000656caff57c1f720f4fbd114b52239edde", + "ce5a28854a509ab309faa433542e73414fef6e903a3d52f5", + "d4714779c6dd32a72077bd79d4a70cb2153b552d7addec15", + "d4d090e60bf9141c6573f0598b84cc1f9817543e55a4d84d", + } + if len(actual) != len(expected) { + t.Fatal("mempool: wrong number of filter results") + } + for i := 0; i < len(actual); i++ { + if actual[i] != expected[i] { + t.Fatal(fmt.Sprintf("mempool: expected: %s actual: %s", + expected[i], actual[i])) + } + } + +} diff --git a/frontend/service.go b/frontend/service.go index 0b6e095..bf86825 100644 --- a/frontend/service.go +++ b/frontend/service.go @@ -12,6 +12,7 @@ import ( "errors" "io" "regexp" + "sort" "strconv" "strings" "sync/atomic" @@ -391,6 +392,134 @@ func (s *lwdStreamer) GetMempoolStream(_empty *walletrpc.Empty, resp walletrpc.C return err } +// Key is 32-byte txid (as a 64-character string), data is pointer to compact tx. +var mempoolMap *map[string]*walletrpc.CompactTx +var mempoolList []string + +// Last time we pulled a copy of the mempool from zcashd. +var lastMempool time.Time + +func (s *lwdStreamer) GetMempoolTx(exclude *walletrpc.Exclude, resp walletrpc.CompactTxStreamer_GetMempoolTxServer) error { + if time.Now().Sub(lastMempool).Seconds() >= 2 { + lastMempool = time.Now() + // Refresh our copy of the mempool. + params := make([]json.RawMessage, 0) + result, rpcErr := common.RawRequest("getrawmempool", params) + if rpcErr != nil { + return rpcErr + } + err := json.Unmarshal(result, &mempoolList) + if err != nil { + return err + } + newmempoolMap := make(map[string]*walletrpc.CompactTx) + if mempoolMap == nil { + mempoolMap = &newmempoolMap + } + for _, txidstr := range mempoolList { + if ctx, ok := (*mempoolMap)[txidstr]; ok { + // This ctx has already been fetched, copy pointer to it. + newmempoolMap[txidstr] = ctx + continue + } + txidJSON, err := json.Marshal(txidstr) + if err != nil { + return err + } + // The "0" is because we only need the raw hex, which is returned as + // just a hex string, and not even a json string (with quotes). + params := []json.RawMessage{txidJSON, json.RawMessage("0")} + result, rpcErr := common.RawRequest("getrawtransaction", params) + if rpcErr != nil { + // Not an error; mempool transactions can disappear + continue + } + // strip the quotes + var txStr string + err = json.Unmarshal(result, &txStr) + if err != nil { + return err + } + + // conver to binary + txBytes, err := hex.DecodeString(txStr) + if err != nil { + return err + } + tx := parser.NewTransaction() + txdata, err := tx.ParseFromSlice(txBytes) + if len(txdata) > 0 { + return errors.New("extra data deserializing transaction") + } + newmempoolMap[txidstr] = &walletrpc.CompactTx{} + if tx.HasShieldedElements() { + newmempoolMap[txidstr] = tx.ToCompact( /* height */ 0) + } + } + mempoolMap = &newmempoolMap + } + excludeHex := make([]string, len(exclude.Txid)) + for i := 0; i < len(exclude.Txid); i++ { + excludeHex[i] = hex.EncodeToString(parser.Reverse(exclude.Txid[i])) + } + for _, txid := range MempoolFilter(mempoolList, excludeHex) { + tx := (*mempoolMap)[txid] + if len(tx.Hash) > 0 { + err := resp.Send(tx) + if err != nil { + return err + } + } + } + return nil +} + +// Return the subset of items that aren't excluded, but +// if more than one item matches an exclude entry, return +// all those items. +func MempoolFilter(items, exclude []string) []string { + sort.Slice(items, func(i, j int) bool { + return items[i] < items[j] + }) + sort.Slice(exclude, func(i, j int) bool { + return exclude[i] < exclude[j] + }) + // Determine how many items match each exclude item. + nmatches := make([]int, len(exclude)) + // is the exclude string less than the item string? + lessthan := func(e, i string) bool { + l := len(e) + if l > len(i) { + l = len(i) + } + return e < i[0:l] + } + ei := 0 + for _, item := range items { + for ei < len(exclude) && lessthan(exclude[ei], item) { + ei++ + } + match := ei < len(exclude) && strings.HasPrefix(item, exclude[ei]) + if match { + nmatches[ei]++ + } + } + + // Add each item that isn't uniquely excluded to the results. + tosend := make([]string, 0) + ei = 0 + for _, item := range items { + for ei < len(exclude) && lessthan(exclude[ei], item) { + ei++ + } + match := ei < len(exclude) && strings.HasPrefix(item, exclude[ei]) + if !match || nmatches[ei] > 1 { + tosend = append(tosend, item) + } + } + return tosend +} + func getAddressUtxos(arg *walletrpc.GetAddressUtxosArg, f func(*walletrpc.GetAddressUtxosReply) error) error { for _, a := range arg.Addresses { if err := checkTaddress(a); err != nil { @@ -501,6 +630,8 @@ func (s *DarksideStreamer) Reset(ctx context.Context, ms *walletrpc.DarksideMeta if err != nil { return nil, err } + mempoolMap = nil + mempoolList = nil return &walletrpc.Empty{}, nil } diff --git a/walletrpc/service.pb.go b/walletrpc/service.pb.go index 55a3d56..c3ba2a7 100644 --- a/walletrpc/service.pb.go +++ b/walletrpc/service.pb.go @@ -857,6 +857,53 @@ func (x *Balance) GetValueZat() int64 { return 0 } +type Exclude struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Txid [][]byte `protobuf:"bytes,1,rep,name=txid,proto3" json:"txid,omitempty"` +} + +func (x *Exclude) Reset() { + *x = Exclude{} + if protoimpl.UnsafeEnabled { + mi := &file_service_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Exclude) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Exclude) ProtoMessage() {} + +func (x *Exclude) ProtoReflect() protoreflect.Message { + mi := &file_service_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Exclude.ProtoReflect.Descriptor instead. +func (*Exclude) Descriptor() ([]byte, []int) { + return file_service_proto_rawDescGZIP(), []int{14} +} + +func (x *Exclude) GetTxid() [][]byte { + if x != nil { + return x.Txid + } + return nil +} + // The TreeState is derived from the Zcash z_gettreestate rpc. type TreeState struct { state protoimpl.MessageState @@ -873,7 +920,7 @@ type TreeState struct { func (x *TreeState) Reset() { *x = TreeState{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[14] + mi := &file_service_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -886,7 +933,7 @@ func (x *TreeState) String() string { func (*TreeState) ProtoMessage() {} func (x *TreeState) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[14] + mi := &file_service_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -899,7 +946,7 @@ func (x *TreeState) ProtoReflect() protoreflect.Message { // Deprecated: Use TreeState.ProtoReflect.Descriptor instead. func (*TreeState) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{14} + return file_service_proto_rawDescGZIP(), []int{15} } func (x *TreeState) GetNetwork() string { @@ -952,7 +999,7 @@ type GetAddressUtxosArg struct { func (x *GetAddressUtxosArg) Reset() { *x = GetAddressUtxosArg{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[15] + mi := &file_service_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -965,7 +1012,7 @@ func (x *GetAddressUtxosArg) String() string { func (*GetAddressUtxosArg) ProtoMessage() {} func (x *GetAddressUtxosArg) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[15] + mi := &file_service_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -978,7 +1025,7 @@ func (x *GetAddressUtxosArg) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAddressUtxosArg.ProtoReflect.Descriptor instead. func (*GetAddressUtxosArg) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{15} + return file_service_proto_rawDescGZIP(), []int{16} } func (x *GetAddressUtxosArg) GetAddresses() []string { @@ -1018,7 +1065,7 @@ type GetAddressUtxosReply struct { func (x *GetAddressUtxosReply) Reset() { *x = GetAddressUtxosReply{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[16] + mi := &file_service_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1031,7 +1078,7 @@ func (x *GetAddressUtxosReply) String() string { func (*GetAddressUtxosReply) ProtoMessage() {} func (x *GetAddressUtxosReply) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[16] + mi := &file_service_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1044,7 +1091,7 @@ func (x *GetAddressUtxosReply) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAddressUtxosReply.ProtoReflect.Descriptor instead. func (*GetAddressUtxosReply) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{16} + return file_service_proto_rawDescGZIP(), []int{17} } func (x *GetAddressUtxosReply) GetAddress() string { @@ -1100,7 +1147,7 @@ type GetAddressUtxosReplyList struct { func (x *GetAddressUtxosReplyList) Reset() { *x = GetAddressUtxosReplyList{} if protoimpl.UnsafeEnabled { - mi := &file_service_proto_msgTypes[17] + mi := &file_service_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1113,7 +1160,7 @@ func (x *GetAddressUtxosReplyList) String() string { func (*GetAddressUtxosReplyList) ProtoMessage() {} func (x *GetAddressUtxosReplyList) ProtoReflect() protoreflect.Message { - mi := &file_service_proto_msgTypes[17] + mi := &file_service_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1126,7 +1173,7 @@ func (x *GetAddressUtxosReplyList) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAddressUtxosReplyList.ProtoReflect.Descriptor instead. func (*GetAddressUtxosReplyList) Descriptor() ([]byte, []int) { - return file_service_proto_rawDescGZIP(), []int{17} + return file_service_proto_rawDescGZIP(), []int{18} } func (x *GetAddressUtxosReplyList) GetAddressUtxos() []*GetAddressUtxosReply { @@ -1223,126 +1270,133 @@ var file_service_proto_rawDesc = []byte{ 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x65, 0x73, 0x22, 0x25, 0x0a, 0x07, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x22, 0x79, 0x0a, 0x09, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x0a, - 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, - 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x72, 0x65, - 0x65, 0x22, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, - 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x45, 0x6e, - 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x78, - 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, - 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x14, - 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, - 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, - 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, - 0x22, 0x6b, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, - 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0c, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x52, - 0x0c, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x32, 0xc2, 0x0a, - 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, - 0x6d, 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, - 0x61, 0x69, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, - 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x08, 0x47, 0x65, 0x74, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, - 0x6d, 0x70, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0d, - 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x21, 0x2e, - 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, - 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, - 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x0e, 0x47, 0x65, 0x74, - 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x63, 0x61, - 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, - 0x72, 0x70, 0x63, 0x2e, 0x54, 0x78, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0x25, 0x2e, 0x63, - 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, - 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x72, 0x61, - 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, - 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, - 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, - 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, - 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x78, 0x69, 0x64, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x61, 0x73, - 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, - 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, - 0x1a, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x12, 0x47, - 0x65, 0x74, 0x54, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, - 0x65, 0x12, 0x22, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, - 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, - 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x54, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x72, - 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, - 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6c, 0x61, - 0x6e, 0x63, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x5b, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x65, - 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x1c, 0x2e, 0x63, 0x61, - 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, - 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, - 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, - 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, + 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x22, 0x1d, 0x0a, 0x07, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, + 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, + 0x04, 0x74, 0x78, 0x69, 0x64, 0x22, 0x79, 0x0a, 0x09, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x0a, 0x06, + 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, + 0x22, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, + 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x65, 0x69, + 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, + 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x45, 0x6e, 0x74, + 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x45, + 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, + 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x14, 0x0a, + 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, + 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, + 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, + 0x6b, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, + 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0c, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x52, 0x0c, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x32, 0x98, 0x0b, 0x0a, + 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x54, 0x72, 0x65, - 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x41, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x12, 0x29, 0x2e, 0x63, 0x61, + 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, + 0x70, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0d, 0x47, + 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x21, 0x2e, 0x63, + 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, + 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x1a, + 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x54, + 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x63, 0x61, 0x73, + 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, + 0x70, 0x63, 0x2e, 0x54, 0x78, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0x25, 0x2e, 0x63, 0x61, + 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, + 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, + 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, + 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x23, + 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, + 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x54, 0x78, 0x69, 0x64, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x61, 0x73, 0x68, + 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, + 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, + 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x12, 0x47, 0x65, + 0x74, 0x54, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, + 0x12, 0x22, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, + 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6c, + 0x61, 0x6e, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x72, 0x65, + 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, + 0x63, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x54, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, + 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x78, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, + 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, + 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x10, + 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, + 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x25, + 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, + 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0c, 0x47, 0x65, 0x74, + 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, + 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, + 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, + 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, + 0x63, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, + 0x0f, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, + 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, - 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, - 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, - 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x15, 0x47, 0x65, 0x74, - 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x73, + 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, + 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, + 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, + 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, + 0x72, 0x67, 0x1a, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2b, 0x2e, - 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, - 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, - 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, - 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x21, 0x2e, - 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, - 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x64, 0x49, 0x6e, 0x66, 0x6f, - 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x2e, 0x63, 0x61, 0x73, - 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, - 0x70, 0x63, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x23, 0x2e, 0x63, 0x61, - 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, - 0x72, 0x70, 0x63, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0xba, 0x02, 0x00, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, + 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x64, + 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, + 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, + 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, + 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, + 0x1f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, 0x67, 0x68, 0x74, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, + 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1357,7 +1411,7 @@ func file_service_proto_rawDescGZIP() []byte { return file_service_proto_rawDescData } -var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 18) +var file_service_proto_msgTypes = make([]protoimpl.MessageInfo, 19) var file_service_proto_goTypes = []interface{}{ (*BlockID)(nil), // 0: cash.z.wallet.sdk.rpc.BlockID (*BlockRange)(nil), // 1: cash.z.wallet.sdk.rpc.BlockRange @@ -1373,18 +1427,20 @@ var file_service_proto_goTypes = []interface{}{ (*Address)(nil), // 11: cash.z.wallet.sdk.rpc.Address (*AddressList)(nil), // 12: cash.z.wallet.sdk.rpc.AddressList (*Balance)(nil), // 13: cash.z.wallet.sdk.rpc.Balance - (*TreeState)(nil), // 14: cash.z.wallet.sdk.rpc.TreeState - (*GetAddressUtxosArg)(nil), // 15: cash.z.wallet.sdk.rpc.GetAddressUtxosArg - (*GetAddressUtxosReply)(nil), // 16: cash.z.wallet.sdk.rpc.GetAddressUtxosReply - (*GetAddressUtxosReplyList)(nil), // 17: cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList - (*CompactBlock)(nil), // 18: cash.z.wallet.sdk.rpc.CompactBlock + (*Exclude)(nil), // 14: cash.z.wallet.sdk.rpc.Exclude + (*TreeState)(nil), // 15: cash.z.wallet.sdk.rpc.TreeState + (*GetAddressUtxosArg)(nil), // 16: cash.z.wallet.sdk.rpc.GetAddressUtxosArg + (*GetAddressUtxosReply)(nil), // 17: cash.z.wallet.sdk.rpc.GetAddressUtxosReply + (*GetAddressUtxosReplyList)(nil), // 18: cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList + (*CompactBlock)(nil), // 19: cash.z.wallet.sdk.rpc.CompactBlock + (*CompactTx)(nil), // 20: cash.z.wallet.sdk.rpc.CompactTx } var file_service_proto_depIdxs = []int32{ 0, // 0: cash.z.wallet.sdk.rpc.BlockRange.start:type_name -> cash.z.wallet.sdk.rpc.BlockID 0, // 1: cash.z.wallet.sdk.rpc.BlockRange.end:type_name -> cash.z.wallet.sdk.rpc.BlockID 0, // 2: cash.z.wallet.sdk.rpc.TxFilter.block:type_name -> cash.z.wallet.sdk.rpc.BlockID 1, // 3: cash.z.wallet.sdk.rpc.TransparentAddressBlockFilter.range:type_name -> cash.z.wallet.sdk.rpc.BlockRange - 16, // 4: cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList.addressUtxos:type_name -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply + 17, // 4: cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList.addressUtxos:type_name -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply 5, // 5: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLatestBlock:input_type -> cash.z.wallet.sdk.rpc.ChainSpec 0, // 6: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlock:input_type -> cash.z.wallet.sdk.rpc.BlockID 1, // 7: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlockRange:input_type -> cash.z.wallet.sdk.rpc.BlockRange @@ -1393,28 +1449,30 @@ var file_service_proto_depIdxs = []int32{ 8, // 10: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:input_type -> cash.z.wallet.sdk.rpc.TransparentAddressBlockFilter 12, // 11: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:input_type -> cash.z.wallet.sdk.rpc.AddressList 11, // 12: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:input_type -> cash.z.wallet.sdk.rpc.Address - 6, // 13: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolStream:input_type -> cash.z.wallet.sdk.rpc.Empty - 0, // 14: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:input_type -> cash.z.wallet.sdk.rpc.BlockID - 15, // 15: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg - 15, // 16: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg - 6, // 17: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:input_type -> cash.z.wallet.sdk.rpc.Empty - 9, // 18: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:input_type -> cash.z.wallet.sdk.rpc.Duration - 0, // 19: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLatestBlock:output_type -> cash.z.wallet.sdk.rpc.BlockID - 18, // 20: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlock:output_type -> cash.z.wallet.sdk.rpc.CompactBlock - 18, // 21: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlockRange:output_type -> cash.z.wallet.sdk.rpc.CompactBlock - 3, // 22: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTransaction:output_type -> cash.z.wallet.sdk.rpc.RawTransaction - 4, // 23: cash.z.wallet.sdk.rpc.CompactTxStreamer.SendTransaction:output_type -> cash.z.wallet.sdk.rpc.SendResponse - 3, // 24: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:output_type -> cash.z.wallet.sdk.rpc.RawTransaction - 13, // 25: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:output_type -> cash.z.wallet.sdk.rpc.Balance - 13, // 26: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:output_type -> cash.z.wallet.sdk.rpc.Balance - 3, // 27: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolStream:output_type -> cash.z.wallet.sdk.rpc.RawTransaction - 14, // 28: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:output_type -> cash.z.wallet.sdk.rpc.TreeState - 17, // 29: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList - 16, // 30: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply - 7, // 31: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:output_type -> cash.z.wallet.sdk.rpc.LightdInfo - 10, // 32: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:output_type -> cash.z.wallet.sdk.rpc.PingResponse - 19, // [19:33] is the sub-list for method output_type - 5, // [5:19] is the sub-list for method input_type + 14, // 13: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolTx:input_type -> cash.z.wallet.sdk.rpc.Exclude + 6, // 14: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolStream:input_type -> cash.z.wallet.sdk.rpc.Empty + 0, // 15: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:input_type -> cash.z.wallet.sdk.rpc.BlockID + 16, // 16: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg + 16, // 17: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:input_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosArg + 6, // 18: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:input_type -> cash.z.wallet.sdk.rpc.Empty + 9, // 19: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:input_type -> cash.z.wallet.sdk.rpc.Duration + 0, // 20: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLatestBlock:output_type -> cash.z.wallet.sdk.rpc.BlockID + 19, // 21: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlock:output_type -> cash.z.wallet.sdk.rpc.CompactBlock + 19, // 22: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetBlockRange:output_type -> cash.z.wallet.sdk.rpc.CompactBlock + 3, // 23: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTransaction:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 4, // 24: cash.z.wallet.sdk.rpc.CompactTxStreamer.SendTransaction:output_type -> cash.z.wallet.sdk.rpc.SendResponse + 3, // 25: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressTxids:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 13, // 26: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalance:output_type -> cash.z.wallet.sdk.rpc.Balance + 13, // 27: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTaddressBalanceStream:output_type -> cash.z.wallet.sdk.rpc.Balance + 20, // 28: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolTx:output_type -> cash.z.wallet.sdk.rpc.CompactTx + 3, // 29: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetMempoolStream:output_type -> cash.z.wallet.sdk.rpc.RawTransaction + 15, // 30: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetTreeState:output_type -> cash.z.wallet.sdk.rpc.TreeState + 18, // 31: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxos:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReplyList + 17, // 32: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetAddressUtxosStream:output_type -> cash.z.wallet.sdk.rpc.GetAddressUtxosReply + 7, // 33: cash.z.wallet.sdk.rpc.CompactTxStreamer.GetLightdInfo:output_type -> cash.z.wallet.sdk.rpc.LightdInfo + 10, // 34: cash.z.wallet.sdk.rpc.CompactTxStreamer.Ping:output_type -> cash.z.wallet.sdk.rpc.PingResponse + 20, // [20:35] is the sub-list for method output_type + 5, // [5:20] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name 5, // [5:5] is the sub-list for extension extendee 0, // [0:5] is the sub-list for field type_name @@ -1596,7 +1654,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*TreeState); i { + switch v := v.(*Exclude); i { case 0: return &v.state case 1: @@ -1608,7 +1666,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAddressUtxosArg); i { + switch v := v.(*TreeState); i { case 0: return &v.state case 1: @@ -1620,7 +1678,7 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetAddressUtxosReply); i { + switch v := v.(*GetAddressUtxosArg); i { case 0: return &v.state case 1: @@ -1632,6 +1690,18 @@ func file_service_proto_init() { } } file_service_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetAddressUtxosReply); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_service_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetAddressUtxosReplyList); i { case 0: return &v.state @@ -1650,7 +1720,7 @@ func file_service_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_service_proto_rawDesc, NumEnums: 0, - NumMessages: 18, + NumMessages: 19, NumExtensions: 0, NumServices: 1, }, diff --git a/walletrpc/service.proto b/walletrpc/service.proto index e55d62c..91d0ada 100644 --- a/walletrpc/service.proto +++ b/walletrpc/service.proto @@ -104,6 +104,10 @@ message Balance { int64 valueZat = 1; } +message Exclude { + repeated bytes txid = 1; +} + // The TreeState is derived from the Zcash z_gettreestate rpc. message TreeState { string network = 1; // "main" or "test" @@ -150,6 +154,17 @@ service CompactTxStreamer { rpc GetTaddressBalance(AddressList) returns (Balance) {} rpc GetTaddressBalanceStream(stream Address) returns (Balance) {} + // Return the compact transactions currently in the mempool; the results + // can be a few seconds out of date. If the Exclude list is empty, return + // all transactions; otherwise return all *except* those in the Exclude list + // (if any); this allows the client to avoid receiving transactions that it + // already has (from an earlier call to this rpc). The transaction IDs in the + // Exclude list can be shortened to any number of bytes to make the request + // more bandwidth-efficient; if two or more transactions in the mempool + // match a shortened txid, they are all sent (none is excluded). Transactions + // in the exclude list that don't exist in the mempool are ignored. + rpc GetMempoolTx(Exclude) returns (stream CompactTx) {} + // Return a stream of current Mempool transactions. This will keep the output stream open while // there are mempool transactions. It will close the returned stream when a new block is mined. rpc GetMempoolStream(Empty) returns (stream RawTransaction) {} diff --git a/walletrpc/service_grpc.pb.go b/walletrpc/service_grpc.pb.go index a0d6cb6..b23cc80 100644 --- a/walletrpc/service_grpc.pb.go +++ b/walletrpc/service_grpc.pb.go @@ -32,6 +32,16 @@ type CompactTxStreamerClient interface { GetTaddressTxids(ctx context.Context, in *TransparentAddressBlockFilter, opts ...grpc.CallOption) (CompactTxStreamer_GetTaddressTxidsClient, error) GetTaddressBalance(ctx context.Context, in *AddressList, opts ...grpc.CallOption) (*Balance, error) GetTaddressBalanceStream(ctx context.Context, opts ...grpc.CallOption) (CompactTxStreamer_GetTaddressBalanceStreamClient, error) + // Return the compact transactions currently in the mempool; the results + // can be a few seconds out of date. If the Exclude list is empty, return + // all transactions; otherwise return all *except* those in the Exclude list + // (if any); this allows the client to avoid receiving transactions that it + // already has (from an earlier call to this rpc). The transaction IDs in the + // Exclude list can be shortened to any number of bytes to make the request + // more bandwidth-efficient; if two or more transactions in the mempool + // match a shortened txid, they are all sent (none is excluded). Transactions + // in the exclude list that don't exist in the mempool are ignored. + GetMempoolTx(ctx context.Context, in *Exclude, opts ...grpc.CallOption) (CompactTxStreamer_GetMempoolTxClient, error) // Return a stream of current Mempool transactions. This will keep the output stream open while // there are mempool transactions. It will close the returned stream when a new block is mined. GetMempoolStream(ctx context.Context, in *Empty, opts ...grpc.CallOption) (CompactTxStreamer_GetMempoolStreamClient, error) @@ -199,8 +209,40 @@ func (x *compactTxStreamerGetTaddressBalanceStreamClient) CloseAndRecv() (*Balan return m, nil } +func (c *compactTxStreamerClient) GetMempoolTx(ctx context.Context, in *Exclude, opts ...grpc.CallOption) (CompactTxStreamer_GetMempoolTxClient, error) { + stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[3], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolTx", opts...) + if err != nil { + return nil, err + } + x := &compactTxStreamerGetMempoolTxClient{stream} + if err := x.ClientStream.SendMsg(in); err != nil { + return nil, err + } + if err := x.ClientStream.CloseSend(); err != nil { + return nil, err + } + return x, nil +} + +type CompactTxStreamer_GetMempoolTxClient interface { + Recv() (*CompactTx, error) + grpc.ClientStream +} + +type compactTxStreamerGetMempoolTxClient struct { + grpc.ClientStream +} + +func (x *compactTxStreamerGetMempoolTxClient) Recv() (*CompactTx, error) { + m := new(CompactTx) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + func (c *compactTxStreamerClient) GetMempoolStream(ctx context.Context, in *Empty, opts ...grpc.CallOption) (CompactTxStreamer_GetMempoolStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[3], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolStream", opts...) + stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[4], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetMempoolStream", opts...) if err != nil { return nil, err } @@ -250,7 +292,7 @@ func (c *compactTxStreamerClient) GetAddressUtxos(ctx context.Context, in *GetAd } func (c *compactTxStreamerClient) GetAddressUtxosStream(ctx context.Context, in *GetAddressUtxosArg, opts ...grpc.CallOption) (CompactTxStreamer_GetAddressUtxosStreamClient, error) { - stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[4], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxosStream", opts...) + stream, err := c.cc.NewStream(ctx, &CompactTxStreamer_ServiceDesc.Streams[5], "/cash.z.wallet.sdk.rpc.CompactTxStreamer/GetAddressUtxosStream", opts...) if err != nil { return nil, err } @@ -317,6 +359,16 @@ type CompactTxStreamerServer interface { GetTaddressTxids(*TransparentAddressBlockFilter, CompactTxStreamer_GetTaddressTxidsServer) error GetTaddressBalance(context.Context, *AddressList) (*Balance, error) GetTaddressBalanceStream(CompactTxStreamer_GetTaddressBalanceStreamServer) error + // Return the compact transactions currently in the mempool; the results + // can be a few seconds out of date. If the Exclude list is empty, return + // all transactions; otherwise return all *except* those in the Exclude list + // (if any); this allows the client to avoid receiving transactions that it + // already has (from an earlier call to this rpc). The transaction IDs in the + // Exclude list can be shortened to any number of bytes to make the request + // more bandwidth-efficient; if two or more transactions in the mempool + // match a shortened txid, they are all sent (none is excluded). Transactions + // in the exclude list that don't exist in the mempool are ignored. + GetMempoolTx(*Exclude, CompactTxStreamer_GetMempoolTxServer) error // Return a stream of current Mempool transactions. This will keep the output stream open while // there are mempool transactions. It will close the returned stream when a new block is mined. GetMempoolStream(*Empty, CompactTxStreamer_GetMempoolStreamServer) error @@ -362,6 +414,9 @@ func (UnimplementedCompactTxStreamerServer) GetTaddressBalance(context.Context, func (UnimplementedCompactTxStreamerServer) GetTaddressBalanceStream(CompactTxStreamer_GetTaddressBalanceStreamServer) error { return status.Errorf(codes.Unimplemented, "method GetTaddressBalanceStream not implemented") } +func (UnimplementedCompactTxStreamerServer) GetMempoolTx(*Exclude, CompactTxStreamer_GetMempoolTxServer) error { + return status.Errorf(codes.Unimplemented, "method GetMempoolTx not implemented") +} func (UnimplementedCompactTxStreamerServer) GetMempoolStream(*Empty, CompactTxStreamer_GetMempoolStreamServer) error { return status.Errorf(codes.Unimplemented, "method GetMempoolStream not implemented") } @@ -551,6 +606,27 @@ func (x *compactTxStreamerGetTaddressBalanceStreamServer) Recv() (*Address, erro return m, nil } +func _CompactTxStreamer_GetMempoolTx_Handler(srv interface{}, stream grpc.ServerStream) error { + m := new(Exclude) + if err := stream.RecvMsg(m); err != nil { + return err + } + return srv.(CompactTxStreamerServer).GetMempoolTx(m, &compactTxStreamerGetMempoolTxServer{stream}) +} + +type CompactTxStreamer_GetMempoolTxServer interface { + Send(*CompactTx) error + grpc.ServerStream +} + +type compactTxStreamerGetMempoolTxServer struct { + grpc.ServerStream +} + +func (x *compactTxStreamerGetMempoolTxServer) Send(m *CompactTx) error { + return x.ServerStream.SendMsg(m) +} + func _CompactTxStreamer_GetMempoolStream_Handler(srv interface{}, stream grpc.ServerStream) error { m := new(Empty) if err := stream.RecvMsg(m); err != nil { @@ -725,6 +801,11 @@ var CompactTxStreamer_ServiceDesc = grpc.ServiceDesc{ Handler: _CompactTxStreamer_GetTaddressBalanceStream_Handler, ClientStreams: true, }, + { + StreamName: "GetMempoolTx", + Handler: _CompactTxStreamer_GetMempoolTx_Handler, + ServerStreams: true, + }, { StreamName: "GetMempoolStream", Handler: _CompactTxStreamer_GetMempoolStream_Handler, From 7c9bbfa9aab26049441d3894e4278d3801e435e3 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Wed, 16 Feb 2022 21:47:43 -0700 Subject: [PATCH 24/31] upgrade protoc toolchain --- go.mod | 3 ++- go.sum | 4 ++++ walletrpc/compact_formats.pb.go | 2 +- walletrpc/darkside.pb.go | 2 +- walletrpc/darkside_grpc.pb.go | 4 ++++ walletrpc/service.pb.go | 2 +- walletrpc/service_grpc.pb.go | 4 ++++ 7 files changed, 17 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index 7aa63b2..1948dea 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,8 @@ require ( golang.org/x/text v0.3.6 // indirect google.golang.org/genproto v0.0.0-20210406143921-e86de6bf7a46 // indirect google.golang.org/grpc v1.37.0 - google.golang.org/protobuf v1.26.0 + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 // indirect + google.golang.org/protobuf v1.27.1 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.51.0 gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8 // indirect diff --git a/go.sum b/go.sum index 7b13508..8798e7c 100644 --- a/go.sum +++ b/go.sum @@ -309,6 +309,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -321,6 +323,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/walletrpc/compact_formats.pb.go b/walletrpc/compact_formats.pb.go index 98f22b2..2109005 100644 --- a/walletrpc/compact_formats.pb.go +++ b/walletrpc/compact_formats.pb.go @@ -5,7 +5,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.15.7 +// protoc v3.19.4 // source: compact_formats.proto package walletrpc diff --git a/walletrpc/darkside.pb.go b/walletrpc/darkside.pb.go index 0fa8b61..1c437b0 100644 --- a/walletrpc/darkside.pb.go +++ b/walletrpc/darkside.pb.go @@ -5,7 +5,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.15.7 +// protoc v3.19.4 // source: darkside.proto package walletrpc diff --git a/walletrpc/darkside_grpc.pb.go b/walletrpc/darkside_grpc.pb.go index ad65f01..0e4e795 100644 --- a/walletrpc/darkside_grpc.pb.go +++ b/walletrpc/darkside_grpc.pb.go @@ -1,4 +1,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.19.4 +// source: darkside.proto package walletrpc diff --git a/walletrpc/service.pb.go b/walletrpc/service.pb.go index c3ba2a7..b3638d1 100644 --- a/walletrpc/service.pb.go +++ b/walletrpc/service.pb.go @@ -5,7 +5,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.27.1 -// protoc v3.15.7 +// protoc v3.19.4 // source: service.proto package walletrpc diff --git a/walletrpc/service_grpc.pb.go b/walletrpc/service_grpc.pb.go index b23cc80..2fb6674 100644 --- a/walletrpc/service_grpc.pb.go +++ b/walletrpc/service_grpc.pb.go @@ -1,4 +1,8 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.2.0 +// - protoc v3.19.4 +// source: service.proto package walletrpc From e53167b6a1748caa8fccd8b588df02386a7b6bb8 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Wed, 16 Feb 2022 21:48:14 -0700 Subject: [PATCH 25/31] go mod tidy && go mod vendor (update dependencies) --- go.mod | 1 - go.sum | 3 - .../protobuf/encoding/prototext/decode.go | 3 - .../protobuf/internal/encoding/text/encode.go | 5 ++ .../protobuf/internal/impl/legacy_message.go | 7 ++ .../protobuf/internal/version/version.go | 4 +- .../reflect/protoregistry/registry.go | 43 ++++++---- .../types/descriptorpb/descriptor.pb.go | 82 ------------------- vendor/modules.txt | 2 +- 9 files changed, 42 insertions(+), 108 deletions(-) diff --git a/go.mod b/go.mod index 1948dea..dc3a617 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,6 @@ require ( golang.org/x/text v0.3.6 // indirect google.golang.org/genproto v0.0.0-20210406143921-e86de6bf7a46 // indirect google.golang.org/grpc v1.37.0 - google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 // indirect google.golang.org/protobuf v1.27.1 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.51.0 diff --git a/go.sum b/go.sum index 8798e7c..384d5a9 100644 --- a/go.sum +++ b/go.sum @@ -309,8 +309,6 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0 h1:TLkBREm4nIsEcexnCjgQd5GQWaHcqMzwQV0TX9pq8S0= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.2.0/go.mod h1:DNq5QpG7LJqD2AamLZ7zvKE0DEpVl2BSEVjFycAAjRY= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -321,7 +319,6 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= diff --git a/vendor/google.golang.org/protobuf/encoding/prototext/decode.go b/vendor/google.golang.org/protobuf/encoding/prototext/decode.go index 8fb1d9e..179d6e8 100644 --- a/vendor/google.golang.org/protobuf/encoding/prototext/decode.go +++ b/vendor/google.golang.org/protobuf/encoding/prototext/decode.go @@ -744,9 +744,6 @@ func (d decoder) skipValue() error { // Skip items. This will not validate whether skipped values are // of the same type or not, same behavior as C++ // TextFormat::Parser::AllowUnknownField(true) version 3.8.0. - if err := d.skipValue(); err != nil { - return err - } } } } diff --git a/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go b/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go index aa66bdd..da289cc 100644 --- a/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go +++ b/vendor/google.golang.org/protobuf/internal/encoding/text/encode.go @@ -263,3 +263,8 @@ func (e *Encoder) Snapshot() encoderState { func (e *Encoder) Reset(es encoderState) { e.encoderState = es } + +// AppendString appends the escaped form of the input string to b. +func AppendString(b []byte, s string) []byte { + return appendString(b, s, false) +} diff --git a/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go b/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go index 3759b01..029feee 100644 --- a/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go +++ b/vendor/google.golang.org/protobuf/internal/impl/legacy_message.go @@ -440,6 +440,13 @@ func legacyMerge(in piface.MergeInput) piface.MergeOutput { if !ok { return piface.MergeOutput{} } + if !in.Source.IsValid() { + // Legacy Marshal methods may not function on nil messages. + // Check for a typed nil source only after we confirm that + // legacy Marshal/Unmarshal methods are present, for + // consistency. + return piface.MergeOutput{Flags: piface.MergeComplete} + } b, err := marshaler.Marshal() if err != nil { return piface.MergeOutput{} diff --git a/vendor/google.golang.org/protobuf/internal/version/version.go b/vendor/google.golang.org/protobuf/internal/version/version.go index 5879131..14e774f 100644 --- a/vendor/google.golang.org/protobuf/internal/version/version.go +++ b/vendor/google.golang.org/protobuf/internal/version/version.go @@ -52,8 +52,8 @@ import ( // 10. Send out the CL for review and submit it. const ( Major = 1 - Minor = 26 - Patch = 0 + Minor = 27 + Patch = 1 PreRelease = "" ) diff --git a/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go b/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go index 66dcbcd..59f024c 100644 --- a/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go +++ b/vendor/google.golang.org/protobuf/reflect/protoregistry/registry.go @@ -94,7 +94,8 @@ type Files struct { // Note that enum values are in the top-level since that are in the same // scope as the parent enum. descsByName map[protoreflect.FullName]interface{} - filesByPath map[string]protoreflect.FileDescriptor + filesByPath map[string][]protoreflect.FileDescriptor + numFiles int } type packageDescriptor struct { @@ -117,17 +118,16 @@ func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error { r.descsByName = map[protoreflect.FullName]interface{}{ "": &packageDescriptor{}, } - r.filesByPath = make(map[string]protoreflect.FileDescriptor) + r.filesByPath = make(map[string][]protoreflect.FileDescriptor) } path := file.Path() - if prev := r.filesByPath[path]; prev != nil { + if prev := r.filesByPath[path]; len(prev) > 0 { r.checkGenProtoConflict(path) err := errors.New("file %q is already registered", file.Path()) - err = amendErrorWithCaller(err, prev, file) - if r == GlobalFiles && ignoreConflict(file, err) { - err = nil + err = amendErrorWithCaller(err, prev[0], file) + if !(r == GlobalFiles && ignoreConflict(file, err)) { + return err } - return err } for name := file.Package(); name != ""; name = name.Parent() { @@ -168,7 +168,8 @@ func (r *Files) RegisterFile(file protoreflect.FileDescriptor) error { rangeTopLevelDescriptors(file, func(d protoreflect.Descriptor) { r.descsByName[d.FullName()] = d }) - r.filesByPath[path] = file + r.filesByPath[path] = append(r.filesByPath[path], file) + r.numFiles++ return nil } @@ -308,6 +309,7 @@ func (s *nameSuffix) Pop() (name protoreflect.Name) { // FindFileByPath looks up a file by the path. // // This returns (nil, NotFound) if not found. +// This returns an error if multiple files have the same path. func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error) { if r == nil { return nil, NotFound @@ -316,13 +318,19 @@ func (r *Files) FindFileByPath(path string) (protoreflect.FileDescriptor, error) globalMutex.RLock() defer globalMutex.RUnlock() } - if fd, ok := r.filesByPath[path]; ok { - return fd, nil + fds := r.filesByPath[path] + switch len(fds) { + case 0: + return nil, NotFound + case 1: + return fds[0], nil + default: + return nil, errors.New("multiple files named %q", path) } - return nil, NotFound } -// NumFiles reports the number of registered files. +// NumFiles reports the number of registered files, +// including duplicate files with the same name. func (r *Files) NumFiles() int { if r == nil { return 0 @@ -331,10 +339,11 @@ func (r *Files) NumFiles() int { globalMutex.RLock() defer globalMutex.RUnlock() } - return len(r.filesByPath) + return r.numFiles } // RangeFiles iterates over all registered files while f returns true. +// If multiple files have the same name, RangeFiles iterates over all of them. // The iteration order is undefined. func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) { if r == nil { @@ -344,9 +353,11 @@ func (r *Files) RangeFiles(f func(protoreflect.FileDescriptor) bool) { globalMutex.RLock() defer globalMutex.RUnlock() } - for _, file := range r.filesByPath { - if !f(file) { - return + for _, files := range r.filesByPath { + for _, file := range files { + if !f(file) { + return + } } } } diff --git a/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go b/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go index f77239f..abe4ab5 100644 --- a/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go +++ b/vendor/google.golang.org/protobuf/types/descriptorpb/descriptor.pb.go @@ -43,7 +43,6 @@ package descriptorpb import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoiface "google.golang.org/protobuf/runtime/protoiface" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" @@ -829,15 +828,6 @@ func (*ExtensionRangeOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{3} } -var extRange_ExtensionRangeOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use ExtensionRangeOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*ExtensionRangeOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_ExtensionRangeOptions -} - func (x *ExtensionRangeOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -1520,15 +1510,6 @@ func (*FileOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{10} } -var extRange_FileOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use FileOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*FileOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_FileOptions -} - func (x *FileOptions) GetJavaPackage() string { if x != nil && x.JavaPackage != nil { return *x.JavaPackage @@ -1776,15 +1757,6 @@ func (*MessageOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{11} } -var extRange_MessageOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use MessageOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*MessageOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_MessageOptions -} - func (x *MessageOptions) GetMessageSetWireFormat() bool { if x != nil && x.MessageSetWireFormat != nil { return *x.MessageSetWireFormat @@ -1930,15 +1902,6 @@ func (*FieldOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{12} } -var extRange_FieldOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use FieldOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*FieldOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_FieldOptions -} - func (x *FieldOptions) GetCtype() FieldOptions_CType { if x != nil && x.Ctype != nil { return *x.Ctype @@ -2030,15 +1993,6 @@ func (*OneofOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{13} } -var extRange_OneofOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use OneofOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*OneofOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_OneofOptions -} - func (x *OneofOptions) GetUninterpretedOption() []*UninterpretedOption { if x != nil { return x.UninterpretedOption @@ -2101,15 +2055,6 @@ func (*EnumOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{14} } -var extRange_EnumOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use EnumOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*EnumOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_EnumOptions -} - func (x *EnumOptions) GetAllowAlias() bool { if x != nil && x.AllowAlias != nil { return *x.AllowAlias @@ -2183,15 +2128,6 @@ func (*EnumValueOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{15} } -var extRange_EnumValueOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use EnumValueOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*EnumValueOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_EnumValueOptions -} - func (x *EnumValueOptions) GetDeprecated() bool { if x != nil && x.Deprecated != nil { return *x.Deprecated @@ -2258,15 +2194,6 @@ func (*ServiceOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{16} } -var extRange_ServiceOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use ServiceOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*ServiceOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_ServiceOptions -} - func (x *ServiceOptions) GetDeprecated() bool { if x != nil && x.Deprecated != nil { return *x.Deprecated @@ -2335,15 +2262,6 @@ func (*MethodOptions) Descriptor() ([]byte, []int) { return file_google_protobuf_descriptor_proto_rawDescGZIP(), []int{17} } -var extRange_MethodOptions = []protoiface.ExtensionRangeV1{ - {Start: 1000, End: 536870911}, -} - -// Deprecated: Use MethodOptions.ProtoReflect.Descriptor.ExtensionRanges instead. -func (*MethodOptions) ExtensionRangeArray() []protoiface.ExtensionRangeV1 { - return extRange_MethodOptions -} - func (x *MethodOptions) GetDeprecated() bool { if x != nil && x.Deprecated != nil { return *x.Deprecated diff --git a/vendor/modules.txt b/vendor/modules.txt index b4e4088..25460a7 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -156,7 +156,7 @@ google.golang.org/grpc/serviceconfig google.golang.org/grpc/stats google.golang.org/grpc/status google.golang.org/grpc/tap -# google.golang.org/protobuf v1.26.0 +# google.golang.org/protobuf v1.27.1 google.golang.org/protobuf/encoding/prototext google.golang.org/protobuf/encoding/protowire google.golang.org/protobuf/internal/descfmt From 631bb16404e3d8b045e74a7c5489db626790b2f6 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Wed, 16 Feb 2022 22:15:08 -0700 Subject: [PATCH 26/31] fix orchard action ciphertext comment No functional change. See: https://github.com/zcash/librustzcash/pull/515#discussion_r808624455 --- parser/transaction.go | 8 ++++---- walletrpc/compact_formats.pb.go | 25 ++++++++++++------------- walletrpc/compact_formats.proto | 2 +- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/parser/transaction.go b/parser/transaction.go index 42c9b8f..81df481 100644 --- a/parser/transaction.go +++ b/parser/transaction.go @@ -329,10 +329,10 @@ func (a *action) ParseFromSlice(data []byte) ([]byte, error) { func (p *action) ToCompact() *walletrpc.CompactOrchardAction { return &walletrpc.CompactOrchardAction{ - Nullifier: p.nullifier, - Cmx: p.cmx, - EphemeralKey: p.ephemeralKey, - EncCiphertext: p.encCiphertext[:52], + Nullifier: p.nullifier, + Cmx: p.cmx, + EphemeralKey: p.ephemeralKey, + Ciphertext: p.encCiphertext[:52], } } diff --git a/walletrpc/compact_formats.pb.go b/walletrpc/compact_formats.pb.go index 2109005..75c1627 100644 --- a/walletrpc/compact_formats.pb.go +++ b/walletrpc/compact_formats.pb.go @@ -339,10 +339,10 @@ type CompactOrchardAction struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Nullifier []byte `protobuf:"bytes,1,opt,name=nullifier,proto3" json:"nullifier,omitempty"` // [32] The nullifier of the input note - Cmx []byte `protobuf:"bytes,2,opt,name=cmx,proto3" json:"cmx,omitempty"` // [32] The x-coordinate of the note commitment for the output note - EphemeralKey []byte `protobuf:"bytes,3,opt,name=ephemeralKey,proto3" json:"ephemeralKey,omitempty"` // [32] An encoding of an ephemeral Pallas public key - EncCiphertext []byte `protobuf:"bytes,4,opt,name=encCiphertext,proto3" json:"encCiphertext,omitempty"` // [580] The encrypted contents of the note plaintext + Nullifier []byte `protobuf:"bytes,1,opt,name=nullifier,proto3" json:"nullifier,omitempty"` // [32] The nullifier of the input note + Cmx []byte `protobuf:"bytes,2,opt,name=cmx,proto3" json:"cmx,omitempty"` // [32] The x-coordinate of the note commitment for the output note + EphemeralKey []byte `protobuf:"bytes,3,opt,name=ephemeralKey,proto3" json:"ephemeralKey,omitempty"` // [32] An encoding of an ephemeral Pallas public key + Ciphertext []byte `protobuf:"bytes,4,opt,name=ciphertext,proto3" json:"ciphertext,omitempty"` // [52] The note plaintext component of the encCiphertext field } func (x *CompactOrchardAction) Reset() { @@ -398,9 +398,9 @@ func (x *CompactOrchardAction) GetEphemeralKey() []byte { return nil } -func (x *CompactOrchardAction) GetEncCiphertext() []byte { +func (x *CompactOrchardAction) GetCiphertext() []byte { if x != nil { - return x.EncCiphertext + return x.Ciphertext } return nil } @@ -450,19 +450,18 @@ var file_compact_formats_proto_rawDesc = []byte{ 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x6d, 0x75, 0x12, 0x10, 0x0a, 0x03, 0x65, 0x70, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x65, 0x70, 0x6b, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, - 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x90, 0x01, 0x0a, 0x14, 0x43, + 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0x8a, 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x4f, 0x72, 0x63, 0x68, 0x61, 0x72, 0x64, 0x41, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x75, 0x6c, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x6e, 0x75, 0x6c, 0x6c, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x10, 0x0a, 0x03, 0x63, 0x6d, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x03, 0x63, 0x6d, 0x78, 0x12, 0x22, 0x0a, 0x0c, 0x65, 0x70, 0x68, 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0c, 0x65, 0x70, 0x68, 0x65, 0x6d, - 0x65, 0x72, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x24, 0x0a, 0x0d, 0x65, 0x6e, 0x63, 0x43, 0x69, - 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0d, - 0x65, 0x6e, 0x63, 0x43, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x42, 0x1b, 0x5a, - 0x16, 0x6c, 0x69, 0x67, 0x68, 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x65, 0x72, 0x61, 0x6c, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, + 0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, + 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, 0x67, 0x68, 0x74, + 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, + 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/walletrpc/compact_formats.proto b/walletrpc/compact_formats.proto index 566545f..f2129f2 100644 --- a/walletrpc/compact_formats.proto +++ b/walletrpc/compact_formats.proto @@ -62,5 +62,5 @@ message CompactOrchardAction { bytes nullifier = 1; // [32] The nullifier of the input note bytes cmx = 2; // [32] The x-coordinate of the note commitment for the output note bytes ephemeralKey = 3; // [32] An encoding of an ephemeral Pallas public key - bytes encCiphertext = 4; // [580] The encrypted contents of the note plaintext + bytes ciphertext = 4; // [52] The note plaintext component of the encCiphertext field } From 26df6f09920b7daaa1f4b7e6bdd728c7de334d27 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Mon, 11 Apr 2022 23:58:00 -0600 Subject: [PATCH 27/31] add V5 transaction (orchard) parsing test --- parser/transaction.go | 3 - parser/transaction_test.go | 112 +++++++++++++++++++++++++++++++++++++ testdata/tx_v5.json | 14 +++++ 3 files changed, 126 insertions(+), 3 deletions(-) create mode 100644 testdata/tx_v5.json diff --git a/parser/transaction.go b/parser/transaction.go index 81df481..e59367e 100644 --- a/parser/transaction.go +++ b/parser/transaction.go @@ -483,9 +483,6 @@ func (tx *Transaction) parseV5(data []byte) ([]byte, error) { if tx.nVersionGroupID != 0x26A7270A { return nil, errors.New(fmt.Sprintf("version group ID %d must be 0x26A7270A", tx.nVersionGroupID)) } - if tx.consensusBranchID != 0x37519621 { - return nil, errors.New("unknown consensusBranchID") - } if !s.Skip(4) { return nil, errors.New("could not skip nLockTime") } diff --git a/parser/transaction_test.go b/parser/transaction_test.go index 4b6a372..dd9498a 100644 --- a/parser/transaction_test.go +++ b/parser/transaction_test.go @@ -2,3 +2,115 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or https://www.opensource.org/licenses/mit-license.php . package parser + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "os" + "testing" +) + +// Some of these values may be "null" (which translates to nil in Go) in +// the test data, so we have *_set variables to indicate if the corresponding +// variable is non-null. (There is an "optional" package we could use for +// these but it doesn't seem worth pulling it in.) +type TxTestData struct { + Tx string + Txid string + Version int + NVersionGroupId int + NConsensusBranchId int + Tx_in_count int + Tx_out_count int + NSpendsSapling int + NoutputsSapling int + NActionsOrchard int +} + +// https://jhall.io/posts/go-json-tricks-array-as-structs/ +func (r *TxTestData) UnmarshalJSON(p []byte) error { + var t []interface{} + if err := json.Unmarshal(p, &t); err != nil { + return err + } + r.Tx = t[0].(string) + r.Txid = t[1].(string) + r.Version = int(t[2].(float64)) + r.NVersionGroupId = int(t[3].(float64)) + r.NConsensusBranchId = int(t[4].(float64)) + r.Tx_in_count = int(t[7].(float64)) + r.Tx_out_count = int(t[8].(float64)) + r.NSpendsSapling = int(t[9].(float64)) + r.NoutputsSapling = int(t[10].(float64)) + r.NActionsOrchard = int(t[14].(float64)) + return nil +} + +func TestV5TransactionParser(t *testing.T) { + // The raw data are stored in a separate file because they're large enough + // to make the test table difficult to scroll through. They are in the same + // order as the test table above. If you update the test table without + // adding a line to the raw file, this test will panic due to index + // misalignment. + s, err := os.ReadFile("../testdata/tx_v5.json") + if err != nil { + t.Fatal(err) + } + + var testdata []json.RawMessage + err = json.Unmarshal(s, &testdata) + if err != nil { + t.Fatal(err) + } + if len(testdata) < 3 { + t.Fatal("tx_vt.json has too few lines") + } + testdata = testdata[2:] + for _, onetx := range testdata { + var txtestdata TxTestData + + err = json.Unmarshal(onetx, &txtestdata) + if err != nil { + t.Fatal(err) + } + t.Logf("txid %s", txtestdata.Txid) + rawTxData, _ := hex.DecodeString(txtestdata.Tx) + + tx := NewTransaction() + rest, err := tx.ParseFromSlice(rawTxData) + if err != nil { + t.Fatalf("%v", err) + } + if len(rest) != 0 { + t.Fatalf("Test did not consume entire buffer, %d remaining", len(rest)) + } + if bytes.Equal(tx.cachedTxID, []byte(txtestdata.Txid)) { + t.Fatal("txid") + } + if tx.version != uint32(txtestdata.Version) { + t.Fatal("version miscompare") + } + if tx.nVersionGroupID != uint32(txtestdata.NVersionGroupId) { + t.Fatal("nVersionGroupId miscompare") + } + if tx.consensusBranchID != uint32(txtestdata.NConsensusBranchId) { + t.Fatal("consensusBranchID miscompare") + } + if len(tx.transparentInputs) != int(txtestdata.Tx_in_count) { + t.Fatal("tx_in_count miscompare") + } + if len(tx.transparentOutputs) != int(txtestdata.Tx_out_count) { + t.Fatal("tx_out_count miscompare") + } + if len(tx.shieldedSpends) != int(txtestdata.NSpendsSapling) { + t.Fatal("NSpendsSapling miscompare") + } + if len(tx.shieldedOutputs) != int(txtestdata.NoutputsSapling) { + t.Fatal("NOutputsSapling miscompare") + } + if len(tx.orchardActions) != int(txtestdata.NActionsOrchard) { + t.Fatal("NActionsOrchard miscompare") + } + } +} diff --git a/testdata/tx_v5.json b/testdata/tx_v5.json new file mode 100644 index 0000000..9b0ed18 --- /dev/null +++ b/testdata/tx_v5.json @@ -0,0 +1,14 @@ +[ + ["From https://github.com/zcash-hackworks/zcash-test-vectors/blob/master/transaction_v5.py"], + ["tx, txid, version, nVersionGroupId, nConsensusBranchId, lock_time, nExpiryHeight, tx_in_count, tx_out_count, nSpendsSapling, nOutputsSapling, valueBalanceSapling, anchorSapling, bindingSigSapling, nActionsOrchard, flagsOrchard, valueBalanceOrchard, anchorOrchard, proofsOrchard, bindingSigOrchard"], + ["050000800a27a7265d7a8f732d9e945b0ce84f13010000000000000000000000000000000000000000000000000000000000000000ffffffff06040ce84f1300ffffffff021f5c73459faf0000016304311792382f000008636553ac530051510000040aec52303bb9bfb5651cd7f15b1c95e618d4cc05f74f837d61144f8fb58f33b1a67993db8dd99714cd92b572420bd27b9d4e30a73594bf5098421c69378af124fc87ca5388f256cca20b2b6e65f3536731185a7d55172d086db8ebf19f2fcf9d531db5fdb9b18d95e94aff57ffa2eac33656d59af80d06a745f44ab023752c35eb40f19c4b1ad1aa3f7239c4fe0fc2cf39fcada75318bee7805fcfed8d7f2d1d3d5a57efc2e1e9b01a035587d5fb1a38e01d94903d3c3e0ad3360c1d3710acd20b183e31d49f25c9a138f49b1a537edcf04be34a9851a7af9db6990ed83dd64af3597c04323ea51b0052ad8084a8b9da948d320dadd64f5431e61ddf658d24ae67c22c8d1309131fc00fe7f235734276d38d47f1e191e00c7a1d48af046827591e9733a97fa6b679f3dc601d008285edcbdae69ce8fc1be4aac00ff2711ebd931de518856878f73476f21a482ec9378365c8f7393c94e2885315eb4671098b79535e790fe53e29fef2b3766697ac32b4f473f468a008e72389fc03880d780cb07fcfaabe3f1a84b27db59a4a153d882d2b2103596555ed9494c6ac893c49723833ec8926c1039586a7afcf4a0d9c731e985d99589c8bb838e8aaf745533ed9e8ae3a1cd074a51a20da8aba18d1dbebbc862ded42435e92476930d069896cff30eb414f727b89e001afa2fb8dc3436d75a4a6f26572504b192232ecb9f0c02411e52596bc5e90457e745939ffedbd12863ce71a02af117d417adb3d15cc54dcb1fce467500c6b8fb86b12b56da9c382857deecc40a98d5f2935395ee4762dd21afdbb5d47fa9a6dd984d567db2857b927b7fae2db587105415d4642789d38f50b8dbcc129cab3d17d19f3355bcf73cecb8cb8a5da01307152f13936a270572670dc82d39026c6cb4cd4b0f7f5aa2a4f5a5341ec5dd715406f2fdd2afa733f5f641c8c21862a1bafce2609d9eecfa158cfb5cd79f88008e315dc7d8388e76c1782fd2795d18a763624c25fa959cc97489ce75745824b77868c53239cfbdf73caec65604037314faaceb56218c6bd30f8374ac13386793f21a9fb80ad03bc0cda4a44946c00e1b1a1df0e5b87b5bece477a709649e950060591394812951e1fe3895b8cc3d14d2cf6556df6ed4b4ddd3d9a69f53357169bb2c92f3879947dbb05811548b937bc17345340deaa3cba669cdd9c34b7b6206d0814e671a7b92723e5ca1513d6ab82ad0229407bbc48985675e3f874a4139ec3c34ecf2b762f7fb006a613b8a136e7481f044efab0cb0e6cea0734a10faf64fa483a23dbd3e6ada2aa9352abe0acd963f5dd5b5010d3d025f0287c4cf11c00ed167ff3cfb0df35137fe56369599909e63c5d9b68e13cf329b23d3a8e61ac101756d9fa4bd0f7d2ddaacb6b0f86a2658e0a07a05ac5b950051cd24c47a88d13d659ba2a46ca1830816d09cd7646f76f716abec5de07fe9b523410806ea6f288f8736c23357c85f45791e1708029d9824d90704607f387a03e49bf9836574431345a7877efaa8a08e73081ef8d62cb780ab6883a50a0d470190dfba10a857f82842d3825b3d6da0573d316eb160dc0b716c48fbd467f75b780149ae8808f4e68f50c0536acddf6f1aeab016b6bc1ec144b4e553acfd670f77e755fc88e0677e31ba459b44e307768958fe3789d41c2b1ff434cb30e15914f01bc6bc2307b488d2556d7b7380ea4ffd712f6b02fe806b94569cd4059f396bf29b99d0a40e5e1711ca944f72d436a102fca4b97693da0b086fe9d2e7162470d02e0f05d4bec9512bfb3f38327296efaa74328b118c27402c70c3a90b49ad4bbc68e37c0aa7d9b3fe17799d73b841e751713a02943905aae0803fd69442eb7681ec2a05600054e92eed555028f21b6a155268a2dd6640a69301a52a38d4d9f9f957ae35af7167118141ce4c9be0a6a492fe79f1581a155fa3a2b9dafd82e650b386ad3a08cb6b83131ac300b0846354a7eef9c410e4b62c47c5426907dfc6685c5c99b7141ac626ab4761fd3f41e728e1a28f89db89ffdeca364dd2f0f0739f0534556483199c71f189341ac9b78a269164206a0ea1ce73bfb2a942e7370b247c046f8e75ef8e3f8bd821cf577491864e20e6d08fd2e32b555c92c661f19588b72a89599710a88061253ca285b6304b37da2b5294f5cb354a894322848ccbdc7c2545b7da568afac87ffa005c312241c2d57f4b45d6419f0d2e2c5af33ae243785b325cdab95404fc7aed70525cddb41872cfcc214b13232edc78609753dbff930eb0dc156612b9cb434bc4b693392deb87c530435312edcedc6a93237c8521aae7a1d481596abc73c4963a012089770eaf4107ef6bb934a6a76a93599d4125b2747abbbfde1659922863734c56d7372af1eb86852f2a732104b1b4ef573e186efd8ab0e444a7340ac451a311ffb7e4f4f36f97f2c5dd5317be1af9c543405e53451a4a2a671a118ef66886f4a8472d144d99f8b8d1dedaa90771469f4202c2851fe308cfeef1f70d2698056143aa55e15b3f83501c585906fab9009695ba0b2f10eea6468cc6e20a66f826e3d14c5006f0563887f5e1289be1b2004caca8d3f34d6e84bf59c1e04619a7c23a996941d889e4622a9b9b1d59d5e319094318cd405ba27b7e2c084762d31453ec4549a4d97729d033460fcf89d6494f2ffd789e98082ea5ce9534b3acd60fe49e37e4f666931677319ed89f85588741b3128901a93bd78e4be0225a9e2692c77c969ed0176bdf9555948cbd5a332d045de6ba6bf4490adfe7444cd467a09075417fcc0062e49f008c51ad4227439c1b4476ccd8e97862dab7be1e8d399c05ef27c6e22ee273e15786e394c8f1be31682a30147963ac8da8d41d804258426a3f70289b8ad19d8de13be4eebe3bd4c8a6f55d6e0c373d456851879f5fbc282db9e134806bff71e11bc33ab75dd6ca067fb73a043b646a7cf39cab4928386786d2f24141ee120fdc34d6764eafc66880ee0204f53cc1167ed20b43a52dea3ca7cff8ef35cd8e6d7c111a68ef44bcd0c1513ad47ca61c659cc5d325b440f6b9f59aff66879bb6688fd2859362b182f207b3175961f6411a493bffd048e7d0d87d82fe6f990a2b0a25f5aa0111a6e68f37bf6f3ac2d26b84686e569d58d99c1383597fad81193c4c1b16e6a90e2d507cdfe6fbdaa86163e9cf5de3100fbca7e8da047b090db9f37952fbfee76af61668190bd52ed490e677b515d014384af07219c7c0ee7fc7bfc79f325644e4df4c0d7db08e9f0bd024943c705abff8994bfa605cfbc7ed746a7d3f7c37d9e8bdc433b7d79e08a12f738a8f0dbddfef2f2657ef3e47d1b0fd11e6a13311fb799c79c641d9da43b33e7ad012e28255398789262275f1175be8462c01491c4d842406d0ec4282c9526174a09878fe8fdde33a29604e5e5e7b2a025d6650b97dbb52befb59b1d30a57433b0a351474444099daa371046613260dcaba6838db07787ea3d88b9e9e83b40ddb5430c60acdb32764bbba34a5e0f82e851ac95dbf5543f81adf62c6566bbac4fcbeb1837570f544d6359eb23faf30a94542176db1892be6253efcb6075f3e5af8be3bb0ad5bfaeaf7f8bf4b53bdf3238a4032dd34606c9cf9f3dd33e576f05cd1dd6811c6298757d77d9e810abdb22f02f0bcffd06f2ed2a2137f1f1f63b854d6338f7113ca98549d844985043ea0b6d09d3e5629ea19737388613d38a34fd0f6e50ee5a0cc9677177f50028c141378187bd2819403fc534f80076e9380cb4964d3b6b45819d3b8e9caf54f051852d671bf8c1ffde2d1510756418cb4810936aa57e6965d6fb656a760b7f19adf96c173488552193b147ee58858033dac7cd0eb204c06490bbdedf5f7571acb2ebe76acef3f2a01ee987486dfe6c3f0a5e234c127258f97a28fb5d164a8176be946b8097d0e317287f33bf9c16f9a545409ce29b1f4273725fc0df02a04ebae178b3414fb0a82d50deb09fcf4e6ee9d180ff4f56ff3bc1d3601fc2dc90d814c3256f4967d3a8d64c83fea339c51f5a8e5801fbb97835581b602465dee04b5922c2761b54245bec0c9eef2db97d22b2b3556cc969fbb13d06509765a52b3fac54b93f421bf08e18d52ddd52cc1c8ca8adfaccab7e5cc2f4573fbbf8239bb0b8aedbf8dad16282da5c9125dba1c059d0df8abf621078f02d6c4bc86d40845ac1d59710c45f07d585eb48b32fc0167ba256e73ca3b9311c62d109497957d8dbe10aa3e866b40c0baa2bc492c19ad1e6372d9622bf163fbffeaeee796a3cd9b6fbbfa4d792f34d7fd6e763cd5859dd26833d21d9bc5452bd19515dff9f4995b35bc0c1f876e6ad11f2452dc9ae85aec01fc56f8cbfda75a7727b75ebbd6bbffb43b63a3b1b671e40feb0db002974a3c3b1a788567231bf6399ff89236981149d423802d2341a3bedb9ddcbac1fe7b6435e1479c72e7089d029e7fbbaf3cf37e9b9a6b776791e4c5e6fda57e8d5f14c8c35a2d270846b9dbe005cda16af4408f3ab06a916eeeb9c9594b70424a4c1d171295b6763b22f47f80b53ccbb904bd68fd65fbd3fbdea1035e98c21a7dbc91a9b5bc7690f05ec317c97f8764eb48e911d428ec8d861b708e8298acb62155145155ae95f0a1d1501030052d92ecde9cd040082e1034ea67bc8ae97404b0c50b2a04f559e49950afcb0ef462a2ae024b0f0226dfd73684b88c7fbe92d02b68f759c4752663cd7b97a14943649305521326bde085630864629291bae25ff8822a14c4b666a9259ad0dc42a8290ac7bc7f53a16f379f758e5de750f04fd7cad47701c8597f97888bea6fa0bf2999956fbfd0ee68ec36e4688809ae231eb8bc4369f3901538a5d79cb93f48757a5b569027f0de993c6445427d8b6e0c9ad8ce6520969b926d62e9596fa825c6bf21aff9e68625a192440ea06828123d97884806f15657bcd1d9b57545b51146fb8658f2c7d5cc0e092d412b6442eff8b6d03a405b15f0f71cd453bb3d18cf7079f10000ebc8a1e886f4050afd8fe94e97d2e9e850674a261f6e7c22da542de35a59aa367676a3fb238a5c70aecddc3c0140882e1b6259408b1fd7bf017e79ec3656d7eed23d8e48bf84cbc000a807899973eb93c1e40bdc093f8cca0607d4d4580b85cda8f5047d6e8552aeca691e78db112e63c205266b61f87c23507551eb092d1744524d304fe6b8fd3b4010348611abdcbd41fbe7da2728cfe5bf7ec9a94a57942910d56b9654145a5c2e76eb7242cc6f97db73b315e665a6cb4c19b070fe11f8db6897b2b30b1cd4018388e1a910f0fc41f30", "bd4a365a38d72376e814e0b9321025d99a287a47e45d082c4cc03b417f50e967", 5, 648488714, 1938782813, 1536466477, 324003852, 1, 2, 0, 0, 0, null, null, 4, 0, 1352303960316242, "22f0b024e02a2a46efb0fc0a95499e554fa0b2500c4b4097aec87ba64e03e182", "fd73684b88c7fbe92d02b68f759c4752663cd7b97a14943649305521326bde085630864629291bae25ff8822a14c4b666a9259ad0dc42a8290ac7bc7f53a16f379f758e5de750f04fd7cad47701c8597f97888bea6fa0bf2999956fbfd0ee68ec36e4688809ae231eb8bc4369f", "be7da2728cfe5bf7ec9a94a57942910d56b9654145a5c2e76eb7242cc6f97db73b315e665a6cb4c19b070fe11f8db6897b2b30b1cd4018388e1a910f0fc41f30"], + ["050000800a27a726877a592566819d375ba5c30f000002f8453d9ff66f3b016d2f89fd9bfe11772ea294afedb9c401a3b64039caf3d81c9699a69fdf1c5ac7732146ee5e1d6b6ca9b9180f964cc9d0878ae1373524d7d510e58227df6de9d30d271867640177b0f1856e28d5c8afb095ef6184fed65158905f96ee25c806a48276f1a9d89a5e475ca3023d288608b2feaef46b4a0233f07b9468287f0eb0c10c4b132520194d3d8d5351fc10d09c15c8cc101aa1663bbfe877f63f40a5ca0d67f6e554124739f805af876aeede53aa8b0f8e5604a73c300043d0779a0d8afeff4fe843b76ef6f223f0f7c894f38f7ab780dfd75f669c8c06cffa43eb47565a509022eeaea4c0ce1fa6f085092b04979489172b3ef8194a798df5724d6b05f1ae000013a08d612bca8a8c31443c10346dbf61de8475c0bbec5104b47556af3d514458e2321d146071789d2335934a680614e83562f82dfd405b54a45eb32c165448d4d5d61ca2859585369f53f1a137e9e82b67b8fdaf01bda54a317311896ae10280a032440c420a421e944d1e952b70d5826cd3b08b7db9630fe4fd5f22125de840fcc40b98038af11d55be25432597b4b65b9ec1c7a8bbfd052cbf7e1c1785cbd09dad963d6f8a5dcc40def40797342113ba206fae8ebe4f3bc3caf69259e462eff9ba8b3f4bfaa1300c26925a8729cd32915bfc966086f0d5560bbe32a598c22adfb48cef72ba5d4287c0cefbacfd8ce195b4963c34a94bba7a175dae4bbe3ef4863d53708915090f47a068e227433f9e49d3aa09e356d8d66d0c0121e91a3c4aa3f27fa1b63396e2b41db908fdab8b18cc7304e94e970568f9421c0dbbbaf84598d972b0534f48a5e52670436aaa776ed2482ad703430201e53443c36dcfdb4e4dd1a51fce98c020f5d3f61b731bafaef0c9bc08316f762de247f5f7cec6f049858cbad3fa6378263db954c5ab47b0c3902618dcd61ec7fbd3ba61c9fb045276f9381617db3519cdbb1bf40c42cd7ea0ba537a5e06aa94bf4ee1e8ae786f27140181dfc0c56ffacdf6ecfad22e10b2d2cd1b692fc2f5d9c0824ad0375603180f2a988522d35ec9bf5f42f3b632c21460b245e319b6ea714085fd6e9e3d693276e9643dc6fd335fe85e00769b7ba7de670873b07569392968f1a115e7040c00", "44fc7cb148821cc205f50561cb5c97b7050dcae6b984de6ce6d93cb008548e08", 5, 648488714, 626621063, 933069158, 264480091, 0, 0, 2, 0, -411158922670013, "505a5647eb43facf068c9c665fd7df80b77a8ff394c8f7f023f2f66eb743e84f", "180f2a988522d35ec9bf5f42f3b632c21460b245e319b6ea714085fd6e9e3d693276e9643dc6fd335fe85e00769b7ba7de670873b07569392968f1a115e7040c", 0, null, 0, null, null, null], + ["050000800a27a726e7917666cf5bc20e48bef119029b9b8a0e39c3df28cb9582ea338601cdc481b32fb82adeebb3dade25d1a3df20c37e712506635165ac526a0f30ddcb91fe9004e1e83294a6c9203d94e8dc2cbb449de4155032604e47997016b304fd437d82350465635251b743a0a900000000", "34cb0d127ec0a4c67c3594d098b170708cad95e63e885de001c0e145ce033841", 5, 648488714, 1719046631, 247618511, 435273288, 2, 0, 0, 0, 0, null, null, 0, null, 0, null, null, null], + ["050000800a27a726f2e336b4ae307bb398b83514000002a95ec688aed0c93cfb8c6da9e14556f1de9b55677da307d859d6845c3be0e953fb00b9f62416c8b9c0f7228f510729e0be3f305313d77f7379dc2af24869c6c74ee4471498861d192f0ff0f508285dab6b6a36ccf7d12256cc76b95503720ac62e9a3415299a464121e09e4db8d4798576a033aa9467784ba15782b8093289967c680467694bc9709d32916c97e8006cbb07ba0e4180a3738038c374c4cce8f37fd8767bea1a24ae7bed65b4afdc8f1278c30e2db98fd172730ac6bbed4f112702fc82cc401c6a6233649f875a16306db092b22aabda93a3857a38922aa06b970b7da3a516c173be1c513323e119f635e8209a074b216b7023fadc2d25949c9003020dc5fc0884616b4393ea2dcc6e3c920aa4f46591e0a9131cc7bd95372979a79e4a5fa87f0a956f5b85509960285c22627c59483a5a4c28cce4b156e551406a7ee8355656a21e43e38ce129fdadb759eddfa08f00fc8e567cef93c6792d01df05e6d580f4d5d48df042451a33590d3e8cf49b2627218f0c292fa66ada945fa55bb23548e33a83a562957a3149a993cc472362298736a8b778d97ce423013d64b32cd172efa551bf7f368f04bdaec6091a3004a757598b801dcf675cb83e43a53ae8b254d333bcda20d4817d3477abfba25bb83df5949c126f149b1d99341e4e6f9120f4d41e629185002c72c012c414d2382a6d47c7b3deaba770c400ca96b2814f6b26c3ef17429f1a98c85d83db20efad48be8996fb1bff591efff360fe1199056c56e5feec61a7b8b9f699d6012c2849232f329fef95c7af370098ffe4918e0ca1df47f275867b739e0a514d3209325e217045927b479c1ce2e5d54f25488cad1513e3f44a21266cfd841633327dee6cf810fbf7393e317d9e53d1be1d5ae7839b66b943b9ed18f2c530e975422332c3439cce49a29f2a336a4851263c5e9bd13d731109e844b7f8c392a5c1dcaa2ae5f50ff63fab9765e016702c35a67cd7364d3fab552fb349e35c15c50250453fd18f7b855992632e2c76c0fbf1ef963ea80e3223de3277bc559251725829ec03f213ba8955cab2822ff21a9b0a4904d668fcd77224bde3dd01f6ffc4828f6b64230b35c6a049873494276ea1d7ed5e92cb4f90ba83a9e49601b194042f2900d99d312d7b70508cf176066d154dbe96ef9d4367e4c840e4a17b5e5122e8ebe2158a3c5f4cbae21ea3fa1ae6c25a9462ebcbb0fd5f14554bc97747c33e34da90c816d8d0d50bfe37618c5812891484fa259322c15092d4155d8696d6f12f24fd364496b3be0871ca3dd9625348a614b59bde45885649bae36de34def8fcec85343475d9734f2ed22512ac2d9a3514cadb37b17630cbd2d7dac18ba42158ec5d14429a585fe718dd9dd8c75e7e656f1e43fbfb51db2eced64e1c6047042ce710514553e1cbf7cd887d0c945a62dbc428da838ebed4ad70ea13232ba1e0f0c3db0a64c155bfd29f4cc477e66f130d630430dcc0104899b4f9f46eb090ef7fc90b479abf61f93955ee00e6a1848f1ab14ad334f2b68035808cdf1bb9e9d9a816baf728a955b960b7701fa626687dc3c9cba646337b53e29816e9482ddf5578a8768aae477fce410ac2d5de6095861c111d7feb3e6bb4fbb5a54955495972798350a253f05f66c2ecfcbc0ed43f5ec2e6d8dba15a51254d97b1821107c07dd9a16ef8406f943e282b95d4b362530c913d6ba421df6027de5af1e4745d5868106954be6c1962780a2941072e95131b1679df0637625042c37d48ffb152e5ebc185c8a2b7d4385f1c95af937df78dfd8757fab434968b0b57c66574468f160b447ac8221e5060676a842a1c6b7172dd3340f764070ab1fe091c5c74c95a5dc043390723a4c127da14cdde1dc2675a62340b3e6afd0522a31de26e7d1ec3a9c8a091ffdc75b7ecfdc7c12995a5e37ce3488bd29f8629d68f696492448dd526697476dc061346ebe3f677217ff9c60efce943af28dfd3f9e59692598a6047c23c4c01400f1ab5730eac0ae8d5843d5051c376240172af218d7a1ecfe65b4f75100638983c14de4974755dade8018c9b8f4543fb095961513e67c61dbc59c607f9b51f8d09bdcad28bcfb9e5d2744ea8848b2623ac07f8ef61a81a35910b8a1baf39a919a7b60bc604d63185f759221d847cc54a22765a4c33475b5791e9af3271fc8d9350667090d8184ec50522d804f23c4fb44ffa481bc92ae408d1b9f2b131904f9705c59e2f4bde7a3b2c085d93fd2abc5e14d163001a12f51938d021afa92239b873dc6c357eaa8af4ee6d00540657fe32914103b5d98f68bd3e2b5359f08ccd88d0c811e4c31fbb49f3a90bbd05dce62f344e7077593159ae35050b04c9e6b86bc432dc8b048c73c0018ca5b69411297732a4e1aa99a928c71155cdc9258d8faffe777fbb34c0ab8cc3d67466c0a88dd4ccad18a07a8d1068df5b629e5718d0f6d72d08268d2cf7773b6ba2a5f664847bf707f2fc10c98f2f006ec22ccb5a8c8b7c40c7c2d49a6639b9f2ce33c25c04bc461e744dfa536b00d94baddf4f4d14044c695a33881477df124f0fcf206a9fb2e65e304cdbf0c4d2390170c130ab849c2f22b5cdd3921640c8cf1976ae1010b0dfd9cb2543e45f99749cc4d61f2e8aabfe98bd905fa39951b33ea769c45ab9531c57209862ad12fd76ba4807e65417b6cd12fa8ec916f013ebb8706a96effeda06c4be24b04846392e9d1e6930eae01facd32b04a95b205526cfcb4c4e1cc955175b3e8de1f5d81b18669692350aaa1a1d797617582e54d7a5b57a683b32fb1098062dad7b0c2eb518f6862e83db25e3dbaf7aed504de932acb99d735992ce62bae9ef893ff6acc0ffcf8e3483e146b9d49dd8c7835f43a37dca0787e3ec9f6605223d5ba7ae0ab9025b73bc03f7fac36c009a56d4d95d1e81d3b3ebca7e54cc1a12d127b57c8138976e791013b015f06a624f521b6ee04ec980893c7e5e01a336203594094f82833d7445fe2d09130f61b8900e26b311a5f6154637a757441c460c8aa737bf345b67d9771a654e58b9a015296aecb4b30b435792d13ee040d450ed12eef980e93d99dde911f66c7e300fe7e3d3d350811b20f97cf1ced37de916df3671c02c69ee9c82b1f17c426e1ee87edba5cfb2695c54aa61d358d7fda20dde0ade55b662e663396ed6ed0fb28076ae1e9b27829ce2ac5efd0b399a8b448be6504294ee6b3c1c6a5342d7c01ae9d8ad3070c2b1a91573af5e0c5e4cbbf4acdc6b54c9272200d9970250c17c1036f06085c41858ed3a0c48150bc697e4a695fef335f7ad07e1a46dc767ff822db70e6669080b9816b2232c81a4c66cc586abfe1eaa8ca6cf41fc3c3e6c7b886fb6dac9f4822b4fc6fff9d0513d61a21c80a377671d135a668a0ae2bb934c82c4142da69d12ca7de9a7df706400ec79878d868e17e8f71ea31495af819a016cc419ee7a24fd277856aa42501e51b012aea9446a2104e93f815a0b3a29b458314f3d8be2b9823d342f46213e942a7e19a46e970b5c506708430317b1bb3b35df68ae33a4926a03e6bfeb5510416fcbb0524c9ca5074156cc5a5d6fe1c995edc60a2f550411aa41e3da3bdcf64bcf04a0510571b936d47e55cec0330ee8dfe73563404f047d7f3a8a3d7743bc554955210f1eb0d08599ea77d5f974d87176d37d98b9c0ad440407209ed6a9f08464d565593e1a63b938536b49244e97d880173b640f2a9d6b0e86578ce7e2ab01fbc42de44642410e4cd9429226fbe0f4608a4142290dc665ef0d6a1c9f02e96add140eca4c49f98c9629b4ee4832fa084c5b2548d0c0490340b8c1cdf0ab25a11469e8d61cb420ef13e8e75b808156b47566be3b9b3a15fc041526e0ac74a87f2456985c32e64768428b4c2b25e3745f009c5dce20b2920f2dbddee21b7836b6dfe41dbe145befddce5dc12ee383c8061e5b977f03d24c397fabaad64a383a86bec05c80e25d88b0e1c68a525d706d6604b2330b6b308138adc13c6c1dde1b21a2ebf4d4dc51e6576b0255d347fe1f229a539e06afdb28e66216286bafe47ff4dbcced51444480a9a5673ece7fac73a0ed41ab0051753a7caa89be3139afd9793b3e02f27f040046595acd47bf13fd0da27f09eda48036d3ee437f2ee8f8606ea97343c33584657f46dba99db5cfe6ca176fab7b0f3bfa0ab61e340c34eb9f17c7ec2be03b180f0bb6f434c2a6542e00e84373f4f4649cda32bf686666143f622aa480460b5afac518607cd9af8bcd6b58c30127316b25d5ea7bf6b0cab8542ff69d9b2f180be12ed75344a395aa10f852f083ad64ef40e9c0309e9bba54b8cb33c95498a69538d3ae5b25e247098306fa8c74a8ee5bca941531d61aac27aab3dc5617d5606c9577a2a8346e8d85b32b8505775108dc85e2ade2eac1e636e1af4054c8b6f57632df269c3723b320872e4c57b218358dc7e9905bb04edf92edf0df635f3bf361e57a13296e1447af5087872d636e27518a9876e15eb01f5e8ded81892511cc2851b00b832712a6d3ba5666517bcd3567621a7cf8445589653262020c33bf78031b8ee0707de072068c170570327e6d9f5c6ddc335402efc548862f5a07094fd428a7bbc15d7b38d05362c9ca985f58a76647d2be4c2cd6b3d17d6870971d7a098baf72c6f6f1214cf1faae488bd7de259d3415c2f0ddec7457004f35708d1eccccc0df65a04943ad5cbc13f295f000fe056c40b2d88f27dc34cfeb803be3483a9ebf9b5a9026057725d63ead2c0c0ff1fe26ac1e7bdfcd6fad875842d194f331750462c06b8d7982d67995ed5d3ae96a05ae0067f4eb1c7c93231bd39773cbe0a9d66b0c9aa8cff6a376e1f372eac6ac4e46cc0942245d4c2dcf02d7640ffcc5a6ac3a87f5c411551bcc2f26cb94961d53f95ddb19ae930c8d70f031b29a5df99ff36695e802cbcb6b58c1ba7ed5eacfa76414a41ad4a44f71f1b580d34c3a952920b254ab72e6a0d3cacb01d9419a86a1e9b3aacb8e7c68016066bd3ff51abb44cb8f0aad7fb8300139f372183a9ff5ca820b46c0f5ac9a8408e925b626bb21a471fe33e1e0a14d48e4436c734eb0137304243be251cf0cf571b6c8d8441f2ab7cab11bdde3eb54b24de62c4566085520d62a57cf53689c2b1a18eaf2d471d13c1ab39197a0f1e753a73290c7f9f096693a7bd3cce1246c20ef32d10ef9dad195227d7be8aa033b65e51c1a08b8a11d84d0409b734f452aaf0d6b18f50258683d3f9a76d399fd047eee288bb4585851dc93eccc62322924cd13b5dd4eed66ed8d9972d772629ea64742e54733981b006c062468e4bd8f7dd9af698f52ae814634e81d7f3e0c420317caca9ae4811c6af06fe80a8c02ab7a00e18e4a6aa1ea1b76945d2615d43ac118b56c2f2960fe93a025f13ec91ffc6d2c353699abb092dedc065db8fa214dbc46466f897b88c58b30152133aa3831af37c74d99e9e36ff7011d3238305691508a2c3a43e755dc081b511d6482a7db65fa9699ea87ff47099ed3637dbb0a3d0ef79796a8ef1e4d94d42b4bc2b4a038ae6e46b24cfc84153d31eaf895063a5ca959be63f37f2ba0d432366736d8632fce072b6ae5b6f3fd59d3faff638275a992fefc87e60d44c2cadc2b5c494e3e72eb4597c96b40167799a9001a2ed3676a8b403ae25ffd772f7081e9a32bcc1c5e2edd4e2a6576b783cce3aae11fa432262548856183ee682d5dc31beb38f061cbdeca7021a444e2dd417df26dcd220f2b731772b439e96d614e1facb486c7a7d5171b1de359f6ad3a96f649c969102a1964fb4b4a1a4279c68e6c372e42187d754e804a61653092069fb9b6d25266890808b015df28c801065da6febdc1a56bfd002625acfaa5373fde149c1cfc3649b4869696d44ecb12479c5ebef995f10029f8b530eeb3fdc2e50e8757fc0bb9e263023db82f878d9ac7ffb0bd4391df1d879899a3ef57bfd0d1f7755648edd85bb052a6edf71cd2628c987429f36dc505ccc43f30e7a869c9e255e2af9fcf30c121796d190000960cb6fe2f1bf246118b498f3247f9d484c73cf09393039e45326b8ffffb3e7e6159c46699f100792d4672950348a90552e45943beeacf03f3216f94e274d63d637d9f190e8a266cdeef153531bba208468306600446dfa578295a310f2d7bc5102040ebfba047be0f04c1e20e604a2807e46186fa5dd477bedeeed4b05e585fbebb48faf58f1b65dca2497204126daa907983d5065339131b57e693a8e1bbdcced799ee128f12543b6f5f407c84a108964e2de74b6ea55b4cb8f6f9bee98b10d415109455f48b776082dc30b1f9a0371e103ce82a937ccc717d3af2b3c47e0e0bba4e8618be2fc3ae82bff12f48b3f7bfefa31bcdc665c6d7123e95350811375947b055a43db07e03f33627df5c638bfad956ddc1ea7d7620a20f2792f63817a1cf32580d04274234af2a51b56bb68a29e43a954142ba4ca6823bde9053d72fdadbc61ad5936c53fdd7579446d11c44607f41630e4c08915e631771550e9ce1fca2c63fe06b7989d584fa7d782a88c1e7d64b6fbf55e3596af9bcb7585f8c7d3aa5c2082b265249df05701dab031c4bac1ea267a2996a2028d1e6a0f80a3847c531dba96ee65a24189bd2712e40e959664981e58b2a4f951ef8f497dfff2f2f271eab89c628e18b5fcb43882537eaf6ad2a6b1754633caa86bf2c76f3993154fc73e6fbba2210c2743f530a427849a301e00e01129f03a4607f87cbe0762c0b1c65855deba8422ca4b88abeea6a4382cf16ccd6dc7c37c44e549c4534819acd8bb0a02a5fa7a1c1d3806fbc3407fd7da93fd0de6400d3ab8977485cddfbed5932f507b79947adb2fad37615aa717db5f298099f20f263b359a1151a6b75c01365eb154ae42140d6e10342f14f34dc33e07ff0e4d1a6be375b32f84b92e5d81ebb639c4f27e715aa42cc75707d4ebd1bbfbe8f90fc7c953e7a9715e65af8267373d3451674ff084efd92ccf3bcc7aca1467b6327e4f9522b2cc579a7a8fff7ca7cf145dfc13eafc34153b2c3e8afbe53444d0c73b3bd5bc870b01cd457911e356313fd1dafb4c8151634a01aff7cf116d433c3d2b3adda9cebe18f7d172443e5e7b5ac9abe8db2256d7ebe2ff28020939503870597b9a955892c7389650a2d42ec92be723fedf2f2ede5a472aa1e74f33ad41901544edbbe3ac464cf439196015f4f22ac2b8fc01496beab4d45907f479812a259431a2cbc93d4f3b84e4dd366020273a6752e501af6ff1b78ddc817e6ea351d6006becf8d2ffb03990f67774a8137618c21494b0d45ed1920f0e6c4a9d77c1712ac61bd88a8f66e4888ead75163eaf1877138f55432c7ef3f14393909959fb358554e94c7e678ce01aebf94e11bfca88478a3142decaaeb9be86b37be1cb9802cd46d3a90bd7e0a686ac747f2c06b41f92f645ea7423b7b8f885c8827a2dbd0eedee871c1c0f48b8e9b8e4be3789bba5b4ad738e629325ec1bc676d37c716d4a735254381a1e790a38814b250ecac9aaa10c2e7de415edb0806c6da03020a134ca7ecdc8da1bd57a37f55a46940b45b241b1c16ee100927d1bd860d445a9de50d4c384d6e1d00108026c0ea5ebbf0b72fbf5c370bce18d3acbc46599099baae1d802f77333494a7ae130fe86e8f818f9261a2dadb4125229ba0ffc0e7090324430b521a90d224ab7a1024e1d893e7404fedb348e4d5e2235c59a7876a0fc60145c6a009687684460271ee133a437fe52fb6cfba97fcec161df515dde905a24da6d37bdc34044a955e682b47471ca1e8c78c51ed377cd4afa894bd9bd12e707156da0726f7cf5729fabe372160463fe0429244d067489ba5d09472ecd9bcdc4d5e4df101e189db8463eb538307b587deff78de9c73af28080b2fd05003e11d3e1b3299dc9521f8b513badb010e91bfeb91b0b2a6cb129c2e825a597b8fb75bc562d654d62104640dd74e56cd14baaba565b84b845e163d1caef2533c3981637204f96a59c8e8024d9041b2029e94c15245f1a958840ba3f380a4d20f1184e77827de3ff8f3d73459afe241f723c084823230e003d3d21e53501ec0499b083a7dad685c57127f4de64733a880c2db28fdaabf1b542d205f664a35135712711dcccd931a50b9c5661882360d4cac0047681bc2e2b3bf6c99760d7cfb4fa21394377a4551c76d1f75ac03c262054dffd79a9ded05e888958199eea4501e2990a53a5cd2a46a401576588fd7d058a26f28438e5782f45ac1d07f6f6f5ed73741d5785837a6b844b474775718c29dd99084e9f88ef153a8329f532a69017dc3a97ed754367723098e5765840b022897244745fbbbb30a7cb54fa0511166e9544122000610bd2aacbd82325a59b95154ecd82c88d23abd1e20770ffb8aabf83fc0734964ccd411d1c935714e24aab566f4f08424014c4eca91b590f082b473f361c87415d00fe08b49d923d000040cbd3f16f113fd5b757ffd15f41de81ae838860adc88aacc7bd6a00ae0c193f6533a485efde082b5f4d1f7a8ebe7ed82b7b05a8cfe1e373459f1bdcbf9525747e8c9508a555facb798740e0bdf994d9739bbe5538a0ae0f076c582c0f5ba878b99b8249db1d7e95056c98af083d98cb0ed9e3f7436e1c7643766f966b83e999206ebd1393b99f2fd8636242fdc102ffea86c76770dc1117571fd3d0743cfb7ede7404909d1fc519a4605f5826312b2eb37ab367e635e06faf7f3ceae7d3419b1fca265a5519775c53afdbd7501d2840d0b0eb97ee708d33a0315ee9d5c203536891e659a3207960c4879236dc9ae865e23a00a331588218b3add9c06893bd02db9b61191d3b6527a0513cf5337aad2b2b6528224fe8fd3b0a71889a87885e9ca7ce0d8a8738c0657eba5d7a2767bf8031135121b766aa51c143b0cb25b9142c61bd790a80177ff15884ed58dbe0f73dc9bb7a1a470cfaf0f2648bed701d8f7705588763a698a5555ac55b71f1d0251e59d2cb32c002041a089c283f19649968c2498cde5635f350a22b08f552e2ec715b9a6eed6b9aaf047a8ab7193ff5bfd24a89a215be8c9635c29827aad86726c9ade3b265b9086c8b5b75ef56fe4bd8b4d62893895b3f", "4b70db7a920aaac381297968f18912f652819dd70506fa943be6f57c200c4cff", 5, 648488714, 3023496178, 3011195054, 339064984, 0, 0, 2, 2, -1450974927627243, "6d0f8d71e529b6f58d06d1a8078ad1ca4cdd880a6c46673dccb80a4cb3fb77e7", "a9d6b0e86578ce7e2ab01fbc42de44642410e4cd9429226fbe0f4608a4142290dc665ef0d6a1c9f02e96add140eca4c49f98c9629b4ee4832fa084c5b2548d0c", 4, 0, 67699920341246, "3f190cae006abdc7ac8ac8ad608883ae81de415fd1ff57b7d53f116ff1d3cb40", "33a485efde082b5f4d1f7a8ebe7ed82b7b05a8cfe1e373459f1bdcbf9525747e8c9508a555facb798740e0bdf994d9739bbe5538a0ae0f076c582c0f5ba878b99b8249db1d7e95056c98af083d98cb0ed9e3f7436e1c7643766f966b83e999206ebd1393b9", "f350a22b08f552e2ec715b9a6eed6b9aaf047a8ab7193ff5bfd24a89a215be8c9635c29827aad86726c9ade3b265b9086c8b5b75ef56fe4bd8b4d62893895b3f"], + ["050000800a27a726d2734fda64156d7e5e5eae1c00000149a3e853e92a33cd1f43b55e4f72f3a3ca562e814f47bd93789101ca251752535e1aa2a452f3731c8cb65082a622a7c2e0013ea47d0bdd42d6990466649a905c32d58e6777765f22a4116344eeb65b2ec516393ab3751b5356d2b0c9500c0f3e0047de4944cd0501001c83b86f669637e3b2e6c2649397ce88efe2ad362d07c3273017bb5a24daa316469181035bc3660f0b8f9fbe6e40b5e89cb79b063714ca75e72e2e100a10d63bf784df0820ef25f8ef40fe5f05fb95683f9105ff3cb2d219ab76605a064f69219f1dc0d00b3b48642f970dc00cca4b8b43308be18286ec5a4288d600a3785cb622d468a4c6969b3792f2485027d0ad9aa4a9c2cc972f9ee5190a95b1eb058dddd8c08e7d753f5e011b2bcfee1d52c1c4f2cacda30bdb6930653c0cc4486e60e89fa849b32083ba9db453fb8df683cd68754c87daa731f570a7a4060af0ce700d6f7dc13ef1b6302a04bce22e12300fb14c9898938383040afc997925cd573090b7c6aa583669fd49169795447e355fa6965ab623939f0a883d75ef75274be8003e09bb11e98342d60aac922035358f6a1ad6481e52934375457e7fbf8a3b0a93969c224cb87d337886c0e29725918aa27f80b46e782a7fe7e4dc7ad975197e0200", "6191d6fee8bbf112b42cd7d163a3aecdfaeee307e520b8880e8a796235ff4f21", 5, 648488714, 3662640082, 2121078116, 481189470, 0, 0, 1, 0, 287854148836935, "16a3da245abb173027c3072d36ade2ef88ce979364c2e6b2e33796666fb8831c", "3e09bb11e98342d60aac922035358f6a1ad6481e52934375457e7fbf8a3b0a93969c224cb87d337886c0e29725918aa27f80b46e782a7fe7e4dc7ad975197e02", 0, null, 0, null, null, null], + ["050000800a27a726f683933071e348fc523dc91100000000013e3d2dd6f369981df1fb9924862f07fe3a17e3041b9f16aaae76e21ed358d884b8b195a433d454d18257e04f9d85f4e4f790789942f55c200b773ecdd7992c3f1c3b15e64835324463b7923d61886aa3696f0ae0d619304c861af905263b763b4bc5d601d42eef2100d128c2920b092283ac05160b7a84eaa7aab74009e57a0544114fbb7dabf8952314682af2e87cafc6b6fc6ec7cd426aaba543c4e25782118a4af6fa8381f0658819eab483f65b325d5aeda15232cfadec75ab1866e4c0155a9c74a7a57ccf34c483ac7da1588a1b6b9941f11040f94cf78fad89bf11fed69aa0d83105adacdd4e5f04a62424023c9b9e33c4fb7f12bdf21f07f265c537d51c6551f4617b915d21991839c3d0d36393d646e0a8a41509217d0e7d2ca1a0a0d677a3eaca23edeb07b74e652a0bc50c6c083a55d6c7306e74086f4768933aa24873681867a7893d77cb7f29b8c847c583f2d071a686616e206719f761ae39c110442e06163d2b84590360695d4e19849e634f24d9ad396c19ff83ce74f46e645f932e141a41195936c85d514414f112e60b1a2537c38d6dc6c4638305c9bd6c62e366bc63123e3e6dd36eedd3136fce8deeca2aa09a3298a39d83859efc9b2b69cf9a7dee08a98e4be558ac7912fdcb42209075420260f7cad0f2c01f2afe33073f26249d944f7a50dd84839bc3ea7fdee4ed71449cf07533d26e1e27a3efb032c3a3b34bd3092622d2062ae536ef5149c49b5bc9475eafab6e675761008b0daddeecaa604470bbe0fada255d290e92b190c2c2d8c2dee5455d1fa9a9f3db7779b584643464aa8014ba66994de25517f83980e66ee4f62314ae6dbef452d5d38b0a16f3991f36d8a8b39ddc0d5595eed98762878cdf3f4a2edc5cda77d5fe4faf63a15f568a540da57dd9beb6fb1a977ccb91b4d79cb39b28911a29e7bf028ac6103796dfb6b20967239ad373c38c53f6df1823d4950a0283e99b9c06ab2966667c9df677716b0caded818df9e449c072e22f9d98bb0f9b03bd5fd013fcef3ed6a49aeb98720254087ef728e31947ffe8f766e63ee46ff20816d5fa8ff55a26398961490ab9ae366fc5a2d1996ed693ccca82356f600ab099f6eca8bfe645270d3f95edba5b0de7a32819233bcc754a5ce2e5ea036400e1696ed1fcff8f2157fcbdb14f7aeec1285cb80de42146619af02456ae695962fe5e931a6335e79052ecd333e18412db91e15f7cbc70b4cd7e8e3c951f358572e37767e7d52704a6721b30efc41017ae4d231558c5c82cc7dd7e3356c09dc24906f0438dfcc300856ac2ced8f77fa8015736c661e80248aeeb774874aa79d290b8f5027a0a509537fc7c689b7ad86116cfec2647ccaae1c74b416f3e6ae8f7cc60eaaf7b6a590d51544138e1732945603a53462c60e1f6cb0c9ca0390c488224c313269fcd59fcb611fb2d9b4c8fa601bb1cb8d07d797bf5de52bceeb02301c8962ac1fc0491dc81affd6c1ebf89a13d6f290eda5d5cef382215c5e951d71305ef33d9737126d0e662905f12509212d6383e7758f9e8ac8bfd14faac138014ee7251ec1bc60ee960f04d180918a032094f6e108558fc1e78f78f388b6ee2d65fe7b4b365d094459250aaa55444098052ef0d6cfc70d7bc82538cac8cd898de00a7f8590fbf480b316116866ace8583438d8a67b2f8d1f0f931c941ceefcfa0f18e26f4fa45d1be8f3dc4a707133e", "133e20c18e7f8e5c48b54d56da9d6a403e3e03f3ea592dd9b109888358f9b5c4", 5, 648488714, 814973942, 4232635249, 298401106, 0, 0, 0, 0, 0, null, null, 1, 3, -895627753881500, "35631a935efe625969ae5624f09a614621e40db85c28c1ee7a4fb1bdfc57218f", "9052ecd333e18412db91e15f7cbc70b4cd7e8e3c951f358572e37767e7d52704a6721b30efc41017ae4d231558c5c82cc7dd7e3356c09dc24906f0438dfcc300856ac2ced8f77fa8015736c661e80248aeeb774874aa79d290b8f5027a0a509537fc7c689b7ad86116cfec2647ccaae1c74b416f3e6ae8f7cc60eaaf7b6a590d51544138e1732945603a53462c60e1f6cb0c9ca0390c488224c313269fcd59fcb611fb2d9b4c8fa601bb1cb8d07d797bf5de52bceeb02301c8962ac1fc0491dc81affd6c1ebf89a13d6f290eda5d5cef382215c5e951d71305ef33d9737126d0e662905f125092", "8052ef0d6cfc70d7bc82538cac8cd898de00a7f8590fbf480b316116866ace8583438d8a67b2f8d1f0f931c941ceefcfa0f18e26f4fa45d1be8f3dc4a707133e"], + ["050000800a27a72695d2ad596c03d2492343dc1302d6425efb9c1d504e6fd5575340945601fe806f5756acb562f13c0ca1d803a195c2ebb2ef02ac53e6a88dea075ba996d3c336648e8694d3a19d3dca531beb50d4327c5c0c23cb7cfdb08ca7cf2cac6b075100acac535365029cb4ab03798fb67e661002000800636aac51516a5174c8d381ec71030005ac6a526551acc23e3ad324070009655251006365656a6500020f3ee263f592f02decbf089d3167cd94c4fe7e642d5efcb1ddd2bb241571579fa9293d3f632b309d65243ee1e393cd16a7770949746b9312a58d9ef0f7f7c319f356133afe06426839ede7b87d6fe36e4d52dfd192f97395e09cfe4870f1cd100e4fba92c7b606a5cb122f140cf1a3596f2788f3c8b92660f14cb65af5dd23dfdbac1371ecf4b33712fed2292c44f70834cf96c05d58827e69bfc2e696fa0874869c02f3dca11c3b90cb214e68bc1cae039d7a146cdc1d609d7a6b3fd5d461b0951c82cfb3e763fad2d1bc7678cdf82779f8fd5a1ce22a8d3c4547abd959838a46fb80afe01f8ecc9931513b1962ec540856cb189387cfbfcc0f7c68223cba47fb0c9b486e4d99171941f7675a8b46328a3bc109bf07c66d5ede771cc4c74ce80333829191eedc493508a644530a6144f22dcf97525a4cdca1ad71073b080b73ea4549f5401bff4318268e6ad637363157a19a53f123a0b0e16d0b77f02028da464100fde76d83dd0bb224f7b57a00c02f68ae648fdc529957a10490dce1fddbb0904f0d518bb387544019983b616975a78e74d854fddc49b255167b55ef4bee465668b20ea4118ca569ae480e0f6e5e043a357b36d3ab36c861f2278301dce57674d5073b3a6f5103a0793af1b7d46f957e22d8d2583bf181836c3be9930bac8fa460e968aa7109870bbed17df5f888c8ca1467ae17dbbcde31c1105cb5bda88ac6c627002ce21c02140ffe81ec58bf1e6d1bb7aaada41fba0bb588778a7f65202ad811ea73d26c74550395aff75325107c9b3f9ae9dcdcd86ed081a2e7424719a3d185b7e0a43a472e298ac0afdc5287d7ad124cd9405a62cd1ca08b282efef7f928df76e2821a418413eb7ceaa5ff1290b03ec91ce6dd28130c3ab0b23b602bd5be5dc26003aae04b33d7bd2590e90c8c388ea7955122dbaca67b30395a928b57b8575123205ae19152e41e002931b45746198e5dd9571a56a7e0d423ff27989d3eb417ecd3c3093fb82c5658e29624c53219a60cd0a8c4da367e29a71779a73032985a3d1fd03dd4d06e05566f3b84367cf02fdb52e6b514ba80685af3d4499a6f7b4d5940d4fb60c0f2d86057324838294194ca80bb0a1d13cd4cd69ab98304ae2515d5f7699d4abee5c20be609d873511041d5b83ea4f9ba03add3364ef3d92cb1a6dc99169418fbceae95cdc39c9ce1c077f261ecdc26bc089d34c6404846e9c647fcfe98cc6acdbb464f64278ad8ce9d1ae0d415bc0c05245fddaf4ebc8dc703a85cb270f796ad2d937e2ac0d5e0a34821758000aa59c9d4652485294ee0ab29696b21430fa54dcfbf2b9c49d142064209eeeed4d471ffc017d4e20a796b0927804c061b9f4a7091fe015ada68fd8442e01825c88dfe55cf5de38936f7ce25311b902ba97a3c12a95cfa1c3a591b818f60832709d9e4839e410fb36b84f3ac4f070fc35e161978259e5b8edc744d90919aa770bb36215128e582b59641e23852e958eb8fc3c0aa96152ba4f77f138d6a6712a3ae3226015883f81db23e583c869c4c71143a6fffd65e8dfdc50c99a2f1f314cdcc71359e235f1d7dc2b5f38ef7b970843163c03f9dd40a8015efdc8791956a3f3cedd9ea64f8efa7a0815a70381d71467817bd04ca529aede07ff60d176aed0f855a2eaea89eaeaca89358c081826a0812a5bca28be1373f086dbdba7e43e203212c9fed21474ba19a055ffcc179412e893a744832298c5fe24cc6b18667f49b34dfb12379267419a9cb9403d8167d8d1e91d2811a043b29243b069b37587847dc6fcddb1831bd1cc2567ca033ac40f74ab6955f683b12e4e8254e4ea760d38b3f46791c5c4cb12bc7ccb0ed1865f25d601c303f81fb1fa1db48533d3d6b288e4d9a4dff8ec21c96f578399710c825fe7e32f93a8c0743f9ebd54cc151c7610337aebf7e9b915720a54351d49ab8c22fa34998dcf583d4387361ef3ff86f50ec53f49249e4ad349603066fc9c661d69f911dfa7241c8d5792d43c457d5de96523a53d667ec5c4ef9d502a16f1522475896d79bc57833e977171c324dce2a1ea1e4304f49e43ae065e3fb196f76d9b879c7200862ead18dea5fb6a17acea33386eb4ca1b51486a9148fbdf9a95332aa605c0854ab81eba9fafffaee9bc3bd7a3a606a9fdb849c5d82d0a61923c2e5d8aa63a8a50c38bd038772c4143d8b7acfd74e72c04d89248dff20fe8dc5ec2149054ea24164e85f6744ad0cacf1a8b70126f482c092ed9f6127d2050d12e878a79653a1e84daec3ebe62d5f6c4abe5ce90a7fe2e52a8d7846e8edf2f2bce05a037c826f22caad1261467dcfb7d6b6133dc21e8096c7e9f8e9e10c1e3fac4058b682c68e54facae0f9c2dd4d64d9046152b4762332939f17e6aaf7d8b9d358e2218d4e0d69a4f119e1c64e5d5483ce4ba8ece01a8ff2b7ef82d05c0b6e861b915f13ca0eb3ea13d5070807a2cb6680a249ea9c7224392cbc8ab82501b26f112ac789a12a31ad1314e2ede08fad3143af30c27f403bc866c755177852afd0abb90ade1d682726f42008b46ad7f8abdb18117f72641390f086b6e1498be69548527e6ada2b38b9fe121ef670af7437d32536d5cf5c4ab19dd99771582d038104b7e039a376f7acbbeadb34f945beb9d7ca0e4e3d5c5e4eb1d8526ebd13dacb1ba35735c6d04a4555acf4bf115dde42d6e155c1f549790e7ff2ceaf12d3eacbefe1edf26ffaee8051d97765c1ab05b3a2e56a82f4cde52b535815e075b8e7a92cb4797329d8e452b217d9b40801fa9c799dba216c8170c2e381d5e38869e6b5358ce67ba5a97c65852b0a38838ef7781ebe5718addd7662c7d057b4646daa9b4e67b14910984802c2a7e381933c5957907b7fdd0ba2295bff88e9b717e9705580d20c48b76fe78471c99080d61035515456dee9f2c517895172f42655b899de0ae2a2613a0612c469df792b8d34e53fc96acfbfb9a1411fcbc56348c001d6ecdd0a25d620e5c77f0cdc831efd3299094babaf1f3f07da9a390b1d9fc9a08327987adfe9564863fbdfa8f6b46a8841583099afb7870118face76347e40b6fd8cd15582ae8e23be9a0219bc3e4e4546a30d3bbbbd1686086876be0e4c859be71fb58f4fab3d28c0b4f7e75ad1edb7f88946fb40cfa5786a0fcba1303c8347ecee93d46d140bb5f69531d666548b109ce764bead7c87bd4c876494de82db6e5073a6c94f7c099a40d7a31c4a04b69c9fccf3c7dd56f5544776c53b4df7953981d55a96a6dcff9904a90842e5bafec8840c2d255bf5ad61c460f98feb82a10fa1c099f62776798236c5ca7f1e46ebdb2b144d8713e56c772f2c3b860ea5b03a8854bc6e6590d63cc0ea54f10b73ba241bf74b635551a2aaca9687ac5269fd368b26d70a737f267685998a3f7d2637914909c746495d24c498635ef97ac66a400894c09f73488eb7cf33f6dad1666a05f91ad7757965c29936e7fa48d77e89ee0962f58c051d11d055fce204a562de68088a1b2648b8174cbcfc8b5b5cd077115afde18405054e5da9a04310342c5d3b526e0b02c5ca1722badeee23d145e8eb2213fc4af1e450e4d5217c6617008c78f4fb1112f4028a704fc5a9382c6b03e7d8085e906cf84ca2c1207c87a2bce2080a9891668d69b044beced6cda32c229c9117917aa07ddffcd377395cba616d63c0b69c01fcc45391fd5b8763fb96d7ca333a12de3cefa91c6c98f9473b8e104a71293e46374705baf65fa41384ba5c8e0c88a3eb07e0be34daddfabb7b65543b5f39cb2023d46789eb7d989af779e5b8d28385a85b0da2abe07f0c2bb4255fcea03188527a307d409159e90166fac6a070ba05b3e4dbfd3a2bfcc9ee6ed016c0f665be8133b7dc1d86044db0f9db40fb0e9f8bc2e4db5382a8b4f815b4e8434ad0dfbc51a5e9b145e1596cbf4670b7e05dfdafbb03f23461a2d0ec0400b3a5944e20950e8b3df72298aaee8e5939975f939ed5c6e4c400d88775943313cd716da0cb446113c7727a64b58c3f8a0f81189f98005233a81366aee73cec85228ebcfd5ee3c3fb44db76ba243f2842b7b5fc746ae51b0bc4bd4fc9fd833565ea852b92b224f6990318ad8c7d9437e20e2a1f20e818f9057c5abaaa2e5c15b94945cd424c28a5fa385dadfe4907b274d842707db3697a5ae6c8f542e5ecc07fe47350d1014670212efe81fb7c73e8450df814ef6232f7490f63ccf07480f884a66eaffc28fea448d7b401cdae10e7c0c7f9a7b15331969fc8cb36396773de191931c750f6ce5caaf29768ebb27d31282137c414971612739509800e2ab30c3aa84e8ce01c50911754463760b182ad6cc3945b00c3605a6dd2172eead43185eafce8488188ea4e27d0cdf7ddd3085e33f5e115f399fc60b9fcc833c3a3e2e1b9dccbdf548f0a6756395a1bbe218eb7f671e9626bd263e31181a604b506a03b439a7ffe4355892477e2bdf338c62c", "8d11f22615de0b6eb9ce2f5eb9ffa541185aa0722faf86e9421bc32eed59459e", 5, 648488714, 1504563861, 1238500204, 333202211, 2, 3, 0, 2, -1502020902366200, "02a45c01578f5052f17a044e31fef91fc8f53b9bd9e45595b4a8f247b926f32e", "5dde42d6e155c1f549790e7ff2ceaf12d3eacbefe1edf26ffaee8051d97765c1ab05b3a2e56a82f4cde52b535815e075b8e7a92cb4797329d8e452b217d9b408", 1, 3, 1386280728474866, "1333947587d800c4e4c6d59e935f9739598eeeaa9822f73d8b0e95204e94a5b3", "716da0cb446113c7727a64b58c3f8a0f81189f98005233a81366aee73cec85228ebcfd5ee3c3fb44db76ba243f2842b7b5fc746ae51b0bc4bd4fc9fd833565ea852b92b224f6990318ad8c7d9437e20e2a1f20e818f9057c5abaaa2e5c15b94945cd424c28a5fa385dadfe4907b274d842707db3697a5ae6c8f542e5ecc07fe47350d1014670212efe81fb7c73e8450df814ef6232f7490f63ccf07480f884a66eaffc28fea448d7b401cdae10e7c0c7f9a7b15331969fc8cb36396773de191931c750f6ce5caaf29768ebb27d", "5e33f5e115f399fc60b9fcc833c3a3e2e1b9dccbdf548f0a6756395a1bbe218eb7f671e9626bd263e31181a604b506a03b439a7ffe4355892477e2bdf338c62c"], + ["050000800a27a7263922f7d3a56c7103d97d5e1301b5ae2dbb16a3761add053a0f967e6b5bc94211b6547153267c6ee1cad0d974a71088583703ac656333156dad00000003e5013132f6ae2d67e3ec68aa9b80612833941e312c13e67f2f7a2a8abe5f59bbd51a75f37b99763985ddb4efca8e17723d0cc949801d63a64cb2d32373b2c7329f701b4d58be9ea9275245c67e5009264bd2867df4505b4f50318cadb5d5582ab978d21c3fda63337fa364554a0a91e8302e2776fa24ece84683e74876c55e2022148027904a67543b478465351afe5565555fcc3cec5654b15380d778306bb51c11a2e1d184c67c528df92d53aec44a40a4ea2a131b4733cfe45c6b0012c3e9e20975baaecb0232df880bd7d1de13e1349462ec8d5df3e780ffa72eba8a8df7fcf398ec230513ca9d6123f8b9d8178560daf975111955a2bca3423eeefc527be3a8543eb90a5ec02f35a7c64b7dd59a72da0074634e01d2abf3637add77c7350f12b011b294168ec75576e47d169e3938bf6ae2aa8ff7cfba7cacb1f92b6e4c2497bffa9f17cad242fa9c3179c1a3aa81f7361649572c715c25a1f6cd5ace82c00ab2342b9c3cb4fffdda160ca5ab9e9baf2139ef9afbe1b1f309462afce462a79bb9698e22c957c590a753a76b87e009121e06f6a1bf62a08bf435d92e2fffe86e2a9cbba9133a68e4aebf33c38436f2545fc2d52832d165af415b244adc5f57377deedf460aa3beb43419c6b082e835ce84ca13b6908a8813c021de9fa9a44e4c18dcb3d21faabdb41931b2fd497644dc3a1507fa5ac7c76beebbdbd1d49299a55bd49927e9d7f4884e6ed3fd5e4b7cb835b83308964e3c46873fd613317b91d29236ea90e365d162cc051c846d242176daf6d28618ae31fbaae999a93f175c6938e631a081f2c1f3fd782549d3f3245759606d9f92d5548acfeadbaf9caa6b93dc08828d74f6d5fdd83331f0969145955297e69f00fd2987f2da2b94b995fecbe622a735ef7f1207f671629489202bea0b475e51681aa16778b39bd923c98dc6ff8373c79bb17030417bc200c8f0b855acfec179f7674cec2721a10fca693d83cfe5b8cdcc18f81ad617fa26f0dfb83655b8a29a7f834232425e8c474588f18dd326aa396c3e4775e00205fc9e45f7b7d2e6d55dcb90e23ff6b508459aa699bfcbd56f10997764d087408986e73d6e284fea9a23c39311782f86cabff9455e4cf699e5f5d4bc0b3905a4e3bd01c54df864342eeeba5cecb7e67ddedca09f1e2add3ee6e550c5fc6959da03acc03f9728728fe0c28ee873d39ef0290ebc467ab99958278ef64979bf6515ed4a6840b0883a1ea3bd692980208c59dbc62741aea7323c578e8d073df1d27ecd72a8cf0ca546202862e4e8a7d9a4a282866f9a7b2cfc9a56313da0c47a34b7b9cda3ace8185f07c8858b16d753225be959351b64cbb277b83330217efea9171bb2388a5642283fe5b51e233ca6155d101585bc2c40158ac2106e66a26e46423370636876b434a74f8ce8060050b082a79b61bb5d344eb5a1158326ced9a9d9f54fb2fe8f9f05cd111ee46c4710f6f63a62694557ef1b12c88006b67872505f4e883b585907929a2f3fdb0d8f7914c42dde2d2000f5ae02d41821c8e1ee0138ebcb728d7c6c3c80027e437594c670fd6f3908222ee7a1b917f8271abe660e39e051aaa6fca1862276e2baa0fe0b162aebcfe3d9349c8d154bb7ee28212c1baa705d82070d7032f2695d1796809fab41246926af992b6eee95a9a06bc4562c5f2f1b19549500372e7ad579a6d6d78b33153130fb448fb79e8a669db8a0f35cdf9ae5d32d732fc79418e23b451ddc95a22ababb056ec6b5e8ba4f524dfafe875262dd7be41cbbc62420d4ad6df5c9b713604f656088a4485e93be1907d27ac6ec3c57259bd6981d42c1b78a29ad9685e63c494d4129623ea1a7ffec85fa29411073edb2978ef4e469ddd5cda986189995f88d6ab366db019001f5b25288cf860fd998ee573c8cc48aa9efcf9b617e043c329cd1aa1a0ed3a402fb96e336c719e6253cb691aa0db52736626ed1978875888ec76c846bc227272a585317dff0b1148d92d6f5fb7d95336770a7d16fac1add860776cb480221f8fb33d7e4e9b07902d2ff86fdac72096234aed48de892ff7355073bbf0615f67b1100cc2ea3ba3d6c1a1a9087b119baeebfa62bc9f0ec479d99c1a3b158b514d1629db3993f11672a26708e5ad816b547ab7e827d071ba7842b3e90305383896ec4905f70c78b694e6a5a3e4312cd8208132b840f05c714523ca819720ae227fd1acba714fa4fc45fc5398857b40dc14879856f354ba4d2581d0cda54b638ba9d76f9b52d17c8f88ee63f5845b5dcefa4c3479bce9acad18b4aeae03c0eae225d42848bdedd6971b4d264bdc0ee24487e461b1edccfb2d16a6cc84d8a8b5dcb56a9a1ed31e106f73c261d8398b2e72698f9554afe8a1e9d479c18237b9828bca8b98c9d1ba40a719ebc170adfedd969f4ccc164dba045144aaae5cdc65c464f086b3584bd9e34a6290335aa1fbd83d54aaf441e319ea47a862ad0293cedf5dd9edadeee3361ae3a450b88bbd5139a883f4841f2780ec968aa51ceb1de9068fea1a7d767b0678bc97214b31b37bab46b88f27f0448decb31622d0f0f87a855ba54000332031f73abffd46591da0b88723504edb2337230dad2acc0d8bb68bc837a2ff930bff06fde74eb90aae4f60dbb6eb827ea99884acd6285a98892802cf59d5d60d01663387b3ed2723bd6489e9c2c106d4aa2de23ced16c720429c7753a7738ec7d9db8624229edd217b80d74875a14cae4863f139e9c0b131b2a4c28071a38ec61f66801aa5956fcb2a46b9587665b7571aa03481fd8d9d5698f836fc8635e69e3bde42f4ac071328b5409f6e42d790aedd73bc1a2354723b3b819d0637a6fa4663946a30ac5afdd30ce830f6791b4575270a1720f91866e2b86f4788894c8da62d8b91faf520e3bedbc1206a5a5e6efd3dfde0843c3b06757643fc006008838ca473087f8977918cc1b81c9e68e3b888fe6f7c630f1bc7ae188f512842041cada1e05f866d2562dbe09c4b43068f754dad34df0fcfc181f31801a7992d2f16be0211b4a22f62aab64701bf4a4e6d666fc304a5c79c609acc43b00b4864893d37d5007f0c329a4755052577570dd38fac043cd91c12ee34e9cfae392a78bdabd4ee31dc0deb02fe7b1d8b0178ac9513105fcc7e30ba8e016aa36a6b5df5e5a1909f63aba095d9877a8f2dc53f46f6c9b07addf146f4ffa501f9dd3cff924e3010faf504e2b8aca7357acbffec73ac34c1a73160f2cea1e0510f84d2fe2f73b6e921907a1b7b3751213241b2cfaa55a5ea4dd517e7b49d2de8c090843730d2408a2a304aa1e2e1370a6bf6c2bc73ff00d893bc1285efca82599d181f12351f939a94ea8b975c065a91ff257cac7a92385fc8fa921b106ba8660c60ac8ba5ece45606f04f36a3a90bb3838c42abf62dd2d84babef3e188e9171aff9bc116669009d887130ac9f7396a627a8474c1811b696f99552b14c402e9019b207452ffff2dbb4e2658b73873dcbaa36c52ccdfd6bcc657585080bb5a0f25973d63eb202dc0166bbd8a39ff93246f2789732ad05587f8db7bc87c242cfd36ce685a4b656986c39fd7fcb23c91913e4611191edcc88b78f145ea29d271b940c69941e4c3fd2d71f3b190690ee16f5d14ac2224e6fc89597654527dabe72e75d2d2a13a9fbaa6378e8a264321087a1900efe3cad14a579686aa3636bd375bd3136bee0bdaabcfac881bc701812721e6fb75aa072d2d187e62258d65a192157cdf2ec321407f682f5eec6a3297ab20b7061c62245716a44f71fbfc34c79b44e09e4212ac2653f662c5b2da843041f4032ac6796efa4131770dcdd613d4bc9d3ebd7db6b5e97519ed0e85a16de5d3ebe84504d840f70efaeeb0bca2003504c99993a9e1c0ff9c2f340836dfcf7f9d796e7cfe98412986f5e75b9213b4684ebbfa0023633e08fab91a5b7252b56832918b18f2717e1d233f1b4a1a36890e224c01acfce48ee3ed1365ba0c3d4233966a1d9ce3ca3cb43d4fb5a9f27181aaa83ca444b0275f4fa7a665e33a3b7a4698999724bd7ba85ab62c4c3d402ed42046f81f974816d279b11160bb635cca3044c3bd024f269016f24a9260fa70a7f052fd91cee315100b9516027584926aa9efc607206d1a2eea1067afbb3f36785f424af044dac5db5f7d38", "1c9aa200ca7fdfdc94cf735cec417e7781a7f67677f3ffe8efa5bafcf476bc57", 5, 648488714, 3556188729, 57765029, 324959705, 1, 0, 0, 0, 0, null, null, 3, 2, -190816259997207, "2d20eb633d97250f5abb80505857c6bcd6dfcc526ca3badc7338b758264ebb2d", "166bbd8a39ff93246f2789732ad05587f8db7bc87c242cfd36ce685a4b656986c39fd7fcb23c91913e4611191edcc88b78f145ea29d271b940c69941e4c3fd2d71f3b190690ee16f5d14ac2224e6fc89597654527dabe72e75d2d2a13a9fbaa6378e8a264321087a1900efe3cad14a579686aa3636bd375bd3136bee0bdaabcfac881bc701812721e6fb75aa072d2d187e62258d65a192157cdf2ec321407f682f5eec6a3297ab20b7061c62245716a44f71fbfc34c79b44e09e4212ac2653f6", "60bb635cca3044c3bd024f269016f24a9260fa70a7f052fd91cee315100b9516027584926aa9efc607206d1a2eea1067afbb3f36785f424af044dac5db5f7d38"], + ["050000800a27a72639eb63c07d8b0c79dbf2fa1c0315be13f79af6f43e5ab0778114798f442258eedc436fcc386b36b57e1917d720177366f4060063535300acfb1358c20aa41dc502e1dd8a1633f3d8e3276b59e7d2c4e624a6f53695bcaf247e36483f13b20442046aac5253eba02fc4142b4297ebb5683db8d24319706ad26aafd81c53b740f34543a6b3e9f5bb7d5c49e8c37f075151636a5252514c797d1c00000000", "17b38a0e93ff5df79278706bb6cc88c53d06a24ce53318cb92dba463a8b08944", 5, 648488714, 3227773753, 2030865277, 486208219, 3, 0, 0, 0, 0, null, null, 0, null, 0, null, null, null], + ["050000800a27a726ee7899b7b65b59b73465f717010000000000000000000000000000000000000000000000000000000000000000ffffffff06043465f71700ffffffff00000118b1c702951281215a743b10227e548dea9616e3a111430e483f91aa902d2655d6eb293b3bbad57fd69413646d89f010b8e2ea8847d83f63315ae26e805ca32b296241b8febfe1b8b6594c333a4f5335443a7d0008c1a2b6d87bc85692a7ed460430423dbdf06605f5b54b808feb22b208b064581847b2f64ca64837007216de6ecaffeb4b69e63347f84abcad8f2e757d5861ce77ee46513da7416837dcb23d33ea72af23d0ad8c9307d0b5858da95b77fff9027b8859e11dcbd598350eee50939481708ea708eb9f664388b9c64d6af0f96690342400348e929e07460253f38390f87bd6c05308c3bde25228e0fa0880b08ef34a5a9cc0ea0a67ca65b6ffd005572909f1c42dd745eeee9dd6b4439c9f3f98a118fe16698e9ceff558f16066975fe39583e9b5853b1311391580019fe55d59d1c828d3feb6a3b9ce92d089ae4b408e23d6a437d4989b519b7a9eb08ae6d448a7a16e8aed26a2ecd0cad80844fd0650d8c4e4d2af90656748d8099a0c756fc16cca06a334430702ae1961665b4845acd1a8e34101e68bb644ac034dc63e6e344c3d63762a7a5bf59f13095410981d6b6b16bcd4c9fa68af6e5365e94ecbe7ab8b8043dfbacb23c84d71a8fe5d9ac5502ce9f73f408e14376db86ef57cc37d09896fa906972e557180a4ab5ad09d8846dd6da748765436e0160240f8d41c0ac783f939f2d0ed262ce859c131ebc93ff2e6e407d4e243e1e931d53a4543b6e26d82596fc53b52312c776d12eb2b659b4fb098df87d683cf9e5412ee56c3fe9841d73fd070dfa51f5bafedf206f13c524e5c50cac9906efa393290042e3bc59f960b7d240ae443fc49269ce00061e65c6d74812a30dd5f5fe74eff61e0cbab3cec75d0aef95083189452dd3d9edf4487bc734c8b24f21296e4e9ef117d7fb977e3b0e6406e63085906331a93033d1cb8360fe6fea61a6826df36255789f92e40bafcb2ebcb9e556f6c0ccadc6af08e31ec4ad5288034e16d155cfdcada7bab599c2fa4ad2e6293f9fe097169148276b6a9eaa72f148b0c9565c3c2dd63125e0fa530ac2f5935bd220300861a710df8e481f2712920f8787e0aedfe618aff50a3b56213884d6262c11debf2ba7e8ad6692cb17078331418da4be064ff5270073934abcd2ab0469ecaf7275b4bd72bc6ed34478ea4089b736a16dd906d49f25c33827c571ce0b5d72177aa3508804bc0f8faa947122231402d2f5cc9a0eb0e09d427b427288d937d9d72b77456f886594cd8c6a462f77fd83076469cc0ecba3cc40cad69e5b54112eab33396aecfbc211f1f79cf33108e93d95378bae6958274b31088fbd8b3a3a0d154a77e229185ba2b9164acf008ff999973c7ce98b10afdc59090521196b90be68b87b11d1d7803da2631259c3f36e65aad69082303f31c2cab1f752cd1aedd0ce60500", "7a55e494f8d969d9a016b7d651fda313fd10273bc427947194bb2c03d76584a6", 5, 648488714, 3080288494, 3076086710, 402089268, 1, 0, 0, 1, 882620969332652, "6c61e0354f955a113c012f559e9b7ca6f9d00bd88c43147431b77079e1591d1c", "7e229185ba2b9164acf008ff999973c7ce98b10afdc59090521196b90be68b87b11d1d7803da2631259c3f36e65aad69082303f31c2cab1f752cd1aedd0ce605", 0, null, 0, null, null, null] +] From 68789356fb1a75f62735a529b38389ef08ea7582 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Tue, 19 Apr 2022 12:29:24 -0600 Subject: [PATCH 28/31] update RTD index.html for Orchard This file is generated from the *.proto files, and had gotten out of date. Updating it required installing docker; I followed these instructions: https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository I also needed to: $ sudo chmod 666 /var/run/docker.sock --- docs/rtd/index.html | 112 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 99 insertions(+), 13 deletions(-) diff --git a/docs/rtd/index.html b/docs/rtd/index.html index 6070357..dc6f8b9 100644 --- a/docs/rtd/index.html +++ b/docs/rtd/index.html @@ -183,11 +183,15 @@
  • - MCompactOutput + MCompactOrchardAction
  • - MCompactSpend + MCompactSaplingOutput +
  • + +
  • + MCompactSaplingSpend
  • @@ -408,7 +412,52 @@ -

    CompactOutput

    +

    CompactOrchardAction

    +

    https://github.com/zcash/zips/blob/main/zip-0225.rst#orchard-action-description-orchardaction

    (but not all fields are needed)

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FieldTypeLabelDescription
    nullifierbytes

    [32] The nullifier of the input note

    cmxbytes

    [32] The x-coordinate of the note commitment for the output note

    ephemeralKeybytes

    [32] An encoding of an ephemeral Pallas public key

    ciphertextbytes

    [52] The note plaintext component of the encCiphertext field

    + + + + + +

    CompactSaplingOutput

    output is a Sapling Output Description as described in section 7.4 of the

    Zcash protocol spec. Total size is 948.

    @@ -436,7 +485,7 @@ ciphertext bytes -

    ciphertext and zkproof

    +

    first 52 bytes of ciphertext

    @@ -446,8 +495,8 @@ -

    CompactSpend

    -

    CompactSpend is a Sapling Spend Description as described in 7.3 of the Zcash

    protocol specification.

    +

    CompactSaplingSpend

    +

    CompactSaplingSpend is a Sapling Spend Description as described in 7.3 of the Zcash

    protocol specification.

    @@ -507,18 +556,25 @@ in a pure-Sapling context, the fee will be calculable as: - + - + + + + + + + +
    spendsCompactSpendCompactSaplingSpend repeated

    inputs

    outputsCompactOutputCompactSaplingOutput repeated

    outputs

    actionsCompactOrchardActionrepeated

    @@ -836,6 +892,21 @@ into a specified block on the next ApplyStaged().

    Clear the incoming transaction pool.

    + + AddAddressUtxo + GetAddressUtxosReply + Empty +

    Add a GetAddressUtxosReply entry to be returned by GetAddressUtxos(). +There is no staging or applying for these, very simple.

    + + + + ClearAddressUtxo + Empty + Empty +

    Clear the list of GetAddressUtxos entries (can't fail)

    + + @@ -1045,7 +1116,7 @@ into a specified block on the next ApplyStaged().

    GetAddressUtxosArg

    -

    +

    Results are sorted by height, which makes it easy to issue another

    request that picks up from where the previous left off.

    @@ -1055,9 +1126,9 @@ into a specified block on the next ApplyStaged().

    - + - + @@ -1092,6 +1163,13 @@ into a specified block on the next ApplyStaged().

    + + + + + + + @@ -1305,7 +1383,7 @@ into a specified block on the next ApplyStaged().

    RawTransaction

    -

    RawTransaction contains the complete transaction data. It also optionally includes

    the block height in which the transaction was included.

    +

    RawTransaction contains the complete transaction data. It also optionally includes

    the block height in which the transaction was included, or, when returned

    by GetMempoolStream(), the latest block height.

    addressaddresses stringrepeated

    addressstring

    txid bytes
    @@ -1572,6 +1650,14 @@ match a shortened txid, they are all sent (none is excluded). Transactions in the exclude list that don't exist in the mempool are ignored.

    + + + + + + + @@ -1607,7 +1693,7 @@ The block can be specified by either height or hash.

    - + From ab4c0fef70dcb195f131d3d794e0c9cec2972dba Mon Sep 17 00:00:00 2001 From: Steven Smith Date: Sat, 2 Apr 2022 09:34:01 -0700 Subject: [PATCH 29/31] Add support to GetTreeState to return Orchard final state --- common/common.go | 6 + docs/rtd/index.html | 11 +- frontend/service.go | 11 +- go.mod | 5 +- go.sum | 231 ++++++++++++++++++++++++++++++++++ walletrpc/service.pb.go | 269 +++++++++++++++++++++------------------- walletrpc/service.proto | 11 +- 7 files changed, 400 insertions(+), 144 deletions(-) diff --git a/common/common.go b/common/common.go index a3a1e22..6ba25ec 100644 --- a/common/common.go +++ b/common/common.go @@ -112,6 +112,12 @@ type ( } SkipHash string } + Orchard struct { + Commitments struct { + FinalState string + } + SkipHash string + } } // zcashd rpc "getrawtransaction txid 1" (1 means verbose), there are diff --git a/docs/rtd/index.html b/docs/rtd/index.html index dc6f8b9..18fa4fc 100644 --- a/docs/rtd/index.html +++ b/docs/rtd/index.html @@ -1496,7 +1496,7 @@ There is no staging or applying for these, very simple.

    - + @@ -1514,12 +1514,19 @@ There is no staging or applying for these, very simple.

    - + + + + + + + +
    GetMempoolStreamEmptyRawTransaction stream

    Return a stream of current Mempool transactions. This will keep the output stream open while +there are mempool transactions. It will close the returned stream when a new block is mined.

    GetTreeState BlockID Ping Duration PingResponse

    Testing-only

    Testing-only, requires lightwalletd --ping-very-insecure (do not enable in production)

    height uint64

    block height

    treesaplingTree string

    sapling commitment tree state

    orchardTreestring

    orchard commitment tree state

    diff --git a/frontend/service.go b/frontend/service.go index bf86825..7ea7ae7 100644 --- a/frontend/service.go +++ b/frontend/service.go @@ -223,11 +223,12 @@ func (s *lwdStreamer) GetTreeState(ctx context.Context, id *walletrpc.BlockID) ( return nil, errors.New("zcashd did not return treestate") } return &walletrpc.TreeState{ - Network: s.chainName, - Height: uint64(gettreestateReply.Height), - Hash: gettreestateReply.Hash, - Time: gettreestateReply.Time, - Tree: gettreestateReply.Sapling.Commitments.FinalState, + Network: s.chainName, + Height: uint64(gettreestateReply.Height), + Hash: gettreestateReply.Hash, + Time: gettreestateReply.Time, + SaplingTree: gettreestateReply.Sapling.Commitments.FinalState, + OrchardTree: gettreestateReply.Orchard.Commitments.FinalState, }, nil } diff --git a/go.mod b/go.mod index dc3a617..7b90532 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.12 require ( github.com/btcsuite/btcd v0.20.1-beta + github.com/fullstorydev/grpcurl v1.8.6 // indirect github.com/golang/protobuf v1.5.2 github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 @@ -17,13 +18,11 @@ require ( github.com/spf13/afero v1.5.1 // indirect github.com/spf13/cobra v0.0.6 github.com/spf13/viper v1.6.2 - github.com/stretchr/testify v1.6.1 // indirect golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 // indirect golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect - golang.org/x/text v0.3.6 // indirect google.golang.org/genproto v0.0.0-20210406143921-e86de6bf7a46 // indirect - google.golang.org/grpc v1.37.0 + google.golang.org/grpc v1.44.0 google.golang.org/protobuf v1.27.1 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.51.0 diff --git a/go.sum b/go.sum index 384d5a9..bc0a310 100644 --- a/go.sum +++ b/go.sum @@ -1,12 +1,37 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.56.0 h1:WRz29PgAsVEyPSDHyk+0fpEkwEFyfhHn+JbksT6gIL4= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -25,13 +50,24 @@ github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3SkEwmHoWBmX1DNXhXZqlTpq6s4tyJGc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 h1:hzAQntlaYRkVSFEfj9OTWlVV1H155FMD8BTKktLv0QI= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 h1:zH8ljVhhq7yC0MIeUL/IviMtY8hx2mK8cN9wEYb8ggw= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -47,12 +83,21 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZm github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021 h1:fP+fF0up6oPY49OrjPrhIJ8yQfdIM85NXMLkMg1EXVs= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fullstorydev/grpcurl v1.8.6 h1:WylAwnPauJIofYSHqqMTC1eEfUIzqzevXyogBxnQquo= +github.com/fullstorydev/grpcurl v1.8.6/go.mod h1:WhP7fRQdhxz2TkL97u+TCb505sxfH78W1usyoB3tepw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -62,10 +107,19 @@ github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7a github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -73,9 +127,11 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= @@ -85,26 +141,44 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de h1:F7WD09S8QB4LrkEpka0dFPLSotH11HRpCsLIbIcJ7sU= github.com/gopherjs/gopherjs v0.0.0-20191106031601-ce3c9ade29de/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jhump/protoreflect v1.10.3 h1:8ogeubpKh2TiulA0apmGlW5YAH4U1Vi4TINIP+gpNfQ= +github.com/jhump/protoreflect v1.10.3/go.mod h1:7GcYQDdMU/O/BBrl/cX6PNHpXh6cenjd8pneu5yW7Tg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -136,6 +210,7 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nishanths/predeclared v0.0.0-20200524104333-86fad755b4d3/go.mod h1:nt3d53pc1VYcphSCIaYAJtnPYnr3Zyn8fMq2wvPGPso= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -171,6 +246,8 @@ github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLk github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -205,53 +282,109 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -259,10 +392,25 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -273,42 +421,114 @@ golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57i golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200522201501-cb1345f3a375/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200717024301-6ddee64345a6/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20210406143921-e86de6bf7a46 h1:f4STrQZf8jaowsiUitigvrqMCCM4QJH1A2JCSI7U1ow= google.golang.org/genproto v0.0.0-20210406143921-e86de6bf7a46/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.44.0 h1:weqSxi/TMs1SqFRMHCtBgXRs8k3X39QIDEZ0pRcttUg= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -318,6 +538,7 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.25.1-0.20200805231151-a709e31e5d12/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= @@ -328,6 +549,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -336,6 +558,7 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWD gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5 h1:ymVxjfMaHvXD8RqPRmzHHsB3VvucivSkIAvJFDI5O3c= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -343,4 +566,12 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8 h1:tH9C0MON9YI3/KuD+u5+tQrQQ8px0MrcJ/avzeALw7o= gopkg.in/yaml.v3 v3.0.0-20210105161348-2e78108cf5f8/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/walletrpc/service.pb.go b/walletrpc/service.pb.go index b3638d1..3e6d8c2 100644 --- a/walletrpc/service.pb.go +++ b/walletrpc/service.pb.go @@ -910,11 +910,12 @@ type TreeState struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` // "main" or "test" - Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` - Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty"` // block id - Time uint32 `protobuf:"varint,4,opt,name=time,proto3" json:"time,omitempty"` // Unix epoch time when the block was mined - Tree string `protobuf:"bytes,5,opt,name=tree,proto3" json:"tree,omitempty"` // sapling commitment tree state + Network string `protobuf:"bytes,1,opt,name=network,proto3" json:"network,omitempty"` // "main" or "test" + Height uint64 `protobuf:"varint,2,opt,name=height,proto3" json:"height,omitempty"` // block height + Hash string `protobuf:"bytes,3,opt,name=hash,proto3" json:"hash,omitempty"` // block id + Time uint32 `protobuf:"varint,4,opt,name=time,proto3" json:"time,omitempty"` // Unix epoch time when the block was mined + SaplingTree string `protobuf:"bytes,5,opt,name=saplingTree,proto3" json:"saplingTree,omitempty"` // sapling commitment tree state + OrchardTree string `protobuf:"bytes,6,opt,name=orchardTree,proto3" json:"orchardTree,omitempty"` // orchard commitment tree state } func (x *TreeState) Reset() { @@ -977,9 +978,16 @@ func (x *TreeState) GetTime() uint32 { return 0 } -func (x *TreeState) GetTree() string { +func (x *TreeState) GetSaplingTree() string { if x != nil { - return x.Tree + return x.SaplingTree + } + return "" +} + +func (x *TreeState) GetOrchardTree() string { + if x != nil { + return x.OrchardTree } return "" } @@ -1272,131 +1280,134 @@ var file_service_proto_rawDesc = []byte{ 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x22, 0x1d, 0x0a, 0x07, 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, 0x64, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0c, 0x52, - 0x04, 0x74, 0x78, 0x69, 0x64, 0x22, 0x79, 0x0a, 0x09, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x0a, 0x06, - 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, - 0x69, 0x67, 0x68, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x74, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, - 0x22, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, - 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, - 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x65, 0x69, - 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, - 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x45, 0x6e, 0x74, - 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x78, 0x45, - 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x12, - 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x14, 0x0a, - 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, 0x6e, - 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x22, - 0x6b, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, - 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0c, 0x61, - 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, - 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x52, 0x0c, - 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x32, 0x98, 0x0b, 0x0a, - 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, - 0x70, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0d, 0x47, - 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x21, 0x2e, 0x63, - 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, - 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x1a, - 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, - 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x54, - 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x63, 0x61, 0x73, - 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, - 0x70, 0x63, 0x2e, 0x54, 0x78, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0x25, 0x2e, 0x63, 0x61, - 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, - 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x72, 0x61, 0x6e, - 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, + 0x04, 0x74, 0x78, 0x69, 0x64, 0x22, 0xa9, 0x01, 0x0a, 0x09, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x16, 0x0a, + 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, + 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x68, 0x61, 0x73, 0x68, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x69, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x04, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x20, 0x0a, + 0x0b, 0x73, 0x61, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x73, 0x61, 0x70, 0x6c, 0x69, 0x6e, 0x67, 0x54, 0x72, 0x65, 0x65, 0x12, + 0x20, 0x0a, 0x0b, 0x6f, 0x72, 0x63, 0x68, 0x61, 0x72, 0x64, 0x54, 0x72, 0x65, 0x65, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6f, 0x72, 0x63, 0x68, 0x61, 0x72, 0x64, 0x54, 0x72, 0x65, + 0x65, 0x22, 0x74, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, + 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x64, 0x64, 0x72, 0x65, + 0x73, 0x73, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x65, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x48, 0x65, + 0x69, 0x67, 0x68, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0b, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x48, 0x65, 0x69, 0x67, 0x68, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x6d, 0x61, 0x78, 0x45, 0x6e, + 0x74, 0x72, 0x69, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6d, 0x61, 0x78, + 0x45, 0x6e, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x78, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x74, 0x78, 0x69, 0x64, 0x12, 0x14, + 0x0a, 0x05, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x05, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x1a, 0x0a, 0x08, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x08, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5a, 0x61, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x68, 0x65, 0x69, 0x67, + 0x68, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x69, 0x67, 0x68, 0x74, + 0x22, 0x6b, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, + 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x0c, + 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, + 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x52, + 0x0c, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x32, 0x98, 0x0b, + 0x0a, 0x11, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x65, 0x72, 0x12, 0x54, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4c, 0x61, 0x74, 0x65, 0x73, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x53, 0x70, 0x65, 0x63, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, - 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x23, - 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, - 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x54, 0x78, 0x69, 0x64, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x61, 0x73, 0x68, - 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x64, 0x64, - 0x72, 0x65, 0x73, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x22, 0x00, 0x12, 0x51, 0x0a, 0x08, 0x47, 0x65, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, + 0x6d, 0x70, 0x61, 0x63, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x12, 0x5b, 0x0a, 0x0d, + 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x21, 0x2e, + 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, + 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x61, 0x6e, 0x67, 0x65, + 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x0e, 0x47, 0x65, 0x74, + 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1f, 0x2e, 0x63, 0x61, + 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, + 0x72, 0x70, 0x63, 0x2e, 0x54, 0x78, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x1a, 0x25, 0x2e, 0x63, + 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, + 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x12, 0x5f, 0x0a, 0x0f, 0x53, 0x65, 0x6e, 0x64, 0x54, 0x72, 0x61, + 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, + 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, + 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x1a, + 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, + 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x53, 0x65, 0x6e, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x73, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x54, 0x78, 0x69, 0x64, 0x73, 0x12, 0x34, 0x2e, 0x63, 0x61, 0x73, + 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, + 0x70, 0x63, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x41, 0x64, + 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x46, 0x69, 0x6c, 0x74, 0x65, 0x72, + 0x1a, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, + 0x73, 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x12, 0x47, + 0x65, 0x74, 0x54, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, + 0x65, 0x12, 0x22, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, + 0x73, 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, + 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x54, 0x61, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x72, + 0x65, 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, + 0x65, 0x73, 0x73, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6c, 0x61, + 0x6e, 0x63, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x54, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4d, 0x65, + 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x78, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, + 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, + 0x45, 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, + 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, + 0x43, 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5b, 0x0a, + 0x10, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, + 0x6d, 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x25, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, - 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5a, 0x0a, 0x12, 0x47, 0x65, - 0x74, 0x54, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, - 0x12, 0x22, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, - 0x4c, 0x69, 0x73, 0x74, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6c, - 0x61, 0x6e, 0x63, 0x65, 0x22, 0x00, 0x12, 0x5e, 0x0a, 0x18, 0x47, 0x65, 0x74, 0x54, 0x61, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x42, 0x61, 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x53, 0x74, 0x72, 0x65, - 0x61, 0x6d, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, - 0x73, 0x73, 0x1a, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x42, 0x61, 0x6c, 0x61, 0x6e, - 0x63, 0x65, 0x22, 0x00, 0x28, 0x01, 0x12, 0x54, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, - 0x70, 0x6f, 0x6f, 0x6c, 0x54, 0x78, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, - 0x78, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x43, - 0x6f, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x54, 0x78, 0x22, 0x00, 0x30, 0x01, 0x12, 0x5b, 0x0a, 0x10, - 0x47, 0x65, 0x74, 0x4d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, - 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x25, - 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, - 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x52, 0x61, 0x77, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x61, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0c, 0x47, 0x65, 0x74, - 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, 0x68, - 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, - 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, 0x68, - 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, - 0x63, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x00, 0x12, 0x6f, 0x0a, - 0x0f, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, - 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, - 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2f, 0x2e, 0x63, 0x61, - 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, - 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, - 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, 0x73, - 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, - 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, - 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, - 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, - 0x72, 0x67, 0x1a, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, - 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, - 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74, 0x64, - 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, - 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, 0x70, - 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, - 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, 0x74, - 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, 0x12, - 0x1f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, - 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, - 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, 0x67, 0x68, 0x74, - 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, 0x70, - 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x61, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0c, 0x47, 0x65, + 0x74, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1e, 0x2e, 0x63, 0x61, 0x73, + 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, + 0x70, 0x63, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x44, 0x1a, 0x20, 0x2e, 0x63, 0x61, 0x73, + 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, + 0x70, 0x63, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x53, 0x74, 0x61, 0x74, 0x65, 0x22, 0x00, 0x12, 0x6f, + 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, + 0x73, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, + 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x41, 0x72, 0x67, 0x1a, 0x2f, 0x2e, 0x63, + 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, + 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, + 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x4c, 0x69, 0x73, 0x74, 0x22, 0x00, 0x12, + 0x73, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, + 0x6f, 0x73, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x29, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, + 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, + 0x2e, 0x47, 0x65, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, + 0x41, 0x72, 0x67, 0x1a, 0x2b, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x47, 0x65, 0x74, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x55, 0x74, 0x78, 0x6f, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, + 0x22, 0x00, 0x30, 0x01, 0x12, 0x52, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4c, 0x69, 0x67, 0x68, 0x74, + 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1c, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, + 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x21, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, + 0x6c, 0x65, 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x4c, 0x69, 0x67, 0x68, + 0x74, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x22, 0x00, 0x12, 0x4e, 0x0a, 0x04, 0x50, 0x69, 0x6e, 0x67, + 0x12, 0x1f, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, + 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x1a, 0x23, 0x2e, 0x63, 0x61, 0x73, 0x68, 0x2e, 0x7a, 0x2e, 0x77, 0x61, 0x6c, 0x6c, 0x65, + 0x74, 0x2e, 0x73, 0x64, 0x6b, 0x2e, 0x72, 0x70, 0x63, 0x2e, 0x50, 0x69, 0x6e, 0x67, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x1b, 0x5a, 0x16, 0x6c, 0x69, 0x67, 0x68, + 0x74, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x64, 0x2f, 0x77, 0x61, 0x6c, 0x6c, 0x65, 0x74, 0x72, + 0x70, 0x63, 0xba, 0x02, 0x00, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/walletrpc/service.proto b/walletrpc/service.proto index 91d0ada..49f97ea 100644 --- a/walletrpc/service.proto +++ b/walletrpc/service.proto @@ -110,11 +110,12 @@ message Exclude { // The TreeState is derived from the Zcash z_gettreestate rpc. message TreeState { - string network = 1; // "main" or "test" - uint64 height = 2; - string hash = 3; // block id - uint32 time = 4; // Unix epoch time when the block was mined - string tree = 5; // sapling commitment tree state + string network = 1; // "main" or "test" + uint64 height = 2; // block height + string hash = 3; // block id + uint32 time = 4; // Unix epoch time when the block was mined + string saplingTree = 5; // sapling commitment tree state + string orchardTree = 6; // orchard commitment tree state } // Results are sorted by height, which makes it easy to issue another From dfac02093d85fb31fb9a8475b884dd6abca966c7 Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Mon, 23 May 2022 16:37:12 -0600 Subject: [PATCH 30/31] Fix V5 transaction txid (#392) This is a shortcut fix. Instead of replicating the zip244 transaction logic that's implemented in librustzcash: zcash_primitives/src/transaction/components/orchard.rs cheat by calling getblock with verbose level 1, which prints the txid of each transaction. This currently requires calling getblock twice, once for the raw hex (as we have always done), and again to get the transaction IDs. An easy improvement would be to add the raw hex to verbosity 1 result. (Or have a new verbosity that shows both.) --- common/common.go | 30 ++++++++++++++++++++++++++++++ parser/transaction.go | 23 ++++++++--------------- parser/transaction_test.go | 7 +++---- 3 files changed, 41 insertions(+), 19 deletions(-) diff --git a/common/common.go b/common/common.go index 6ba25ec..56ad5f6 100644 --- a/common/common.go +++ b/common/common.go @@ -147,6 +147,11 @@ type ( Satoshis uint64 Height int } + + // reply to getblock verbose=1 (json includes txid list) + ZcashRpcReplyGetblock1 struct { + Tx []string + } ) // FirstRPC tests that we can successfully reach zcashd through the RPC @@ -271,6 +276,31 @@ func getBlockFromRPC(height int) (*walletrpc.CompactBlock, error) { return nil, errors.New("received unexpected height block") } + // `block.ParseFromSlice` correctly parses blocks containing v5 transactions, but + // incorrectly computes the IDs of the v5 transactions. We temporarily paper over this + // bug by fetching the correct txids via a second getblock RPC call. + // https://github.com/zcash/lightwalletd/issues/392 + { + params[1] = json.RawMessage("1") // JSON with list of txids + result, rpcErr := RawRequest("getblock", params) + if rpcErr != nil { + return nil, errors.Wrap(rpcErr, "error requesting verbose block") + } + var block1 ZcashRpcReplyGetblock1 + err = json.Unmarshal(result, &block1) + if err != nil { + return nil, err + } + for i, t := range block.Transactions() { + txid, err := hex.DecodeString(block1.Tx[i]) + if err != nil { + return nil, errors.Wrap(err, "error decoding getblock txid") + } + // convert from big-endian + t.SetTxID(parser.Reverse(txid)) + } + } + return block.ToCompact(), nil } diff --git a/parser/transaction.go b/parser/transaction.go index e59367e..5c3fb24 100644 --- a/parser/transaction.go +++ b/parser/transaction.go @@ -6,7 +6,6 @@ package parser import ( - "crypto/sha256" "fmt" "github.com/pkg/errors" @@ -339,29 +338,23 @@ func (p *action) ToCompact() *walletrpc.CompactOrchardAction { // Transaction encodes a full (zcashd) transaction. type Transaction struct { *rawTransaction - rawBytes []byte - cachedTxID []byte // cached for performance + rawBytes []byte + txID []byte // from getblock verbose=1 +} + +func (tx *Transaction) SetTxID(txid []byte) { + tx.txID = txid } // GetDisplayHash returns the transaction hash in big-endian display order. func (tx *Transaction) GetDisplayHash() []byte { - if tx.cachedTxID != nil { - return tx.cachedTxID - } - - // SHA256d - digest := sha256.Sum256(tx.rawBytes) - digest = sha256.Sum256(digest[:]) // Convert to big-endian - tx.cachedTxID = Reverse(digest[:]) - return tx.cachedTxID + return Reverse(tx.txID[:]) } // GetEncodableHash returns the transaction hash in little-endian wire format order. func (tx *Transaction) GetEncodableHash() []byte { - digest := sha256.Sum256(tx.rawBytes) - digest = sha256.Sum256(digest[:]) - return digest[:] + return tx.txID } // Bytes returns a full transaction's raw bytes. diff --git a/parser/transaction_test.go b/parser/transaction_test.go index dd9498a..018cd89 100644 --- a/parser/transaction_test.go +++ b/parser/transaction_test.go @@ -4,7 +4,6 @@ package parser import ( - "bytes" "encoding/hex" "encoding/json" "os" @@ -85,9 +84,9 @@ func TestV5TransactionParser(t *testing.T) { if len(rest) != 0 { t.Fatalf("Test did not consume entire buffer, %d remaining", len(rest)) } - if bytes.Equal(tx.cachedTxID, []byte(txtestdata.Txid)) { - t.Fatal("txid") - } + // Currently, we can't check the txid because we get that from + // zcashd (getblock rpc) rather than computing it ourselves. + // https://github.com/zcash/lightwalletd/issues/392 if tx.version != uint32(txtestdata.Version) { t.Fatal("version miscompare") } From ba1b9319865b583476b8ef0d6862fee83f2c31aa Mon Sep 17 00:00:00 2001 From: Larry Ruane Date: Fri, 1 Apr 2022 12:31:36 -0600 Subject: [PATCH 31/31] add --sync-from-height command-line option This causes lightwalletd to discard cached blocks at the given height and beyond. This in turn causes it to re-fetch those blocks from zcashd. It's similar to --redownload, except that option discards all blocks (equivalent to --sync-from-height 0, but the existing --redownload is retained for compatibility). This is mostly intended for testing. It's sometimes useful to force the node to (re)sync some recent blocks, but redownloading all of them takes around an hour. --- cmd/root.go | 10 +++++++++- common/cache.go | 21 ++++++++++++--------- common/cache_test.go | 4 ++-- common/common.go | 1 + common/common_test.go | 8 ++++---- frontend/frontend_test.go | 2 +- 6 files changed, 29 insertions(+), 17 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 461a8c7..b88342e 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -55,6 +55,7 @@ var rootCmd = &cobra.Command{ GenCertVeryInsecure: viper.GetBool("gen-cert-very-insecure"), DataDir: viper.GetString("data-dir"), Redownload: viper.GetBool("redownload"), + SyncFromHeight: viper.GetInt("sync-from-height"), PingEnable: viper.GetBool("ping-very-insecure"), Darkside: viper.GetBool("darkside-very-insecure"), DarksideTimeout: viper.GetUint64("darkside-timeout"), @@ -239,7 +240,11 @@ func startServer(opts *common.Options) error { os.Stderr.WriteString(fmt.Sprintf("\n ** Can't create db directory: %s\n\n", dbPath)) os.Exit(1) } - cache := common.NewBlockCache(dbPath, chainName, saplingHeight, opts.Redownload) + syncFromHeight := opts.SyncFromHeight + if opts.Redownload { + syncFromHeight = 0 + } + cache := common.NewBlockCache(dbPath, chainName, saplingHeight, syncFromHeight) if !opts.Darkside { go common.BlockIngestor(cache, 0 /*loop forever*/) } else { @@ -325,6 +330,7 @@ func init() { rootCmd.Flags().Bool("no-tls-very-insecure", false, "run without the required TLS certificate, only for debugging, DO NOT use in production") rootCmd.Flags().Bool("gen-cert-very-insecure", false, "run with self-signed TLS certificate, only for debugging, DO NOT use in production") rootCmd.Flags().Bool("redownload", false, "re-fetch all blocks from zcashd; reinitialize local cache files") + rootCmd.Flags().Int("sync-from-height", -1, "re-fetch blocks from zcashd start at this height") rootCmd.Flags().String("data-dir", "/var/lib/lightwalletd", "data directory (such as db)") rootCmd.Flags().Bool("ping-very-insecure", false, "allow Ping GRPC for testing") rootCmd.Flags().Bool("darkside-very-insecure", false, "run with GRPC-controllable mock zcashd for integration testing (shuts down after 30 minutes)") @@ -356,6 +362,8 @@ func init() { viper.SetDefault("gen-cert-very-insecure", false) viper.BindPFlag("redownload", rootCmd.Flags().Lookup("redownload")) viper.SetDefault("redownload", false) + viper.BindPFlag("sync-from-height", rootCmd.Flags().Lookup("sync-from-height")) + viper.SetDefault("sync-from-height", -1) viper.BindPFlag("data-dir", rootCmd.Flags().Lookup("data-dir")) viper.SetDefault("data-dir", "/var/lib/lightwalletd") viper.BindPFlag("ping-very-insecure", rootCmd.Flags().Lookup("ping-very-insecure")) diff --git a/common/cache.go b/common/cache.go index 8b5ffb8..d4c68d1 100644 --- a/common/cache.go +++ b/common/cache.go @@ -191,7 +191,8 @@ func (c *BlockCache) Reset(startHeight int) { // NewBlockCache returns an instance of a block cache object. // (No locking here, we assume this is single-threaded.) -func NewBlockCache(dbPath string, chainName string, startHeight int, redownload bool) *BlockCache { +// syncFromHeight < 0 means latest (tip) height. +func NewBlockCache(dbPath string, chainName string, startHeight int, syncFromHeight int) *BlockCache { c := &BlockCache{} c.firstBlock = startHeight c.nextBlock = startHeight @@ -208,18 +209,20 @@ func NewBlockCache(dbPath string, chainName string, startHeight int, redownload if err != nil { Log.Fatal("open ", c.lengthsName, " failed: ", err) } - if redownload { - if err := c.lengthsFile.Truncate(0); err != nil { - Log.Fatal("truncate lengths file failed: ", err) - } - if err := c.blocksFile.Truncate(0); err != nil { - Log.Fatal("truncate blocks file failed: ", err) - } - } lengths, err := ioutil.ReadFile(c.lengthsName) if err != nil { Log.Fatal("read ", c.lengthsName, " failed: ", err) } + // 4 bytes per lengths[] value (block length) + if syncFromHeight >= 0 { + if syncFromHeight < startHeight { + syncFromHeight = startHeight + } + if (syncFromHeight-startHeight)*4 < len(lengths) { + // discard the entries at and beyond (newer than) the specified height + lengths = lengths[:(syncFromHeight-startHeight)*4] + } + } // The last entry in starts[] is where to write the next block. var offset int64 diff --git a/common/cache_test.go b/common/cache_test.go index 2493163..11a803e 100644 --- a/common/cache_test.go +++ b/common/cache_test.go @@ -58,7 +58,7 @@ func TestCache(t *testing.T) { // Pretend Sapling starts at 289460. os.RemoveAll(unitTestPath) - cache = NewBlockCache(unitTestPath, unitTestChain, 289460, true) + cache = NewBlockCache(unitTestPath, unitTestChain, 289460, 0) // Initially cache is empty. if cache.GetLatestHeight() != -1 { @@ -75,7 +75,7 @@ func TestCache(t *testing.T) { fillCache(t) // Simulate a restart to ensure the db files are read correctly. - cache = NewBlockCache(unitTestPath, unitTestChain, 289460, false) + cache = NewBlockCache(unitTestPath, unitTestChain, 289460, -1) // Should still be 6 blocks. if cache.nextBlock != 289466 { diff --git a/common/common.go b/common/common.go index 56ad5f6..c8a1a92 100644 --- a/common/common.go +++ b/common/common.go @@ -42,6 +42,7 @@ type Options struct { NoTLSVeryInsecure bool `json:"no_tls_very_insecure,omitempty"` GenCertVeryInsecure bool `json:"gen_cert_very_insecure,omitempty"` Redownload bool `json:"redownload"` + SyncFromHeight int `json:"sync_from_height"` DataDir string `json:"data_dir"` PingEnable bool `json:"ping_enable"` Darkside bool `json:"darkside"` diff --git a/common/common_test.go b/common/common_test.go index af2b3aa..1ff26d3 100644 --- a/common/common_test.go +++ b/common/common_test.go @@ -62,7 +62,7 @@ func TestMain(m *testing.M) { blockJSON, _ := json.Marshal(scan.Text()) blocks = append(blocks, blockJSON) } - testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, true) + testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, 0) // Setup is done; run all tests. exitcode := m.Run() @@ -355,7 +355,7 @@ func TestBlockIngestor(t *testing.T) { Time.Sleep = sleepStub Time.Now = nowStub os.RemoveAll(unitTestPath) - testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, false) + testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, -1) BlockIngestor(testcache, 11) if step != 19 { t.Error("unexpected final step", step) @@ -488,7 +488,7 @@ func TestGetBlockRange(t *testing.T) { testT = t RawRequest = getblockStub os.RemoveAll(unitTestPath) - testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, true) + testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, 0) blockChan := make(chan *walletrpc.CompactBlock) errChan := make(chan error) go GetBlockRange(testcache, blockChan, errChan, 380640, 380642) @@ -567,7 +567,7 @@ func TestGetBlockRangeReverse(t *testing.T) { testT = t RawRequest = getblockStubReverse os.RemoveAll(unitTestPath) - testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, true) + testcache = NewBlockCache(unitTestPath, unitTestChain, 380640, 0) blockChan := make(chan *walletrpc.CompactBlock) errChan := make(chan error) diff --git a/frontend/frontend_test.go b/frontend/frontend_test.go index 65a2c26..38199df 100644 --- a/frontend/frontend_test.go +++ b/frontend/frontend_test.go @@ -36,7 +36,7 @@ const ( func testsetup() (walletrpc.CompactTxStreamerServer, *common.BlockCache) { os.RemoveAll(unitTestPath) - cache := common.NewBlockCache(unitTestPath, unitTestChain, 380640, true) + cache := common.NewBlockCache(unitTestPath, unitTestChain, 380640, 0) lwd, err := NewLwdStreamer(cache, "main", false /* enablePing */) if err != nil { os.Stderr.WriteString(fmt.Sprint("NewLwdStreamer failed:", err))