From 637fcac780fd151c531c679527dcf35da7b597a2 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sun, 5 Nov 2023 18:25:02 +0100 Subject: [PATCH 01/46] move code over --- go.mod | 4 +- go.sum | 6 + pkg/net/grpc/grpctransport.pb.go | 4 +- pkg/net/grpc/grpctransport_grpc.pb.go | 15 +- pkg/pb/apppb/apppb.pb.go | 5 +- pkg/pb/availabilitypb/availabilitypb.pb.go | 9 +- .../availabilitypb/batchdbpb/batchdbpb.pb.go | 7 +- pkg/pb/availabilitypb/mscpb/mscpb.pb.go | 5 +- pkg/pb/batchfetcherpb/batchfetcherpb.pb.go | 5 +- pkg/pb/bcbpb/bcbpb.pb.go | 6 +- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go | 244 +++++++++++ pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go | 13 + pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go | 16 + pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go | 29 ++ .../blockchainpb/bcmpb/events/events.mir.go | 25 ++ .../bcmpb/oneof_interfaces.mir.go | 14 + pkg/pb/blockchainpb/bcmpb/types/types.mir.go | 118 +++++ pkg/pb/blockchainpb/blockchainpb.pb.go | 379 ++++++++++++++++ .../communicationpb/communicationpb.pb.go | 405 +++++++++++++++++ .../communicationpb/communicationpb.pb.mir.go | 19 + .../communicationpb/dsl/emit.mir.go | 16 + .../communicationpb/dsl/messages.mir.go | 31 ++ .../communicationpb/dsl/upon.mir.go | 29 ++ .../communicationpb/events/events.mir.go | 25 ++ .../communicationpb/msgs/msgs.mir.go | 25 ++ .../communicationpb/oneof_interfaces.mir.go | 25 ++ .../communicationpb/types/types.mir.go | 226 ++++++++++ pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go | 16 + pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go | 29 ++ .../blockchainpb/minerpb/events/events.mir.go | 26 ++ pkg/pb/blockchainpb/minerpb/minerpb.pb.go | 255 +++++++++++ pkg/pb/blockchainpb/minerpb/minerpb.pb.mir.go | 13 + .../minerpb/oneof_interfaces.mir.go | 14 + .../blockchainpb/minerpb/types/types.mir.go | 121 ++++++ pkg/pb/blockchainpb/oneof_interfaces.mir.go | 3 + pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go | 15 + pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go | 28 ++ .../blockchainpb/tpmpb/events/events.mir.go | 24 ++ .../tpmpb/oneof_interfaces.mir.go | 14 + pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go | 238 ++++++++++ pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go | 13 + pkg/pb/blockchainpb/tpmpb/types/types.mir.go | 115 +++++ pkg/pb/blockchainpb/types/types.mir.go | 3 + pkg/pb/checkpointpb/checkpointpb.pb.go | 6 +- .../chkpvalidatorpb/chkpvalidatorpb.pb.go | 6 +- pkg/pb/contextstorepb/contextstorepb.pb.go | 4 +- pkg/pb/cryptopb/cryptopb.pb.go | 7 +- pkg/pb/dslpb/dslpb.pb.go | 4 +- pkg/pb/eventpb/eventpb.pb.go | 407 +++++++++++------- pkg/pb/eventpb/eventpb.pb.mir.go | 4 + pkg/pb/eventpb/oneof_interfaces.mir.go | 20 + pkg/pb/eventpb/types/types.mir.go | 150 ++++++- pkg/pb/factorypb/factorypb.pb.go | 6 +- pkg/pb/hasherpb/hasherpb.pb.go | 6 +- pkg/pb/isspb/isspb.pb.go | 6 +- pkg/pb/mempoolpb/mempoolpb.pb.go | 9 +- pkg/pb/messagepb/messagepb.pb.go | 126 +++--- pkg/pb/messagepb/messagepb.pb.mir.go | 1 + pkg/pb/messagepb/oneof_interfaces.mir.go | 5 + pkg/pb/messagepb/types/types.mir.go | 27 ++ pkg/pb/mir/codegen_extensions.pb.go | 4 +- pkg/pb/net/codegen_extensions.pb.go | 4 +- pkg/pb/ordererpb/ordererpb.pb.go | 6 +- .../pprepvalidatorpb/pprepvalidatorpb.pb.go | 6 +- pkg/pb/pbftpb/pbftpb.pb.go | 6 +- pkg/pb/pingpongpb/pingpongpb.pb.go | 6 +- pkg/pb/recordingpb/recordingpb.pb.go | 4 +- pkg/pb/testerpb/testerpb.pb.go | 4 +- pkg/pb/threshcryptopb/threshcryptopb.pb.go | 9 +- pkg/pb/transportpb/transportpb.pb.go | 5 +- pkg/pb/trantorpb/trantorpb.pb.go | 4 +- .../transactionreceiver.pb.go | 4 +- .../transactionreceiver_grpc.pb.go | 15 +- protos/blockchainpb/bcmpb/bcmpb.proto | 25 ++ protos/blockchainpb/blockchainpb.proto | 26 ++ .../communicationpb/communicationpb.proto | 42 ++ protos/blockchainpb/minerpb/minerpb.proto | 25 ++ protos/blockchainpb/tpmpb/tpmpb.proto | 24 ++ protos/eventpb/eventpb.proto | 10 + protos/generate.go | 10 + protos/messagepb/messagepb.proto | 5 + samples/blockchain/bcm.go | 240 +++++++++++ samples/blockchain/communication.go | 71 +++ samples/blockchain/main.go | 106 +++++ samples/blockchain/miner.go | 92 ++++ samples/blockchain/old/miner_passive.go | 132 ++++++ samples/blockchain/tpm.go | 44 ++ samples/blockchain/utils.go | 15 + samples/blockchain/ws/main.go | 16 + samples/blockchain/ws/ws/wss.go | 138 ++++++ 90 files changed, 4240 insertions(+), 284 deletions(-) create mode 100644 pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go create mode 100644 pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go create mode 100644 pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go create mode 100644 pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go create mode 100644 pkg/pb/blockchainpb/bcmpb/events/events.mir.go create mode 100644 pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go create mode 100644 pkg/pb/blockchainpb/bcmpb/types/types.mir.go create mode 100644 pkg/pb/blockchainpb/blockchainpb.pb.go create mode 100644 pkg/pb/blockchainpb/communicationpb/communicationpb.pb.go create mode 100644 pkg/pb/blockchainpb/communicationpb/communicationpb.pb.mir.go create mode 100644 pkg/pb/blockchainpb/communicationpb/dsl/emit.mir.go create mode 100644 pkg/pb/blockchainpb/communicationpb/dsl/messages.mir.go create mode 100644 pkg/pb/blockchainpb/communicationpb/dsl/upon.mir.go create mode 100644 pkg/pb/blockchainpb/communicationpb/events/events.mir.go create mode 100644 pkg/pb/blockchainpb/communicationpb/msgs/msgs.mir.go create mode 100644 pkg/pb/blockchainpb/communicationpb/oneof_interfaces.mir.go create mode 100644 pkg/pb/blockchainpb/communicationpb/types/types.mir.go create mode 100644 pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go create mode 100644 pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go create mode 100644 pkg/pb/blockchainpb/minerpb/events/events.mir.go create mode 100644 pkg/pb/blockchainpb/minerpb/minerpb.pb.go create mode 100644 pkg/pb/blockchainpb/minerpb/minerpb.pb.mir.go create mode 100644 pkg/pb/blockchainpb/minerpb/oneof_interfaces.mir.go create mode 100644 pkg/pb/blockchainpb/minerpb/types/types.mir.go create mode 100644 pkg/pb/blockchainpb/oneof_interfaces.mir.go create mode 100644 pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go create mode 100644 pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go create mode 100644 pkg/pb/blockchainpb/tpmpb/events/events.mir.go create mode 100644 pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go create mode 100644 pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go create mode 100644 pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go create mode 100644 pkg/pb/blockchainpb/tpmpb/types/types.mir.go create mode 100644 pkg/pb/blockchainpb/types/types.mir.go create mode 100644 protos/blockchainpb/bcmpb/bcmpb.proto create mode 100644 protos/blockchainpb/blockchainpb.proto create mode 100644 protos/blockchainpb/communicationpb/communicationpb.proto create mode 100644 protos/blockchainpb/minerpb/minerpb.proto create mode 100644 protos/blockchainpb/tpmpb/tpmpb.proto create mode 100644 samples/blockchain/bcm.go create mode 100644 samples/blockchain/communication.go create mode 100644 samples/blockchain/main.go create mode 100644 samples/blockchain/miner.go create mode 100644 samples/blockchain/old/miner_passive.go create mode 100644 samples/blockchain/tpm.go create mode 100644 samples/blockchain/utils.go create mode 100644 samples/blockchain/ws/main.go create mode 100644 samples/blockchain/ws/ws/wss.go diff --git a/go.mod b/go.mod index 3d561be59..5e44796f4 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,8 @@ require ( github.com/golang/protobuf v1.5.3 // indirect github.com/google/gopacket v1.1.19 // indirect github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 // indirect - github.com/gorilla/websocket v1.5.0 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/gorilla/websocket v1.5.1 // indirect github.com/huin/goupnp v1.2.0 // indirect github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/ipfs/go-log/v2 v2.5.1 // indirect @@ -94,6 +95,7 @@ require ( github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect github.com/minio/sha256-simd v1.0.1 // indirect + github.com/mitchellh/hashstructure v1.1.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect diff --git a/go.sum b/go.sum index 78ce51628..ee178f61b 100644 --- a/go.sum +++ b/go.sum @@ -139,12 +139,16 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20230602150820-91b7bce49751 h1:hR7/MlvK23p6+lIw9SN1TigNLn9ZnF3W4SYRKq2gAHs= github.com/google/pprof v0.0.0-20230602150820-91b7bce49751/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= +github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ= @@ -252,6 +256,8 @@ github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8Rv github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= +github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0= +github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= diff --git a/pkg/net/grpc/grpctransport.pb.go b/pkg/net/grpc/grpctransport.pb.go index 4d1221563..03f807471 100644 --- a/pkg/net/grpc/grpctransport.pb.go +++ b/pkg/net/grpc/grpctransport.pb.go @@ -5,8 +5,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: net/grpc/grpctransport.proto package grpc diff --git a/pkg/net/grpc/grpctransport_grpc.pb.go b/pkg/net/grpc/grpctransport_grpc.pb.go index ba59dbbbe..cdb7b2e2c 100644 --- a/pkg/net/grpc/grpctransport_grpc.pb.go +++ b/pkg/net/grpc/grpctransport_grpc.pb.go @@ -1,4 +1,13 @@ +// +//Copyright IBM Corp. All Rights Reserved. +// +//SPDX-License-Identifier: Apache-2.0 + // Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v4.24.4 +// source: net/grpc/grpctransport.proto package grpc @@ -14,6 +23,10 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 +const ( + GrpcTransport_Listen_FullMethodName = "/grpctransport.GrpcTransport/Listen" +) + // GrpcTransportClient is the client API for GrpcTransport service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. @@ -30,7 +43,7 @@ func NewGrpcTransportClient(cc grpc.ClientConnInterface) GrpcTransportClient { } func (c *grpcTransportClient) Listen(ctx context.Context, opts ...grpc.CallOption) (GrpcTransport_ListenClient, error) { - stream, err := c.cc.NewStream(ctx, &GrpcTransport_ServiceDesc.Streams[0], "/grpctransport.GrpcTransport/Listen", opts...) + stream, err := c.cc.NewStream(ctx, &GrpcTransport_ServiceDesc.Streams[0], GrpcTransport_Listen_FullMethodName, opts...) if err != nil { return nil, err } diff --git a/pkg/pb/apppb/apppb.pb.go b/pkg/pb/apppb/apppb.pb.go index e06601992..49f04ce5d 100644 --- a/pkg/pb/apppb/apppb.pb.go +++ b/pkg/pb/apppb/apppb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: apppb/apppb.proto package apppb @@ -28,6 +28,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_SnapshotRequest // *Event_Snapshot // *Event_RestoreState diff --git a/pkg/pb/availabilitypb/availabilitypb.pb.go b/pkg/pb/availabilitypb/availabilitypb.pb.go index 28985f452..f1c92a5b3 100644 --- a/pkg/pb/availabilitypb/availabilitypb.pb.go +++ b/pkg/pb/availabilitypb/availabilitypb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: availabilitypb/availabilitypb.proto package availabilitypb @@ -31,6 +31,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_RequestCert // *Event_NewCert // *Event_VerifyCert @@ -521,6 +522,7 @@ type RequestCertOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *RequestCertOrigin_ContextStore // *RequestCertOrigin_Dsl Type isRequestCertOrigin_Type `protobuf_oneof:"type"` @@ -609,6 +611,7 @@ type RequestTransactionsOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *RequestTransactionsOrigin_ContextStore // *RequestTransactionsOrigin_Dsl Type isRequestTransactionsOrigin_Type `protobuf_oneof:"type"` @@ -697,6 +700,7 @@ type VerifyCertOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *VerifyCertOrigin_ContextStore // *VerifyCertOrigin_Dsl Type isVerifyCertOrigin_Type `protobuf_oneof:"type"` @@ -784,6 +788,7 @@ type Cert struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Cert_Mscs Type isCert_Type `protobuf_oneof:"type"` } diff --git a/pkg/pb/availabilitypb/batchdbpb/batchdbpb.pb.go b/pkg/pb/availabilitypb/batchdbpb/batchdbpb.pb.go index 1aeb34ad7..37e92832f 100644 --- a/pkg/pb/availabilitypb/batchdbpb/batchdbpb.pb.go +++ b/pkg/pb/availabilitypb/batchdbpb/batchdbpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: availabilitypb/batchdbpb/batchdbpb.proto package batchdbpb @@ -30,6 +30,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_Lookup // *Event_LookupResponse // *Event_Store @@ -440,6 +441,7 @@ type LookupBatchOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *LookupBatchOrigin_ContextStore // *LookupBatchOrigin_Dsl Type isLookupBatchOrigin_Type `protobuf_oneof:"Type"` @@ -528,6 +530,7 @@ type StoreBatchOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *StoreBatchOrigin_ContextStore // *StoreBatchOrigin_Dsl Type isStoreBatchOrigin_Type `protobuf_oneof:"Type"` diff --git a/pkg/pb/availabilitypb/mscpb/mscpb.pb.go b/pkg/pb/availabilitypb/mscpb/mscpb.pb.go index b12d59ada..869a6a424 100644 --- a/pkg/pb/availabilitypb/mscpb/mscpb.pb.go +++ b/pkg/pb/availabilitypb/mscpb/mscpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: availabilitypb/mscpb/mscpb.proto package mscpb @@ -29,6 +29,7 @@ type Message struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Message_RequestSig // *Message_Sig // *Message_RequestBatch diff --git a/pkg/pb/batchfetcherpb/batchfetcherpb.pb.go b/pkg/pb/batchfetcherpb/batchfetcherpb.pb.go index f67eaeecf..3f8ea3436 100644 --- a/pkg/pb/batchfetcherpb/batchfetcherpb.pb.go +++ b/pkg/pb/batchfetcherpb/batchfetcherpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: batchfetcherpb/batchfetcherpb.proto package batchfetcherpb @@ -28,6 +28,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_NewOrderedBatch // *Event_ClientProgress Type isEvent_Type `protobuf_oneof:"Type"` diff --git a/pkg/pb/bcbpb/bcbpb.pb.go b/pkg/pb/bcbpb/bcbpb.pb.go index 67620e1e8..b72376997 100644 --- a/pkg/pb/bcbpb/bcbpb.pb.go +++ b/pkg/pb/bcbpb/bcbpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: bcbpb/bcbpb.proto package bcbpb @@ -28,6 +28,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_Request // *Event_Deliver Type isEvent_Type `protobuf_oneof:"type"` @@ -202,6 +203,7 @@ type Message struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Message_StartMessage // *Message_EchoMessage // *Message_FinalMessage diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go new file mode 100644 index 000000000..44088f3b5 --- /dev/null +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go @@ -0,0 +1,244 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.4 +// source: blockchainpb/bcmpb/bcmpb.proto + +package bcmpb + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + _ "github.com/filecoin-project/mir/pkg/pb/mir" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *Event_NewBlock + Type isEvent_Type `protobuf_oneof:"type"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[0] + 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 Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{0} +} + +func (m *Event) GetType() isEvent_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *Event) GetNewBlock() *NewBlock { + if x, ok := x.GetType().(*Event_NewBlock); ok { + return x.NewBlock + } + return nil +} + +type isEvent_Type interface { + isEvent_Type() +} + +type Event_NewBlock struct { + NewBlock *NewBlock `protobuf:"bytes,1,opt,name=new_block,json=newBlock,proto3,oneof"` +} + +func (*Event_NewBlock) isEvent_Type() {} + +type NewBlock struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Block *blockchainpb.Block `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"` +} + +func (x *NewBlock) Reset() { + *x = NewBlock{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewBlock) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewBlock) ProtoMessage() {} + +func (x *NewBlock) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[1] + 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 NewBlock.ProtoReflect.Descriptor instead. +func (*NewBlock) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{1} +} + +func (x *NewBlock) GetBlock() *blockchainpb.Block { + if x != nil { + return x.Block + } + return nil +} + +var File_blockchainpb_bcmpb_bcmpb_proto protoreflect.FileDescriptor + +var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ + 0x0a, 0x1e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, + 0x63, 0x6d, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x05, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, + 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4b, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, + 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, + 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, + 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, + 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, + 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, + 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_blockchainpb_bcmpb_bcmpb_proto_rawDescOnce sync.Once + file_blockchainpb_bcmpb_bcmpb_proto_rawDescData = file_blockchainpb_bcmpb_bcmpb_proto_rawDesc +) + +func file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP() []byte { + file_blockchainpb_bcmpb_bcmpb_proto_rawDescOnce.Do(func() { + file_blockchainpb_bcmpb_bcmpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_bcmpb_bcmpb_proto_rawDescData) + }) + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescData +} + +var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_blockchainpb_bcmpb_bcmpb_proto_goTypes = []interface{}{ + (*Event)(nil), // 0: bcmpb.Event + (*NewBlock)(nil), // 1: bcmpb.NewBlock + (*blockchainpb.Block)(nil), // 2: blockchainpb.Block +} +var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ + 1, // 0: bcmpb.Event.new_block:type_name -> bcmpb.NewBlock + 2, // 1: bcmpb.NewBlock.block:type_name -> blockchainpb.Block + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_blockchainpb_bcmpb_bcmpb_proto_init() } +func file_blockchainpb_bcmpb_bcmpb_proto_init() { + if File_blockchainpb_bcmpb_bcmpb_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewBlock); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Event_NewBlock)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_blockchainpb_bcmpb_bcmpb_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_blockchainpb_bcmpb_bcmpb_proto_goTypes, + DependencyIndexes: file_blockchainpb_bcmpb_bcmpb_proto_depIdxs, + MessageInfos: file_blockchainpb_bcmpb_bcmpb_proto_msgTypes, + }.Build() + File_blockchainpb_bcmpb_bcmpb_proto = out.File + file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = nil + file_blockchainpb_bcmpb_bcmpb_proto_goTypes = nil + file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = nil +} diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go new file mode 100644 index 000000000..74724e330 --- /dev/null +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go @@ -0,0 +1,13 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package bcmpb + +import ( + reflect "reflect" +) + +func (*Event) ReflectTypeOptions() []reflect.Type { + return []reflect.Type{ + reflect.TypeOf((*Event_NewBlock)(nil)), + } +} diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go new file mode 100644 index 000000000..339584ef7 --- /dev/null +++ b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go @@ -0,0 +1,16 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package bcmpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/events" + types "github.com/filecoin-project/mir/pkg/types" +) + +// Module-specific dsl functions for emitting events. + +func NewBlock(m dsl.Module, destModule types.ModuleID, block *blockchainpb.Block) { + dsl.EmitMirEvent(m, events.NewBlock(destModule, block)) +} diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go new file mode 100644 index 000000000..78be19936 --- /dev/null +++ b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go @@ -0,0 +1,29 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package bcmpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" +) + +// Module-specific dsl functions for processing events. + +func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func(ev *Ev) error) { + dsl.UponMirEvent[*types1.Event_Bcm](m, func(ev *types.Event) error { + w, ok := ev.Type.(W) + if !ok { + return nil + } + + return handler(w.Unwrap()) + }) +} + +func UponNewBlock(m dsl.Module, handler func(block *blockchainpb.Block) error) { + UponEvent[*types.Event_NewBlock](m, func(ev *types.NewBlock) error { + return handler(ev.Block) + }) +} diff --git a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go new file mode 100644 index 000000000..0b739b589 --- /dev/null +++ b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go @@ -0,0 +1,25 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package bcmpbevents + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types "github.com/filecoin-project/mir/pkg/types" +) + +func NewBlock(destModule types.ModuleID, block *blockchainpb.Block) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcm{ + Bcm: &types2.Event{ + Type: &types2.Event_NewBlock{ + NewBlock: &types2.NewBlock{ + Block: block, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go new file mode 100644 index 000000000..9371708b2 --- /dev/null +++ b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go @@ -0,0 +1,14 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package bcmpb + +type Event_Type = isEvent_Type + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func (w *Event_NewBlock) Unwrap() *NewBlock { + return w.NewBlock +} diff --git a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go new file mode 100644 index 000000000..837a0a77a --- /dev/null +++ b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go @@ -0,0 +1,118 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package bcmpbtypes + +import ( + mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" + reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" +) + +type Event struct { + Type Event_Type +} + +type Event_Type interface { + mirreflect.GeneratedType + isEvent_Type() + Pb() bcmpb.Event_Type +} + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func Event_TypeFromPb(pb bcmpb.Event_Type) Event_Type { + if pb == nil { + return nil + } + switch pb := pb.(type) { + case *bcmpb.Event_NewBlock: + return &Event_NewBlock{NewBlock: NewBlockFromPb(pb.NewBlock)} + } + return nil +} + +type Event_NewBlock struct { + NewBlock *NewBlock +} + +func (*Event_NewBlock) isEvent_Type() {} + +func (w *Event_NewBlock) Unwrap() *NewBlock { + return w.NewBlock +} + +func (w *Event_NewBlock) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.NewBlock == nil { + return &bcmpb.Event_NewBlock{} + } + return &bcmpb.Event_NewBlock{NewBlock: (w.NewBlock).Pb()} +} + +func (*Event_NewBlock) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_NewBlock]()} +} + +func EventFromPb(pb *bcmpb.Event) *Event { + if pb == nil { + return nil + } + return &Event{ + Type: Event_TypeFromPb(pb.Type), + } +} + +func (m *Event) Pb() *bcmpb.Event { + if m == nil { + return nil + } + pbMessage := &bcmpb.Event{} + { + if m.Type != nil { + pbMessage.Type = (m.Type).Pb() + } + } + + return pbMessage +} + +func (*Event) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event]()} +} + +type NewBlock struct { + Block *blockchainpb.Block +} + +func NewBlockFromPb(pb *bcmpb.NewBlock) *NewBlock { + if pb == nil { + return nil + } + return &NewBlock{ + Block: pb.Block, + } +} + +func (m *NewBlock) Pb() *bcmpb.NewBlock { + if m == nil { + return nil + } + pbMessage := &bcmpb.NewBlock{} + { + if m.Block != nil { + pbMessage.Block = m.Block + } + } + + return pbMessage +} + +func (*NewBlock) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.NewBlock]()} +} diff --git a/pkg/pb/blockchainpb/blockchainpb.pb.go b/pkg/pb/blockchainpb/blockchainpb.pb.go new file mode 100644 index 000000000..16c3c4cb1 --- /dev/null +++ b/pkg/pb/blockchainpb/blockchainpb.pb.go @@ -0,0 +1,379 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.4 +// source: blockchainpb/blockchainpb.proto + +package blockchainpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Blocktree struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Blocks []*Block `protobuf:"bytes,1,rep,name=blocks,proto3" json:"blocks,omitempty"` + Leaves []uint64 `protobuf:"varint,2,rep,packed,name=leaves,proto3" json:"leaves,omitempty"` +} + +func (x *Blocktree) Reset() { + *x = Blocktree{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Blocktree) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Blocktree) ProtoMessage() {} + +func (x *Blocktree) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[0] + 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 Blocktree.ProtoReflect.Descriptor instead. +func (*Blocktree) Descriptor() ([]byte, []int) { + return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{0} +} + +func (x *Blocktree) GetBlocks() []*Block { + if x != nil { + return x.Blocks + } + return nil +} + +func (x *Blocktree) GetLeaves() []uint64 { + if x != nil { + return x.Leaves + } + return nil +} + +type Blockchain struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Blocks []*Block `protobuf:"bytes,1,rep,name=blocks,proto3" json:"blocks,omitempty"` // ordered, no forks -> the 'current' chain +} + +func (x *Blockchain) Reset() { + *x = Blockchain{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Blockchain) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Blockchain) ProtoMessage() {} + +func (x *Blockchain) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[1] + 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 Blockchain.ProtoReflect.Descriptor instead. +func (*Blockchain) Descriptor() ([]byte, []int) { + return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{1} +} + +func (x *Blockchain) GetBlocks() []*Block { + if x != nil { + return x.Blocks + } + return nil +} + +type Block struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BlockId uint64 `protobuf:"varint,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + PreviousBlockId uint64 `protobuf:"varint,2,opt,name=previous_block_id,json=previousBlockId,proto3" json:"previous_block_id,omitempty"` + Payload *Payload `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` + Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *Block) Reset() { + *x = Block{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Block) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Block) ProtoMessage() {} + +func (x *Block) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[2] + 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 Block.ProtoReflect.Descriptor instead. +func (*Block) Descriptor() ([]byte, []int) { + return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{2} +} + +func (x *Block) GetBlockId() uint64 { + if x != nil { + return x.BlockId + } + return 0 +} + +func (x *Block) GetPreviousBlockId() uint64 { + if x != nil { + return x.PreviousBlockId + } + return 0 +} + +func (x *Block) GetPayload() *Payload { + if x != nil { + return x.Payload + } + return nil +} + +func (x *Block) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + +type Payload struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` +} + +func (x *Payload) Reset() { + *x = Payload{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Payload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payload) ProtoMessage() {} + +func (x *Payload) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[3] + 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 Payload.ProtoReflect.Descriptor instead. +func (*Payload) Descriptor() ([]byte, []int) { + return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{3} +} + +func (x *Payload) GetText() string { + if x != nil { + return x.Text + } + return "" +} + +var File_blockchainpb_blockchainpb_proto protoreflect.FileDescriptor + +var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ + 0x0a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x12, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x22, + 0x50, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, + 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, + 0x73, 0x22, 0x39, 0x0a, 0x0a, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, + 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x9d, 0x01, 0x0a, + 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, + 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x70, 0x72, + 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2f, 0x0a, + 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1c, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x1d, 0x0a, 0x07, + 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x42, 0x35, 0x5a, 0x33, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, + 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_blockchainpb_blockchainpb_proto_rawDescOnce sync.Once + file_blockchainpb_blockchainpb_proto_rawDescData = file_blockchainpb_blockchainpb_proto_rawDesc +) + +func file_blockchainpb_blockchainpb_proto_rawDescGZIP() []byte { + file_blockchainpb_blockchainpb_proto_rawDescOnce.Do(func() { + file_blockchainpb_blockchainpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_blockchainpb_proto_rawDescData) + }) + return file_blockchainpb_blockchainpb_proto_rawDescData +} + +var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_blockchainpb_blockchainpb_proto_goTypes = []interface{}{ + (*Blocktree)(nil), // 0: blockchainpb.Blocktree + (*Blockchain)(nil), // 1: blockchainpb.Blockchain + (*Block)(nil), // 2: blockchainpb.Block + (*Payload)(nil), // 3: blockchainpb.Payload +} +var file_blockchainpb_blockchainpb_proto_depIdxs = []int32{ + 2, // 0: blockchainpb.Blocktree.blocks:type_name -> blockchainpb.Block + 2, // 1: blockchainpb.Blockchain.blocks:type_name -> blockchainpb.Block + 3, // 2: blockchainpb.Block.payload:type_name -> blockchainpb.Payload + 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 +} + +func init() { file_blockchainpb_blockchainpb_proto_init() } +func file_blockchainpb_blockchainpb_proto_init() { + if File_blockchainpb_blockchainpb_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_blockchainpb_blockchainpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Blocktree); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_blockchainpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Blockchain); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_blockchainpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Block); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_blockchainpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Payload); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_blockchainpb_blockchainpb_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_blockchainpb_blockchainpb_proto_goTypes, + DependencyIndexes: file_blockchainpb_blockchainpb_proto_depIdxs, + MessageInfos: file_blockchainpb_blockchainpb_proto_msgTypes, + }.Build() + File_blockchainpb_blockchainpb_proto = out.File + file_blockchainpb_blockchainpb_proto_rawDesc = nil + file_blockchainpb_blockchainpb_proto_goTypes = nil + file_blockchainpb_blockchainpb_proto_depIdxs = nil +} diff --git a/pkg/pb/blockchainpb/communicationpb/communicationpb.pb.go b/pkg/pb/blockchainpb/communicationpb/communicationpb.pb.go new file mode 100644 index 000000000..3fb2e3242 --- /dev/null +++ b/pkg/pb/blockchainpb/communicationpb/communicationpb.pb.go @@ -0,0 +1,405 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.4 +// source: blockchainpb/communicationpb/communicationpb.proto + +package communicationpb + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + _ "github.com/filecoin-project/mir/pkg/pb/mir" + _ "github.com/filecoin-project/mir/pkg/pb/net" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *Event_NewBlock + Type isEvent_Type `protobuf_oneof:"type"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[0] + 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 Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_blockchainpb_communicationpb_communicationpb_proto_rawDescGZIP(), []int{0} +} + +func (m *Event) GetType() isEvent_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *Event) GetNewBlock() *NewBlock { + if x, ok := x.GetType().(*Event_NewBlock); ok { + return x.NewBlock + } + return nil +} + +type isEvent_Type interface { + isEvent_Type() +} + +type Event_NewBlock struct { + NewBlock *NewBlock `protobuf:"bytes,1,opt,name=new_block,json=newBlock,proto3,oneof"` +} + +func (*Event_NewBlock) isEvent_Type() {} + +type NewBlock struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Block *blockchainpb.Block `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"` +} + +func (x *NewBlock) Reset() { + *x = NewBlock{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewBlock) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewBlock) ProtoMessage() {} + +func (x *NewBlock) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[1] + 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 NewBlock.ProtoReflect.Descriptor instead. +func (*NewBlock) Descriptor() ([]byte, []int) { + return file_blockchainpb_communicationpb_communicationpb_proto_rawDescGZIP(), []int{1} +} + +func (x *NewBlock) GetBlock() *blockchainpb.Block { + if x != nil { + return x.Block + } + return nil +} + +type Message struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *Message_NewBlock + Type isMessage_Type `protobuf_oneof:"type"` +} + +func (x *Message) Reset() { + *x = Message{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Message) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Message) ProtoMessage() {} + +func (x *Message) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[2] + 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 Message.ProtoReflect.Descriptor instead. +func (*Message) Descriptor() ([]byte, []int) { + return file_blockchainpb_communicationpb_communicationpb_proto_rawDescGZIP(), []int{2} +} + +func (m *Message) GetType() isMessage_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *Message) GetNewBlock() *NewBlockMessage { + if x, ok := x.GetType().(*Message_NewBlock); ok { + return x.NewBlock + } + return nil +} + +type isMessage_Type interface { + isMessage_Type() +} + +type Message_NewBlock struct { + NewBlock *NewBlockMessage `protobuf:"bytes,1,opt,name=new_block,json=newBlock,proto3,oneof"` +} + +func (*Message_NewBlock) isMessage_Type() {} + +type NewBlockMessage struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Block *blockchainpb.Block `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"` +} + +func (x *NewBlockMessage) Reset() { + *x = NewBlockMessage{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewBlockMessage) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewBlockMessage) ProtoMessage() {} + +func (x *NewBlockMessage) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[3] + 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 NewBlockMessage.ProtoReflect.Descriptor instead. +func (*NewBlockMessage) Descriptor() ([]byte, []int) { + return file_blockchainpb_communicationpb_communicationpb_proto_rawDescGZIP(), []int{3} +} + +func (x *NewBlockMessage) GetBlock() *blockchainpb.Block { + if x != nil { + return x.Block + } + return nil +} + +var File_blockchainpb_communicationpb_communicationpb_proto protoreflect.FileDescriptor + +var file_blockchainpb_communicationpb_communicationpb_proto_rawDesc = []byte{ + 0x0a, 0x32, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x63, + 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, + 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, + 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, + 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0x55, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x6e, + 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x77, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x3f, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x3a, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x22, 0x42, 0x0a, 0x0f, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0xd0, 0xe4, 0x1d, 0x01, 0x42, 0x45, 0x5a, 0x43, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, + 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, + 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, + 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_blockchainpb_communicationpb_communicationpb_proto_rawDescOnce sync.Once + file_blockchainpb_communicationpb_communicationpb_proto_rawDescData = file_blockchainpb_communicationpb_communicationpb_proto_rawDesc +) + +func file_blockchainpb_communicationpb_communicationpb_proto_rawDescGZIP() []byte { + file_blockchainpb_communicationpb_communicationpb_proto_rawDescOnce.Do(func() { + file_blockchainpb_communicationpb_communicationpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_communicationpb_communicationpb_proto_rawDescData) + }) + return file_blockchainpb_communicationpb_communicationpb_proto_rawDescData +} + +var file_blockchainpb_communicationpb_communicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_blockchainpb_communicationpb_communicationpb_proto_goTypes = []interface{}{ + (*Event)(nil), // 0: communicationpb.Event + (*NewBlock)(nil), // 1: communicationpb.NewBlock + (*Message)(nil), // 2: communicationpb.Message + (*NewBlockMessage)(nil), // 3: communicationpb.NewBlockMessage + (*blockchainpb.Block)(nil), // 4: blockchainpb.Block +} +var file_blockchainpb_communicationpb_communicationpb_proto_depIdxs = []int32{ + 1, // 0: communicationpb.Event.new_block:type_name -> communicationpb.NewBlock + 4, // 1: communicationpb.NewBlock.block:type_name -> blockchainpb.Block + 3, // 2: communicationpb.Message.new_block:type_name -> communicationpb.NewBlockMessage + 4, // 3: communicationpb.NewBlockMessage.block:type_name -> blockchainpb.Block + 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_blockchainpb_communicationpb_communicationpb_proto_init() } +func file_blockchainpb_communicationpb_communicationpb_proto_init() { + if File_blockchainpb_communicationpb_communicationpb_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewBlock); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Message); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewBlockMessage); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Event_NewBlock)(nil), + } + file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*Message_NewBlock)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_blockchainpb_communicationpb_communicationpb_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_blockchainpb_communicationpb_communicationpb_proto_goTypes, + DependencyIndexes: file_blockchainpb_communicationpb_communicationpb_proto_depIdxs, + MessageInfos: file_blockchainpb_communicationpb_communicationpb_proto_msgTypes, + }.Build() + File_blockchainpb_communicationpb_communicationpb_proto = out.File + file_blockchainpb_communicationpb_communicationpb_proto_rawDesc = nil + file_blockchainpb_communicationpb_communicationpb_proto_goTypes = nil + file_blockchainpb_communicationpb_communicationpb_proto_depIdxs = nil +} diff --git a/pkg/pb/blockchainpb/communicationpb/communicationpb.pb.mir.go b/pkg/pb/blockchainpb/communicationpb/communicationpb.pb.mir.go new file mode 100644 index 000000000..1eabded66 --- /dev/null +++ b/pkg/pb/blockchainpb/communicationpb/communicationpb.pb.mir.go @@ -0,0 +1,19 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package communicationpb + +import ( + reflect "reflect" +) + +func (*Event) ReflectTypeOptions() []reflect.Type { + return []reflect.Type{ + reflect.TypeOf((*Event_NewBlock)(nil)), + } +} + +func (*Message) ReflectTypeOptions() []reflect.Type { + return []reflect.Type{ + reflect.TypeOf((*Message_NewBlock)(nil)), + } +} diff --git a/pkg/pb/blockchainpb/communicationpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/communicationpb/dsl/emit.mir.go new file mode 100644 index 000000000..7d47bebf8 --- /dev/null +++ b/pkg/pb/blockchainpb/communicationpb/dsl/emit.mir.go @@ -0,0 +1,16 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package communicationpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/events" + types "github.com/filecoin-project/mir/pkg/types" +) + +// Module-specific dsl functions for emitting events. + +func NewBlock(m dsl.Module, destModule types.ModuleID, block *blockchainpb.Block) { + dsl.EmitMirEvent(m, events.NewBlock(destModule, block)) +} diff --git a/pkg/pb/blockchainpb/communicationpb/dsl/messages.mir.go b/pkg/pb/blockchainpb/communicationpb/dsl/messages.mir.go new file mode 100644 index 000000000..53f1cfbd2 --- /dev/null +++ b/pkg/pb/blockchainpb/communicationpb/dsl/messages.mir.go @@ -0,0 +1,31 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package communicationpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + dsl1 "github.com/filecoin-project/mir/pkg/pb/messagepb/dsl" + types2 "github.com/filecoin-project/mir/pkg/pb/messagepb/types" + types1 "github.com/filecoin-project/mir/pkg/types" +) + +// Module-specific dsl functions for processing net messages. + +func UponMessageReceived[W types.Message_TypeWrapper[M], M any](m dsl.Module, handler func(from types1.NodeID, msg *M) error) { + dsl1.UponMessageReceived[*types2.Message_Communicationpb](m, func(from types1.NodeID, msg *types.Message) error { + w, ok := msg.Type.(W) + if !ok { + return nil + } + + return handler(from, w.Unwrap()) + }) +} + +func UponNewBlockMessageReceived(m dsl.Module, handler func(from types1.NodeID, block *blockchainpb.Block) error) { + UponMessageReceived[*types.Message_NewBlock](m, func(from types1.NodeID, msg *types.NewBlockMessage) error { + return handler(from, msg.Block) + }) +} diff --git a/pkg/pb/blockchainpb/communicationpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/communicationpb/dsl/upon.mir.go new file mode 100644 index 000000000..976afedfe --- /dev/null +++ b/pkg/pb/blockchainpb/communicationpb/dsl/upon.mir.go @@ -0,0 +1,29 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package communicationpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" +) + +// Module-specific dsl functions for processing events. + +func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func(ev *Ev) error) { + dsl.UponMirEvent[*types1.Event_Communication](m, func(ev *types.Event) error { + w, ok := ev.Type.(W) + if !ok { + return nil + } + + return handler(w.Unwrap()) + }) +} + +func UponNewBlock(m dsl.Module, handler func(block *blockchainpb.Block) error) { + UponEvent[*types.Event_NewBlock](m, func(ev *types.NewBlock) error { + return handler(ev.Block) + }) +} diff --git a/pkg/pb/blockchainpb/communicationpb/events/events.mir.go b/pkg/pb/blockchainpb/communicationpb/events/events.mir.go new file mode 100644 index 000000000..a79e7352c --- /dev/null +++ b/pkg/pb/blockchainpb/communicationpb/events/events.mir.go @@ -0,0 +1,25 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package communicationpbevents + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types "github.com/filecoin-project/mir/pkg/types" +) + +func NewBlock(destModule types.ModuleID, block *blockchainpb.Block) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Communication{ + Communication: &types2.Event{ + Type: &types2.Event_NewBlock{ + NewBlock: &types2.NewBlock{ + Block: block, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/communicationpb/msgs/msgs.mir.go b/pkg/pb/blockchainpb/communicationpb/msgs/msgs.mir.go new file mode 100644 index 000000000..9a4f1c8e5 --- /dev/null +++ b/pkg/pb/blockchainpb/communicationpb/msgs/msgs.mir.go @@ -0,0 +1,25 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package communicationpbmsgs + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/messagepb/types" + types "github.com/filecoin-project/mir/pkg/types" +) + +func NewBlockMessage(destModule types.ModuleID, block *blockchainpb.Block) *types1.Message { + return &types1.Message{ + DestModule: destModule, + Type: &types1.Message_Communicationpb{ + Communicationpb: &types2.Message{ + Type: &types2.Message_NewBlock{ + NewBlock: &types2.NewBlockMessage{ + Block: block, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/communicationpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/communicationpb/oneof_interfaces.mir.go new file mode 100644 index 000000000..1664d6a4a --- /dev/null +++ b/pkg/pb/blockchainpb/communicationpb/oneof_interfaces.mir.go @@ -0,0 +1,25 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package communicationpb + +type Event_Type = isEvent_Type + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func (w *Event_NewBlock) Unwrap() *NewBlock { + return w.NewBlock +} + +type Message_Type = isMessage_Type + +type Message_TypeWrapper[T any] interface { + Message_Type + Unwrap() *T +} + +func (w *Message_NewBlock) Unwrap() *NewBlockMessage { + return w.NewBlock +} diff --git a/pkg/pb/blockchainpb/communicationpb/types/types.mir.go b/pkg/pb/blockchainpb/communicationpb/types/types.mir.go new file mode 100644 index 000000000..74b4afe28 --- /dev/null +++ b/pkg/pb/blockchainpb/communicationpb/types/types.mir.go @@ -0,0 +1,226 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package communicationpbtypes + +import ( + mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" +) + +type Event struct { + Type Event_Type +} + +type Event_Type interface { + mirreflect.GeneratedType + isEvent_Type() + Pb() communicationpb.Event_Type +} + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func Event_TypeFromPb(pb communicationpb.Event_Type) Event_Type { + if pb == nil { + return nil + } + switch pb := pb.(type) { + case *communicationpb.Event_NewBlock: + return &Event_NewBlock{NewBlock: NewBlockFromPb(pb.NewBlock)} + } + return nil +} + +type Event_NewBlock struct { + NewBlock *NewBlock +} + +func (*Event_NewBlock) isEvent_Type() {} + +func (w *Event_NewBlock) Unwrap() *NewBlock { + return w.NewBlock +} + +func (w *Event_NewBlock) Pb() communicationpb.Event_Type { + if w == nil { + return nil + } + if w.NewBlock == nil { + return &communicationpb.Event_NewBlock{} + } + return &communicationpb.Event_NewBlock{NewBlock: (w.NewBlock).Pb()} +} + +func (*Event_NewBlock) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.Event_NewBlock]()} +} + +func EventFromPb(pb *communicationpb.Event) *Event { + if pb == nil { + return nil + } + return &Event{ + Type: Event_TypeFromPb(pb.Type), + } +} + +func (m *Event) Pb() *communicationpb.Event { + if m == nil { + return nil + } + pbMessage := &communicationpb.Event{} + { + if m.Type != nil { + pbMessage.Type = (m.Type).Pb() + } + } + + return pbMessage +} + +func (*Event) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.Event]()} +} + +type NewBlock struct { + Block *blockchainpb.Block +} + +func NewBlockFromPb(pb *communicationpb.NewBlock) *NewBlock { + if pb == nil { + return nil + } + return &NewBlock{ + Block: pb.Block, + } +} + +func (m *NewBlock) Pb() *communicationpb.NewBlock { + if m == nil { + return nil + } + pbMessage := &communicationpb.NewBlock{} + { + if m.Block != nil { + pbMessage.Block = m.Block + } + } + + return pbMessage +} + +func (*NewBlock) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.NewBlock]()} +} + +type Message struct { + Type Message_Type +} + +type Message_Type interface { + mirreflect.GeneratedType + isMessage_Type() + Pb() communicationpb.Message_Type +} + +type Message_TypeWrapper[T any] interface { + Message_Type + Unwrap() *T +} + +func Message_TypeFromPb(pb communicationpb.Message_Type) Message_Type { + if pb == nil { + return nil + } + switch pb := pb.(type) { + case *communicationpb.Message_NewBlock: + return &Message_NewBlock{NewBlock: NewBlockMessageFromPb(pb.NewBlock)} + } + return nil +} + +type Message_NewBlock struct { + NewBlock *NewBlockMessage +} + +func (*Message_NewBlock) isMessage_Type() {} + +func (w *Message_NewBlock) Unwrap() *NewBlockMessage { + return w.NewBlock +} + +func (w *Message_NewBlock) Pb() communicationpb.Message_Type { + if w == nil { + return nil + } + if w.NewBlock == nil { + return &communicationpb.Message_NewBlock{} + } + return &communicationpb.Message_NewBlock{NewBlock: (w.NewBlock).Pb()} +} + +func (*Message_NewBlock) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.Message_NewBlock]()} +} + +func MessageFromPb(pb *communicationpb.Message) *Message { + if pb == nil { + return nil + } + return &Message{ + Type: Message_TypeFromPb(pb.Type), + } +} + +func (m *Message) Pb() *communicationpb.Message { + if m == nil { + return nil + } + pbMessage := &communicationpb.Message{} + { + if m.Type != nil { + pbMessage.Type = (m.Type).Pb() + } + } + + return pbMessage +} + +func (*Message) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.Message]()} +} + +type NewBlockMessage struct { + Block *blockchainpb.Block +} + +func NewBlockMessageFromPb(pb *communicationpb.NewBlockMessage) *NewBlockMessage { + if pb == nil { + return nil + } + return &NewBlockMessage{ + Block: pb.Block, + } +} + +func (m *NewBlockMessage) Pb() *communicationpb.NewBlockMessage { + if m == nil { + return nil + } + pbMessage := &communicationpb.NewBlockMessage{} + { + if m.Block != nil { + pbMessage.Block = m.Block + } + } + + return pbMessage +} + +func (*NewBlockMessage) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.NewBlockMessage]()} +} diff --git a/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go new file mode 100644 index 000000000..0822426ea --- /dev/null +++ b/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go @@ -0,0 +1,16 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package minerpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/events" + types "github.com/filecoin-project/mir/pkg/types" +) + +// Module-specific dsl functions for emitting events. + +func BlockRequest(m dsl.Module, destModule types.ModuleID, headId uint64, payload *blockchainpb.Payload) { + dsl.EmitMirEvent(m, events.BlockRequest(destModule, headId, payload)) +} diff --git a/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go new file mode 100644 index 000000000..395302269 --- /dev/null +++ b/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go @@ -0,0 +1,29 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package minerpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" +) + +// Module-specific dsl functions for processing events. + +func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func(ev *Ev) error) { + dsl.UponMirEvent[*types1.Event_Miner](m, func(ev *types.Event) error { + w, ok := ev.Type.(W) + if !ok { + return nil + } + + return handler(w.Unwrap()) + }) +} + +func UponBlockRequest(m dsl.Module, handler func(headId uint64, payload *blockchainpb.Payload) error) { + UponEvent[*types.Event_BlockRequest](m, func(ev *types.BlockRequest) error { + return handler(ev.HeadId, ev.Payload) + }) +} diff --git a/pkg/pb/blockchainpb/minerpb/events/events.mir.go b/pkg/pb/blockchainpb/minerpb/events/events.mir.go new file mode 100644 index 000000000..e72bf6c94 --- /dev/null +++ b/pkg/pb/blockchainpb/minerpb/events/events.mir.go @@ -0,0 +1,26 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package minerpbevents + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types "github.com/filecoin-project/mir/pkg/types" +) + +func BlockRequest(destModule types.ModuleID, headId uint64, payload *blockchainpb.Payload) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Miner{ + Miner: &types2.Event{ + Type: &types2.Event_BlockRequest{ + BlockRequest: &types2.BlockRequest{ + HeadId: headId, + Payload: payload, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/minerpb/minerpb.pb.go b/pkg/pb/blockchainpb/minerpb/minerpb.pb.go new file mode 100644 index 000000000..e8da24d42 --- /dev/null +++ b/pkg/pb/blockchainpb/minerpb/minerpb.pb.go @@ -0,0 +1,255 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.4 +// source: blockchainpb/minerpb/minerpb.proto + +package minerpb + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + _ "github.com/filecoin-project/mir/pkg/pb/mir" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *Event_BlockRequest + Type isEvent_Type `protobuf_oneof:"type"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_minerpb_minerpb_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_minerpb_minerpb_proto_msgTypes[0] + 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 Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_blockchainpb_minerpb_minerpb_proto_rawDescGZIP(), []int{0} +} + +func (m *Event) GetType() isEvent_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *Event) GetBlockRequest() *BlockRequest { + if x, ok := x.GetType().(*Event_BlockRequest); ok { + return x.BlockRequest + } + return nil +} + +type isEvent_Type interface { + isEvent_Type() +} + +type Event_BlockRequest struct { + BlockRequest *BlockRequest `protobuf:"bytes,1,opt,name=block_request,json=blockRequest,proto3,oneof"` +} + +func (*Event_BlockRequest) isEvent_Type() {} + +type BlockRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + HeadId uint64 `protobuf:"varint,1,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` + Payload *blockchainpb.Payload `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *BlockRequest) Reset() { + *x = BlockRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_minerpb_minerpb_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockRequest) ProtoMessage() {} + +func (x *BlockRequest) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_minerpb_minerpb_proto_msgTypes[1] + 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 BlockRequest.ProtoReflect.Descriptor instead. +func (*BlockRequest) Descriptor() ([]byte, []int) { + return file_blockchainpb_minerpb_minerpb_proto_rawDescGZIP(), []int{1} +} + +func (x *BlockRequest) GetHeadId() uint64 { + if x != nil { + return x.HeadId + } + return 0 +} + +func (x *BlockRequest) GetPayload() *blockchainpb.Payload { + if x != nil { + return x.Payload + } + return nil +} + +var File_blockchainpb_minerpb_minerpb_proto protoreflect.FileDescriptor + +var file_blockchainpb_minerpb_minerpb_proto_rawDesc = []byte{ + 0x0a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x6d, + 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x1a, 0x1c, 0x6d, + 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x59, 0x0a, 0x05, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, + 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, + 0x12, 0x2f, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, + 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x6d, + 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_blockchainpb_minerpb_minerpb_proto_rawDescOnce sync.Once + file_blockchainpb_minerpb_minerpb_proto_rawDescData = file_blockchainpb_minerpb_minerpb_proto_rawDesc +) + +func file_blockchainpb_minerpb_minerpb_proto_rawDescGZIP() []byte { + file_blockchainpb_minerpb_minerpb_proto_rawDescOnce.Do(func() { + file_blockchainpb_minerpb_minerpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_minerpb_minerpb_proto_rawDescData) + }) + return file_blockchainpb_minerpb_minerpb_proto_rawDescData +} + +var file_blockchainpb_minerpb_minerpb_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_blockchainpb_minerpb_minerpb_proto_goTypes = []interface{}{ + (*Event)(nil), // 0: minerpb.Event + (*BlockRequest)(nil), // 1: minerpb.BlockRequest + (*blockchainpb.Payload)(nil), // 2: blockchainpb.Payload +} +var file_blockchainpb_minerpb_minerpb_proto_depIdxs = []int32{ + 1, // 0: minerpb.Event.block_request:type_name -> minerpb.BlockRequest + 2, // 1: minerpb.BlockRequest.payload:type_name -> blockchainpb.Payload + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name +} + +func init() { file_blockchainpb_minerpb_minerpb_proto_init() } +func file_blockchainpb_minerpb_minerpb_proto_init() { + if File_blockchainpb_minerpb_minerpb_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_blockchainpb_minerpb_minerpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_minerpb_minerpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_blockchainpb_minerpb_minerpb_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Event_BlockRequest)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_blockchainpb_minerpb_minerpb_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_blockchainpb_minerpb_minerpb_proto_goTypes, + DependencyIndexes: file_blockchainpb_minerpb_minerpb_proto_depIdxs, + MessageInfos: file_blockchainpb_minerpb_minerpb_proto_msgTypes, + }.Build() + File_blockchainpb_minerpb_minerpb_proto = out.File + file_blockchainpb_minerpb_minerpb_proto_rawDesc = nil + file_blockchainpb_minerpb_minerpb_proto_goTypes = nil + file_blockchainpb_minerpb_minerpb_proto_depIdxs = nil +} diff --git a/pkg/pb/blockchainpb/minerpb/minerpb.pb.mir.go b/pkg/pb/blockchainpb/minerpb/minerpb.pb.mir.go new file mode 100644 index 000000000..5956b9521 --- /dev/null +++ b/pkg/pb/blockchainpb/minerpb/minerpb.pb.mir.go @@ -0,0 +1,13 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package minerpb + +import ( + reflect "reflect" +) + +func (*Event) ReflectTypeOptions() []reflect.Type { + return []reflect.Type{ + reflect.TypeOf((*Event_BlockRequest)(nil)), + } +} diff --git a/pkg/pb/blockchainpb/minerpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/minerpb/oneof_interfaces.mir.go new file mode 100644 index 000000000..13c055770 --- /dev/null +++ b/pkg/pb/blockchainpb/minerpb/oneof_interfaces.mir.go @@ -0,0 +1,14 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package minerpb + +type Event_Type = isEvent_Type + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func (w *Event_BlockRequest) Unwrap() *BlockRequest { + return w.BlockRequest +} diff --git a/pkg/pb/blockchainpb/minerpb/types/types.mir.go b/pkg/pb/blockchainpb/minerpb/types/types.mir.go new file mode 100644 index 000000000..cb36f4ddc --- /dev/null +++ b/pkg/pb/blockchainpb/minerpb/types/types.mir.go @@ -0,0 +1,121 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package minerpbtypes + +import ( + mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" + reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" +) + +type Event struct { + Type Event_Type +} + +type Event_Type interface { + mirreflect.GeneratedType + isEvent_Type() + Pb() minerpb.Event_Type +} + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func Event_TypeFromPb(pb minerpb.Event_Type) Event_Type { + if pb == nil { + return nil + } + switch pb := pb.(type) { + case *minerpb.Event_BlockRequest: + return &Event_BlockRequest{BlockRequest: BlockRequestFromPb(pb.BlockRequest)} + } + return nil +} + +type Event_BlockRequest struct { + BlockRequest *BlockRequest +} + +func (*Event_BlockRequest) isEvent_Type() {} + +func (w *Event_BlockRequest) Unwrap() *BlockRequest { + return w.BlockRequest +} + +func (w *Event_BlockRequest) Pb() minerpb.Event_Type { + if w == nil { + return nil + } + if w.BlockRequest == nil { + return &minerpb.Event_BlockRequest{} + } + return &minerpb.Event_BlockRequest{BlockRequest: (w.BlockRequest).Pb()} +} + +func (*Event_BlockRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*minerpb.Event_BlockRequest]()} +} + +func EventFromPb(pb *minerpb.Event) *Event { + if pb == nil { + return nil + } + return &Event{ + Type: Event_TypeFromPb(pb.Type), + } +} + +func (m *Event) Pb() *minerpb.Event { + if m == nil { + return nil + } + pbMessage := &minerpb.Event{} + { + if m.Type != nil { + pbMessage.Type = (m.Type).Pb() + } + } + + return pbMessage +} + +func (*Event) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*minerpb.Event]()} +} + +type BlockRequest struct { + HeadId uint64 + Payload *blockchainpb.Payload +} + +func BlockRequestFromPb(pb *minerpb.BlockRequest) *BlockRequest { + if pb == nil { + return nil + } + return &BlockRequest{ + HeadId: pb.HeadId, + Payload: pb.Payload, + } +} + +func (m *BlockRequest) Pb() *minerpb.BlockRequest { + if m == nil { + return nil + } + pbMessage := &minerpb.BlockRequest{} + { + pbMessage.HeadId = m.HeadId + if m.Payload != nil { + pbMessage.Payload = m.Payload + } + } + + return pbMessage +} + +func (*BlockRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*minerpb.BlockRequest]()} +} diff --git a/pkg/pb/blockchainpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/oneof_interfaces.mir.go new file mode 100644 index 000000000..a1e6dd597 --- /dev/null +++ b/pkg/pb/blockchainpb/oneof_interfaces.mir.go @@ -0,0 +1,3 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package blockchainpb diff --git a/pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go new file mode 100644 index 000000000..2d2377c84 --- /dev/null +++ b/pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go @@ -0,0 +1,15 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package tpmpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/events" + types "github.com/filecoin-project/mir/pkg/types" +) + +// Module-specific dsl functions for emitting events. + +func NewHead(m dsl.Module, destModule types.ModuleID, headId uint64) { + dsl.EmitMirEvent(m, events.NewHead(destModule, headId)) +} diff --git a/pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go new file mode 100644 index 000000000..b4fd28eec --- /dev/null +++ b/pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go @@ -0,0 +1,28 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package tpmpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" +) + +// Module-specific dsl functions for processing events. + +func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func(ev *Ev) error) { + dsl.UponMirEvent[*types1.Event_Tpm](m, func(ev *types.Event) error { + w, ok := ev.Type.(W) + if !ok { + return nil + } + + return handler(w.Unwrap()) + }) +} + +func UponNewHead(m dsl.Module, handler func(headId uint64) error) { + UponEvent[*types.Event_NewHead](m, func(ev *types.NewHead) error { + return handler(ev.HeadId) + }) +} diff --git a/pkg/pb/blockchainpb/tpmpb/events/events.mir.go b/pkg/pb/blockchainpb/tpmpb/events/events.mir.go new file mode 100644 index 000000000..87cc301cf --- /dev/null +++ b/pkg/pb/blockchainpb/tpmpb/events/events.mir.go @@ -0,0 +1,24 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package tpmpbevents + +import ( + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types "github.com/filecoin-project/mir/pkg/types" +) + +func NewHead(destModule types.ModuleID, headId uint64) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Tpm{ + Tpm: &types2.Event{ + Type: &types2.Event_NewHead{ + NewHead: &types2.NewHead{ + HeadId: headId, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go new file mode 100644 index 000000000..e76988397 --- /dev/null +++ b/pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go @@ -0,0 +1,14 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package tpmpb + +type Event_Type = isEvent_Type + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func (w *Event_NewHead) Unwrap() *NewHead { + return w.NewHead +} diff --git a/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go b/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go new file mode 100644 index 000000000..91ca10c4a --- /dev/null +++ b/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go @@ -0,0 +1,238 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.4 +// source: blockchainpb/tpmpb/tpmpb.proto + +package tpmpb + +import ( + _ "github.com/filecoin-project/mir/pkg/pb/mir" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *Event_NewHead + Type isEvent_Type `protobuf_oneof:"type"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[0] + 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 Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_blockchainpb_tpmpb_tpmpb_proto_rawDescGZIP(), []int{0} +} + +func (m *Event) GetType() isEvent_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *Event) GetNewHead() *NewHead { + if x, ok := x.GetType().(*Event_NewHead); ok { + return x.NewHead + } + return nil +} + +type isEvent_Type interface { + isEvent_Type() +} + +type Event_NewHead struct { + NewHead *NewHead `protobuf:"bytes,1,opt,name=new_head,json=newHead,proto3,oneof"` +} + +func (*Event_NewHead) isEvent_Type() {} + +type NewHead struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // blockchainpb.Blockchain chain = 1; + HeadId uint64 `protobuf:"varint,1,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` +} + +func (x *NewHead) Reset() { + *x = NewHead{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewHead) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewHead) ProtoMessage() {} + +func (x *NewHead) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[1] + 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 NewHead.ProtoReflect.Descriptor instead. +func (*NewHead) Descriptor() ([]byte, []int) { + return file_blockchainpb_tpmpb_tpmpb_proto_rawDescGZIP(), []int{1} +} + +func (x *NewHead) GetHeadId() uint64 { + if x != nil { + return x.HeadId + } + return 0 +} + +var File_blockchainpb_tpmpb_tpmpb_proto protoreflect.FileDescriptor + +var file_blockchainpb_tpmpb_tpmpb_proto_rawDesc = []byte{ + 0x0a, 0x1e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x74, + 0x70, 0x6d, 0x70, 0x62, 0x2f, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x05, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, + 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x48, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2b, + 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, + 0x48, 0x00, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x3a, 0x04, 0x90, 0xa6, 0x1d, + 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, + 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, + 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, + 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, + 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2f, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_blockchainpb_tpmpb_tpmpb_proto_rawDescOnce sync.Once + file_blockchainpb_tpmpb_tpmpb_proto_rawDescData = file_blockchainpb_tpmpb_tpmpb_proto_rawDesc +) + +func file_blockchainpb_tpmpb_tpmpb_proto_rawDescGZIP() []byte { + file_blockchainpb_tpmpb_tpmpb_proto_rawDescOnce.Do(func() { + file_blockchainpb_tpmpb_tpmpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_tpmpb_tpmpb_proto_rawDescData) + }) + return file_blockchainpb_tpmpb_tpmpb_proto_rawDescData +} + +var file_blockchainpb_tpmpb_tpmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_blockchainpb_tpmpb_tpmpb_proto_goTypes = []interface{}{ + (*Event)(nil), // 0: tpmpb.Event + (*NewHead)(nil), // 1: tpmpb.NewHead +} +var file_blockchainpb_tpmpb_tpmpb_proto_depIdxs = []int32{ + 1, // 0: tpmpb.Event.new_head:type_name -> tpmpb.NewHead + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name +} + +func init() { file_blockchainpb_tpmpb_tpmpb_proto_init() } +func file_blockchainpb_tpmpb_tpmpb_proto_init() { + if File_blockchainpb_tpmpb_tpmpb_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewHead); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Event_NewHead)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_blockchainpb_tpmpb_tpmpb_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_blockchainpb_tpmpb_tpmpb_proto_goTypes, + DependencyIndexes: file_blockchainpb_tpmpb_tpmpb_proto_depIdxs, + MessageInfos: file_blockchainpb_tpmpb_tpmpb_proto_msgTypes, + }.Build() + File_blockchainpb_tpmpb_tpmpb_proto = out.File + file_blockchainpb_tpmpb_tpmpb_proto_rawDesc = nil + file_blockchainpb_tpmpb_tpmpb_proto_goTypes = nil + file_blockchainpb_tpmpb_tpmpb_proto_depIdxs = nil +} diff --git a/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go b/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go new file mode 100644 index 000000000..313bb410b --- /dev/null +++ b/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go @@ -0,0 +1,13 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package tpmpb + +import ( + reflect "reflect" +) + +func (*Event) ReflectTypeOptions() []reflect.Type { + return []reflect.Type{ + reflect.TypeOf((*Event_NewHead)(nil)), + } +} diff --git a/pkg/pb/blockchainpb/tpmpb/types/types.mir.go b/pkg/pb/blockchainpb/tpmpb/types/types.mir.go new file mode 100644 index 000000000..00ead972e --- /dev/null +++ b/pkg/pb/blockchainpb/tpmpb/types/types.mir.go @@ -0,0 +1,115 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package tpmpbtypes + +import ( + mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" + reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" +) + +type Event struct { + Type Event_Type +} + +type Event_Type interface { + mirreflect.GeneratedType + isEvent_Type() + Pb() tpmpb.Event_Type +} + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func Event_TypeFromPb(pb tpmpb.Event_Type) Event_Type { + if pb == nil { + return nil + } + switch pb := pb.(type) { + case *tpmpb.Event_NewHead: + return &Event_NewHead{NewHead: NewHeadFromPb(pb.NewHead)} + } + return nil +} + +type Event_NewHead struct { + NewHead *NewHead +} + +func (*Event_NewHead) isEvent_Type() {} + +func (w *Event_NewHead) Unwrap() *NewHead { + return w.NewHead +} + +func (w *Event_NewHead) Pb() tpmpb.Event_Type { + if w == nil { + return nil + } + if w.NewHead == nil { + return &tpmpb.Event_NewHead{} + } + return &tpmpb.Event_NewHead{NewHead: (w.NewHead).Pb()} +} + +func (*Event_NewHead) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*tpmpb.Event_NewHead]()} +} + +func EventFromPb(pb *tpmpb.Event) *Event { + if pb == nil { + return nil + } + return &Event{ + Type: Event_TypeFromPb(pb.Type), + } +} + +func (m *Event) Pb() *tpmpb.Event { + if m == nil { + return nil + } + pbMessage := &tpmpb.Event{} + { + if m.Type != nil { + pbMessage.Type = (m.Type).Pb() + } + } + + return pbMessage +} + +func (*Event) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*tpmpb.Event]()} +} + +type NewHead struct { + HeadId uint64 +} + +func NewHeadFromPb(pb *tpmpb.NewHead) *NewHead { + if pb == nil { + return nil + } + return &NewHead{ + HeadId: pb.HeadId, + } +} + +func (m *NewHead) Pb() *tpmpb.NewHead { + if m == nil { + return nil + } + pbMessage := &tpmpb.NewHead{} + { + pbMessage.HeadId = m.HeadId + } + + return pbMessage +} + +func (*NewHead) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*tpmpb.NewHead]()} +} diff --git a/pkg/pb/blockchainpb/types/types.mir.go b/pkg/pb/blockchainpb/types/types.mir.go new file mode 100644 index 000000000..fe15cb316 --- /dev/null +++ b/pkg/pb/blockchainpb/types/types.mir.go @@ -0,0 +1,3 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package blockchainpbtypes diff --git a/pkg/pb/checkpointpb/checkpointpb.pb.go b/pkg/pb/checkpointpb/checkpointpb.pb.go index c3e6da9de..5fb060f45 100644 --- a/pkg/pb/checkpointpb/checkpointpb.pb.go +++ b/pkg/pb/checkpointpb/checkpointpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: checkpointpb/checkpointpb.proto package checkpointpb @@ -29,6 +29,7 @@ type Message struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Message_Checkpoint Type isMessage_Type `protobuf_oneof:"type"` } @@ -166,6 +167,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_EpochConfig // *Event_StableCheckpoint // *Event_EpochProgress diff --git a/pkg/pb/checkpointpb/chkpvalidatorpb/chkpvalidatorpb.pb.go b/pkg/pb/checkpointpb/chkpvalidatorpb/chkpvalidatorpb.pb.go index a3347588f..a19da6d2f 100644 --- a/pkg/pb/checkpointpb/chkpvalidatorpb/chkpvalidatorpb.pb.go +++ b/pkg/pb/checkpointpb/chkpvalidatorpb/chkpvalidatorpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: checkpointpb/chkpvalidatorpb/chkpvalidatorpb.proto package chkpvalidatorpb @@ -31,6 +31,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_ValidateCheckpoint // *Event_CheckpointValidated Type isEvent_Type `protobuf_oneof:"type"` @@ -238,6 +239,7 @@ type ValidateChkpOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *ValidateChkpOrigin_ContextStore // *ValidateChkpOrigin_Dsl Type isValidateChkpOrigin_Type `protobuf_oneof:"type"` diff --git a/pkg/pb/contextstorepb/contextstorepb.pb.go b/pkg/pb/contextstorepb/contextstorepb.pb.go index 91d4deb90..ab3d71c45 100644 --- a/pkg/pb/contextstorepb/contextstorepb.pb.go +++ b/pkg/pb/contextstorepb/contextstorepb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: contextstorepb/contextstorepb.proto package contextstorepb diff --git a/pkg/pb/cryptopb/cryptopb.pb.go b/pkg/pb/cryptopb/cryptopb.pb.go index 8c1406f2f..e6c852095 100644 --- a/pkg/pb/cryptopb/cryptopb.pb.go +++ b/pkg/pb/cryptopb/cryptopb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: cryptopb/cryptopb.proto package cryptopb @@ -29,6 +29,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_SignRequest // *Event_SignResult // *Event_VerifySig @@ -552,6 +553,7 @@ type SignOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *SignOrigin_ContextStore // *SignOrigin_Dsl Type isSignOrigin_Type `protobuf_oneof:"type"` @@ -640,6 +642,7 @@ type SigVerOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *SigVerOrigin_ContextStore // *SigVerOrigin_Dsl Type isSigVerOrigin_Type `protobuf_oneof:"type"` diff --git a/pkg/pb/dslpb/dslpb.pb.go b/pkg/pb/dslpb/dslpb.pb.go index 9080eddbb..c1f7e8f3e 100644 --- a/pkg/pb/dslpb/dslpb.pb.go +++ b/pkg/pb/dslpb/dslpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: dslpb/dslpb.proto package dslpb diff --git a/pkg/pb/eventpb/eventpb.pb.go b/pkg/pb/eventpb/eventpb.pb.go index 4fbd16948..8565ecfed 100644 --- a/pkg/pb/eventpb/eventpb.pb.go +++ b/pkg/pb/eventpb/eventpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: eventpb/eventpb.proto package eventpb @@ -12,6 +12,10 @@ import ( batchdbpb "github.com/filecoin-project/mir/pkg/pb/availabilitypb/batchdbpb" batchfetcherpb "github.com/filecoin-project/mir/pkg/pb/batchfetcherpb" bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" + bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" + communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" + tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" chkpvalidatorpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb/chkpvalidatorpb" cryptopb "github.com/filecoin-project/mir/pkg/pb/cryptopb" @@ -48,6 +52,7 @@ type Event struct { DestModule string `protobuf:"bytes,1,opt,name=dest_module,json=destModule,proto3" json:"dest_module,omitempty"` // Types that are assignable to Type: + // // *Event_Init // *Event_Timer // *Event_Hasher @@ -67,6 +72,10 @@ type Event struct { // *Event_ChkpValidator // *Event_PprepValiadtor // *Event_PingPong + // *Event_Bcm + // *Event_Miner + // *Event_Tpm + // *Event_Communication // *Event_TestingString // *Event_TestingUint // *Event_Tester @@ -260,6 +269,34 @@ func (x *Event) GetPingPong() *pingpongpb.Event { return nil } +func (x *Event) GetBcm() *bcmpb.Event { + if x, ok := x.GetType().(*Event_Bcm); ok { + return x.Bcm + } + return nil +} + +func (x *Event) GetMiner() *minerpb.Event { + if x, ok := x.GetType().(*Event_Miner); ok { + return x.Miner + } + return nil +} + +func (x *Event) GetTpm() *tpmpb.Event { + if x, ok := x.GetType().(*Event_Tpm); ok { + return x.Tpm + } + return nil +} + +func (x *Event) GetCommunication() *communicationpb.Event { + if x, ok := x.GetType().(*Event_Communication); ok { + return x.Communication + } + return nil +} + func (x *Event) GetTestingString() *wrapperspb.StringValue { if x, ok := x.GetType().(*Event_TestingString); ok { return x.TestingString @@ -372,6 +409,23 @@ type Event_PingPong struct { PingPong *pingpongpb.Event `protobuf:"bytes,200,opt,name=ping_pong,json=pingPong,proto3,oneof"` } +type Event_Bcm struct { + // Events for blockchain + Bcm *bcmpb.Event `protobuf:"bytes,201,opt,name=bcm,proto3,oneof"` +} + +type Event_Miner struct { + Miner *minerpb.Event `protobuf:"bytes,202,opt,name=miner,proto3,oneof"` +} + +type Event_Tpm struct { + Tpm *tpmpb.Event `protobuf:"bytes,203,opt,name=tpm,proto3,oneof"` +} + +type Event_Communication struct { + Communication *communicationpb.Event `protobuf:"bytes,204,opt,name=communication,proto3,oneof"` +} + type Event_TestingString struct { // for unit-tests TestingString *wrapperspb.StringValue `protobuf:"bytes,301,opt,name=testingString,proto3,oneof"` @@ -423,6 +477,14 @@ func (*Event_PprepValiadtor) isEvent_Type() {} func (*Event_PingPong) isEvent_Type() {} +func (*Event_Bcm) isEvent_Type() {} + +func (*Event_Miner) isEvent_Type() {} + +func (*Event_Tpm) isEvent_Type() {} + +func (*Event_Communication) isEvent_Type() {} + func (*Event_TestingString) isEvent_Type() {} func (*Event_TestingUint) isEvent_Type() {} @@ -473,6 +535,7 @@ type TimerEvent struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *TimerEvent_Delay // *TimerEvent_Repeat // *TimerEvent_GarbageCollect @@ -567,7 +630,8 @@ type TimerDelay struct { unknownFields protoimpl.UnknownFields // TODO: The field name must not be `events`, since it conflicts with a package name in the generated code. - // This is a bug in the Mir code generator that should be fixed. + // + // This is a bug in the Mir code generator that should be fixed. EventsToDelay []*Event `protobuf:"bytes,1,rep,name=events_to_delay,json=eventsToDelay,proto3" json:"events_to_delay,omitempty"` Delay uint64 `protobuf:"varint,2,opt,name=delay,proto3" json:"delay,omitempty"` } @@ -770,141 +834,162 @@ var file_eventpb_eventpb_proto_rawDesc = []byte{ 0x73, 0x70, 0x6f, 0x72, 0x74, 0x70, 0x62, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, - 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x22, 0xf9, 0x09, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, - 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, - 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x74, 0x69, 0x6d, 0x65, - 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x05, - 0x74, 0x69, 0x6d, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x18, - 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x70, 0x62, - 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, - 0x12, 0x20, 0x0a, 0x03, 0x62, 0x63, 0x62, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, - 0x62, 0x63, 0x62, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x62, - 0x63, 0x62, 0x12, 0x2c, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x0c, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x70, 0x62, 0x2e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, - 0x12, 0x3b, 0x0a, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, - 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x79, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, - 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x0a, - 0x08, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x64, 0x62, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x10, 0x2e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x64, 0x62, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x48, 0x00, 0x52, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x44, 0x62, 0x12, 0x3c, 0x0a, 0x0d, - 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x0f, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x66, 0x65, 0x74, 0x63, 0x68, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x61, - 0x74, 0x63, 0x68, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x0d, 0x74, 0x68, - 0x72, 0x65, 0x73, 0x68, 0x5f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x68, 0x72, 0x65, - 0x73, 0x68, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x12, 0x35, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, - 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, - 0x2c, 0x0a, 0x07, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, - 0x03, 0x69, 0x73, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x69, 0x73, 0x73, - 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x69, 0x73, 0x73, 0x12, - 0x2c, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x29, 0x0a, - 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, - 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, - 0x52, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x12, 0x20, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, - 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x70, 0x70, 0x62, 0x2e, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x32, 0x0a, 0x09, 0x74, 0x72, - 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, - 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x48, 0x00, 0x52, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3f, - 0x0a, 0x0e, 0x63, 0x68, 0x6b, 0x70, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x68, 0x6b, 0x70, 0x76, 0x61, 0x6c, - 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, - 0x52, 0x0d, 0x63, 0x68, 0x6b, 0x70, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x12, - 0x42, 0x0a, 0x0f, 0x70, 0x70, 0x72, 0x65, 0x70, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x61, 0x64, 0x74, - 0x6f, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x70, 0x72, 0x65, 0x70, - 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x70, 0x72, 0x65, 0x70, 0x56, 0x61, 0x6c, 0x69, 0x61, 0x64, - 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x09, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x6e, 0x67, - 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, - 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x08, 0x70, 0x69, - 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x12, 0x45, 0x0a, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, - 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0xad, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0d, - 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a, - 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x18, 0xae, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, - 0x65, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, - 0x12, 0x2b, 0x0a, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0xaf, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x73, - 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, - 0x04, 0x6e, 0x65, 0x78, 0x74, 0x18, 0x90, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x04, 0x90, 0xa6, - 0x1d, 0x01, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x88, 0xa6, 0x1d, 0x01, 0x42, 0x0c, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x0c, 0x0a, 0x04, - 0x49, 0x6e, 0x69, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xc6, 0x01, 0x0a, 0x0a, 0x54, - 0x69, 0x6d, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x64, 0x65, 0x6c, - 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, - 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2e, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, - 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x47, 0x0a, 0x0f, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, - 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, - 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, - 0x0e, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x3a, - 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, - 0xa6, 0x1d, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, - 0x61, 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, - 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0d, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, - 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, - 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x3a, 0x04, - 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, - 0x70, 0x65, 0x61, 0x74, 0x12, 0x38, 0x0a, 0x10, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, - 0x6f, 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0e, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x52, - 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, - 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, - 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, - 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, - 0x61, 0x79, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, - 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, - 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, - 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, - 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x8a, 0x01, 0x0a, 0x13, 0x54, 0x69, 0x6d, 0x65, - 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x12, - 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, - 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, - 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, - 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, - 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, - 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, - 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x6f, 0x1a, 0x1e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x2f, 0x74, 0x70, 0x6d, 0x70, 0x62, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x32, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, + 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa9, 0x0b, 0x0a, 0x05, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, + 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, + 0x0a, 0x64, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x69, + 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, + 0x12, 0x2b, 0x0a, 0x05, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x05, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x12, 0x29, 0x0a, + 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, + 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, + 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x03, 0x62, 0x63, 0x62, 0x18, + 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x63, 0x62, 0x70, 0x62, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, 0x62, 0x12, 0x2c, 0x0a, 0x07, 0x6d, 0x65, + 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, + 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, + 0x07, 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x3b, 0x0a, 0x0c, 0x61, 0x76, 0x61, 0x69, + 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x70, 0x62, 0x2e, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, + 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x08, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x64, + 0x62, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x64, + 0x62, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x62, 0x61, 0x74, + 0x63, 0x68, 0x44, 0x62, 0x12, 0x3c, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x66, 0x65, + 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x61, + 0x74, 0x63, 0x68, 0x66, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x46, 0x65, 0x74, 0x63, 0x68, + 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x0d, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x63, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x68, 0x72, 0x65, + 0x73, 0x68, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x48, 0x00, 0x52, 0x0c, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x12, 0x35, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x11, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x65, + 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x07, 0x66, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x66, 0x61, 0x63, 0x74, 0x6f, + 0x72, 0x79, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x66, 0x61, + 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x03, 0x69, 0x73, 0x73, 0x18, 0x13, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x69, 0x73, 0x73, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x48, 0x00, 0x52, 0x03, 0x69, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6f, 0x72, 0x64, 0x65, 0x72, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, + 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x62, + 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x12, 0x20, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, + 0x61, 0x70, 0x70, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x61, + 0x70, 0x70, 0x12, 0x32, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, + 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, + 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x74, 0x72, 0x61, + 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x68, 0x6b, 0x70, 0x5f, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x63, 0x68, 0x6b, 0x70, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x70, 0x62, + 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x68, 0x6b, 0x70, 0x56, 0x61, + 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x42, 0x0a, 0x0f, 0x70, 0x70, 0x72, 0x65, 0x70, + 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x61, 0x64, 0x74, 0x6f, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x70, 0x70, 0x72, 0x65, 0x70, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x70, 0x72, + 0x65, 0x70, 0x56, 0x61, 0x6c, 0x69, 0x61, 0x64, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x09, 0x70, + 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x6e, 0x67, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x11, 0x2e, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x12, 0x21, + 0x0a, 0x03, 0x62, 0x63, 0x6d, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, + 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, + 0x6d, 0x12, 0x27, 0x0a, 0x05, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x48, 0x00, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x03, 0x74, 0x70, + 0x6d, 0x18, 0xcb, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x70, 0x6d, 0x70, 0x62, + 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x74, 0x70, 0x6d, 0x12, 0x3f, 0x0a, + 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xcc, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, + 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, + 0x0a, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, + 0xad, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, + 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, + 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, + 0x55, 0x69, 0x6e, 0x74, 0x18, 0xae, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, + 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, 0x73, + 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x74, 0x65, 0x73, 0x74, + 0x65, 0x72, 0x18, 0xaf, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, + 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x74, + 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x18, 0x90, 0x03, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x42, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, + 0x3a, 0x04, 0x88, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, + 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x0c, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x22, 0xc6, 0x01, 0x0a, 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, + 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, + 0x44, 0x65, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2e, + 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, + 0x70, 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x47, + 0x0a, 0x0f, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, + 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, + 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0a, + 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x44, 0x65, 0x6c, + 0x61, 0x79, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, + 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x90, 0x02, 0x0a, + 0x0b, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x38, 0x0a, 0x10, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, + 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, + 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, + 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, + 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, + 0x8a, 0x01, 0x0a, 0x13, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, + 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, + 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, + 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, + 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x30, 0x5a, 0x2e, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, + 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -944,9 +1029,13 @@ var file_eventpb_eventpb_proto_goTypes = []interface{}{ (*chkpvalidatorpb.Event)(nil), // 20: chkpvalidatorpb.Event (*pprepvalidatorpb.Event)(nil), // 21: pprepvalidatorpb.Event (*pingpongpb.Event)(nil), // 22: pingpongpb.Event - (*wrapperspb.StringValue)(nil), // 23: google.protobuf.StringValue - (*wrapperspb.UInt64Value)(nil), // 24: google.protobuf.UInt64Value - (*testerpb.Tester)(nil), // 25: testerpb.Tester + (*bcmpb.Event)(nil), // 23: bcmpb.Event + (*minerpb.Event)(nil), // 24: minerpb.Event + (*tpmpb.Event)(nil), // 25: tpmpb.Event + (*communicationpb.Event)(nil), // 26: communicationpb.Event + (*wrapperspb.StringValue)(nil), // 27: google.protobuf.StringValue + (*wrapperspb.UInt64Value)(nil), // 28: google.protobuf.UInt64Value + (*testerpb.Tester)(nil), // 29: testerpb.Tester } var file_eventpb_eventpb_proto_depIdxs = []int32{ 1, // 0: eventpb.Event.init:type_name -> eventpb.Init @@ -968,20 +1057,24 @@ var file_eventpb_eventpb_proto_depIdxs = []int32{ 20, // 16: eventpb.Event.chkp_validator:type_name -> chkpvalidatorpb.Event 21, // 17: eventpb.Event.pprep_valiadtor:type_name -> pprepvalidatorpb.Event 22, // 18: eventpb.Event.ping_pong:type_name -> pingpongpb.Event - 23, // 19: eventpb.Event.testingString:type_name -> google.protobuf.StringValue - 24, // 20: eventpb.Event.testingUint:type_name -> google.protobuf.UInt64Value - 25, // 21: eventpb.Event.tester:type_name -> testerpb.Tester - 0, // 22: eventpb.Event.next:type_name -> eventpb.Event - 3, // 23: eventpb.TimerEvent.delay:type_name -> eventpb.TimerDelay - 4, // 24: eventpb.TimerEvent.repeat:type_name -> eventpb.TimerRepeat - 5, // 25: eventpb.TimerEvent.garbage_collect:type_name -> eventpb.TimerGarbageCollect - 0, // 26: eventpb.TimerDelay.events_to_delay:type_name -> eventpb.Event - 0, // 27: eventpb.TimerRepeat.events_to_repeat:type_name -> eventpb.Event - 28, // [28:28] is the sub-list for method output_type - 28, // [28:28] is the sub-list for method input_type - 28, // [28:28] is the sub-list for extension type_name - 28, // [28:28] is the sub-list for extension extendee - 0, // [0:28] is the sub-list for field type_name + 23, // 19: eventpb.Event.bcm:type_name -> bcmpb.Event + 24, // 20: eventpb.Event.miner:type_name -> minerpb.Event + 25, // 21: eventpb.Event.tpm:type_name -> tpmpb.Event + 26, // 22: eventpb.Event.communication:type_name -> communicationpb.Event + 27, // 23: eventpb.Event.testingString:type_name -> google.protobuf.StringValue + 28, // 24: eventpb.Event.testingUint:type_name -> google.protobuf.UInt64Value + 29, // 25: eventpb.Event.tester:type_name -> testerpb.Tester + 0, // 26: eventpb.Event.next:type_name -> eventpb.Event + 3, // 27: eventpb.TimerEvent.delay:type_name -> eventpb.TimerDelay + 4, // 28: eventpb.TimerEvent.repeat:type_name -> eventpb.TimerRepeat + 5, // 29: eventpb.TimerEvent.garbage_collect:type_name -> eventpb.TimerGarbageCollect + 0, // 30: eventpb.TimerDelay.events_to_delay:type_name -> eventpb.Event + 0, // 31: eventpb.TimerRepeat.events_to_repeat:type_name -> eventpb.Event + 32, // [32:32] is the sub-list for method output_type + 32, // [32:32] is the sub-list for method input_type + 32, // [32:32] is the sub-list for extension type_name + 32, // [32:32] is the sub-list for extension extendee + 0, // [0:32] is the sub-list for field type_name } func init() { file_eventpb_eventpb_proto_init() } @@ -1083,6 +1176,10 @@ func file_eventpb_eventpb_proto_init() { (*Event_ChkpValidator)(nil), (*Event_PprepValiadtor)(nil), (*Event_PingPong)(nil), + (*Event_Bcm)(nil), + (*Event_Miner)(nil), + (*Event_Tpm)(nil), + (*Event_Communication)(nil), (*Event_TestingString)(nil), (*Event_TestingUint)(nil), (*Event_Tester)(nil), diff --git a/pkg/pb/eventpb/eventpb.pb.mir.go b/pkg/pb/eventpb/eventpb.pb.mir.go index 8808e7d1a..1cef9b204 100644 --- a/pkg/pb/eventpb/eventpb.pb.mir.go +++ b/pkg/pb/eventpb/eventpb.pb.mir.go @@ -27,6 +27,10 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_ChkpValidator)(nil)), reflect.TypeOf((*Event_PprepValiadtor)(nil)), reflect.TypeOf((*Event_PingPong)(nil)), + reflect.TypeOf((*Event_Bcm)(nil)), + reflect.TypeOf((*Event_Miner)(nil)), + reflect.TypeOf((*Event_Tpm)(nil)), + reflect.TypeOf((*Event_Communication)(nil)), reflect.TypeOf((*Event_TestingString)(nil)), reflect.TypeOf((*Event_TestingUint)(nil)), reflect.TypeOf((*Event_Tester)(nil)), diff --git a/pkg/pb/eventpb/oneof_interfaces.mir.go b/pkg/pb/eventpb/oneof_interfaces.mir.go index 2baab82b9..1534c97fd 100644 --- a/pkg/pb/eventpb/oneof_interfaces.mir.go +++ b/pkg/pb/eventpb/oneof_interfaces.mir.go @@ -8,6 +8,10 @@ import ( batchdbpb "github.com/filecoin-project/mir/pkg/pb/availabilitypb/batchdbpb" batchfetcherpb "github.com/filecoin-project/mir/pkg/pb/batchfetcherpb" bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" + bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" + communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" + tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" chkpvalidatorpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb/chkpvalidatorpb" cryptopb "github.com/filecoin-project/mir/pkg/pb/cryptopb" @@ -107,6 +111,22 @@ func (w *Event_PingPong) Unwrap() *pingpongpb.Event { return w.PingPong } +func (w *Event_Bcm) Unwrap() *bcmpb.Event { + return w.Bcm +} + +func (w *Event_Miner) Unwrap() *minerpb.Event { + return w.Miner +} + +func (w *Event_Tpm) Unwrap() *tpmpb.Event { + return w.Tpm +} + +func (w *Event_Communication) Unwrap() *communicationpb.Event { + return w.Communication +} + func (w *Event_TestingString) Unwrap() *wrapperspb.StringValue { return w.TestingString } diff --git a/pkg/pb/eventpb/types/types.mir.go b/pkg/pb/eventpb/types/types.mir.go index 119300d27..98cc5467a 100644 --- a/pkg/pb/eventpb/types/types.mir.go +++ b/pkg/pb/eventpb/types/types.mir.go @@ -4,12 +4,16 @@ package eventpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - types19 "github.com/filecoin-project/mir/codegen/model/types" + types23 "github.com/filecoin-project/mir/codegen/model/types" types13 "github.com/filecoin-project/mir/pkg/pb/apppb/types" types5 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/batchdbpb/types" types4 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/types" types6 "github.com/filecoin-project/mir/pkg/pb/batchfetcherpb/types" types2 "github.com/filecoin-project/mir/pkg/pb/bcbpb/types" + types18 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" + types21 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types19 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" + types20 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/types" types15 "github.com/filecoin-project/mir/pkg/pb/checkpointpb/chkpvalidatorpb/types" types8 "github.com/filecoin-project/mir/pkg/pb/checkpointpb/types" types12 "github.com/filecoin-project/mir/pkg/pb/cryptopb/types" @@ -21,11 +25,11 @@ import ( types16 "github.com/filecoin-project/mir/pkg/pb/ordererpb/pprepvalidatorpb/types" types11 "github.com/filecoin-project/mir/pkg/pb/ordererpb/types" types17 "github.com/filecoin-project/mir/pkg/pb/pingpongpb/types" - types18 "github.com/filecoin-project/mir/pkg/pb/testerpb/types" + types22 "github.com/filecoin-project/mir/pkg/pb/testerpb/types" types7 "github.com/filecoin-project/mir/pkg/pb/threshcryptopb/types" types14 "github.com/filecoin-project/mir/pkg/pb/transportpb/types" - types20 "github.com/filecoin-project/mir/pkg/timer/types" - types21 "github.com/filecoin-project/mir/pkg/trantor/types" + types24 "github.com/filecoin-project/mir/pkg/timer/types" + types25 "github.com/filecoin-project/mir/pkg/trantor/types" types "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" @@ -91,12 +95,20 @@ func Event_TypeFromPb(pb eventpb.Event_Type) Event_Type { return &Event_PprepValiadtor{PprepValiadtor: types16.EventFromPb(pb.PprepValiadtor)} case *eventpb.Event_PingPong: return &Event_PingPong{PingPong: types17.EventFromPb(pb.PingPong)} + case *eventpb.Event_Bcm: + return &Event_Bcm{Bcm: types18.EventFromPb(pb.Bcm)} + case *eventpb.Event_Miner: + return &Event_Miner{Miner: types19.EventFromPb(pb.Miner)} + case *eventpb.Event_Tpm: + return &Event_Tpm{Tpm: types20.EventFromPb(pb.Tpm)} + case *eventpb.Event_Communication: + return &Event_Communication{Communication: types21.EventFromPb(pb.Communication)} case *eventpb.Event_TestingString: return &Event_TestingString{TestingString: pb.TestingString} case *eventpb.Event_TestingUint: return &Event_TestingUint{TestingUint: pb.TestingUint} case *eventpb.Event_Tester: - return &Event_Tester{Tester: types18.TesterFromPb(pb.Tester)} + return &Event_Tester{Tester: types22.TesterFromPb(pb.Tester)} } return nil } @@ -557,6 +569,102 @@ func (*Event_PingPong) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_PingPong]()} } +type Event_Bcm struct { + Bcm *types18.Event +} + +func (*Event_Bcm) isEvent_Type() {} + +func (w *Event_Bcm) Unwrap() *types18.Event { + return w.Bcm +} + +func (w *Event_Bcm) Pb() eventpb.Event_Type { + if w == nil { + return nil + } + if w.Bcm == nil { + return &eventpb.Event_Bcm{} + } + return &eventpb.Event_Bcm{Bcm: (w.Bcm).Pb()} +} + +func (*Event_Bcm) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Bcm]()} +} + +type Event_Miner struct { + Miner *types19.Event +} + +func (*Event_Miner) isEvent_Type() {} + +func (w *Event_Miner) Unwrap() *types19.Event { + return w.Miner +} + +func (w *Event_Miner) Pb() eventpb.Event_Type { + if w == nil { + return nil + } + if w.Miner == nil { + return &eventpb.Event_Miner{} + } + return &eventpb.Event_Miner{Miner: (w.Miner).Pb()} +} + +func (*Event_Miner) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Miner]()} +} + +type Event_Tpm struct { + Tpm *types20.Event +} + +func (*Event_Tpm) isEvent_Type() {} + +func (w *Event_Tpm) Unwrap() *types20.Event { + return w.Tpm +} + +func (w *Event_Tpm) Pb() eventpb.Event_Type { + if w == nil { + return nil + } + if w.Tpm == nil { + return &eventpb.Event_Tpm{} + } + return &eventpb.Event_Tpm{Tpm: (w.Tpm).Pb()} +} + +func (*Event_Tpm) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Tpm]()} +} + +type Event_Communication struct { + Communication *types21.Event +} + +func (*Event_Communication) isEvent_Type() {} + +func (w *Event_Communication) Unwrap() *types21.Event { + return w.Communication +} + +func (w *Event_Communication) Pb() eventpb.Event_Type { + if w == nil { + return nil + } + if w.Communication == nil { + return &eventpb.Event_Communication{} + } + return &eventpb.Event_Communication{Communication: (w.Communication).Pb()} +} + +func (*Event_Communication) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Communication]()} +} + type Event_TestingString struct { TestingString *wrapperspb.StringValue } @@ -606,12 +714,12 @@ func (*Event_TestingUint) MirReflect() mirreflect.Type { } type Event_Tester struct { - Tester *types18.Tester + Tester *types22.Tester } func (*Event_Tester) isEvent_Type() {} -func (w *Event_Tester) Unwrap() *types18.Tester { +func (w *Event_Tester) Unwrap() *types22.Tester { return w.Tester } @@ -636,7 +744,7 @@ func EventFromPb(pb *eventpb.Event) *Event { return &Event{ DestModule: (types.ModuleID)(pb.DestModule), Type: Event_TypeFromPb(pb.Type), - Next: types19.ConvertSlice(pb.Next, func(t *eventpb.Event) *Event { + Next: types23.ConvertSlice(pb.Next, func(t *eventpb.Event) *Event { return EventFromPb(t) }), } @@ -652,7 +760,7 @@ func (m *Event) Pb() *eventpb.Event { if m.Type != nil { pbMessage.Type = (m.Type).Pb() } - pbMessage.Next = types19.ConvertSlice(m.Next, func(t *Event) *eventpb.Event { + pbMessage.Next = types23.ConvertSlice(m.Next, func(t *Event) *eventpb.Event { return (t).Pb() }) } @@ -819,7 +927,7 @@ func (*TimerEvent) MirReflect() mirreflect.Type { type TimerDelay struct { EventsToDelay []*Event - Delay types20.Duration + Delay types24.Duration } func TimerDelayFromPb(pb *eventpb.TimerDelay) *TimerDelay { @@ -827,10 +935,10 @@ func TimerDelayFromPb(pb *eventpb.TimerDelay) *TimerDelay { return nil } return &TimerDelay{ - EventsToDelay: types19.ConvertSlice(pb.EventsToDelay, func(t *eventpb.Event) *Event { + EventsToDelay: types23.ConvertSlice(pb.EventsToDelay, func(t *eventpb.Event) *Event { return EventFromPb(t) }), - Delay: (types20.Duration)(pb.Delay), + Delay: (types24.Duration)(pb.Delay), } } @@ -840,7 +948,7 @@ func (m *TimerDelay) Pb() *eventpb.TimerDelay { } pbMessage := &eventpb.TimerDelay{} { - pbMessage.EventsToDelay = types19.ConvertSlice(m.EventsToDelay, func(t *Event) *eventpb.Event { + pbMessage.EventsToDelay = types23.ConvertSlice(m.EventsToDelay, func(t *Event) *eventpb.Event { return (t).Pb() }) pbMessage.Delay = (uint64)(m.Delay) @@ -855,8 +963,8 @@ func (*TimerDelay) MirReflect() mirreflect.Type { type TimerRepeat struct { EventsToRepeat []*Event - Delay types20.Duration - RetentionIndex types21.RetentionIndex + Delay types24.Duration + RetentionIndex types25.RetentionIndex } func TimerRepeatFromPb(pb *eventpb.TimerRepeat) *TimerRepeat { @@ -864,11 +972,11 @@ func TimerRepeatFromPb(pb *eventpb.TimerRepeat) *TimerRepeat { return nil } return &TimerRepeat{ - EventsToRepeat: types19.ConvertSlice(pb.EventsToRepeat, func(t *eventpb.Event) *Event { + EventsToRepeat: types23.ConvertSlice(pb.EventsToRepeat, func(t *eventpb.Event) *Event { return EventFromPb(t) }), - Delay: (types20.Duration)(pb.Delay), - RetentionIndex: (types21.RetentionIndex)(pb.RetentionIndex), + Delay: (types24.Duration)(pb.Delay), + RetentionIndex: (types25.RetentionIndex)(pb.RetentionIndex), } } @@ -878,7 +986,7 @@ func (m *TimerRepeat) Pb() *eventpb.TimerRepeat { } pbMessage := &eventpb.TimerRepeat{} { - pbMessage.EventsToRepeat = types19.ConvertSlice(m.EventsToRepeat, func(t *Event) *eventpb.Event { + pbMessage.EventsToRepeat = types23.ConvertSlice(m.EventsToRepeat, func(t *Event) *eventpb.Event { return (t).Pb() }) pbMessage.Delay = (uint64)(m.Delay) @@ -893,7 +1001,7 @@ func (*TimerRepeat) MirReflect() mirreflect.Type { } type TimerGarbageCollect struct { - RetentionIndex types21.RetentionIndex + RetentionIndex types25.RetentionIndex } func TimerGarbageCollectFromPb(pb *eventpb.TimerGarbageCollect) *TimerGarbageCollect { @@ -901,7 +1009,7 @@ func TimerGarbageCollectFromPb(pb *eventpb.TimerGarbageCollect) *TimerGarbageCol return nil } return &TimerGarbageCollect{ - RetentionIndex: (types21.RetentionIndex)(pb.RetentionIndex), + RetentionIndex: (types25.RetentionIndex)(pb.RetentionIndex), } } diff --git a/pkg/pb/factorypb/factorypb.pb.go b/pkg/pb/factorypb/factorypb.pb.go index a0bed354d..62849de7f 100644 --- a/pkg/pb/factorypb/factorypb.pb.go +++ b/pkg/pb/factorypb/factorypb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: factorypb/factorypb.proto package factorypb @@ -31,6 +31,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_NewModule // *Event_GarbageCollect Type isEvent_Type `protobuf_oneof:"type"` @@ -227,6 +228,7 @@ type GeneratorParams struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *GeneratorParams_MultisigCollector // *GeneratorParams_Checkpoint // *GeneratorParams_EchoTestModule diff --git a/pkg/pb/hasherpb/hasherpb.pb.go b/pkg/pb/hasherpb/hasherpb.pb.go index cdc0413ee..6c7ca4459 100644 --- a/pkg/pb/hasherpb/hasherpb.pb.go +++ b/pkg/pb/hasherpb/hasherpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: hasherpb/hasherpb.proto package hasherpb @@ -29,6 +29,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_Request // *Event_Result // *Event_RequestOne @@ -358,6 +359,7 @@ type HashOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *HashOrigin_ContextStore // *HashOrigin_Dsl Type isHashOrigin_Type `protobuf_oneof:"type"` diff --git a/pkg/pb/isspb/isspb.pb.go b/pkg/pb/isspb/isspb.pb.go index 825142a0a..a96af63ec 100644 --- a/pkg/pb/isspb/isspb.pb.go +++ b/pkg/pb/isspb/isspb.pb.go @@ -5,8 +5,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: isspb/isspb.proto package isspb @@ -36,6 +36,7 @@ type ISSMessage struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *ISSMessage_StableCheckpoint Type isISSMessage_Type `protobuf_oneof:"type"` } @@ -102,6 +103,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_PushCheckpoint // *Event_SbDeliver // *Event_DeliverCert diff --git a/pkg/pb/mempoolpb/mempoolpb.pb.go b/pkg/pb/mempoolpb/mempoolpb.pb.go index 2bfc92bc6..6bb80ee39 100644 --- a/pkg/pb/mempoolpb/mempoolpb.pb.go +++ b/pkg/pb/mempoolpb/mempoolpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: mempoolpb/mempoolpb.proto package mempoolpb @@ -30,6 +30,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_RequestBatch // *Event_NewBatch // *Event_RequestTransactions @@ -862,6 +863,7 @@ type RequestBatchOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *RequestBatchOrigin_ContextStore // *RequestBatchOrigin_Dsl Type isRequestBatchOrigin_Type `protobuf_oneof:"Type"` @@ -950,6 +952,7 @@ type RequestTransactionsOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *RequestTransactionsOrigin_ContextStore // *RequestTransactionsOrigin_Dsl Type isRequestTransactionsOrigin_Type `protobuf_oneof:"Type"` @@ -1038,6 +1041,7 @@ type RequestTransactionIDsOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *RequestTransactionIDsOrigin_ContextStore // *RequestTransactionIDsOrigin_Dsl Type isRequestTransactionIDsOrigin_Type `protobuf_oneof:"Type"` @@ -1126,6 +1130,7 @@ type RequestBatchIDOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *RequestBatchIDOrigin_ContextStore // *RequestBatchIDOrigin_Dsl Type isRequestBatchIDOrigin_Type `protobuf_oneof:"Type"` diff --git a/pkg/pb/messagepb/messagepb.pb.go b/pkg/pb/messagepb/messagepb.pb.go index f642796cf..be5564889 100644 --- a/pkg/pb/messagepb/messagepb.pb.go +++ b/pkg/pb/messagepb/messagepb.pb.go @@ -5,8 +5,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: messagepb/messagepb.proto package messagepb @@ -14,6 +14,7 @@ package messagepb import ( mscpb "github.com/filecoin-project/mir/pkg/pb/availabilitypb/mscpb" bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" + communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" isspb "github.com/filecoin-project/mir/pkg/pb/isspb" _ "github.com/filecoin-project/mir/pkg/pb/mir" @@ -40,12 +41,14 @@ type Message struct { DestModule string `protobuf:"bytes,1,opt,name=dest_module,json=destModule,proto3" json:"dest_module,omitempty"` // Types that are assignable to Type: + // // *Message_Iss // *Message_Bcb // *Message_MultisigCollector // *Message_Pingpong // *Message_Checkpoint // *Message_Orderer + // *Message_Communicationpb Type isMessage_Type `protobuf_oneof:"type"` } @@ -137,6 +140,13 @@ func (x *Message) GetOrderer() *ordererpb.Message { return nil } +func (x *Message) GetCommunicationpb() *communicationpb.Message { + if x, ok := x.GetType().(*Message_Communicationpb); ok { + return x.Communicationpb + } + return nil +} + type isMessage_Type interface { isMessage_Type() } @@ -165,6 +175,11 @@ type Message_Orderer struct { Orderer *ordererpb.Message `protobuf:"bytes,7,opt,name=orderer,proto3,oneof"` } +type Message_Communicationpb struct { + // Messages for blockchain + Communicationpb *communicationpb.Message `protobuf:"bytes,8,opt,name=communicationpb,proto3,oneof"` +} + func (*Message_Iss) isMessage_Type() {} func (*Message_Bcb) isMessage_Type() {} @@ -177,6 +192,8 @@ func (*Message_Checkpoint) isMessage_Type() {} func (*Message_Orderer) isMessage_Type() {} +func (*Message_Communicationpb) isMessage_Type() {} + var File_messagepb_messagepb_proto protoreflect.FileDescriptor var file_messagepb_messagepb_proto_rawDesc = []byte{ @@ -192,41 +209,49 @@ var file_messagepb_messagepb_proto_rawDesc = []byte{ 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x70, - 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, - 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, - 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0xad, 0x03, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, - 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, - 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0a, 0x64, 0x65, - 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x03, 0x69, 0x73, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69, 0x73, 0x73, 0x70, 0x62, 0x2e, 0x49, 0x53, - 0x53, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x03, 0x69, 0x73, 0x73, 0x12, - 0x22, 0x0a, 0x03, 0x62, 0x63, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, - 0x63, 0x62, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x03, - 0x62, 0x63, 0x62, 0x12, 0x4e, 0x0a, 0x12, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x73, 0x69, 0x67, 0x5f, - 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x1d, 0x2e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x70, 0x62, - 0x2e, 0x6d, 0x73, 0x63, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, - 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x73, 0x69, 0x67, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, - 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x70, 0x69, - 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x12, 0x37, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x68, 0x65, - 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, - 0x2e, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x3a, - 0x04, 0xc0, 0xe4, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0xc8, - 0xe4, 0x1d, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x32, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, + 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, + 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6e, 0x65, 0x74, 0x2f, 0x63, + 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf3, 0x03, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, + 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, + 0x52, 0x0a, 0x64, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x03, + 0x69, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69, 0x73, 0x73, 0x70, + 0x62, 0x2e, 0x49, 0x53, 0x53, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x03, + 0x69, 0x73, 0x73, 0x12, 0x22, 0x0a, 0x03, 0x62, 0x63, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x62, 0x63, 0x62, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, 0x62, 0x12, 0x4e, 0x0a, 0x12, 0x6d, 0x75, 0x6c, 0x74, 0x69, + 0x73, 0x69, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x70, 0x62, 0x2e, 0x6d, 0x73, 0x63, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x48, 0x00, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x73, 0x69, 0x67, 0x43, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x70, + 0x6f, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x69, 0x6e, 0x67, + 0x70, 0x6f, 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, + 0x52, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x12, 0x37, 0x0a, 0x0a, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, + 0x72, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, + 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x3a, 0x04, 0xc0, 0xe4, 0x1d, 0x01, 0x42, + 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x32, 0x5a, + 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, + 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, + 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x70, + 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -243,13 +268,14 @@ func file_messagepb_messagepb_proto_rawDescGZIP() []byte { var file_messagepb_messagepb_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_messagepb_messagepb_proto_goTypes = []interface{}{ - (*Message)(nil), // 0: messagepb.Message - (*isspb.ISSMessage)(nil), // 1: isspb.ISSMessage - (*bcbpb.Message)(nil), // 2: bcbpb.Message - (*mscpb.Message)(nil), // 3: availabilitypb.mscpb.Message - (*pingpongpb.Message)(nil), // 4: pingpongpb.Message - (*checkpointpb.Message)(nil), // 5: checkpointpb.Message - (*ordererpb.Message)(nil), // 6: ordererpb.Message + (*Message)(nil), // 0: messagepb.Message + (*isspb.ISSMessage)(nil), // 1: isspb.ISSMessage + (*bcbpb.Message)(nil), // 2: bcbpb.Message + (*mscpb.Message)(nil), // 3: availabilitypb.mscpb.Message + (*pingpongpb.Message)(nil), // 4: pingpongpb.Message + (*checkpointpb.Message)(nil), // 5: checkpointpb.Message + (*ordererpb.Message)(nil), // 6: ordererpb.Message + (*communicationpb.Message)(nil), // 7: communicationpb.Message } var file_messagepb_messagepb_proto_depIdxs = []int32{ 1, // 0: messagepb.Message.iss:type_name -> isspb.ISSMessage @@ -258,11 +284,12 @@ var file_messagepb_messagepb_proto_depIdxs = []int32{ 4, // 3: messagepb.Message.pingpong:type_name -> pingpongpb.Message 5, // 4: messagepb.Message.checkpoint:type_name -> checkpointpb.Message 6, // 5: messagepb.Message.orderer:type_name -> ordererpb.Message - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 7, // 6: messagepb.Message.communicationpb:type_name -> communicationpb.Message + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_messagepb_messagepb_proto_init() } @@ -291,6 +318,7 @@ func file_messagepb_messagepb_proto_init() { (*Message_Pingpong)(nil), (*Message_Checkpoint)(nil), (*Message_Orderer)(nil), + (*Message_Communicationpb)(nil), } type x struct{} out := protoimpl.TypeBuilder{ diff --git a/pkg/pb/messagepb/messagepb.pb.mir.go b/pkg/pb/messagepb/messagepb.pb.mir.go index 4a44aaad3..991370475 100644 --- a/pkg/pb/messagepb/messagepb.pb.mir.go +++ b/pkg/pb/messagepb/messagepb.pb.mir.go @@ -14,5 +14,6 @@ func (*Message) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Message_Pingpong)(nil)), reflect.TypeOf((*Message_Checkpoint)(nil)), reflect.TypeOf((*Message_Orderer)(nil)), + reflect.TypeOf((*Message_Communicationpb)(nil)), } } diff --git a/pkg/pb/messagepb/oneof_interfaces.mir.go b/pkg/pb/messagepb/oneof_interfaces.mir.go index 9cd5cdcd6..987cdcf53 100644 --- a/pkg/pb/messagepb/oneof_interfaces.mir.go +++ b/pkg/pb/messagepb/oneof_interfaces.mir.go @@ -5,6 +5,7 @@ package messagepb import ( mscpb "github.com/filecoin-project/mir/pkg/pb/availabilitypb/mscpb" bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" + communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" isspb "github.com/filecoin-project/mir/pkg/pb/isspb" ordererpb "github.com/filecoin-project/mir/pkg/pb/ordererpb" @@ -41,3 +42,7 @@ func (w *Message_Checkpoint) Unwrap() *checkpointpb.Message { func (w *Message_Orderer) Unwrap() *ordererpb.Message { return w.Orderer } + +func (w *Message_Communicationpb) Unwrap() *communicationpb.Message { + return w.Communicationpb +} diff --git a/pkg/pb/messagepb/types/types.mir.go b/pkg/pb/messagepb/types/types.mir.go index 3d55bc14b..af6d27849 100644 --- a/pkg/pb/messagepb/types/types.mir.go +++ b/pkg/pb/messagepb/types/types.mir.go @@ -6,6 +6,7 @@ import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" types3 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/mscpb/types" types2 "github.com/filecoin-project/mir/pkg/pb/bcbpb/types" + types7 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" types5 "github.com/filecoin-project/mir/pkg/pb/checkpointpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/isspb/types" messagepb "github.com/filecoin-project/mir/pkg/pb/messagepb" @@ -48,6 +49,8 @@ func Message_TypeFromPb(pb messagepb.Message_Type) Message_Type { return &Message_Checkpoint{Checkpoint: types5.MessageFromPb(pb.Checkpoint)} case *messagepb.Message_Orderer: return &Message_Orderer{Orderer: types6.MessageFromPb(pb.Orderer)} + case *messagepb.Message_Communicationpb: + return &Message_Communicationpb{Communicationpb: types7.MessageFromPb(pb.Communicationpb)} } return nil } @@ -196,6 +199,30 @@ func (*Message_Orderer) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*messagepb.Message_Orderer]()} } +type Message_Communicationpb struct { + Communicationpb *types7.Message +} + +func (*Message_Communicationpb) isMessage_Type() {} + +func (w *Message_Communicationpb) Unwrap() *types7.Message { + return w.Communicationpb +} + +func (w *Message_Communicationpb) Pb() messagepb.Message_Type { + if w == nil { + return nil + } + if w.Communicationpb == nil { + return &messagepb.Message_Communicationpb{} + } + return &messagepb.Message_Communicationpb{Communicationpb: (w.Communicationpb).Pb()} +} + +func (*Message_Communicationpb) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*messagepb.Message_Communicationpb]()} +} + func MessageFromPb(pb *messagepb.Message) *Message { if pb == nil { return nil diff --git a/pkg/pb/mir/codegen_extensions.pb.go b/pkg/pb/mir/codegen_extensions.pb.go index 7e20985ec..b956c9892 100644 --- a/pkg/pb/mir/codegen_extensions.pb.go +++ b/pkg/pb/mir/codegen_extensions.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: mir/codegen_extensions.proto package mir diff --git a/pkg/pb/net/codegen_extensions.pb.go b/pkg/pb/net/codegen_extensions.pb.go index 8175c8679..e94241aeb 100644 --- a/pkg/pb/net/codegen_extensions.pb.go +++ b/pkg/pb/net/codegen_extensions.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: net/codegen_extensions.proto package net diff --git a/pkg/pb/ordererpb/ordererpb.pb.go b/pkg/pb/ordererpb/ordererpb.pb.go index 6be8f8e28..0f779a5db 100644 --- a/pkg/pb/ordererpb/ordererpb.pb.go +++ b/pkg/pb/ordererpb/ordererpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: ordererpb/ordererpb.proto package ordererpb @@ -30,6 +30,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_Pbft Type isEvent_Type `protobuf_oneof:"type"` } @@ -96,6 +97,7 @@ type Message struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Message_Pbft Type isMessage_Type `protobuf_oneof:"type"` } diff --git a/pkg/pb/ordererpb/pprepvalidatorpb/pprepvalidatorpb.pb.go b/pkg/pb/ordererpb/pprepvalidatorpb/pprepvalidatorpb.pb.go index 40f6d4855..970334dfa 100644 --- a/pkg/pb/ordererpb/pprepvalidatorpb/pprepvalidatorpb.pb.go +++ b/pkg/pb/ordererpb/pprepvalidatorpb/pprepvalidatorpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: ordererpb/pprepvalidatorpb/pprepvalidatorpb.proto package pprepvalidatorpb @@ -31,6 +31,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_ValidatePreprepare // *Event_PreprepareValidated Type isEvent_Type `protobuf_oneof:"type"` @@ -222,6 +223,7 @@ type ValidatePreprepareOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *ValidatePreprepareOrigin_ContextStore // *ValidatePreprepareOrigin_Dsl Type isValidatePreprepareOrigin_Type `protobuf_oneof:"type"` diff --git a/pkg/pb/pbftpb/pbftpb.pb.go b/pkg/pb/pbftpb/pbftpb.pb.go index 62b472a49..099be2f90 100644 --- a/pkg/pb/pbftpb/pbftpb.pb.go +++ b/pkg/pb/pbftpb/pbftpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: pbftpb/pbftpb.proto package pbftpb @@ -28,6 +28,7 @@ type Message struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Message_Preprepare // *Message_Prepare // *Message_Commit @@ -994,6 +995,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_ProposeTimeout // *Event_ViewChangeSnTimeout // *Event_ViewChangeSegTimeout diff --git a/pkg/pb/pingpongpb/pingpongpb.pb.go b/pkg/pb/pingpongpb/pingpongpb.pb.go index 2f88aec7c..46647c4f9 100644 --- a/pkg/pb/pingpongpb/pingpongpb.pb.go +++ b/pkg/pb/pingpongpb/pingpongpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: pingpongpb/pingpongpb.proto package pingpongpb @@ -28,6 +28,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_PingTime Type isEvent_Type `protobuf_oneof:"type"` } @@ -132,6 +133,7 @@ type Message struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Message_Ping // *Message_Pong Type isMessage_Type `protobuf_oneof:"type"` diff --git a/pkg/pb/recordingpb/recordingpb.pb.go b/pkg/pb/recordingpb/recordingpb.pb.go index 2585b98fa..2c7685b39 100644 --- a/pkg/pb/recordingpb/recordingpb.pb.go +++ b/pkg/pb/recordingpb/recordingpb.pb.go @@ -5,8 +5,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: recordingpb/recordingpb.proto package recordingpb diff --git a/pkg/pb/testerpb/testerpb.pb.go b/pkg/pb/testerpb/testerpb.pb.go index 7dedd89c7..33d137a9b 100644 --- a/pkg/pb/testerpb/testerpb.pb.go +++ b/pkg/pb/testerpb/testerpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: testerpb/testerpb.proto package testerpb diff --git a/pkg/pb/threshcryptopb/threshcryptopb.pb.go b/pkg/pb/threshcryptopb/threshcryptopb.pb.go index 739438330..a890a1d9f 100644 --- a/pkg/pb/threshcryptopb/threshcryptopb.pb.go +++ b/pkg/pb/threshcryptopb/threshcryptopb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: threshcryptopb/threshcryptopb.proto package threshcryptopb @@ -29,6 +29,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_SignShare // *Event_SignShareResult // *Event_VerifyShare @@ -304,6 +305,7 @@ type SignShareOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *SignShareOrigin_ContextStore // *SignShareOrigin_Dsl Type isSignShareOrigin_Type `protobuf_oneof:"type"` @@ -526,6 +528,7 @@ type VerifyShareOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *VerifyShareOrigin_ContextStore // *VerifyShareOrigin_Dsl Type isVerifyShareOrigin_Type `protobuf_oneof:"type"` @@ -740,6 +743,7 @@ type VerifyFullOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *VerifyFullOrigin_ContextStore // *VerifyFullOrigin_Dsl Type isVerifyFullOrigin_Type `protobuf_oneof:"type"` @@ -962,6 +966,7 @@ type RecoverOrigin struct { Module string `protobuf:"bytes,1,opt,name=module,proto3" json:"module,omitempty"` // Types that are assignable to Type: + // // *RecoverOrigin_ContextStore // *RecoverOrigin_Dsl Type isRecoverOrigin_Type `protobuf_oneof:"type"` diff --git a/pkg/pb/transportpb/transportpb.pb.go b/pkg/pb/transportpb/transportpb.pb.go index ef42d1f2b..17ac6ee68 100644 --- a/pkg/pb/transportpb/transportpb.pb.go +++ b/pkg/pb/transportpb/transportpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: transportpb/transportpb.proto package transportpb @@ -28,6 +28,7 @@ type Event struct { unknownFields protoimpl.UnknownFields // Types that are assignable to Type: + // // *Event_SendMessage // *Event_MessageReceived Type isEvent_Type `protobuf_oneof:"Type"` diff --git a/pkg/pb/trantorpb/trantorpb.pb.go b/pkg/pb/trantorpb/trantorpb.pb.go index ad414366b..d0c353c46 100644 --- a/pkg/pb/trantorpb/trantorpb.pb.go +++ b/pkg/pb/trantorpb/trantorpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: trantorpb/trantorpb.proto package trantorpb diff --git a/pkg/transactionreceiver/transactionreceiver.pb.go b/pkg/transactionreceiver/transactionreceiver.pb.go index c67b5b369..2045bd2fc 100644 --- a/pkg/transactionreceiver/transactionreceiver.pb.go +++ b/pkg/transactionreceiver/transactionreceiver.pb.go @@ -5,8 +5,8 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.25.0-devel -// protoc v3.14.0 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: transactionreceiver/transactionreceiver.proto package transactionreceiver diff --git a/pkg/transactionreceiver/transactionreceiver_grpc.pb.go b/pkg/transactionreceiver/transactionreceiver_grpc.pb.go index c088751b1..94d0f6e6d 100644 --- a/pkg/transactionreceiver/transactionreceiver_grpc.pb.go +++ b/pkg/transactionreceiver/transactionreceiver_grpc.pb.go @@ -1,4 +1,13 @@ +// +//Copyright IBM Corp. All Rights Reserved. +// +//SPDX-License-Identifier: Apache-2.0 + // Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.3.0 +// - protoc v4.24.4 +// source: transactionreceiver/transactionreceiver.proto package transactionreceiver @@ -15,6 +24,10 @@ import ( // Requires gRPC-Go v1.32.0 or later. const _ = grpc.SupportPackageIsVersion7 +const ( + TransactionReceiver_Listen_FullMethodName = "/receiver.TransactionReceiver/Listen" +) + // TransactionReceiverClient is the client API for TransactionReceiver service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. @@ -31,7 +44,7 @@ func NewTransactionReceiverClient(cc grpc.ClientConnInterface) TransactionReceiv } func (c *transactionReceiverClient) Listen(ctx context.Context, opts ...grpc.CallOption) (TransactionReceiver_ListenClient, error) { - stream, err := c.cc.NewStream(ctx, &TransactionReceiver_ServiceDesc.Streams[0], "/receiver.TransactionReceiver/Listen", opts...) + stream, err := c.cc.NewStream(ctx, &TransactionReceiver_ServiceDesc.Streams[0], TransactionReceiver_Listen_FullMethodName, opts...) if err != nil { return nil, err } diff --git a/protos/blockchainpb/bcmpb/bcmpb.proto b/protos/blockchainpb/bcmpb/bcmpb.proto new file mode 100644 index 000000000..f6380d6b4 --- /dev/null +++ b/protos/blockchainpb/bcmpb/bcmpb.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package bcmpb; + +option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb"; + +import "blockchainpb/blockchainpb.proto"; + +import "mir/codegen_extensions.proto"; + +message Event { + option (mir.event_class) = true; + + oneof type { + option (mir.event_type) = true; + + NewBlock new_block = 1; + } +} + +message NewBlock { + option (mir.event) = true; + + blockchainpb.Block block = 1; +} \ No newline at end of file diff --git a/protos/blockchainpb/blockchainpb.proto b/protos/blockchainpb/blockchainpb.proto new file mode 100644 index 000000000..bb6472d94 --- /dev/null +++ b/protos/blockchainpb/blockchainpb.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +package blockchainpb; + +option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb"; + +message Blocktree { + repeated Block blocks = 1; + repeated uint64 leaves = 2; +} + +message Blockchain { + repeated Block blocks = 1; // ordered, no forks -> the 'current' chain +} + +message Block { + uint64 block_id = 1; + uint64 previous_block_id = 2; + Payload payload = 3; + + int64 timestamp = 4; +} + +message Payload { + string text = 1; +} \ No newline at end of file diff --git a/protos/blockchainpb/communicationpb/communicationpb.proto b/protos/blockchainpb/communicationpb/communicationpb.proto new file mode 100644 index 000000000..88e8bda0f --- /dev/null +++ b/protos/blockchainpb/communicationpb/communicationpb.proto @@ -0,0 +1,42 @@ +syntax = "proto3"; + +package communicationpb; + +option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb"; + +import "blockchainpb/blockchainpb.proto"; + +import "mir/codegen_extensions.proto"; +import "net/codegen_extensions.proto"; + +message Event { + option (mir.event_class) = true; + + oneof type { + option (mir.event_type) = true; + + NewBlock new_block = 1; + } +} + +message NewBlock { + option (mir.event) = true; + + blockchainpb.Block block = 1; +} + +message Message { + option (net.message_class) = true; + + oneof type { + option (net.message_type) = true; + + NewBlockMessage new_block = 1; + } +} + +message NewBlockMessage { + option (net.message) = true; + + blockchainpb.Block block = 1; +} diff --git a/protos/blockchainpb/minerpb/minerpb.proto b/protos/blockchainpb/minerpb/minerpb.proto new file mode 100644 index 000000000..136396065 --- /dev/null +++ b/protos/blockchainpb/minerpb/minerpb.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package minerpb; + +option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb"; + +import "mir/codegen_extensions.proto"; +import "blockchainpb/blockchainpb.proto"; + +message Event { + option (mir.event_class) = true; + + oneof type { + option (mir.event_type) = true; + + BlockRequest block_request = 1; + } +} + +message BlockRequest { + option (mir.event) = true; + + uint64 head_id = 1; + blockchainpb.Payload payload = 2; +} diff --git a/protos/blockchainpb/tpmpb/tpmpb.proto b/protos/blockchainpb/tpmpb/tpmpb.proto new file mode 100644 index 000000000..3786f817c --- /dev/null +++ b/protos/blockchainpb/tpmpb/tpmpb.proto @@ -0,0 +1,24 @@ +syntax = "proto3"; + +package tpmpb; + +option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb"; + +import "mir/codegen_extensions.proto"; + +message Event { + option (mir.event_class) = true; + + oneof type { + option (mir.event_type) = true; + + NewHead new_head = 1; + } +} + +message NewHead { + option (mir.event) = true; + + //blockchainpb.Blockchain chain = 1; + uint64 head_id = 1; +} diff --git a/protos/eventpb/eventpb.proto b/protos/eventpb/eventpb.proto index 7e71e0fdf..df26dffc7 100644 --- a/protos/eventpb/eventpb.proto +++ b/protos/eventpb/eventpb.proto @@ -23,6 +23,10 @@ import "hasherpb/hasherpb.proto"; import "cryptopb/cryptopb.proto"; import "transportpb/transportpb.proto"; import "testerpb/testerpb.proto"; +import "blockchainpb/bcmpb/bcmpb.proto"; +import "blockchainpb/minerpb/minerpb.proto"; +import "blockchainpb/tpmpb/tpmpb.proto"; +import "blockchainpb/communicationpb/communicationpb.proto"; import "mir/codegen_extensions.proto"; @@ -63,6 +67,12 @@ message Event { // Events for code samples pingpongpb.Event ping_pong = 200; + // Events for blockchain + bcmpb.Event bcm = 201; + minerpb.Event miner = 202; + tpmpb.Event tpm = 203; + communicationpb.Event communication = 204; + // for unit-tests google.protobuf.StringValue testingString = 301; google.protobuf.UInt64Value testingUint = 302; diff --git a/protos/generate.go b/protos/generate.go index b16ff152b..85f74154b 100644 --- a/protos/generate.go +++ b/protos/generate.go @@ -44,6 +44,11 @@ package protos //go:generate protoc-events apppb/apppb.proto //go:generate protoc-events transportpb/transportpb.proto //go:generate protoc-events testerpb/testerpb.proto +//go:generate protoc-events blockchainpb/blockchainpb.proto +//go:generate protoc-events blockchainpb/bcmpb/bcmpb.proto +//go:generate protoc-events blockchainpb/minerpb/minerpb.proto +//go:generate protoc-events blockchainpb/tpmpb/tpmpb.proto +//go:generate protoc-events blockchainpb/communicationpb/communicationpb.proto // Build the custom code generators. //go:generate go build -o ../codegen/generators/mir-std-gen/mir-std-gen.bin ../codegen/generators/mir-std-gen @@ -75,6 +80,11 @@ package protos //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/transportpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/testerpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/pingpongpb" +//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb" +//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" +//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" +//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" +//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" // Generate other things. //go:generate protoc --go_out=../pkg/ --go_opt=paths=source_relative --go-grpc_out=../pkg/ --go-grpc_opt=paths=source_relative transactionreceiver/transactionreceiver.proto diff --git a/protos/messagepb/messagepb.proto b/protos/messagepb/messagepb.proto index b440113e6..30aed2c08 100644 --- a/protos/messagepb/messagepb.proto +++ b/protos/messagepb/messagepb.proto @@ -16,6 +16,7 @@ import "availabilitypb/mscpb/mscpb.proto"; import "pingpongpb/pingpongpb.proto"; import "checkpointpb/checkpointpb.proto"; import "ordererpb/ordererpb.proto"; +import "blockchainpb/communicationpb/communicationpb.proto"; import "mir/codegen_extensions.proto"; import "net/codegen_extensions.proto"; @@ -34,5 +35,9 @@ message Message { pingpongpb.Message pingpong = 5; checkpointpb.Message checkpoint = 6; ordererpb.Message orderer = 7; + + // Messages for blockchain + communicationpb.Message communicationpb = 8; + } } diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go new file mode 100644 index 000000000..14b018fc4 --- /dev/null +++ b/samples/blockchain/bcm.go @@ -0,0 +1,240 @@ +// blockchain manager + +package main + +import ( + "errors" + "fmt" + "strconv" + "sync" + + "github.com/filecoin-project/mir/pkg/dsl" + "github.com/filecoin-project/mir/pkg/modules" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" + tpmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/dsl" + "github.com/filecoin-project/mir/samples/blockchain/ws/ws" + "google.golang.org/protobuf/proto" +) + +var ( + parentNotFound = errors.New("parent not found") +) + +type bcmBlock struct { + block *blockchainpb.Block + parent *bcmBlock + depth uint64 + scanCount uint64 +} + +type bcmModule struct { + m *dsl.Module + blocks []bcmBlock + leaves map[uint64]*bcmBlock + head *bcmBlock + blockCount uint64 + writeMutex *sync.Mutex // can be smarted about locking but just locking everything for now + updateChan chan string +} + +// TODO: might need to add some "cleanup" to handle very old leaves in case this grows too much + +func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { + println("Adding block...", block.BlockId) + + bcm.writeMutex.Lock() + defer bcm.writeMutex.Unlock() + + parentId := block.PreviousBlockId + // check leaves for parent with its id + // return nil if it isn't there + if parent, ok := bcm.leaves[parentId]; ok { + println("Found parent in leaves") + blockNode := bcmBlock{block: block, parent: parent, depth: parent.depth + 1} + bcm.blocks = append(bcm.blocks, blockNode) + // replace leave + delete(bcm.leaves, parentId) + bcm.leaves[block.BlockId] = &blockNode + if blockNode.depth > bcm.head.depth { // update head only if new block is deeper, i.e. is the longest chain + bcm.head = &blockNode + } + bcm.blockCount++ + return nil + } + + // NOTE: should probably create a structure where very old leaves are scanned later than the newer ones + currentScanCount := uint64(0) // to mark nodes that have been scanned in this round + queue := make([]*bcmBlock, 0) + for _, v := range bcm.leaves { + queue = append(queue, v) + if v.scanCount > currentScanCount { + currentScanCount = v.scanCount + } + } + for len(queue) > 0 { + // pop from queue + curr := queue[0] + queue = queue[1:] + + // mark as scanned + curr.scanCount = currentScanCount + 1 + + // check if curr is parent + if curr.block.BlockId == parentId { + println("Found parent in tree") + blockNode := bcmBlock{block: block, parent: curr, depth: curr.depth + 1, scanCount: 0} + // add new leave + bcm.leaves[block.BlockId] = &blockNode + bcm.blocks = append(bcm.blocks, blockNode) + if blockNode.depth > bcm.head.depth { // update head only if new block is deeper, i.e. is the longest chain + bcm.head = &blockNode + } + bcm.blockCount++ + return nil + } + + // add curr's parents to queue if it hasn't been scanned yet + if curr.parent != nil && curr.parent.scanCount <= currentScanCount { + queue = append(queue, curr.parent) + } + } + + println("Couldn't find parent - invalid block. (TODO: add \"ask around for parent\")") + + return parentNotFound + +} + +func (bcm *bcmModule) getHead() *blockchainpb.Block { + return bcm.head.block +} + +func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { + currentHead := bcm.getHead() + // insert block + if err := bcm.addBlock(block); err != nil { + // silently ignore, just treat it as an invalid block + // TODO: handle "rebuild" of chain if one missed a block or started later + println(err) + } + + bcm.updateChan <- strconv.FormatUint(block.BlockId, 10) + + // if head changed, trigger get tpm to prep new block + if newHead := bcm.getHead(); newHead != currentHead { + // new head + println("New longest chain - head changed!") + tpmpbdsl.NewHead(*bcm.m, "tpm", newHead.BlockId) + } +} + +func NewBCM(chainServerPort int) modules.PassiveModule { + m := dsl.NewModule("bcm") + var bcm bcmModule + + dsl.UponInit(m, func() error { + + // making up a genisis block + genesis := &blockchainpb.Block{ + BlockId: 0, + PreviousBlockId: 0, + Payload: &blockchainpb.Payload{Text: "genesis"}, + Timestamp: 0, // unix 0 + } + + hash := hashBlock(genesis) + genesis.BlockId = hash + + // init bcm with genisis block + bcm = bcmModule{m: &m, leaves: make(map[uint64]*bcmBlock), head: &bcmBlock{block: genesis, parent: nil, depth: 0}, blockCount: 1} + bcm.leaves[hash] = bcm.head + + // init mutex + bcm.writeMutex = &sync.Mutex{} + + // setup update channel + bcm.updateChan = make(chan string) + + // start chain server (for visualization) + go bcm.chainServer(chainServerPort) + + // kick off tpm with genisis block + tpmpbdsl.NewHead(m, "tpm", hash) + + return nil + }) + + bcmpbdsl.UponNewBlock(m, func(block *blockchainpb.Block) error { + // self-mined block - no need to verify + println("~ Received block from miner") + bcm.handleNewBlock(block) + return nil + }) + + return m +} + +func (bcm *bcmModule) chainServer(port int) error { + fmt.Println("Starting chain server") + + websocket, send, _ := ws.NewWsServer(port) + go websocket.StartServers() + + for { + _ = <-bcm.updateChan + + bcm.writeMutex.Lock() + blocks := func() []*blockchainpb.Block { + blocks := make([]*blockchainpb.Block, 0, len(bcm.blocks)) + for _, v := range bcm.blocks { + blocks = append(blocks, v.block) + } + return blocks + }() + leaves := func() []uint64 { + leaves := make([]uint64, 0, len(bcm.leaves)) + for _, v := range bcm.leaves { + leaves = append(leaves, v.block.BlockId) + } + return leaves + }() + + bcm.writeMutex.Unlock() + + fmt.Printf("%d BLOCK COUNT @@@@@@@@\n", len(blocks)) + + blockTreeMessage := blockchainpb.Blocktree{Blocks: blocks, Leaves: leaves} + payload, err := proto.Marshal(&blockTreeMessage) + if err != nil { + panic(err) + } + + send <- ws.WsMessage{MessageType: 2, Payload: payload} + } + + // for { + // _ = <-bcm.updateChan + // bcm.writeMutex.Lock() + // fmt.Println("new print") + // queue := make([]bcmBlock, 0) + // for _, v := range bcm.leaves { + // queue = append(queue, v) + // } + // for len(queue) > 0 { + // // pop from queue + // curr := queue[0] + // queue = queue[1:] + + // fmt.Println(curr.block.BlockId) + + // if curr.parent != nil { + // queue = append(queue, *curr.parent) + // } + // } + // bcm.writeMutex.Unlock() + // } + fmt.Println("Chain server stopped") + + return nil +} diff --git a/samples/blockchain/communication.go b/samples/blockchain/communication.go new file mode 100644 index 000000000..fbd1ab8d5 --- /dev/null +++ b/samples/blockchain/communication.go @@ -0,0 +1,71 @@ +// handles all commmunication between nodes + +package main + +import ( + "math/rand" + + "log" + + "github.com/filecoin-project/mir/pkg/dsl" + "github.com/filecoin-project/mir/pkg/modules" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" + communicationpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/dsl" + communicationpbmsgs "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/msgs" + transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" + t "github.com/filecoin-project/mir/pkg/types" +) + +/* Note on simulated message loss and similar: + * Although package loss is very easy to simulate here, it might be more appropriate to use the event mangler. + * Not sure about this yet but something to keep in mind later + */ + +func NewCommunication(otherNodes []t.NodeID, probabilityMessageLost float32) modules.PassiveModule { + m := dsl.NewModule("communication") + + dsl.UponInit(m, func() error { + return nil + }) + + communicationpbdsl.UponNewBlock(m, func(block *blockchainpb.Block) error { + // take the block and send it to all other nodes + // could add so randomization here - only send to a subset of nodes + + // simulate message loss + receivers := make([]t.NodeID, 0, len(otherNodes)) + if probabilityMessageLost == 0 { + receivers = otherNodes + } else { + for _, node := range otherNodes { + if probabilityMessageLost > 1 && rand.Float32() < probabilityMessageLost { + receivers = append(receivers, node) + } + } + } + + if len(receivers) == 0 { + log.Printf("Block %d send to NO nodes", block.BlockId) + return nil + } else if len(receivers) == len(otherNodes) { + log.Printf("Block %d send to all nodes", block.BlockId) + } else { + log.Printf("Block %d send to %d/%d nodes", block.BlockId, len(receivers), len(otherNodes)) + } + + transportpbdsl.SendMessage(m, "transport", communicationpbmsgs.NewBlockMessage("communication", block), receivers) + + return nil + }) + + communicationpbdsl.UponNewBlockMessageReceived(m, func(from t.NodeID, block *blockchainpb.Block) error { + // take the block and add it to the blockchain + // could add some randomization here - delay/drop (drop implemented in send) + + bcmpbdsl.NewBlock(m, "bcm", block) + return nil + }) + + return m +} diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go new file mode 100644 index 000000000..46be220d4 --- /dev/null +++ b/samples/blockchain/main.go @@ -0,0 +1,106 @@ +package main + +import ( + "context" + "fmt" + "os" + "strconv" + + "github.com/filecoin-project/mir" + "github.com/filecoin-project/mir/pkg/logging" + "github.com/filecoin-project/mir/pkg/modules" + "github.com/filecoin-project/mir/pkg/net/grpc" + trantorpbtypes "github.com/filecoin-project/mir/pkg/pb/trantorpb/types" + t "github.com/filecoin-project/mir/pkg/types" +) + +func main() { + fmt.Println("Starting blockchain") + + numberOfNodes, err := strconv.Atoi(os.Args[1]) + if err != nil { + panic(err) + } + idInput := os.Args[2] + ownID, err := strconv.Atoi(idInput) + if err != nil { + panic(err) + } + ownNodeID := t.NodeID(idInput) + + msgLossProb := 0.0 + if len(os.Args) <= 3 { + fmt.Printf("No message loss probability provided, defaulting to 0.0\n") + } else { + msgLossProb, err = strconv.ParseFloat(os.Args[3], 32) + if err != nil { + panic(err) + } + } + + nodes := make(map[t.NodeID]*trantorpbtypes.NodeIdentity, numberOfNodes) + allNodeIds := make([]t.NodeID, numberOfNodes) + otherNodes := make([]t.NodeID, numberOfNodes-1) + otherNodesIndex := 0 + for i := 0; i < numberOfNodes; i++ { + nodeIdStr := strconv.Itoa(i) + nodeId := t.NodeID(nodeIdStr) + allNodeIds[i] = nodeId + if nodeId != ownNodeID { + otherNodes[otherNodesIndex] = nodeId + otherNodesIndex++ + } + nodes[nodeId] = &trantorpbtypes.NodeIdentity{Id: nodeId, Addr: "/ip4/127.0.0.1/tcp/1000" + nodeIdStr, Key: nil, Weight: "1"} + } + membership := &trantorpbtypes.Membership{Nodes: nodes} + + // Instantiate network trnasport module and establish connections. + transport, err := grpc.NewTransport(ownNodeID, membership.Nodes[ownNodeID].Addr, logging.ConsoleWarnLogger) + if err != nil { + panic(err) + } + if err := transport.Start(); err != nil { + panic(err) + } + transport.Connect(membership) + + // Instantiate Mir node. + node, err := mir.NewNode( + ownNodeID, + mir.DefaultNodeConfig(), + map[t.ModuleID]modules.Module{ + "transport": transport, + "bcm": NewBCM(8080 + ownID), + "miner": NewMiner(), + "communication": NewCommunication(otherNodes, float32(msgLossProb)), + "tpm": NewTPM(), + }, + nil, + ) + if err != nil { + panic(err) + } + + // Run the node for 5 seconds. + nodeError := make(chan error) + go func() { + nodeError <- node.Run(context.Background()) + }() + fmt.Println("Mir node running.") + + // block until nodeError receives an error + // fmt.Printf("timer started\n") + + switch <-nodeError { + default: + fmt.Printf("Mir node stopped: %v\n", <-nodeError) + } + + // time.Sleep(5 * time.Second) + // fmt.Printf("timer up") + + // Stop the node. + node.Stop() + transport.Stop() + fmt.Printf("Mir node stopped: %v\n", <-nodeError) +} diff --git a/samples/blockchain/miner.go b/samples/blockchain/miner.go new file mode 100644 index 000000000..15bee577c --- /dev/null +++ b/samples/blockchain/miner.go @@ -0,0 +1,92 @@ +package main + +import ( + "context" + "math/rand" + "time" + + "github.com/filecoin-project/mir/pkg/events" + "github.com/filecoin-project/mir/pkg/modules" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + bcmpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/events" + communicationpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/events" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" + "github.com/filecoin-project/mir/pkg/pb/eventpb" + "github.com/go-errors/errors" + "github.com/mitchellh/hashstructure" +) + +type blockRequest struct { + HeadId uint64 + Payload *blockchainpb.Payload +} + +type minerModule struct { + blockRequests chan blockRequest + eventsOut chan *events.EventList +} + +func NewMiner() modules.ActiveModule { + return &minerModule{ + blockRequests: make(chan blockRequest), + eventsOut: make(chan *events.EventList), + } +} + +func (m *minerModule) ImplementsModule() {} + +func (m *minerModule) EventsOut() <-chan *events.EventList { + return m.eventsOut +} + +func (m *minerModule) ApplyEvents(context context.Context, events *events.EventList) error { + for _, event := range events.Slice() { + switch e := event.Type.(type) { + case *eventpb.Event_Init: + go m.mineWorkerManager() + case *eventpb.Event_Miner: + switch e := e.Miner.Type.(type) { + case *minerpb.Event_BlockRequest: + m.blockRequests <- blockRequest{e.BlockRequest.HeadId, e.BlockRequest.Payload} + return nil + default: + return errors.Errorf("unknown miner event: %T", e) + } + default: + return errors.Errorf("unknown event: %T", e) + } + } + + return nil +} + +func (m *minerModule) mineWorkerManager() { + ctx, cancel := context.WithCancel(context.Background()) + + for { + blockRequest := <-m.blockRequests + cancel() // abort ongoing mining + ctx, cancel = context.WithCancel(context.Background()) // new context for new mining + println("Mining block on top of", blockRequest.HeadId) + go func() { + delay := time.Duration(rand.ExpFloat64() * float64(time.Minute)) + select { + case <-ctx.Done(): + println("##### Mining aborted #####") + return + case <-time.After(delay): + println("Block mined!") + block := &blockchainpb.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: time.Now().Unix()} + hash, err := hashstructure.Hash(block, nil) + if err != nil { + panic(err) + } + block.BlockId = hash + // Block mined! Broadcast to blockchain and send event to bcm. + m.eventsOut <- events.ListOf(bcmpbevents.NewBlock("bcm", block).Pb(), communicationpbevents.NewBlock("communication", block).Pb()) + println("Block shared!") + return + } + }() + } +} diff --git a/samples/blockchain/old/miner_passive.go b/samples/blockchain/old/miner_passive.go new file mode 100644 index 000000000..50a2da188 --- /dev/null +++ b/samples/blockchain/old/miner_passive.go @@ -0,0 +1,132 @@ +// miner + +package main + +import ( + "context" + "math/rand" + "time" + + "github.com/filecoin-project/mir/pkg/dsl" + "github.com/filecoin-project/mir/pkg/modules" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + bcmmsgs "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/msgs" + minerdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" + transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" + t "github.com/filecoin-project/mir/pkg/types" + "github.com/mitchellh/hashstructure" +) + +type minerModule struct { + ctx context.Context // to cancel ongoing mining when new block is requested + cancel context.CancelFunc +} + +type blockRequest struct { + HeadId uint64 + Payload *blockchainpb.Payload +} + +func NewMiner(otherNodes []t.NodeID) modules.PassiveModule { + + m := dsl.NewModule("miner") + var miner minerModule + // blockRequests := make(chan blockRequest) + + dsl.UponInit(m, func() error { + ctx, cancel := context.WithCancel(context.Background()) + miner = minerModule{ctx: ctx, cancel: cancel} + + println("Othr nodes:") + for _, node := range otherNodes { + println(node) + } + // go mineWorker(&m, blockRequests, otherNodes) + + return nil + }) + + minerdsl.UponBlockRequest(m, func(head_id uint64, payload *blockchainpb.Payload) error { + + // blockRequests <- blockRequest{head_id, payload} + + println("Mining block on top of", head_id) + + // cancel ongoing mining + miner.cancel() + // start new mining + miner.ctx, miner.cancel = context.WithCancel(context.Background()) + ownCtx := &miner.ctx + // random mining time to simulate PoW + endtime := time.Now().Add(time.Duration(rand.ExpFloat64() * float64(2*time.Minute))) + for { + if time.Now().After(endtime) { + break + } + + select { + case <-(*ownCtx).Done(): + println("Mining aborted") + return nil + default: + // mine block + time.Sleep(time.Second) + } + } + println("Block mined!") + block := &blockchainpb.Block{BlockId: 0, PreviousBlockId: head_id, Payload: payload, Timestamp: time.Now().Unix()} + hash, err := hashstructure.Hash(block, nil) + if err != nil { + panic(err) + } + block.BlockId = hash + // Block mined! Broadcast to blockchain and send event to bcm. + // bcmdsl.NewBlock(m, "bcm", block) + transportpbdsl.SendMessage(m, "transport", bcmmsgs.NewBlockMessage("bcm", block), otherNodes) + println("Block broadcasted!") + return nil + + }) + + return m +} + +// func mineWorker(m *dsl.Module, blockRequests chan blockRequest, otherNodes []t.NodeID) { +// ctx, cancel := context.WithCancel(context.Background()) + +// for { +// blockRequest := <-blockRequests +// cancel() // abort ongoing mining +// ctx, cancel = context.WithCancel(context.Background()) // new context for new mining +// println("Mining block on top of", blockRequest.HeadId) +// go func() { +// endtime := time.Now().Add(time.Duration(rand.ExpFloat64() * float64(time.Minute))) +// for { +// if time.Now().After(endtime) { +// println("Block mined!") +// block := &blockchainpb.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: time.Now().Unix()} +// hash, err := hashstructure.Hash(block, nil) +// if err != nil { +// panic(err) +// } +// block.BlockId = hash +// // Block mined! Broadcast to blockchain and send event to bcm. +// bcmdsl.NewBlock(*m, "bcm", block) +// transportpbdsl.SendMessage(*m, "transport", bcmmsgs.NewBlockMessage("bcm", block), otherNodes) +// println("Block broadcasted!") +// return +// } + +// select { +// case <-ctx.Done(): +// println("Mining aborted") +// return +// default: +// // mine block +// time.Sleep(time.Second) +// } +// } +// }() +// println("not blocking...") +// } +// } diff --git a/samples/blockchain/tpm.go b/samples/blockchain/tpm.go new file mode 100644 index 000000000..34399f01f --- /dev/null +++ b/samples/blockchain/tpm.go @@ -0,0 +1,44 @@ +// transaction pool manager + +package main + +import ( + "math/rand" + "time" + + "github.com/filecoin-project/mir/pkg/dsl" + "github.com/filecoin-project/mir/pkg/modules" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + minerdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" + tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/dsl" +) + +const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + +func NewTPM() modules.PassiveModule { + + m := dsl.NewModule("tpm") + + dsl.UponInit(m, func() error { + return nil + }) + + tpmpb.UponNewHead(m, func(headId uint64) error { + // just generating random payloads for now + // generate random string + println("Generating random payload...") + var seed *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) + length := seed.Intn(15) + b := make([]byte, length) + for i := range b { + b[i] = charset[seed.Intn(len(charset))] + } + payload := &blockchainpb.Payload{Text: string(b)} + + minerdsl.BlockRequest(m, "miner", headId, payload) + + return nil + }) + + return m +} diff --git a/samples/blockchain/utils.go b/samples/blockchain/utils.go new file mode 100644 index 000000000..a52179fee --- /dev/null +++ b/samples/blockchain/utils.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + "github.com/mitchellh/hashstructure" +) + +func hashBlock(block *blockchainpb.Block) uint64 { + hashBlock := &blockchainpb.Block{BlockId: 0, PreviousBlockId: block.PreviousBlockId, Payload: block.Payload, Timestamp: block.Timestamp} + hash, err := hashstructure.Hash(hashBlock, nil) + if err != nil { + panic(err) + } + return hash +} diff --git a/samples/blockchain/ws/main.go b/samples/blockchain/ws/main.go new file mode 100644 index 000000000..70b26afbf --- /dev/null +++ b/samples/blockchain/ws/main.go @@ -0,0 +1,16 @@ +package main + +import "github.com/filecoin-project/mir/samples/blockchain/ws/ws" + +func main() { + ws, send, recv := ws.NewWsServer(8080) + go ws.StartServers() + + for { + msg := <-recv + println("Received message...") + println(msg.Payload) + send <- msg + } + +} diff --git a/samples/blockchain/ws/ws/wss.go b/samples/blockchain/ws/ws/wss.go new file mode 100644 index 000000000..2ee41f8a2 --- /dev/null +++ b/samples/blockchain/ws/ws/wss.go @@ -0,0 +1,138 @@ +package ws + +import ( + "fmt" + "log" + "net/http" + "sync" + + "github.com/google/uuid" + "github.com/gorilla/websocket" +) + +type WsMessage struct { + MessageType int + Payload []byte +} + +type connection struct { + id string + server *wsServer + ws *websocket.Conn + sendChan chan WsMessage + recvChan chan WsMessage +} + +type wsServer struct { + port int + connections map[string]connection + sendChan chan WsMessage + recvChan chan WsMessage + connectionsLock sync.Mutex +} + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +func (wss *wsServer) handleHome(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.Error(w, "Not found", http.StatusNotFound) + return + } + if r.Method != "GET" { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + w.Write([]byte("Hello world!")) +} + +func (wsc *connection) wsConnection() { + log.Println("wsConnection") + // close connection + defer wsc.ws.Close() + // close up channels and remove connection + defer func() { + wsc.server.connectionsLock.Lock() + delete(wsc.server.connections, wsc.id) + close(wsc.sendChan) + wsc.server.connectionsLock.Unlock() + }() + + // sending msgs + go func() { + for { + message := <-wsc.sendChan + err := wsc.ws.WriteMessage(message.MessageType, message.Payload) + if err != nil { + log.Println(err) + panic(err) + } + } + }() + + // receiving msgs + for { + messageType, p, err := wsc.ws.ReadMessage() + if err != nil { + log.Println(err) + return + } + wsc.recvChan <- WsMessage{messageType, p} + } +} + +func (wss *wsServer) handleWs(w http.ResponseWriter, r *http.Request) { + // cors * + upgrader.CheckOrigin = func(r *http.Request) bool { return true } + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Printf("Upgrade failed: %v", err) + return + } + wss.connectionsLock.Lock() + id := "" + for { + id = uuid.New().String() + if _, ok := wss.connections[id]; !ok { + break + } + } + conn := connection{id, wss, ws, make(chan WsMessage), wss.recvChan} + wss.connections[id] = conn + wss.connectionsLock.Unlock() + go conn.wsConnection() +} + +func (wss *wsServer) setupHttpRoutes() { + http.HandleFunc("/", wss.handleHome) + http.HandleFunc("/ws", wss.handleWs) +} + +func (wss *wsServer) sendHandler() { + for { + message := <-wss.sendChan + for _, conn := range wss.connections { + conn.sendChan <- message + } + } +} + +func (wss *wsServer) StartServers() { + log.Println("Starting servers...") + wss.setupHttpRoutes() + go wss.sendHandler() + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", wss.port), nil)) +} + +func NewWsServer(port int) (*wsServer, chan WsMessage, chan WsMessage) { + wss := &wsServer{ + port: port, + connections: make(map[string]connection), + sendChan: make(chan WsMessage), + recvChan: make(chan WsMessage), + } + + return wss, wss.sendChan, wss.recvChan +} From 7002720eba87d4006557064739b8dc09c5d78c6c Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Thu, 9 Nov 2023 12:47:26 +0100 Subject: [PATCH 02/46] wip synchronizer --- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go | 333 ++++++++++- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go | 3 + pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go | 12 + pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go | 19 + .../blockchainpb/bcmpb/events/events.mir.go | 49 ++ .../bcmpb/oneof_interfaces.mir.go | 12 + pkg/pb/blockchainpb/bcmpb/types/types.mir.go | 180 ++++++ .../synchronizerpb/dsl/emit.mir.go | 15 + .../synchronizerpb/dsl/messages.mir.go | 37 ++ .../synchronizerpb/dsl/upon.mir.go | 28 + .../synchronizerpb/events/events.mir.go | 25 + .../synchronizerpb/msgs/msgs.mir.go | 43 ++ .../synchronizerpb/oneof_interfaces.mir.go | 29 + .../synchronizerpb/synchronizerpb.pb.go | 527 ++++++++++++++++++ .../synchronizerpb/synchronizerpb.pb.mir.go | 20 + .../synchronizerpb/types/types.mir.go | 291 ++++++++++ pkg/pb/eventpb/eventpb.pb.go | 215 +++---- pkg/pb/eventpb/eventpb.pb.mir.go | 1 + pkg/pb/eventpb/oneof_interfaces.mir.go | 5 + pkg/pb/eventpb/types/types.mir.go | 69 ++- pkg/pb/messagepb/messagepb.pb.go | 113 ++-- pkg/pb/messagepb/messagepb.pb.mir.go | 1 + pkg/pb/messagepb/oneof_interfaces.mir.go | 5 + pkg/pb/messagepb/types/types.mir.go | 27 + protos/blockchainpb/bcmpb/bcmpb.proto | 30 +- .../synchronizerpb/synchronizerpb.proto | 54 ++ protos/eventpb/eventpb.proto | 2 + protos/generate.go | 2 + protos/messagepb/messagepb.proto | 2 + samples/blockchain/bcm.go | 156 ++++-- samples/blockchain/old/miner_passive.go | 132 ----- samples/blockchain/synchronizer.go | 198 +++++++ 32 files changed, 2290 insertions(+), 345 deletions(-) create mode 100644 pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go create mode 100644 pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go create mode 100644 pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go create mode 100644 pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go create mode 100644 pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go create mode 100644 pkg/pb/blockchainpb/synchronizerpb/oneof_interfaces.mir.go create mode 100644 pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go create mode 100644 pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.mir.go create mode 100644 pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go create mode 100644 protos/blockchainpb/synchronizerpb/synchronizerpb.proto delete mode 100644 samples/blockchain/old/miner_passive.go create mode 100644 samples/blockchain/synchronizer.go diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go index 44088f3b5..915d76993 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go @@ -30,6 +30,9 @@ type Event struct { // Types that are assignable to Type: // // *Event_NewBlock + // *Event_NewChain + // *Event_GetBlockRequest + // *Event_GetBlockResponse Type isEvent_Type `protobuf_oneof:"type"` } @@ -79,6 +82,27 @@ func (x *Event) GetNewBlock() *NewBlock { return nil } +func (x *Event) GetNewChain() *NewChain { + if x, ok := x.GetType().(*Event_NewChain); ok { + return x.NewChain + } + return nil +} + +func (x *Event) GetGetBlockRequest() *GetBlockRequest { + if x, ok := x.GetType().(*Event_GetBlockRequest); ok { + return x.GetBlockRequest + } + return nil +} + +func (x *Event) GetGetBlockResponse() *GetBlockResponse { + if x, ok := x.GetType().(*Event_GetBlockResponse); ok { + return x.GetBlockResponse + } + return nil +} + type isEvent_Type interface { isEvent_Type() } @@ -87,8 +111,26 @@ type Event_NewBlock struct { NewBlock *NewBlock `protobuf:"bytes,1,opt,name=new_block,json=newBlock,proto3,oneof"` } +type Event_NewChain struct { + NewChain *NewChain `protobuf:"bytes,2,opt,name=new_chain,json=newChain,proto3,oneof"` +} + +type Event_GetBlockRequest struct { + GetBlockRequest *GetBlockRequest `protobuf:"bytes,3,opt,name=get_block_request,json=getBlockRequest,proto3,oneof"` +} + +type Event_GetBlockResponse struct { + GetBlockResponse *GetBlockResponse `protobuf:"bytes,4,opt,name=get_block_response,json=getBlockResponse,proto3,oneof"` +} + func (*Event_NewBlock) isEvent_Type() {} +func (*Event_NewChain) isEvent_Type() {} + +func (*Event_GetBlockRequest) isEvent_Type() {} + +func (*Event_GetBlockResponse) isEvent_Type() {} + type NewBlock struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -136,6 +178,180 @@ func (x *NewBlock) GetBlock() *blockchainpb.Block { return nil } +type NewChain struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // chain segment ordered blocks[0]<-blocks[1]<-blocks[2]... + Blocks []*blockchainpb.Block `protobuf:"bytes,1,rep,name=blocks,proto3" json:"blocks,omitempty"` +} + +func (x *NewChain) Reset() { + *x = NewChain{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewChain) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewChain) ProtoMessage() {} + +func (x *NewChain) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[2] + 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 NewChain.ProtoReflect.Descriptor instead. +func (*NewChain) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{2} +} + +func (x *NewChain) GetBlocks() []*blockchainpb.Block { + if x != nil { + return x.Blocks + } + return nil +} + +type GetBlockRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + SourceModule string `protobuf:"bytes,2,opt,name=source_module,json=sourceModule,proto3" json:"source_module,omitempty"` + BlockId uint64 `protobuf:"varint,3,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` +} + +func (x *GetBlockRequest) Reset() { + *x = GetBlockRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockRequest) ProtoMessage() {} + +func (x *GetBlockRequest) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[3] + 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 GetBlockRequest.ProtoReflect.Descriptor instead. +func (*GetBlockRequest) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{3} +} + +func (x *GetBlockRequest) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *GetBlockRequest) GetSourceModule() string { + if x != nil { + return x.SourceModule + } + return "" +} + +func (x *GetBlockRequest) GetBlockId() uint64 { + if x != nil { + return x.BlockId + } + return 0 +} + +type GetBlockResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Found bool `protobuf:"varint,2,opt,name=found,proto3" json:"found,omitempty"` + Block *blockchainpb.Block `protobuf:"bytes,3,opt,name=block,proto3" json:"block,omitempty"` +} + +func (x *GetBlockResponse) Reset() { + *x = GetBlockResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetBlockResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetBlockResponse) ProtoMessage() {} + +func (x *GetBlockResponse) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_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 GetBlockResponse.ProtoReflect.Descriptor instead. +func (*GetBlockResponse) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{4} +} + +func (x *GetBlockResponse) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *GetBlockResponse) GetFound() bool { + if x != nil { + return x.Found + } + return false +} + +func (x *GetBlockResponse) GetBlock() *blockchainpb.Block { + if x != nil { + return x.Block + } + return nil +} + var File_blockchainpb_bcmpb_bcmpb_proto protoreflect.FileDescriptor var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ @@ -145,20 +361,54 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x4b, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, - 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8a, 0x02, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x12, 0x44, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x63, + 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x10, 0x67, + 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, - 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, - 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, - 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x22, 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, + 0xae, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, + 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, + 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, + 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, + 0x22, 0x78, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, + 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, + 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -173,20 +423,28 @@ func file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP() []byte { return file_blockchainpb_bcmpb_bcmpb_proto_rawDescData } -var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 5) var file_blockchainpb_bcmpb_bcmpb_proto_goTypes = []interface{}{ (*Event)(nil), // 0: bcmpb.Event (*NewBlock)(nil), // 1: bcmpb.NewBlock - (*blockchainpb.Block)(nil), // 2: blockchainpb.Block + (*NewChain)(nil), // 2: bcmpb.NewChain + (*GetBlockRequest)(nil), // 3: bcmpb.GetBlockRequest + (*GetBlockResponse)(nil), // 4: bcmpb.GetBlockResponse + (*blockchainpb.Block)(nil), // 5: blockchainpb.Block } var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ 1, // 0: bcmpb.Event.new_block:type_name -> bcmpb.NewBlock - 2, // 1: bcmpb.NewBlock.block:type_name -> blockchainpb.Block - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 2, // 1: bcmpb.Event.new_chain:type_name -> bcmpb.NewChain + 3, // 2: bcmpb.Event.get_block_request:type_name -> bcmpb.GetBlockRequest + 4, // 3: bcmpb.Event.get_block_response:type_name -> bcmpb.GetBlockResponse + 5, // 4: bcmpb.NewBlock.block:type_name -> blockchainpb.Block + 5, // 5: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block + 5, // 6: bcmpb.GetBlockResponse.block:type_name -> blockchainpb.Block + 7, // [7:7] is the sub-list for method output_type + 7, // [7:7] is the sub-list for method input_type + 7, // [7:7] is the sub-list for extension type_name + 7, // [7:7] is the sub-list for extension extendee + 0, // [0:7] is the sub-list for field type_name } func init() { file_blockchainpb_bcmpb_bcmpb_proto_init() } @@ -219,9 +477,48 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { return nil } } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewChain); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlockRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetBlockResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_NewBlock)(nil), + (*Event_NewChain)(nil), + (*Event_GetBlockRequest)(nil), + (*Event_GetBlockResponse)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -229,7 +526,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_bcmpb_bcmpb_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 5, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go index 74724e330..3dac6804e 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go @@ -9,5 +9,8 @@ import ( func (*Event) ReflectTypeOptions() []reflect.Type { return []reflect.Type{ reflect.TypeOf((*Event_NewBlock)(nil)), + reflect.TypeOf((*Event_NewChain)(nil)), + reflect.TypeOf((*Event_GetBlockRequest)(nil)), + reflect.TypeOf((*Event_GetBlockResponse)(nil)), } } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go index 339584ef7..2ed68b151 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go @@ -14,3 +14,15 @@ import ( func NewBlock(m dsl.Module, destModule types.ModuleID, block *blockchainpb.Block) { dsl.EmitMirEvent(m, events.NewBlock(destModule, block)) } + +func NewChain(m dsl.Module, destModule types.ModuleID, blocks []*blockchainpb.Block) { + dsl.EmitMirEvent(m, events.NewChain(destModule, blocks)) +} + +func GetBlockRequest(m dsl.Module, destModule types.ModuleID, requestId uint64, sourceModule types.ModuleID, blockId uint64) { + dsl.EmitMirEvent(m, events.GetBlockRequest(destModule, requestId, sourceModule, blockId)) +} + +func GetBlockResponse(m dsl.Module, destModule types.ModuleID, requestId uint64, found bool, block *blockchainpb.Block) { + dsl.EmitMirEvent(m, events.GetBlockResponse(destModule, requestId, found, block)) +} diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go index 78be19936..13a9f0b12 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go @@ -7,6 +7,7 @@ import ( blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types2 "github.com/filecoin-project/mir/pkg/types" ) // Module-specific dsl functions for processing events. @@ -27,3 +28,21 @@ func UponNewBlock(m dsl.Module, handler func(block *blockchainpb.Block) error) { return handler(ev.Block) }) } + +func UponNewChain(m dsl.Module, handler func(blocks []*blockchainpb.Block) error) { + UponEvent[*types.Event_NewChain](m, func(ev *types.NewChain) error { + return handler(ev.Blocks) + }) +} + +func UponGetBlockRequest(m dsl.Module, handler func(requestId uint64, sourceModule types2.ModuleID, blockId uint64) error) { + UponEvent[*types.Event_GetBlockRequest](m, func(ev *types.GetBlockRequest) error { + return handler(ev.RequestId, ev.SourceModule, ev.BlockId) + }) +} + +func UponGetBlockResponse(m dsl.Module, handler func(requestId uint64, found bool, block *blockchainpb.Block) error) { + UponEvent[*types.Event_GetBlockResponse](m, func(ev *types.GetBlockResponse) error { + return handler(ev.RequestId, ev.Found, ev.Block) + }) +} diff --git a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go index 0b739b589..9266071d8 100644 --- a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go @@ -23,3 +23,52 @@ func NewBlock(destModule types.ModuleID, block *blockchainpb.Block) *types1.Even }, } } + +func NewChain(destModule types.ModuleID, blocks []*blockchainpb.Block) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcm{ + Bcm: &types2.Event{ + Type: &types2.Event_NewChain{ + NewChain: &types2.NewChain{ + Blocks: blocks, + }, + }, + }, + }, + } +} + +func GetBlockRequest(destModule types.ModuleID, requestId uint64, sourceModule types.ModuleID, blockId uint64) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcm{ + Bcm: &types2.Event{ + Type: &types2.Event_GetBlockRequest{ + GetBlockRequest: &types2.GetBlockRequest{ + RequestId: requestId, + SourceModule: sourceModule, + BlockId: blockId, + }, + }, + }, + }, + } +} + +func GetBlockResponse(destModule types.ModuleID, requestId uint64, found bool, block *blockchainpb.Block) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcm{ + Bcm: &types2.Event{ + Type: &types2.Event_GetBlockResponse{ + GetBlockResponse: &types2.GetBlockResponse{ + RequestId: requestId, + Found: found, + Block: block, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go index 9371708b2..b69b62a57 100644 --- a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go @@ -12,3 +12,15 @@ type Event_TypeWrapper[T any] interface { func (w *Event_NewBlock) Unwrap() *NewBlock { return w.NewBlock } + +func (w *Event_NewChain) Unwrap() *NewChain { + return w.NewChain +} + +func (w *Event_GetBlockRequest) Unwrap() *GetBlockRequest { + return w.GetBlockRequest +} + +func (w *Event_GetBlockResponse) Unwrap() *GetBlockResponse { + return w.GetBlockResponse +} diff --git a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go index 837a0a77a..a82680560 100644 --- a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go @@ -6,6 +6,7 @@ import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" + types "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -31,6 +32,12 @@ func Event_TypeFromPb(pb bcmpb.Event_Type) Event_Type { switch pb := pb.(type) { case *bcmpb.Event_NewBlock: return &Event_NewBlock{NewBlock: NewBlockFromPb(pb.NewBlock)} + case *bcmpb.Event_NewChain: + return &Event_NewChain{NewChain: NewChainFromPb(pb.NewChain)} + case *bcmpb.Event_GetBlockRequest: + return &Event_GetBlockRequest{GetBlockRequest: GetBlockRequestFromPb(pb.GetBlockRequest)} + case *bcmpb.Event_GetBlockResponse: + return &Event_GetBlockResponse{GetBlockResponse: GetBlockResponseFromPb(pb.GetBlockResponse)} } return nil } @@ -59,6 +66,78 @@ func (*Event_NewBlock) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_NewBlock]()} } +type Event_NewChain struct { + NewChain *NewChain +} + +func (*Event_NewChain) isEvent_Type() {} + +func (w *Event_NewChain) Unwrap() *NewChain { + return w.NewChain +} + +func (w *Event_NewChain) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.NewChain == nil { + return &bcmpb.Event_NewChain{} + } + return &bcmpb.Event_NewChain{NewChain: (w.NewChain).Pb()} +} + +func (*Event_NewChain) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_NewChain]()} +} + +type Event_GetBlockRequest struct { + GetBlockRequest *GetBlockRequest +} + +func (*Event_GetBlockRequest) isEvent_Type() {} + +func (w *Event_GetBlockRequest) Unwrap() *GetBlockRequest { + return w.GetBlockRequest +} + +func (w *Event_GetBlockRequest) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.GetBlockRequest == nil { + return &bcmpb.Event_GetBlockRequest{} + } + return &bcmpb.Event_GetBlockRequest{GetBlockRequest: (w.GetBlockRequest).Pb()} +} + +func (*Event_GetBlockRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetBlockRequest]()} +} + +type Event_GetBlockResponse struct { + GetBlockResponse *GetBlockResponse +} + +func (*Event_GetBlockResponse) isEvent_Type() {} + +func (w *Event_GetBlockResponse) Unwrap() *GetBlockResponse { + return w.GetBlockResponse +} + +func (w *Event_GetBlockResponse) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.GetBlockResponse == nil { + return &bcmpb.Event_GetBlockResponse{} + } + return &bcmpb.Event_GetBlockResponse{GetBlockResponse: (w.GetBlockResponse).Pb()} +} + +func (*Event_GetBlockResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetBlockResponse]()} +} + func EventFromPb(pb *bcmpb.Event) *Event { if pb == nil { return nil @@ -116,3 +195,104 @@ func (m *NewBlock) Pb() *bcmpb.NewBlock { func (*NewBlock) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.NewBlock]()} } + +type NewChain struct { + Blocks []*blockchainpb.Block +} + +func NewChainFromPb(pb *bcmpb.NewChain) *NewChain { + if pb == nil { + return nil + } + return &NewChain{ + Blocks: pb.Blocks, + } +} + +func (m *NewChain) Pb() *bcmpb.NewChain { + if m == nil { + return nil + } + pbMessage := &bcmpb.NewChain{} + { + pbMessage.Blocks = m.Blocks + } + + return pbMessage +} + +func (*NewChain) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.NewChain]()} +} + +type GetBlockRequest struct { + RequestId uint64 + SourceModule types.ModuleID + BlockId uint64 +} + +func GetBlockRequestFromPb(pb *bcmpb.GetBlockRequest) *GetBlockRequest { + if pb == nil { + return nil + } + return &GetBlockRequest{ + RequestId: pb.RequestId, + SourceModule: (types.ModuleID)(pb.SourceModule), + BlockId: pb.BlockId, + } +} + +func (m *GetBlockRequest) Pb() *bcmpb.GetBlockRequest { + if m == nil { + return nil + } + pbMessage := &bcmpb.GetBlockRequest{} + { + pbMessage.RequestId = m.RequestId + pbMessage.SourceModule = (string)(m.SourceModule) + pbMessage.BlockId = m.BlockId + } + + return pbMessage +} + +func (*GetBlockRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetBlockRequest]()} +} + +type GetBlockResponse struct { + RequestId uint64 + Found bool + Block *blockchainpb.Block +} + +func GetBlockResponseFromPb(pb *bcmpb.GetBlockResponse) *GetBlockResponse { + if pb == nil { + return nil + } + return &GetBlockResponse{ + RequestId: pb.RequestId, + Found: pb.Found, + Block: pb.Block, + } +} + +func (m *GetBlockResponse) Pb() *bcmpb.GetBlockResponse { + if m == nil { + return nil + } + pbMessage := &bcmpb.GetBlockResponse{} + { + pbMessage.RequestId = m.RequestId + pbMessage.Found = m.Found + if m.Block != nil { + pbMessage.Block = m.Block + } + } + + return pbMessage +} + +func (*GetBlockResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetBlockResponse]()} +} diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go new file mode 100644 index 000000000..b9d8518cf --- /dev/null +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go @@ -0,0 +1,15 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package synchronizerpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/events" + types "github.com/filecoin-project/mir/pkg/types" +) + +// Module-specific dsl functions for emitting events. + +func SyncRequest(m dsl.Module, destModule types.ModuleID, orphanBlockId uint64, leaveIds uint64) { + dsl.EmitMirEvent(m, events.SyncRequest(destModule, orphanBlockId, leaveIds)) +} diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go new file mode 100644 index 000000000..cab93e40d --- /dev/null +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go @@ -0,0 +1,37 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package synchronizerpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" + dsl1 "github.com/filecoin-project/mir/pkg/pb/messagepb/dsl" + types2 "github.com/filecoin-project/mir/pkg/pb/messagepb/types" + types1 "github.com/filecoin-project/mir/pkg/types" +) + +// Module-specific dsl functions for processing net messages. + +func UponMessageReceived[W types.Message_TypeWrapper[M], M any](m dsl.Module, handler func(from types1.NodeID, msg *M) error) { + dsl1.UponMessageReceived[*types2.Message_Synchronizer](m, func(from types1.NodeID, msg *types.Message) error { + w, ok := msg.Type.(W) + if !ok { + return nil + } + + return handler(from, w.Unwrap()) + }) +} + +func UponBlockRequestReceived(m dsl.Module, handler func(from types1.NodeID, requestId uint64, blockId uint64) error) { + UponMessageReceived[*types.Message_BlockRequest](m, func(from types1.NodeID, msg *types.BlockRequest) error { + return handler(from, msg.RequestId, msg.BlockId) + }) +} + +func UponBlockResponseReceived(m dsl.Module, handler func(from types1.NodeID, requestId uint64, found bool, block *blockchainpb.Block) error) { + UponMessageReceived[*types.Message_BlockResponse](m, func(from types1.NodeID, msg *types.BlockResponse) error { + return handler(from, msg.RequestId, msg.Found, msg.Block) + }) +} diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go new file mode 100644 index 000000000..8d4827010 --- /dev/null +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go @@ -0,0 +1,28 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package synchronizerpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" +) + +// Module-specific dsl functions for processing events. + +func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func(ev *Ev) error) { + dsl.UponMirEvent[*types1.Event_Synchronizer](m, func(ev *types.Event) error { + w, ok := ev.Type.(W) + if !ok { + return nil + } + + return handler(w.Unwrap()) + }) +} + +func UponSyncRequest(m dsl.Module, handler func(orphanBlockId uint64, leaveIds uint64) error) { + UponEvent[*types.Event_SyncRequest](m, func(ev *types.SyncRequest) error { + return handler(ev.OrphanBlockId, ev.LeaveIds) + }) +} diff --git a/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go b/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go new file mode 100644 index 000000000..9f631f8c1 --- /dev/null +++ b/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go @@ -0,0 +1,25 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package synchronizerpbevents + +import ( + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types "github.com/filecoin-project/mir/pkg/types" +) + +func SyncRequest(destModule types.ModuleID, orphanBlockId uint64, leaveIds uint64) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Synchronizer{ + Synchronizer: &types2.Event{ + Type: &types2.Event_SyncRequest{ + SyncRequest: &types2.SyncRequest{ + OrphanBlockId: orphanBlockId, + LeaveIds: leaveIds, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go b/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go new file mode 100644 index 000000000..d48c1f861 --- /dev/null +++ b/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go @@ -0,0 +1,43 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package synchronizerpbmsgs + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/messagepb/types" + types "github.com/filecoin-project/mir/pkg/types" +) + +func BlockRequest(destModule types.ModuleID, requestId uint64, blockId uint64) *types1.Message { + return &types1.Message{ + DestModule: destModule, + Type: &types1.Message_Synchronizer{ + Synchronizer: &types2.Message{ + Type: &types2.Message_BlockRequest{ + BlockRequest: &types2.BlockRequest{ + RequestId: requestId, + BlockId: blockId, + }, + }, + }, + }, + } +} + +func BlockResponse(destModule types.ModuleID, requestId uint64, found bool, block *blockchainpb.Block) *types1.Message { + return &types1.Message{ + DestModule: destModule, + Type: &types1.Message_Synchronizer{ + Synchronizer: &types2.Message{ + Type: &types2.Message_BlockResponse{ + BlockResponse: &types2.BlockResponse{ + RequestId: requestId, + Found: found, + Block: block, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/synchronizerpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/synchronizerpb/oneof_interfaces.mir.go new file mode 100644 index 000000000..08b914b89 --- /dev/null +++ b/pkg/pb/blockchainpb/synchronizerpb/oneof_interfaces.mir.go @@ -0,0 +1,29 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package synchronizerpb + +type Event_Type = isEvent_Type + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func (w *Event_SyncRequest) Unwrap() *SyncRequest { + return w.SyncRequest +} + +type Message_Type = isMessage_Type + +type Message_TypeWrapper[T any] interface { + Message_Type + Unwrap() *T +} + +func (w *Message_BlockRequest) Unwrap() *BlockRequest { + return w.BlockRequest +} + +func (w *Message_BlockResponse) Unwrap() *BlockResponse { + return w.BlockResponse +} diff --git a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go new file mode 100644 index 000000000..49d55116b --- /dev/null +++ b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go @@ -0,0 +1,527 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.4 +// source: blockchainpb/synchronizerpb/synchronizerpb.proto + +package synchronizerpb + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + _ "github.com/filecoin-project/mir/pkg/pb/mir" + _ "github.com/filecoin-project/mir/pkg/pb/net" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *Event_SyncRequest + Type isEvent_Type `protobuf_oneof:"type"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[0] + 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 Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescGZIP(), []int{0} +} + +func (m *Event) GetType() isEvent_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *Event) GetSyncRequest() *SyncRequest { + if x, ok := x.GetType().(*Event_SyncRequest); ok { + return x.SyncRequest + } + return nil +} + +type isEvent_Type interface { + isEvent_Type() +} + +type Event_SyncRequest struct { + SyncRequest *SyncRequest `protobuf:"bytes,1,opt,name=sync_request,json=syncRequest,proto3,oneof"` +} + +func (*Event_SyncRequest) isEvent_Type() {} + +type SyncRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + OrphanBlockId uint64 `protobuf:"varint,1,opt,name=orphan_block_id,json=orphanBlockId,proto3" json:"orphan_block_id,omitempty"` + LeaveIds uint64 `protobuf:"varint,2,opt,name=leave_ids,json=leaveIds,proto3" json:"leave_ids,omitempty"` +} + +func (x *SyncRequest) Reset() { + *x = SyncRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *SyncRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SyncRequest) ProtoMessage() {} + +func (x *SyncRequest) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[1] + 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 SyncRequest.ProtoReflect.Descriptor instead. +func (*SyncRequest) Descriptor() ([]byte, []int) { + return file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescGZIP(), []int{1} +} + +func (x *SyncRequest) GetOrphanBlockId() uint64 { + if x != nil { + return x.OrphanBlockId + } + return 0 +} + +func (x *SyncRequest) GetLeaveIds() uint64 { + if x != nil { + return x.LeaveIds + } + return 0 +} + +type Message struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *Message_BlockRequest + // *Message_BlockResponse + Type isMessage_Type `protobuf_oneof:"type"` +} + +func (x *Message) Reset() { + *x = Message{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Message) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Message) ProtoMessage() {} + +func (x *Message) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[2] + 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 Message.ProtoReflect.Descriptor instead. +func (*Message) Descriptor() ([]byte, []int) { + return file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescGZIP(), []int{2} +} + +func (m *Message) GetType() isMessage_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *Message) GetBlockRequest() *BlockRequest { + if x, ok := x.GetType().(*Message_BlockRequest); ok { + return x.BlockRequest + } + return nil +} + +func (x *Message) GetBlockResponse() *BlockResponse { + if x, ok := x.GetType().(*Message_BlockResponse); ok { + return x.BlockResponse + } + return nil +} + +type isMessage_Type interface { + isMessage_Type() +} + +type Message_BlockRequest struct { + BlockRequest *BlockRequest `protobuf:"bytes,1,opt,name=block_request,json=blockRequest,proto3,oneof"` +} + +type Message_BlockResponse struct { + BlockResponse *BlockResponse `protobuf:"bytes,2,opt,name=block_response,json=blockResponse,proto3,oneof"` +} + +func (*Message_BlockRequest) isMessage_Type() {} + +func (*Message_BlockResponse) isMessage_Type() {} + +type BlockRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + BlockId uint64 `protobuf:"varint,2,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` +} + +func (x *BlockRequest) Reset() { + *x = BlockRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockRequest) ProtoMessage() {} + +func (x *BlockRequest) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[3] + 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 BlockRequest.ProtoReflect.Descriptor instead. +func (*BlockRequest) Descriptor() ([]byte, []int) { + return file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescGZIP(), []int{3} +} + +func (x *BlockRequest) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *BlockRequest) GetBlockId() uint64 { + if x != nil { + return x.BlockId + } + return 0 +} + +type BlockResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Found bool `protobuf:"varint,2,opt,name=found,proto3" json:"found,omitempty"` + Block *blockchainpb.Block `protobuf:"bytes,3,opt,name=block,proto3" json:"block,omitempty"` // possibly undefined (proto3 spec no-longer supports optional/required...) +} + +func (x *BlockResponse) Reset() { + *x = BlockResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockResponse) ProtoMessage() {} + +func (x *BlockResponse) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_synchronizerpb_synchronizerpb_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 BlockResponse.ProtoReflect.Descriptor instead. +func (*BlockResponse) Descriptor() ([]byte, []int) { + return file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescGZIP(), []int{4} +} + +func (x *BlockResponse) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *BlockResponse) GetFound() bool { + if x != nil { + return x.Found + } + return false +} + +func (x *BlockResponse) GetBlock() *blockchainpb.Block { + if x != nil { + return x.Block + } + return nil +} + +var File_blockchainpb_synchronizerpb_synchronizerpb_proto protoreflect.FileDescriptor + +var file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDesc = []byte{ + 0x0a, 0x30, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, + 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x73, 0x79, + 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x12, 0x0e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, + 0x70, 0x62, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, + 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x1a, 0x1c, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, + 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, + 0x5d, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0c, 0x73, 0x79, 0x6e, 0x63, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x73, + 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, + 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x58, + 0x0a, 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, + 0x0f, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x49, + 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xaa, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x79, + 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, + 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x48, 0x00, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x3a, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x22, 0x4e, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x3a, + 0x04, 0xd0, 0xe4, 0x1d, 0x01, 0x22, 0x75, 0x0a, 0x0d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0xd0, 0xe4, 0x1d, 0x01, 0x42, 0x44, 0x5a, 0x42, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, + 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescOnce sync.Once + file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescData = file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDesc +) + +func file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescGZIP() []byte { + file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescOnce.Do(func() { + file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescData) + }) + return file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescData +} + +var file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_blockchainpb_synchronizerpb_synchronizerpb_proto_goTypes = []interface{}{ + (*Event)(nil), // 0: synchronizerpb.Event + (*SyncRequest)(nil), // 1: synchronizerpb.SyncRequest + (*Message)(nil), // 2: synchronizerpb.Message + (*BlockRequest)(nil), // 3: synchronizerpb.BlockRequest + (*BlockResponse)(nil), // 4: synchronizerpb.BlockResponse + (*blockchainpb.Block)(nil), // 5: blockchainpb.Block +} +var file_blockchainpb_synchronizerpb_synchronizerpb_proto_depIdxs = []int32{ + 1, // 0: synchronizerpb.Event.sync_request:type_name -> synchronizerpb.SyncRequest + 3, // 1: synchronizerpb.Message.block_request:type_name -> synchronizerpb.BlockRequest + 4, // 2: synchronizerpb.Message.block_response:type_name -> synchronizerpb.BlockResponse + 5, // 3: synchronizerpb.BlockResponse.block:type_name -> blockchainpb.Block + 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_blockchainpb_synchronizerpb_synchronizerpb_proto_init() } +func file_blockchainpb_synchronizerpb_synchronizerpb_proto_init() { + if File_blockchainpb_synchronizerpb_synchronizerpb_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*SyncRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Message); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Event_SyncRequest)(nil), + } + file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[2].OneofWrappers = []interface{}{ + (*Message_BlockRequest)(nil), + (*Message_BlockResponse)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_blockchainpb_synchronizerpb_synchronizerpb_proto_goTypes, + DependencyIndexes: file_blockchainpb_synchronizerpb_synchronizerpb_proto_depIdxs, + MessageInfos: file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes, + }.Build() + File_blockchainpb_synchronizerpb_synchronizerpb_proto = out.File + file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDesc = nil + file_blockchainpb_synchronizerpb_synchronizerpb_proto_goTypes = nil + file_blockchainpb_synchronizerpb_synchronizerpb_proto_depIdxs = nil +} diff --git a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.mir.go b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.mir.go new file mode 100644 index 000000000..c60ed12f3 --- /dev/null +++ b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.mir.go @@ -0,0 +1,20 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package synchronizerpb + +import ( + reflect "reflect" +) + +func (*Event) ReflectTypeOptions() []reflect.Type { + return []reflect.Type{ + reflect.TypeOf((*Event_SyncRequest)(nil)), + } +} + +func (*Message) ReflectTypeOptions() []reflect.Type { + return []reflect.Type{ + reflect.TypeOf((*Message_BlockRequest)(nil)), + reflect.TypeOf((*Message_BlockResponse)(nil)), + } +} diff --git a/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go new file mode 100644 index 000000000..85181c9a8 --- /dev/null +++ b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go @@ -0,0 +1,291 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package synchronizerpbtypes + +import ( + mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" + reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" +) + +type Event struct { + Type Event_Type +} + +type Event_Type interface { + mirreflect.GeneratedType + isEvent_Type() + Pb() synchronizerpb.Event_Type +} + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func Event_TypeFromPb(pb synchronizerpb.Event_Type) Event_Type { + if pb == nil { + return nil + } + switch pb := pb.(type) { + case *synchronizerpb.Event_SyncRequest: + return &Event_SyncRequest{SyncRequest: SyncRequestFromPb(pb.SyncRequest)} + } + return nil +} + +type Event_SyncRequest struct { + SyncRequest *SyncRequest +} + +func (*Event_SyncRequest) isEvent_Type() {} + +func (w *Event_SyncRequest) Unwrap() *SyncRequest { + return w.SyncRequest +} + +func (w *Event_SyncRequest) Pb() synchronizerpb.Event_Type { + if w == nil { + return nil + } + if w.SyncRequest == nil { + return &synchronizerpb.Event_SyncRequest{} + } + return &synchronizerpb.Event_SyncRequest{SyncRequest: (w.SyncRequest).Pb()} +} + +func (*Event_SyncRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.Event_SyncRequest]()} +} + +func EventFromPb(pb *synchronizerpb.Event) *Event { + if pb == nil { + return nil + } + return &Event{ + Type: Event_TypeFromPb(pb.Type), + } +} + +func (m *Event) Pb() *synchronizerpb.Event { + if m == nil { + return nil + } + pbMessage := &synchronizerpb.Event{} + { + if m.Type != nil { + pbMessage.Type = (m.Type).Pb() + } + } + + return pbMessage +} + +func (*Event) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.Event]()} +} + +type SyncRequest struct { + OrphanBlockId uint64 + LeaveIds uint64 +} + +func SyncRequestFromPb(pb *synchronizerpb.SyncRequest) *SyncRequest { + if pb == nil { + return nil + } + return &SyncRequest{ + OrphanBlockId: pb.OrphanBlockId, + LeaveIds: pb.LeaveIds, + } +} + +func (m *SyncRequest) Pb() *synchronizerpb.SyncRequest { + if m == nil { + return nil + } + pbMessage := &synchronizerpb.SyncRequest{} + { + pbMessage.OrphanBlockId = m.OrphanBlockId + pbMessage.LeaveIds = m.LeaveIds + } + + return pbMessage +} + +func (*SyncRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.SyncRequest]()} +} + +type Message struct { + Type Message_Type +} + +type Message_Type interface { + mirreflect.GeneratedType + isMessage_Type() + Pb() synchronizerpb.Message_Type +} + +type Message_TypeWrapper[T any] interface { + Message_Type + Unwrap() *T +} + +func Message_TypeFromPb(pb synchronizerpb.Message_Type) Message_Type { + if pb == nil { + return nil + } + switch pb := pb.(type) { + case *synchronizerpb.Message_BlockRequest: + return &Message_BlockRequest{BlockRequest: BlockRequestFromPb(pb.BlockRequest)} + case *synchronizerpb.Message_BlockResponse: + return &Message_BlockResponse{BlockResponse: BlockResponseFromPb(pb.BlockResponse)} + } + return nil +} + +type Message_BlockRequest struct { + BlockRequest *BlockRequest +} + +func (*Message_BlockRequest) isMessage_Type() {} + +func (w *Message_BlockRequest) Unwrap() *BlockRequest { + return w.BlockRequest +} + +func (w *Message_BlockRequest) Pb() synchronizerpb.Message_Type { + if w == nil { + return nil + } + if w.BlockRequest == nil { + return &synchronizerpb.Message_BlockRequest{} + } + return &synchronizerpb.Message_BlockRequest{BlockRequest: (w.BlockRequest).Pb()} +} + +func (*Message_BlockRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.Message_BlockRequest]()} +} + +type Message_BlockResponse struct { + BlockResponse *BlockResponse +} + +func (*Message_BlockResponse) isMessage_Type() {} + +func (w *Message_BlockResponse) Unwrap() *BlockResponse { + return w.BlockResponse +} + +func (w *Message_BlockResponse) Pb() synchronizerpb.Message_Type { + if w == nil { + return nil + } + if w.BlockResponse == nil { + return &synchronizerpb.Message_BlockResponse{} + } + return &synchronizerpb.Message_BlockResponse{BlockResponse: (w.BlockResponse).Pb()} +} + +func (*Message_BlockResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.Message_BlockResponse]()} +} + +func MessageFromPb(pb *synchronizerpb.Message) *Message { + if pb == nil { + return nil + } + return &Message{ + Type: Message_TypeFromPb(pb.Type), + } +} + +func (m *Message) Pb() *synchronizerpb.Message { + if m == nil { + return nil + } + pbMessage := &synchronizerpb.Message{} + { + if m.Type != nil { + pbMessage.Type = (m.Type).Pb() + } + } + + return pbMessage +} + +func (*Message) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.Message]()} +} + +type BlockRequest struct { + RequestId uint64 + BlockId uint64 +} + +func BlockRequestFromPb(pb *synchronizerpb.BlockRequest) *BlockRequest { + if pb == nil { + return nil + } + return &BlockRequest{ + RequestId: pb.RequestId, + BlockId: pb.BlockId, + } +} + +func (m *BlockRequest) Pb() *synchronizerpb.BlockRequest { + if m == nil { + return nil + } + pbMessage := &synchronizerpb.BlockRequest{} + { + pbMessage.RequestId = m.RequestId + pbMessage.BlockId = m.BlockId + } + + return pbMessage +} + +func (*BlockRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.BlockRequest]()} +} + +type BlockResponse struct { + RequestId uint64 + Found bool + Block *blockchainpb.Block +} + +func BlockResponseFromPb(pb *synchronizerpb.BlockResponse) *BlockResponse { + if pb == nil { + return nil + } + return &BlockResponse{ + RequestId: pb.RequestId, + Found: pb.Found, + Block: pb.Block, + } +} + +func (m *BlockResponse) Pb() *synchronizerpb.BlockResponse { + if m == nil { + return nil + } + pbMessage := &synchronizerpb.BlockResponse{} + { + pbMessage.RequestId = m.RequestId + pbMessage.Found = m.Found + if m.Block != nil { + pbMessage.Block = m.Block + } + } + + return pbMessage +} + +func (*BlockResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.BlockResponse]()} +} diff --git a/pkg/pb/eventpb/eventpb.pb.go b/pkg/pb/eventpb/eventpb.pb.go index 8565ecfed..a578ac5f5 100644 --- a/pkg/pb/eventpb/eventpb.pb.go +++ b/pkg/pb/eventpb/eventpb.pb.go @@ -15,6 +15,7 @@ import ( bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" + synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" chkpvalidatorpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb/chkpvalidatorpb" @@ -76,6 +77,7 @@ type Event struct { // *Event_Miner // *Event_Tpm // *Event_Communication + // *Event_Synchronizer // *Event_TestingString // *Event_TestingUint // *Event_Tester @@ -297,6 +299,13 @@ func (x *Event) GetCommunication() *communicationpb.Event { return nil } +func (x *Event) GetSynchronizer() *synchronizerpb.Event { + if x, ok := x.GetType().(*Event_Synchronizer); ok { + return x.Synchronizer + } + return nil +} + func (x *Event) GetTestingString() *wrapperspb.StringValue { if x, ok := x.GetType().(*Event_TestingString); ok { return x.TestingString @@ -426,6 +435,10 @@ type Event_Communication struct { Communication *communicationpb.Event `protobuf:"bytes,204,opt,name=communication,proto3,oneof"` } +type Event_Synchronizer struct { + Synchronizer *synchronizerpb.Event `protobuf:"bytes,205,opt,name=synchronizer,proto3,oneof"` +} + type Event_TestingString struct { // for unit-tests TestingString *wrapperspb.StringValue `protobuf:"bytes,301,opt,name=testingString,proto3,oneof"` @@ -485,6 +498,8 @@ func (*Event_Tpm) isEvent_Type() {} func (*Event_Communication) isEvent_Type() {} +func (*Event_Synchronizer) isEvent_Type() {} + func (*Event_TestingString) isEvent_Type() {} func (*Event_TestingUint) isEvent_Type() {} @@ -843,84 +858,91 @@ var file_eventpb_eventpb_proto_rawDesc = []byte{ 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x32, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, - 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, - 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa9, 0x0b, 0x0a, 0x05, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, - 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, - 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, - 0x0a, 0x64, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x69, - 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, - 0x12, 0x2b, 0x0a, 0x05, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x05, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x12, 0x29, 0x0a, - 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, - 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, - 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x03, 0x62, 0x63, 0x62, 0x18, - 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x63, 0x62, 0x70, 0x62, 0x2e, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, 0x62, 0x12, 0x2c, 0x0a, 0x07, 0x6d, 0x65, - 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, - 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, - 0x07, 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x3b, 0x0a, 0x0c, 0x61, 0x76, 0x61, 0x69, - 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x70, 0x62, 0x2e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, - 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x08, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x64, - 0x62, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x64, - 0x62, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x62, 0x61, 0x74, - 0x63, 0x68, 0x44, 0x62, 0x12, 0x3c, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x66, 0x65, - 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x61, - 0x74, 0x63, 0x68, 0x66, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x46, 0x65, 0x74, 0x63, 0x68, - 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x0d, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x63, 0x72, 0x79, - 0x70, 0x74, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x68, 0x72, 0x65, - 0x73, 0x68, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x48, 0x00, 0x52, 0x0c, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x12, 0x35, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x11, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x65, - 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x07, 0x66, 0x61, 0x63, 0x74, 0x6f, - 0x72, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x66, 0x61, 0x63, 0x74, 0x6f, - 0x72, 0x79, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x66, 0x61, - 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x03, 0x69, 0x73, 0x73, 0x18, 0x13, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x69, 0x73, 0x73, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x48, 0x00, 0x52, 0x03, 0x69, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6f, 0x72, 0x64, 0x65, 0x72, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, - 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x62, - 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x12, 0x20, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, - 0x61, 0x70, 0x70, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x61, - 0x70, 0x70, 0x12, 0x32, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, - 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, - 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x74, 0x72, 0x61, - 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x68, 0x6b, 0x70, 0x5f, 0x76, - 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x63, 0x68, 0x6b, 0x70, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x70, 0x62, - 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x68, 0x6b, 0x70, 0x56, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x42, 0x0a, 0x0f, 0x70, 0x70, 0x72, 0x65, 0x70, - 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x61, 0x64, 0x74, 0x6f, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x70, 0x70, 0x72, 0x65, 0x70, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, - 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x70, 0x72, - 0x65, 0x70, 0x56, 0x61, 0x6c, 0x69, 0x61, 0x64, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x09, 0x70, - 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x6e, 0x67, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x11, 0x2e, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x48, 0x00, 0x52, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x12, 0x21, - 0x0a, 0x03, 0x62, 0x63, 0x6d, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, - 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, - 0x6d, 0x12, 0x27, 0x0a, 0x05, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0e, 0x2e, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x48, 0x00, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x03, 0x74, 0x70, - 0x6d, 0x18, 0xcb, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x70, 0x6d, 0x70, 0x62, - 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x74, 0x70, 0x6d, 0x12, 0x3f, 0x0a, - 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xcc, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, - 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x45, + 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, + 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, + 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, + 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe7, 0x0b, 0x0a, 0x05, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, + 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, + 0x44, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a, + 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, + 0x69, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x05, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x12, + 0x29, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x48, 0x00, 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x03, 0x62, 0x63, + 0x62, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x63, 0x62, 0x70, 0x62, 0x2e, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, 0x62, 0x12, 0x2c, 0x0a, 0x07, + 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, + 0x00, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x3b, 0x0a, 0x0c, 0x61, 0x76, + 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x70, + 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x08, 0x62, 0x61, 0x74, 0x63, 0x68, + 0x5f, 0x64, 0x62, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, 0x61, 0x74, 0x63, + 0x68, 0x64, 0x62, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x62, + 0x61, 0x74, 0x63, 0x68, 0x44, 0x62, 0x12, 0x3c, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, + 0x66, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x62, 0x61, 0x74, 0x63, 0x68, 0x66, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x46, 0x65, 0x74, + 0x63, 0x68, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x0d, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x63, + 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x68, + 0x72, 0x65, 0x73, 0x68, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x43, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x12, 0x35, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x07, 0x66, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x66, 0x61, 0x63, + 0x74, 0x6f, 0x72, 0x79, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, + 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x03, 0x69, 0x73, 0x73, 0x18, 0x13, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x69, 0x73, 0x73, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x69, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, + 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x12, 0x20, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x61, 0x70, 0x70, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, + 0x03, 0x61, 0x70, 0x70, 0x12, 0x32, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, + 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, + 0x6f, 0x72, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x74, + 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x68, 0x6b, 0x70, + 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x16, 0x2e, 0x63, 0x68, 0x6b, 0x70, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, + 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x68, 0x6b, 0x70, + 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x42, 0x0a, 0x0f, 0x70, 0x70, 0x72, + 0x65, 0x70, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x61, 0x64, 0x74, 0x6f, 0x72, 0x18, 0x19, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x70, 0x72, 0x65, 0x70, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, + 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, + 0x70, 0x72, 0x65, 0x70, 0x56, 0x61, 0x6c, 0x69, 0x61, 0x64, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, + 0x09, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x6e, 0x67, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, + 0x12, 0x21, 0x0a, 0x03, 0x62, 0x63, 0x6d, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, + 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, + 0x62, 0x63, 0x6d, 0x12, 0x27, 0x0a, 0x05, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x18, 0xca, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x03, + 0x74, 0x70, 0x6d, 0x18, 0xcb, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x70, 0x6d, + 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x74, 0x70, 0x6d, 0x12, + 0x3f, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x18, 0xcc, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, + 0x00, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x12, 0x3c, 0x0a, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, + 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, + 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, + 0x52, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x12, 0x45, 0x0a, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0xad, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, @@ -1033,9 +1055,10 @@ var file_eventpb_eventpb_proto_goTypes = []interface{}{ (*minerpb.Event)(nil), // 24: minerpb.Event (*tpmpb.Event)(nil), // 25: tpmpb.Event (*communicationpb.Event)(nil), // 26: communicationpb.Event - (*wrapperspb.StringValue)(nil), // 27: google.protobuf.StringValue - (*wrapperspb.UInt64Value)(nil), // 28: google.protobuf.UInt64Value - (*testerpb.Tester)(nil), // 29: testerpb.Tester + (*synchronizerpb.Event)(nil), // 27: synchronizerpb.Event + (*wrapperspb.StringValue)(nil), // 28: google.protobuf.StringValue + (*wrapperspb.UInt64Value)(nil), // 29: google.protobuf.UInt64Value + (*testerpb.Tester)(nil), // 30: testerpb.Tester } var file_eventpb_eventpb_proto_depIdxs = []int32{ 1, // 0: eventpb.Event.init:type_name -> eventpb.Init @@ -1061,20 +1084,21 @@ var file_eventpb_eventpb_proto_depIdxs = []int32{ 24, // 20: eventpb.Event.miner:type_name -> minerpb.Event 25, // 21: eventpb.Event.tpm:type_name -> tpmpb.Event 26, // 22: eventpb.Event.communication:type_name -> communicationpb.Event - 27, // 23: eventpb.Event.testingString:type_name -> google.protobuf.StringValue - 28, // 24: eventpb.Event.testingUint:type_name -> google.protobuf.UInt64Value - 29, // 25: eventpb.Event.tester:type_name -> testerpb.Tester - 0, // 26: eventpb.Event.next:type_name -> eventpb.Event - 3, // 27: eventpb.TimerEvent.delay:type_name -> eventpb.TimerDelay - 4, // 28: eventpb.TimerEvent.repeat:type_name -> eventpb.TimerRepeat - 5, // 29: eventpb.TimerEvent.garbage_collect:type_name -> eventpb.TimerGarbageCollect - 0, // 30: eventpb.TimerDelay.events_to_delay:type_name -> eventpb.Event - 0, // 31: eventpb.TimerRepeat.events_to_repeat:type_name -> eventpb.Event - 32, // [32:32] is the sub-list for method output_type - 32, // [32:32] is the sub-list for method input_type - 32, // [32:32] is the sub-list for extension type_name - 32, // [32:32] is the sub-list for extension extendee - 0, // [0:32] is the sub-list for field type_name + 27, // 23: eventpb.Event.synchronizer:type_name -> synchronizerpb.Event + 28, // 24: eventpb.Event.testingString:type_name -> google.protobuf.StringValue + 29, // 25: eventpb.Event.testingUint:type_name -> google.protobuf.UInt64Value + 30, // 26: eventpb.Event.tester:type_name -> testerpb.Tester + 0, // 27: eventpb.Event.next:type_name -> eventpb.Event + 3, // 28: eventpb.TimerEvent.delay:type_name -> eventpb.TimerDelay + 4, // 29: eventpb.TimerEvent.repeat:type_name -> eventpb.TimerRepeat + 5, // 30: eventpb.TimerEvent.garbage_collect:type_name -> eventpb.TimerGarbageCollect + 0, // 31: eventpb.TimerDelay.events_to_delay:type_name -> eventpb.Event + 0, // 32: eventpb.TimerRepeat.events_to_repeat:type_name -> eventpb.Event + 33, // [33:33] is the sub-list for method output_type + 33, // [33:33] is the sub-list for method input_type + 33, // [33:33] is the sub-list for extension type_name + 33, // [33:33] is the sub-list for extension extendee + 0, // [0:33] is the sub-list for field type_name } func init() { file_eventpb_eventpb_proto_init() } @@ -1180,6 +1204,7 @@ func file_eventpb_eventpb_proto_init() { (*Event_Miner)(nil), (*Event_Tpm)(nil), (*Event_Communication)(nil), + (*Event_Synchronizer)(nil), (*Event_TestingString)(nil), (*Event_TestingUint)(nil), (*Event_Tester)(nil), diff --git a/pkg/pb/eventpb/eventpb.pb.mir.go b/pkg/pb/eventpb/eventpb.pb.mir.go index 1cef9b204..9fd0cb0b4 100644 --- a/pkg/pb/eventpb/eventpb.pb.mir.go +++ b/pkg/pb/eventpb/eventpb.pb.mir.go @@ -31,6 +31,7 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_Miner)(nil)), reflect.TypeOf((*Event_Tpm)(nil)), reflect.TypeOf((*Event_Communication)(nil)), + reflect.TypeOf((*Event_Synchronizer)(nil)), reflect.TypeOf((*Event_TestingString)(nil)), reflect.TypeOf((*Event_TestingUint)(nil)), reflect.TypeOf((*Event_Tester)(nil)), diff --git a/pkg/pb/eventpb/oneof_interfaces.mir.go b/pkg/pb/eventpb/oneof_interfaces.mir.go index 1534c97fd..eeac8121e 100644 --- a/pkg/pb/eventpb/oneof_interfaces.mir.go +++ b/pkg/pb/eventpb/oneof_interfaces.mir.go @@ -11,6 +11,7 @@ import ( bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" + synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" chkpvalidatorpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb/chkpvalidatorpb" @@ -127,6 +128,10 @@ func (w *Event_Communication) Unwrap() *communicationpb.Event { return w.Communication } +func (w *Event_Synchronizer) Unwrap() *synchronizerpb.Event { + return w.Synchronizer +} + func (w *Event_TestingString) Unwrap() *wrapperspb.StringValue { return w.TestingString } diff --git a/pkg/pb/eventpb/types/types.mir.go b/pkg/pb/eventpb/types/types.mir.go index 98cc5467a..08344448b 100644 --- a/pkg/pb/eventpb/types/types.mir.go +++ b/pkg/pb/eventpb/types/types.mir.go @@ -4,7 +4,7 @@ package eventpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - types23 "github.com/filecoin-project/mir/codegen/model/types" + types24 "github.com/filecoin-project/mir/codegen/model/types" types13 "github.com/filecoin-project/mir/pkg/pb/apppb/types" types5 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/batchdbpb/types" types4 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/types" @@ -13,6 +13,7 @@ import ( types18 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" types21 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" types19 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" + types22 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" types20 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/types" types15 "github.com/filecoin-project/mir/pkg/pb/checkpointpb/chkpvalidatorpb/types" types8 "github.com/filecoin-project/mir/pkg/pb/checkpointpb/types" @@ -25,11 +26,11 @@ import ( types16 "github.com/filecoin-project/mir/pkg/pb/ordererpb/pprepvalidatorpb/types" types11 "github.com/filecoin-project/mir/pkg/pb/ordererpb/types" types17 "github.com/filecoin-project/mir/pkg/pb/pingpongpb/types" - types22 "github.com/filecoin-project/mir/pkg/pb/testerpb/types" + types23 "github.com/filecoin-project/mir/pkg/pb/testerpb/types" types7 "github.com/filecoin-project/mir/pkg/pb/threshcryptopb/types" types14 "github.com/filecoin-project/mir/pkg/pb/transportpb/types" - types24 "github.com/filecoin-project/mir/pkg/timer/types" - types25 "github.com/filecoin-project/mir/pkg/trantor/types" + types25 "github.com/filecoin-project/mir/pkg/timer/types" + types26 "github.com/filecoin-project/mir/pkg/trantor/types" types "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" @@ -103,12 +104,14 @@ func Event_TypeFromPb(pb eventpb.Event_Type) Event_Type { return &Event_Tpm{Tpm: types20.EventFromPb(pb.Tpm)} case *eventpb.Event_Communication: return &Event_Communication{Communication: types21.EventFromPb(pb.Communication)} + case *eventpb.Event_Synchronizer: + return &Event_Synchronizer{Synchronizer: types22.EventFromPb(pb.Synchronizer)} case *eventpb.Event_TestingString: return &Event_TestingString{TestingString: pb.TestingString} case *eventpb.Event_TestingUint: return &Event_TestingUint{TestingUint: pb.TestingUint} case *eventpb.Event_Tester: - return &Event_Tester{Tester: types22.TesterFromPb(pb.Tester)} + return &Event_Tester{Tester: types23.TesterFromPb(pb.Tester)} } return nil } @@ -665,6 +668,30 @@ func (*Event_Communication) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Communication]()} } +type Event_Synchronizer struct { + Synchronizer *types22.Event +} + +func (*Event_Synchronizer) isEvent_Type() {} + +func (w *Event_Synchronizer) Unwrap() *types22.Event { + return w.Synchronizer +} + +func (w *Event_Synchronizer) Pb() eventpb.Event_Type { + if w == nil { + return nil + } + if w.Synchronizer == nil { + return &eventpb.Event_Synchronizer{} + } + return &eventpb.Event_Synchronizer{Synchronizer: (w.Synchronizer).Pb()} +} + +func (*Event_Synchronizer) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Synchronizer]()} +} + type Event_TestingString struct { TestingString *wrapperspb.StringValue } @@ -714,12 +741,12 @@ func (*Event_TestingUint) MirReflect() mirreflect.Type { } type Event_Tester struct { - Tester *types22.Tester + Tester *types23.Tester } func (*Event_Tester) isEvent_Type() {} -func (w *Event_Tester) Unwrap() *types22.Tester { +func (w *Event_Tester) Unwrap() *types23.Tester { return w.Tester } @@ -744,7 +771,7 @@ func EventFromPb(pb *eventpb.Event) *Event { return &Event{ DestModule: (types.ModuleID)(pb.DestModule), Type: Event_TypeFromPb(pb.Type), - Next: types23.ConvertSlice(pb.Next, func(t *eventpb.Event) *Event { + Next: types24.ConvertSlice(pb.Next, func(t *eventpb.Event) *Event { return EventFromPb(t) }), } @@ -760,7 +787,7 @@ func (m *Event) Pb() *eventpb.Event { if m.Type != nil { pbMessage.Type = (m.Type).Pb() } - pbMessage.Next = types23.ConvertSlice(m.Next, func(t *Event) *eventpb.Event { + pbMessage.Next = types24.ConvertSlice(m.Next, func(t *Event) *eventpb.Event { return (t).Pb() }) } @@ -927,7 +954,7 @@ func (*TimerEvent) MirReflect() mirreflect.Type { type TimerDelay struct { EventsToDelay []*Event - Delay types24.Duration + Delay types25.Duration } func TimerDelayFromPb(pb *eventpb.TimerDelay) *TimerDelay { @@ -935,10 +962,10 @@ func TimerDelayFromPb(pb *eventpb.TimerDelay) *TimerDelay { return nil } return &TimerDelay{ - EventsToDelay: types23.ConvertSlice(pb.EventsToDelay, func(t *eventpb.Event) *Event { + EventsToDelay: types24.ConvertSlice(pb.EventsToDelay, func(t *eventpb.Event) *Event { return EventFromPb(t) }), - Delay: (types24.Duration)(pb.Delay), + Delay: (types25.Duration)(pb.Delay), } } @@ -948,7 +975,7 @@ func (m *TimerDelay) Pb() *eventpb.TimerDelay { } pbMessage := &eventpb.TimerDelay{} { - pbMessage.EventsToDelay = types23.ConvertSlice(m.EventsToDelay, func(t *Event) *eventpb.Event { + pbMessage.EventsToDelay = types24.ConvertSlice(m.EventsToDelay, func(t *Event) *eventpb.Event { return (t).Pb() }) pbMessage.Delay = (uint64)(m.Delay) @@ -963,8 +990,8 @@ func (*TimerDelay) MirReflect() mirreflect.Type { type TimerRepeat struct { EventsToRepeat []*Event - Delay types24.Duration - RetentionIndex types25.RetentionIndex + Delay types25.Duration + RetentionIndex types26.RetentionIndex } func TimerRepeatFromPb(pb *eventpb.TimerRepeat) *TimerRepeat { @@ -972,11 +999,11 @@ func TimerRepeatFromPb(pb *eventpb.TimerRepeat) *TimerRepeat { return nil } return &TimerRepeat{ - EventsToRepeat: types23.ConvertSlice(pb.EventsToRepeat, func(t *eventpb.Event) *Event { + EventsToRepeat: types24.ConvertSlice(pb.EventsToRepeat, func(t *eventpb.Event) *Event { return EventFromPb(t) }), - Delay: (types24.Duration)(pb.Delay), - RetentionIndex: (types25.RetentionIndex)(pb.RetentionIndex), + Delay: (types25.Duration)(pb.Delay), + RetentionIndex: (types26.RetentionIndex)(pb.RetentionIndex), } } @@ -986,7 +1013,7 @@ func (m *TimerRepeat) Pb() *eventpb.TimerRepeat { } pbMessage := &eventpb.TimerRepeat{} { - pbMessage.EventsToRepeat = types23.ConvertSlice(m.EventsToRepeat, func(t *Event) *eventpb.Event { + pbMessage.EventsToRepeat = types24.ConvertSlice(m.EventsToRepeat, func(t *Event) *eventpb.Event { return (t).Pb() }) pbMessage.Delay = (uint64)(m.Delay) @@ -1001,7 +1028,7 @@ func (*TimerRepeat) MirReflect() mirreflect.Type { } type TimerGarbageCollect struct { - RetentionIndex types25.RetentionIndex + RetentionIndex types26.RetentionIndex } func TimerGarbageCollectFromPb(pb *eventpb.TimerGarbageCollect) *TimerGarbageCollect { @@ -1009,7 +1036,7 @@ func TimerGarbageCollectFromPb(pb *eventpb.TimerGarbageCollect) *TimerGarbageCol return nil } return &TimerGarbageCollect{ - RetentionIndex: (types25.RetentionIndex)(pb.RetentionIndex), + RetentionIndex: (types26.RetentionIndex)(pb.RetentionIndex), } } diff --git a/pkg/pb/messagepb/messagepb.pb.go b/pkg/pb/messagepb/messagepb.pb.go index be5564889..cef15e9a6 100644 --- a/pkg/pb/messagepb/messagepb.pb.go +++ b/pkg/pb/messagepb/messagepb.pb.go @@ -15,6 +15,7 @@ import ( mscpb "github.com/filecoin-project/mir/pkg/pb/availabilitypb/mscpb" bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" isspb "github.com/filecoin-project/mir/pkg/pb/isspb" _ "github.com/filecoin-project/mir/pkg/pb/mir" @@ -49,6 +50,7 @@ type Message struct { // *Message_Checkpoint // *Message_Orderer // *Message_Communicationpb + // *Message_Synchronizer Type isMessage_Type `protobuf_oneof:"type"` } @@ -147,6 +149,13 @@ func (x *Message) GetCommunicationpb() *communicationpb.Message { return nil } +func (x *Message) GetSynchronizer() *synchronizerpb.Message { + if x, ok := x.GetType().(*Message_Synchronizer); ok { + return x.Synchronizer + } + return nil +} + type isMessage_Type interface { isMessage_Type() } @@ -180,6 +189,10 @@ type Message_Communicationpb struct { Communicationpb *communicationpb.Message `protobuf:"bytes,8,opt,name=communicationpb,proto3,oneof"` } +type Message_Synchronizer struct { + Synchronizer *synchronizerpb.Message `protobuf:"bytes,9,opt,name=synchronizer,proto3,oneof"` +} + func (*Message_Iss) isMessage_Type() {} func (*Message_Bcb) isMessage_Type() {} @@ -194,6 +207,8 @@ func (*Message_Orderer) isMessage_Type() {} func (*Message_Communicationpb) isMessage_Type() {} +func (*Message_Synchronizer) isMessage_Type() {} + var File_messagepb_messagepb_proto protoreflect.FileDescriptor var file_messagepb_messagepb_proto_rawDesc = []byte{ @@ -212,46 +227,53 @@ var file_messagepb_messagepb_proto_rawDesc = []byte{ 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x32, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, + 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, + 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, + 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, + 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6e, 0x65, 0x74, 0x2f, 0x63, - 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, - 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf3, 0x03, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, - 0x61, 0x67, 0x65, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, - 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, - 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, - 0x52, 0x0a, 0x64, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x03, - 0x69, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69, 0x73, 0x73, 0x70, - 0x62, 0x2e, 0x49, 0x53, 0x53, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x03, - 0x69, 0x73, 0x73, 0x12, 0x22, 0x0a, 0x03, 0x62, 0x63, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x62, 0x63, 0x62, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, 0x62, 0x12, 0x4e, 0x0a, 0x12, 0x6d, 0x75, 0x6c, 0x74, 0x69, - 0x73, 0x69, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, - 0x74, 0x79, 0x70, 0x62, 0x2e, 0x6d, 0x73, 0x63, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x48, 0x00, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x73, 0x69, 0x67, 0x43, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x70, - 0x6f, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x69, 0x6e, 0x67, - 0x70, 0x6f, 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, - 0x52, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x12, 0x37, 0x0a, 0x0a, 0x63, 0x68, - 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x70, 0x62, - 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, - 0x72, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x3a, 0x04, 0xc0, 0xe4, 0x1d, 0x01, 0x42, - 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x32, 0x5a, - 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, - 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x70, - 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb2, 0x04, 0x0a, 0x07, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, + 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x49, 0x44, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x25, + 0x0a, 0x03, 0x69, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69, 0x73, + 0x73, 0x70, 0x62, 0x2e, 0x49, 0x53, 0x53, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, + 0x52, 0x03, 0x69, 0x73, 0x73, 0x12, 0x22, 0x0a, 0x03, 0x62, 0x63, 0x62, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x63, 0x62, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, 0x62, 0x12, 0x4e, 0x0a, 0x12, 0x6d, 0x75, 0x6c, + 0x74, 0x69, 0x73, 0x69, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, + 0x6c, 0x69, 0x74, 0x79, 0x70, 0x62, 0x2e, 0x6d, 0x73, 0x63, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, + 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x73, 0x69, 0x67, + 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x08, 0x70, 0x69, 0x6e, + 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x69, + 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x48, 0x00, 0x52, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x12, 0x37, 0x0a, 0x0a, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, + 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, + 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x12, 0x3d, 0x0a, 0x0c, 0x73, + 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, + 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x79, + 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x3a, 0x04, 0xc0, 0xe4, 0x1d, 0x01, + 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x32, + 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, + 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, + 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -276,6 +298,7 @@ var file_messagepb_messagepb_proto_goTypes = []interface{}{ (*checkpointpb.Message)(nil), // 5: checkpointpb.Message (*ordererpb.Message)(nil), // 6: ordererpb.Message (*communicationpb.Message)(nil), // 7: communicationpb.Message + (*synchronizerpb.Message)(nil), // 8: synchronizerpb.Message } var file_messagepb_messagepb_proto_depIdxs = []int32{ 1, // 0: messagepb.Message.iss:type_name -> isspb.ISSMessage @@ -285,11 +308,12 @@ var file_messagepb_messagepb_proto_depIdxs = []int32{ 5, // 4: messagepb.Message.checkpoint:type_name -> checkpointpb.Message 6, // 5: messagepb.Message.orderer:type_name -> ordererpb.Message 7, // 6: messagepb.Message.communicationpb:type_name -> communicationpb.Message - 7, // [7:7] is the sub-list for method output_type - 7, // [7:7] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 8, // 7: messagepb.Message.synchronizer:type_name -> synchronizerpb.Message + 8, // [8:8] is the sub-list for method output_type + 8, // [8:8] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_messagepb_messagepb_proto_init() } @@ -319,6 +343,7 @@ func file_messagepb_messagepb_proto_init() { (*Message_Checkpoint)(nil), (*Message_Orderer)(nil), (*Message_Communicationpb)(nil), + (*Message_Synchronizer)(nil), } type x struct{} out := protoimpl.TypeBuilder{ diff --git a/pkg/pb/messagepb/messagepb.pb.mir.go b/pkg/pb/messagepb/messagepb.pb.mir.go index 991370475..f6532da0e 100644 --- a/pkg/pb/messagepb/messagepb.pb.mir.go +++ b/pkg/pb/messagepb/messagepb.pb.mir.go @@ -15,5 +15,6 @@ func (*Message) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Message_Checkpoint)(nil)), reflect.TypeOf((*Message_Orderer)(nil)), reflect.TypeOf((*Message_Communicationpb)(nil)), + reflect.TypeOf((*Message_Synchronizer)(nil)), } } diff --git a/pkg/pb/messagepb/oneof_interfaces.mir.go b/pkg/pb/messagepb/oneof_interfaces.mir.go index 987cdcf53..fd232dc47 100644 --- a/pkg/pb/messagepb/oneof_interfaces.mir.go +++ b/pkg/pb/messagepb/oneof_interfaces.mir.go @@ -6,6 +6,7 @@ import ( mscpb "github.com/filecoin-project/mir/pkg/pb/availabilitypb/mscpb" bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" isspb "github.com/filecoin-project/mir/pkg/pb/isspb" ordererpb "github.com/filecoin-project/mir/pkg/pb/ordererpb" @@ -46,3 +47,7 @@ func (w *Message_Orderer) Unwrap() *ordererpb.Message { func (w *Message_Communicationpb) Unwrap() *communicationpb.Message { return w.Communicationpb } + +func (w *Message_Synchronizer) Unwrap() *synchronizerpb.Message { + return w.Synchronizer +} diff --git a/pkg/pb/messagepb/types/types.mir.go b/pkg/pb/messagepb/types/types.mir.go index af6d27849..fad742b87 100644 --- a/pkg/pb/messagepb/types/types.mir.go +++ b/pkg/pb/messagepb/types/types.mir.go @@ -7,6 +7,7 @@ import ( types3 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/mscpb/types" types2 "github.com/filecoin-project/mir/pkg/pb/bcbpb/types" types7 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types8 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" types5 "github.com/filecoin-project/mir/pkg/pb/checkpointpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/isspb/types" messagepb "github.com/filecoin-project/mir/pkg/pb/messagepb" @@ -51,6 +52,8 @@ func Message_TypeFromPb(pb messagepb.Message_Type) Message_Type { return &Message_Orderer{Orderer: types6.MessageFromPb(pb.Orderer)} case *messagepb.Message_Communicationpb: return &Message_Communicationpb{Communicationpb: types7.MessageFromPb(pb.Communicationpb)} + case *messagepb.Message_Synchronizer: + return &Message_Synchronizer{Synchronizer: types8.MessageFromPb(pb.Synchronizer)} } return nil } @@ -223,6 +226,30 @@ func (*Message_Communicationpb) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*messagepb.Message_Communicationpb]()} } +type Message_Synchronizer struct { + Synchronizer *types8.Message +} + +func (*Message_Synchronizer) isMessage_Type() {} + +func (w *Message_Synchronizer) Unwrap() *types8.Message { + return w.Synchronizer +} + +func (w *Message_Synchronizer) Pb() messagepb.Message_Type { + if w == nil { + return nil + } + if w.Synchronizer == nil { + return &messagepb.Message_Synchronizer{} + } + return &messagepb.Message_Synchronizer{Synchronizer: (w.Synchronizer).Pb()} +} + +func (*Message_Synchronizer) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*messagepb.Message_Synchronizer]()} +} + func MessageFromPb(pb *messagepb.Message) *Message { if pb == nil { return nil diff --git a/protos/blockchainpb/bcmpb/bcmpb.proto b/protos/blockchainpb/bcmpb/bcmpb.proto index f6380d6b4..6c05e50f2 100644 --- a/protos/blockchainpb/bcmpb/bcmpb.proto +++ b/protos/blockchainpb/bcmpb/bcmpb.proto @@ -14,7 +14,11 @@ message Event { oneof type { option (mir.event_type) = true; - NewBlock new_block = 1; + NewBlock new_block = 1; + NewChain new_chain = 2; + + GetBlockRequest get_block_request = 3; + GetBlockResponse get_block_response = 4; } } @@ -22,4 +26,28 @@ message NewBlock { option (mir.event) = true; blockchainpb.Block block = 1; +} + +message NewChain { + option (mir.event) = true; + + // chain segment ordered blocks[0]<-blocks[1]<-blocks[2]... + repeated blockchainpb.Block blocks = 1; +} + +message GetBlockRequest { + option (mir.event) = true; + + uint64 request_id = 1; + string source_module = 2 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.ModuleID"]; + + uint64 block_id = 3; +} + +message GetBlockResponse { + option (mir.event) = true; + + uint64 request_id = 1; + bool found = 2; + blockchainpb.Block block = 3; } \ No newline at end of file diff --git a/protos/blockchainpb/synchronizerpb/synchronizerpb.proto b/protos/blockchainpb/synchronizerpb/synchronizerpb.proto new file mode 100644 index 000000000..494371e90 --- /dev/null +++ b/protos/blockchainpb/synchronizerpb/synchronizerpb.proto @@ -0,0 +1,54 @@ +syntax = "proto3"; + +package synchronizerpb; + +option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb"; + +import "blockchainpb/blockchainpb.proto"; + +import "mir/codegen_extensions.proto"; +import "net/codegen_extensions.proto"; + +message Event { + option (mir.event_class) = true; + + oneof type { + option (mir.event_type) = true; + + SyncRequest sync_request = 1; + } +} + +message SyncRequest { + option (mir.event) = true; + + uint64 orphan_block_id = 1; + uint64 leave_ids = 2; +} + +message Message { + option (net.message_class) = true; + + oneof type { + option (net.message_type) = true; + + BlockRequest block_request = 1; + BlockResponse block_response = 2; + } +} + +message BlockRequest { + option (net.message) = true; + + uint64 request_id = 1; + uint64 block_id = 2; +} + +message BlockResponse { + option (net.message) = true; + + uint64 request_id = 1; + bool found = 2; + blockchainpb.Block block = 3; // possibly undefined (proto3 spec no-longer supports optional/required...) + +} diff --git a/protos/eventpb/eventpb.proto b/protos/eventpb/eventpb.proto index df26dffc7..d7a45b4a9 100644 --- a/protos/eventpb/eventpb.proto +++ b/protos/eventpb/eventpb.proto @@ -27,6 +27,7 @@ import "blockchainpb/bcmpb/bcmpb.proto"; import "blockchainpb/minerpb/minerpb.proto"; import "blockchainpb/tpmpb/tpmpb.proto"; import "blockchainpb/communicationpb/communicationpb.proto"; +import "blockchainpb/synchronizerpb/synchronizerpb.proto"; import "mir/codegen_extensions.proto"; @@ -72,6 +73,7 @@ message Event { minerpb.Event miner = 202; tpmpb.Event tpm = 203; communicationpb.Event communication = 204; + synchronizerpb.Event synchronizer = 205; // for unit-tests google.protobuf.StringValue testingString = 301; diff --git a/protos/generate.go b/protos/generate.go index 85f74154b..c02b1397b 100644 --- a/protos/generate.go +++ b/protos/generate.go @@ -49,6 +49,7 @@ package protos //go:generate protoc-events blockchainpb/minerpb/minerpb.proto //go:generate protoc-events blockchainpb/tpmpb/tpmpb.proto //go:generate protoc-events blockchainpb/communicationpb/communicationpb.proto +//go:generate protoc-events blockchainpb/synchronizerpb/synchronizerpb.proto // Build the custom code generators. //go:generate go build -o ../codegen/generators/mir-std-gen/mir-std-gen.bin ../codegen/generators/mir-std-gen @@ -85,6 +86,7 @@ package protos //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" +//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" // Generate other things. //go:generate protoc --go_out=../pkg/ --go_opt=paths=source_relative --go-grpc_out=../pkg/ --go-grpc_opt=paths=source_relative transactionreceiver/transactionreceiver.proto diff --git a/protos/messagepb/messagepb.proto b/protos/messagepb/messagepb.proto index 30aed2c08..6c58fba0e 100644 --- a/protos/messagepb/messagepb.proto +++ b/protos/messagepb/messagepb.proto @@ -17,6 +17,7 @@ import "pingpongpb/pingpongpb.proto"; import "checkpointpb/checkpointpb.proto"; import "ordererpb/ordererpb.proto"; import "blockchainpb/communicationpb/communicationpb.proto"; +import "blockchainpb/synchronizerpb/synchronizerpb.proto"; import "mir/codegen_extensions.proto"; import "net/codegen_extensions.proto"; @@ -38,6 +39,7 @@ message Message { // Messages for blockchain communicationpb.Message communicationpb = 8; + synchronizerpb.Message synchronizer = 9; } } diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 14b018fc4..4c0588689 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -13,12 +13,14 @@ import ( "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" tpmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/dsl" + t "github.com/filecoin-project/mir/pkg/types" "github.com/filecoin-project/mir/samples/blockchain/ws/ws" "google.golang.org/protobuf/proto" ) var ( - parentNotFound = errors.New("parent not found") + ErrParentNotFound = errors.New("parent not found") + ErrNodeAlreadyInTree = errors.New("node already in tree") ) type bcmBlock struct { @@ -30,7 +32,7 @@ type bcmBlock struct { type bcmModule struct { m *dsl.Module - blocks []bcmBlock + blocks []bcmBlock // IMPORTANT: only to be used for visualization, not for actual block lookup leaves map[uint64]*bcmBlock head *bcmBlock blockCount uint64 @@ -38,13 +40,51 @@ type bcmModule struct { updateChan chan string } -// TODO: might need to add some "cleanup" to handle very old leaves in case this grows too much +// traversalFunc should return true if traversal should continue, false otherwise +// if it shouldn't continue, the function will return the current block +// if it should continue but there are no more blocks, the function will return nil +// if an error is thrown, the traversal is aborted and nil is returned +func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) (bool, error)) *bcmBlock { + currentScanCount := uint64(0) // to mark nodes that have been scanned in this round + queue := make([]*bcmBlock, 0) + for _, v := range bcm.leaves { + queue = append(queue, v) + if v.scanCount > currentScanCount { + currentScanCount = v.scanCount + } + } + for len(queue) > 0 { + // pop from queue + curr := queue[0] + queue = queue[1:] + + // mark as scanned + curr.scanCount = currentScanCount + 1 + if match, err := traversalFunc(curr); err != nil { + return nil + } else if match { + return curr + } + + // add curr's parents to queue if it hasn't been scanned yet + if curr.parent != nil && curr.parent.scanCount <= currentScanCount { + queue = append(queue, curr.parent) + } + } + return nil +} + +// TODO: might need to add some "cleanup" to handle very old leaves in case this grows too much +// function assumes lock is held (is this even necessary? can a mir module process multiple events at once?) func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { println("Adding block...", block.BlockId) - bcm.writeMutex.Lock() - defer bcm.writeMutex.Unlock() + // check if block is already in the leaves, reject if so + if _, ok := bcm.leaves[block.BlockId]; ok { + println("Block already in leaves - ignore") + return nil + } parentId := block.PreviousBlockId // check leaves for parent with its id @@ -63,27 +103,17 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { return nil } - // NOTE: should probably create a structure where very old leaves are scanned later than the newer ones - currentScanCount := uint64(0) // to mark nodes that have been scanned in this round - queue := make([]*bcmBlock, 0) - for _, v := range bcm.leaves { - queue = append(queue, v) - if v.scanCount > currentScanCount { - currentScanCount = v.scanCount + if hit := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { + // check if curr matches the block to be added - if so, ignore + if currBlock.block.BlockId == block.BlockId { + println("Block already in tree - ignore") + return false, ErrNodeAlreadyInTree } - } - for len(queue) > 0 { - // pop from queue - curr := queue[0] - queue = queue[1:] - - // mark as scanned - curr.scanCount = currentScanCount + 1 // check if curr is parent - if curr.block.BlockId == parentId { + if currBlock.block.BlockId == parentId { println("Found parent in tree") - blockNode := bcmBlock{block: block, parent: curr, depth: curr.depth + 1, scanCount: 0} + blockNode := bcmBlock{block: block, parent: currBlock, depth: currBlock.depth + 1, scanCount: 0} // add new leave bcm.leaves[block.BlockId] = &blockNode bcm.blocks = append(bcm.blocks, blockNode) @@ -91,19 +121,16 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { bcm.head = &blockNode } bcm.blockCount++ - return nil + return true, nil } - // add curr's parents to queue if it hasn't been scanned yet - if curr.parent != nil && curr.parent.scanCount <= currentScanCount { - queue = append(queue, curr.parent) - } + return false, nil + }); hit == nil { + println("Couldn't find parent - invalid block. (TODO: add \"ask around for parent\")") + return ErrParentNotFound } - println("Couldn't find parent - invalid block. (TODO: add \"ask around for parent\")") - - return parentNotFound - + return nil } func (bcm *bcmModule) getHead() *blockchainpb.Block { @@ -111,12 +138,20 @@ func (bcm *bcmModule) getHead() *blockchainpb.Block { } func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { + bcm.writeMutex.Lock() + defer bcm.writeMutex.Unlock() + currentHead := bcm.getHead() // insert block if err := bcm.addBlock(block); err != nil { - // silently ignore, just treat it as an invalid block - // TODO: handle "rebuild" of chain if one missed a block or started later - println(err) + if err == ErrParentNotFound { + // ask synchronizer for help + println("Missing parent, asking for help.") + } else { + // some other error + println(err) + } + } bcm.updateChan <- strconv.FormatUint(block.BlockId, 10) @@ -129,6 +164,29 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { } } +func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { + bcm.writeMutex.Lock() + defer bcm.writeMutex.Unlock() + + currentHead := bcm.getHead() + // insert block + for _, block := range blocks { + if err := bcm.addBlock(block); err != nil { + println(err) + } + } + + // get last block + bcm.updateChan <- strconv.FormatUint(blocks[len(blocks)-1].BlockId, 10) + + // if head changed, trigger get tpm to prep new block + if newHead := bcm.getHead(); newHead != currentHead { + // new head + println("New longest chain - head changed!") + tpmpbdsl.NewHead(*bcm.m, "tpm", newHead.BlockId) + } +} + func NewBCM(chainServerPort int) modules.PassiveModule { m := dsl.NewModule("bcm") var bcm bcmModule @@ -172,6 +230,36 @@ func NewBCM(chainServerPort int) modules.PassiveModule { return nil }) + bcmpbdsl.UponNewChain(m, func(blocks []*blockchainpb.Block) error { + // self-mined block - no need to verify + println("~ Received chain from synchronizer") + bcm.handleNewChain(blocks) + return nil + }) + + bcmpbdsl.UponGetBlockRequest(m, func(requestID uint64, sourceModule t.ModuleID, blockID uint64) error { + // check if block is in tree + hit := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { + // check if curr matches the block to be added - if so, ignore + if currBlock.block.BlockId == blockID { + println("Found block in tree") + // respond to sync request + return true, nil + } + + return false, nil + }) + + if hit == nil { + println("Couldn't find block") + bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) + } + + bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block) + + return nil + }) + return m } diff --git a/samples/blockchain/old/miner_passive.go b/samples/blockchain/old/miner_passive.go deleted file mode 100644 index 50a2da188..000000000 --- a/samples/blockchain/old/miner_passive.go +++ /dev/null @@ -1,132 +0,0 @@ -// miner - -package main - -import ( - "context" - "math/rand" - "time" - - "github.com/filecoin-project/mir/pkg/dsl" - "github.com/filecoin-project/mir/pkg/modules" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb" - bcmmsgs "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/msgs" - minerdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" - transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" - t "github.com/filecoin-project/mir/pkg/types" - "github.com/mitchellh/hashstructure" -) - -type minerModule struct { - ctx context.Context // to cancel ongoing mining when new block is requested - cancel context.CancelFunc -} - -type blockRequest struct { - HeadId uint64 - Payload *blockchainpb.Payload -} - -func NewMiner(otherNodes []t.NodeID) modules.PassiveModule { - - m := dsl.NewModule("miner") - var miner minerModule - // blockRequests := make(chan blockRequest) - - dsl.UponInit(m, func() error { - ctx, cancel := context.WithCancel(context.Background()) - miner = minerModule{ctx: ctx, cancel: cancel} - - println("Othr nodes:") - for _, node := range otherNodes { - println(node) - } - // go mineWorker(&m, blockRequests, otherNodes) - - return nil - }) - - minerdsl.UponBlockRequest(m, func(head_id uint64, payload *blockchainpb.Payload) error { - - // blockRequests <- blockRequest{head_id, payload} - - println("Mining block on top of", head_id) - - // cancel ongoing mining - miner.cancel() - // start new mining - miner.ctx, miner.cancel = context.WithCancel(context.Background()) - ownCtx := &miner.ctx - // random mining time to simulate PoW - endtime := time.Now().Add(time.Duration(rand.ExpFloat64() * float64(2*time.Minute))) - for { - if time.Now().After(endtime) { - break - } - - select { - case <-(*ownCtx).Done(): - println("Mining aborted") - return nil - default: - // mine block - time.Sleep(time.Second) - } - } - println("Block mined!") - block := &blockchainpb.Block{BlockId: 0, PreviousBlockId: head_id, Payload: payload, Timestamp: time.Now().Unix()} - hash, err := hashstructure.Hash(block, nil) - if err != nil { - panic(err) - } - block.BlockId = hash - // Block mined! Broadcast to blockchain and send event to bcm. - // bcmdsl.NewBlock(m, "bcm", block) - transportpbdsl.SendMessage(m, "transport", bcmmsgs.NewBlockMessage("bcm", block), otherNodes) - println("Block broadcasted!") - return nil - - }) - - return m -} - -// func mineWorker(m *dsl.Module, blockRequests chan blockRequest, otherNodes []t.NodeID) { -// ctx, cancel := context.WithCancel(context.Background()) - -// for { -// blockRequest := <-blockRequests -// cancel() // abort ongoing mining -// ctx, cancel = context.WithCancel(context.Background()) // new context for new mining -// println("Mining block on top of", blockRequest.HeadId) -// go func() { -// endtime := time.Now().Add(time.Duration(rand.ExpFloat64() * float64(time.Minute))) -// for { -// if time.Now().After(endtime) { -// println("Block mined!") -// block := &blockchainpb.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: time.Now().Unix()} -// hash, err := hashstructure.Hash(block, nil) -// if err != nil { -// panic(err) -// } -// block.BlockId = hash -// // Block mined! Broadcast to blockchain and send event to bcm. -// bcmdsl.NewBlock(*m, "bcm", block) -// transportpbdsl.SendMessage(*m, "transport", bcmmsgs.NewBlockMessage("bcm", block), otherNodes) -// println("Block broadcasted!") -// return -// } - -// select { -// case <-ctx.Done(): -// println("Mining aborted") -// return -// default: -// // mine block -// time.Sleep(time.Second) -// } -// } -// }() -// println("not blocking...") -// } -// } diff --git a/samples/blockchain/synchronizer.go b/samples/blockchain/synchronizer.go new file mode 100644 index 000000000..3e9698639 --- /dev/null +++ b/samples/blockchain/synchronizer.go @@ -0,0 +1,198 @@ +// blockchain manager + +package main + +import ( + "errors" + "math/rand" + + "github.com/filecoin-project/mir/pkg/dsl" + "github.com/filecoin-project/mir/pkg/modules" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" + synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" + synchronizerpbmsgs "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/msgs" + transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" + t "github.com/filecoin-project/mir/pkg/types" +) + +const ( + STRIKE_THRESHOLD = 2 +) + +var ( + ErrRequestAlreadyExists = errors.New("request already exists") + ErrNoMoreNodes = errors.New("no more nodes to contact") + ErrUnknownRequest = errors.New("unknown request") + ErrUnkownGetBlockRequest = errors.New("unknown get block request") +) + +// NOTE: add 'crashed node detection' - remove nodes that don't respond to requests +// rn it would just get stuck on such a node... + +type synchronizerModule struct { + m *dsl.Module + syncRequests map[uint64]*syncRequest + getRequests map[uint64]*getRequest + otherNodes []*t.NodeID // we remove nodes that we presume have crashed (see strikeList) // TODO: add a timeout to 're-add' them? exponential backoff? + // stikeList map[t.NodeID]int // a node gets a strike whenever it failed to handle a request, after STRIKE_THRESHOLD strikes it is removed from the otherNodes. Set to 0 if it handles a request successfully +} + +/************************* + * Outgoing sync requests + *************************/ + +type syncRequest struct { + requestID uint64 + blockID uint64 + leaveIDs []uint64 + nodesContacted []int // index of node in otherNodes + chain []*blockchainpb.Block +} + +func (sm *synchronizerModule) registerSyncRequest(blockID uint64) (uint64, error) { + requestId := blockID + + // check if request already exists + if _, ok := sm.syncRequests[requestId]; ok { + return 0, ErrRequestAlreadyExists + } + + sm.syncRequests[requestId] = &syncRequest{requestID: requestId, blockID: blockID, nodesContacted: []int{}} + + return requestId, nil +} + +func (sm *synchronizerModule) contactNextNode(requestID uint64) error { + request, ok := sm.syncRequests[requestID] + if !ok { + return ErrUnknownRequest + } + + var nextNodeIndex int + // check if we have contacted all nodes + if len(request.nodesContacted) == len(sm.otherNodes) { + return ErrNoMoreNodes + } else if len(request.nodesContacted) == 0 { + // first node to contact, pick randomly + nextNodeIndex = rand.Intn(len(sm.otherNodes)) + + } else { + // get next node + // NOTE: stupid to keep an array of indices but it's more flexible in case I want to change the way I pick the next node + nextNodeIndex = (request.nodesContacted[len(request.nodesContacted)] + 1) % len(sm.otherNodes) + } + + request.nodesContacted = append(request.nodesContacted, nextNodeIndex) + nextNode := *sm.otherNodes[nextNodeIndex] + + // send request + transportpbdsl.SendMessage(*sm.m, "synchronizer", synchronizerpbmsgs.BlockRequest("synchronizer", requestID, request.blockID), []t.NodeID{nextNode}) + + return nil +} + +func (sm *synchronizerModule) handleSyncRequest(orphanBlockId uint64, leaveIds uint64) error { + return nil +} + +func (sm *synchronizerModule) handleBlockResponseReceived(from t.NodeID, requestID uint64, found bool, block *blockchainpb.Block) error { + // check if request exists + request, ok := sm.syncRequests[requestID] + if !ok { + // assume this is a delayed response and we already handled it + return nil + } + + if !found { + // check whether the response came fromt he last node we contacted + // NOTE: this is getting messy + if *sm.otherNodes[request.nodesContacted[len(request.nodesContacted)-1]] != from { + // there is still a request out in the ether - don't send another one + return nil + + } + // send request to the next node + sm.contactNextNode(requestID) + return nil + } + + // we got a block + request.chain = append(request.chain, block) + // check if block.parentId is in leaveIDs + for _, leaveID := range request.leaveIDs { + if block.PreviousBlockId == leaveID { + // we are done, send the chain to the blockchain manager, delete the request + for i, j := 0, len(request.chain)-1; i < j; i, j = i+1, j-1 { + request.chain[i], request.chain[j] = request.chain[j], request.chain[i] + } + bcmpbdsl.NewChain(*sm.m, "bcm", request.chain) + delete(sm.syncRequests, requestID) + return nil + } + } + + // update request + request.blockID = block.PreviousBlockId + request.nodesContacted = []int{} + sm.contactNextNode(requestID) + return nil +} + +/************************* + * Outgoing sync requests + *************************/ +type getRequest struct { + requestID uint64 + from t.NodeID +} + +func (sm *synchronizerModule) handleBlockRequestReceived(from t.NodeID, requestID uint64, blockID uint64) error { + // check if request is already being processed + if _, ok := sm.getRequests[requestID]; ok { + return ErrRequestAlreadyExists + } + + // register request + sm.getRequests[requestID] = &getRequest{requestID: requestID, from: from} + + // send get request to blockchain manager + bcmpbdsl.GetBlockRequest(*sm.m, "bcm", requestID, "synchronizer", blockID) + + return nil + +} + +func (sm *synchronizerModule) handleGetBlockResponse(requestID uint64, found bool, block *blockchainpb.Block) error { + request, ok := sm.getRequests[requestID] + if !ok { + return ErrUnkownGetBlockRequest + } + + // respond to sync request + transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.BlockResponse("synchronizer", requestID, found, block), []t.NodeID{request.from}) + + // delete request + delete(sm.getRequests, requestID) + + return nil +} + +func NewSynchronizer(otherNodes []*t.NodeID) modules.PassiveModule { + m := dsl.NewModule("synchronizer") + var sm synchronizerModule + + dsl.UponInit(m, func() error { + return nil + }) + + // outgoing sync requests + synchronizerpbdsl.UponSyncRequest(m, sm.handleSyncRequest) + synchronizerpbdsl.UponBlockResponseReceived(m, sm.handleBlockResponseReceived) + + // for incoming sync requests + synchronizerpbdsl.UponBlockRequestReceived(m, sm.handleBlockRequestReceived) + bcmpbdsl.UponGetBlockResponse(m, sm.handleGetBlockResponse) + + return m +} From 719fa7015e954dba0fa240d5a584d1182a641226 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Thu, 16 Nov 2023 11:44:49 +0100 Subject: [PATCH 03/46] working sync --- pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go | 4 + pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go | 6 + .../blockchainpb/minerpb/events/events.mir.go | 15 +++ pkg/pb/blockchainpb/minerpb/minerpb.pb.go | 122 +++++++++++++++--- pkg/pb/blockchainpb/minerpb/minerpb.pb.mir.go | 1 + .../minerpb/oneof_interfaces.mir.go | 4 + .../blockchainpb/minerpb/types/types.mir.go | 55 ++++++++ .../synchronizerpb/dsl/emit.mir.go | 5 +- .../synchronizerpb/dsl/upon.mir.go | 5 +- .../synchronizerpb/events/events.mir.go | 7 +- .../synchronizerpb/synchronizerpb.pb.go | 44 ++++--- .../synchronizerpb/types/types.mir.go | 12 +- pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go | 4 +- pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go | 4 +- .../blockchainpb/tpmpb/events/events.mir.go | 6 +- .../tpmpb/oneof_interfaces.mir.go | 4 +- pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go | 68 +++++----- pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go | 2 +- pkg/pb/blockchainpb/tpmpb/types/types.mir.go | 40 +++--- protos/blockchainpb/minerpb/minerpb.proto | 9 +- .../synchronizerpb/synchronizerpb.proto | 4 +- protos/blockchainpb/tpmpb/tpmpb.proto | 4 +- samples/blockchain/bcm.go | 27 ++-- samples/blockchain/communication.go | 34 +---- samples/blockchain/main.go | 26 +++- samples/blockchain/miner.go | 15 ++- samples/blockchain/synchronizer.go | 64 +++++++-- samples/blockchain/tpm.go | 2 +- 28 files changed, 406 insertions(+), 187 deletions(-) diff --git a/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go index 0822426ea..9b6160373 100644 --- a/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go @@ -14,3 +14,7 @@ import ( func BlockRequest(m dsl.Module, destModule types.ModuleID, headId uint64, payload *blockchainpb.Payload) { dsl.EmitMirEvent(m, events.BlockRequest(destModule, headId, payload)) } + +func NewHead(m dsl.Module, destModule types.ModuleID, headId uint64) { + dsl.EmitMirEvent(m, events.NewHead(destModule, headId)) +} diff --git a/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go index 395302269..fa88e6697 100644 --- a/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go @@ -27,3 +27,9 @@ func UponBlockRequest(m dsl.Module, handler func(headId uint64, payload *blockch return handler(ev.HeadId, ev.Payload) }) } + +func UponNewHead(m dsl.Module, handler func(headId uint64) error) { + UponEvent[*types.Event_NewHead](m, func(ev *types.NewHead) error { + return handler(ev.HeadId) + }) +} diff --git a/pkg/pb/blockchainpb/minerpb/events/events.mir.go b/pkg/pb/blockchainpb/minerpb/events/events.mir.go index e72bf6c94..1a8d9daa7 100644 --- a/pkg/pb/blockchainpb/minerpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/minerpb/events/events.mir.go @@ -24,3 +24,18 @@ func BlockRequest(destModule types.ModuleID, headId uint64, payload *blockchainp }, } } + +func NewHead(destModule types.ModuleID, headId uint64) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Miner{ + Miner: &types2.Event{ + Type: &types2.Event_NewHead{ + NewHead: &types2.NewHead{ + HeadId: headId, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/minerpb/minerpb.pb.go b/pkg/pb/blockchainpb/minerpb/minerpb.pb.go index e8da24d42..e3a0567f1 100644 --- a/pkg/pb/blockchainpb/minerpb/minerpb.pb.go +++ b/pkg/pb/blockchainpb/minerpb/minerpb.pb.go @@ -30,6 +30,7 @@ type Event struct { // Types that are assignable to Type: // // *Event_BlockRequest + // *Event_NewHead Type isEvent_Type `protobuf_oneof:"type"` } @@ -79,6 +80,13 @@ func (x *Event) GetBlockRequest() *BlockRequest { return nil } +func (x *Event) GetNewHead() *NewHead { + if x, ok := x.GetType().(*Event_NewHead); ok { + return x.NewHead + } + return nil +} + type isEvent_Type interface { isEvent_Type() } @@ -87,8 +95,14 @@ type Event_BlockRequest struct { BlockRequest *BlockRequest `protobuf:"bytes,1,opt,name=block_request,json=blockRequest,proto3,oneof"` } +type Event_NewHead struct { + NewHead *NewHead `protobuf:"bytes,2,opt,name=new_head,json=newHead,proto3,oneof"` +} + func (*Event_BlockRequest) isEvent_Type() {} +func (*Event_NewHead) isEvent_Type() {} + type BlockRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -144,6 +158,53 @@ func (x *BlockRequest) GetPayload() *blockchainpb.Payload { return nil } +type NewHead struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + HeadId uint64 `protobuf:"varint,1,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` +} + +func (x *NewHead) Reset() { + *x = NewHead{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_minerpb_minerpb_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewHead) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewHead) ProtoMessage() {} + +func (x *NewHead) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_minerpb_minerpb_proto_msgTypes[2] + 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 NewHead.ProtoReflect.Descriptor instead. +func (*NewHead) Descriptor() ([]byte, []int) { + return file_blockchainpb_minerpb_minerpb_proto_rawDescGZIP(), []int{2} +} + +func (x *NewHead) GetHeadId() uint64 { + if x != nil { + return x.HeadId + } + return 0 +} + var File_blockchainpb_minerpb_minerpb_proto protoreflect.FileDescriptor var file_blockchainpb_minerpb_minerpb_proto_rawDesc = []byte{ @@ -153,23 +214,29 @@ var file_blockchainpb_minerpb_minerpb_proto_rawDesc = []byte{ 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x59, 0x0a, 0x05, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, - 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x88, 0x01, 0x0a, + 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, + 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, + 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x48, 0x00, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48, + 0x65, 0x61, 0x64, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2f, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, - 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x6d, - 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, + 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, + 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -184,20 +251,22 @@ func file_blockchainpb_minerpb_minerpb_proto_rawDescGZIP() []byte { return file_blockchainpb_minerpb_minerpb_proto_rawDescData } -var file_blockchainpb_minerpb_minerpb_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_blockchainpb_minerpb_minerpb_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_blockchainpb_minerpb_minerpb_proto_goTypes = []interface{}{ (*Event)(nil), // 0: minerpb.Event (*BlockRequest)(nil), // 1: minerpb.BlockRequest - (*blockchainpb.Payload)(nil), // 2: blockchainpb.Payload + (*NewHead)(nil), // 2: minerpb.NewHead + (*blockchainpb.Payload)(nil), // 3: blockchainpb.Payload } var file_blockchainpb_minerpb_minerpb_proto_depIdxs = []int32{ 1, // 0: minerpb.Event.block_request:type_name -> minerpb.BlockRequest - 2, // 1: minerpb.BlockRequest.payload:type_name -> blockchainpb.Payload - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 2, // 1: minerpb.Event.new_head:type_name -> minerpb.NewHead + 3, // 2: minerpb.BlockRequest.payload:type_name -> blockchainpb.Payload + 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 } func init() { file_blockchainpb_minerpb_minerpb_proto_init() } @@ -230,9 +299,22 @@ func file_blockchainpb_minerpb_minerpb_proto_init() { return nil } } + file_blockchainpb_minerpb_minerpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewHead); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_blockchainpb_minerpb_minerpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_BlockRequest)(nil), + (*Event_NewHead)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -240,7 +322,7 @@ func file_blockchainpb_minerpb_minerpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_minerpb_minerpb_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/minerpb/minerpb.pb.mir.go b/pkg/pb/blockchainpb/minerpb/minerpb.pb.mir.go index 5956b9521..bdc485481 100644 --- a/pkg/pb/blockchainpb/minerpb/minerpb.pb.mir.go +++ b/pkg/pb/blockchainpb/minerpb/minerpb.pb.mir.go @@ -9,5 +9,6 @@ import ( func (*Event) ReflectTypeOptions() []reflect.Type { return []reflect.Type{ reflect.TypeOf((*Event_BlockRequest)(nil)), + reflect.TypeOf((*Event_NewHead)(nil)), } } diff --git a/pkg/pb/blockchainpb/minerpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/minerpb/oneof_interfaces.mir.go index 13c055770..9a8f8c8f1 100644 --- a/pkg/pb/blockchainpb/minerpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/minerpb/oneof_interfaces.mir.go @@ -12,3 +12,7 @@ type Event_TypeWrapper[T any] interface { func (w *Event_BlockRequest) Unwrap() *BlockRequest { return w.BlockRequest } + +func (w *Event_NewHead) Unwrap() *NewHead { + return w.NewHead +} diff --git a/pkg/pb/blockchainpb/minerpb/types/types.mir.go b/pkg/pb/blockchainpb/minerpb/types/types.mir.go index cb36f4ddc..684ad11ae 100644 --- a/pkg/pb/blockchainpb/minerpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/minerpb/types/types.mir.go @@ -31,6 +31,8 @@ func Event_TypeFromPb(pb minerpb.Event_Type) Event_Type { switch pb := pb.(type) { case *minerpb.Event_BlockRequest: return &Event_BlockRequest{BlockRequest: BlockRequestFromPb(pb.BlockRequest)} + case *minerpb.Event_NewHead: + return &Event_NewHead{NewHead: NewHeadFromPb(pb.NewHead)} } return nil } @@ -59,6 +61,30 @@ func (*Event_BlockRequest) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*minerpb.Event_BlockRequest]()} } +type Event_NewHead struct { + NewHead *NewHead +} + +func (*Event_NewHead) isEvent_Type() {} + +func (w *Event_NewHead) Unwrap() *NewHead { + return w.NewHead +} + +func (w *Event_NewHead) Pb() minerpb.Event_Type { + if w == nil { + return nil + } + if w.NewHead == nil { + return &minerpb.Event_NewHead{} + } + return &minerpb.Event_NewHead{NewHead: (w.NewHead).Pb()} +} + +func (*Event_NewHead) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*minerpb.Event_NewHead]()} +} + func EventFromPb(pb *minerpb.Event) *Event { if pb == nil { return nil @@ -119,3 +145,32 @@ func (m *BlockRequest) Pb() *minerpb.BlockRequest { func (*BlockRequest) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*minerpb.BlockRequest]()} } + +type NewHead struct { + HeadId uint64 +} + +func NewHeadFromPb(pb *minerpb.NewHead) *NewHead { + if pb == nil { + return nil + } + return &NewHead{ + HeadId: pb.HeadId, + } +} + +func (m *NewHead) Pb() *minerpb.NewHead { + if m == nil { + return nil + } + pbMessage := &minerpb.NewHead{} + { + pbMessage.HeadId = m.HeadId + } + + return pbMessage +} + +func (*NewHead) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*minerpb.NewHead]()} +} diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go index b9d8518cf..3f4cd734a 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go @@ -4,12 +4,13 @@ package synchronizerpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/events" types "github.com/filecoin-project/mir/pkg/types" ) // Module-specific dsl functions for emitting events. -func SyncRequest(m dsl.Module, destModule types.ModuleID, orphanBlockId uint64, leaveIds uint64) { - dsl.EmitMirEvent(m, events.SyncRequest(destModule, orphanBlockId, leaveIds)) +func SyncRequest(m dsl.Module, destModule types.ModuleID, orphanBlock *blockchainpb.Block, leaveIds []uint64) { + dsl.EmitMirEvent(m, events.SyncRequest(destModule, orphanBlock, leaveIds)) } diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go index 8d4827010..d3b0790b8 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go @@ -4,6 +4,7 @@ package synchronizerpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" ) @@ -21,8 +22,8 @@ func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func }) } -func UponSyncRequest(m dsl.Module, handler func(orphanBlockId uint64, leaveIds uint64) error) { +func UponSyncRequest(m dsl.Module, handler func(orphanBlock *blockchainpb.Block, leaveIds []uint64) error) { UponEvent[*types.Event_SyncRequest](m, func(ev *types.SyncRequest) error { - return handler(ev.OrphanBlockId, ev.LeaveIds) + return handler(ev.OrphanBlock, ev.LeaveIds) }) } diff --git a/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go b/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go index 9f631f8c1..a5f5b99f4 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go @@ -3,20 +3,21 @@ package synchronizerpbevents import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) -func SyncRequest(destModule types.ModuleID, orphanBlockId uint64, leaveIds uint64) *types1.Event { +func SyncRequest(destModule types.ModuleID, orphanBlock *blockchainpb.Block, leaveIds []uint64) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Synchronizer{ Synchronizer: &types2.Event{ Type: &types2.Event_SyncRequest{ SyncRequest: &types2.SyncRequest{ - OrphanBlockId: orphanBlockId, - LeaveIds: leaveIds, + OrphanBlock: orphanBlock, + LeaveIds: leaveIds, }, }, }, diff --git a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go index 49d55116b..812b2111f 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go +++ b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go @@ -95,8 +95,8 @@ type SyncRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - OrphanBlockId uint64 `protobuf:"varint,1,opt,name=orphan_block_id,json=orphanBlockId,proto3" json:"orphan_block_id,omitempty"` - LeaveIds uint64 `protobuf:"varint,2,opt,name=leave_ids,json=leaveIds,proto3" json:"leave_ids,omitempty"` + OrphanBlock *blockchainpb.Block `protobuf:"bytes,1,opt,name=orphan_block,json=orphanBlock,proto3" json:"orphan_block,omitempty"` + LeaveIds []uint64 `protobuf:"varint,2,rep,packed,name=leave_ids,json=leaveIds,proto3" json:"leave_ids,omitempty"` } func (x *SyncRequest) Reset() { @@ -131,18 +131,18 @@ func (*SyncRequest) Descriptor() ([]byte, []int) { return file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescGZIP(), []int{1} } -func (x *SyncRequest) GetOrphanBlockId() uint64 { +func (x *SyncRequest) GetOrphanBlock() *blockchainpb.Block { if x != nil { - return x.OrphanBlockId + return x.OrphanBlock } - return 0 + return nil } -func (x *SyncRequest) GetLeaveIds() uint64 { +func (x *SyncRequest) GetLeaveIds() []uint64 { if x != nil { return x.LeaveIds } - return 0 + return nil } type Message struct { @@ -362,12 +362,13 @@ var file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDesc = []byte{ 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, - 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x58, - 0x0a, 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x26, 0x0a, - 0x0f, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0d, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x69, - 0x64, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x49, + 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x68, + 0x0a, 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, + 0x0c, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0b, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x69, + 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x49, 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xaa, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x79, @@ -422,14 +423,15 @@ var file_blockchainpb_synchronizerpb_synchronizerpb_proto_goTypes = []interface{ } var file_blockchainpb_synchronizerpb_synchronizerpb_proto_depIdxs = []int32{ 1, // 0: synchronizerpb.Event.sync_request:type_name -> synchronizerpb.SyncRequest - 3, // 1: synchronizerpb.Message.block_request:type_name -> synchronizerpb.BlockRequest - 4, // 2: synchronizerpb.Message.block_response:type_name -> synchronizerpb.BlockResponse - 5, // 3: synchronizerpb.BlockResponse.block:type_name -> blockchainpb.Block - 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 + 5, // 1: synchronizerpb.SyncRequest.orphan_block:type_name -> blockchainpb.Block + 3, // 2: synchronizerpb.Message.block_request:type_name -> synchronizerpb.BlockRequest + 4, // 3: synchronizerpb.Message.block_response:type_name -> synchronizerpb.BlockResponse + 5, // 4: synchronizerpb.BlockResponse.block:type_name -> blockchainpb.Block + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] 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 } func init() { file_blockchainpb_synchronizerpb_synchronizerpb_proto_init() } diff --git a/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go index 85181c9a8..9e853c987 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go @@ -87,8 +87,8 @@ func (*Event) MirReflect() mirreflect.Type { } type SyncRequest struct { - OrphanBlockId uint64 - LeaveIds uint64 + OrphanBlock *blockchainpb.Block + LeaveIds []uint64 } func SyncRequestFromPb(pb *synchronizerpb.SyncRequest) *SyncRequest { @@ -96,8 +96,8 @@ func SyncRequestFromPb(pb *synchronizerpb.SyncRequest) *SyncRequest { return nil } return &SyncRequest{ - OrphanBlockId: pb.OrphanBlockId, - LeaveIds: pb.LeaveIds, + OrphanBlock: pb.OrphanBlock, + LeaveIds: pb.LeaveIds, } } @@ -107,7 +107,9 @@ func (m *SyncRequest) Pb() *synchronizerpb.SyncRequest { } pbMessage := &synchronizerpb.SyncRequest{} { - pbMessage.OrphanBlockId = m.OrphanBlockId + if m.OrphanBlock != nil { + pbMessage.OrphanBlock = m.OrphanBlock + } pbMessage.LeaveIds = m.LeaveIds } diff --git a/pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go index 2d2377c84..aacc52b41 100644 --- a/pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go @@ -10,6 +10,6 @@ import ( // Module-specific dsl functions for emitting events. -func NewHead(m dsl.Module, destModule types.ModuleID, headId uint64) { - dsl.EmitMirEvent(m, events.NewHead(destModule, headId)) +func NewBlockRequest(m dsl.Module, destModule types.ModuleID, headId uint64) { + dsl.EmitMirEvent(m, events.NewBlockRequest(destModule, headId)) } diff --git a/pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go index b4fd28eec..e048f603c 100644 --- a/pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go @@ -21,8 +21,8 @@ func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func }) } -func UponNewHead(m dsl.Module, handler func(headId uint64) error) { - UponEvent[*types.Event_NewHead](m, func(ev *types.NewHead) error { +func UponNewBlockRequest(m dsl.Module, handler func(headId uint64) error) { + UponEvent[*types.Event_NewBlockRequest](m, func(ev *types.NewBlockRequest) error { return handler(ev.HeadId) }) } diff --git a/pkg/pb/blockchainpb/tpmpb/events/events.mir.go b/pkg/pb/blockchainpb/tpmpb/events/events.mir.go index 87cc301cf..36abbfffa 100644 --- a/pkg/pb/blockchainpb/tpmpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/tpmpb/events/events.mir.go @@ -8,13 +8,13 @@ import ( types "github.com/filecoin-project/mir/pkg/types" ) -func NewHead(destModule types.ModuleID, headId uint64) *types1.Event { +func NewBlockRequest(destModule types.ModuleID, headId uint64) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Tpm{ Tpm: &types2.Event{ - Type: &types2.Event_NewHead{ - NewHead: &types2.NewHead{ + Type: &types2.Event_NewBlockRequest{ + NewBlockRequest: &types2.NewBlockRequest{ HeadId: headId, }, }, diff --git a/pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go index e76988397..234641010 100644 --- a/pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go @@ -9,6 +9,6 @@ type Event_TypeWrapper[T any] interface { Unwrap() *T } -func (w *Event_NewHead) Unwrap() *NewHead { - return w.NewHead +func (w *Event_NewBlockRequest) Unwrap() *NewBlockRequest { + return w.NewBlockRequest } diff --git a/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go b/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go index 91ca10c4a..43d549f3a 100644 --- a/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go +++ b/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go @@ -28,7 +28,7 @@ type Event struct { // Types that are assignable to Type: // - // *Event_NewHead + // *Event_NewBlockRequest Type isEvent_Type `protobuf_oneof:"type"` } @@ -71,9 +71,9 @@ func (m *Event) GetType() isEvent_Type { return nil } -func (x *Event) GetNewHead() *NewHead { - if x, ok := x.GetType().(*Event_NewHead); ok { - return x.NewHead +func (x *Event) GetNewBlockRequest() *NewBlockRequest { + if x, ok := x.GetType().(*Event_NewBlockRequest); ok { + return x.NewBlockRequest } return nil } @@ -82,13 +82,13 @@ type isEvent_Type interface { isEvent_Type() } -type Event_NewHead struct { - NewHead *NewHead `protobuf:"bytes,1,opt,name=new_head,json=newHead,proto3,oneof"` +type Event_NewBlockRequest struct { + NewBlockRequest *NewBlockRequest `protobuf:"bytes,1,opt,name=new_block_request,json=newBlockRequest,proto3,oneof"` } -func (*Event_NewHead) isEvent_Type() {} +func (*Event_NewBlockRequest) isEvent_Type() {} -type NewHead struct { +type NewBlockRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -97,8 +97,8 @@ type NewHead struct { HeadId uint64 `protobuf:"varint,1,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` } -func (x *NewHead) Reset() { - *x = NewHead{} +func (x *NewBlockRequest) Reset() { + *x = NewBlockRequest{} if protoimpl.UnsafeEnabled { mi := &file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -106,13 +106,13 @@ func (x *NewHead) Reset() { } } -func (x *NewHead) String() string { +func (x *NewBlockRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NewHead) ProtoMessage() {} +func (*NewBlockRequest) ProtoMessage() {} -func (x *NewHead) ProtoReflect() protoreflect.Message { +func (x *NewBlockRequest) ProtoReflect() protoreflect.Message { mi := &file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -124,12 +124,12 @@ func (x *NewHead) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NewHead.ProtoReflect.Descriptor instead. -func (*NewHead) Descriptor() ([]byte, []int) { +// Deprecated: Use NewBlockRequest.ProtoReflect.Descriptor instead. +func (*NewBlockRequest) Descriptor() ([]byte, []int) { return file_blockchainpb_tpmpb_tpmpb_proto_rawDescGZIP(), []int{1} } -func (x *NewHead) GetHeadId() uint64 { +func (x *NewBlockRequest) GetHeadId() uint64 { if x != nil { return x.HeadId } @@ -143,18 +143,20 @@ var file_blockchainpb_tpmpb_tpmpb_proto_rawDesc = []byte{ 0x70, 0x6d, 0x70, 0x62, 0x2f, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x48, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2b, - 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, - 0x48, 0x00, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x3a, 0x04, 0x90, 0xa6, 0x1d, - 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, - 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, - 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, - 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, - 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, - 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, - 0x2f, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x61, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x44, + 0x0a, 0x11, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x70, 0x6d, 0x70, + 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x48, 0x00, 0x52, 0x0f, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, + 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x30, 0x0a, 0x0f, 0x4e, 0x65, 0x77, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, + 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, + 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, + 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, + 0x62, 0x2f, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -171,11 +173,11 @@ func file_blockchainpb_tpmpb_tpmpb_proto_rawDescGZIP() []byte { var file_blockchainpb_tpmpb_tpmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_blockchainpb_tpmpb_tpmpb_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: tpmpb.Event - (*NewHead)(nil), // 1: tpmpb.NewHead + (*Event)(nil), // 0: tpmpb.Event + (*NewBlockRequest)(nil), // 1: tpmpb.NewBlockRequest } var file_blockchainpb_tpmpb_tpmpb_proto_depIdxs = []int32{ - 1, // 0: tpmpb.Event.new_head:type_name -> tpmpb.NewHead + 1, // 0: tpmpb.Event.new_block_request:type_name -> tpmpb.NewBlockRequest 1, // [1:1] is the sub-list for method output_type 1, // [1:1] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name @@ -202,7 +204,7 @@ func file_blockchainpb_tpmpb_tpmpb_proto_init() { } } file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NewHead); i { + switch v := v.(*NewBlockRequest); i { case 0: return &v.state case 1: @@ -215,7 +217,7 @@ func file_blockchainpb_tpmpb_tpmpb_proto_init() { } } file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[0].OneofWrappers = []interface{}{ - (*Event_NewHead)(nil), + (*Event_NewBlockRequest)(nil), } type x struct{} out := protoimpl.TypeBuilder{ diff --git a/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go b/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go index 313bb410b..91ebfb31f 100644 --- a/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go +++ b/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go @@ -8,6 +8,6 @@ import ( func (*Event) ReflectTypeOptions() []reflect.Type { return []reflect.Type{ - reflect.TypeOf((*Event_NewHead)(nil)), + reflect.TypeOf((*Event_NewBlockRequest)(nil)), } } diff --git a/pkg/pb/blockchainpb/tpmpb/types/types.mir.go b/pkg/pb/blockchainpb/tpmpb/types/types.mir.go index 00ead972e..95dd7efbd 100644 --- a/pkg/pb/blockchainpb/tpmpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/tpmpb/types/types.mir.go @@ -28,34 +28,34 @@ func Event_TypeFromPb(pb tpmpb.Event_Type) Event_Type { return nil } switch pb := pb.(type) { - case *tpmpb.Event_NewHead: - return &Event_NewHead{NewHead: NewHeadFromPb(pb.NewHead)} + case *tpmpb.Event_NewBlockRequest: + return &Event_NewBlockRequest{NewBlockRequest: NewBlockRequestFromPb(pb.NewBlockRequest)} } return nil } -type Event_NewHead struct { - NewHead *NewHead +type Event_NewBlockRequest struct { + NewBlockRequest *NewBlockRequest } -func (*Event_NewHead) isEvent_Type() {} +func (*Event_NewBlockRequest) isEvent_Type() {} -func (w *Event_NewHead) Unwrap() *NewHead { - return w.NewHead +func (w *Event_NewBlockRequest) Unwrap() *NewBlockRequest { + return w.NewBlockRequest } -func (w *Event_NewHead) Pb() tpmpb.Event_Type { +func (w *Event_NewBlockRequest) Pb() tpmpb.Event_Type { if w == nil { return nil } - if w.NewHead == nil { - return &tpmpb.Event_NewHead{} + if w.NewBlockRequest == nil { + return &tpmpb.Event_NewBlockRequest{} } - return &tpmpb.Event_NewHead{NewHead: (w.NewHead).Pb()} + return &tpmpb.Event_NewBlockRequest{NewBlockRequest: (w.NewBlockRequest).Pb()} } -func (*Event_NewHead) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*tpmpb.Event_NewHead]()} +func (*Event_NewBlockRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*tpmpb.Event_NewBlockRequest]()} } func EventFromPb(pb *tpmpb.Event) *Event { @@ -85,24 +85,24 @@ func (*Event) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*tpmpb.Event]()} } -type NewHead struct { +type NewBlockRequest struct { HeadId uint64 } -func NewHeadFromPb(pb *tpmpb.NewHead) *NewHead { +func NewBlockRequestFromPb(pb *tpmpb.NewBlockRequest) *NewBlockRequest { if pb == nil { return nil } - return &NewHead{ + return &NewBlockRequest{ HeadId: pb.HeadId, } } -func (m *NewHead) Pb() *tpmpb.NewHead { +func (m *NewBlockRequest) Pb() *tpmpb.NewBlockRequest { if m == nil { return nil } - pbMessage := &tpmpb.NewHead{} + pbMessage := &tpmpb.NewBlockRequest{} { pbMessage.HeadId = m.HeadId } @@ -110,6 +110,6 @@ func (m *NewHead) Pb() *tpmpb.NewHead { return pbMessage } -func (*NewHead) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*tpmpb.NewHead]()} +func (*NewBlockRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*tpmpb.NewBlockRequest]()} } diff --git a/protos/blockchainpb/minerpb/minerpb.proto b/protos/blockchainpb/minerpb/minerpb.proto index 136396065..120e16fef 100644 --- a/protos/blockchainpb/minerpb/minerpb.proto +++ b/protos/blockchainpb/minerpb/minerpb.proto @@ -13,7 +13,8 @@ message Event { oneof type { option (mir.event_type) = true; - BlockRequest block_request = 1; + BlockRequest block_request = 1; + NewHead new_head = 2; } } @@ -23,3 +24,9 @@ message BlockRequest { uint64 head_id = 1; blockchainpb.Payload payload = 2; } + +message NewHead { + option (mir.event) = true; + + uint64 head_id = 1; +} \ No newline at end of file diff --git a/protos/blockchainpb/synchronizerpb/synchronizerpb.proto b/protos/blockchainpb/synchronizerpb/synchronizerpb.proto index 494371e90..9c8527c60 100644 --- a/protos/blockchainpb/synchronizerpb/synchronizerpb.proto +++ b/protos/blockchainpb/synchronizerpb/synchronizerpb.proto @@ -22,8 +22,8 @@ message Event { message SyncRequest { option (mir.event) = true; - uint64 orphan_block_id = 1; - uint64 leave_ids = 2; + blockchainpb.Block orphan_block = 1; + repeated uint64 leave_ids = 2; } message Message { diff --git a/protos/blockchainpb/tpmpb/tpmpb.proto b/protos/blockchainpb/tpmpb/tpmpb.proto index 3786f817c..fce9e761d 100644 --- a/protos/blockchainpb/tpmpb/tpmpb.proto +++ b/protos/blockchainpb/tpmpb/tpmpb.proto @@ -12,11 +12,11 @@ message Event { oneof type { option (mir.event_type) = true; - NewHead new_head = 1; + NewBlockRequest new_block_request = 1; } } -message NewHead { +message NewBlockRequest { option (mir.event) = true; //blockchainpb.Blockchain chain = 1; diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 4c0588689..4a77b6141 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -12,9 +12,11 @@ import ( "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" - tpmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/dsl" + minerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" + synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" t "github.com/filecoin-project/mir/pkg/types" "github.com/filecoin-project/mir/samples/blockchain/ws/ws" + "golang.org/x/exp/maps" "google.golang.org/protobuf/proto" ) @@ -35,6 +37,7 @@ type bcmModule struct { blocks []bcmBlock // IMPORTANT: only to be used for visualization, not for actual block lookup leaves map[uint64]*bcmBlock head *bcmBlock + genesis *bcmBlock blockCount uint64 writeMutex *sync.Mutex // can be smarted about locking but just locking everything for now updateChan chan string @@ -126,7 +129,7 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { return false, nil }); hit == nil { - println("Couldn't find parent - invalid block. (TODO: add \"ask around for parent\")") + println("Couldn't find parent - asking around...") return ErrParentNotFound } @@ -146,6 +149,7 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { if err := bcm.addBlock(block); err != nil { if err == ErrParentNotFound { // ask synchronizer for help + synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", block, append(maps.Keys(bcm.leaves), bcm.genesis.block.BlockId)) println("Missing parent, asking for help.") } else { // some other error @@ -160,7 +164,7 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { if newHead := bcm.getHead(); newHead != currentHead { // new head println("New longest chain - head changed!") - tpmpbdsl.NewHead(*bcm.m, "tpm", newHead.BlockId) + minerpbdsl.NewHead(*bcm.m, "miner", newHead.BlockId) } } @@ -183,7 +187,7 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { if newHead := bcm.getHead(); newHead != currentHead { // new head println("New longest chain - head changed!") - tpmpbdsl.NewHead(*bcm.m, "tpm", newHead.BlockId) + minerpbdsl.NewHead(*bcm.m, "miner", newHead.BlockId) } } @@ -203,9 +207,10 @@ func NewBCM(chainServerPort int) modules.PassiveModule { hash := hashBlock(genesis) genesis.BlockId = hash + genesisBcm := bcmBlock{block: genesis, parent: nil, depth: 0} // init bcm with genisis block - bcm = bcmModule{m: &m, leaves: make(map[uint64]*bcmBlock), head: &bcmBlock{block: genesis, parent: nil, depth: 0}, blockCount: 1} + bcm = bcmModule{m: &m, blocks: []bcmBlock{genesisBcm}, leaves: make(map[uint64]*bcmBlock), head: &genesisBcm, genesis: &genesisBcm, blockCount: 1} bcm.leaves[hash] = bcm.head // init mutex @@ -218,14 +223,13 @@ func NewBCM(chainServerPort int) modules.PassiveModule { go bcm.chainServer(chainServerPort) // kick off tpm with genisis block - tpmpbdsl.NewHead(m, "tpm", hash) + minerpbdsl.NewHead(m, "miner", hash) return nil }) bcmpbdsl.UponNewBlock(m, func(block *blockchainpb.Block) error { // self-mined block - no need to verify - println("~ Received block from miner") bcm.handleNewBlock(block) return nil }) @@ -242,7 +246,7 @@ func NewBCM(chainServerPort int) modules.PassiveModule { hit := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { // check if curr matches the block to be added - if so, ignore if currBlock.block.BlockId == blockID { - println("Found block in tree") + println("Found block in tree -", blockID) // respond to sync request return true, nil } @@ -251,8 +255,9 @@ func NewBCM(chainServerPort int) modules.PassiveModule { }) if hit == nil { - println("Couldn't find block") + println("Couldn't find block -", blockID) bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) + return nil } bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block) @@ -270,7 +275,7 @@ func (bcm *bcmModule) chainServer(port int) error { go websocket.StartServers() for { - _ = <-bcm.updateChan + <-bcm.updateChan bcm.writeMutex.Lock() blocks := func() []*blockchainpb.Block { @@ -290,8 +295,6 @@ func (bcm *bcmModule) chainServer(port int) error { bcm.writeMutex.Unlock() - fmt.Printf("%d BLOCK COUNT @@@@@@@@\n", len(blocks)) - blockTreeMessage := blockchainpb.Blocktree{Blocks: blocks, Leaves: leaves} payload, err := proto.Marshal(&blockTreeMessage) if err != nil { diff --git a/samples/blockchain/communication.go b/samples/blockchain/communication.go index fbd1ab8d5..df615e11b 100644 --- a/samples/blockchain/communication.go +++ b/samples/blockchain/communication.go @@ -3,10 +3,6 @@ package main import ( - "math/rand" - - "log" - "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/pb/blockchainpb" @@ -17,12 +13,7 @@ import ( t "github.com/filecoin-project/mir/pkg/types" ) -/* Note on simulated message loss and similar: - * Although package loss is very easy to simulate here, it might be more appropriate to use the event mangler. - * Not sure about this yet but something to keep in mind later - */ - -func NewCommunication(otherNodes []t.NodeID, probabilityMessageLost float32) modules.PassiveModule { +func NewCommunication(otherNodes []t.NodeID, mangle bool) modules.PassiveModule { m := dsl.NewModule("communication") dsl.UponInit(m, func() error { @@ -31,31 +22,16 @@ func NewCommunication(otherNodes []t.NodeID, probabilityMessageLost float32) mod communicationpbdsl.UponNewBlock(m, func(block *blockchainpb.Block) error { // take the block and send it to all other nodes - // could add so randomization here - only send to a subset of nodes - // simulate message loss - receivers := make([]t.NodeID, 0, len(otherNodes)) - if probabilityMessageLost == 0 { - receivers = otherNodes - } else { + if mangle { + // send via mangles for _, node := range otherNodes { - if probabilityMessageLost > 1 && rand.Float32() < probabilityMessageLost { - receivers = append(receivers, node) - } + transportpbdsl.SendMessage(m, "mangler", communicationpbmsgs.NewBlockMessage("communication", block), []t.NodeID{node}) } - } - - if len(receivers) == 0 { - log.Printf("Block %d send to NO nodes", block.BlockId) - return nil - } else if len(receivers) == len(otherNodes) { - log.Printf("Block %d send to all nodes", block.BlockId) } else { - log.Printf("Block %d send to %d/%d nodes", block.BlockId, len(receivers), len(otherNodes)) + transportpbdsl.SendMessage(m, "transport", communicationpbmsgs.NewBlockMessage("communication", block), otherNodes) } - transportpbdsl.SendMessage(m, "transport", communicationpbmsgs.NewBlockMessage("communication", block), receivers) - return nil }) diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index 46be220d4..632977873 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -5,12 +5,15 @@ import ( "fmt" "os" "strconv" + "time" "github.com/filecoin-project/mir" + "github.com/filecoin-project/mir/pkg/eventmangler" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/net/grpc" trantorpbtypes "github.com/filecoin-project/mir/pkg/pb/trantorpb/types" + "github.com/filecoin-project/mir/pkg/timer" t "github.com/filecoin-project/mir/pkg/types" ) @@ -28,11 +31,9 @@ func main() { } ownNodeID := t.NodeID(idInput) - msgLossProb := 0.0 - if len(os.Args) <= 3 { - fmt.Printf("No message loss probability provided, defaulting to 0.0\n") - } else { - msgLossProb, err = strconv.ParseFloat(os.Args[3], 32) + mangle := false + if len(os.Args) >= 4 { + mangle, err = strconv.ParseBool(os.Args[3]) if err != nil { panic(err) } @@ -64,6 +65,16 @@ func main() { } transport.Connect(membership) + timer := timer.New() + + mangler, err := eventmangler.NewModule( + eventmangler.ModuleConfig{Self: "mangler", Dest: "transport", Timer: "timer"}, + &eventmangler.ModuleParams{MinDelay: time.Second / 100, MaxDelay: 2 * time.Second, DropRate: 0.1}, + ) + if err != nil { + panic(err) + } + // Instantiate Mir node. node, err := mir.NewNode( ownNodeID, @@ -72,8 +83,11 @@ func main() { "transport": transport, "bcm": NewBCM(8080 + ownID), "miner": NewMiner(), - "communication": NewCommunication(otherNodes, float32(msgLossProb)), + "communication": NewCommunication(otherNodes, mangle), "tpm": NewTPM(), + "synchronizer": NewSynchronizer(otherNodes, false), + "timer": timer, + "mangler": mangler, }, nil, ) diff --git a/samples/blockchain/miner.go b/samples/blockchain/miner.go index 15bee577c..895c6539b 100644 --- a/samples/blockchain/miner.go +++ b/samples/blockchain/miner.go @@ -11,11 +11,16 @@ import ( bcmpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/events" communicationpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/events" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" + tpmpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/events" "github.com/filecoin-project/mir/pkg/pb/eventpb" "github.com/go-errors/errors" "github.com/mitchellh/hashstructure" ) +const ( + EXPONENTIAL_MINUTE_FACTOR = 0.3 +) + type blockRequest struct { HeadId uint64 Payload *blockchainpb.Payload @@ -39,16 +44,18 @@ func (m *minerModule) EventsOut() <-chan *events.EventList { return m.eventsOut } -func (m *minerModule) ApplyEvents(context context.Context, events *events.EventList) error { - for _, event := range events.Slice() { +func (m *minerModule) ApplyEvents(context context.Context, eventList *events.EventList) error { + for _, event := range eventList.Slice() { switch e := event.Type.(type) { case *eventpb.Event_Init: go m.mineWorkerManager() case *eventpb.Event_Miner: switch e := e.Miner.Type.(type) { case *minerpb.Event_BlockRequest: - m.blockRequests <- blockRequest{e.BlockRequest.HeadId, e.BlockRequest.Payload} + m.blockRequests <- blockRequest{e.BlockRequest.GetHeadId(), e.BlockRequest.GetPayload()} return nil + case *minerpb.Event_NewHead: + m.eventsOut <- events.ListOf(tpmpbevents.NewBlockRequest("tpm", e.NewHead.GetHeadId()).Pb()) default: return errors.Errorf("unknown miner event: %T", e) } @@ -69,7 +76,7 @@ func (m *minerModule) mineWorkerManager() { ctx, cancel = context.WithCancel(context.Background()) // new context for new mining println("Mining block on top of", blockRequest.HeadId) go func() { - delay := time.Duration(rand.ExpFloat64() * float64(time.Minute)) + delay := time.Duration(rand.ExpFloat64() * float64(time.Minute) * EXPONENTIAL_MINUTE_FACTOR) select { case <-ctx.Done(): println("##### Mining aborted #####") diff --git a/samples/blockchain/synchronizer.go b/samples/blockchain/synchronizer.go index 3e9698639..acae6db4a 100644 --- a/samples/blockchain/synchronizer.go +++ b/samples/blockchain/synchronizer.go @@ -34,8 +34,9 @@ type synchronizerModule struct { m *dsl.Module syncRequests map[uint64]*syncRequest getRequests map[uint64]*getRequest - otherNodes []*t.NodeID // we remove nodes that we presume have crashed (see strikeList) // TODO: add a timeout to 're-add' them? exponential backoff? + otherNodes []t.NodeID // we remove nodes that we presume have crashed (see strikeList) // TODO: add a timeout to 're-add' them? exponential backoff? // stikeList map[t.NodeID]int // a node gets a strike whenever it failed to handle a request, after STRIKE_THRESHOLD strikes it is removed from the otherNodes. Set to 0 if it handles a request successfully + mangle bool } /************************* @@ -50,15 +51,14 @@ type syncRequest struct { chain []*blockchainpb.Block } -func (sm *synchronizerModule) registerSyncRequest(blockID uint64) (uint64, error) { - requestId := blockID - +func (sm *synchronizerModule) registerSyncRequest(block *blockchainpb.Block, leaves []uint64) (uint64, error) { + requestId := block.BlockId // check if request already exists if _, ok := sm.syncRequests[requestId]; ok { return 0, ErrRequestAlreadyExists } - sm.syncRequests[requestId] = &syncRequest{requestID: requestId, blockID: blockID, nodesContacted: []int{}} + sm.syncRequests[requestId] = &syncRequest{requestID: requestId, blockID: block.BlockId, leaveIDs: leaves, nodesContacted: []int{}, chain: []*blockchainpb.Block{block}} return requestId, nil } @@ -80,19 +80,38 @@ func (sm *synchronizerModule) contactNextNode(requestID uint64) error { } else { // get next node // NOTE: stupid to keep an array of indices but it's more flexible in case I want to change the way I pick the next node - nextNodeIndex = (request.nodesContacted[len(request.nodesContacted)] + 1) % len(sm.otherNodes) + nextNodeIndex = (request.nodesContacted[len(request.nodesContacted)-1] + 1) % len(sm.otherNodes) } request.nodesContacted = append(request.nodesContacted, nextNodeIndex) - nextNode := *sm.otherNodes[nextNodeIndex] + nextNode := sm.otherNodes[nextNodeIndex] + println("asking node", nextNode, "for block", request.blockID) // send request - transportpbdsl.SendMessage(*sm.m, "synchronizer", synchronizerpbmsgs.BlockRequest("synchronizer", requestID, request.blockID), []t.NodeID{nextNode}) + if sm.mangle { + transportpbdsl.SendMessage(*sm.m, "mangler", synchronizerpbmsgs.BlockRequest("synchronizer", requestID, request.blockID), []t.NodeID{nextNode}) + } else { + transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.BlockRequest("synchronizer", requestID, request.blockID), []t.NodeID{nextNode}) + } return nil } -func (sm *synchronizerModule) handleSyncRequest(orphanBlockId uint64, leaveIds uint64) error { +func (sm *synchronizerModule) handleSyncRequest(orphanBlock *blockchainpb.Block, leaveIds []uint64) error { + + // register request + requestId, err := sm.registerSyncRequest(orphanBlock, leaveIds) + if err != nil { + println("sync registration failed", err.Error()) + return err + } + + if err := sm.contactNextNode(requestId); err != nil { + // could check for 'ErrNoMoreNodes' here but there should always be at least one node + println("contacting next node failed", err.Error()) + return err + } + return nil } @@ -107,13 +126,20 @@ func (sm *synchronizerModule) handleBlockResponseReceived(from t.NodeID, request if !found { // check whether the response came fromt he last node we contacted // NOTE: this is getting messy - if *sm.otherNodes[request.nodesContacted[len(request.nodesContacted)-1]] != from { + if sm.otherNodes[request.nodesContacted[len(request.nodesContacted)-1]] != from { // there is still a request out in the ether - don't send another one return nil } // send request to the next node - sm.contactNextNode(requestID) + if err := sm.contactNextNode(requestID); err == ErrNoMoreNodes { + println("No more nodes to contact... Let's forget about this request", request.blockID) + panic(err) // NOTE: this should never happen as long as we don't start mangling + } else if err != nil { + println("contacting next node failed", err.Error()) + return err + } + return nil } @@ -170,7 +196,11 @@ func (sm *synchronizerModule) handleGetBlockResponse(requestID uint64, found boo } // respond to sync request - transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.BlockResponse("synchronizer", requestID, found, block), []t.NodeID{request.from}) + if sm.mangle { + transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.BlockResponse("synchronizer", requestID, found, block), []t.NodeID{request.from}) + } else { + transportpbdsl.SendMessage(*sm.m, "mangler", synchronizerpbmsgs.BlockResponse("synchronizer", requestID, found, block), []t.NodeID{request.from}) + } // delete request delete(sm.getRequests, requestID) @@ -178,9 +208,15 @@ func (sm *synchronizerModule) handleGetBlockResponse(requestID uint64, found boo return nil } -func NewSynchronizer(otherNodes []*t.NodeID) modules.PassiveModule { +func NewSynchronizer(otherNodes []t.NodeID, mangle bool) modules.PassiveModule { m := dsl.NewModule("synchronizer") - var sm synchronizerModule + var sm = synchronizerModule{ + m: &m, + getRequests: make(map[uint64]*getRequest), + syncRequests: make(map[uint64]*syncRequest), + otherNodes: otherNodes, + mangle: mangle, + } dsl.UponInit(m, func() error { return nil diff --git a/samples/blockchain/tpm.go b/samples/blockchain/tpm.go index 34399f01f..60497148c 100644 --- a/samples/blockchain/tpm.go +++ b/samples/blockchain/tpm.go @@ -23,7 +23,7 @@ func NewTPM() modules.PassiveModule { return nil }) - tpmpb.UponNewHead(m, func(headId uint64) error { + tpmpb.UponNewBlockRequest(m, func(headId uint64) error { // just generating random payloads for now // generate random string println("Generating random payload...") From 4a001c5a7497a767f129f9ca0c3f535d33a33671 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Thu, 16 Nov 2023 12:52:25 +0100 Subject: [PATCH 04/46] blockchain logging --- samples/blockchain/bcm.go | 91 ++++++++++++++++------------- samples/blockchain/communication.go | 8 ++- samples/blockchain/main.go | 14 +++-- samples/blockchain/miner.go | 13 +++-- samples/blockchain/synchronizer.go | 39 ++++++++----- samples/blockchain/tpm.go | 5 +- samples/blockchain/utils.go | 19 ++++++ 7 files changed, 120 insertions(+), 69 deletions(-) diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 4a77b6141..b6c977b3a 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -9,6 +9,7 @@ import ( "sync" "github.com/filecoin-project/mir/pkg/dsl" + "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" @@ -41,6 +42,7 @@ type bcmModule struct { blockCount uint64 writeMutex *sync.Mutex // can be smarted about locking but just locking everything for now updateChan chan string + logger logging.Logger } // traversalFunc should return true if traversal should continue, false otherwise @@ -81,11 +83,11 @@ func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) // TODO: might need to add some "cleanup" to handle very old leaves in case this grows too much // function assumes lock is held (is this even necessary? can a mir module process multiple events at once?) func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { - println("Adding block...", block.BlockId) + bcm.logger.Log(logging.LevelInfo, "Adding block...", "blockId", formatBlockId(block.BlockId)) // check if block is already in the leaves, reject if so if _, ok := bcm.leaves[block.BlockId]; ok { - println("Block already in leaves - ignore") + bcm.logger.Log(logging.LevelDebug, "Block already in leaves - ignore", "blockId", formatBlockId(block.BlockId)) return nil } @@ -93,7 +95,7 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { // check leaves for parent with its id // return nil if it isn't there if parent, ok := bcm.leaves[parentId]; ok { - println("Found parent in leaves") + bcm.logger.Log(logging.LevelDebug, "Found parend in leaves", "blockId", formatBlockId(block.BlockId), "parentId", formatBlockId(parentId)) blockNode := bcmBlock{block: block, parent: parent, depth: parent.depth + 1} bcm.blocks = append(bcm.blocks, blockNode) // replace leave @@ -109,13 +111,13 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { if hit := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { // check if curr matches the block to be added - if so, ignore if currBlock.block.BlockId == block.BlockId { - println("Block already in tree - ignore") + bcm.logger.Log(logging.LevelDebug, "Block already in tree", "blockId", formatBlockId(block.BlockId)) return false, ErrNodeAlreadyInTree } // check if curr is parent if currBlock.block.BlockId == parentId { - println("Found parent in tree") + bcm.logger.Log(logging.LevelDebug, "Found parend in tree", "blockId", formatBlockId(block.BlockId), "parentId", formatBlockId(parentId)) blockNode := bcmBlock{block: block, parent: currBlock, depth: currBlock.depth + 1, scanCount: 0} // add new leave bcm.leaves[block.BlockId] = &blockNode @@ -129,7 +131,7 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { return false, nil }); hit == nil { - println("Couldn't find parent - asking around...") + bcm.logger.Log(logging.LevelInfo, "Couldn't find parent", "blockId", formatBlockId(block.BlockId)) return ErrParentNotFound } @@ -149,11 +151,12 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { if err := bcm.addBlock(block); err != nil { if err == ErrParentNotFound { // ask synchronizer for help - synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", block, append(maps.Keys(bcm.leaves), bcm.genesis.block.BlockId)) - println("Missing parent, asking for help.") + leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.BlockId) + bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", formatBlockId(block.BlockId), "leaves+genesis", formatBlockIdSlice(leavesPlusGenesis)) + synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", block, leavesPlusGenesis) } else { // some other error - println(err) + bcm.logger.Log(logging.LevelError, "Unexpected error adding block", "blockId", formatBlockId(block.BlockId), "error", err) } } @@ -163,7 +166,7 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { // if head changed, trigger get tpm to prep new block if newHead := bcm.getHead(); newHead != currentHead { // new head - println("New longest chain - head changed!") + bcm.logger.Log(logging.LevelInfo, "Head changed", "head", formatBlockId(newHead.BlockId)) minerpbdsl.NewHead(*bcm.m, "miner", newHead.BlockId) } } @@ -176,7 +179,7 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { // insert block for _, block := range blocks { if err := bcm.addBlock(block); err != nil { - println(err) + bcm.logger.Log(logging.LevelError, "Unexpected error adding block from chain", "blockId", formatBlockId(block.BlockId), "error", err) } } @@ -186,41 +189,51 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { // if head changed, trigger get tpm to prep new block if newHead := bcm.getHead(); newHead != currentHead { // new head - println("New longest chain - head changed!") + + bcm.logger.Log(logging.LevelInfo, "Head changed", "head", formatBlockId(newHead.BlockId)) minerpbdsl.NewHead(*bcm.m, "miner", newHead.BlockId) } } -func NewBCM(chainServerPort int) modules.PassiveModule { +func NewBCM(chainServerPort int, logger logging.Logger) modules.PassiveModule { m := dsl.NewModule("bcm") - var bcm bcmModule - dsl.UponInit(m, func() error { + // making up a genisis block + genesis := &blockchainpb.Block{ + BlockId: 0, + PreviousBlockId: 0, + Payload: &blockchainpb.Payload{Text: "genesis"}, + Timestamp: 0, // unix 0 + } - // making up a genisis block - genesis := &blockchainpb.Block{ - BlockId: 0, - PreviousBlockId: 0, - Payload: &blockchainpb.Payload{Text: "genesis"}, - Timestamp: 0, // unix 0 - } + hash := hashBlock(genesis) + genesis.BlockId = hash + genesisBcm := bcmBlock{block: genesis, parent: nil, depth: 0} + + // init bcm genisis block + bcm := bcmModule{ + m: &m, + blocks: []bcmBlock{genesisBcm}, + leaves: make(map[uint64]*bcmBlock), + head: &genesisBcm, + genesis: &genesisBcm, + blockCount: 1, + logger: logger, + } - hash := hashBlock(genesis) - genesis.BlockId = hash - genesisBcm := bcmBlock{block: genesis, parent: nil, depth: 0} + // add genesis to leaves + bcm.leaves[hash] = bcm.head - // init bcm with genisis block - bcm = bcmModule{m: &m, blocks: []bcmBlock{genesisBcm}, leaves: make(map[uint64]*bcmBlock), head: &genesisBcm, genesis: &genesisBcm, blockCount: 1} - bcm.leaves[hash] = bcm.head + // init mutex + bcm.writeMutex = &sync.Mutex{} - // init mutex - bcm.writeMutex = &sync.Mutex{} + // setup update channel + bcm.updateChan = make(chan string) - // setup update channel - bcm.updateChan = make(chan string) + // start chain server (for visualization) + go bcm.chainServer(chainServerPort) - // start chain server (for visualization) - go bcm.chainServer(chainServerPort) + dsl.UponInit(m, func() error { // kick off tpm with genisis block minerpbdsl.NewHead(m, "miner", hash) @@ -235,18 +248,18 @@ func NewBCM(chainServerPort int) modules.PassiveModule { }) bcmpbdsl.UponNewChain(m, func(blocks []*blockchainpb.Block) error { - // self-mined block - no need to verify - println("~ Received chain from synchronizer") + bcm.logger.Log(logging.LevelInfo, "Received chain from synchronizer") bcm.handleNewChain(blocks) return nil }) bcmpbdsl.UponGetBlockRequest(m, func(requestID uint64, sourceModule t.ModuleID, blockID uint64) error { + bcm.logger.Log(logging.LevelInfo, "Received get block request", "requestId", formatBlockId(requestID), "sourceModule", sourceModule) // check if block is in tree hit := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { // check if curr matches the block to be added - if so, ignore if currBlock.block.BlockId == blockID { - println("Found block in tree -", blockID) + bcm.logger.Log(logging.LevelDebug, "Found block in tree", "requestId", formatBlockId(requestID)) // respond to sync request return true, nil } @@ -255,7 +268,7 @@ func NewBCM(chainServerPort int) modules.PassiveModule { }) if hit == nil { - println("Couldn't find block -", blockID) + bcm.logger.Log(logging.LevelDebug, "Block not found", "requestId", formatBlockId(requestID)) bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) return nil } @@ -269,7 +282,7 @@ func NewBCM(chainServerPort int) modules.PassiveModule { } func (bcm *bcmModule) chainServer(port int) error { - fmt.Println("Starting chain server") + bcm.logger.Log(logging.LevelInfo, "Starting chain server") websocket, send, _ := ws.NewWsServer(port) go websocket.StartServers() diff --git a/samples/blockchain/communication.go b/samples/blockchain/communication.go index df615e11b..f6d1a7708 100644 --- a/samples/blockchain/communication.go +++ b/samples/blockchain/communication.go @@ -4,6 +4,7 @@ package main import ( "github.com/filecoin-project/mir/pkg/dsl" + "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" @@ -13,7 +14,7 @@ import ( t "github.com/filecoin-project/mir/pkg/types" ) -func NewCommunication(otherNodes []t.NodeID, mangle bool) modules.PassiveModule { +func NewCommunication(otherNodes []t.NodeID, mangle bool, logger logging.Logger) modules.PassiveModule { m := dsl.NewModule("communication") dsl.UponInit(m, func() error { @@ -23,6 +24,8 @@ func NewCommunication(otherNodes []t.NodeID, mangle bool) modules.PassiveModule communicationpbdsl.UponNewBlock(m, func(block *blockchainpb.Block) error { // take the block and send it to all other nodes + logger.Log(logging.LevelDebug, "broadcasting block", "blockId", formatBlockId(block.BlockId), "manlge", mangle) + if mangle { // send via mangles for _, node := range otherNodes { @@ -36,8 +39,7 @@ func NewCommunication(otherNodes []t.NodeID, mangle bool) modules.PassiveModule }) communicationpbdsl.UponNewBlockMessageReceived(m, func(from t.NodeID, block *blockchainpb.Block) error { - // take the block and add it to the blockchain - // could add some randomization here - delay/drop (drop implemented in send) + logger.Log(logging.LevelDebug, "new block received", "blockId", formatBlockId(block.BlockId)) bcmpbdsl.NewBlock(m, "bcm", block) return nil diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index 632977873..4250f62a8 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -20,6 +20,8 @@ import ( func main() { fmt.Println("Starting blockchain") + logger := logging.ConsoleDebugLogger + numberOfNodes, err := strconv.Atoi(os.Args[1]) if err != nil { panic(err) @@ -56,7 +58,7 @@ func main() { membership := &trantorpbtypes.Membership{Nodes: nodes} // Instantiate network trnasport module and establish connections. - transport, err := grpc.NewTransport(ownNodeID, membership.Nodes[ownNodeID].Addr, logging.ConsoleWarnLogger) + transport, err := grpc.NewTransport(ownNodeID, membership.Nodes[ownNodeID].Addr, logger) if err != nil { panic(err) } @@ -81,11 +83,11 @@ func main() { mir.DefaultNodeConfig(), map[t.ModuleID]modules.Module{ "transport": transport, - "bcm": NewBCM(8080 + ownID), - "miner": NewMiner(), - "communication": NewCommunication(otherNodes, mangle), - "tpm": NewTPM(), - "synchronizer": NewSynchronizer(otherNodes, false), + "bcm": NewBCM(8080+ownID, logging.Decorate(logger, "BCM:\t")), + "miner": NewMiner(logging.Decorate(logger, "Miner:\t")), + "communication": NewCommunication(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), + "tpm": NewTPM(logging.Decorate(logger, "TPM:\t")), + "synchronizer": NewSynchronizer(otherNodes, false, logging.Decorate(logger, "Sync:\t")), "timer": timer, "mangler": mangler, }, diff --git a/samples/blockchain/miner.go b/samples/blockchain/miner.go index 895c6539b..5ab96232f 100644 --- a/samples/blockchain/miner.go +++ b/samples/blockchain/miner.go @@ -6,6 +6,7 @@ import ( "time" "github.com/filecoin-project/mir/pkg/events" + "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/events" @@ -29,12 +30,14 @@ type blockRequest struct { type minerModule struct { blockRequests chan blockRequest eventsOut chan *events.EventList + logger logging.Logger } -func NewMiner() modules.ActiveModule { +func NewMiner(logger logging.Logger) modules.ActiveModule { return &minerModule{ blockRequests: make(chan blockRequest), eventsOut: make(chan *events.EventList), + logger: logger, } } @@ -74,24 +77,24 @@ func (m *minerModule) mineWorkerManager() { blockRequest := <-m.blockRequests cancel() // abort ongoing mining ctx, cancel = context.WithCancel(context.Background()) // new context for new mining - println("Mining block on top of", blockRequest.HeadId) + m.logger.Log(logging.LevelInfo, "New mining operation", "headId", formatBlockId(blockRequest.HeadId)) go func() { delay := time.Duration(rand.ExpFloat64() * float64(time.Minute) * EXPONENTIAL_MINUTE_FACTOR) select { case <-ctx.Done(): - println("##### Mining aborted #####") + m.logger.Log(logging.LevelDebug, "Mining aborted", "headId", formatBlockId(blockRequest.HeadId)) return case <-time.After(delay): - println("Block mined!") block := &blockchainpb.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: time.Now().Unix()} hash, err := hashstructure.Hash(block, nil) if err != nil { + m.logger.Log(logging.LevelError, "Failed to hash block", "error", err) panic(err) } block.BlockId = hash // Block mined! Broadcast to blockchain and send event to bcm. + m.logger.Log(logging.LevelInfo, "Block mined", "headId", formatBlockId(hash), "parentId", formatBlockId(blockRequest.HeadId)) m.eventsOut <- events.ListOf(bcmpbevents.NewBlock("bcm", block).Pb(), communicationpbevents.NewBlock("communication", block).Pb()) - println("Block shared!") return } }() diff --git a/samples/blockchain/synchronizer.go b/samples/blockchain/synchronizer.go index acae6db4a..77e0112d5 100644 --- a/samples/blockchain/synchronizer.go +++ b/samples/blockchain/synchronizer.go @@ -7,6 +7,7 @@ import ( "math/rand" "github.com/filecoin-project/mir/pkg/dsl" + "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" @@ -36,7 +37,9 @@ type synchronizerModule struct { getRequests map[uint64]*getRequest otherNodes []t.NodeID // we remove nodes that we presume have crashed (see strikeList) // TODO: add a timeout to 're-add' them? exponential backoff? // stikeList map[t.NodeID]int // a node gets a strike whenever it failed to handle a request, after STRIKE_THRESHOLD strikes it is removed from the otherNodes. Set to 0 if it handles a request successfully - mangle bool + mangle bool + internalLogger logging.Logger + externaLogger logging.Logger } /************************* @@ -86,7 +89,7 @@ func (sm *synchronizerModule) contactNextNode(requestID uint64) error { request.nodesContacted = append(request.nodesContacted, nextNodeIndex) nextNode := sm.otherNodes[nextNodeIndex] - println("asking node", nextNode, "for block", request.blockID) + sm.internalLogger.Log(logging.LevelDebug, "asking node for block", "block", formatBlockId(request.blockID), "node", nextNode, "mangle", sm.mangle) // send request if sm.mangle { transportpbdsl.SendMessage(*sm.m, "mangler", synchronizerpbmsgs.BlockRequest("synchronizer", requestID, request.blockID), []t.NodeID{nextNode}) @@ -102,13 +105,13 @@ func (sm *synchronizerModule) handleSyncRequest(orphanBlock *blockchainpb.Block, // register request requestId, err := sm.registerSyncRequest(orphanBlock, leaveIds) if err != nil { - println("sync registration failed", err.Error()) + sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", formatBlockId(orphanBlock.BlockId), "leaveIds", formatBlockIdSlice(leaveIds)) return err } if err := sm.contactNextNode(requestId); err != nil { // could check for 'ErrNoMoreNodes' here but there should always be at least one node - println("contacting next node failed", err.Error()) + sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", formatBlockId(orphanBlock.BlockId), "leaveIds", formatBlockIdSlice(leaveIds)) return err } @@ -133,11 +136,14 @@ func (sm *synchronizerModule) handleBlockResponseReceived(from t.NodeID, request } // send request to the next node if err := sm.contactNextNode(requestID); err == ErrNoMoreNodes { - println("No more nodes to contact... Let's forget about this request", request.blockID) - panic(err) // NOTE: this should never happen as long as we don't start mangling + sm.internalLogger.Log(logging.LevelError, "no more nodes to contact - forgetting request - shouldn't happen if not mangling", "error", err.Error(), "requestId", formatBlockId(requestID), "mangle", sm.mangle) + delete(sm.syncRequests, requestID) + if sm.mangle { + panic(err) // NOTE: this should never happen as long as we don't start mangling + } } else if err != nil { - println("contacting next node failed", err.Error()) - return err + sm.internalLogger.Log(logging.LevelError, "Unexpected error contacting next node", "error", err.Error(), "requestId", formatBlockId(requestID), "mangle", sm.mangle) + panic(err) } return nil @@ -192,9 +198,12 @@ func (sm *synchronizerModule) handleBlockRequestReceived(from t.NodeID, requestI func (sm *synchronizerModule) handleGetBlockResponse(requestID uint64, found bool, block *blockchainpb.Block) error { request, ok := sm.getRequests[requestID] if !ok { + sm.externaLogger.Log(logging.LevelError, "Unknown get block request", "requestId", formatBlockId(requestID), "mangle", sm.mangle) return ErrUnkownGetBlockRequest } + sm.externaLogger.Log(logging.LevelInfo, "Responsing to block request", "requestId", formatBlockId(requestID), "found", found, "mangle", sm.mangle) + // respond to sync request if sm.mangle { transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.BlockResponse("synchronizer", requestID, found, block), []t.NodeID{request.from}) @@ -208,14 +217,16 @@ func (sm *synchronizerModule) handleGetBlockResponse(requestID uint64, found boo return nil } -func NewSynchronizer(otherNodes []t.NodeID, mangle bool) modules.PassiveModule { +func NewSynchronizer(otherNodes []t.NodeID, mangle bool, logger logging.Logger) modules.PassiveModule { m := dsl.NewModule("synchronizer") var sm = synchronizerModule{ - m: &m, - getRequests: make(map[uint64]*getRequest), - syncRequests: make(map[uint64]*syncRequest), - otherNodes: otherNodes, - mangle: mangle, + m: &m, + getRequests: make(map[uint64]*getRequest), + syncRequests: make(map[uint64]*syncRequest), + otherNodes: otherNodes, + mangle: mangle, + internalLogger: logging.Decorate(logger, "Intern - "), + externaLogger: logging.Decorate(logger, "External - "), } dsl.UponInit(m, func() error { diff --git a/samples/blockchain/tpm.go b/samples/blockchain/tpm.go index 60497148c..bfa43f5b6 100644 --- a/samples/blockchain/tpm.go +++ b/samples/blockchain/tpm.go @@ -7,6 +7,7 @@ import ( "time" "github.com/filecoin-project/mir/pkg/dsl" + "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/pb/blockchainpb" minerdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" @@ -15,7 +16,7 @@ import ( const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" -func NewTPM() modules.PassiveModule { +func NewTPM(logger logging.Logger) modules.PassiveModule { m := dsl.NewModule("tpm") @@ -26,7 +27,7 @@ func NewTPM() modules.PassiveModule { tpmpb.UponNewBlockRequest(m, func(headId uint64) error { // just generating random payloads for now // generate random string - println("Generating random payload...") + logger.Log(logging.LevelInfo, "Processing block request", "headId", formatBlockId(headId)) var seed *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) length := seed.Intn(15) b := make([]byte, length) diff --git a/samples/blockchain/utils.go b/samples/blockchain/utils.go index a52179fee..9feee5b7c 100644 --- a/samples/blockchain/utils.go +++ b/samples/blockchain/utils.go @@ -1,6 +1,8 @@ package main import ( + "fmt" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb" "github.com/mitchellh/hashstructure" ) @@ -13,3 +15,20 @@ func hashBlock(block *blockchainpb.Block) uint64 { } return hash } + +func formatBlockId(blockId uint64) string { + strId := fmt.Sprint(blockId) + // just in case we have a very small id for some reason + if len(strId) > 6 { + strId = strId[:6] + } + return strId +} + +func formatBlockIdSlice(blockIds []uint64) string { + str := "" + for _, blockId := range blockIds { + str += formatBlockId(blockId) + " " + } + return str +} From 486d1b9d770611eaa44e0256aae501ee17afbb8d Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Thu, 16 Nov 2023 14:32:20 +0100 Subject: [PATCH 05/46] drop not needed synchronization --- samples/blockchain/bcm.go | 38 -------------------------------------- 1 file changed, 38 deletions(-) diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index b6c977b3a..5db527c59 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -4,9 +4,7 @@ package main import ( "errors" - "fmt" "strconv" - "sync" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" @@ -40,7 +38,6 @@ type bcmModule struct { head *bcmBlock genesis *bcmBlock blockCount uint64 - writeMutex *sync.Mutex // can be smarted about locking but just locking everything for now updateChan chan string logger logging.Logger } @@ -81,7 +78,6 @@ func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) } // TODO: might need to add some "cleanup" to handle very old leaves in case this grows too much -// function assumes lock is held (is this even necessary? can a mir module process multiple events at once?) func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { bcm.logger.Log(logging.LevelInfo, "Adding block...", "blockId", formatBlockId(block.BlockId)) @@ -143,9 +139,6 @@ func (bcm *bcmModule) getHead() *blockchainpb.Block { } func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { - bcm.writeMutex.Lock() - defer bcm.writeMutex.Unlock() - currentHead := bcm.getHead() // insert block if err := bcm.addBlock(block); err != nil { @@ -172,8 +165,6 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { } func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { - bcm.writeMutex.Lock() - defer bcm.writeMutex.Unlock() currentHead := bcm.getHead() // insert block @@ -224,9 +215,6 @@ func NewBCM(chainServerPort int, logger logging.Logger) modules.PassiveModule { // add genesis to leaves bcm.leaves[hash] = bcm.head - // init mutex - bcm.writeMutex = &sync.Mutex{} - // setup update channel bcm.updateChan = make(chan string) @@ -290,7 +278,6 @@ func (bcm *bcmModule) chainServer(port int) error { for { <-bcm.updateChan - bcm.writeMutex.Lock() blocks := func() []*blockchainpb.Block { blocks := make([]*blockchainpb.Block, 0, len(bcm.blocks)) for _, v := range bcm.blocks { @@ -306,8 +293,6 @@ func (bcm *bcmModule) chainServer(port int) error { return leaves }() - bcm.writeMutex.Unlock() - blockTreeMessage := blockchainpb.Blocktree{Blocks: blocks, Leaves: leaves} payload, err := proto.Marshal(&blockTreeMessage) if err != nil { @@ -317,28 +302,5 @@ func (bcm *bcmModule) chainServer(port int) error { send <- ws.WsMessage{MessageType: 2, Payload: payload} } - // for { - // _ = <-bcm.updateChan - // bcm.writeMutex.Lock() - // fmt.Println("new print") - // queue := make([]bcmBlock, 0) - // for _, v := range bcm.leaves { - // queue = append(queue, v) - // } - // for len(queue) > 0 { - // // pop from queue - // curr := queue[0] - // queue = queue[1:] - - // fmt.Println(curr.block.BlockId) - - // if curr.parent != nil { - // queue = append(queue, *curr.parent) - // } - // } - // bcm.writeMutex.Unlock() - // } - fmt.Println("Chain server stopped") - return nil } From 72d7c1e17f7ac59b8e0104ecb8bb8d4f9eeb5047 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Thu, 16 Nov 2023 22:20:23 +0100 Subject: [PATCH 06/46] change ws server to interceptor --- .../interceptorpb/dsl/emit.mir.go | 20 + .../interceptorpb/dsl/upon.mir.go | 35 ++ .../interceptorpb/events/events.mir.go | 41 +++ .../interceptorpb/interceptorpb.pb.go | 342 ++++++++++++++++++ .../interceptorpb/interceptorpb.pb.mir.go | 14 + .../interceptorpb/oneof_interfaces.mir.go | 18 + .../interceptorpb/types/types.mir.go | 178 +++++++++ pkg/pb/eventpb/eventpb.pb.go | 200 +++++----- pkg/pb/eventpb/eventpb.pb.mir.go | 1 + pkg/pb/eventpb/oneof_interfaces.mir.go | 5 + pkg/pb/eventpb/types/types.mir.go | 69 ++-- .../interceptorpb/interceptorpb.proto | 32 ++ protos/eventpb/eventpb.proto | 4 + protos/generate.go | 2 + samples/blockchain/bcm.go | 81 ++--- samples/blockchain/main.go | 32 +- samples/blockchain/miner.go | 2 +- samples/blockchain/ws/ws/wss.go | 1 - .../blockchain/wsinterceptor/wsinterceptor.go | 46 +++ .../wsinterceptor/wsserver.go/wssserver.go | 133 +++++++ 20 files changed, 1084 insertions(+), 172 deletions(-) create mode 100644 pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go create mode 100644 pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go create mode 100644 pkg/pb/blockchainpb/interceptorpb/events/events.mir.go create mode 100644 pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go create mode 100644 pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go create mode 100644 pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go create mode 100644 pkg/pb/blockchainpb/interceptorpb/types/types.mir.go create mode 100644 protos/blockchainpb/interceptorpb/interceptorpb.proto create mode 100644 samples/blockchain/wsinterceptor/wsinterceptor.go create mode 100644 samples/blockchain/wsinterceptor/wsserver.go/wssserver.go diff --git a/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go new file mode 100644 index 000000000..30852e0e3 --- /dev/null +++ b/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go @@ -0,0 +1,20 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package interceptorpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/events" + types "github.com/filecoin-project/mir/pkg/types" +) + +// Module-specific dsl functions for emitting events. + +func TreeUpdate(m dsl.Module, destModule types.ModuleID, tree *blockchainpb.Blocktree, headId uint64) { + dsl.EmitMirEvent(m, events.TreeUpdate(destModule, tree, headId)) +} + +func NewOrphan(m dsl.Module, destModule types.ModuleID, orphan *blockchainpb.Block) { + dsl.EmitMirEvent(m, events.NewOrphan(destModule, orphan)) +} diff --git a/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go new file mode 100644 index 000000000..6c9135441 --- /dev/null +++ b/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go @@ -0,0 +1,35 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package interceptorpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" +) + +// Module-specific dsl functions for processing events. + +func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func(ev *Ev) error) { + dsl.UponMirEvent[*types1.Event_Bcinterceptor](m, func(ev *types.Event) error { + w, ok := ev.Type.(W) + if !ok { + return nil + } + + return handler(w.Unwrap()) + }) +} + +func UponTreeUpdate(m dsl.Module, handler func(tree *blockchainpb.Blocktree, headId uint64) error) { + UponEvent[*types.Event_TreeUpdate](m, func(ev *types.TreeUpdate) error { + return handler(ev.Tree, ev.HeadId) + }) +} + +func UponNewOrphan(m dsl.Module, handler func(orphan *blockchainpb.Block) error) { + UponEvent[*types.Event_NewOrphan](m, func(ev *types.NewOrphan) error { + return handler(ev.Orphan) + }) +} diff --git a/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go b/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go new file mode 100644 index 000000000..a844d0091 --- /dev/null +++ b/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go @@ -0,0 +1,41 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package interceptorpbevents + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types "github.com/filecoin-project/mir/pkg/types" +) + +func TreeUpdate(destModule types.ModuleID, tree *blockchainpb.Blocktree, headId uint64) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcinterceptor{ + Bcinterceptor: &types2.Event{ + Type: &types2.Event_TreeUpdate{ + TreeUpdate: &types2.TreeUpdate{ + Tree: tree, + HeadId: headId, + }, + }, + }, + }, + } +} + +func NewOrphan(destModule types.ModuleID, orphan *blockchainpb.Block) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcinterceptor{ + Bcinterceptor: &types2.Event{ + Type: &types2.Event_NewOrphan{ + NewOrphan: &types2.NewOrphan{ + Orphan: orphan, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go new file mode 100644 index 000000000..741233afa --- /dev/null +++ b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go @@ -0,0 +1,342 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.4 +// source: blockchainpb/interceptorpb/interceptorpb.proto + +package interceptorpb + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + _ "github.com/filecoin-project/mir/pkg/pb/mir" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *Event_TreeUpdate + // *Event_NewOrphan + Type isEvent_Type `protobuf_oneof:"type"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[0] + 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 Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescGZIP(), []int{0} +} + +func (m *Event) GetType() isEvent_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *Event) GetTreeUpdate() *TreeUpdate { + if x, ok := x.GetType().(*Event_TreeUpdate); ok { + return x.TreeUpdate + } + return nil +} + +func (x *Event) GetNewOrphan() *NewOrphan { + if x, ok := x.GetType().(*Event_NewOrphan); ok { + return x.NewOrphan + } + return nil +} + +type isEvent_Type interface { + isEvent_Type() +} + +type Event_TreeUpdate struct { + TreeUpdate *TreeUpdate `protobuf:"bytes,1,opt,name=tree_update,json=treeUpdate,proto3,oneof"` +} + +type Event_NewOrphan struct { + NewOrphan *NewOrphan `protobuf:"bytes,2,opt,name=new_orphan,json=newOrphan,proto3,oneof"` +} + +func (*Event_TreeUpdate) isEvent_Type() {} + +func (*Event_NewOrphan) isEvent_Type() {} + +type TreeUpdate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Tree *blockchainpb.Blocktree `protobuf:"bytes,1,opt,name=tree,proto3" json:"tree,omitempty"` + HeadId uint64 `protobuf:"varint,2,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` +} + +func (x *TreeUpdate) Reset() { + *x = TreeUpdate{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *TreeUpdate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TreeUpdate) ProtoMessage() {} + +func (x *TreeUpdate) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[1] + 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 TreeUpdate.ProtoReflect.Descriptor instead. +func (*TreeUpdate) Descriptor() ([]byte, []int) { + return file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescGZIP(), []int{1} +} + +func (x *TreeUpdate) GetTree() *blockchainpb.Blocktree { + if x != nil { + return x.Tree + } + return nil +} + +func (x *TreeUpdate) GetHeadId() uint64 { + if x != nil { + return x.HeadId + } + return 0 +} + +type NewOrphan struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Orphan *blockchainpb.Block `protobuf:"bytes,1,opt,name=orphan,proto3" json:"orphan,omitempty"` +} + +func (x *NewOrphan) Reset() { + *x = NewOrphan{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewOrphan) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewOrphan) ProtoMessage() {} + +func (x *NewOrphan) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[2] + 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 NewOrphan.ProtoReflect.Descriptor instead. +func (*NewOrphan) Descriptor() ([]byte, []int) { + return file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescGZIP(), []int{2} +} + +func (x *NewOrphan) GetOrphan() *blockchainpb.Block { + if x != nil { + return x.Orphan + } + return nil +} + +var File_blockchainpb_interceptorpb_interceptorpb_proto protoreflect.FileDescriptor + +var file_blockchainpb_interceptorpb_interceptorpb_proto_rawDesc = []byte{ + 0x0a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x1a, + 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, + 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x94, + 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0b, 0x74, 0x72, 0x65, 0x65, + 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x72, + 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x72, 0x65, 0x65, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x6e, 0x65, 0x77, 0x5f, 0x6f, 0x72, + 0x70, 0x68, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x4f, 0x72, + 0x70, 0x68, 0x61, 0x6e, 0x48, 0x00, 0x52, 0x09, 0x6e, 0x65, 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, + 0x6e, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, + 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x58, 0x0a, 0x0a, 0x54, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, + 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, + 0x3e, 0x0a, 0x09, 0x4e, 0x65, 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x12, 0x2b, 0x0a, 0x06, + 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x06, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, + 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, + 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, + 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, + 0x6f, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescOnce sync.Once + file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescData = file_blockchainpb_interceptorpb_interceptorpb_proto_rawDesc +) + +func file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescGZIP() []byte { + file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescOnce.Do(func() { + file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescData) + }) + return file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescData +} + +var file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_blockchainpb_interceptorpb_interceptorpb_proto_goTypes = []interface{}{ + (*Event)(nil), // 0: interceptorpb.Event + (*TreeUpdate)(nil), // 1: interceptorpb.TreeUpdate + (*NewOrphan)(nil), // 2: interceptorpb.NewOrphan + (*blockchainpb.Blocktree)(nil), // 3: blockchainpb.Blocktree + (*blockchainpb.Block)(nil), // 4: blockchainpb.Block +} +var file_blockchainpb_interceptorpb_interceptorpb_proto_depIdxs = []int32{ + 1, // 0: interceptorpb.Event.tree_update:type_name -> interceptorpb.TreeUpdate + 2, // 1: interceptorpb.Event.new_orphan:type_name -> interceptorpb.NewOrphan + 3, // 2: interceptorpb.TreeUpdate.tree:type_name -> blockchainpb.Blocktree + 4, // 3: interceptorpb.NewOrphan.orphan:type_name -> blockchainpb.Block + 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_blockchainpb_interceptorpb_interceptorpb_proto_init() } +func file_blockchainpb_interceptorpb_interceptorpb_proto_init() { + if File_blockchainpb_interceptorpb_interceptorpb_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*TreeUpdate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewOrphan); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Event_TreeUpdate)(nil), + (*Event_NewOrphan)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_blockchainpb_interceptorpb_interceptorpb_proto_rawDesc, + NumEnums: 0, + NumMessages: 3, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_blockchainpb_interceptorpb_interceptorpb_proto_goTypes, + DependencyIndexes: file_blockchainpb_interceptorpb_interceptorpb_proto_depIdxs, + MessageInfos: file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes, + }.Build() + File_blockchainpb_interceptorpb_interceptorpb_proto = out.File + file_blockchainpb_interceptorpb_interceptorpb_proto_rawDesc = nil + file_blockchainpb_interceptorpb_interceptorpb_proto_goTypes = nil + file_blockchainpb_interceptorpb_interceptorpb_proto_depIdxs = nil +} diff --git a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go new file mode 100644 index 000000000..a259261c1 --- /dev/null +++ b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go @@ -0,0 +1,14 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package interceptorpb + +import ( + reflect "reflect" +) + +func (*Event) ReflectTypeOptions() []reflect.Type { + return []reflect.Type{ + reflect.TypeOf((*Event_TreeUpdate)(nil)), + reflect.TypeOf((*Event_NewOrphan)(nil)), + } +} diff --git a/pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go new file mode 100644 index 000000000..c46b3e3ff --- /dev/null +++ b/pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go @@ -0,0 +1,18 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package interceptorpb + +type Event_Type = isEvent_Type + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func (w *Event_TreeUpdate) Unwrap() *TreeUpdate { + return w.TreeUpdate +} + +func (w *Event_NewOrphan) Unwrap() *NewOrphan { + return w.NewOrphan +} diff --git a/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go b/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go new file mode 100644 index 000000000..21d4519e8 --- /dev/null +++ b/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go @@ -0,0 +1,178 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package interceptorpbtypes + +import ( + mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" + reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" +) + +type Event struct { + Type Event_Type +} + +type Event_Type interface { + mirreflect.GeneratedType + isEvent_Type() + Pb() interceptorpb.Event_Type +} + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func Event_TypeFromPb(pb interceptorpb.Event_Type) Event_Type { + if pb == nil { + return nil + } + switch pb := pb.(type) { + case *interceptorpb.Event_TreeUpdate: + return &Event_TreeUpdate{TreeUpdate: TreeUpdateFromPb(pb.TreeUpdate)} + case *interceptorpb.Event_NewOrphan: + return &Event_NewOrphan{NewOrphan: NewOrphanFromPb(pb.NewOrphan)} + } + return nil +} + +type Event_TreeUpdate struct { + TreeUpdate *TreeUpdate +} + +func (*Event_TreeUpdate) isEvent_Type() {} + +func (w *Event_TreeUpdate) Unwrap() *TreeUpdate { + return w.TreeUpdate +} + +func (w *Event_TreeUpdate) Pb() interceptorpb.Event_Type { + if w == nil { + return nil + } + if w.TreeUpdate == nil { + return &interceptorpb.Event_TreeUpdate{} + } + return &interceptorpb.Event_TreeUpdate{TreeUpdate: (w.TreeUpdate).Pb()} +} + +func (*Event_TreeUpdate) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.Event_TreeUpdate]()} +} + +type Event_NewOrphan struct { + NewOrphan *NewOrphan +} + +func (*Event_NewOrphan) isEvent_Type() {} + +func (w *Event_NewOrphan) Unwrap() *NewOrphan { + return w.NewOrphan +} + +func (w *Event_NewOrphan) Pb() interceptorpb.Event_Type { + if w == nil { + return nil + } + if w.NewOrphan == nil { + return &interceptorpb.Event_NewOrphan{} + } + return &interceptorpb.Event_NewOrphan{NewOrphan: (w.NewOrphan).Pb()} +} + +func (*Event_NewOrphan) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.Event_NewOrphan]()} +} + +func EventFromPb(pb *interceptorpb.Event) *Event { + if pb == nil { + return nil + } + return &Event{ + Type: Event_TypeFromPb(pb.Type), + } +} + +func (m *Event) Pb() *interceptorpb.Event { + if m == nil { + return nil + } + pbMessage := &interceptorpb.Event{} + { + if m.Type != nil { + pbMessage.Type = (m.Type).Pb() + } + } + + return pbMessage +} + +func (*Event) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.Event]()} +} + +type TreeUpdate struct { + Tree *blockchainpb.Blocktree + HeadId uint64 +} + +func TreeUpdateFromPb(pb *interceptorpb.TreeUpdate) *TreeUpdate { + if pb == nil { + return nil + } + return &TreeUpdate{ + Tree: pb.Tree, + HeadId: pb.HeadId, + } +} + +func (m *TreeUpdate) Pb() *interceptorpb.TreeUpdate { + if m == nil { + return nil + } + pbMessage := &interceptorpb.TreeUpdate{} + { + if m.Tree != nil { + pbMessage.Tree = m.Tree + } + pbMessage.HeadId = m.HeadId + } + + return pbMessage +} + +func (*TreeUpdate) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.TreeUpdate]()} +} + +type NewOrphan struct { + Orphan *blockchainpb.Block +} + +func NewOrphanFromPb(pb *interceptorpb.NewOrphan) *NewOrphan { + if pb == nil { + return nil + } + return &NewOrphan{ + Orphan: pb.Orphan, + } +} + +func (m *NewOrphan) Pb() *interceptorpb.NewOrphan { + if m == nil { + return nil + } + pbMessage := &interceptorpb.NewOrphan{} + { + if m.Orphan != nil { + pbMessage.Orphan = m.Orphan + } + } + + return pbMessage +} + +func (*NewOrphan) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.NewOrphan]()} +} diff --git a/pkg/pb/eventpb/eventpb.pb.go b/pkg/pb/eventpb/eventpb.pb.go index a578ac5f5..9b719d5df 100644 --- a/pkg/pb/eventpb/eventpb.pb.go +++ b/pkg/pb/eventpb/eventpb.pb.go @@ -14,6 +14,7 @@ import ( bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" @@ -78,6 +79,7 @@ type Event struct { // *Event_Tpm // *Event_Communication // *Event_Synchronizer + // *Event_Bcinterceptor // *Event_TestingString // *Event_TestingUint // *Event_Tester @@ -306,6 +308,13 @@ func (x *Event) GetSynchronizer() *synchronizerpb.Event { return nil } +func (x *Event) GetBcinterceptor() *interceptorpb.Event { + if x, ok := x.GetType().(*Event_Bcinterceptor); ok { + return x.Bcinterceptor + } + return nil +} + func (x *Event) GetTestingString() *wrapperspb.StringValue { if x, ok := x.GetType().(*Event_TestingString); ok { return x.TestingString @@ -439,6 +448,11 @@ type Event_Synchronizer struct { Synchronizer *synchronizerpb.Event `protobuf:"bytes,205,opt,name=synchronizer,proto3,oneof"` } +type Event_Bcinterceptor struct { + // Events for blockchain interceptor + Bcinterceptor *interceptorpb.Event `protobuf:"bytes,210,opt,name=bcinterceptor,proto3,oneof"` +} + type Event_TestingString struct { // for unit-tests TestingString *wrapperspb.StringValue `protobuf:"bytes,301,opt,name=testingString,proto3,oneof"` @@ -500,6 +514,8 @@ func (*Event_Communication) isEvent_Type() {} func (*Event_Synchronizer) isEvent_Type() {} +func (*Event_Bcinterceptor) isEvent_Type() {} + func (*Event_TestingString) isEvent_Type() {} func (*Event_TestingUint) isEvent_Type() {} @@ -861,9 +877,12 @@ var file_eventpb_eventpb_proto_rawDesc = []byte{ 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, - 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, + 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, + 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe7, 0x0b, 0x0a, 0x05, 0x45, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa6, 0x0c, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, @@ -942,76 +961,80 @@ var file_eventpb_eventpb_proto_rawDesc = []byte{ 0x12, 0x3c, 0x0a, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, - 0x52, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x12, 0x45, - 0x0a, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, - 0xad, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, - 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, - 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, - 0x55, 0x69, 0x6e, 0x74, 0x18, 0xae, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, - 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, 0x73, - 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x74, 0x65, 0x73, 0x74, - 0x65, 0x72, 0x18, 0xaf, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, - 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x74, - 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x18, 0x90, 0x03, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x42, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, - 0x3a, 0x04, 0x88, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, - 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x0c, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x3a, 0x04, 0x98, 0xa6, - 0x1d, 0x01, 0x22, 0xc6, 0x01, 0x0a, 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, - 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, - 0x44, 0x65, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2e, - 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, - 0x70, 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x47, - 0x0a, 0x0f, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, - 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, - 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, - 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0a, - 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, + 0x52, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x12, 0x3d, + 0x0a, 0x0d, 0x62, 0x63, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x18, + 0xd2, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, + 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, + 0x62, 0x63, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x45, 0x0a, + 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0xad, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, + 0x72, 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, + 0x69, 0x6e, 0x74, 0x18, 0xae, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, + 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x18, 0xaf, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x65, + 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x74, 0x65, + 0x73, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x18, 0x90, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x44, 0x65, 0x6c, - 0x61, 0x79, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, - 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x90, 0x02, 0x0a, - 0x0b, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x38, 0x0a, 0x10, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, - 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, - 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, - 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, - 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, - 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, - 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, - 0x8a, 0x01, 0x0a, 0x13, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, - 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, - 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x65, 0x6e, 0x74, 0x42, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x3a, + 0x04, 0x88, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, + 0xa6, 0x1d, 0x01, 0x22, 0x0c, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, + 0x01, 0x22, 0xc6, 0x01, 0x0a, 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x2b, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, + 0x65, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2e, 0x0a, + 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, + 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x47, 0x0a, + 0x0f, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, + 0x54, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0a, 0x54, + 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, + 0x6e, 0x74, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x44, 0x65, 0x6c, 0x61, + 0x79, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, - 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, - 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x30, 0x5a, 0x2e, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, - 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, + 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, + 0x64, 0x65, 0x6c, 0x61, 0x79, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, + 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x38, 0x0a, 0x10, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x52, + 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, + 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, + 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, + 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x8a, + 0x01, 0x0a, 0x13, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, + 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, + 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, + 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, + 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, + 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1056,9 +1079,10 @@ var file_eventpb_eventpb_proto_goTypes = []interface{}{ (*tpmpb.Event)(nil), // 25: tpmpb.Event (*communicationpb.Event)(nil), // 26: communicationpb.Event (*synchronizerpb.Event)(nil), // 27: synchronizerpb.Event - (*wrapperspb.StringValue)(nil), // 28: google.protobuf.StringValue - (*wrapperspb.UInt64Value)(nil), // 29: google.protobuf.UInt64Value - (*testerpb.Tester)(nil), // 30: testerpb.Tester + (*interceptorpb.Event)(nil), // 28: interceptorpb.Event + (*wrapperspb.StringValue)(nil), // 29: google.protobuf.StringValue + (*wrapperspb.UInt64Value)(nil), // 30: google.protobuf.UInt64Value + (*testerpb.Tester)(nil), // 31: testerpb.Tester } var file_eventpb_eventpb_proto_depIdxs = []int32{ 1, // 0: eventpb.Event.init:type_name -> eventpb.Init @@ -1085,20 +1109,21 @@ var file_eventpb_eventpb_proto_depIdxs = []int32{ 25, // 21: eventpb.Event.tpm:type_name -> tpmpb.Event 26, // 22: eventpb.Event.communication:type_name -> communicationpb.Event 27, // 23: eventpb.Event.synchronizer:type_name -> synchronizerpb.Event - 28, // 24: eventpb.Event.testingString:type_name -> google.protobuf.StringValue - 29, // 25: eventpb.Event.testingUint:type_name -> google.protobuf.UInt64Value - 30, // 26: eventpb.Event.tester:type_name -> testerpb.Tester - 0, // 27: eventpb.Event.next:type_name -> eventpb.Event - 3, // 28: eventpb.TimerEvent.delay:type_name -> eventpb.TimerDelay - 4, // 29: eventpb.TimerEvent.repeat:type_name -> eventpb.TimerRepeat - 5, // 30: eventpb.TimerEvent.garbage_collect:type_name -> eventpb.TimerGarbageCollect - 0, // 31: eventpb.TimerDelay.events_to_delay:type_name -> eventpb.Event - 0, // 32: eventpb.TimerRepeat.events_to_repeat:type_name -> eventpb.Event - 33, // [33:33] is the sub-list for method output_type - 33, // [33:33] is the sub-list for method input_type - 33, // [33:33] is the sub-list for extension type_name - 33, // [33:33] is the sub-list for extension extendee - 0, // [0:33] is the sub-list for field type_name + 28, // 24: eventpb.Event.bcinterceptor:type_name -> interceptorpb.Event + 29, // 25: eventpb.Event.testingString:type_name -> google.protobuf.StringValue + 30, // 26: eventpb.Event.testingUint:type_name -> google.protobuf.UInt64Value + 31, // 27: eventpb.Event.tester:type_name -> testerpb.Tester + 0, // 28: eventpb.Event.next:type_name -> eventpb.Event + 3, // 29: eventpb.TimerEvent.delay:type_name -> eventpb.TimerDelay + 4, // 30: eventpb.TimerEvent.repeat:type_name -> eventpb.TimerRepeat + 5, // 31: eventpb.TimerEvent.garbage_collect:type_name -> eventpb.TimerGarbageCollect + 0, // 32: eventpb.TimerDelay.events_to_delay:type_name -> eventpb.Event + 0, // 33: eventpb.TimerRepeat.events_to_repeat:type_name -> eventpb.Event + 34, // [34:34] is the sub-list for method output_type + 34, // [34:34] is the sub-list for method input_type + 34, // [34:34] is the sub-list for extension type_name + 34, // [34:34] is the sub-list for extension extendee + 0, // [0:34] is the sub-list for field type_name } func init() { file_eventpb_eventpb_proto_init() } @@ -1205,6 +1230,7 @@ func file_eventpb_eventpb_proto_init() { (*Event_Tpm)(nil), (*Event_Communication)(nil), (*Event_Synchronizer)(nil), + (*Event_Bcinterceptor)(nil), (*Event_TestingString)(nil), (*Event_TestingUint)(nil), (*Event_Tester)(nil), diff --git a/pkg/pb/eventpb/eventpb.pb.mir.go b/pkg/pb/eventpb/eventpb.pb.mir.go index 9fd0cb0b4..7d077a2c9 100644 --- a/pkg/pb/eventpb/eventpb.pb.mir.go +++ b/pkg/pb/eventpb/eventpb.pb.mir.go @@ -32,6 +32,7 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_Tpm)(nil)), reflect.TypeOf((*Event_Communication)(nil)), reflect.TypeOf((*Event_Synchronizer)(nil)), + reflect.TypeOf((*Event_Bcinterceptor)(nil)), reflect.TypeOf((*Event_TestingString)(nil)), reflect.TypeOf((*Event_TestingUint)(nil)), reflect.TypeOf((*Event_Tester)(nil)), diff --git a/pkg/pb/eventpb/oneof_interfaces.mir.go b/pkg/pb/eventpb/oneof_interfaces.mir.go index eeac8121e..7dbeb3cb0 100644 --- a/pkg/pb/eventpb/oneof_interfaces.mir.go +++ b/pkg/pb/eventpb/oneof_interfaces.mir.go @@ -10,6 +10,7 @@ import ( bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" @@ -132,6 +133,10 @@ func (w *Event_Synchronizer) Unwrap() *synchronizerpb.Event { return w.Synchronizer } +func (w *Event_Bcinterceptor) Unwrap() *interceptorpb.Event { + return w.Bcinterceptor +} + func (w *Event_TestingString) Unwrap() *wrapperspb.StringValue { return w.TestingString } diff --git a/pkg/pb/eventpb/types/types.mir.go b/pkg/pb/eventpb/types/types.mir.go index 08344448b..8d8545b11 100644 --- a/pkg/pb/eventpb/types/types.mir.go +++ b/pkg/pb/eventpb/types/types.mir.go @@ -4,7 +4,7 @@ package eventpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - types24 "github.com/filecoin-project/mir/codegen/model/types" + types25 "github.com/filecoin-project/mir/codegen/model/types" types13 "github.com/filecoin-project/mir/pkg/pb/apppb/types" types5 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/batchdbpb/types" types4 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/types" @@ -12,6 +12,7 @@ import ( types2 "github.com/filecoin-project/mir/pkg/pb/bcbpb/types" types18 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" types21 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types23 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" types19 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" types22 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" types20 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/types" @@ -26,11 +27,11 @@ import ( types16 "github.com/filecoin-project/mir/pkg/pb/ordererpb/pprepvalidatorpb/types" types11 "github.com/filecoin-project/mir/pkg/pb/ordererpb/types" types17 "github.com/filecoin-project/mir/pkg/pb/pingpongpb/types" - types23 "github.com/filecoin-project/mir/pkg/pb/testerpb/types" + types24 "github.com/filecoin-project/mir/pkg/pb/testerpb/types" types7 "github.com/filecoin-project/mir/pkg/pb/threshcryptopb/types" types14 "github.com/filecoin-project/mir/pkg/pb/transportpb/types" - types25 "github.com/filecoin-project/mir/pkg/timer/types" - types26 "github.com/filecoin-project/mir/pkg/trantor/types" + types26 "github.com/filecoin-project/mir/pkg/timer/types" + types27 "github.com/filecoin-project/mir/pkg/trantor/types" types "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" @@ -106,12 +107,14 @@ func Event_TypeFromPb(pb eventpb.Event_Type) Event_Type { return &Event_Communication{Communication: types21.EventFromPb(pb.Communication)} case *eventpb.Event_Synchronizer: return &Event_Synchronizer{Synchronizer: types22.EventFromPb(pb.Synchronizer)} + case *eventpb.Event_Bcinterceptor: + return &Event_Bcinterceptor{Bcinterceptor: types23.EventFromPb(pb.Bcinterceptor)} case *eventpb.Event_TestingString: return &Event_TestingString{TestingString: pb.TestingString} case *eventpb.Event_TestingUint: return &Event_TestingUint{TestingUint: pb.TestingUint} case *eventpb.Event_Tester: - return &Event_Tester{Tester: types23.TesterFromPb(pb.Tester)} + return &Event_Tester{Tester: types24.TesterFromPb(pb.Tester)} } return nil } @@ -692,6 +695,30 @@ func (*Event_Synchronizer) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Synchronizer]()} } +type Event_Bcinterceptor struct { + Bcinterceptor *types23.Event +} + +func (*Event_Bcinterceptor) isEvent_Type() {} + +func (w *Event_Bcinterceptor) Unwrap() *types23.Event { + return w.Bcinterceptor +} + +func (w *Event_Bcinterceptor) Pb() eventpb.Event_Type { + if w == nil { + return nil + } + if w.Bcinterceptor == nil { + return &eventpb.Event_Bcinterceptor{} + } + return &eventpb.Event_Bcinterceptor{Bcinterceptor: (w.Bcinterceptor).Pb()} +} + +func (*Event_Bcinterceptor) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Bcinterceptor]()} +} + type Event_TestingString struct { TestingString *wrapperspb.StringValue } @@ -741,12 +768,12 @@ func (*Event_TestingUint) MirReflect() mirreflect.Type { } type Event_Tester struct { - Tester *types23.Tester + Tester *types24.Tester } func (*Event_Tester) isEvent_Type() {} -func (w *Event_Tester) Unwrap() *types23.Tester { +func (w *Event_Tester) Unwrap() *types24.Tester { return w.Tester } @@ -771,7 +798,7 @@ func EventFromPb(pb *eventpb.Event) *Event { return &Event{ DestModule: (types.ModuleID)(pb.DestModule), Type: Event_TypeFromPb(pb.Type), - Next: types24.ConvertSlice(pb.Next, func(t *eventpb.Event) *Event { + Next: types25.ConvertSlice(pb.Next, func(t *eventpb.Event) *Event { return EventFromPb(t) }), } @@ -787,7 +814,7 @@ func (m *Event) Pb() *eventpb.Event { if m.Type != nil { pbMessage.Type = (m.Type).Pb() } - pbMessage.Next = types24.ConvertSlice(m.Next, func(t *Event) *eventpb.Event { + pbMessage.Next = types25.ConvertSlice(m.Next, func(t *Event) *eventpb.Event { return (t).Pb() }) } @@ -954,7 +981,7 @@ func (*TimerEvent) MirReflect() mirreflect.Type { type TimerDelay struct { EventsToDelay []*Event - Delay types25.Duration + Delay types26.Duration } func TimerDelayFromPb(pb *eventpb.TimerDelay) *TimerDelay { @@ -962,10 +989,10 @@ func TimerDelayFromPb(pb *eventpb.TimerDelay) *TimerDelay { return nil } return &TimerDelay{ - EventsToDelay: types24.ConvertSlice(pb.EventsToDelay, func(t *eventpb.Event) *Event { + EventsToDelay: types25.ConvertSlice(pb.EventsToDelay, func(t *eventpb.Event) *Event { return EventFromPb(t) }), - Delay: (types25.Duration)(pb.Delay), + Delay: (types26.Duration)(pb.Delay), } } @@ -975,7 +1002,7 @@ func (m *TimerDelay) Pb() *eventpb.TimerDelay { } pbMessage := &eventpb.TimerDelay{} { - pbMessage.EventsToDelay = types24.ConvertSlice(m.EventsToDelay, func(t *Event) *eventpb.Event { + pbMessage.EventsToDelay = types25.ConvertSlice(m.EventsToDelay, func(t *Event) *eventpb.Event { return (t).Pb() }) pbMessage.Delay = (uint64)(m.Delay) @@ -990,8 +1017,8 @@ func (*TimerDelay) MirReflect() mirreflect.Type { type TimerRepeat struct { EventsToRepeat []*Event - Delay types25.Duration - RetentionIndex types26.RetentionIndex + Delay types26.Duration + RetentionIndex types27.RetentionIndex } func TimerRepeatFromPb(pb *eventpb.TimerRepeat) *TimerRepeat { @@ -999,11 +1026,11 @@ func TimerRepeatFromPb(pb *eventpb.TimerRepeat) *TimerRepeat { return nil } return &TimerRepeat{ - EventsToRepeat: types24.ConvertSlice(pb.EventsToRepeat, func(t *eventpb.Event) *Event { + EventsToRepeat: types25.ConvertSlice(pb.EventsToRepeat, func(t *eventpb.Event) *Event { return EventFromPb(t) }), - Delay: (types25.Duration)(pb.Delay), - RetentionIndex: (types26.RetentionIndex)(pb.RetentionIndex), + Delay: (types26.Duration)(pb.Delay), + RetentionIndex: (types27.RetentionIndex)(pb.RetentionIndex), } } @@ -1013,7 +1040,7 @@ func (m *TimerRepeat) Pb() *eventpb.TimerRepeat { } pbMessage := &eventpb.TimerRepeat{} { - pbMessage.EventsToRepeat = types24.ConvertSlice(m.EventsToRepeat, func(t *Event) *eventpb.Event { + pbMessage.EventsToRepeat = types25.ConvertSlice(m.EventsToRepeat, func(t *Event) *eventpb.Event { return (t).Pb() }) pbMessage.Delay = (uint64)(m.Delay) @@ -1028,7 +1055,7 @@ func (*TimerRepeat) MirReflect() mirreflect.Type { } type TimerGarbageCollect struct { - RetentionIndex types26.RetentionIndex + RetentionIndex types27.RetentionIndex } func TimerGarbageCollectFromPb(pb *eventpb.TimerGarbageCollect) *TimerGarbageCollect { @@ -1036,7 +1063,7 @@ func TimerGarbageCollectFromPb(pb *eventpb.TimerGarbageCollect) *TimerGarbageCol return nil } return &TimerGarbageCollect{ - RetentionIndex: (types26.RetentionIndex)(pb.RetentionIndex), + RetentionIndex: (types27.RetentionIndex)(pb.RetentionIndex), } } diff --git a/protos/blockchainpb/interceptorpb/interceptorpb.proto b/protos/blockchainpb/interceptorpb/interceptorpb.proto new file mode 100644 index 000000000..198137e67 --- /dev/null +++ b/protos/blockchainpb/interceptorpb/interceptorpb.proto @@ -0,0 +1,32 @@ +syntax = "proto3"; + +package interceptorpb; + +option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb"; + +import "blockchainpb/blockchainpb.proto"; +import "mir/codegen_extensions.proto"; + +message Event { + option (mir.event_class) = true; + + oneof type { + option (mir.event_type) = true; + + TreeUpdate tree_update = 1; + NewOrphan new_orphan = 2; + } +} + +message TreeUpdate { + option (mir.event) = true; + + blockchainpb.Blocktree tree = 1; + uint64 head_id = 2; +} + +message NewOrphan { + option (mir.event) = true; + + blockchainpb.Block orphan = 1; +} diff --git a/protos/eventpb/eventpb.proto b/protos/eventpb/eventpb.proto index d7a45b4a9..23cb0bcaa 100644 --- a/protos/eventpb/eventpb.proto +++ b/protos/eventpb/eventpb.proto @@ -28,6 +28,7 @@ import "blockchainpb/minerpb/minerpb.proto"; import "blockchainpb/tpmpb/tpmpb.proto"; import "blockchainpb/communicationpb/communicationpb.proto"; import "blockchainpb/synchronizerpb/synchronizerpb.proto"; +import "blockchainpb/interceptorpb/interceptorpb.proto"; import "mir/codegen_extensions.proto"; @@ -75,6 +76,9 @@ message Event { communicationpb.Event communication = 204; synchronizerpb.Event synchronizer = 205; + // Events for blockchain interceptor + interceptorpb.Event bcinterceptor = 210; + // for unit-tests google.protobuf.StringValue testingString = 301; google.protobuf.UInt64Value testingUint = 302; diff --git a/protos/generate.go b/protos/generate.go index c02b1397b..59e51c488 100644 --- a/protos/generate.go +++ b/protos/generate.go @@ -50,6 +50,7 @@ package protos //go:generate protoc-events blockchainpb/tpmpb/tpmpb.proto //go:generate protoc-events blockchainpb/communicationpb/communicationpb.proto //go:generate protoc-events blockchainpb/synchronizerpb/synchronizerpb.proto +//go:generate protoc-events blockchainpb/interceptorpb/interceptorpb.proto // Build the custom code generators. //go:generate go build -o ../codegen/generators/mir-std-gen/mir-std-gen.bin ../codegen/generators/mir-std-gen @@ -87,6 +88,7 @@ package protos //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" +//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" // Generate other things. //go:generate protoc --go_out=../pkg/ --go_opt=paths=source_relative --go-grpc_out=../pkg/ --go-grpc_opt=paths=source_relative transactionreceiver/transactionreceiver.proto diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 5db527c59..f849e0613 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -4,19 +4,17 @@ package main import ( "errors" - "strconv" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" + interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" minerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" t "github.com/filecoin-project/mir/pkg/types" - "github.com/filecoin-project/mir/samples/blockchain/ws/ws" "golang.org/x/exp/maps" - "google.golang.org/protobuf/proto" ) var ( @@ -38,7 +36,6 @@ type bcmModule struct { head *bcmBlock genesis *bcmBlock blockCount uint64 - updateChan chan string logger logging.Logger } @@ -154,14 +151,15 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { } - bcm.updateChan <- strconv.FormatUint(block.BlockId, 10) - // if head changed, trigger get tpm to prep new block if newHead := bcm.getHead(); newHead != currentHead { // new head bcm.logger.Log(logging.LevelInfo, "Head changed", "head", formatBlockId(newHead.BlockId)) minerpbdsl.NewHead(*bcm.m, "miner", newHead.BlockId) } + + // send to chainviewer + bcm.sendTreeUpdate() } func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { @@ -174,9 +172,6 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { } } - // get last block - bcm.updateChan <- strconv.FormatUint(blocks[len(blocks)-1].BlockId, 10) - // if head changed, trigger get tpm to prep new block if newHead := bcm.getHead(); newHead != currentHead { // new head @@ -184,9 +179,33 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { bcm.logger.Log(logging.LevelInfo, "Head changed", "head", formatBlockId(newHead.BlockId)) minerpbdsl.NewHead(*bcm.m, "miner", newHead.BlockId) } + + // send to chainviewer + bcm.sendTreeUpdate() } -func NewBCM(chainServerPort int, logger logging.Logger) modules.PassiveModule { +func (bcm *bcmModule) sendTreeUpdate() { + blocks := func() []*blockchainpb.Block { + blocks := make([]*blockchainpb.Block, 0, len(bcm.blocks)) + for _, v := range bcm.blocks { + blocks = append(blocks, v.block) + } + return blocks + }() + leaves := func() []uint64 { + leaves := make([]uint64, 0, len(bcm.leaves)) + for _, v := range bcm.leaves { + leaves = append(leaves, v.block.BlockId) + } + return leaves + }() + + blockTree := blockchainpb.Blocktree{Blocks: blocks, Leaves: leaves} + + interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", &blockTree, bcm.head.block.BlockId) +} + +func NewBCM(logger logging.Logger) modules.PassiveModule { m := dsl.NewModule("bcm") // making up a genisis block @@ -215,12 +234,6 @@ func NewBCM(chainServerPort int, logger logging.Logger) modules.PassiveModule { // add genesis to leaves bcm.leaves[hash] = bcm.head - // setup update channel - bcm.updateChan = make(chan string) - - // start chain server (for visualization) - go bcm.chainServer(chainServerPort) - dsl.UponInit(m, func() error { // kick off tpm with genisis block @@ -268,39 +281,3 @@ func NewBCM(chainServerPort int, logger logging.Logger) modules.PassiveModule { return m } - -func (bcm *bcmModule) chainServer(port int) error { - bcm.logger.Log(logging.LevelInfo, "Starting chain server") - - websocket, send, _ := ws.NewWsServer(port) - go websocket.StartServers() - - for { - <-bcm.updateChan - - blocks := func() []*blockchainpb.Block { - blocks := make([]*blockchainpb.Block, 0, len(bcm.blocks)) - for _, v := range bcm.blocks { - blocks = append(blocks, v.block) - } - return blocks - }() - leaves := func() []uint64 { - leaves := make([]uint64, 0, len(bcm.leaves)) - for _, v := range bcm.leaves { - leaves = append(leaves, v.block.BlockId) - } - return leaves - }() - - blockTreeMessage := blockchainpb.Blocktree{Blocks: blocks, Leaves: leaves} - payload, err := proto.Marshal(&blockTreeMessage) - if err != nil { - panic(err) - } - - send <- ws.WsMessage{MessageType: 2, Payload: payload} - } - - return nil -} diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index 4250f62a8..4136eb3dd 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -12,9 +12,11 @@ import ( "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/net/grpc" + "github.com/filecoin-project/mir/pkg/pb/eventpb" trantorpbtypes "github.com/filecoin-project/mir/pkg/pb/trantorpb/types" "github.com/filecoin-project/mir/pkg/timer" t "github.com/filecoin-project/mir/pkg/types" + wsinterceptor "github.com/filecoin-project/mir/samples/blockchain/wsInterceptor" ) func main() { @@ -58,7 +60,7 @@ func main() { membership := &trantorpbtypes.Membership{Nodes: nodes} // Instantiate network trnasport module and establish connections. - transport, err := grpc.NewTransport(ownNodeID, membership.Nodes[ownNodeID].Addr, logger) + transport, err := grpc.NewTransport(ownNodeID, membership.Nodes[ownNodeID].Addr, logging.ConsoleInfoLogger) if err != nil { panic(err) } @@ -71,7 +73,7 @@ func main() { mangler, err := eventmangler.NewModule( eventmangler.ModuleConfig{Self: "mangler", Dest: "transport", Timer: "timer"}, - &eventmangler.ModuleParams{MinDelay: time.Second / 100, MaxDelay: 2 * time.Second, DropRate: 0.1}, + &eventmangler.ModuleParams{MinDelay: time.Second / 1000, MaxDelay: 2 * time.Second, DropRate: 0.05}, ) if err != nil { panic(err) @@ -83,16 +85,27 @@ func main() { mir.DefaultNodeConfig(), map[t.ModuleID]modules.Module{ "transport": transport, - "bcm": NewBCM(8080+ownID, logging.Decorate(logger, "BCM:\t")), + "bcm": NewBCM(logging.Decorate(logger, "BCM:\t")), "miner": NewMiner(logging.Decorate(logger, "Miner:\t")), "communication": NewCommunication(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), "tpm": NewTPM(logging.Decorate(logger, "TPM:\t")), "synchronizer": NewSynchronizer(otherNodes, false, logging.Decorate(logger, "Sync:\t")), "timer": timer, "mangler": mangler, + "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor }, - nil, - ) + wsinterceptor.NewWsInterceptor( + func(e *eventpb.Event) bool { + switch e.Type.(type) { + case *eventpb.Event_Bcinterceptor: + return true + default: + return false + } + }, + 8080+ownID, + logging.Decorate(logger, "WSInter:\t"), + )) if err != nil { panic(err) } @@ -107,10 +120,9 @@ func main() { // block until nodeError receives an error // fmt.Printf("timer started\n") - switch <-nodeError { - default: - fmt.Printf("Mir node stopped: %v\n", <-nodeError) - } + fmt.Printf("Mir node stopped: %v\n", <-nodeError) + + // Dead code below // time.Sleep(5 * time.Second) // fmt.Printf("timer up") @@ -118,5 +130,5 @@ func main() { // Stop the node. node.Stop() transport.Stop() - fmt.Printf("Mir node stopped: %v\n", <-nodeError) + fmt.Printf("Mir node stopped") } diff --git a/samples/blockchain/miner.go b/samples/blockchain/miner.go index 5ab96232f..bc6aaaba5 100644 --- a/samples/blockchain/miner.go +++ b/samples/blockchain/miner.go @@ -19,7 +19,7 @@ import ( ) const ( - EXPONENTIAL_MINUTE_FACTOR = 0.3 + EXPONENTIAL_MINUTE_FACTOR = 0.2 ) type blockRequest struct { diff --git a/samples/blockchain/ws/ws/wss.go b/samples/blockchain/ws/ws/wss.go index 2ee41f8a2..135b6cbde 100644 --- a/samples/blockchain/ws/ws/wss.go +++ b/samples/blockchain/ws/ws/wss.go @@ -67,7 +67,6 @@ func (wsc *connection) wsConnection() { err := wsc.ws.WriteMessage(message.MessageType, message.Payload) if err != nil { log.Println(err) - panic(err) } } }() diff --git a/samples/blockchain/wsinterceptor/wsinterceptor.go b/samples/blockchain/wsinterceptor/wsinterceptor.go new file mode 100644 index 000000000..f82e38ee7 --- /dev/null +++ b/samples/blockchain/wsinterceptor/wsinterceptor.go @@ -0,0 +1,46 @@ +package wsInterceptor + +import ( + "github.com/filecoin-project/mir/pkg/events" + "github.com/filecoin-project/mir/pkg/logging" + "github.com/filecoin-project/mir/pkg/pb/eventpb" + "github.com/filecoin-project/mir/samples/blockchain/wsinterceptor/wsserver.go" + "google.golang.org/protobuf/proto" +) + +type eventFilterFn func(*eventpb.Event) bool + +type wsInterceptor struct { + server *wsserver.WsServer + eventFilter eventFilterFn + logger logging.Logger +} + +func (i *wsInterceptor) Intercept(events *events.EventList) error { + for _, e := range events.Slice() { + if i.eventFilter(e) { + payload, err := proto.Marshal(e) + if err != nil { + // log? + return err + } + i.server.SendChan <- wsserver.WsMessage{ + MessageType: 2, + Payload: []byte(payload), + } + } + } + + return nil +} + +func NewWsInterceptor(eventFilter eventFilterFn, port int, logger logging.Logger) *wsInterceptor { + server := wsserver.NewWsServer(port, logger) + logger.Log(logging.LevelInfo, "Starting websocker interceptor server") + go server.StartServers() + return &wsInterceptor{ + server: server, + eventFilter: eventFilter, + logger: logger, + } +} diff --git a/samples/blockchain/wsinterceptor/wsserver.go/wssserver.go b/samples/blockchain/wsinterceptor/wsserver.go/wssserver.go new file mode 100644 index 000000000..a0f67d42c --- /dev/null +++ b/samples/blockchain/wsinterceptor/wsserver.go/wssserver.go @@ -0,0 +1,133 @@ +package wsserver + +import ( + "fmt" + "log" + "net/http" + "sync" + + "github.com/filecoin-project/mir/pkg/logging" + "github.com/google/uuid" + "github.com/gorilla/websocket" +) + +type WsMessage struct { + MessageType int + Payload []byte +} + +type connection struct { + id string + server *WsServer + ws *websocket.Conn + sendChan chan WsMessage +} + +type WsServer struct { + SendChan chan WsMessage + port int + connections map[string]connection + connectionsLock sync.Mutex + logger logging.Logger +} + +var upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, +} + +func (wss *WsServer) handleHome(w http.ResponseWriter, r *http.Request) { + if r.URL.Path != "/" { + http.Error(w, "Not found", http.StatusNotFound) + return + } + if r.Method != "GET" { + http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) + return + } + w.Write([]byte("Hello world!")) +} + +func (wsc *connection) wsConnection() { + log.Println("wsConnection") + // close connection + defer wsc.ws.Close() + // close up channels and remove connection + defer func() { + wsc.server.connectionsLock.Lock() + delete(wsc.server.connections, wsc.id) + close(wsc.sendChan) + wsc.server.connectionsLock.Unlock() + }() + + // sending msgs + go func() { + for { + message := <-wsc.sendChan + err := wsc.ws.WriteMessage(message.MessageType, message.Payload) + if err != nil { + log.Println(err) + } + } + }() + + // "receiving" msgs + for { + wsc.ws.ReadMessage() + wsc.server.logger.Log(logging.LevelWarn, "Received WS message - ignored") + } +} + +func (wss *WsServer) handleWs(w http.ResponseWriter, r *http.Request) { + // cors * + upgrader.CheckOrigin = func(r *http.Request) bool { return true } + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + log.Printf("Upgrade failed: %v", err) + return + } + wss.connectionsLock.Lock() + id := "" + for { + id = uuid.New().String() + if _, ok := wss.connections[id]; !ok { + break + } + } + conn := connection{id, wss, ws, make(chan WsMessage)} + wss.connections[id] = conn + wss.connectionsLock.Unlock() + go conn.wsConnection() +} + +func (wss *WsServer) setupHttpRoutes() { + http.HandleFunc("/", wss.handleHome) + http.HandleFunc("/ws", wss.handleWs) +} + +func (wss *WsServer) sendHandler() { + for { + message := <-wss.SendChan + for _, conn := range wss.connections { + conn.sendChan <- message + } + } +} + +func (wss *WsServer) StartServers() { + log.Println("Starting servers...") + wss.setupHttpRoutes() + go wss.sendHandler() + log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", wss.port), nil)) +} + +func NewWsServer(port int, logger logging.Logger) *WsServer { + wss := &WsServer{ + port: port, + connections: make(map[string]connection), + SendChan: make(chan WsMessage), + logger: logger, + } + + return wss +} From e34cbc00d3439810c7d0dad31b846ad0fab3ace9 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Thu, 23 Nov 2023 11:23:54 +0100 Subject: [PATCH 07/46] first app + direct chain sync reqs --- .../applicationpb/applicationpb.pb.go | 514 ++++++++++++++++++ .../applicationpb/applicationpb.pb.mir.go | 16 + .../applicationpb/dsl/emit.mir.go | 29 + .../applicationpb/dsl/upon.mir.go | 48 ++ .../applicationpb/events/events.mir.go | 72 +++ .../applicationpb/oneof_interfaces.mir.go | 26 + .../applicationpb/types/types.mir.go | 289 ++++++++++ pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go | 318 +++++++++-- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go | 2 + pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go | 8 + pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go | 12 + .../blockchainpb/bcmpb/events/events.mir.go | 35 ++ .../bcmpb/oneof_interfaces.mir.go | 8 + pkg/pb/blockchainpb/bcmpb/types/types.mir.go | 125 +++++ pkg/pb/blockchainpb/blockchainpb.pb.go | 136 ++--- .../interceptorpb/dsl/emit.mir.go | 4 + .../interceptorpb/dsl/upon.mir.go | 6 + .../interceptorpb/events/events.mir.go | 15 + .../interceptorpb/interceptorpb.pb.go | 136 ++++- .../interceptorpb/interceptorpb.pb.mir.go | 1 + .../interceptorpb/oneof_interfaces.mir.go | 4 + .../interceptorpb/types/types.mir.go | 55 ++ pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go | 4 +- pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go | 4 +- .../blockchainpb/minerpb/events/events.mir.go | 4 +- pkg/pb/blockchainpb/minerpb/minerpb.pb.go | 68 +-- .../blockchainpb/minerpb/types/types.mir.go | 4 +- .../payloadpb/oneof_interfaces.mir.go | 3 + pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go | 148 +++++ .../blockchainpb/payloadpb/types/types.mir.go | 3 + .../synchronizerpb/dsl/messages.mir.go | 12 +- .../synchronizerpb/msgs/msgs.mir.go | 15 +- .../synchronizerpb/oneof_interfaces.mir.go | 8 +- .../synchronizerpb/synchronizerpb.pb.go | 154 +++--- .../synchronizerpb/synchronizerpb.pb.mir.go | 4 +- .../synchronizerpb/types/types.mir.go | 91 ++-- pkg/pb/eventpb/eventpb.pb.go | 208 +++---- pkg/pb/eventpb/eventpb.pb.mir.go | 1 + pkg/pb/eventpb/oneof_interfaces.mir.go | 5 + pkg/pb/eventpb/types/types.mir.go | 77 ++- .../applicationpb/applicationpb.proto | 48 ++ protos/blockchainpb/bcmpb/bcmpb.proto | 20 + protos/blockchainpb/blockchainpb.proto | 14 +- .../interceptorpb/interceptorpb.proto | 7 + protos/blockchainpb/minerpb/minerpb.proto | 4 +- protos/blockchainpb/payloadpb/payloadpb.proto | 10 + .../synchronizerpb/synchronizerpb.proto | 19 +- protos/eventpb/eventpb.proto | 2 + protos/generate.go | 4 + samples/blockchain/application/application.go | 69 +++ samples/blockchain/application/state.go | 41 ++ samples/blockchain/bcm.go | 136 +++-- samples/blockchain/communication.go | 5 +- samples/blockchain/main.go | 14 +- samples/blockchain/miner.go | 26 +- samples/blockchain/synchronizer.go | 61 +-- samples/blockchain/tpm.go | 17 +- samples/blockchain/utils.go | 34 -- samples/blockchain/utils/format.go | 22 + samples/blockchain/utils/hash.go | 15 + 60 files changed, 2624 insertions(+), 616 deletions(-) create mode 100644 pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go create mode 100644 pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go create mode 100644 pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go create mode 100644 pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go create mode 100644 pkg/pb/blockchainpb/applicationpb/events/events.mir.go create mode 100644 pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go create mode 100644 pkg/pb/blockchainpb/applicationpb/types/types.mir.go create mode 100644 pkg/pb/blockchainpb/payloadpb/oneof_interfaces.mir.go create mode 100644 pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go create mode 100644 pkg/pb/blockchainpb/payloadpb/types/types.mir.go create mode 100644 protos/blockchainpb/applicationpb/applicationpb.proto create mode 100644 protos/blockchainpb/payloadpb/payloadpb.proto create mode 100644 samples/blockchain/application/application.go create mode 100644 samples/blockchain/application/state.go delete mode 100644 samples/blockchain/utils.go create mode 100644 samples/blockchain/utils/format.go create mode 100644 samples/blockchain/utils/hash.go diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go new file mode 100644 index 000000000..61e3321ca --- /dev/null +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go @@ -0,0 +1,514 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.4 +// source: blockchainpb/applicationpb/applicationpb.proto + +package applicationpb + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + _ "github.com/filecoin-project/mir/pkg/pb/mir" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Event struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // Types that are assignable to Type: + // + // *Event_NewHead + // *Event_RegisterBlock + // *Event_PayloadRequest + // *Event_PayloadResponse + Type isEvent_Type `protobuf_oneof:"type"` +} + +func (x *Event) Reset() { + *x = Event{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Event) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Event) ProtoMessage() {} + +func (x *Event) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[0] + 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 Event.ProtoReflect.Descriptor instead. +func (*Event) Descriptor() ([]byte, []int) { + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{0} +} + +func (m *Event) GetType() isEvent_Type { + if m != nil { + return m.Type + } + return nil +} + +func (x *Event) GetNewHead() *NewHead { + if x, ok := x.GetType().(*Event_NewHead); ok { + return x.NewHead + } + return nil +} + +func (x *Event) GetRegisterBlock() *RegisterBlock { + if x, ok := x.GetType().(*Event_RegisterBlock); ok { + return x.RegisterBlock + } + return nil +} + +func (x *Event) GetPayloadRequest() *PayloadRequest { + if x, ok := x.GetType().(*Event_PayloadRequest); ok { + return x.PayloadRequest + } + return nil +} + +func (x *Event) GetPayloadResponse() *PayloadResponse { + if x, ok := x.GetType().(*Event_PayloadResponse); ok { + return x.PayloadResponse + } + return nil +} + +type isEvent_Type interface { + isEvent_Type() +} + +type Event_NewHead struct { + NewHead *NewHead `protobuf:"bytes,1,opt,name=new_head,json=newHead,proto3,oneof"` +} + +type Event_RegisterBlock struct { + RegisterBlock *RegisterBlock `protobuf:"bytes,2,opt,name=register_block,json=registerBlock,proto3,oneof"` +} + +type Event_PayloadRequest struct { + PayloadRequest *PayloadRequest `protobuf:"bytes,3,opt,name=payload_request,json=payloadRequest,proto3,oneof"` +} + +type Event_PayloadResponse struct { + PayloadResponse *PayloadResponse `protobuf:"bytes,4,opt,name=payload_response,json=payloadResponse,proto3,oneof"` +} + +func (*Event_NewHead) isEvent_Type() {} + +func (*Event_RegisterBlock) isEvent_Type() {} + +func (*Event_PayloadRequest) isEvent_Type() {} + +func (*Event_PayloadResponse) isEvent_Type() {} + +type NewHead struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + HeadId uint64 `protobuf:"varint,1,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` +} + +func (x *NewHead) Reset() { + *x = NewHead{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *NewHead) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*NewHead) ProtoMessage() {} + +func (x *NewHead) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[1] + 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 NewHead.ProtoReflect.Descriptor instead. +func (*NewHead) Descriptor() ([]byte, []int) { + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{1} +} + +func (x *NewHead) GetHeadId() uint64 { + if x != nil { + return x.HeadId + } + return 0 +} + +type RegisterBlock struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BlockId *blockchainpb.Block `protobuf:"bytes,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` +} + +func (x *RegisterBlock) Reset() { + *x = RegisterBlock{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterBlock) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterBlock) ProtoMessage() {} + +func (x *RegisterBlock) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] + 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 RegisterBlock.ProtoReflect.Descriptor instead. +func (*RegisterBlock) Descriptor() ([]byte, []int) { + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{2} +} + +func (x *RegisterBlock) GetBlockId() *blockchainpb.Block { + if x != nil { + return x.BlockId + } + return nil +} + +type PayloadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + HeadId uint64 `protobuf:"varint,1,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` +} + +func (x *PayloadRequest) Reset() { + *x = PayloadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayloadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayloadRequest) ProtoMessage() {} + +func (x *PayloadRequest) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] + 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 PayloadRequest.ProtoReflect.Descriptor instead. +func (*PayloadRequest) Descriptor() ([]byte, []int) { + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{3} +} + +func (x *PayloadRequest) GetHeadId() uint64 { + if x != nil { + return x.HeadId + } + return 0 +} + +type PayloadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + HeadId uint64 `protobuf:"varint,1,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` + Payload *payloadpb.Payload `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` +} + +func (x *PayloadResponse) Reset() { + *x = PayloadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *PayloadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PayloadResponse) ProtoMessage() {} + +func (x *PayloadResponse) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_applicationpb_applicationpb_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 PayloadResponse.ProtoReflect.Descriptor instead. +func (*PayloadResponse) Descriptor() ([]byte, []int) { + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{4} +} + +func (x *PayloadResponse) GetHeadId() uint64 { + if x != nil { + return x.HeadId + } + return 0 +} + +func (x *PayloadResponse) GetPayload() *payloadpb.Payload { + if x != nil { + return x.Payload + } + return nil +} + +var File_blockchainpb_applicationpb_applicationpb_proto protoreflect.FileDescriptor + +var file_blockchainpb_applicationpb_applicationpb_proto_rawDesc = []byte{ + 0x0a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x61, + 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x12, 0x0d, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x1a, + 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x26, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xae, 0x02, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x12, 0x33, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x48, 0x00, 0x52, 0x07, 0x6e, 0x65, + 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x45, 0x0a, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x0d, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x48, 0x0a, 0x0f, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x48, 0x00, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, + 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, + 0x01, 0x22, 0x45, 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x12, 0x2e, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, + 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, + 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0f, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, + 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, + 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, + 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_blockchainpb_applicationpb_applicationpb_proto_rawDescOnce sync.Once + file_blockchainpb_applicationpb_applicationpb_proto_rawDescData = file_blockchainpb_applicationpb_applicationpb_proto_rawDesc +) + +func file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP() []byte { + file_blockchainpb_applicationpb_applicationpb_proto_rawDescOnce.Do(func() { + file_blockchainpb_applicationpb_applicationpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_applicationpb_applicationpb_proto_rawDescData) + }) + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescData +} + +var file_blockchainpb_applicationpb_applicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_blockchainpb_applicationpb_applicationpb_proto_goTypes = []interface{}{ + (*Event)(nil), // 0: applicationpb.Event + (*NewHead)(nil), // 1: applicationpb.NewHead + (*RegisterBlock)(nil), // 2: applicationpb.RegisterBlock + (*PayloadRequest)(nil), // 3: applicationpb.PayloadRequest + (*PayloadResponse)(nil), // 4: applicationpb.PayloadResponse + (*blockchainpb.Block)(nil), // 5: blockchainpb.Block + (*payloadpb.Payload)(nil), // 6: payloadpb.Payload +} +var file_blockchainpb_applicationpb_applicationpb_proto_depIdxs = []int32{ + 1, // 0: applicationpb.Event.new_head:type_name -> applicationpb.NewHead + 2, // 1: applicationpb.Event.register_block:type_name -> applicationpb.RegisterBlock + 3, // 2: applicationpb.Event.payload_request:type_name -> applicationpb.PayloadRequest + 4, // 3: applicationpb.Event.payload_response:type_name -> applicationpb.PayloadResponse + 5, // 4: applicationpb.RegisterBlock.block_id:type_name -> blockchainpb.Block + 6, // 5: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name +} + +func init() { file_blockchainpb_applicationpb_applicationpb_proto_init() } +func file_blockchainpb_applicationpb_applicationpb_proto_init() { + if File_blockchainpb_applicationpb_applicationpb_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Event); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*NewHead); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterBlock); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayloadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayloadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[0].OneofWrappers = []interface{}{ + (*Event_NewHead)(nil), + (*Event_RegisterBlock)(nil), + (*Event_PayloadRequest)(nil), + (*Event_PayloadResponse)(nil), + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_blockchainpb_applicationpb_applicationpb_proto_rawDesc, + NumEnums: 0, + NumMessages: 5, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_blockchainpb_applicationpb_applicationpb_proto_goTypes, + DependencyIndexes: file_blockchainpb_applicationpb_applicationpb_proto_depIdxs, + MessageInfos: file_blockchainpb_applicationpb_applicationpb_proto_msgTypes, + }.Build() + File_blockchainpb_applicationpb_applicationpb_proto = out.File + file_blockchainpb_applicationpb_applicationpb_proto_rawDesc = nil + file_blockchainpb_applicationpb_applicationpb_proto_goTypes = nil + file_blockchainpb_applicationpb_applicationpb_proto_depIdxs = nil +} diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go new file mode 100644 index 000000000..86a02f33e --- /dev/null +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go @@ -0,0 +1,16 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package applicationpb + +import ( + reflect "reflect" +) + +func (*Event) ReflectTypeOptions() []reflect.Type { + return []reflect.Type{ + reflect.TypeOf((*Event_NewHead)(nil)), + reflect.TypeOf((*Event_RegisterBlock)(nil)), + reflect.TypeOf((*Event_PayloadRequest)(nil)), + reflect.TypeOf((*Event_PayloadResponse)(nil)), + } +} diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go new file mode 100644 index 000000000..c96dee17d --- /dev/null +++ b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go @@ -0,0 +1,29 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package applicationpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/events" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + types "github.com/filecoin-project/mir/pkg/types" +) + +// Module-specific dsl functions for emitting events. + +func NewHead(m dsl.Module, destModule types.ModuleID, headId uint64) { + dsl.EmitMirEvent(m, events.NewHead(destModule, headId)) +} + +func RegisterBlock(m dsl.Module, destModule types.ModuleID, blockId *blockchainpb.Block) { + dsl.EmitMirEvent(m, events.RegisterBlock(destModule, blockId)) +} + +func PayloadRequest(m dsl.Module, destModule types.ModuleID, headId uint64) { + dsl.EmitMirEvent(m, events.PayloadRequest(destModule, headId)) +} + +func PayloadResponse(m dsl.Module, destModule types.ModuleID, headId uint64, payload *payloadpb.Payload) { + dsl.EmitMirEvent(m, events.PayloadResponse(destModule, headId, payload)) +} diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go new file mode 100644 index 000000000..333f09c31 --- /dev/null +++ b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go @@ -0,0 +1,48 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package applicationpbdsl + +import ( + dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" +) + +// Module-specific dsl functions for processing events. + +func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func(ev *Ev) error) { + dsl.UponMirEvent[*types1.Event_Application](m, func(ev *types.Event) error { + w, ok := ev.Type.(W) + if !ok { + return nil + } + + return handler(w.Unwrap()) + }) +} + +func UponNewHead(m dsl.Module, handler func(headId uint64) error) { + UponEvent[*types.Event_NewHead](m, func(ev *types.NewHead) error { + return handler(ev.HeadId) + }) +} + +func UponRegisterBlock(m dsl.Module, handler func(blockId *blockchainpb.Block) error) { + UponEvent[*types.Event_RegisterBlock](m, func(ev *types.RegisterBlock) error { + return handler(ev.BlockId) + }) +} + +func UponPayloadRequest(m dsl.Module, handler func(headId uint64) error) { + UponEvent[*types.Event_PayloadRequest](m, func(ev *types.PayloadRequest) error { + return handler(ev.HeadId) + }) +} + +func UponPayloadResponse(m dsl.Module, handler func(headId uint64, payload *payloadpb.Payload) error) { + UponEvent[*types.Event_PayloadResponse](m, func(ev *types.PayloadResponse) error { + return handler(ev.HeadId, ev.Payload) + }) +} diff --git a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go new file mode 100644 index 000000000..4b1f6d7f6 --- /dev/null +++ b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go @@ -0,0 +1,72 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package applicationpbevents + +import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types "github.com/filecoin-project/mir/pkg/types" +) + +func NewHead(destModule types.ModuleID, headId uint64) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Application{ + Application: &types2.Event{ + Type: &types2.Event_NewHead{ + NewHead: &types2.NewHead{ + HeadId: headId, + }, + }, + }, + }, + } +} + +func RegisterBlock(destModule types.ModuleID, blockId *blockchainpb.Block) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Application{ + Application: &types2.Event{ + Type: &types2.Event_RegisterBlock{ + RegisterBlock: &types2.RegisterBlock{ + BlockId: blockId, + }, + }, + }, + }, + } +} + +func PayloadRequest(destModule types.ModuleID, headId uint64) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Application{ + Application: &types2.Event{ + Type: &types2.Event_PayloadRequest{ + PayloadRequest: &types2.PayloadRequest{ + HeadId: headId, + }, + }, + }, + }, + } +} + +func PayloadResponse(destModule types.ModuleID, headId uint64, payload *payloadpb.Payload) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Application{ + Application: &types2.Event{ + Type: &types2.Event_PayloadResponse{ + PayloadResponse: &types2.PayloadResponse{ + HeadId: headId, + Payload: payload, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go new file mode 100644 index 000000000..d9a164a4b --- /dev/null +++ b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go @@ -0,0 +1,26 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package applicationpb + +type Event_Type = isEvent_Type + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func (w *Event_NewHead) Unwrap() *NewHead { + return w.NewHead +} + +func (w *Event_RegisterBlock) Unwrap() *RegisterBlock { + return w.RegisterBlock +} + +func (w *Event_PayloadRequest) Unwrap() *PayloadRequest { + return w.PayloadRequest +} + +func (w *Event_PayloadResponse) Unwrap() *PayloadResponse { + return w.PayloadResponse +} diff --git a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go new file mode 100644 index 000000000..2d3b22953 --- /dev/null +++ b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go @@ -0,0 +1,289 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package applicationpbtypes + +import ( + mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + applicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" +) + +type Event struct { + Type Event_Type +} + +type Event_Type interface { + mirreflect.GeneratedType + isEvent_Type() + Pb() applicationpb.Event_Type +} + +type Event_TypeWrapper[T any] interface { + Event_Type + Unwrap() *T +} + +func Event_TypeFromPb(pb applicationpb.Event_Type) Event_Type { + if pb == nil { + return nil + } + switch pb := pb.(type) { + case *applicationpb.Event_NewHead: + return &Event_NewHead{NewHead: NewHeadFromPb(pb.NewHead)} + case *applicationpb.Event_RegisterBlock: + return &Event_RegisterBlock{RegisterBlock: RegisterBlockFromPb(pb.RegisterBlock)} + case *applicationpb.Event_PayloadRequest: + return &Event_PayloadRequest{PayloadRequest: PayloadRequestFromPb(pb.PayloadRequest)} + case *applicationpb.Event_PayloadResponse: + return &Event_PayloadResponse{PayloadResponse: PayloadResponseFromPb(pb.PayloadResponse)} + } + return nil +} + +type Event_NewHead struct { + NewHead *NewHead +} + +func (*Event_NewHead) isEvent_Type() {} + +func (w *Event_NewHead) Unwrap() *NewHead { + return w.NewHead +} + +func (w *Event_NewHead) Pb() applicationpb.Event_Type { + if w == nil { + return nil + } + if w.NewHead == nil { + return &applicationpb.Event_NewHead{} + } + return &applicationpb.Event_NewHead{NewHead: (w.NewHead).Pb()} +} + +func (*Event_NewHead) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_NewHead]()} +} + +type Event_RegisterBlock struct { + RegisterBlock *RegisterBlock +} + +func (*Event_RegisterBlock) isEvent_Type() {} + +func (w *Event_RegisterBlock) Unwrap() *RegisterBlock { + return w.RegisterBlock +} + +func (w *Event_RegisterBlock) Pb() applicationpb.Event_Type { + if w == nil { + return nil + } + if w.RegisterBlock == nil { + return &applicationpb.Event_RegisterBlock{} + } + return &applicationpb.Event_RegisterBlock{RegisterBlock: (w.RegisterBlock).Pb()} +} + +func (*Event_RegisterBlock) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_RegisterBlock]()} +} + +type Event_PayloadRequest struct { + PayloadRequest *PayloadRequest +} + +func (*Event_PayloadRequest) isEvent_Type() {} + +func (w *Event_PayloadRequest) Unwrap() *PayloadRequest { + return w.PayloadRequest +} + +func (w *Event_PayloadRequest) Pb() applicationpb.Event_Type { + if w == nil { + return nil + } + if w.PayloadRequest == nil { + return &applicationpb.Event_PayloadRequest{} + } + return &applicationpb.Event_PayloadRequest{PayloadRequest: (w.PayloadRequest).Pb()} +} + +func (*Event_PayloadRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_PayloadRequest]()} +} + +type Event_PayloadResponse struct { + PayloadResponse *PayloadResponse +} + +func (*Event_PayloadResponse) isEvent_Type() {} + +func (w *Event_PayloadResponse) Unwrap() *PayloadResponse { + return w.PayloadResponse +} + +func (w *Event_PayloadResponse) Pb() applicationpb.Event_Type { + if w == nil { + return nil + } + if w.PayloadResponse == nil { + return &applicationpb.Event_PayloadResponse{} + } + return &applicationpb.Event_PayloadResponse{PayloadResponse: (w.PayloadResponse).Pb()} +} + +func (*Event_PayloadResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_PayloadResponse]()} +} + +func EventFromPb(pb *applicationpb.Event) *Event { + if pb == nil { + return nil + } + return &Event{ + Type: Event_TypeFromPb(pb.Type), + } +} + +func (m *Event) Pb() *applicationpb.Event { + if m == nil { + return nil + } + pbMessage := &applicationpb.Event{} + { + if m.Type != nil { + pbMessage.Type = (m.Type).Pb() + } + } + + return pbMessage +} + +func (*Event) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event]()} +} + +type NewHead struct { + HeadId uint64 +} + +func NewHeadFromPb(pb *applicationpb.NewHead) *NewHead { + if pb == nil { + return nil + } + return &NewHead{ + HeadId: pb.HeadId, + } +} + +func (m *NewHead) Pb() *applicationpb.NewHead { + if m == nil { + return nil + } + pbMessage := &applicationpb.NewHead{} + { + pbMessage.HeadId = m.HeadId + } + + return pbMessage +} + +func (*NewHead) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.NewHead]()} +} + +type RegisterBlock struct { + BlockId *blockchainpb.Block +} + +func RegisterBlockFromPb(pb *applicationpb.RegisterBlock) *RegisterBlock { + if pb == nil { + return nil + } + return &RegisterBlock{ + BlockId: pb.BlockId, + } +} + +func (m *RegisterBlock) Pb() *applicationpb.RegisterBlock { + if m == nil { + return nil + } + pbMessage := &applicationpb.RegisterBlock{} + { + if m.BlockId != nil { + pbMessage.BlockId = m.BlockId + } + } + + return pbMessage +} + +func (*RegisterBlock) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.RegisterBlock]()} +} + +type PayloadRequest struct { + HeadId uint64 +} + +func PayloadRequestFromPb(pb *applicationpb.PayloadRequest) *PayloadRequest { + if pb == nil { + return nil + } + return &PayloadRequest{ + HeadId: pb.HeadId, + } +} + +func (m *PayloadRequest) Pb() *applicationpb.PayloadRequest { + if m == nil { + return nil + } + pbMessage := &applicationpb.PayloadRequest{} + { + pbMessage.HeadId = m.HeadId + } + + return pbMessage +} + +func (*PayloadRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.PayloadRequest]()} +} + +type PayloadResponse struct { + HeadId uint64 + Payload *payloadpb.Payload +} + +func PayloadResponseFromPb(pb *applicationpb.PayloadResponse) *PayloadResponse { + if pb == nil { + return nil + } + return &PayloadResponse{ + HeadId: pb.HeadId, + Payload: pb.Payload, + } +} + +func (m *PayloadResponse) Pb() *applicationpb.PayloadResponse { + if m == nil { + return nil + } + pbMessage := &applicationpb.PayloadResponse{} + { + pbMessage.HeadId = m.HeadId + if m.Payload != nil { + pbMessage.Payload = m.Payload + } + } + + return pbMessage +} + +func (*PayloadResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.PayloadResponse]()} +} diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go index 915d76993..84e779703 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go @@ -33,6 +33,8 @@ type Event struct { // *Event_NewChain // *Event_GetBlockRequest // *Event_GetBlockResponse + // *Event_GetChainRequest + // *Event_GetChainResponse Type isEvent_Type `protobuf_oneof:"type"` } @@ -103,6 +105,20 @@ func (x *Event) GetGetBlockResponse() *GetBlockResponse { return nil } +func (x *Event) GetGetChainRequest() *GetChainRequest { + if x, ok := x.GetType().(*Event_GetChainRequest); ok { + return x.GetChainRequest + } + return nil +} + +func (x *Event) GetGetChainResponse() *GetChainResponse { + if x, ok := x.GetType().(*Event_GetChainResponse); ok { + return x.GetChainResponse + } + return nil +} + type isEvent_Type interface { isEvent_Type() } @@ -123,6 +139,14 @@ type Event_GetBlockResponse struct { GetBlockResponse *GetBlockResponse `protobuf:"bytes,4,opt,name=get_block_response,json=getBlockResponse,proto3,oneof"` } +type Event_GetChainRequest struct { + GetChainRequest *GetChainRequest `protobuf:"bytes,5,opt,name=get_chain_request,json=getChainRequest,proto3,oneof"` +} + +type Event_GetChainResponse struct { + GetChainResponse *GetChainResponse `protobuf:"bytes,6,opt,name=get_chain_response,json=getChainResponse,proto3,oneof"` +} + func (*Event_NewBlock) isEvent_Type() {} func (*Event_NewChain) isEvent_Type() {} @@ -131,6 +155,10 @@ func (*Event_GetBlockRequest) isEvent_Type() {} func (*Event_GetBlockResponse) isEvent_Type() {} +func (*Event_GetChainRequest) isEvent_Type() {} + +func (*Event_GetChainResponse) isEvent_Type() {} + type NewBlock struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -352,6 +380,140 @@ func (x *GetBlockResponse) GetBlock() *blockchainpb.Block { return nil } +type GetChainRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + SourceModule string `protobuf:"bytes,2,opt,name=source_module,json=sourceModule,proto3" json:"source_module,omitempty"` + EndBlockId uint64 `protobuf:"varint,3,opt,name=end_block_id,json=endBlockId,proto3" json:"end_block_id,omitempty"` + SourceBlockIds []uint64 `protobuf:"varint,4,rep,packed,name=source_block_ids,json=sourceBlockIds,proto3" json:"source_block_ids,omitempty"` +} + +func (x *GetChainRequest) Reset() { + *x = GetChainRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetChainRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetChainRequest) ProtoMessage() {} + +func (x *GetChainRequest) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] + 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 GetChainRequest.ProtoReflect.Descriptor instead. +func (*GetChainRequest) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{5} +} + +func (x *GetChainRequest) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *GetChainRequest) GetSourceModule() string { + if x != nil { + return x.SourceModule + } + return "" +} + +func (x *GetChainRequest) GetEndBlockId() uint64 { + if x != nil { + return x.EndBlockId + } + return 0 +} + +func (x *GetChainRequest) GetSourceBlockIds() []uint64 { + if x != nil { + return x.SourceBlockIds + } + return nil +} + +type GetChainResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` + Chain []*blockchainpb.Block `protobuf:"bytes,3,rep,name=chain,proto3" json:"chain,omitempty"` // sorted from oldest to youngest +} + +func (x *GetChainResponse) Reset() { + *x = GetChainResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetChainResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetChainResponse) ProtoMessage() {} + +func (x *GetChainResponse) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] + 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 GetChainResponse.ProtoReflect.Descriptor instead. +func (*GetChainResponse) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{6} +} + +func (x *GetChainResponse) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *GetChainResponse) GetSuccess() bool { + if x != nil { + return x.Success + } + return false +} + +func (x *GetChainResponse) GetChain() []*blockchainpb.Block { + if x != nil { + return x.Chain + } + return nil +} + var File_blockchainpb_bcmpb_bcmpb_proto protoreflect.FileDescriptor var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ @@ -361,7 +523,7 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8a, 0x02, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x99, 0x03, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, @@ -376,39 +538,70 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x10, 0x67, - 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x3a, - 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, - 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, - 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, - 0x22, 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, - 0xae, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, - 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, - 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, - 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, - 0x22, 0x78, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, + 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x44, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x63, 0x6d, + 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x10, 0x67, 0x65, + 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x3a, 0x04, + 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, + 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, + 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, + 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xae, + 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, + 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, - 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, - 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, + 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x19, + 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, + 0x78, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, 0x65, + 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, + 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, + 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, + 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x6e, 0x64, + 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, + 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x49, 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x47, + 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, + 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -423,28 +616,33 @@ func file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP() []byte { return file_blockchainpb_bcmpb_bcmpb_proto_rawDescData } -var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_blockchainpb_bcmpb_bcmpb_proto_goTypes = []interface{}{ (*Event)(nil), // 0: bcmpb.Event (*NewBlock)(nil), // 1: bcmpb.NewBlock (*NewChain)(nil), // 2: bcmpb.NewChain (*GetBlockRequest)(nil), // 3: bcmpb.GetBlockRequest (*GetBlockResponse)(nil), // 4: bcmpb.GetBlockResponse - (*blockchainpb.Block)(nil), // 5: blockchainpb.Block + (*GetChainRequest)(nil), // 5: bcmpb.GetChainRequest + (*GetChainResponse)(nil), // 6: bcmpb.GetChainResponse + (*blockchainpb.Block)(nil), // 7: blockchainpb.Block } var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ - 1, // 0: bcmpb.Event.new_block:type_name -> bcmpb.NewBlock - 2, // 1: bcmpb.Event.new_chain:type_name -> bcmpb.NewChain - 3, // 2: bcmpb.Event.get_block_request:type_name -> bcmpb.GetBlockRequest - 4, // 3: bcmpb.Event.get_block_response:type_name -> bcmpb.GetBlockResponse - 5, // 4: bcmpb.NewBlock.block:type_name -> blockchainpb.Block - 5, // 5: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block - 5, // 6: bcmpb.GetBlockResponse.block:type_name -> blockchainpb.Block - 7, // [7:7] is the sub-list for method output_type - 7, // [7:7] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 1, // 0: bcmpb.Event.new_block:type_name -> bcmpb.NewBlock + 2, // 1: bcmpb.Event.new_chain:type_name -> bcmpb.NewChain + 3, // 2: bcmpb.Event.get_block_request:type_name -> bcmpb.GetBlockRequest + 4, // 3: bcmpb.Event.get_block_response:type_name -> bcmpb.GetBlockResponse + 5, // 4: bcmpb.Event.get_chain_request:type_name -> bcmpb.GetChainRequest + 6, // 5: bcmpb.Event.get_chain_response:type_name -> bcmpb.GetChainResponse + 7, // 6: bcmpb.NewBlock.block:type_name -> blockchainpb.Block + 7, // 7: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block + 7, // 8: bcmpb.GetBlockResponse.block:type_name -> blockchainpb.Block + 7, // 9: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block + 10, // [10:10] is the sub-list for method output_type + 10, // [10:10] is the sub-list for method input_type + 10, // [10:10] is the sub-list for extension type_name + 10, // [10:10] is the sub-list for extension extendee + 0, // [0:10] is the sub-list for field type_name } func init() { file_blockchainpb_bcmpb_bcmpb_proto_init() } @@ -513,12 +711,38 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { return nil } } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetChainRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetChainResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_NewBlock)(nil), (*Event_NewChain)(nil), (*Event_GetBlockRequest)(nil), (*Event_GetBlockResponse)(nil), + (*Event_GetChainRequest)(nil), + (*Event_GetChainResponse)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -526,7 +750,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_bcmpb_bcmpb_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go index 3dac6804e..438519226 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go @@ -12,5 +12,7 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_NewChain)(nil)), reflect.TypeOf((*Event_GetBlockRequest)(nil)), reflect.TypeOf((*Event_GetBlockResponse)(nil)), + reflect.TypeOf((*Event_GetChainRequest)(nil)), + reflect.TypeOf((*Event_GetChainResponse)(nil)), } } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go index 2ed68b151..c4e396541 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go @@ -26,3 +26,11 @@ func GetBlockRequest(m dsl.Module, destModule types.ModuleID, requestId uint64, func GetBlockResponse(m dsl.Module, destModule types.ModuleID, requestId uint64, found bool, block *blockchainpb.Block) { dsl.EmitMirEvent(m, events.GetBlockResponse(destModule, requestId, found, block)) } + +func GetChainRequest(m dsl.Module, destModule types.ModuleID, requestId uint64, sourceModule types.ModuleID, endBlockId uint64, sourceBlockIds []uint64) { + dsl.EmitMirEvent(m, events.GetChainRequest(destModule, requestId, sourceModule, endBlockId, sourceBlockIds)) +} + +func GetChainResponse(m dsl.Module, destModule types.ModuleID, requestId uint64, success bool, chain []*blockchainpb.Block) { + dsl.EmitMirEvent(m, events.GetChainResponse(destModule, requestId, success, chain)) +} diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go index 13a9f0b12..39855f428 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go @@ -46,3 +46,15 @@ func UponGetBlockResponse(m dsl.Module, handler func(requestId uint64, found boo return handler(ev.RequestId, ev.Found, ev.Block) }) } + +func UponGetChainRequest(m dsl.Module, handler func(requestId uint64, sourceModule types2.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error) { + UponEvent[*types.Event_GetChainRequest](m, func(ev *types.GetChainRequest) error { + return handler(ev.RequestId, ev.SourceModule, ev.EndBlockId, ev.SourceBlockIds) + }) +} + +func UponGetChainResponse(m dsl.Module, handler func(requestId uint64, success bool, chain []*blockchainpb.Block) error) { + UponEvent[*types.Event_GetChainResponse](m, func(ev *types.GetChainResponse) error { + return handler(ev.RequestId, ev.Success, ev.Chain) + }) +} diff --git a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go index 9266071d8..a438bc44f 100644 --- a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go @@ -72,3 +72,38 @@ func GetBlockResponse(destModule types.ModuleID, requestId uint64, found bool, b }, } } + +func GetChainRequest(destModule types.ModuleID, requestId uint64, sourceModule types.ModuleID, endBlockId uint64, sourceBlockIds []uint64) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcm{ + Bcm: &types2.Event{ + Type: &types2.Event_GetChainRequest{ + GetChainRequest: &types2.GetChainRequest{ + RequestId: requestId, + SourceModule: sourceModule, + EndBlockId: endBlockId, + SourceBlockIds: sourceBlockIds, + }, + }, + }, + }, + } +} + +func GetChainResponse(destModule types.ModuleID, requestId uint64, success bool, chain []*blockchainpb.Block) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcm{ + Bcm: &types2.Event{ + Type: &types2.Event_GetChainResponse{ + GetChainResponse: &types2.GetChainResponse{ + RequestId: requestId, + Success: success, + Chain: chain, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go index b69b62a57..0b79872bc 100644 --- a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go @@ -24,3 +24,11 @@ func (w *Event_GetBlockRequest) Unwrap() *GetBlockRequest { func (w *Event_GetBlockResponse) Unwrap() *GetBlockResponse { return w.GetBlockResponse } + +func (w *Event_GetChainRequest) Unwrap() *GetChainRequest { + return w.GetChainRequest +} + +func (w *Event_GetChainResponse) Unwrap() *GetChainResponse { + return w.GetChainResponse +} diff --git a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go index a82680560..1dce2fe69 100644 --- a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go @@ -38,6 +38,10 @@ func Event_TypeFromPb(pb bcmpb.Event_Type) Event_Type { return &Event_GetBlockRequest{GetBlockRequest: GetBlockRequestFromPb(pb.GetBlockRequest)} case *bcmpb.Event_GetBlockResponse: return &Event_GetBlockResponse{GetBlockResponse: GetBlockResponseFromPb(pb.GetBlockResponse)} + case *bcmpb.Event_GetChainRequest: + return &Event_GetChainRequest{GetChainRequest: GetChainRequestFromPb(pb.GetChainRequest)} + case *bcmpb.Event_GetChainResponse: + return &Event_GetChainResponse{GetChainResponse: GetChainResponseFromPb(pb.GetChainResponse)} } return nil } @@ -138,6 +142,54 @@ func (*Event_GetBlockResponse) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetBlockResponse]()} } +type Event_GetChainRequest struct { + GetChainRequest *GetChainRequest +} + +func (*Event_GetChainRequest) isEvent_Type() {} + +func (w *Event_GetChainRequest) Unwrap() *GetChainRequest { + return w.GetChainRequest +} + +func (w *Event_GetChainRequest) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.GetChainRequest == nil { + return &bcmpb.Event_GetChainRequest{} + } + return &bcmpb.Event_GetChainRequest{GetChainRequest: (w.GetChainRequest).Pb()} +} + +func (*Event_GetChainRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetChainRequest]()} +} + +type Event_GetChainResponse struct { + GetChainResponse *GetChainResponse +} + +func (*Event_GetChainResponse) isEvent_Type() {} + +func (w *Event_GetChainResponse) Unwrap() *GetChainResponse { + return w.GetChainResponse +} + +func (w *Event_GetChainResponse) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.GetChainResponse == nil { + return &bcmpb.Event_GetChainResponse{} + } + return &bcmpb.Event_GetChainResponse{GetChainResponse: (w.GetChainResponse).Pb()} +} + +func (*Event_GetChainResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetChainResponse]()} +} + func EventFromPb(pb *bcmpb.Event) *Event { if pb == nil { return nil @@ -296,3 +348,76 @@ func (m *GetBlockResponse) Pb() *bcmpb.GetBlockResponse { func (*GetBlockResponse) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetBlockResponse]()} } + +type GetChainRequest struct { + RequestId uint64 + SourceModule types.ModuleID + EndBlockId uint64 + SourceBlockIds []uint64 +} + +func GetChainRequestFromPb(pb *bcmpb.GetChainRequest) *GetChainRequest { + if pb == nil { + return nil + } + return &GetChainRequest{ + RequestId: pb.RequestId, + SourceModule: (types.ModuleID)(pb.SourceModule), + EndBlockId: pb.EndBlockId, + SourceBlockIds: pb.SourceBlockIds, + } +} + +func (m *GetChainRequest) Pb() *bcmpb.GetChainRequest { + if m == nil { + return nil + } + pbMessage := &bcmpb.GetChainRequest{} + { + pbMessage.RequestId = m.RequestId + pbMessage.SourceModule = (string)(m.SourceModule) + pbMessage.EndBlockId = m.EndBlockId + pbMessage.SourceBlockIds = m.SourceBlockIds + } + + return pbMessage +} + +func (*GetChainRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetChainRequest]()} +} + +type GetChainResponse struct { + RequestId uint64 + Success bool + Chain []*blockchainpb.Block +} + +func GetChainResponseFromPb(pb *bcmpb.GetChainResponse) *GetChainResponse { + if pb == nil { + return nil + } + return &GetChainResponse{ + RequestId: pb.RequestId, + Success: pb.Success, + Chain: pb.Chain, + } +} + +func (m *GetChainResponse) Pb() *bcmpb.GetChainResponse { + if m == nil { + return nil + } + pbMessage := &bcmpb.GetChainResponse{} + { + pbMessage.RequestId = m.RequestId + pbMessage.Success = m.Success + pbMessage.Chain = m.Chain + } + + return pbMessage +} + +func (*GetChainResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetChainResponse]()} +} diff --git a/pkg/pb/blockchainpb/blockchainpb.pb.go b/pkg/pb/blockchainpb/blockchainpb.pb.go index 16c3c4cb1..a88eb3e2e 100644 --- a/pkg/pb/blockchainpb/blockchainpb.pb.go +++ b/pkg/pb/blockchainpb/blockchainpb.pb.go @@ -7,6 +7,7 @@ package blockchainpb import ( + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -127,10 +128,10 @@ type Block struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - BlockId uint64 `protobuf:"varint,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` - PreviousBlockId uint64 `protobuf:"varint,2,opt,name=previous_block_id,json=previousBlockId,proto3" json:"previous_block_id,omitempty"` - Payload *Payload `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` - Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + BlockId uint64 `protobuf:"varint,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + PreviousBlockId uint64 `protobuf:"varint,2,opt,name=previous_block_id,json=previousBlockId,proto3" json:"previous_block_id,omitempty"` + Payload *payloadpb.Payload `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` + Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (x *Block) Reset() { @@ -179,7 +180,7 @@ func (x *Block) GetPreviousBlockId() uint64 { return 0 } -func (x *Block) GetPayload() *Payload { +func (x *Block) GetPayload() *payloadpb.Payload { if x != nil { return x.Payload } @@ -193,84 +194,37 @@ func (x *Block) GetTimestamp() int64 { return 0 } -type Payload struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` -} - -func (x *Payload) Reset() { - *x = Payload{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Payload) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Payload) ProtoMessage() {} - -func (x *Payload) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[3] - 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 Payload.ProtoReflect.Descriptor instead. -func (*Payload) Descriptor() ([]byte, []int) { - return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{3} -} - -func (x *Payload) GetText() string { - if x != nil { - return x.Text - } - return "" -} - var File_blockchainpb_blockchainpb_proto protoreflect.FileDescriptor var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ 0x0a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x12, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x22, - 0x50, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, - 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, - 0x73, 0x22, 0x39, 0x0a, 0x0a, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, - 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x9d, 0x01, 0x0a, - 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, - 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x70, 0x72, - 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2f, 0x0a, - 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1c, - 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x1d, 0x0a, 0x07, - 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x42, 0x35, 0x5a, 0x33, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, - 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6f, 0x12, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x1a, + 0x26, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, + 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x50, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x74, 0x72, 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x04, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x22, 0x39, 0x0a, 0x0a, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, + 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, + 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, + 0x70, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -285,17 +239,17 @@ func file_blockchainpb_blockchainpb_proto_rawDescGZIP() []byte { return file_blockchainpb_blockchainpb_proto_rawDescData } -var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_blockchainpb_blockchainpb_proto_goTypes = []interface{}{ - (*Blocktree)(nil), // 0: blockchainpb.Blocktree - (*Blockchain)(nil), // 1: blockchainpb.Blockchain - (*Block)(nil), // 2: blockchainpb.Block - (*Payload)(nil), // 3: blockchainpb.Payload + (*Blocktree)(nil), // 0: blockchainpb.Blocktree + (*Blockchain)(nil), // 1: blockchainpb.Blockchain + (*Block)(nil), // 2: blockchainpb.Block + (*payloadpb.Payload)(nil), // 3: payloadpb.Payload } var file_blockchainpb_blockchainpb_proto_depIdxs = []int32{ 2, // 0: blockchainpb.Blocktree.blocks:type_name -> blockchainpb.Block 2, // 1: blockchainpb.Blockchain.blocks:type_name -> blockchainpb.Block - 3, // 2: blockchainpb.Block.payload:type_name -> blockchainpb.Payload + 3, // 2: blockchainpb.Block.payload:type_name -> payloadpb.Payload 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 @@ -345,18 +299,6 @@ func file_blockchainpb_blockchainpb_proto_init() { return nil } } - file_blockchainpb_blockchainpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Payload); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } } type x struct{} out := protoimpl.TypeBuilder{ @@ -364,7 +306,7 @@ func file_blockchainpb_blockchainpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_blockchainpb_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go index 30852e0e3..fb6cc6881 100644 --- a/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go @@ -18,3 +18,7 @@ func TreeUpdate(m dsl.Module, destModule types.ModuleID, tree *blockchainpb.Bloc func NewOrphan(m dsl.Module, destModule types.ModuleID, orphan *blockchainpb.Block) { dsl.EmitMirEvent(m, events.NewOrphan(destModule, orphan)) } + +func AppUpdate(m dsl.Module, destModule types.ModuleID, state int64) { + dsl.EmitMirEvent(m, events.AppUpdate(destModule, state)) +} diff --git a/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go index 6c9135441..856125510 100644 --- a/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go @@ -33,3 +33,9 @@ func UponNewOrphan(m dsl.Module, handler func(orphan *blockchainpb.Block) error) return handler(ev.Orphan) }) } + +func UponAppUpdate(m dsl.Module, handler func(state int64) error) { + UponEvent[*types.Event_AppUpdate](m, func(ev *types.AppUpdate) error { + return handler(ev.State) + }) +} diff --git a/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go b/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go index a844d0091..84a51075a 100644 --- a/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go @@ -39,3 +39,18 @@ func NewOrphan(destModule types.ModuleID, orphan *blockchainpb.Block) *types1.Ev }, } } + +func AppUpdate(destModule types.ModuleID, state int64) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcinterceptor{ + Bcinterceptor: &types2.Event{ + Type: &types2.Event_AppUpdate{ + AppUpdate: &types2.AppUpdate{ + State: state, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go index 741233afa..5b88faed7 100644 --- a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go +++ b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go @@ -31,6 +31,7 @@ type Event struct { // // *Event_TreeUpdate // *Event_NewOrphan + // *Event_AppUpdate Type isEvent_Type `protobuf_oneof:"type"` } @@ -87,6 +88,13 @@ func (x *Event) GetNewOrphan() *NewOrphan { return nil } +func (x *Event) GetAppUpdate() *AppUpdate { + if x, ok := x.GetType().(*Event_AppUpdate); ok { + return x.AppUpdate + } + return nil +} + type isEvent_Type interface { isEvent_Type() } @@ -99,10 +107,16 @@ type Event_NewOrphan struct { NewOrphan *NewOrphan `protobuf:"bytes,2,opt,name=new_orphan,json=newOrphan,proto3,oneof"` } +type Event_AppUpdate struct { + AppUpdate *AppUpdate `protobuf:"bytes,3,opt,name=app_update,json=appUpdate,proto3,oneof"` +} + func (*Event_TreeUpdate) isEvent_Type() {} func (*Event_NewOrphan) isEvent_Type() {} +func (*Event_AppUpdate) isEvent_Type() {} + type TreeUpdate struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -205,6 +219,53 @@ func (x *NewOrphan) GetOrphan() *blockchainpb.Block { return nil } +type AppUpdate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + State int64 `protobuf:"varint,1,opt,name=state,proto3" json:"state,omitempty"` +} + +func (x *AppUpdate) Reset() { + *x = AppUpdate{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *AppUpdate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*AppUpdate) ProtoMessage() {} + +func (x *AppUpdate) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[3] + 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 AppUpdate.ProtoReflect.Descriptor instead. +func (*AppUpdate) Descriptor() ([]byte, []int) { + return file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescGZIP(), []int{3} +} + +func (x *AppUpdate) GetState() int64 { + if x != nil { + return x.State + } + return 0 +} + var File_blockchainpb_interceptorpb_interceptorpb_proto protoreflect.FileDescriptor var file_blockchainpb_interceptorpb_interceptorpb_proto_rawDesc = []byte{ @@ -215,7 +276,7 @@ var file_blockchainpb_interceptorpb_interceptorpb_proto_rawDesc = []byte{ 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, - 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x94, + 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcf, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x72, @@ -224,22 +285,28 @@ var file_blockchainpb_interceptorpb_interceptorpb_proto_rawDesc = []byte{ 0x70, 0x68, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x48, 0x00, 0x52, 0x09, 0x6e, 0x65, 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, - 0x6e, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x58, 0x0a, 0x0a, 0x54, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, - 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, - 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, - 0x3e, 0x0a, 0x09, 0x4e, 0x65, 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x12, 0x2b, 0x0a, 0x06, - 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, + 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x61, 0x70, 0x70, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, + 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, + 0x00, 0x52, 0x09, 0x61, 0x70, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x90, 0xa6, + 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, + 0x22, 0x58, 0x0a, 0x0a, 0x54, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2b, + 0x0a, 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x06, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, - 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, - 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, - 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, - 0x6f, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6b, 0x74, 0x72, 0x65, 0x65, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, + 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, + 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3e, 0x0a, 0x09, 0x4e, 0x65, + 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x6f, 0x72, 0x70, 0x68, 0x61, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x6f, 0x72, + 0x70, 0x68, 0x61, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x27, 0x0a, 0x09, 0x41, 0x70, + 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, + 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -254,24 +321,26 @@ func file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescGZIP() []byte { return file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescData } -var file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_blockchainpb_interceptorpb_interceptorpb_proto_goTypes = []interface{}{ (*Event)(nil), // 0: interceptorpb.Event (*TreeUpdate)(nil), // 1: interceptorpb.TreeUpdate (*NewOrphan)(nil), // 2: interceptorpb.NewOrphan - (*blockchainpb.Blocktree)(nil), // 3: blockchainpb.Blocktree - (*blockchainpb.Block)(nil), // 4: blockchainpb.Block + (*AppUpdate)(nil), // 3: interceptorpb.AppUpdate + (*blockchainpb.Blocktree)(nil), // 4: blockchainpb.Blocktree + (*blockchainpb.Block)(nil), // 5: blockchainpb.Block } var file_blockchainpb_interceptorpb_interceptorpb_proto_depIdxs = []int32{ 1, // 0: interceptorpb.Event.tree_update:type_name -> interceptorpb.TreeUpdate 2, // 1: interceptorpb.Event.new_orphan:type_name -> interceptorpb.NewOrphan - 3, // 2: interceptorpb.TreeUpdate.tree:type_name -> blockchainpb.Blocktree - 4, // 3: interceptorpb.NewOrphan.orphan:type_name -> blockchainpb.Block - 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 + 3, // 2: interceptorpb.Event.app_update:type_name -> interceptorpb.AppUpdate + 4, // 3: interceptorpb.TreeUpdate.tree:type_name -> blockchainpb.Blocktree + 5, // 4: interceptorpb.NewOrphan.orphan:type_name -> blockchainpb.Block + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] 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 } func init() { file_blockchainpb_interceptorpb_interceptorpb_proto_init() } @@ -316,10 +385,23 @@ func file_blockchainpb_interceptorpb_interceptorpb_proto_init() { return nil } } + file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*AppUpdate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_TreeUpdate)(nil), (*Event_NewOrphan)(nil), + (*Event_AppUpdate)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -327,7 +409,7 @@ func file_blockchainpb_interceptorpb_interceptorpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_interceptorpb_interceptorpb_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go index a259261c1..a1da29437 100644 --- a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go @@ -10,5 +10,6 @@ func (*Event) ReflectTypeOptions() []reflect.Type { return []reflect.Type{ reflect.TypeOf((*Event_TreeUpdate)(nil)), reflect.TypeOf((*Event_NewOrphan)(nil)), + reflect.TypeOf((*Event_AppUpdate)(nil)), } } diff --git a/pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go index c46b3e3ff..c3d7bb4ab 100644 --- a/pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go @@ -16,3 +16,7 @@ func (w *Event_TreeUpdate) Unwrap() *TreeUpdate { func (w *Event_NewOrphan) Unwrap() *NewOrphan { return w.NewOrphan } + +func (w *Event_AppUpdate) Unwrap() *AppUpdate { + return w.AppUpdate +} diff --git a/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go b/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go index 21d4519e8..a19db5818 100644 --- a/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go @@ -33,6 +33,8 @@ func Event_TypeFromPb(pb interceptorpb.Event_Type) Event_Type { return &Event_TreeUpdate{TreeUpdate: TreeUpdateFromPb(pb.TreeUpdate)} case *interceptorpb.Event_NewOrphan: return &Event_NewOrphan{NewOrphan: NewOrphanFromPb(pb.NewOrphan)} + case *interceptorpb.Event_AppUpdate: + return &Event_AppUpdate{AppUpdate: AppUpdateFromPb(pb.AppUpdate)} } return nil } @@ -85,6 +87,30 @@ func (*Event_NewOrphan) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.Event_NewOrphan]()} } +type Event_AppUpdate struct { + AppUpdate *AppUpdate +} + +func (*Event_AppUpdate) isEvent_Type() {} + +func (w *Event_AppUpdate) Unwrap() *AppUpdate { + return w.AppUpdate +} + +func (w *Event_AppUpdate) Pb() interceptorpb.Event_Type { + if w == nil { + return nil + } + if w.AppUpdate == nil { + return &interceptorpb.Event_AppUpdate{} + } + return &interceptorpb.Event_AppUpdate{AppUpdate: (w.AppUpdate).Pb()} +} + +func (*Event_AppUpdate) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.Event_AppUpdate]()} +} + func EventFromPb(pb *interceptorpb.Event) *Event { if pb == nil { return nil @@ -176,3 +202,32 @@ func (m *NewOrphan) Pb() *interceptorpb.NewOrphan { func (*NewOrphan) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.NewOrphan]()} } + +type AppUpdate struct { + State int64 +} + +func AppUpdateFromPb(pb *interceptorpb.AppUpdate) *AppUpdate { + if pb == nil { + return nil + } + return &AppUpdate{ + State: pb.State, + } +} + +func (m *AppUpdate) Pb() *interceptorpb.AppUpdate { + if m == nil { + return nil + } + pbMessage := &interceptorpb.AppUpdate{} + { + pbMessage.State = m.State + } + + return pbMessage +} + +func (*AppUpdate) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.AppUpdate]()} +} diff --git a/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go index 9b6160373..303c00c92 100644 --- a/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go @@ -4,14 +4,14 @@ package minerpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/events" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" types "github.com/filecoin-project/mir/pkg/types" ) // Module-specific dsl functions for emitting events. -func BlockRequest(m dsl.Module, destModule types.ModuleID, headId uint64, payload *blockchainpb.Payload) { +func BlockRequest(m dsl.Module, destModule types.ModuleID, headId uint64, payload *payloadpb.Payload) { dsl.EmitMirEvent(m, events.BlockRequest(destModule, headId, payload)) } diff --git a/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go index fa88e6697..84805450c 100644 --- a/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go @@ -4,8 +4,8 @@ package minerpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" ) @@ -22,7 +22,7 @@ func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func }) } -func UponBlockRequest(m dsl.Module, handler func(headId uint64, payload *blockchainpb.Payload) error) { +func UponBlockRequest(m dsl.Module, handler func(headId uint64, payload *payloadpb.Payload) error) { UponEvent[*types.Event_BlockRequest](m, func(ev *types.BlockRequest) error { return handler(ev.HeadId, ev.Payload) }) diff --git a/pkg/pb/blockchainpb/minerpb/events/events.mir.go b/pkg/pb/blockchainpb/minerpb/events/events.mir.go index 1a8d9daa7..13e930775 100644 --- a/pkg/pb/blockchainpb/minerpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/minerpb/events/events.mir.go @@ -3,13 +3,13 @@ package minerpbevents import ( - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) -func BlockRequest(destModule types.ModuleID, headId uint64, payload *blockchainpb.Payload) *types1.Event { +func BlockRequest(destModule types.ModuleID, headId uint64, payload *payloadpb.Payload) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Miner{ diff --git a/pkg/pb/blockchainpb/minerpb/minerpb.pb.go b/pkg/pb/blockchainpb/minerpb/minerpb.pb.go index e3a0567f1..9d49f69bd 100644 --- a/pkg/pb/blockchainpb/minerpb/minerpb.pb.go +++ b/pkg/pb/blockchainpb/minerpb/minerpb.pb.go @@ -7,7 +7,7 @@ package minerpb import ( - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -108,8 +108,8 @@ type BlockRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - HeadId uint64 `protobuf:"varint,1,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` - Payload *blockchainpb.Payload `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` + HeadId uint64 `protobuf:"varint,1,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` + Payload *payloadpb.Payload `protobuf:"bytes,2,opt,name=payload,proto3" json:"payload,omitempty"` } func (x *BlockRequest) Reset() { @@ -151,7 +151,7 @@ func (x *BlockRequest) GetHeadId() uint64 { return 0 } -func (x *BlockRequest) GetPayload() *blockchainpb.Payload { +func (x *BlockRequest) GetPayload() *payloadpb.Payload { if x != nil { return x.Payload } @@ -212,31 +212,31 @@ var file_blockchainpb_minerpb_minerpb_proto_rawDesc = []byte{ 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x88, 0x01, 0x0a, - 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, - 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x48, 0x00, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48, - 0x65, 0x61, 0x64, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, - 0x12, 0x2f, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, - 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, - 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, - 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x26, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x22, 0x88, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, + 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2d, 0x0a, 0x08, 0x6e, + 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, + 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x48, + 0x00, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, + 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x5b, + 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, + 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x07, 0x4e, + 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, + 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x6d, 0x69, 0x6e, + 0x65, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -253,15 +253,15 @@ func file_blockchainpb_minerpb_minerpb_proto_rawDescGZIP() []byte { var file_blockchainpb_minerpb_minerpb_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_blockchainpb_minerpb_minerpb_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: minerpb.Event - (*BlockRequest)(nil), // 1: minerpb.BlockRequest - (*NewHead)(nil), // 2: minerpb.NewHead - (*blockchainpb.Payload)(nil), // 3: blockchainpb.Payload + (*Event)(nil), // 0: minerpb.Event + (*BlockRequest)(nil), // 1: minerpb.BlockRequest + (*NewHead)(nil), // 2: minerpb.NewHead + (*payloadpb.Payload)(nil), // 3: payloadpb.Payload } var file_blockchainpb_minerpb_minerpb_proto_depIdxs = []int32{ 1, // 0: minerpb.Event.block_request:type_name -> minerpb.BlockRequest 2, // 1: minerpb.Event.new_head:type_name -> minerpb.NewHead - 3, // 2: minerpb.BlockRequest.payload:type_name -> blockchainpb.Payload + 3, // 2: minerpb.BlockRequest.payload:type_name -> payloadpb.Payload 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 diff --git a/pkg/pb/blockchainpb/minerpb/types/types.mir.go b/pkg/pb/blockchainpb/minerpb/types/types.mir.go index 684ad11ae..055f98415 100644 --- a/pkg/pb/blockchainpb/minerpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/minerpb/types/types.mir.go @@ -4,8 +4,8 @@ package minerpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -114,7 +114,7 @@ func (*Event) MirReflect() mirreflect.Type { type BlockRequest struct { HeadId uint64 - Payload *blockchainpb.Payload + Payload *payloadpb.Payload } func BlockRequestFromPb(pb *minerpb.BlockRequest) *BlockRequest { diff --git a/pkg/pb/blockchainpb/payloadpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/payloadpb/oneof_interfaces.mir.go new file mode 100644 index 000000000..8f5d1f8ea --- /dev/null +++ b/pkg/pb/blockchainpb/payloadpb/oneof_interfaces.mir.go @@ -0,0 +1,3 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package payloadpb diff --git a/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go b/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go new file mode 100644 index 000000000..aeb72e2a8 --- /dev/null +++ b/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go @@ -0,0 +1,148 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.4 +// source: blockchainpb/payloadpb/payloadpb.proto + +package payloadpb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type Payload struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // application specific payload + AddMinus int64 `protobuf:"varint,1,opt,name=add_minus,json=addMinus,proto3" json:"add_minus,omitempty"` +} + +func (x *Payload) Reset() { + *x = Payload{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_payloadpb_payloadpb_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Payload) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Payload) ProtoMessage() {} + +func (x *Payload) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_payloadpb_payloadpb_proto_msgTypes[0] + 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 Payload.ProtoReflect.Descriptor instead. +func (*Payload) Descriptor() ([]byte, []int) { + return file_blockchainpb_payloadpb_payloadpb_proto_rawDescGZIP(), []int{0} +} + +func (x *Payload) GetAddMinus() int64 { + if x != nil { + return x.AddMinus + } + return 0 +} + +var File_blockchainpb_payloadpb_payloadpb_proto protoreflect.FileDescriptor + +var file_blockchainpb_payloadpb_payloadpb_proto_rawDesc = []byte{ + 0x0a, 0x26, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x70, 0x62, 0x22, 0x26, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1b, + 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x03, 0x52, 0x08, 0x61, 0x64, 0x64, 0x4d, 0x69, 0x6e, 0x75, 0x73, 0x42, 0x3f, 0x5a, 0x3d, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, + 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_blockchainpb_payloadpb_payloadpb_proto_rawDescOnce sync.Once + file_blockchainpb_payloadpb_payloadpb_proto_rawDescData = file_blockchainpb_payloadpb_payloadpb_proto_rawDesc +) + +func file_blockchainpb_payloadpb_payloadpb_proto_rawDescGZIP() []byte { + file_blockchainpb_payloadpb_payloadpb_proto_rawDescOnce.Do(func() { + file_blockchainpb_payloadpb_payloadpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_payloadpb_payloadpb_proto_rawDescData) + }) + return file_blockchainpb_payloadpb_payloadpb_proto_rawDescData +} + +var file_blockchainpb_payloadpb_payloadpb_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_blockchainpb_payloadpb_payloadpb_proto_goTypes = []interface{}{ + (*Payload)(nil), // 0: payloadpb.Payload +} +var file_blockchainpb_payloadpb_payloadpb_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] 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_blockchainpb_payloadpb_payloadpb_proto_init() } +func file_blockchainpb_payloadpb_payloadpb_proto_init() { + if File_blockchainpb_payloadpb_payloadpb_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_blockchainpb_payloadpb_payloadpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Payload); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_blockchainpb_payloadpb_payloadpb_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_blockchainpb_payloadpb_payloadpb_proto_goTypes, + DependencyIndexes: file_blockchainpb_payloadpb_payloadpb_proto_depIdxs, + MessageInfos: file_blockchainpb_payloadpb_payloadpb_proto_msgTypes, + }.Build() + File_blockchainpb_payloadpb_payloadpb_proto = out.File + file_blockchainpb_payloadpb_payloadpb_proto_rawDesc = nil + file_blockchainpb_payloadpb_payloadpb_proto_goTypes = nil + file_blockchainpb_payloadpb_payloadpb_proto_depIdxs = nil +} diff --git a/pkg/pb/blockchainpb/payloadpb/types/types.mir.go b/pkg/pb/blockchainpb/payloadpb/types/types.mir.go new file mode 100644 index 000000000..83cd966c3 --- /dev/null +++ b/pkg/pb/blockchainpb/payloadpb/types/types.mir.go @@ -0,0 +1,3 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package payloadpbtypes diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go index cab93e40d..77f097414 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go @@ -24,14 +24,14 @@ func UponMessageReceived[W types.Message_TypeWrapper[M], M any](m dsl.Module, ha }) } -func UponBlockRequestReceived(m dsl.Module, handler func(from types1.NodeID, requestId uint64, blockId uint64) error) { - UponMessageReceived[*types.Message_BlockRequest](m, func(from types1.NodeID, msg *types.BlockRequest) error { - return handler(from, msg.RequestId, msg.BlockId) +func UponChainRequestReceived(m dsl.Module, handler func(from types1.NodeID, requestId uint64, blockId uint64, leaveIds []uint64) error) { + UponMessageReceived[*types.Message_ChainRequest](m, func(from types1.NodeID, msg *types.ChainRequest) error { + return handler(from, msg.RequestId, msg.BlockId, msg.LeaveIds) }) } -func UponBlockResponseReceived(m dsl.Module, handler func(from types1.NodeID, requestId uint64, found bool, block *blockchainpb.Block) error) { - UponMessageReceived[*types.Message_BlockResponse](m, func(from types1.NodeID, msg *types.BlockResponse) error { - return handler(from, msg.RequestId, msg.Found, msg.Block) +func UponChainResponseReceived(m dsl.Module, handler func(from types1.NodeID, requestId uint64, found bool, chain []*blockchainpb.Block) error) { + UponMessageReceived[*types.Message_ChainResponse](m, func(from types1.NodeID, msg *types.ChainResponse) error { + return handler(from, msg.RequestId, msg.Found, msg.Chain) }) } diff --git a/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go b/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go index d48c1f861..e2661512a 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go @@ -9,15 +9,16 @@ import ( types "github.com/filecoin-project/mir/pkg/types" ) -func BlockRequest(destModule types.ModuleID, requestId uint64, blockId uint64) *types1.Message { +func ChainRequest(destModule types.ModuleID, requestId uint64, blockId uint64, leaveIds []uint64) *types1.Message { return &types1.Message{ DestModule: destModule, Type: &types1.Message_Synchronizer{ Synchronizer: &types2.Message{ - Type: &types2.Message_BlockRequest{ - BlockRequest: &types2.BlockRequest{ + Type: &types2.Message_ChainRequest{ + ChainRequest: &types2.ChainRequest{ RequestId: requestId, BlockId: blockId, + LeaveIds: leaveIds, }, }, }, @@ -25,16 +26,16 @@ func BlockRequest(destModule types.ModuleID, requestId uint64, blockId uint64) * } } -func BlockResponse(destModule types.ModuleID, requestId uint64, found bool, block *blockchainpb.Block) *types1.Message { +func ChainResponse(destModule types.ModuleID, requestId uint64, found bool, chain []*blockchainpb.Block) *types1.Message { return &types1.Message{ DestModule: destModule, Type: &types1.Message_Synchronizer{ Synchronizer: &types2.Message{ - Type: &types2.Message_BlockResponse{ - BlockResponse: &types2.BlockResponse{ + Type: &types2.Message_ChainResponse{ + ChainResponse: &types2.ChainResponse{ RequestId: requestId, Found: found, - Block: block, + Chain: chain, }, }, }, diff --git a/pkg/pb/blockchainpb/synchronizerpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/synchronizerpb/oneof_interfaces.mir.go index 08b914b89..4a78f3920 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/oneof_interfaces.mir.go @@ -20,10 +20,10 @@ type Message_TypeWrapper[T any] interface { Unwrap() *T } -func (w *Message_BlockRequest) Unwrap() *BlockRequest { - return w.BlockRequest +func (w *Message_ChainRequest) Unwrap() *ChainRequest { + return w.ChainRequest } -func (w *Message_BlockResponse) Unwrap() *BlockResponse { - return w.BlockResponse +func (w *Message_ChainResponse) Unwrap() *ChainResponse { + return w.ChainResponse } diff --git a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go index 812b2111f..cd26dff5b 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go +++ b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go @@ -152,8 +152,8 @@ type Message struct { // Types that are assignable to Type: // - // *Message_BlockRequest - // *Message_BlockResponse + // *Message_ChainRequest + // *Message_ChainResponse Type isMessage_Type `protobuf_oneof:"type"` } @@ -196,16 +196,16 @@ func (m *Message) GetType() isMessage_Type { return nil } -func (x *Message) GetBlockRequest() *BlockRequest { - if x, ok := x.GetType().(*Message_BlockRequest); ok { - return x.BlockRequest +func (x *Message) GetChainRequest() *ChainRequest { + if x, ok := x.GetType().(*Message_ChainRequest); ok { + return x.ChainRequest } return nil } -func (x *Message) GetBlockResponse() *BlockResponse { - if x, ok := x.GetType().(*Message_BlockResponse); ok { - return x.BlockResponse +func (x *Message) GetChainResponse() *ChainResponse { + if x, ok := x.GetType().(*Message_ChainResponse); ok { + return x.ChainResponse } return nil } @@ -214,29 +214,30 @@ type isMessage_Type interface { isMessage_Type() } -type Message_BlockRequest struct { - BlockRequest *BlockRequest `protobuf:"bytes,1,opt,name=block_request,json=blockRequest,proto3,oneof"` +type Message_ChainRequest struct { + ChainRequest *ChainRequest `protobuf:"bytes,1,opt,name=chain_request,json=chainRequest,proto3,oneof"` } -type Message_BlockResponse struct { - BlockResponse *BlockResponse `protobuf:"bytes,2,opt,name=block_response,json=blockResponse,proto3,oneof"` +type Message_ChainResponse struct { + ChainResponse *ChainResponse `protobuf:"bytes,2,opt,name=chain_response,json=chainResponse,proto3,oneof"` } -func (*Message_BlockRequest) isMessage_Type() {} +func (*Message_ChainRequest) isMessage_Type() {} -func (*Message_BlockResponse) isMessage_Type() {} +func (*Message_ChainResponse) isMessage_Type() {} -type BlockRequest struct { +type ChainRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - BlockId uint64 `protobuf:"varint,2,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + BlockId uint64 `protobuf:"varint,2,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + LeaveIds []uint64 `protobuf:"varint,3,rep,packed,name=leave_ids,json=leaveIds,proto3" json:"leave_ids,omitempty"` } -func (x *BlockRequest) Reset() { - *x = BlockRequest{} +func (x *ChainRequest) Reset() { + *x = ChainRequest{} if protoimpl.UnsafeEnabled { mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -244,13 +245,13 @@ func (x *BlockRequest) Reset() { } } -func (x *BlockRequest) String() string { +func (x *ChainRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*BlockRequest) ProtoMessage() {} +func (*ChainRequest) ProtoMessage() {} -func (x *BlockRequest) ProtoReflect() protoreflect.Message { +func (x *ChainRequest) ProtoReflect() protoreflect.Message { mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -262,37 +263,44 @@ func (x *BlockRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use BlockRequest.ProtoReflect.Descriptor instead. -func (*BlockRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use ChainRequest.ProtoReflect.Descriptor instead. +func (*ChainRequest) Descriptor() ([]byte, []int) { return file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescGZIP(), []int{3} } -func (x *BlockRequest) GetRequestId() uint64 { +func (x *ChainRequest) GetRequestId() uint64 { if x != nil { return x.RequestId } return 0 } -func (x *BlockRequest) GetBlockId() uint64 { +func (x *ChainRequest) GetBlockId() uint64 { if x != nil { return x.BlockId } return 0 } -type BlockResponse struct { +func (x *ChainRequest) GetLeaveIds() []uint64 { + if x != nil { + return x.LeaveIds + } + return nil +} + +type ChainResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - Found bool `protobuf:"varint,2,opt,name=found,proto3" json:"found,omitempty"` - Block *blockchainpb.Block `protobuf:"bytes,3,opt,name=block,proto3" json:"block,omitempty"` // possibly undefined (proto3 spec no-longer supports optional/required...) + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Found bool `protobuf:"varint,2,opt,name=found,proto3" json:"found,omitempty"` + Chain []*blockchainpb.Block `protobuf:"bytes,3,rep,name=chain,proto3" json:"chain,omitempty"` // possibly undefined (proto3 spec no-longer supports optional/required...) } -func (x *BlockResponse) Reset() { - *x = BlockResponse{} +func (x *ChainResponse) Reset() { + *x = ChainResponse{} if protoimpl.UnsafeEnabled { mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -300,13 +308,13 @@ func (x *BlockResponse) Reset() { } } -func (x *BlockResponse) String() string { +func (x *ChainResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*BlockResponse) ProtoMessage() {} +func (*ChainResponse) ProtoMessage() {} -func (x *BlockResponse) ProtoReflect() protoreflect.Message { +func (x *ChainResponse) ProtoReflect() protoreflect.Message { mi := &file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -318,28 +326,28 @@ func (x *BlockResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use BlockResponse.ProtoReflect.Descriptor instead. -func (*BlockResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use ChainResponse.ProtoReflect.Descriptor instead. +func (*ChainResponse) Descriptor() ([]byte, []int) { return file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescGZIP(), []int{4} } -func (x *BlockResponse) GetRequestId() uint64 { +func (x *ChainResponse) GetRequestId() uint64 { if x != nil { return x.RequestId } return 0 } -func (x *BlockResponse) GetFound() bool { +func (x *ChainResponse) GetFound() bool { if x != nil { return x.Found } return false } -func (x *BlockResponse) GetBlock() *blockchainpb.Block { +func (x *ChainResponse) GetChain() []*blockchainpb.Block { if x != nil { - return x.Block + return x.Chain } return nil } @@ -370,34 +378,36 @@ var file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDesc = []byte{ 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x49, 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xaa, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, + 0x73, 0x61, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x79, - 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x0e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x0e, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x48, 0x00, 0x52, 0x0d, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x70, 0x62, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x48, 0x00, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x3a, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x22, 0x4e, 0x0a, 0x0c, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, + 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x22, 0x6b, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x3a, - 0x04, 0xd0, 0xe4, 0x1d, 0x01, 0x22, 0x75, 0x0a, 0x0d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, - 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0xd0, 0xe4, 0x1d, 0x01, 0x42, 0x44, 0x5a, 0x42, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, - 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, - 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, + 0x1b, 0x0a, 0x09, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, + 0x28, 0x04, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x49, 0x64, 0x73, 0x3a, 0x04, 0xd0, 0xe4, + 0x1d, 0x01, 0x22, 0x75, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x3a, 0x04, 0xd0, 0xe4, 0x1d, 0x01, 0x42, 0x44, 0x5a, 0x42, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, + 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -417,16 +427,16 @@ var file_blockchainpb_synchronizerpb_synchronizerpb_proto_goTypes = []interface{ (*Event)(nil), // 0: synchronizerpb.Event (*SyncRequest)(nil), // 1: synchronizerpb.SyncRequest (*Message)(nil), // 2: synchronizerpb.Message - (*BlockRequest)(nil), // 3: synchronizerpb.BlockRequest - (*BlockResponse)(nil), // 4: synchronizerpb.BlockResponse + (*ChainRequest)(nil), // 3: synchronizerpb.ChainRequest + (*ChainResponse)(nil), // 4: synchronizerpb.ChainResponse (*blockchainpb.Block)(nil), // 5: blockchainpb.Block } var file_blockchainpb_synchronizerpb_synchronizerpb_proto_depIdxs = []int32{ 1, // 0: synchronizerpb.Event.sync_request:type_name -> synchronizerpb.SyncRequest 5, // 1: synchronizerpb.SyncRequest.orphan_block:type_name -> blockchainpb.Block - 3, // 2: synchronizerpb.Message.block_request:type_name -> synchronizerpb.BlockRequest - 4, // 3: synchronizerpb.Message.block_response:type_name -> synchronizerpb.BlockResponse - 5, // 4: synchronizerpb.BlockResponse.block:type_name -> blockchainpb.Block + 3, // 2: synchronizerpb.Message.chain_request:type_name -> synchronizerpb.ChainRequest + 4, // 3: synchronizerpb.Message.chain_response:type_name -> synchronizerpb.ChainResponse + 5, // 4: synchronizerpb.ChainResponse.chain:type_name -> blockchainpb.Block 5, // [5:5] is the sub-list for method output_type 5, // [5:5] is the sub-list for method input_type 5, // [5:5] is the sub-list for extension type_name @@ -477,7 +487,7 @@ func file_blockchainpb_synchronizerpb_synchronizerpb_proto_init() { } } file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlockRequest); i { + switch v := v.(*ChainRequest); i { case 0: return &v.state case 1: @@ -489,7 +499,7 @@ func file_blockchainpb_synchronizerpb_synchronizerpb_proto_init() { } } file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlockResponse); i { + switch v := v.(*ChainResponse); i { case 0: return &v.state case 1: @@ -505,8 +515,8 @@ func file_blockchainpb_synchronizerpb_synchronizerpb_proto_init() { (*Event_SyncRequest)(nil), } file_blockchainpb_synchronizerpb_synchronizerpb_proto_msgTypes[2].OneofWrappers = []interface{}{ - (*Message_BlockRequest)(nil), - (*Message_BlockResponse)(nil), + (*Message_ChainRequest)(nil), + (*Message_ChainResponse)(nil), } type x struct{} out := protoimpl.TypeBuilder{ diff --git a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.mir.go b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.mir.go index c60ed12f3..304545144 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.mir.go @@ -14,7 +14,7 @@ func (*Event) ReflectTypeOptions() []reflect.Type { func (*Message) ReflectTypeOptions() []reflect.Type { return []reflect.Type{ - reflect.TypeOf((*Message_BlockRequest)(nil)), - reflect.TypeOf((*Message_BlockResponse)(nil)), + reflect.TypeOf((*Message_ChainRequest)(nil)), + reflect.TypeOf((*Message_ChainResponse)(nil)), } } diff --git a/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go index 9e853c987..c23f84c53 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go @@ -140,60 +140,60 @@ func Message_TypeFromPb(pb synchronizerpb.Message_Type) Message_Type { return nil } switch pb := pb.(type) { - case *synchronizerpb.Message_BlockRequest: - return &Message_BlockRequest{BlockRequest: BlockRequestFromPb(pb.BlockRequest)} - case *synchronizerpb.Message_BlockResponse: - return &Message_BlockResponse{BlockResponse: BlockResponseFromPb(pb.BlockResponse)} + case *synchronizerpb.Message_ChainRequest: + return &Message_ChainRequest{ChainRequest: ChainRequestFromPb(pb.ChainRequest)} + case *synchronizerpb.Message_ChainResponse: + return &Message_ChainResponse{ChainResponse: ChainResponseFromPb(pb.ChainResponse)} } return nil } -type Message_BlockRequest struct { - BlockRequest *BlockRequest +type Message_ChainRequest struct { + ChainRequest *ChainRequest } -func (*Message_BlockRequest) isMessage_Type() {} +func (*Message_ChainRequest) isMessage_Type() {} -func (w *Message_BlockRequest) Unwrap() *BlockRequest { - return w.BlockRequest +func (w *Message_ChainRequest) Unwrap() *ChainRequest { + return w.ChainRequest } -func (w *Message_BlockRequest) Pb() synchronizerpb.Message_Type { +func (w *Message_ChainRequest) Pb() synchronizerpb.Message_Type { if w == nil { return nil } - if w.BlockRequest == nil { - return &synchronizerpb.Message_BlockRequest{} + if w.ChainRequest == nil { + return &synchronizerpb.Message_ChainRequest{} } - return &synchronizerpb.Message_BlockRequest{BlockRequest: (w.BlockRequest).Pb()} + return &synchronizerpb.Message_ChainRequest{ChainRequest: (w.ChainRequest).Pb()} } -func (*Message_BlockRequest) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.Message_BlockRequest]()} +func (*Message_ChainRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.Message_ChainRequest]()} } -type Message_BlockResponse struct { - BlockResponse *BlockResponse +type Message_ChainResponse struct { + ChainResponse *ChainResponse } -func (*Message_BlockResponse) isMessage_Type() {} +func (*Message_ChainResponse) isMessage_Type() {} -func (w *Message_BlockResponse) Unwrap() *BlockResponse { - return w.BlockResponse +func (w *Message_ChainResponse) Unwrap() *ChainResponse { + return w.ChainResponse } -func (w *Message_BlockResponse) Pb() synchronizerpb.Message_Type { +func (w *Message_ChainResponse) Pb() synchronizerpb.Message_Type { if w == nil { return nil } - if w.BlockResponse == nil { - return &synchronizerpb.Message_BlockResponse{} + if w.ChainResponse == nil { + return &synchronizerpb.Message_ChainResponse{} } - return &synchronizerpb.Message_BlockResponse{BlockResponse: (w.BlockResponse).Pb()} + return &synchronizerpb.Message_ChainResponse{ChainResponse: (w.ChainResponse).Pb()} } -func (*Message_BlockResponse) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.Message_BlockResponse]()} +func (*Message_ChainResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.Message_ChainResponse]()} } func MessageFromPb(pb *synchronizerpb.Message) *Message { @@ -223,71 +223,72 @@ func (*Message) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.Message]()} } -type BlockRequest struct { +type ChainRequest struct { RequestId uint64 BlockId uint64 + LeaveIds []uint64 } -func BlockRequestFromPb(pb *synchronizerpb.BlockRequest) *BlockRequest { +func ChainRequestFromPb(pb *synchronizerpb.ChainRequest) *ChainRequest { if pb == nil { return nil } - return &BlockRequest{ + return &ChainRequest{ RequestId: pb.RequestId, BlockId: pb.BlockId, + LeaveIds: pb.LeaveIds, } } -func (m *BlockRequest) Pb() *synchronizerpb.BlockRequest { +func (m *ChainRequest) Pb() *synchronizerpb.ChainRequest { if m == nil { return nil } - pbMessage := &synchronizerpb.BlockRequest{} + pbMessage := &synchronizerpb.ChainRequest{} { pbMessage.RequestId = m.RequestId pbMessage.BlockId = m.BlockId + pbMessage.LeaveIds = m.LeaveIds } return pbMessage } -func (*BlockRequest) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.BlockRequest]()} +func (*ChainRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.ChainRequest]()} } -type BlockResponse struct { +type ChainResponse struct { RequestId uint64 Found bool - Block *blockchainpb.Block + Chain []*blockchainpb.Block } -func BlockResponseFromPb(pb *synchronizerpb.BlockResponse) *BlockResponse { +func ChainResponseFromPb(pb *synchronizerpb.ChainResponse) *ChainResponse { if pb == nil { return nil } - return &BlockResponse{ + return &ChainResponse{ RequestId: pb.RequestId, Found: pb.Found, - Block: pb.Block, + Chain: pb.Chain, } } -func (m *BlockResponse) Pb() *synchronizerpb.BlockResponse { +func (m *ChainResponse) Pb() *synchronizerpb.ChainResponse { if m == nil { return nil } - pbMessage := &synchronizerpb.BlockResponse{} + pbMessage := &synchronizerpb.ChainResponse{} { pbMessage.RequestId = m.RequestId pbMessage.Found = m.Found - if m.Block != nil { - pbMessage.Block = m.Block - } + pbMessage.Chain = m.Chain } return pbMessage } -func (*BlockResponse) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.BlockResponse]()} +func (*ChainResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*synchronizerpb.ChainResponse]()} } diff --git a/pkg/pb/eventpb/eventpb.pb.go b/pkg/pb/eventpb/eventpb.pb.go index 9b719d5df..e0aa68824 100644 --- a/pkg/pb/eventpb/eventpb.pb.go +++ b/pkg/pb/eventpb/eventpb.pb.go @@ -12,6 +12,7 @@ import ( batchdbpb "github.com/filecoin-project/mir/pkg/pb/availabilitypb/batchdbpb" batchfetcherpb "github.com/filecoin-project/mir/pkg/pb/batchfetcherpb" bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" + applicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" @@ -79,6 +80,7 @@ type Event struct { // *Event_Tpm // *Event_Communication // *Event_Synchronizer + // *Event_Application // *Event_Bcinterceptor // *Event_TestingString // *Event_TestingUint @@ -308,6 +310,13 @@ func (x *Event) GetSynchronizer() *synchronizerpb.Event { return nil } +func (x *Event) GetApplication() *applicationpb.Event { + if x, ok := x.GetType().(*Event_Application); ok { + return x.Application + } + return nil +} + func (x *Event) GetBcinterceptor() *interceptorpb.Event { if x, ok := x.GetType().(*Event_Bcinterceptor); ok { return x.Bcinterceptor @@ -448,6 +457,10 @@ type Event_Synchronizer struct { Synchronizer *synchronizerpb.Event `protobuf:"bytes,205,opt,name=synchronizer,proto3,oneof"` } +type Event_Application struct { + Application *applicationpb.Event `protobuf:"bytes,206,opt,name=application,proto3,oneof"` +} + type Event_Bcinterceptor struct { // Events for blockchain interceptor Bcinterceptor *interceptorpb.Event `protobuf:"bytes,210,opt,name=bcinterceptor,proto3,oneof"` @@ -514,6 +527,8 @@ func (*Event_Communication) isEvent_Type() {} func (*Event_Synchronizer) isEvent_Type() {} +func (*Event_Application) isEvent_Type() {} + func (*Event_Bcinterceptor) isEvent_Type() {} func (*Event_TestingString) isEvent_Type() {} @@ -878,11 +893,14 @@ var file_eventpb_eventpb_proto_rawDesc = []byte{ 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa6, 0x0c, 0x0a, 0x05, 0x45, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe1, 0x0c, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, @@ -961,80 +979,83 @@ var file_eventpb_eventpb_proto_rawDesc = []byte{ 0x12, 0x3c, 0x0a, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, - 0x52, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x12, 0x3d, - 0x0a, 0x0d, 0x62, 0x63, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x18, - 0xd2, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, - 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, - 0x62, 0x63, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x45, 0x0a, - 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0xad, + 0x52, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x12, 0x39, + 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xce, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x0d, 0x62, 0x63, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x18, 0xd2, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, + 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x63, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x45, 0x0a, 0x0d, 0x74, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0xad, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, + 0x52, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, + 0x41, 0x0a, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x18, 0xae, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, - 0x72, 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, - 0x69, 0x6e, 0x74, 0x18, 0xae, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, - 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, 0x73, 0x74, - 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, - 0x72, 0x18, 0xaf, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x65, - 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x74, 0x65, - 0x73, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x18, 0x90, 0x03, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x42, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x3a, - 0x04, 0x88, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, - 0xa6, 0x1d, 0x01, 0x22, 0x0c, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, - 0x01, 0x22, 0xc6, 0x01, 0x0a, 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x2b, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, - 0x65, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2e, 0x0a, - 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, - 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x47, 0x0a, - 0x0f, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, - 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, - 0x6c, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0a, 0x54, - 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x44, 0x65, 0x6c, 0x61, - 0x79, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, - 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, - 0x64, 0x65, 0x6c, 0x61, 0x79, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, - 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x38, 0x0a, 0x10, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x52, - 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, - 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, - 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, - 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, - 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x8a, - 0x01, 0x0a, 0x13, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, - 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, - 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, - 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, + 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, + 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0xaf, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, + 0x65, 0x73, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, + 0x29, 0x0a, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x18, 0x90, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x04, + 0x90, 0xa6, 0x1d, 0x01, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x88, 0xa6, 0x1d, 0x01, + 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x0c, + 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xc6, 0x01, 0x0a, + 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x64, + 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x48, + 0x00, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2e, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, + 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x48, 0x00, + 0x52, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x47, 0x0a, 0x0f, 0x67, 0x61, 0x72, 0x62, + 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x48, + 0x00, 0x52, 0x0e, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, + 0x65, 0x6c, 0x61, 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, + 0x6f, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0d, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x52, 0x0a, 0x05, + 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, + 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, + 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, + 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, + 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, 0x54, 0x69, 0x6d, 0x65, 0x72, + 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x38, 0x0a, 0x10, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, + 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, + 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, + 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, - 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, - 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, - 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, + 0x65, 0x6c, 0x61, 0x79, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, + 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, + 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, + 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, + 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, + 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x8a, 0x01, 0x0a, 0x13, 0x54, 0x69, + 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, + 0x74, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, + 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, + 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, + 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1079,10 +1100,11 @@ var file_eventpb_eventpb_proto_goTypes = []interface{}{ (*tpmpb.Event)(nil), // 25: tpmpb.Event (*communicationpb.Event)(nil), // 26: communicationpb.Event (*synchronizerpb.Event)(nil), // 27: synchronizerpb.Event - (*interceptorpb.Event)(nil), // 28: interceptorpb.Event - (*wrapperspb.StringValue)(nil), // 29: google.protobuf.StringValue - (*wrapperspb.UInt64Value)(nil), // 30: google.protobuf.UInt64Value - (*testerpb.Tester)(nil), // 31: testerpb.Tester + (*applicationpb.Event)(nil), // 28: applicationpb.Event + (*interceptorpb.Event)(nil), // 29: interceptorpb.Event + (*wrapperspb.StringValue)(nil), // 30: google.protobuf.StringValue + (*wrapperspb.UInt64Value)(nil), // 31: google.protobuf.UInt64Value + (*testerpb.Tester)(nil), // 32: testerpb.Tester } var file_eventpb_eventpb_proto_depIdxs = []int32{ 1, // 0: eventpb.Event.init:type_name -> eventpb.Init @@ -1109,21 +1131,22 @@ var file_eventpb_eventpb_proto_depIdxs = []int32{ 25, // 21: eventpb.Event.tpm:type_name -> tpmpb.Event 26, // 22: eventpb.Event.communication:type_name -> communicationpb.Event 27, // 23: eventpb.Event.synchronizer:type_name -> synchronizerpb.Event - 28, // 24: eventpb.Event.bcinterceptor:type_name -> interceptorpb.Event - 29, // 25: eventpb.Event.testingString:type_name -> google.protobuf.StringValue - 30, // 26: eventpb.Event.testingUint:type_name -> google.protobuf.UInt64Value - 31, // 27: eventpb.Event.tester:type_name -> testerpb.Tester - 0, // 28: eventpb.Event.next:type_name -> eventpb.Event - 3, // 29: eventpb.TimerEvent.delay:type_name -> eventpb.TimerDelay - 4, // 30: eventpb.TimerEvent.repeat:type_name -> eventpb.TimerRepeat - 5, // 31: eventpb.TimerEvent.garbage_collect:type_name -> eventpb.TimerGarbageCollect - 0, // 32: eventpb.TimerDelay.events_to_delay:type_name -> eventpb.Event - 0, // 33: eventpb.TimerRepeat.events_to_repeat:type_name -> eventpb.Event - 34, // [34:34] is the sub-list for method output_type - 34, // [34:34] is the sub-list for method input_type - 34, // [34:34] is the sub-list for extension type_name - 34, // [34:34] is the sub-list for extension extendee - 0, // [0:34] is the sub-list for field type_name + 28, // 24: eventpb.Event.application:type_name -> applicationpb.Event + 29, // 25: eventpb.Event.bcinterceptor:type_name -> interceptorpb.Event + 30, // 26: eventpb.Event.testingString:type_name -> google.protobuf.StringValue + 31, // 27: eventpb.Event.testingUint:type_name -> google.protobuf.UInt64Value + 32, // 28: eventpb.Event.tester:type_name -> testerpb.Tester + 0, // 29: eventpb.Event.next:type_name -> eventpb.Event + 3, // 30: eventpb.TimerEvent.delay:type_name -> eventpb.TimerDelay + 4, // 31: eventpb.TimerEvent.repeat:type_name -> eventpb.TimerRepeat + 5, // 32: eventpb.TimerEvent.garbage_collect:type_name -> eventpb.TimerGarbageCollect + 0, // 33: eventpb.TimerDelay.events_to_delay:type_name -> eventpb.Event + 0, // 34: eventpb.TimerRepeat.events_to_repeat:type_name -> eventpb.Event + 35, // [35:35] is the sub-list for method output_type + 35, // [35:35] is the sub-list for method input_type + 35, // [35:35] is the sub-list for extension type_name + 35, // [35:35] is the sub-list for extension extendee + 0, // [0:35] is the sub-list for field type_name } func init() { file_eventpb_eventpb_proto_init() } @@ -1230,6 +1253,7 @@ func file_eventpb_eventpb_proto_init() { (*Event_Tpm)(nil), (*Event_Communication)(nil), (*Event_Synchronizer)(nil), + (*Event_Application)(nil), (*Event_Bcinterceptor)(nil), (*Event_TestingString)(nil), (*Event_TestingUint)(nil), diff --git a/pkg/pb/eventpb/eventpb.pb.mir.go b/pkg/pb/eventpb/eventpb.pb.mir.go index 7d077a2c9..be4d2a265 100644 --- a/pkg/pb/eventpb/eventpb.pb.mir.go +++ b/pkg/pb/eventpb/eventpb.pb.mir.go @@ -32,6 +32,7 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_Tpm)(nil)), reflect.TypeOf((*Event_Communication)(nil)), reflect.TypeOf((*Event_Synchronizer)(nil)), + reflect.TypeOf((*Event_Application)(nil)), reflect.TypeOf((*Event_Bcinterceptor)(nil)), reflect.TypeOf((*Event_TestingString)(nil)), reflect.TypeOf((*Event_TestingUint)(nil)), diff --git a/pkg/pb/eventpb/oneof_interfaces.mir.go b/pkg/pb/eventpb/oneof_interfaces.mir.go index 7dbeb3cb0..18e800408 100644 --- a/pkg/pb/eventpb/oneof_interfaces.mir.go +++ b/pkg/pb/eventpb/oneof_interfaces.mir.go @@ -8,6 +8,7 @@ import ( batchdbpb "github.com/filecoin-project/mir/pkg/pb/availabilitypb/batchdbpb" batchfetcherpb "github.com/filecoin-project/mir/pkg/pb/batchfetcherpb" bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" + applicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" @@ -133,6 +134,10 @@ func (w *Event_Synchronizer) Unwrap() *synchronizerpb.Event { return w.Synchronizer } +func (w *Event_Application) Unwrap() *applicationpb.Event { + return w.Application +} + func (w *Event_Bcinterceptor) Unwrap() *interceptorpb.Event { return w.Bcinterceptor } diff --git a/pkg/pb/eventpb/types/types.mir.go b/pkg/pb/eventpb/types/types.mir.go index 8d8545b11..32a2cf2c7 100644 --- a/pkg/pb/eventpb/types/types.mir.go +++ b/pkg/pb/eventpb/types/types.mir.go @@ -4,15 +4,16 @@ package eventpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - types25 "github.com/filecoin-project/mir/codegen/model/types" + types26 "github.com/filecoin-project/mir/codegen/model/types" types13 "github.com/filecoin-project/mir/pkg/pb/apppb/types" types5 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/batchdbpb/types" types4 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/types" types6 "github.com/filecoin-project/mir/pkg/pb/batchfetcherpb/types" types2 "github.com/filecoin-project/mir/pkg/pb/bcbpb/types" + types23 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" types18 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" types21 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" - types23 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" + types24 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" types19 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" types22 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" types20 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/types" @@ -27,11 +28,11 @@ import ( types16 "github.com/filecoin-project/mir/pkg/pb/ordererpb/pprepvalidatorpb/types" types11 "github.com/filecoin-project/mir/pkg/pb/ordererpb/types" types17 "github.com/filecoin-project/mir/pkg/pb/pingpongpb/types" - types24 "github.com/filecoin-project/mir/pkg/pb/testerpb/types" + types25 "github.com/filecoin-project/mir/pkg/pb/testerpb/types" types7 "github.com/filecoin-project/mir/pkg/pb/threshcryptopb/types" types14 "github.com/filecoin-project/mir/pkg/pb/transportpb/types" - types26 "github.com/filecoin-project/mir/pkg/timer/types" - types27 "github.com/filecoin-project/mir/pkg/trantor/types" + types27 "github.com/filecoin-project/mir/pkg/timer/types" + types28 "github.com/filecoin-project/mir/pkg/trantor/types" types "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" @@ -107,14 +108,16 @@ func Event_TypeFromPb(pb eventpb.Event_Type) Event_Type { return &Event_Communication{Communication: types21.EventFromPb(pb.Communication)} case *eventpb.Event_Synchronizer: return &Event_Synchronizer{Synchronizer: types22.EventFromPb(pb.Synchronizer)} + case *eventpb.Event_Application: + return &Event_Application{Application: types23.EventFromPb(pb.Application)} case *eventpb.Event_Bcinterceptor: - return &Event_Bcinterceptor{Bcinterceptor: types23.EventFromPb(pb.Bcinterceptor)} + return &Event_Bcinterceptor{Bcinterceptor: types24.EventFromPb(pb.Bcinterceptor)} case *eventpb.Event_TestingString: return &Event_TestingString{TestingString: pb.TestingString} case *eventpb.Event_TestingUint: return &Event_TestingUint{TestingUint: pb.TestingUint} case *eventpb.Event_Tester: - return &Event_Tester{Tester: types24.TesterFromPb(pb.Tester)} + return &Event_Tester{Tester: types25.TesterFromPb(pb.Tester)} } return nil } @@ -695,13 +698,37 @@ func (*Event_Synchronizer) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Synchronizer]()} } +type Event_Application struct { + Application *types23.Event +} + +func (*Event_Application) isEvent_Type() {} + +func (w *Event_Application) Unwrap() *types23.Event { + return w.Application +} + +func (w *Event_Application) Pb() eventpb.Event_Type { + if w == nil { + return nil + } + if w.Application == nil { + return &eventpb.Event_Application{} + } + return &eventpb.Event_Application{Application: (w.Application).Pb()} +} + +func (*Event_Application) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Application]()} +} + type Event_Bcinterceptor struct { - Bcinterceptor *types23.Event + Bcinterceptor *types24.Event } func (*Event_Bcinterceptor) isEvent_Type() {} -func (w *Event_Bcinterceptor) Unwrap() *types23.Event { +func (w *Event_Bcinterceptor) Unwrap() *types24.Event { return w.Bcinterceptor } @@ -768,12 +795,12 @@ func (*Event_TestingUint) MirReflect() mirreflect.Type { } type Event_Tester struct { - Tester *types24.Tester + Tester *types25.Tester } func (*Event_Tester) isEvent_Type() {} -func (w *Event_Tester) Unwrap() *types24.Tester { +func (w *Event_Tester) Unwrap() *types25.Tester { return w.Tester } @@ -798,7 +825,7 @@ func EventFromPb(pb *eventpb.Event) *Event { return &Event{ DestModule: (types.ModuleID)(pb.DestModule), Type: Event_TypeFromPb(pb.Type), - Next: types25.ConvertSlice(pb.Next, func(t *eventpb.Event) *Event { + Next: types26.ConvertSlice(pb.Next, func(t *eventpb.Event) *Event { return EventFromPb(t) }), } @@ -814,7 +841,7 @@ func (m *Event) Pb() *eventpb.Event { if m.Type != nil { pbMessage.Type = (m.Type).Pb() } - pbMessage.Next = types25.ConvertSlice(m.Next, func(t *Event) *eventpb.Event { + pbMessage.Next = types26.ConvertSlice(m.Next, func(t *Event) *eventpb.Event { return (t).Pb() }) } @@ -981,7 +1008,7 @@ func (*TimerEvent) MirReflect() mirreflect.Type { type TimerDelay struct { EventsToDelay []*Event - Delay types26.Duration + Delay types27.Duration } func TimerDelayFromPb(pb *eventpb.TimerDelay) *TimerDelay { @@ -989,10 +1016,10 @@ func TimerDelayFromPb(pb *eventpb.TimerDelay) *TimerDelay { return nil } return &TimerDelay{ - EventsToDelay: types25.ConvertSlice(pb.EventsToDelay, func(t *eventpb.Event) *Event { + EventsToDelay: types26.ConvertSlice(pb.EventsToDelay, func(t *eventpb.Event) *Event { return EventFromPb(t) }), - Delay: (types26.Duration)(pb.Delay), + Delay: (types27.Duration)(pb.Delay), } } @@ -1002,7 +1029,7 @@ func (m *TimerDelay) Pb() *eventpb.TimerDelay { } pbMessage := &eventpb.TimerDelay{} { - pbMessage.EventsToDelay = types25.ConvertSlice(m.EventsToDelay, func(t *Event) *eventpb.Event { + pbMessage.EventsToDelay = types26.ConvertSlice(m.EventsToDelay, func(t *Event) *eventpb.Event { return (t).Pb() }) pbMessage.Delay = (uint64)(m.Delay) @@ -1017,8 +1044,8 @@ func (*TimerDelay) MirReflect() mirreflect.Type { type TimerRepeat struct { EventsToRepeat []*Event - Delay types26.Duration - RetentionIndex types27.RetentionIndex + Delay types27.Duration + RetentionIndex types28.RetentionIndex } func TimerRepeatFromPb(pb *eventpb.TimerRepeat) *TimerRepeat { @@ -1026,11 +1053,11 @@ func TimerRepeatFromPb(pb *eventpb.TimerRepeat) *TimerRepeat { return nil } return &TimerRepeat{ - EventsToRepeat: types25.ConvertSlice(pb.EventsToRepeat, func(t *eventpb.Event) *Event { + EventsToRepeat: types26.ConvertSlice(pb.EventsToRepeat, func(t *eventpb.Event) *Event { return EventFromPb(t) }), - Delay: (types26.Duration)(pb.Delay), - RetentionIndex: (types27.RetentionIndex)(pb.RetentionIndex), + Delay: (types27.Duration)(pb.Delay), + RetentionIndex: (types28.RetentionIndex)(pb.RetentionIndex), } } @@ -1040,7 +1067,7 @@ func (m *TimerRepeat) Pb() *eventpb.TimerRepeat { } pbMessage := &eventpb.TimerRepeat{} { - pbMessage.EventsToRepeat = types25.ConvertSlice(m.EventsToRepeat, func(t *Event) *eventpb.Event { + pbMessage.EventsToRepeat = types26.ConvertSlice(m.EventsToRepeat, func(t *Event) *eventpb.Event { return (t).Pb() }) pbMessage.Delay = (uint64)(m.Delay) @@ -1055,7 +1082,7 @@ func (*TimerRepeat) MirReflect() mirreflect.Type { } type TimerGarbageCollect struct { - RetentionIndex types27.RetentionIndex + RetentionIndex types28.RetentionIndex } func TimerGarbageCollectFromPb(pb *eventpb.TimerGarbageCollect) *TimerGarbageCollect { @@ -1063,7 +1090,7 @@ func TimerGarbageCollectFromPb(pb *eventpb.TimerGarbageCollect) *TimerGarbageCol return nil } return &TimerGarbageCollect{ - RetentionIndex: (types27.RetentionIndex)(pb.RetentionIndex), + RetentionIndex: (types28.RetentionIndex)(pb.RetentionIndex), } } diff --git a/protos/blockchainpb/applicationpb/applicationpb.proto b/protos/blockchainpb/applicationpb/applicationpb.proto new file mode 100644 index 000000000..c84335454 --- /dev/null +++ b/protos/blockchainpb/applicationpb/applicationpb.proto @@ -0,0 +1,48 @@ +syntax = "proto3"; + +package applicationpb; + +option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb"; + +import "mir/codegen_extensions.proto"; +import "blockchainpb/blockchainpb.proto"; +import "blockchainpb/payloadpb/payloadpb.proto"; + +message Event { + option (mir.event_class) = true; + + oneof type { + option (mir.event_type) = true; + + NewHead new_head = 1; + RegisterBlock register_block = 2; + PayloadRequest payload_request = 3; + PayloadResponse payload_response = 4; + + } +} + +message NewHead { + option (mir.event) = true; + + uint64 head_id = 1; +} + +message RegisterBlock { + option (mir.event) = true; + + blockchainpb.Block block_id = 1; +} + +message PayloadRequest { + option (mir.event) = true; + + uint64 head_id = 1; +} + +message PayloadResponse { + option (mir.event) = true; + + uint64 head_id = 1; + payloadpb.Payload payload = 2; +} diff --git a/protos/blockchainpb/bcmpb/bcmpb.proto b/protos/blockchainpb/bcmpb/bcmpb.proto index 6c05e50f2..d887e51c5 100644 --- a/protos/blockchainpb/bcmpb/bcmpb.proto +++ b/protos/blockchainpb/bcmpb/bcmpb.proto @@ -19,6 +19,8 @@ message Event { GetBlockRequest get_block_request = 3; GetBlockResponse get_block_response = 4; + GetChainRequest get_chain_request = 5; + GetChainResponse get_chain_response = 6; } } @@ -50,4 +52,22 @@ message GetBlockResponse { uint64 request_id = 1; bool found = 2; blockchainpb.Block block = 3; +} + +message GetChainRequest { + option (mir.event) = true; + + uint64 request_id = 1; + string source_module = 2 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.ModuleID"]; + + uint64 end_block_id = 3; + repeated uint64 source_block_ids = 4; +} + +message GetChainResponse { + option (mir.event) = true; + + uint64 request_id = 1; + bool success = 2; + repeated blockchainpb.Block chain = 3; // sorted from oldest to youngest } \ No newline at end of file diff --git a/protos/blockchainpb/blockchainpb.proto b/protos/blockchainpb/blockchainpb.proto index bb6472d94..357e990ea 100644 --- a/protos/blockchainpb/blockchainpb.proto +++ b/protos/blockchainpb/blockchainpb.proto @@ -4,6 +4,8 @@ package blockchainpb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb"; +import "blockchainpb/payloadpb/payloadpb.proto"; + message Blocktree { repeated Block blocks = 1; repeated uint64 leaves = 2; @@ -14,13 +16,9 @@ message Blockchain { } message Block { - uint64 block_id = 1; - uint64 previous_block_id = 2; - Payload payload = 3; + uint64 block_id = 1; + uint64 previous_block_id = 2; + payloadpb.Payload payload = 3; - int64 timestamp = 4; + int64 timestamp = 4; } - -message Payload { - string text = 1; -} \ No newline at end of file diff --git a/protos/blockchainpb/interceptorpb/interceptorpb.proto b/protos/blockchainpb/interceptorpb/interceptorpb.proto index 198137e67..e68550887 100644 --- a/protos/blockchainpb/interceptorpb/interceptorpb.proto +++ b/protos/blockchainpb/interceptorpb/interceptorpb.proto @@ -15,6 +15,7 @@ message Event { TreeUpdate tree_update = 1; NewOrphan new_orphan = 2; + AppUpdate app_update = 3; } } @@ -30,3 +31,9 @@ message NewOrphan { blockchainpb.Block orphan = 1; } + +message AppUpdate { + option (mir.event) = true; + + int64 state = 1; +} \ No newline at end of file diff --git a/protos/blockchainpb/minerpb/minerpb.proto b/protos/blockchainpb/minerpb/minerpb.proto index 120e16fef..5dd99191b 100644 --- a/protos/blockchainpb/minerpb/minerpb.proto +++ b/protos/blockchainpb/minerpb/minerpb.proto @@ -5,7 +5,7 @@ package minerpb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb"; import "mir/codegen_extensions.proto"; -import "blockchainpb/blockchainpb.proto"; +import "blockchainpb/payloadpb/payloadpb.proto"; message Event { option (mir.event_class) = true; @@ -22,7 +22,7 @@ message BlockRequest { option (mir.event) = true; uint64 head_id = 1; - blockchainpb.Payload payload = 2; + payloadpb.Payload payload = 2; } message NewHead { diff --git a/protos/blockchainpb/payloadpb/payloadpb.proto b/protos/blockchainpb/payloadpb/payloadpb.proto new file mode 100644 index 000000000..e18edf361 --- /dev/null +++ b/protos/blockchainpb/payloadpb/payloadpb.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package payloadpb; + +option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb"; + +message Payload { + // application specific payload + int64 add_minus = 1; +} \ No newline at end of file diff --git a/protos/blockchainpb/synchronizerpb/synchronizerpb.proto b/protos/blockchainpb/synchronizerpb/synchronizerpb.proto index 9c8527c60..44227ff94 100644 --- a/protos/blockchainpb/synchronizerpb/synchronizerpb.proto +++ b/protos/blockchainpb/synchronizerpb/synchronizerpb.proto @@ -32,23 +32,24 @@ message Message { oneof type { option (net.message_type) = true; - BlockRequest block_request = 1; - BlockResponse block_response = 2; + ChainRequest chain_request = 1; + ChainResponse chain_response = 2; } } -message BlockRequest { +message ChainRequest { option (net.message) = true; - uint64 request_id = 1; - uint64 block_id = 2; + uint64 request_id = 1; + uint64 block_id = 2; + repeated uint64 leave_ids = 3; } -message BlockResponse { +message ChainResponse { option (net.message) = true; - uint64 request_id = 1; - bool found = 2; - blockchainpb.Block block = 3; // possibly undefined (proto3 spec no-longer supports optional/required...) + uint64 request_id = 1; + bool found = 2; + repeated blockchainpb.Block chain = 3; // possibly undefined (proto3 spec no-longer supports optional/required...) } diff --git a/protos/eventpb/eventpb.proto b/protos/eventpb/eventpb.proto index 23cb0bcaa..2a21fce98 100644 --- a/protos/eventpb/eventpb.proto +++ b/protos/eventpb/eventpb.proto @@ -28,6 +28,7 @@ import "blockchainpb/minerpb/minerpb.proto"; import "blockchainpb/tpmpb/tpmpb.proto"; import "blockchainpb/communicationpb/communicationpb.proto"; import "blockchainpb/synchronizerpb/synchronizerpb.proto"; +import "blockchainpb/applicationpb/applicationpb.proto"; import "blockchainpb/interceptorpb/interceptorpb.proto"; import "mir/codegen_extensions.proto"; @@ -75,6 +76,7 @@ message Event { tpmpb.Event tpm = 203; communicationpb.Event communication = 204; synchronizerpb.Event synchronizer = 205; + applicationpb.Event application = 206; // Events for blockchain interceptor interceptorpb.Event bcinterceptor = 210; diff --git a/protos/generate.go b/protos/generate.go index 59e51c488..66cc351a8 100644 --- a/protos/generate.go +++ b/protos/generate.go @@ -50,6 +50,8 @@ package protos //go:generate protoc-events blockchainpb/tpmpb/tpmpb.proto //go:generate protoc-events blockchainpb/communicationpb/communicationpb.proto //go:generate protoc-events blockchainpb/synchronizerpb/synchronizerpb.proto +//go:generate protoc-events blockchainpb/applicationpb/applicationpb.proto +//go:generate protoc-events blockchainpb/payloadpb/payloadpb.proto //go:generate protoc-events blockchainpb/interceptorpb/interceptorpb.proto // Build the custom code generators. @@ -88,6 +90,8 @@ package protos //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" +//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" +//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" // Generate other things. diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go new file mode 100644 index 000000000..497633004 --- /dev/null +++ b/samples/blockchain/application/application.go @@ -0,0 +1,69 @@ +package application + +// app module + +import ( + "math/rand" + + "github.com/filecoin-project/mir/pkg/dsl" + "github.com/filecoin-project/mir/pkg/logging" + "github.com/filecoin-project/mir/pkg/modules" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + applicationpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/dsl" + interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + "github.com/filecoin-project/mir/samples/blockchain/utils" +) + +type ApplicationModule struct { + m *dsl.Module + state *state + logger logging.Logger +} + +func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { + // NOTE: reject request for head that doesn't match current head? + am.logger.Log(logging.LevelDebug, "Processing payload request", "headId", utils.FormatBlockId(head_id)) + + var summand int64 + if rand.Intn(2) == 0 { + summand = -1 + } else { + summand = 1 + } + + applicationpbdsl.PayloadResponse(*am.m, "miner", head_id, &payloadpb.Payload{AddMinus: summand}) + + return nil +} + +func (am *ApplicationModule) handleRegisterBlock(block *blockchainpb.Block) error { + am.state.applyBlock(block) + am.logger.Log(logging.LevelDebug, "Registered new block", "block_id", utils.FormatBlockId(block.BlockId)) + return nil +} + +func (am *ApplicationModule) handleNewHead(head_id uint64) error { + previousState := am.state.getCurrentState() + am.state.setHead(head_id) + currentState := am.state.getCurrentState() + interceptorpbdsl.AppUpdate(*am.m, "devnull", currentState) + am.logger.Log(logging.LevelInfo, "Application state updated", "previousState", previousState, "currentState", currentState, "headId", utils.FormatBlockId(head_id)) + return nil +} + +func NewApplication(logger logging.Logger) modules.PassiveModule { + + m := dsl.NewModule("application") + am := &ApplicationModule{m: &m, state: initState(), logger: logger} + + dsl.UponInit(m, func() error { + return nil + }) + + applicationpbdsl.UponPayloadRequest(m, am.handlePayloadRequest) + applicationpbdsl.UponNewHead(m, am.handleNewHead) + applicationpbdsl.UponRegisterBlock(m, am.handleRegisterBlock) + + return m +} diff --git a/samples/blockchain/application/state.go b/samples/blockchain/application/state.go new file mode 100644 index 000000000..f4feb1bf1 --- /dev/null +++ b/samples/blockchain/application/state.go @@ -0,0 +1,41 @@ +package application + +import "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + +type state struct { + head uint64 + state map[uint64]int64 + headCounter int +} + +func initState() *state { + + initialState := state{ + head: 0, // referenced by genesis block + state: make(map[uint64]int64), + } + + initialState.state[0] = 0 + + return &initialState +} + +func (s *state) applyBlock(block *blockchainpb.Block) { + if _, ok := s.state[block.PreviousBlockId]; !ok { + panic("previous block not found") + } + + s.state[block.BlockId] = s.state[block.PreviousBlockId] + block.Payload.AddMinus + +} + +func (s *state) setHead(head uint64) { + if _, ok := s.state[head]; !ok { + panic("head not found") + } + s.head = head +} + +func (s *state) getCurrentState() int64 { + return s.state[s.head] +} diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index f849e0613..38a12b2ee 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -9,11 +9,14 @@ import ( "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + applicationpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/dsl" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" minerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" t "github.com/filecoin-project/mir/pkg/types" + "github.com/filecoin-project/mir/samples/blockchain/utils" "golang.org/x/exp/maps" ) @@ -76,11 +79,11 @@ func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) // TODO: might need to add some "cleanup" to handle very old leaves in case this grows too much func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { - bcm.logger.Log(logging.LevelInfo, "Adding block...", "blockId", formatBlockId(block.BlockId)) + bcm.logger.Log(logging.LevelInfo, "Adding block...", "blockId", utils.FormatBlockId(block.BlockId)) // check if block is already in the leaves, reject if so if _, ok := bcm.leaves[block.BlockId]; ok { - bcm.logger.Log(logging.LevelDebug, "Block already in leaves - ignore", "blockId", formatBlockId(block.BlockId)) + bcm.logger.Log(logging.LevelDebug, "Block already in leaves - ignore", "blockId", utils.FormatBlockId(block.BlockId)) return nil } @@ -88,7 +91,7 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { // check leaves for parent with its id // return nil if it isn't there if parent, ok := bcm.leaves[parentId]; ok { - bcm.logger.Log(logging.LevelDebug, "Found parend in leaves", "blockId", formatBlockId(block.BlockId), "parentId", formatBlockId(parentId)) + bcm.logger.Log(logging.LevelDebug, "Found parend in leaves", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(parentId)) blockNode := bcmBlock{block: block, parent: parent, depth: parent.depth + 1} bcm.blocks = append(bcm.blocks, blockNode) // replace leave @@ -104,13 +107,13 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { if hit := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { // check if curr matches the block to be added - if so, ignore if currBlock.block.BlockId == block.BlockId { - bcm.logger.Log(logging.LevelDebug, "Block already in tree", "blockId", formatBlockId(block.BlockId)) + bcm.logger.Log(logging.LevelDebug, "Block already in tree", "blockId", utils.FormatBlockId(block.BlockId)) return false, ErrNodeAlreadyInTree } // check if curr is parent if currBlock.block.BlockId == parentId { - bcm.logger.Log(logging.LevelDebug, "Found parend in tree", "blockId", formatBlockId(block.BlockId), "parentId", formatBlockId(parentId)) + bcm.logger.Log(logging.LevelDebug, "Found parend in tree", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(parentId)) blockNode := bcmBlock{block: block, parent: currBlock, depth: currBlock.depth + 1, scanCount: 0} // add new leave bcm.leaves[block.BlockId] = &blockNode @@ -124,13 +127,22 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { return false, nil }); hit == nil { - bcm.logger.Log(logging.LevelInfo, "Couldn't find parent", "blockId", formatBlockId(block.BlockId)) + bcm.logger.Log(logging.LevelInfo, "Couldn't find parent", "blockId", utils.FormatBlockId(block.BlockId)) return ErrParentNotFound } return nil } +func (bcm *bcmModule) findBlock(blockId uint64) *bcmBlock { + return bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { + if currBlock.block.BlockId == blockId { + return true, nil + } + return false, nil + }) +} + func (bcm *bcmModule) getHead() *blockchainpb.Block { return bcm.head.block } @@ -142,19 +154,25 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { if err == ErrParentNotFound { // ask synchronizer for help leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.BlockId) - bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", formatBlockId(block.BlockId), "leaves+genesis", formatBlockIdSlice(leavesPlusGenesis)) + bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", utils.FormatBlockId(block.BlockId), "leaves+genesis", utils.FormatBlockIdSlice(leavesPlusGenesis)) synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", block, leavesPlusGenesis) + // announce orphan block to interceptor + interceptorpbdsl.NewOrphan(*bcm.m, "devnull", block) } else { // some other error - bcm.logger.Log(logging.LevelError, "Unexpected error adding block", "blockId", formatBlockId(block.BlockId), "error", err) + bcm.logger.Log(logging.LevelError, "Unexpected error adding block", "blockId", utils.FormatBlockId(block.BlockId), "error", err) } - + return } + // register block in app + applicationpbdsl.RegisterBlock(*bcm.m, "application", block) + // if head changed, trigger get tpm to prep new block if newHead := bcm.getHead(); newHead != currentHead { // new head - bcm.logger.Log(logging.LevelInfo, "Head changed", "head", formatBlockId(newHead.BlockId)) + bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.BlockId)) + applicationpbdsl.NewHead(*bcm.m, "application", newHead.BlockId) minerpbdsl.NewHead(*bcm.m, "miner", newHead.BlockId) } @@ -163,20 +181,36 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { } func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { - currentHead := bcm.getHead() + blockIds := make([]uint64, 0, len(blocks)) + for _, v := range blocks { + blockIds = append(blockIds, v.BlockId) + } + bcm.logger.Log(logging.LevelDebug, "Adding new chain", "chain", utils.FormatBlockIdSlice(blockIds)) // insert block for _, block := range blocks { if err := bcm.addBlock(block); err != nil { - bcm.logger.Log(logging.LevelError, "Unexpected error adding block from chain", "blockId", formatBlockId(block.BlockId), "error", err) + if err == ErrNodeAlreadyInTree { + // ignore + bcm.logger.Log(logging.LevelDebug, "Block already in tree - ignore", "blockId", utils.FormatBlockId(block.BlockId)) + continue + } else { + // some other error + bcm.logger.Log(logging.LevelError, "Unexpected error adding block from chain", "blockId", utils.FormatBlockId(block.BlockId), "error", err, "leaves", utils.FormatBlockIdSlice(maps.Keys(bcm.leaves))) + panic("Unexpected error adding block from chain") + return + } } + // register block in app + applicationpbdsl.RegisterBlock(*bcm.m, "application", block) + } - // if head changed, trigger get tpm to prep new block + // if head changed, trigger get tpm to prep new block (only need to do this once) if newHead := bcm.getHead(); newHead != currentHead { // new head - - bcm.logger.Log(logging.LevelInfo, "Head changed", "head", formatBlockId(newHead.BlockId)) + bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.BlockId)) + applicationpbdsl.NewHead(*bcm.m, "application", newHead.BlockId) minerpbdsl.NewHead(*bcm.m, "miner", newHead.BlockId) } @@ -212,11 +246,11 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { genesis := &blockchainpb.Block{ BlockId: 0, PreviousBlockId: 0, - Payload: &blockchainpb.Payload{Text: "genesis"}, + Payload: &payloadpb.Payload{AddMinus: 0}, Timestamp: 0, // unix 0 } - hash := hashBlock(genesis) + hash := utils.HashBlock(genesis) genesis.BlockId = hash genesisBcm := bcmBlock{block: genesis, parent: nil, depth: 0} @@ -236,14 +270,17 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { dsl.UponInit(m, func() error { - // kick off tpm with genisis block + // start with genesis block + // setup app state accordingly (expects first block to have 0 as previous block id) + applicationpbdsl.RegisterBlock(m, "application", genesis) + applicationpbdsl.NewHead(m, "application", hash) + // start mining on genesis block minerpbdsl.NewHead(m, "miner", hash) return nil }) bcmpbdsl.UponNewBlock(m, func(block *blockchainpb.Block) error { - // self-mined block - no need to verify bcm.handleNewBlock(block) return nil }) @@ -255,29 +292,66 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { }) bcmpbdsl.UponGetBlockRequest(m, func(requestID uint64, sourceModule t.ModuleID, blockID uint64) error { - bcm.logger.Log(logging.LevelInfo, "Received get block request", "requestId", formatBlockId(requestID), "sourceModule", sourceModule) + bcm.logger.Log(logging.LevelInfo, "Received get block request", "requestId", utils.FormatBlockId(requestID), "sourceModule", sourceModule) // check if block is in tree - hit := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { - // check if curr matches the block to be added - if so, ignore - if currBlock.block.BlockId == blockID { - bcm.logger.Log(logging.LevelDebug, "Found block in tree", "requestId", formatBlockId(requestID)) - // respond to sync request - return true, nil - } - - return false, nil - }) + hit := bcm.findBlock(blockID) if hit == nil { - bcm.logger.Log(logging.LevelDebug, "Block not found", "requestId", formatBlockId(requestID)) + bcm.logger.Log(logging.LevelDebug, "Block not found", "requestId", utils.FormatBlockId(requestID)) bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) return nil } + bcm.logger.Log(logging.LevelDebug, "Found block in tree", "requestId", utils.FormatBlockId(requestID)) bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block) return nil }) + bcmpbdsl.UponGetChainRequest(m, func(requestID uint64, sourceModule t.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error { + bcm.logger.Log(logging.LevelInfo, "Received get chain request", "requestId", utils.FormatBlockId(requestID), "sourceModule", sourceModule, "endBlockId", utils.FormatBlockId(endBlockId), "sourceBlockIds", utils.FormatBlockIdSlice(sourceBlockIds)) + chain := make([]*blockchainpb.Block, 0) + // for easier lookup... + sourceBlockIdsMap := make(map[uint64]uint64) + for _, v := range sourceBlockIds { + sourceBlockIdsMap[v] = v + } + currentBlock := bcm.findBlock(endBlockId) + + if currentBlock == nil { + bcm.logger.Log(logging.LevelDebug, "Block not found - can't build chain", "requestId", utils.FormatBlockId(requestID)) + bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) + return nil + } + + bcm.logger.Log(logging.LevelDebug, "Found block in tree - building chain", "requestId", utils.FormatBlockId(requestID)) + + // initialize chain + chain = append(chain, currentBlock.block) + + for currentBlock.parent != nil { + // if parent is in sourceBlockIds, stop + if _, ok := sourceBlockIdsMap[currentBlock.parent.block.BlockId]; ok { + // chain build + bcm.logger.Log(logging.LevelDebug, "Found link with sourceBlockIds - chain build", "requestId", utils.FormatBlockId(requestID)) + // reversing chain to send it in the right order + for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 { + chain[i], chain[j] = chain[j], chain[i] + } + bcmpbdsl.GetChainResponse(*bcm.m, sourceModule, requestID, true, chain) + return nil + } + + chain = append(chain, currentBlock.parent.block) + currentBlock = currentBlock.parent + } + + // if we get here, we didn't find a link with sourceBlockIds + bcm.logger.Log(logging.LevelDebug, "No link with sourceBlockIds - can't build chain", "requestId", utils.FormatBlockId(requestID)) + bcmpbdsl.GetChainResponse(*bcm.m, sourceModule, requestID, false, nil) + return nil + + }) + return m } diff --git a/samples/blockchain/communication.go b/samples/blockchain/communication.go index f6d1a7708..0a2db85d0 100644 --- a/samples/blockchain/communication.go +++ b/samples/blockchain/communication.go @@ -12,6 +12,7 @@ import ( communicationpbmsgs "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/msgs" transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" t "github.com/filecoin-project/mir/pkg/types" + "github.com/filecoin-project/mir/samples/blockchain/utils" ) func NewCommunication(otherNodes []t.NodeID, mangle bool, logger logging.Logger) modules.PassiveModule { @@ -24,7 +25,7 @@ func NewCommunication(otherNodes []t.NodeID, mangle bool, logger logging.Logger) communicationpbdsl.UponNewBlock(m, func(block *blockchainpb.Block) error { // take the block and send it to all other nodes - logger.Log(logging.LevelDebug, "broadcasting block", "blockId", formatBlockId(block.BlockId), "manlge", mangle) + logger.Log(logging.LevelDebug, "broadcasting block", "blockId", utils.FormatBlockId(block.BlockId), "manlge", mangle) if mangle { // send via mangles @@ -39,7 +40,7 @@ func NewCommunication(otherNodes []t.NodeID, mangle bool, logger logging.Logger) }) communicationpbdsl.UponNewBlockMessageReceived(m, func(from t.NodeID, block *blockchainpb.Block) error { - logger.Log(logging.LevelDebug, "new block received", "blockId", formatBlockId(block.BlockId)) + logger.Log(logging.LevelDebug, "new block received", "blockId", utils.FormatBlockId(block.BlockId)) bcmpbdsl.NewBlock(m, "bcm", block) return nil diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index 4136eb3dd..7beb00eda 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -16,6 +16,7 @@ import ( trantorpbtypes "github.com/filecoin-project/mir/pkg/pb/trantorpb/types" "github.com/filecoin-project/mir/pkg/timer" t "github.com/filecoin-project/mir/pkg/types" + application "github.com/filecoin-project/mir/samples/blockchain/application" wsinterceptor "github.com/filecoin-project/mir/samples/blockchain/wsInterceptor" ) @@ -73,7 +74,7 @@ func main() { mangler, err := eventmangler.NewModule( eventmangler.ModuleConfig{Self: "mangler", Dest: "transport", Timer: "timer"}, - &eventmangler.ModuleParams{MinDelay: time.Second / 1000, MaxDelay: 2 * time.Second, DropRate: 0.05}, + &eventmangler.ModuleParams{MinDelay: time.Second / 1000, MaxDelay: 2 * time.Second, DropRate: 0.01}, ) if err != nil { panic(err) @@ -88,11 +89,12 @@ func main() { "bcm": NewBCM(logging.Decorate(logger, "BCM:\t")), "miner": NewMiner(logging.Decorate(logger, "Miner:\t")), "communication": NewCommunication(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), - "tpm": NewTPM(logging.Decorate(logger, "TPM:\t")), - "synchronizer": NewSynchronizer(otherNodes, false, logging.Decorate(logger, "Sync:\t")), - "timer": timer, - "mangler": mangler, - "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor + // "tpm": NewTPM(logging.Decorate(logger, "TPM:\t")), + "application": application.NewApplication(logging.Decorate(logger, "App:\t")), + "synchronizer": NewSynchronizer(otherNodes, false, logging.Decorate(logger, "Sync:\t")), + "timer": timer, + "mangler": mangler, + "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor }, wsinterceptor.NewWsInterceptor( func(e *eventpb.Event) bool { diff --git a/samples/blockchain/miner.go b/samples/blockchain/miner.go index bc6aaaba5..4df4d0bb2 100644 --- a/samples/blockchain/miner.go +++ b/samples/blockchain/miner.go @@ -9,11 +9,14 @@ import ( "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" + applicationpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/events" bcmpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/events" communicationpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/events" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" - tpmpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/events" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" "github.com/filecoin-project/mir/pkg/pb/eventpb" + "github.com/filecoin-project/mir/samples/blockchain/utils" "github.com/go-errors/errors" "github.com/mitchellh/hashstructure" ) @@ -24,7 +27,7 @@ const ( type blockRequest struct { HeadId uint64 - Payload *blockchainpb.Payload + Payload *payloadpb.Payload } type minerModule struct { @@ -52,13 +55,18 @@ func (m *minerModule) ApplyEvents(context context.Context, eventList *events.Eve switch e := event.Type.(type) { case *eventpb.Event_Init: go m.mineWorkerManager() + case *eventpb.Event_Application: + switch e := e.Application.Type.(type) { + case *applicationpb.Event_PayloadResponse: + m.blockRequests <- blockRequest{e.PayloadResponse.GetHeadId(), e.PayloadResponse.GetPayload()} + return nil + default: + return errors.Errorf("unknown event: %T", e) + } case *eventpb.Event_Miner: switch e := e.Miner.Type.(type) { - case *minerpb.Event_BlockRequest: - m.blockRequests <- blockRequest{e.BlockRequest.GetHeadId(), e.BlockRequest.GetPayload()} - return nil case *minerpb.Event_NewHead: - m.eventsOut <- events.ListOf(tpmpbevents.NewBlockRequest("tpm", e.NewHead.GetHeadId()).Pb()) + m.eventsOut <- events.ListOf(applicationpbevents.PayloadRequest("application", e.NewHead.GetHeadId()).Pb()) default: return errors.Errorf("unknown miner event: %T", e) } @@ -77,12 +85,12 @@ func (m *minerModule) mineWorkerManager() { blockRequest := <-m.blockRequests cancel() // abort ongoing mining ctx, cancel = context.WithCancel(context.Background()) // new context for new mining - m.logger.Log(logging.LevelInfo, "New mining operation", "headId", formatBlockId(blockRequest.HeadId)) + m.logger.Log(logging.LevelInfo, "New mining operation", "headId", utils.FormatBlockId(blockRequest.HeadId)) go func() { delay := time.Duration(rand.ExpFloat64() * float64(time.Minute) * EXPONENTIAL_MINUTE_FACTOR) select { case <-ctx.Done(): - m.logger.Log(logging.LevelDebug, "Mining aborted", "headId", formatBlockId(blockRequest.HeadId)) + m.logger.Log(logging.LevelDebug, "Mining aborted", "headId", utils.FormatBlockId(blockRequest.HeadId)) return case <-time.After(delay): block := &blockchainpb.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: time.Now().Unix()} @@ -93,7 +101,7 @@ func (m *minerModule) mineWorkerManager() { } block.BlockId = hash // Block mined! Broadcast to blockchain and send event to bcm. - m.logger.Log(logging.LevelInfo, "Block mined", "headId", formatBlockId(hash), "parentId", formatBlockId(blockRequest.HeadId)) + m.logger.Log(logging.LevelInfo, "Block mined", "headId", utils.FormatBlockId(hash), "parentId", utils.FormatBlockId(blockRequest.HeadId)) m.eventsOut <- events.ListOf(bcmpbevents.NewBlock("bcm", block).Pb(), communicationpbevents.NewBlock("communication", block).Pb()) return } diff --git a/samples/blockchain/synchronizer.go b/samples/blockchain/synchronizer.go index 77e0112d5..0084084b1 100644 --- a/samples/blockchain/synchronizer.go +++ b/samples/blockchain/synchronizer.go @@ -15,6 +15,7 @@ import ( synchronizerpbmsgs "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/msgs" transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" t "github.com/filecoin-project/mir/pkg/types" + "github.com/filecoin-project/mir/samples/blockchain/utils" ) const ( @@ -51,7 +52,6 @@ type syncRequest struct { blockID uint64 leaveIDs []uint64 nodesContacted []int // index of node in otherNodes - chain []*blockchainpb.Block } func (sm *synchronizerModule) registerSyncRequest(block *blockchainpb.Block, leaves []uint64) (uint64, error) { @@ -61,7 +61,7 @@ func (sm *synchronizerModule) registerSyncRequest(block *blockchainpb.Block, lea return 0, ErrRequestAlreadyExists } - sm.syncRequests[requestId] = &syncRequest{requestID: requestId, blockID: block.BlockId, leaveIDs: leaves, nodesContacted: []int{}, chain: []*blockchainpb.Block{block}} + sm.syncRequests[requestId] = &syncRequest{requestID: requestId, blockID: block.BlockId, leaveIDs: leaves, nodesContacted: []int{}} return requestId, nil } @@ -89,12 +89,12 @@ func (sm *synchronizerModule) contactNextNode(requestID uint64) error { request.nodesContacted = append(request.nodesContacted, nextNodeIndex) nextNode := sm.otherNodes[nextNodeIndex] - sm.internalLogger.Log(logging.LevelDebug, "asking node for block", "block", formatBlockId(request.blockID), "node", nextNode, "mangle", sm.mangle) + sm.internalLogger.Log(logging.LevelDebug, "asking node for block", "block", utils.FormatBlockId(request.blockID), "node", nextNode, "mangle", sm.mangle) // send request if sm.mangle { - transportpbdsl.SendMessage(*sm.m, "mangler", synchronizerpbmsgs.BlockRequest("synchronizer", requestID, request.blockID), []t.NodeID{nextNode}) + transportpbdsl.SendMessage(*sm.m, "mangler", synchronizerpbmsgs.ChainRequest("synchronizer", requestID, request.blockID, request.leaveIDs), []t.NodeID{nextNode}) } else { - transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.BlockRequest("synchronizer", requestID, request.blockID), []t.NodeID{nextNode}) + transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.ChainRequest("synchronizer", requestID, request.blockID, request.leaveIDs), []t.NodeID{nextNode}) } return nil @@ -105,20 +105,20 @@ func (sm *synchronizerModule) handleSyncRequest(orphanBlock *blockchainpb.Block, // register request requestId, err := sm.registerSyncRequest(orphanBlock, leaveIds) if err != nil { - sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", formatBlockId(orphanBlock.BlockId), "leaveIds", formatBlockIdSlice(leaveIds)) + sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", utils.FormatBlockId(orphanBlock.BlockId), "leaveIds", utils.FormatBlockIdSlice(leaveIds)) return err } if err := sm.contactNextNode(requestId); err != nil { // could check for 'ErrNoMoreNodes' here but there should always be at least one node - sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", formatBlockId(orphanBlock.BlockId), "leaveIds", formatBlockIdSlice(leaveIds)) + sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", utils.FormatBlockId(orphanBlock.BlockId), "leaveIds", utils.FormatBlockIdSlice(leaveIds)) return err } return nil } -func (sm *synchronizerModule) handleBlockResponseReceived(from t.NodeID, requestID uint64, found bool, block *blockchainpb.Block) error { +func (sm *synchronizerModule) handleChainResponseReceived(from t.NodeID, requestID uint64, found bool, chain []*blockchainpb.Block) error { // check if request exists request, ok := sm.syncRequests[requestID] if !ok { @@ -136,38 +136,23 @@ func (sm *synchronizerModule) handleBlockResponseReceived(from t.NodeID, request } // send request to the next node if err := sm.contactNextNode(requestID); err == ErrNoMoreNodes { - sm.internalLogger.Log(logging.LevelError, "no more nodes to contact - forgetting request - shouldn't happen if not mangling", "error", err.Error(), "requestId", formatBlockId(requestID), "mangle", sm.mangle) + sm.internalLogger.Log(logging.LevelError, "no more nodes to contact - forgetting request - shouldn't happen if not mangling", "error", err.Error(), "requestId", utils.FormatBlockId(requestID), "mangle", sm.mangle) delete(sm.syncRequests, requestID) if sm.mangle { panic(err) // NOTE: this should never happen as long as we don't start mangling } } else if err != nil { - sm.internalLogger.Log(logging.LevelError, "Unexpected error contacting next node", "error", err.Error(), "requestId", formatBlockId(requestID), "mangle", sm.mangle) + sm.internalLogger.Log(logging.LevelError, "Unexpected error contacting next node", "error", err.Error(), "requestId", utils.FormatBlockId(requestID), "mangle", sm.mangle) panic(err) } return nil } - // we got a block - request.chain = append(request.chain, block) - // check if block.parentId is in leaveIDs - for _, leaveID := range request.leaveIDs { - if block.PreviousBlockId == leaveID { - // we are done, send the chain to the blockchain manager, delete the request - for i, j := 0, len(request.chain)-1; i < j; i, j = i+1, j-1 { - request.chain[i], request.chain[j] = request.chain[j], request.chain[i] - } - bcmpbdsl.NewChain(*sm.m, "bcm", request.chain) - delete(sm.syncRequests, requestID) - return nil - } - } + delete(sm.syncRequests, requestID) + bcmpbdsl.NewChain(*sm.m, "bcm", chain) - // update request - request.blockID = block.PreviousBlockId - request.nodesContacted = []int{} - sm.contactNextNode(requestID) + // we got a block return nil } @@ -179,7 +164,7 @@ type getRequest struct { from t.NodeID } -func (sm *synchronizerModule) handleBlockRequestReceived(from t.NodeID, requestID uint64, blockID uint64) error { +func (sm *synchronizerModule) handleChainRequestReceived(from t.NodeID, requestID uint64, blockID uint64, leaveIds []uint64) error { // check if request is already being processed if _, ok := sm.getRequests[requestID]; ok { return ErrRequestAlreadyExists @@ -189,26 +174,26 @@ func (sm *synchronizerModule) handleBlockRequestReceived(from t.NodeID, requestI sm.getRequests[requestID] = &getRequest{requestID: requestID, from: from} // send get request to blockchain manager - bcmpbdsl.GetBlockRequest(*sm.m, "bcm", requestID, "synchronizer", blockID) + bcmpbdsl.GetChainRequest(*sm.m, "bcm", requestID, "synchronizer", blockID, leaveIds) return nil } -func (sm *synchronizerModule) handleGetBlockResponse(requestID uint64, found bool, block *blockchainpb.Block) error { +func (sm *synchronizerModule) handleGetChainResponse(requestID uint64, found bool, chain []*blockchainpb.Block) error { request, ok := sm.getRequests[requestID] if !ok { - sm.externaLogger.Log(logging.LevelError, "Unknown get block request", "requestId", formatBlockId(requestID), "mangle", sm.mangle) + sm.externaLogger.Log(logging.LevelError, "Unknown get block request", "requestId", utils.FormatBlockId(requestID), "mangle", sm.mangle) return ErrUnkownGetBlockRequest } - sm.externaLogger.Log(logging.LevelInfo, "Responsing to block request", "requestId", formatBlockId(requestID), "found", found, "mangle", sm.mangle) + sm.externaLogger.Log(logging.LevelInfo, "Responsing to block request", "requestId", utils.FormatBlockId(requestID), "found", found, "mangle", sm.mangle) // respond to sync request if sm.mangle { - transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.BlockResponse("synchronizer", requestID, found, block), []t.NodeID{request.from}) + transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.ChainResponse("synchronizer", requestID, found, chain), []t.NodeID{request.from}) } else { - transportpbdsl.SendMessage(*sm.m, "mangler", synchronizerpbmsgs.BlockResponse("synchronizer", requestID, found, block), []t.NodeID{request.from}) + transportpbdsl.SendMessage(*sm.m, "mangler", synchronizerpbmsgs.ChainResponse("synchronizer", requestID, found, chain), []t.NodeID{request.from}) } // delete request @@ -235,11 +220,11 @@ func NewSynchronizer(otherNodes []t.NodeID, mangle bool, logger logging.Logger) // outgoing sync requests synchronizerpbdsl.UponSyncRequest(m, sm.handleSyncRequest) - synchronizerpbdsl.UponBlockResponseReceived(m, sm.handleBlockResponseReceived) + synchronizerpbdsl.UponChainResponseReceived(m, sm.handleChainResponseReceived) // for incoming sync requests - synchronizerpbdsl.UponBlockRequestReceived(m, sm.handleBlockRequestReceived) - bcmpbdsl.UponGetBlockResponse(m, sm.handleGetBlockResponse) + synchronizerpbdsl.UponChainRequestReceived(m, sm.handleChainRequestReceived) + bcmpbdsl.UponGetChainResponse(m, sm.handleGetChainResponse) return m } diff --git a/samples/blockchain/tpm.go b/samples/blockchain/tpm.go index bfa43f5b6..41c5a0787 100644 --- a/samples/blockchain/tpm.go +++ b/samples/blockchain/tpm.go @@ -9,16 +9,16 @@ import ( "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb" minerdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/dsl" + "github.com/filecoin-project/mir/samples/blockchain/utils" ) -const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - func NewTPM(logger logging.Logger) modules.PassiveModule { m := dsl.NewModule("tpm") + r := rand.New(rand.NewSource(time.Now().UnixNano())) dsl.UponInit(m, func() error { return nil @@ -27,14 +27,9 @@ func NewTPM(logger logging.Logger) modules.PassiveModule { tpmpb.UponNewBlockRequest(m, func(headId uint64) error { // just generating random payloads for now // generate random string - logger.Log(logging.LevelInfo, "Processing block request", "headId", formatBlockId(headId)) - var seed *rand.Rand = rand.New(rand.NewSource(time.Now().UnixNano())) - length := seed.Intn(15) - b := make([]byte, length) - for i := range b { - b[i] = charset[seed.Intn(len(charset))] - } - payload := &blockchainpb.Payload{Text: string(b)} + logger.Log(logging.LevelInfo, "Processing block request", "headId", utils.FormatBlockId(headId)) + value := int64(r.Intn(10000)) + payload := &payloadpb.Payload{AddMinus: value} minerdsl.BlockRequest(m, "miner", headId, payload) diff --git a/samples/blockchain/utils.go b/samples/blockchain/utils.go deleted file mode 100644 index 9feee5b7c..000000000 --- a/samples/blockchain/utils.go +++ /dev/null @@ -1,34 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/filecoin-project/mir/pkg/pb/blockchainpb" - "github.com/mitchellh/hashstructure" -) - -func hashBlock(block *blockchainpb.Block) uint64 { - hashBlock := &blockchainpb.Block{BlockId: 0, PreviousBlockId: block.PreviousBlockId, Payload: block.Payload, Timestamp: block.Timestamp} - hash, err := hashstructure.Hash(hashBlock, nil) - if err != nil { - panic(err) - } - return hash -} - -func formatBlockId(blockId uint64) string { - strId := fmt.Sprint(blockId) - // just in case we have a very small id for some reason - if len(strId) > 6 { - strId = strId[:6] - } - return strId -} - -func formatBlockIdSlice(blockIds []uint64) string { - str := "" - for _, blockId := range blockIds { - str += formatBlockId(blockId) + " " - } - return str -} diff --git a/samples/blockchain/utils/format.go b/samples/blockchain/utils/format.go new file mode 100644 index 000000000..583dc2186 --- /dev/null +++ b/samples/blockchain/utils/format.go @@ -0,0 +1,22 @@ +package utils + +import ( + "fmt" +) + +func FormatBlockId(blockId uint64) string { + strId := fmt.Sprint(blockId) + // just in case we have a very small id for some reason + if len(strId) > 6 { + strId = strId[:6] + } + return strId +} + +func FormatBlockIdSlice(blockIds []uint64) string { + str := "" + for _, blockId := range blockIds { + str += FormatBlockId(blockId) + " " + } + return str +} diff --git a/samples/blockchain/utils/hash.go b/samples/blockchain/utils/hash.go new file mode 100644 index 000000000..7528b9675 --- /dev/null +++ b/samples/blockchain/utils/hash.go @@ -0,0 +1,15 @@ +package utils + +import ( + "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + "github.com/mitchellh/hashstructure" +) + +func HashBlock(block *blockchainpb.Block) uint64 { + hashBlock := &blockchainpb.Block{BlockId: 0, PreviousBlockId: block.PreviousBlockId, Payload: block.Payload, Timestamp: block.Timestamp} + hash, err := hashstructure.Hash(hashBlock, nil) + if err != nil { + panic(err) + } + return hash +} From d92cbc250eef7fe70e7f7275aa7f972a27ee7334 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Wed, 6 Dec 2023 17:38:23 +0100 Subject: [PATCH 08/46] make request ids strings and node specific, error stuff --- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go | 32 +++++------ pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go | 8 +-- pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go | 8 +-- .../blockchainpb/bcmpb/events/events.mir.go | 8 +-- pkg/pb/blockchainpb/bcmpb/types/types.mir.go | 8 +-- .../synchronizerpb/dsl/messages.mir.go | 4 +- .../synchronizerpb/msgs/msgs.mir.go | 4 +- .../synchronizerpb/synchronizerpb.pb.go | 16 +++--- .../synchronizerpb/types/types.mir.go | 4 +- protos/blockchainpb/bcmpb/bcmpb.proto | 8 +-- .../synchronizerpb/synchronizerpb.proto | 4 +- samples/blockchain/bcm.go | 49 ++++++++++------- samples/blockchain/main.go | 4 +- samples/blockchain/synchronizer.go | 55 +++++++++++-------- 14 files changed, 117 insertions(+), 95 deletions(-) diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go index 84e779703..5d09ad506 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go @@ -259,7 +259,7 @@ type GetBlockRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` SourceModule string `protobuf:"bytes,2,opt,name=source_module,json=sourceModule,proto3" json:"source_module,omitempty"` BlockId uint64 `protobuf:"varint,3,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` } @@ -296,11 +296,11 @@ func (*GetBlockRequest) Descriptor() ([]byte, []int) { return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{3} } -func (x *GetBlockRequest) GetRequestId() uint64 { +func (x *GetBlockRequest) GetRequestId() string { if x != nil { return x.RequestId } - return 0 + return "" } func (x *GetBlockRequest) GetSourceModule() string { @@ -322,7 +322,7 @@ type GetBlockResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` Found bool `protobuf:"varint,2,opt,name=found,proto3" json:"found,omitempty"` Block *blockchainpb.Block `protobuf:"bytes,3,opt,name=block,proto3" json:"block,omitempty"` } @@ -359,11 +359,11 @@ func (*GetBlockResponse) Descriptor() ([]byte, []int) { return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{4} } -func (x *GetBlockResponse) GetRequestId() uint64 { +func (x *GetBlockResponse) GetRequestId() string { if x != nil { return x.RequestId } - return 0 + return "" } func (x *GetBlockResponse) GetFound() bool { @@ -385,7 +385,7 @@ type GetChainRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` SourceModule string `protobuf:"bytes,2,opt,name=source_module,json=sourceModule,proto3" json:"source_module,omitempty"` EndBlockId uint64 `protobuf:"varint,3,opt,name=end_block_id,json=endBlockId,proto3" json:"end_block_id,omitempty"` SourceBlockIds []uint64 `protobuf:"varint,4,rep,packed,name=source_block_ids,json=sourceBlockIds,proto3" json:"source_block_ids,omitempty"` @@ -423,11 +423,11 @@ func (*GetChainRequest) Descriptor() ([]byte, []int) { return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{5} } -func (x *GetChainRequest) GetRequestId() uint64 { +func (x *GetChainRequest) GetRequestId() string { if x != nil { return x.RequestId } - return 0 + return "" } func (x *GetChainRequest) GetSourceModule() string { @@ -456,7 +456,7 @@ type GetChainResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` Success bool `protobuf:"varint,2,opt,name=success,proto3" json:"success,omitempty"` Chain []*blockchainpb.Block `protobuf:"bytes,3,rep,name=chain,proto3" json:"chain,omitempty"` // sorted from oldest to youngest } @@ -493,11 +493,11 @@ func (*GetChainResponse) Descriptor() ([]byte, []int) { return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{6} } -func (x *GetChainResponse) GetRequestId() uint64 { +func (x *GetChainResponse) GetRequestId() string { if x != nil { return x.RequestId } - return 0 + return "" } func (x *GetChainResponse) GetSuccess() bool { @@ -559,7 +559,7 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xae, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, @@ -570,7 +570,7 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x78, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, @@ -578,7 +578,7 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, + 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, @@ -592,7 +592,7 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x63, 0x6b, 0x49, 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go index c4e396541..f4e647643 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go @@ -19,18 +19,18 @@ func NewChain(m dsl.Module, destModule types.ModuleID, blocks []*blockchainpb.Bl dsl.EmitMirEvent(m, events.NewChain(destModule, blocks)) } -func GetBlockRequest(m dsl.Module, destModule types.ModuleID, requestId uint64, sourceModule types.ModuleID, blockId uint64) { +func GetBlockRequest(m dsl.Module, destModule types.ModuleID, requestId string, sourceModule types.ModuleID, blockId uint64) { dsl.EmitMirEvent(m, events.GetBlockRequest(destModule, requestId, sourceModule, blockId)) } -func GetBlockResponse(m dsl.Module, destModule types.ModuleID, requestId uint64, found bool, block *blockchainpb.Block) { +func GetBlockResponse(m dsl.Module, destModule types.ModuleID, requestId string, found bool, block *blockchainpb.Block) { dsl.EmitMirEvent(m, events.GetBlockResponse(destModule, requestId, found, block)) } -func GetChainRequest(m dsl.Module, destModule types.ModuleID, requestId uint64, sourceModule types.ModuleID, endBlockId uint64, sourceBlockIds []uint64) { +func GetChainRequest(m dsl.Module, destModule types.ModuleID, requestId string, sourceModule types.ModuleID, endBlockId uint64, sourceBlockIds []uint64) { dsl.EmitMirEvent(m, events.GetChainRequest(destModule, requestId, sourceModule, endBlockId, sourceBlockIds)) } -func GetChainResponse(m dsl.Module, destModule types.ModuleID, requestId uint64, success bool, chain []*blockchainpb.Block) { +func GetChainResponse(m dsl.Module, destModule types.ModuleID, requestId string, success bool, chain []*blockchainpb.Block) { dsl.EmitMirEvent(m, events.GetChainResponse(destModule, requestId, success, chain)) } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go index 39855f428..2d3ed93ed 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go @@ -35,25 +35,25 @@ func UponNewChain(m dsl.Module, handler func(blocks []*blockchainpb.Block) error }) } -func UponGetBlockRequest(m dsl.Module, handler func(requestId uint64, sourceModule types2.ModuleID, blockId uint64) error) { +func UponGetBlockRequest(m dsl.Module, handler func(requestId string, sourceModule types2.ModuleID, blockId uint64) error) { UponEvent[*types.Event_GetBlockRequest](m, func(ev *types.GetBlockRequest) error { return handler(ev.RequestId, ev.SourceModule, ev.BlockId) }) } -func UponGetBlockResponse(m dsl.Module, handler func(requestId uint64, found bool, block *blockchainpb.Block) error) { +func UponGetBlockResponse(m dsl.Module, handler func(requestId string, found bool, block *blockchainpb.Block) error) { UponEvent[*types.Event_GetBlockResponse](m, func(ev *types.GetBlockResponse) error { return handler(ev.RequestId, ev.Found, ev.Block) }) } -func UponGetChainRequest(m dsl.Module, handler func(requestId uint64, sourceModule types2.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error) { +func UponGetChainRequest(m dsl.Module, handler func(requestId string, sourceModule types2.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error) { UponEvent[*types.Event_GetChainRequest](m, func(ev *types.GetChainRequest) error { return handler(ev.RequestId, ev.SourceModule, ev.EndBlockId, ev.SourceBlockIds) }) } -func UponGetChainResponse(m dsl.Module, handler func(requestId uint64, success bool, chain []*blockchainpb.Block) error) { +func UponGetChainResponse(m dsl.Module, handler func(requestId string, success bool, chain []*blockchainpb.Block) error) { UponEvent[*types.Event_GetChainResponse](m, func(ev *types.GetChainResponse) error { return handler(ev.RequestId, ev.Success, ev.Chain) }) diff --git a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go index a438bc44f..59639e365 100644 --- a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go @@ -39,7 +39,7 @@ func NewChain(destModule types.ModuleID, blocks []*blockchainpb.Block) *types1.E } } -func GetBlockRequest(destModule types.ModuleID, requestId uint64, sourceModule types.ModuleID, blockId uint64) *types1.Event { +func GetBlockRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID, blockId uint64) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Bcm{ @@ -56,7 +56,7 @@ func GetBlockRequest(destModule types.ModuleID, requestId uint64, sourceModule t } } -func GetBlockResponse(destModule types.ModuleID, requestId uint64, found bool, block *blockchainpb.Block) *types1.Event { +func GetBlockResponse(destModule types.ModuleID, requestId string, found bool, block *blockchainpb.Block) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Bcm{ @@ -73,7 +73,7 @@ func GetBlockResponse(destModule types.ModuleID, requestId uint64, found bool, b } } -func GetChainRequest(destModule types.ModuleID, requestId uint64, sourceModule types.ModuleID, endBlockId uint64, sourceBlockIds []uint64) *types1.Event { +func GetChainRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID, endBlockId uint64, sourceBlockIds []uint64) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Bcm{ @@ -91,7 +91,7 @@ func GetChainRequest(destModule types.ModuleID, requestId uint64, sourceModule t } } -func GetChainResponse(destModule types.ModuleID, requestId uint64, success bool, chain []*blockchainpb.Block) *types1.Event { +func GetChainResponse(destModule types.ModuleID, requestId string, success bool, chain []*blockchainpb.Block) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Bcm{ diff --git a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go index 1dce2fe69..caf487770 100644 --- a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go @@ -278,7 +278,7 @@ func (*NewChain) MirReflect() mirreflect.Type { } type GetBlockRequest struct { - RequestId uint64 + RequestId string SourceModule types.ModuleID BlockId uint64 } @@ -313,7 +313,7 @@ func (*GetBlockRequest) MirReflect() mirreflect.Type { } type GetBlockResponse struct { - RequestId uint64 + RequestId string Found bool Block *blockchainpb.Block } @@ -350,7 +350,7 @@ func (*GetBlockResponse) MirReflect() mirreflect.Type { } type GetChainRequest struct { - RequestId uint64 + RequestId string SourceModule types.ModuleID EndBlockId uint64 SourceBlockIds []uint64 @@ -388,7 +388,7 @@ func (*GetChainRequest) MirReflect() mirreflect.Type { } type GetChainResponse struct { - RequestId uint64 + RequestId string Success bool Chain []*blockchainpb.Block } diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go index 77f097414..f83bd996b 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go @@ -24,13 +24,13 @@ func UponMessageReceived[W types.Message_TypeWrapper[M], M any](m dsl.Module, ha }) } -func UponChainRequestReceived(m dsl.Module, handler func(from types1.NodeID, requestId uint64, blockId uint64, leaveIds []uint64) error) { +func UponChainRequestReceived(m dsl.Module, handler func(from types1.NodeID, requestId string, blockId uint64, leaveIds []uint64) error) { UponMessageReceived[*types.Message_ChainRequest](m, func(from types1.NodeID, msg *types.ChainRequest) error { return handler(from, msg.RequestId, msg.BlockId, msg.LeaveIds) }) } -func UponChainResponseReceived(m dsl.Module, handler func(from types1.NodeID, requestId uint64, found bool, chain []*blockchainpb.Block) error) { +func UponChainResponseReceived(m dsl.Module, handler func(from types1.NodeID, requestId string, found bool, chain []*blockchainpb.Block) error) { UponMessageReceived[*types.Message_ChainResponse](m, func(from types1.NodeID, msg *types.ChainResponse) error { return handler(from, msg.RequestId, msg.Found, msg.Chain) }) diff --git a/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go b/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go index e2661512a..1ec14adf2 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go @@ -9,7 +9,7 @@ import ( types "github.com/filecoin-project/mir/pkg/types" ) -func ChainRequest(destModule types.ModuleID, requestId uint64, blockId uint64, leaveIds []uint64) *types1.Message { +func ChainRequest(destModule types.ModuleID, requestId string, blockId uint64, leaveIds []uint64) *types1.Message { return &types1.Message{ DestModule: destModule, Type: &types1.Message_Synchronizer{ @@ -26,7 +26,7 @@ func ChainRequest(destModule types.ModuleID, requestId uint64, blockId uint64, l } } -func ChainResponse(destModule types.ModuleID, requestId uint64, found bool, chain []*blockchainpb.Block) *types1.Message { +func ChainResponse(destModule types.ModuleID, requestId string, found bool, chain []*blockchainpb.Block) *types1.Message { return &types1.Message{ DestModule: destModule, Type: &types1.Message_Synchronizer{ diff --git a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go index cd26dff5b..c145b283b 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go +++ b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go @@ -231,7 +231,7 @@ type ChainRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` BlockId uint64 `protobuf:"varint,2,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` LeaveIds []uint64 `protobuf:"varint,3,rep,packed,name=leave_ids,json=leaveIds,proto3" json:"leave_ids,omitempty"` } @@ -268,11 +268,11 @@ func (*ChainRequest) Descriptor() ([]byte, []int) { return file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescGZIP(), []int{3} } -func (x *ChainRequest) GetRequestId() uint64 { +func (x *ChainRequest) GetRequestId() string { if x != nil { return x.RequestId } - return 0 + return "" } func (x *ChainRequest) GetBlockId() uint64 { @@ -294,7 +294,7 @@ type ChainResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` Found bool `protobuf:"varint,2,opt,name=found,proto3" json:"found,omitempty"` Chain []*blockchainpb.Block `protobuf:"bytes,3,rep,name=chain,proto3" json:"chain,omitempty"` // possibly undefined (proto3 spec no-longer supports optional/required...) } @@ -331,11 +331,11 @@ func (*ChainResponse) Descriptor() ([]byte, []int) { return file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDescGZIP(), []int{4} } -func (x *ChainResponse) GetRequestId() uint64 { +func (x *ChainResponse) GetRequestId() string { if x != nil { return x.RequestId } - return 0 + return "" } func (x *ChainResponse) GetFound() bool { @@ -390,14 +390,14 @@ var file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDesc = []byte{ 0x65, 0x3a, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x22, 0x6b, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x49, 0x64, 0x73, 0x3a, 0x04, 0xd0, 0xe4, 0x1d, 0x01, 0x22, 0x75, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, diff --git a/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go index c23f84c53..18db20e04 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go @@ -224,7 +224,7 @@ func (*Message) MirReflect() mirreflect.Type { } type ChainRequest struct { - RequestId uint64 + RequestId string BlockId uint64 LeaveIds []uint64 } @@ -259,7 +259,7 @@ func (*ChainRequest) MirReflect() mirreflect.Type { } type ChainResponse struct { - RequestId uint64 + RequestId string Found bool Chain []*blockchainpb.Block } diff --git a/protos/blockchainpb/bcmpb/bcmpb.proto b/protos/blockchainpb/bcmpb/bcmpb.proto index d887e51c5..980b37d47 100644 --- a/protos/blockchainpb/bcmpb/bcmpb.proto +++ b/protos/blockchainpb/bcmpb/bcmpb.proto @@ -40,7 +40,7 @@ message NewChain { message GetBlockRequest { option (mir.event) = true; - uint64 request_id = 1; + string request_id = 1; string source_module = 2 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.ModuleID"]; uint64 block_id = 3; @@ -49,7 +49,7 @@ message GetBlockRequest { message GetBlockResponse { option (mir.event) = true; - uint64 request_id = 1; + string request_id = 1; bool found = 2; blockchainpb.Block block = 3; } @@ -57,7 +57,7 @@ message GetBlockResponse { message GetChainRequest { option (mir.event) = true; - uint64 request_id = 1; + string request_id = 1; string source_module = 2 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.ModuleID"]; uint64 end_block_id = 3; @@ -67,7 +67,7 @@ message GetChainRequest { message GetChainResponse { option (mir.event) = true; - uint64 request_id = 1; + string request_id = 1; bool success = 2; repeated blockchainpb.Block chain = 3; // sorted from oldest to youngest } \ No newline at end of file diff --git a/protos/blockchainpb/synchronizerpb/synchronizerpb.proto b/protos/blockchainpb/synchronizerpb/synchronizerpb.proto index 44227ff94..6210c1266 100644 --- a/protos/blockchainpb/synchronizerpb/synchronizerpb.proto +++ b/protos/blockchainpb/synchronizerpb/synchronizerpb.proto @@ -40,7 +40,7 @@ message Message { message ChainRequest { option (net.message) = true; - uint64 request_id = 1; + string request_id = 1; uint64 block_id = 2; repeated uint64 leave_ids = 3; } @@ -48,7 +48,7 @@ message ChainRequest { message ChainResponse { option (net.message) = true; - uint64 request_id = 1; + string request_id = 1; bool found = 2; repeated blockchainpb.Block chain = 3; // possibly undefined (proto3 spec no-longer supports optional/required...) diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 38a12b2ee..312fb723d 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -108,7 +108,7 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { // check if curr matches the block to be added - if so, ignore if currBlock.block.BlockId == block.BlockId { bcm.logger.Log(logging.LevelDebug, "Block already in tree", "blockId", utils.FormatBlockId(block.BlockId)) - return false, ErrNodeAlreadyInTree + return true, ErrNodeAlreadyInTree } // check if curr is parent @@ -151,16 +151,21 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { currentHead := bcm.getHead() // insert block if err := bcm.addBlock(block); err != nil { - if err == ErrParentNotFound { + switch err { + case ErrNodeAlreadyInTree: + // ignore + bcm.logger.Log(logging.LevelDebug, "Block already in tree - ignore", "blockId", utils.FormatBlockId(block.BlockId)) + case ErrParentNotFound: // ask synchronizer for help leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.BlockId) bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", utils.FormatBlockId(block.BlockId), "leaves+genesis", utils.FormatBlockIdSlice(leavesPlusGenesis)) synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", block, leavesPlusGenesis) // announce orphan block to interceptor interceptorpbdsl.NewOrphan(*bcm.m, "devnull", block) - } else { + default: // some other error bcm.logger.Log(logging.LevelError, "Unexpected error adding block", "blockId", utils.FormatBlockId(block.BlockId), "error", err) + panic(err) } return } @@ -190,15 +195,21 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { // insert block for _, block := range blocks { if err := bcm.addBlock(block); err != nil { - if err == ErrNodeAlreadyInTree { + switch err { + case ErrNodeAlreadyInTree: // ignore bcm.logger.Log(logging.LevelDebug, "Block already in tree - ignore", "blockId", utils.FormatBlockId(block.BlockId)) - continue - } else { + case ErrParentNotFound: + // ask synchronizer for help + leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.BlockId) + bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", utils.FormatBlockId(block.BlockId), "leaves+genesis", utils.FormatBlockIdSlice(leavesPlusGenesis)) + synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", block, leavesPlusGenesis) + // announce orphan block to interceptor + interceptorpbdsl.NewOrphan(*bcm.m, "devnull", block) + default: // some other error - bcm.logger.Log(logging.LevelError, "Unexpected error adding block from chain", "blockId", utils.FormatBlockId(block.BlockId), "error", err, "leaves", utils.FormatBlockIdSlice(maps.Keys(bcm.leaves))) - panic("Unexpected error adding block from chain") - return + bcm.logger.Log(logging.LevelError, "Unexpected error adding block", "blockId", utils.FormatBlockId(block.BlockId), "error", err) + panic(err) } } // register block in app @@ -291,25 +302,25 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { return nil }) - bcmpbdsl.UponGetBlockRequest(m, func(requestID uint64, sourceModule t.ModuleID, blockID uint64) error { - bcm.logger.Log(logging.LevelInfo, "Received get block request", "requestId", utils.FormatBlockId(requestID), "sourceModule", sourceModule) + bcmpbdsl.UponGetBlockRequest(m, func(requestID string, sourceModule t.ModuleID, blockID uint64) error { + bcm.logger.Log(logging.LevelInfo, "Received get block request", "requestId", requestID, "sourceModule", sourceModule) // check if block is in tree hit := bcm.findBlock(blockID) if hit == nil { - bcm.logger.Log(logging.LevelDebug, "Block not found", "requestId", utils.FormatBlockId(requestID)) + bcm.logger.Log(logging.LevelDebug, "Block not found", "requestId", requestID) bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) return nil } - bcm.logger.Log(logging.LevelDebug, "Found block in tree", "requestId", utils.FormatBlockId(requestID)) + bcm.logger.Log(logging.LevelDebug, "Found block in tree", "requestId", requestID) bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block) return nil }) - bcmpbdsl.UponGetChainRequest(m, func(requestID uint64, sourceModule t.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error { - bcm.logger.Log(logging.LevelInfo, "Received get chain request", "requestId", utils.FormatBlockId(requestID), "sourceModule", sourceModule, "endBlockId", utils.FormatBlockId(endBlockId), "sourceBlockIds", utils.FormatBlockIdSlice(sourceBlockIds)) + bcmpbdsl.UponGetChainRequest(m, func(requestID string, sourceModule t.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error { + bcm.logger.Log(logging.LevelInfo, "Received get chain request", "requestId", requestID, "sourceModule", sourceModule, "endBlockId", utils.FormatBlockId(endBlockId), "sourceBlockIds", utils.FormatBlockIdSlice(sourceBlockIds)) chain := make([]*blockchainpb.Block, 0) // for easier lookup... sourceBlockIdsMap := make(map[uint64]uint64) @@ -319,12 +330,12 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { currentBlock := bcm.findBlock(endBlockId) if currentBlock == nil { - bcm.logger.Log(logging.LevelDebug, "Block not found - can't build chain", "requestId", utils.FormatBlockId(requestID)) + bcm.logger.Log(logging.LevelDebug, "Block not found - can't build chain", "requestId", requestID) bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) return nil } - bcm.logger.Log(logging.LevelDebug, "Found block in tree - building chain", "requestId", utils.FormatBlockId(requestID)) + bcm.logger.Log(logging.LevelDebug, "Found block in tree - building chain", "requestId", requestID) // initialize chain chain = append(chain, currentBlock.block) @@ -333,7 +344,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { // if parent is in sourceBlockIds, stop if _, ok := sourceBlockIdsMap[currentBlock.parent.block.BlockId]; ok { // chain build - bcm.logger.Log(logging.LevelDebug, "Found link with sourceBlockIds - chain build", "requestId", utils.FormatBlockId(requestID)) + bcm.logger.Log(logging.LevelDebug, "Found link with sourceBlockIds - chain build", "requestId", requestID) // reversing chain to send it in the right order for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 { chain[i], chain[j] = chain[j], chain[i] @@ -347,7 +358,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { } // if we get here, we didn't find a link with sourceBlockIds - bcm.logger.Log(logging.LevelDebug, "No link with sourceBlockIds - can't build chain", "requestId", utils.FormatBlockId(requestID)) + bcm.logger.Log(logging.LevelDebug, "No link with sourceBlockIds - can't build chain", "requestId", requestID) bcmpbdsl.GetChainResponse(*bcm.m, sourceModule, requestID, false, nil) return nil diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index 7beb00eda..c2cee68e7 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -74,7 +74,7 @@ func main() { mangler, err := eventmangler.NewModule( eventmangler.ModuleConfig{Self: "mangler", Dest: "transport", Timer: "timer"}, - &eventmangler.ModuleParams{MinDelay: time.Second / 1000, MaxDelay: 2 * time.Second, DropRate: 0.01}, + &eventmangler.ModuleParams{MinDelay: time.Second / 1000, MaxDelay: 2 * time.Second, DropRate: 0.15}, ) if err != nil { panic(err) @@ -91,7 +91,7 @@ func main() { "communication": NewCommunication(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), // "tpm": NewTPM(logging.Decorate(logger, "TPM:\t")), "application": application.NewApplication(logging.Decorate(logger, "App:\t")), - "synchronizer": NewSynchronizer(otherNodes, false, logging.Decorate(logger, "Sync:\t")), + "synchronizer": NewSynchronizer(ownNodeID, otherNodes, false, logging.Decorate(logger, "Sync:\t")), "timer": timer, "mangler": mangler, "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor diff --git a/samples/blockchain/synchronizer.go b/samples/blockchain/synchronizer.go index 0084084b1..f65e420c5 100644 --- a/samples/blockchain/synchronizer.go +++ b/samples/blockchain/synchronizer.go @@ -4,6 +4,7 @@ package main import ( "errors" + "fmt" "math/rand" "github.com/filecoin-project/mir/pkg/dsl" @@ -34,8 +35,9 @@ var ( type synchronizerModule struct { m *dsl.Module - syncRequests map[uint64]*syncRequest - getRequests map[uint64]*getRequest + nodeID t.NodeID // id of this node + syncRequests map[string]*syncRequest + getRequests map[string]*getRequest otherNodes []t.NodeID // we remove nodes that we presume have crashed (see strikeList) // TODO: add a timeout to 're-add' them? exponential backoff? // stikeList map[t.NodeID]int // a node gets a strike whenever it failed to handle a request, after STRIKE_THRESHOLD strikes it is removed from the otherNodes. Set to 0 if it handles a request successfully mangle bool @@ -48,17 +50,17 @@ type synchronizerModule struct { *************************/ type syncRequest struct { - requestID uint64 + requestID string blockID uint64 leaveIDs []uint64 nodesContacted []int // index of node in otherNodes } -func (sm *synchronizerModule) registerSyncRequest(block *blockchainpb.Block, leaves []uint64) (uint64, error) { - requestId := block.BlockId +func (sm *synchronizerModule) registerSyncRequest(block *blockchainpb.Block, leaves []uint64) (string, error) { + requestId := fmt.Sprint(block.BlockId) + "-" + string(sm.nodeID) // check if request already exists if _, ok := sm.syncRequests[requestId]; ok { - return 0, ErrRequestAlreadyExists + return "", ErrRequestAlreadyExists } sm.syncRequests[requestId] = &syncRequest{requestID: requestId, blockID: block.BlockId, leaveIDs: leaves, nodesContacted: []int{}} @@ -66,7 +68,7 @@ func (sm *synchronizerModule) registerSyncRequest(block *blockchainpb.Block, lea return requestId, nil } -func (sm *synchronizerModule) contactNextNode(requestID uint64) error { +func (sm *synchronizerModule) contactNextNode(requestID string) error { request, ok := sm.syncRequests[requestID] if !ok { return ErrUnknownRequest @@ -105,8 +107,15 @@ func (sm *synchronizerModule) handleSyncRequest(orphanBlock *blockchainpb.Block, // register request requestId, err := sm.registerSyncRequest(orphanBlock, leaveIds) if err != nil { - sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", utils.FormatBlockId(orphanBlock.BlockId), "leaveIds", utils.FormatBlockIdSlice(leaveIds)) - return err + switch err { + case ErrRequestAlreadyExists: + // ignore request + sm.internalLogger.Log(logging.LevelDebug, "sync request already exists", "orphanBlock", utils.FormatBlockId(orphanBlock.BlockId), "leaveIds", utils.FormatBlockIdSlice(leaveIds)) + return nil + default: + sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", utils.FormatBlockId(orphanBlock.BlockId), "leaveIds", utils.FormatBlockIdSlice(leaveIds)) + return err + } } if err := sm.contactNextNode(requestId); err != nil { @@ -118,7 +127,7 @@ func (sm *synchronizerModule) handleSyncRequest(orphanBlock *blockchainpb.Block, return nil } -func (sm *synchronizerModule) handleChainResponseReceived(from t.NodeID, requestID uint64, found bool, chain []*blockchainpb.Block) error { +func (sm *synchronizerModule) handleChainResponseReceived(from t.NodeID, requestID string, found bool, chain []*blockchainpb.Block) error { // check if request exists request, ok := sm.syncRequests[requestID] if !ok { @@ -136,13 +145,13 @@ func (sm *synchronizerModule) handleChainResponseReceived(from t.NodeID, request } // send request to the next node if err := sm.contactNextNode(requestID); err == ErrNoMoreNodes { - sm.internalLogger.Log(logging.LevelError, "no more nodes to contact - forgetting request - shouldn't happen if not mangling", "error", err.Error(), "requestId", utils.FormatBlockId(requestID), "mangle", sm.mangle) + sm.internalLogger.Log(logging.LevelError, "no more nodes to contact - forgetting request - shouldn't happen if not mangling", "error", err.Error(), "requestId", requestID, "mangle", sm.mangle) delete(sm.syncRequests, requestID) - if sm.mangle { + if !sm.mangle { panic(err) // NOTE: this should never happen as long as we don't start mangling } } else if err != nil { - sm.internalLogger.Log(logging.LevelError, "Unexpected error contacting next node", "error", err.Error(), "requestId", utils.FormatBlockId(requestID), "mangle", sm.mangle) + sm.internalLogger.Log(logging.LevelError, "Unexpected error contacting next node", "error", err.Error(), "requestId", requestID, "mangle", sm.mangle) panic(err) } @@ -160,14 +169,15 @@ func (sm *synchronizerModule) handleChainResponseReceived(from t.NodeID, request * Outgoing sync requests *************************/ type getRequest struct { - requestID uint64 + requestID string from t.NodeID } -func (sm *synchronizerModule) handleChainRequestReceived(from t.NodeID, requestID uint64, blockID uint64, leaveIds []uint64) error { +func (sm *synchronizerModule) handleChainRequestReceived(from t.NodeID, requestID string, blockID uint64, leaveIds []uint64) error { // check if request is already being processed if _, ok := sm.getRequests[requestID]; ok { - return ErrRequestAlreadyExists + sm.externaLogger.Log(logging.LevelError, "Get block request already exists", "from", from, "requestId", requestID, "mangle", sm.mangle) + return nil } // register request @@ -180,14 +190,14 @@ func (sm *synchronizerModule) handleChainRequestReceived(from t.NodeID, requestI } -func (sm *synchronizerModule) handleGetChainResponse(requestID uint64, found bool, chain []*blockchainpb.Block) error { +func (sm *synchronizerModule) handleGetChainResponse(requestID string, found bool, chain []*blockchainpb.Block) error { request, ok := sm.getRequests[requestID] if !ok { - sm.externaLogger.Log(logging.LevelError, "Unknown get block request", "requestId", utils.FormatBlockId(requestID), "mangle", sm.mangle) + sm.externaLogger.Log(logging.LevelError, "Unknown get block request", "requestId", requestID, "mangle", sm.mangle) return ErrUnkownGetBlockRequest } - sm.externaLogger.Log(logging.LevelInfo, "Responsing to block request", "requestId", utils.FormatBlockId(requestID), "found", found, "mangle", sm.mangle) + sm.externaLogger.Log(logging.LevelInfo, "Responsing to block request", "requestId", requestID, "found", found, "mangle", sm.mangle) // respond to sync request if sm.mangle { @@ -202,12 +212,13 @@ func (sm *synchronizerModule) handleGetChainResponse(requestID uint64, found boo return nil } -func NewSynchronizer(otherNodes []t.NodeID, mangle bool, logger logging.Logger) modules.PassiveModule { +func NewSynchronizer(nodeID t.NodeID, otherNodes []t.NodeID, mangle bool, logger logging.Logger) modules.PassiveModule { m := dsl.NewModule("synchronizer") var sm = synchronizerModule{ m: &m, - getRequests: make(map[uint64]*getRequest), - syncRequests: make(map[uint64]*syncRequest), + nodeID: nodeID, + getRequests: make(map[string]*getRequest), + syncRequests: make(map[string]*syncRequest), otherNodes: otherNodes, mangle: mangle, internalLogger: logging.Decorate(logger, "Intern - "), From e49f8a10f37c1db0262be5b39992997ae9c051f0 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Thu, 7 Dec 2023 13:35:09 +0100 Subject: [PATCH 09/46] keep state in internal blocks --- .../applicationpb/applicationpb.pb.go | 193 ++----- .../applicationpb/applicationpb.pb.mir.go | 1 - .../applicationpb/dsl/emit.mir.go | 5 - .../applicationpb/dsl/upon.mir.go | 7 - .../applicationpb/events/events.mir.go | 16 - .../applicationpb/oneof_interfaces.mir.go | 4 - .../applicationpb/types/types.mir.go | 58 -- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go | 504 ++++++++++++++---- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go | 3 + pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go | 13 + pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go | 19 + .../blockchainpb/bcmpb/events/events.mir.go | 49 ++ .../bcmpb/oneof_interfaces.mir.go | 12 + pkg/pb/blockchainpb/bcmpb/types/types.mir.go | 177 ++++++ pkg/pb/blockchainpb/blockchainpb.pb.go | 145 +++-- .../statepb/oneof_interfaces.mir.go | 3 + pkg/pb/blockchainpb/statepb/statepb.pb.go | 147 +++++ .../blockchainpb/statepb/types/types.mir.go | 3 + .../applicationpb/applicationpb.proto | 12 +- protos/blockchainpb/bcmpb/bcmpb.proto | 37 +- protos/blockchainpb/blockchainpb.proto | 8 +- protos/blockchainpb/statepb/statepb.proto | 10 + protos/generate.go | 2 + samples/blockchain/application/application.go | 62 ++- samples/blockchain/application/state.go | 41 -- samples/blockchain/bcm.go | 167 ++++-- 26 files changed, 1215 insertions(+), 483 deletions(-) create mode 100644 pkg/pb/blockchainpb/statepb/oneof_interfaces.mir.go create mode 100644 pkg/pb/blockchainpb/statepb/statepb.pb.go create mode 100644 pkg/pb/blockchainpb/statepb/types/types.mir.go create mode 100644 protos/blockchainpb/statepb/statepb.proto delete mode 100644 samples/blockchain/application/state.go diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go index 61e3321ca..3c692fe40 100644 --- a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go @@ -7,7 +7,7 @@ package applicationpb import ( - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + _ "github.com/filecoin-project/mir/pkg/pb/blockchainpb" payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" @@ -31,7 +31,6 @@ type Event struct { // Types that are assignable to Type: // // *Event_NewHead - // *Event_RegisterBlock // *Event_PayloadRequest // *Event_PayloadResponse Type isEvent_Type `protobuf_oneof:"type"` @@ -83,13 +82,6 @@ func (x *Event) GetNewHead() *NewHead { return nil } -func (x *Event) GetRegisterBlock() *RegisterBlock { - if x, ok := x.GetType().(*Event_RegisterBlock); ok { - return x.RegisterBlock - } - return nil -} - func (x *Event) GetPayloadRequest() *PayloadRequest { if x, ok := x.GetType().(*Event_PayloadRequest); ok { return x.PayloadRequest @@ -112,22 +104,16 @@ type Event_NewHead struct { NewHead *NewHead `protobuf:"bytes,1,opt,name=new_head,json=newHead,proto3,oneof"` } -type Event_RegisterBlock struct { - RegisterBlock *RegisterBlock `protobuf:"bytes,2,opt,name=register_block,json=registerBlock,proto3,oneof"` -} - type Event_PayloadRequest struct { - PayloadRequest *PayloadRequest `protobuf:"bytes,3,opt,name=payload_request,json=payloadRequest,proto3,oneof"` + PayloadRequest *PayloadRequest `protobuf:"bytes,2,opt,name=payload_request,json=payloadRequest,proto3,oneof"` } type Event_PayloadResponse struct { - PayloadResponse *PayloadResponse `protobuf:"bytes,4,opt,name=payload_response,json=payloadResponse,proto3,oneof"` + PayloadResponse *PayloadResponse `protobuf:"bytes,3,opt,name=payload_response,json=payloadResponse,proto3,oneof"` } func (*Event_NewHead) isEvent_Type() {} -func (*Event_RegisterBlock) isEvent_Type() {} - func (*Event_PayloadRequest) isEvent_Type() {} func (*Event_PayloadResponse) isEvent_Type() {} @@ -179,53 +165,6 @@ func (x *NewHead) GetHeadId() uint64 { return 0 } -type RegisterBlock struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - BlockId *blockchainpb.Block `protobuf:"bytes,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` -} - -func (x *RegisterBlock) Reset() { - *x = RegisterBlock{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *RegisterBlock) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*RegisterBlock) ProtoMessage() {} - -func (x *RegisterBlock) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] - 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 RegisterBlock.ProtoReflect.Descriptor instead. -func (*RegisterBlock) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{2} -} - -func (x *RegisterBlock) GetBlockId() *blockchainpb.Block { - if x != nil { - return x.BlockId - } - return nil -} - type PayloadRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -237,7 +176,7 @@ type PayloadRequest struct { func (x *PayloadRequest) Reset() { *x = PayloadRequest{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -250,7 +189,7 @@ func (x *PayloadRequest) String() string { func (*PayloadRequest) ProtoMessage() {} func (x *PayloadRequest) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -263,7 +202,7 @@ func (x *PayloadRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PayloadRequest.ProtoReflect.Descriptor instead. func (*PayloadRequest) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{3} + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{2} } func (x *PayloadRequest) GetHeadId() uint64 { @@ -285,7 +224,7 @@ type PayloadResponse struct { func (x *PayloadResponse) Reset() { *x = PayloadResponse{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -298,7 +237,7 @@ func (x *PayloadResponse) String() string { func (*PayloadResponse) ProtoMessage() {} func (x *PayloadResponse) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -311,7 +250,7 @@ func (x *PayloadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PayloadResponse.ProtoReflect.Descriptor instead. func (*PayloadResponse) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{4} + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{3} } func (x *PayloadResponse) GetHeadId() uint64 { @@ -341,47 +280,38 @@ var file_blockchainpb_applicationpb_applicationpb_proto_rawDesc = []byte{ 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x26, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xae, 0x02, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe7, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x48, 0x00, 0x52, 0x07, 0x6e, 0x65, - 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x45, 0x0a, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x0d, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x48, 0x0a, 0x0f, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, - 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x48, 0x00, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, - 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, - 0x01, 0x22, 0x45, 0x0a, 0x0d, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x12, 0x2e, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, - 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, - 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0f, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, + 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x48, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, + 0x0e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x4b, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x3a, 0x04, 0x90, 0xa6, + 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, + 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, + 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, + 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, - 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, - 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, - 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, - 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0f, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, + 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, + 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -396,28 +326,24 @@ func file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP() []byte { return file_blockchainpb_applicationpb_applicationpb_proto_rawDescData } -var file_blockchainpb_applicationpb_applicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 5) +var file_blockchainpb_applicationpb_applicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_blockchainpb_applicationpb_applicationpb_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: applicationpb.Event - (*NewHead)(nil), // 1: applicationpb.NewHead - (*RegisterBlock)(nil), // 2: applicationpb.RegisterBlock - (*PayloadRequest)(nil), // 3: applicationpb.PayloadRequest - (*PayloadResponse)(nil), // 4: applicationpb.PayloadResponse - (*blockchainpb.Block)(nil), // 5: blockchainpb.Block - (*payloadpb.Payload)(nil), // 6: payloadpb.Payload + (*Event)(nil), // 0: applicationpb.Event + (*NewHead)(nil), // 1: applicationpb.NewHead + (*PayloadRequest)(nil), // 2: applicationpb.PayloadRequest + (*PayloadResponse)(nil), // 3: applicationpb.PayloadResponse + (*payloadpb.Payload)(nil), // 4: payloadpb.Payload } var file_blockchainpb_applicationpb_applicationpb_proto_depIdxs = []int32{ 1, // 0: applicationpb.Event.new_head:type_name -> applicationpb.NewHead - 2, // 1: applicationpb.Event.register_block:type_name -> applicationpb.RegisterBlock - 3, // 2: applicationpb.Event.payload_request:type_name -> applicationpb.PayloadRequest - 4, // 3: applicationpb.Event.payload_response:type_name -> applicationpb.PayloadResponse - 5, // 4: applicationpb.RegisterBlock.block_id:type_name -> blockchainpb.Block - 6, // 5: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 2, // 1: applicationpb.Event.payload_request:type_name -> applicationpb.PayloadRequest + 3, // 2: applicationpb.Event.payload_response:type_name -> applicationpb.PayloadResponse + 4, // 3: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload + 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_blockchainpb_applicationpb_applicationpb_proto_init() } @@ -451,18 +377,6 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterBlock); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PayloadRequest); i { case 0: return &v.state @@ -474,7 +388,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { return nil } } - file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PayloadResponse); i { case 0: return &v.state @@ -489,7 +403,6 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_NewHead)(nil), - (*Event_RegisterBlock)(nil), (*Event_PayloadRequest)(nil), (*Event_PayloadResponse)(nil), } @@ -499,7 +412,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_applicationpb_applicationpb_proto_rawDesc, NumEnums: 0, - NumMessages: 5, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go index 86a02f33e..f14dcfd8a 100644 --- a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go @@ -9,7 +9,6 @@ import ( func (*Event) ReflectTypeOptions() []reflect.Type { return []reflect.Type{ reflect.TypeOf((*Event_NewHead)(nil)), - reflect.TypeOf((*Event_RegisterBlock)(nil)), reflect.TypeOf((*Event_PayloadRequest)(nil)), reflect.TypeOf((*Event_PayloadResponse)(nil)), } diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go index c96dee17d..e45621ecb 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go @@ -4,7 +4,6 @@ package applicationpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/events" payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" types "github.com/filecoin-project/mir/pkg/types" @@ -16,10 +15,6 @@ func NewHead(m dsl.Module, destModule types.ModuleID, headId uint64) { dsl.EmitMirEvent(m, events.NewHead(destModule, headId)) } -func RegisterBlock(m dsl.Module, destModule types.ModuleID, blockId *blockchainpb.Block) { - dsl.EmitMirEvent(m, events.RegisterBlock(destModule, blockId)) -} - func PayloadRequest(m dsl.Module, destModule types.ModuleID, headId uint64) { dsl.EmitMirEvent(m, events.PayloadRequest(destModule, headId)) } diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go index 333f09c31..b02555b6a 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go @@ -4,7 +4,6 @@ package applicationpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" @@ -29,12 +28,6 @@ func UponNewHead(m dsl.Module, handler func(headId uint64) error) { }) } -func UponRegisterBlock(m dsl.Module, handler func(blockId *blockchainpb.Block) error) { - UponEvent[*types.Event_RegisterBlock](m, func(ev *types.RegisterBlock) error { - return handler(ev.BlockId) - }) -} - func UponPayloadRequest(m dsl.Module, handler func(headId uint64) error) { UponEvent[*types.Event_PayloadRequest](m, func(ev *types.PayloadRequest) error { return handler(ev.HeadId) diff --git a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go index 4b1f6d7f6..5cf745842 100644 --- a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go @@ -3,7 +3,6 @@ package applicationpbevents import ( - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" @@ -25,21 +24,6 @@ func NewHead(destModule types.ModuleID, headId uint64) *types1.Event { } } -func RegisterBlock(destModule types.ModuleID, blockId *blockchainpb.Block) *types1.Event { - return &types1.Event{ - DestModule: destModule, - Type: &types1.Event_Application{ - Application: &types2.Event{ - Type: &types2.Event_RegisterBlock{ - RegisterBlock: &types2.RegisterBlock{ - BlockId: blockId, - }, - }, - }, - }, - } -} - func PayloadRequest(destModule types.ModuleID, headId uint64) *types1.Event { return &types1.Event{ DestModule: destModule, diff --git a/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go index d9a164a4b..28b930c6c 100644 --- a/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go @@ -13,10 +13,6 @@ func (w *Event_NewHead) Unwrap() *NewHead { return w.NewHead } -func (w *Event_RegisterBlock) Unwrap() *RegisterBlock { - return w.RegisterBlock -} - func (w *Event_PayloadRequest) Unwrap() *PayloadRequest { return w.PayloadRequest } diff --git a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go index 2d3b22953..de90dd96d 100644 --- a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go @@ -4,7 +4,6 @@ package applicationpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" applicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" @@ -32,8 +31,6 @@ func Event_TypeFromPb(pb applicationpb.Event_Type) Event_Type { switch pb := pb.(type) { case *applicationpb.Event_NewHead: return &Event_NewHead{NewHead: NewHeadFromPb(pb.NewHead)} - case *applicationpb.Event_RegisterBlock: - return &Event_RegisterBlock{RegisterBlock: RegisterBlockFromPb(pb.RegisterBlock)} case *applicationpb.Event_PayloadRequest: return &Event_PayloadRequest{PayloadRequest: PayloadRequestFromPb(pb.PayloadRequest)} case *applicationpb.Event_PayloadResponse: @@ -66,30 +63,6 @@ func (*Event_NewHead) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_NewHead]()} } -type Event_RegisterBlock struct { - RegisterBlock *RegisterBlock -} - -func (*Event_RegisterBlock) isEvent_Type() {} - -func (w *Event_RegisterBlock) Unwrap() *RegisterBlock { - return w.RegisterBlock -} - -func (w *Event_RegisterBlock) Pb() applicationpb.Event_Type { - if w == nil { - return nil - } - if w.RegisterBlock == nil { - return &applicationpb.Event_RegisterBlock{} - } - return &applicationpb.Event_RegisterBlock{RegisterBlock: (w.RegisterBlock).Pb()} -} - -func (*Event_RegisterBlock) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_RegisterBlock]()} -} - type Event_PayloadRequest struct { PayloadRequest *PayloadRequest } @@ -194,37 +167,6 @@ func (*NewHead) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.NewHead]()} } -type RegisterBlock struct { - BlockId *blockchainpb.Block -} - -func RegisterBlockFromPb(pb *applicationpb.RegisterBlock) *RegisterBlock { - if pb == nil { - return nil - } - return &RegisterBlock{ - BlockId: pb.BlockId, - } -} - -func (m *RegisterBlock) Pb() *applicationpb.RegisterBlock { - if m == nil { - return nil - } - pbMessage := &applicationpb.RegisterBlock{} - { - if m.BlockId != nil { - pbMessage.BlockId = m.BlockId - } - } - - return pbMessage -} - -func (*RegisterBlock) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.RegisterBlock]()} -} - type PayloadRequest struct { HeadId uint64 } diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go index 5d09ad506..074fbfa48 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go @@ -8,6 +8,7 @@ package bcmpb import ( blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -35,6 +36,9 @@ type Event struct { // *Event_GetBlockResponse // *Event_GetChainRequest // *Event_GetChainResponse + // *Event_GetHeadToCheckpointChainRequest + // *Event_GetHeadToCheckpointChainResponse + // *Event_RegisterCheckpoint Type isEvent_Type `protobuf_oneof:"type"` } @@ -119,6 +123,27 @@ func (x *Event) GetGetChainResponse() *GetChainResponse { return nil } +func (x *Event) GetGetHeadToCheckpointChainRequest() *GetHeadToCheckpointChainRequest { + if x, ok := x.GetType().(*Event_GetHeadToCheckpointChainRequest); ok { + return x.GetHeadToCheckpointChainRequest + } + return nil +} + +func (x *Event) GetGetHeadToCheckpointChainResponse() *GetHeadToCheckpointChainResponse { + if x, ok := x.GetType().(*Event_GetHeadToCheckpointChainResponse); ok { + return x.GetHeadToCheckpointChainResponse + } + return nil +} + +func (x *Event) GetRegisterCheckpoint() *RegisterCheckpoint { + if x, ok := x.GetType().(*Event_RegisterCheckpoint); ok { + return x.RegisterCheckpoint + } + return nil +} + type isEvent_Type interface { isEvent_Type() } @@ -147,6 +172,18 @@ type Event_GetChainResponse struct { GetChainResponse *GetChainResponse `protobuf:"bytes,6,opt,name=get_chain_response,json=getChainResponse,proto3,oneof"` } +type Event_GetHeadToCheckpointChainRequest struct { + GetHeadToCheckpointChainRequest *GetHeadToCheckpointChainRequest `protobuf:"bytes,7,opt,name=get_head_to_checkpoint_chain_request,json=getHeadToCheckpointChainRequest,proto3,oneof"` +} + +type Event_GetHeadToCheckpointChainResponse struct { + GetHeadToCheckpointChainResponse *GetHeadToCheckpointChainResponse `protobuf:"bytes,8,opt,name=get_head_to_checkpoint_chain_response,json=getHeadToCheckpointChainResponse,proto3,oneof"` +} + +type Event_RegisterCheckpoint struct { + RegisterCheckpoint *RegisterCheckpoint `protobuf:"bytes,9,opt,name=register_checkpoint,json=registerCheckpoint,proto3,oneof"` +} + func (*Event_NewBlock) isEvent_Type() {} func (*Event_NewChain) isEvent_Type() {} @@ -159,6 +196,12 @@ func (*Event_GetChainRequest) isEvent_Type() {} func (*Event_GetChainResponse) isEvent_Type() {} +func (*Event_GetHeadToCheckpointChainRequest) isEvent_Type() {} + +func (*Event_GetHeadToCheckpointChainResponse) isEvent_Type() {} + +func (*Event_RegisterCheckpoint) isEvent_Type() {} + type NewBlock struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -514,6 +557,171 @@ func (x *GetChainResponse) GetChain() []*blockchainpb.Block { return nil } +type GetHeadToCheckpointChainRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + SourceModule string `protobuf:"bytes,2,opt,name=source_module,json=sourceModule,proto3" json:"source_module,omitempty"` +} + +func (x *GetHeadToCheckpointChainRequest) Reset() { + *x = GetHeadToCheckpointChainRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetHeadToCheckpointChainRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetHeadToCheckpointChainRequest) ProtoMessage() {} + +func (x *GetHeadToCheckpointChainRequest) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7] + 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 GetHeadToCheckpointChainRequest.ProtoReflect.Descriptor instead. +func (*GetHeadToCheckpointChainRequest) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{7} +} + +func (x *GetHeadToCheckpointChainRequest) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *GetHeadToCheckpointChainRequest) GetSourceModule() string { + if x != nil { + return x.SourceModule + } + return "" +} + +type GetHeadToCheckpointChainResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Chain []*blockchainpb.BlockInternal `protobuf:"bytes,3,rep,name=chain,proto3" json:"chain,omitempty"` // from checkpoint to head +} + +func (x *GetHeadToCheckpointChainResponse) Reset() { + *x = GetHeadToCheckpointChainResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetHeadToCheckpointChainResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetHeadToCheckpointChainResponse) ProtoMessage() {} + +func (x *GetHeadToCheckpointChainResponse) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8] + 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 GetHeadToCheckpointChainResponse.ProtoReflect.Descriptor instead. +func (*GetHeadToCheckpointChainResponse) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{8} +} + +func (x *GetHeadToCheckpointChainResponse) GetRequestId() string { + if x != nil { + return x.RequestId + } + return "" +} + +func (x *GetHeadToCheckpointChainResponse) GetChain() []*blockchainpb.BlockInternal { + if x != nil { + return x.Chain + } + return nil +} + +type RegisterCheckpoint struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + BlockId uint64 `protobuf:"varint,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + State *statepb.State `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` +} + +func (x *RegisterCheckpoint) Reset() { + *x = RegisterCheckpoint{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegisterCheckpoint) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegisterCheckpoint) ProtoMessage() {} + +func (x *RegisterCheckpoint) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[9] + 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 RegisterCheckpoint.ProtoReflect.Descriptor instead. +func (*RegisterCheckpoint) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{9} +} + +func (x *RegisterCheckpoint) GetBlockId() uint64 { + if x != nil { + return x.BlockId + } + return 0 +} + +func (x *RegisterCheckpoint) GetState() *statepb.State { + if x != nil { + return x.State + } + return nil +} + var File_blockchainpb_bcmpb_bcmpb_proto protoreflect.FileDescriptor var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ @@ -521,87 +729,134 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x63, 0x6d, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, - 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x99, 0x03, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x12, 0x44, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x63, - 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x10, 0x67, - 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x44, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x62, 0x63, 0x6d, - 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x12, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x10, 0x67, 0x65, - 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x3a, 0x04, - 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, - 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, - 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, - 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xae, - 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, - 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, - 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, - 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, - 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x19, - 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, - 0x78, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, 0x65, - 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, - 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, - 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, - 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, - 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x6e, 0x64, - 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, - 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x49, 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x47, - 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, - 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, - 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, - 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, - 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, + 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdc, 0x05, 0x0a, 0x05, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, + 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, + 0x4e, 0x65, 0x77, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x12, 0x44, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x67, 0x65, 0x74, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x12, 0x67, 0x65, + 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, + 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, + 0x00, 0x52, 0x10, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, + 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x12, 0x67, 0x65, 0x74, + 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, + 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, + 0x52, 0x10, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x77, 0x0a, 0x24, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, + 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x26, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, + 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, + 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x1f, 0x67, 0x65, 0x74, 0x48, + 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x7a, 0x0a, 0x25, 0x67, + 0x65, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x62, 0x63, 0x6d, + 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x20, 0x67, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x13, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x48, + 0x00, 0x52, 0x12, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x77, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, + 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, + 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xae, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, + 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, + 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x78, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, + 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, + 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, + 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, + 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, + 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, + 0x22, 0xa3, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, + 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, + 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7a, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, + 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x05, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x22, 0x5b, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, + 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, + 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, + 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, + 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -616,16 +871,21 @@ func file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP() []byte { return file_blockchainpb_bcmpb_bcmpb_proto_rawDescData } -var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 10) var file_blockchainpb_bcmpb_bcmpb_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: bcmpb.Event - (*NewBlock)(nil), // 1: bcmpb.NewBlock - (*NewChain)(nil), // 2: bcmpb.NewChain - (*GetBlockRequest)(nil), // 3: bcmpb.GetBlockRequest - (*GetBlockResponse)(nil), // 4: bcmpb.GetBlockResponse - (*GetChainRequest)(nil), // 5: bcmpb.GetChainRequest - (*GetChainResponse)(nil), // 6: bcmpb.GetChainResponse - (*blockchainpb.Block)(nil), // 7: blockchainpb.Block + (*Event)(nil), // 0: bcmpb.Event + (*NewBlock)(nil), // 1: bcmpb.NewBlock + (*NewChain)(nil), // 2: bcmpb.NewChain + (*GetBlockRequest)(nil), // 3: bcmpb.GetBlockRequest + (*GetBlockResponse)(nil), // 4: bcmpb.GetBlockResponse + (*GetChainRequest)(nil), // 5: bcmpb.GetChainRequest + (*GetChainResponse)(nil), // 6: bcmpb.GetChainResponse + (*GetHeadToCheckpointChainRequest)(nil), // 7: bcmpb.GetHeadToCheckpointChainRequest + (*GetHeadToCheckpointChainResponse)(nil), // 8: bcmpb.GetHeadToCheckpointChainResponse + (*RegisterCheckpoint)(nil), // 9: bcmpb.RegisterCheckpoint + (*blockchainpb.Block)(nil), // 10: blockchainpb.Block + (*blockchainpb.BlockInternal)(nil), // 11: blockchainpb.BlockInternal + (*statepb.State)(nil), // 12: statepb.State } var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ 1, // 0: bcmpb.Event.new_block:type_name -> bcmpb.NewBlock @@ -634,15 +894,20 @@ var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ 4, // 3: bcmpb.Event.get_block_response:type_name -> bcmpb.GetBlockResponse 5, // 4: bcmpb.Event.get_chain_request:type_name -> bcmpb.GetChainRequest 6, // 5: bcmpb.Event.get_chain_response:type_name -> bcmpb.GetChainResponse - 7, // 6: bcmpb.NewBlock.block:type_name -> blockchainpb.Block - 7, // 7: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block - 7, // 8: bcmpb.GetBlockResponse.block:type_name -> blockchainpb.Block - 7, // 9: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block - 10, // [10:10] is the sub-list for method output_type - 10, // [10:10] is the sub-list for method input_type - 10, // [10:10] is the sub-list for extension type_name - 10, // [10:10] is the sub-list for extension extendee - 0, // [0:10] is the sub-list for field type_name + 7, // 6: bcmpb.Event.get_head_to_checkpoint_chain_request:type_name -> bcmpb.GetHeadToCheckpointChainRequest + 8, // 7: bcmpb.Event.get_head_to_checkpoint_chain_response:type_name -> bcmpb.GetHeadToCheckpointChainResponse + 9, // 8: bcmpb.Event.register_checkpoint:type_name -> bcmpb.RegisterCheckpoint + 10, // 9: bcmpb.NewBlock.block:type_name -> blockchainpb.Block + 10, // 10: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block + 10, // 11: bcmpb.GetBlockResponse.block:type_name -> blockchainpb.Block + 10, // 12: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block + 11, // 13: bcmpb.GetHeadToCheckpointChainResponse.chain:type_name -> blockchainpb.BlockInternal + 12, // 14: bcmpb.RegisterCheckpoint.state:type_name -> statepb.State + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_blockchainpb_bcmpb_bcmpb_proto_init() } @@ -735,6 +1000,42 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { return nil } } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetHeadToCheckpointChainRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetHeadToCheckpointChainResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterCheckpoint); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_NewBlock)(nil), @@ -743,6 +1044,9 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { (*Event_GetBlockResponse)(nil), (*Event_GetChainRequest)(nil), (*Event_GetChainResponse)(nil), + (*Event_GetHeadToCheckpointChainRequest)(nil), + (*Event_GetHeadToCheckpointChainResponse)(nil), + (*Event_RegisterCheckpoint)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -750,7 +1054,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_bcmpb_bcmpb_proto_rawDesc, NumEnums: 0, - NumMessages: 7, + NumMessages: 10, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go index 438519226..b5c31b8fa 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go @@ -14,5 +14,8 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_GetBlockResponse)(nil)), reflect.TypeOf((*Event_GetChainRequest)(nil)), reflect.TypeOf((*Event_GetChainResponse)(nil)), + reflect.TypeOf((*Event_GetHeadToCheckpointChainRequest)(nil)), + reflect.TypeOf((*Event_GetHeadToCheckpointChainResponse)(nil)), + reflect.TypeOf((*Event_RegisterCheckpoint)(nil)), } } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go index f4e647643..3fad09bf3 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go @@ -6,6 +6,7 @@ import ( dsl "github.com/filecoin-project/mir/pkg/dsl" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/events" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" types "github.com/filecoin-project/mir/pkg/types" ) @@ -34,3 +35,15 @@ func GetChainRequest(m dsl.Module, destModule types.ModuleID, requestId string, func GetChainResponse(m dsl.Module, destModule types.ModuleID, requestId string, success bool, chain []*blockchainpb.Block) { dsl.EmitMirEvent(m, events.GetChainResponse(destModule, requestId, success, chain)) } + +func GetHeadToCheckpointChainRequest(m dsl.Module, destModule types.ModuleID, requestId string, sourceModule types.ModuleID) { + dsl.EmitMirEvent(m, events.GetHeadToCheckpointChainRequest(destModule, requestId, sourceModule)) +} + +func GetHeadToCheckpointChainResponse(m dsl.Module, destModule types.ModuleID, requestId string, chain []*blockchainpb.BlockInternal) { + dsl.EmitMirEvent(m, events.GetHeadToCheckpointChainResponse(destModule, requestId, chain)) +} + +func RegisterCheckpoint(m dsl.Module, destModule types.ModuleID, blockId uint64, state *statepb.State) { + dsl.EmitMirEvent(m, events.RegisterCheckpoint(destModule, blockId, state)) +} diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go index 2d3ed93ed..48c47ca0b 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go @@ -6,6 +6,7 @@ import ( dsl "github.com/filecoin-project/mir/pkg/dsl" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types2 "github.com/filecoin-project/mir/pkg/types" ) @@ -58,3 +59,21 @@ func UponGetChainResponse(m dsl.Module, handler func(requestId string, success b return handler(ev.RequestId, ev.Success, ev.Chain) }) } + +func UponGetHeadToCheckpointChainRequest(m dsl.Module, handler func(requestId string, sourceModule types2.ModuleID) error) { + UponEvent[*types.Event_GetHeadToCheckpointChainRequest](m, func(ev *types.GetHeadToCheckpointChainRequest) error { + return handler(ev.RequestId, ev.SourceModule) + }) +} + +func UponGetHeadToCheckpointChainResponse(m dsl.Module, handler func(requestId string, chain []*blockchainpb.BlockInternal) error) { + UponEvent[*types.Event_GetHeadToCheckpointChainResponse](m, func(ev *types.GetHeadToCheckpointChainResponse) error { + return handler(ev.RequestId, ev.Chain) + }) +} + +func UponRegisterCheckpoint(m dsl.Module, handler func(blockId uint64, state *statepb.State) error) { + UponEvent[*types.Event_RegisterCheckpoint](m, func(ev *types.RegisterCheckpoint) error { + return handler(ev.BlockId, ev.State) + }) +} diff --git a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go index 59639e365..4e03edff8 100644 --- a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go @@ -5,6 +5,7 @@ package bcmpbevents import ( blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) @@ -107,3 +108,51 @@ func GetChainResponse(destModule types.ModuleID, requestId string, success bool, }, } } + +func GetHeadToCheckpointChainRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcm{ + Bcm: &types2.Event{ + Type: &types2.Event_GetHeadToCheckpointChainRequest{ + GetHeadToCheckpointChainRequest: &types2.GetHeadToCheckpointChainRequest{ + RequestId: requestId, + SourceModule: sourceModule, + }, + }, + }, + }, + } +} + +func GetHeadToCheckpointChainResponse(destModule types.ModuleID, requestId string, chain []*blockchainpb.BlockInternal) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcm{ + Bcm: &types2.Event{ + Type: &types2.Event_GetHeadToCheckpointChainResponse{ + GetHeadToCheckpointChainResponse: &types2.GetHeadToCheckpointChainResponse{ + RequestId: requestId, + Chain: chain, + }, + }, + }, + }, + } +} + +func RegisterCheckpoint(destModule types.ModuleID, blockId uint64, state *statepb.State) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcm{ + Bcm: &types2.Event{ + Type: &types2.Event_RegisterCheckpoint{ + RegisterCheckpoint: &types2.RegisterCheckpoint{ + BlockId: blockId, + State: state, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go index 0b79872bc..b4ba75b65 100644 --- a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go @@ -32,3 +32,15 @@ func (w *Event_GetChainRequest) Unwrap() *GetChainRequest { func (w *Event_GetChainResponse) Unwrap() *GetChainResponse { return w.GetChainResponse } + +func (w *Event_GetHeadToCheckpointChainRequest) Unwrap() *GetHeadToCheckpointChainRequest { + return w.GetHeadToCheckpointChainRequest +} + +func (w *Event_GetHeadToCheckpointChainResponse) Unwrap() *GetHeadToCheckpointChainResponse { + return w.GetHeadToCheckpointChainResponse +} + +func (w *Event_RegisterCheckpoint) Unwrap() *RegisterCheckpoint { + return w.RegisterCheckpoint +} diff --git a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go index caf487770..0f9c01228 100644 --- a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go @@ -6,6 +6,7 @@ import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" types "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -42,6 +43,12 @@ func Event_TypeFromPb(pb bcmpb.Event_Type) Event_Type { return &Event_GetChainRequest{GetChainRequest: GetChainRequestFromPb(pb.GetChainRequest)} case *bcmpb.Event_GetChainResponse: return &Event_GetChainResponse{GetChainResponse: GetChainResponseFromPb(pb.GetChainResponse)} + case *bcmpb.Event_GetHeadToCheckpointChainRequest: + return &Event_GetHeadToCheckpointChainRequest{GetHeadToCheckpointChainRequest: GetHeadToCheckpointChainRequestFromPb(pb.GetHeadToCheckpointChainRequest)} + case *bcmpb.Event_GetHeadToCheckpointChainResponse: + return &Event_GetHeadToCheckpointChainResponse{GetHeadToCheckpointChainResponse: GetHeadToCheckpointChainResponseFromPb(pb.GetHeadToCheckpointChainResponse)} + case *bcmpb.Event_RegisterCheckpoint: + return &Event_RegisterCheckpoint{RegisterCheckpoint: RegisterCheckpointFromPb(pb.RegisterCheckpoint)} } return nil } @@ -190,6 +197,78 @@ func (*Event_GetChainResponse) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetChainResponse]()} } +type Event_GetHeadToCheckpointChainRequest struct { + GetHeadToCheckpointChainRequest *GetHeadToCheckpointChainRequest +} + +func (*Event_GetHeadToCheckpointChainRequest) isEvent_Type() {} + +func (w *Event_GetHeadToCheckpointChainRequest) Unwrap() *GetHeadToCheckpointChainRequest { + return w.GetHeadToCheckpointChainRequest +} + +func (w *Event_GetHeadToCheckpointChainRequest) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.GetHeadToCheckpointChainRequest == nil { + return &bcmpb.Event_GetHeadToCheckpointChainRequest{} + } + return &bcmpb.Event_GetHeadToCheckpointChainRequest{GetHeadToCheckpointChainRequest: (w.GetHeadToCheckpointChainRequest).Pb()} +} + +func (*Event_GetHeadToCheckpointChainRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetHeadToCheckpointChainRequest]()} +} + +type Event_GetHeadToCheckpointChainResponse struct { + GetHeadToCheckpointChainResponse *GetHeadToCheckpointChainResponse +} + +func (*Event_GetHeadToCheckpointChainResponse) isEvent_Type() {} + +func (w *Event_GetHeadToCheckpointChainResponse) Unwrap() *GetHeadToCheckpointChainResponse { + return w.GetHeadToCheckpointChainResponse +} + +func (w *Event_GetHeadToCheckpointChainResponse) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.GetHeadToCheckpointChainResponse == nil { + return &bcmpb.Event_GetHeadToCheckpointChainResponse{} + } + return &bcmpb.Event_GetHeadToCheckpointChainResponse{GetHeadToCheckpointChainResponse: (w.GetHeadToCheckpointChainResponse).Pb()} +} + +func (*Event_GetHeadToCheckpointChainResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetHeadToCheckpointChainResponse]()} +} + +type Event_RegisterCheckpoint struct { + RegisterCheckpoint *RegisterCheckpoint +} + +func (*Event_RegisterCheckpoint) isEvent_Type() {} + +func (w *Event_RegisterCheckpoint) Unwrap() *RegisterCheckpoint { + return w.RegisterCheckpoint +} + +func (w *Event_RegisterCheckpoint) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.RegisterCheckpoint == nil { + return &bcmpb.Event_RegisterCheckpoint{} + } + return &bcmpb.Event_RegisterCheckpoint{RegisterCheckpoint: (w.RegisterCheckpoint).Pb()} +} + +func (*Event_RegisterCheckpoint) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_RegisterCheckpoint]()} +} + func EventFromPb(pb *bcmpb.Event) *Event { if pb == nil { return nil @@ -421,3 +500,101 @@ func (m *GetChainResponse) Pb() *bcmpb.GetChainResponse { func (*GetChainResponse) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetChainResponse]()} } + +type GetHeadToCheckpointChainRequest struct { + RequestId string + SourceModule types.ModuleID +} + +func GetHeadToCheckpointChainRequestFromPb(pb *bcmpb.GetHeadToCheckpointChainRequest) *GetHeadToCheckpointChainRequest { + if pb == nil { + return nil + } + return &GetHeadToCheckpointChainRequest{ + RequestId: pb.RequestId, + SourceModule: (types.ModuleID)(pb.SourceModule), + } +} + +func (m *GetHeadToCheckpointChainRequest) Pb() *bcmpb.GetHeadToCheckpointChainRequest { + if m == nil { + return nil + } + pbMessage := &bcmpb.GetHeadToCheckpointChainRequest{} + { + pbMessage.RequestId = m.RequestId + pbMessage.SourceModule = (string)(m.SourceModule) + } + + return pbMessage +} + +func (*GetHeadToCheckpointChainRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetHeadToCheckpointChainRequest]()} +} + +type GetHeadToCheckpointChainResponse struct { + RequestId string + Chain []*blockchainpb.BlockInternal +} + +func GetHeadToCheckpointChainResponseFromPb(pb *bcmpb.GetHeadToCheckpointChainResponse) *GetHeadToCheckpointChainResponse { + if pb == nil { + return nil + } + return &GetHeadToCheckpointChainResponse{ + RequestId: pb.RequestId, + Chain: pb.Chain, + } +} + +func (m *GetHeadToCheckpointChainResponse) Pb() *bcmpb.GetHeadToCheckpointChainResponse { + if m == nil { + return nil + } + pbMessage := &bcmpb.GetHeadToCheckpointChainResponse{} + { + pbMessage.RequestId = m.RequestId + pbMessage.Chain = m.Chain + } + + return pbMessage +} + +func (*GetHeadToCheckpointChainResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetHeadToCheckpointChainResponse]()} +} + +type RegisterCheckpoint struct { + BlockId uint64 + State *statepb.State +} + +func RegisterCheckpointFromPb(pb *bcmpb.RegisterCheckpoint) *RegisterCheckpoint { + if pb == nil { + return nil + } + return &RegisterCheckpoint{ + BlockId: pb.BlockId, + State: pb.State, + } +} + +func (m *RegisterCheckpoint) Pb() *bcmpb.RegisterCheckpoint { + if m == nil { + return nil + } + pbMessage := &bcmpb.RegisterCheckpoint{} + { + pbMessage.BlockId = m.BlockId + if m.State != nil { + pbMessage.State = m.State + } + } + + return pbMessage +} + +func (*RegisterCheckpoint) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.RegisterCheckpoint]()} +} diff --git a/pkg/pb/blockchainpb/blockchainpb.pb.go b/pkg/pb/blockchainpb/blockchainpb.pb.go index a88eb3e2e..9e44ec90d 100644 --- a/pkg/pb/blockchainpb/blockchainpb.pb.go +++ b/pkg/pb/blockchainpb/blockchainpb.pb.go @@ -8,6 +8,7 @@ package blockchainpb import ( payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -194,6 +195,61 @@ func (x *Block) GetTimestamp() int64 { return 0 } +type BlockInternal struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Block *Block `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"` + State *statepb.State `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` +} + +func (x *BlockInternal) Reset() { + *x = BlockInternal{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *BlockInternal) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*BlockInternal) ProtoMessage() {} + +func (x *BlockInternal) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[3] + 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 BlockInternal.ProtoReflect.Descriptor instead. +func (*BlockInternal) Descriptor() ([]byte, []int) { + return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{3} +} + +func (x *BlockInternal) GetBlock() *Block { + if x != nil { + return x.Block + } + return nil +} + +func (x *BlockInternal) GetState() *statepb.State { + if x != nil { + return x.State + } + return nil +} + var File_blockchainpb_blockchainpb_proto protoreflect.FileDescriptor var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ @@ -202,29 +258,38 @@ var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ 0x6f, 0x12, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x1a, 0x26, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, - 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x50, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x74, 0x72, 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, - 0x04, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x22, 0x39, 0x0a, 0x0a, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, - 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, - 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, - 0x70, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x50, 0x0a, 0x09, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, + 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x22, 0x39, 0x0a, + 0x0a, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, + 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, + 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, + 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x60, 0x0a, 0x0d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, + 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -239,22 +304,26 @@ func file_blockchainpb_blockchainpb_proto_rawDescGZIP() []byte { return file_blockchainpb_blockchainpb_proto_rawDescData } -var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 4) var file_blockchainpb_blockchainpb_proto_goTypes = []interface{}{ (*Blocktree)(nil), // 0: blockchainpb.Blocktree (*Blockchain)(nil), // 1: blockchainpb.Blockchain (*Block)(nil), // 2: blockchainpb.Block - (*payloadpb.Payload)(nil), // 3: payloadpb.Payload + (*BlockInternal)(nil), // 3: blockchainpb.BlockInternal + (*payloadpb.Payload)(nil), // 4: payloadpb.Payload + (*statepb.State)(nil), // 5: statepb.State } var file_blockchainpb_blockchainpb_proto_depIdxs = []int32{ 2, // 0: blockchainpb.Blocktree.blocks:type_name -> blockchainpb.Block 2, // 1: blockchainpb.Blockchain.blocks:type_name -> blockchainpb.Block - 3, // 2: blockchainpb.Block.payload:type_name -> payloadpb.Payload - 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 + 4, // 2: blockchainpb.Block.payload:type_name -> payloadpb.Payload + 2, // 3: blockchainpb.BlockInternal.block:type_name -> blockchainpb.Block + 5, // 4: blockchainpb.BlockInternal.state:type_name -> statepb.State + 5, // [5:5] is the sub-list for method output_type + 5, // [5:5] 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 } func init() { file_blockchainpb_blockchainpb_proto_init() } @@ -299,6 +368,18 @@ func file_blockchainpb_blockchainpb_proto_init() { return nil } } + file_blockchainpb_blockchainpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*BlockInternal); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -306,7 +387,7 @@ func file_blockchainpb_blockchainpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_blockchainpb_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 4, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/statepb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/statepb/oneof_interfaces.mir.go new file mode 100644 index 000000000..e03a0f056 --- /dev/null +++ b/pkg/pb/blockchainpb/statepb/oneof_interfaces.mir.go @@ -0,0 +1,3 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package statepb diff --git a/pkg/pb/blockchainpb/statepb/statepb.pb.go b/pkg/pb/blockchainpb/statepb/statepb.pb.go new file mode 100644 index 000000000..6f2ac176e --- /dev/null +++ b/pkg/pb/blockchainpb/statepb/statepb.pb.go @@ -0,0 +1,147 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.31.0 +// protoc v4.24.4 +// source: blockchainpb/statepb/statepb.proto + +package statepb + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type State struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // application specific state + Counter int64 `protobuf:"varint,1,opt,name=counter,proto3" json:"counter,omitempty"` +} + +func (x *State) Reset() { + *x = State{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_statepb_statepb_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *State) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*State) ProtoMessage() {} + +func (x *State) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_statepb_statepb_proto_msgTypes[0] + 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 State.ProtoReflect.Descriptor instead. +func (*State) Descriptor() ([]byte, []int) { + return file_blockchainpb_statepb_statepb_proto_rawDescGZIP(), []int{0} +} + +func (x *State) GetCounter() int64 { + if x != nil { + return x.Counter + } + return 0 +} + +var File_blockchainpb_statepb_statepb_proto protoreflect.FileDescriptor + +var file_blockchainpb_statepb_statepb_proto_rawDesc = []byte{ + 0x0a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x22, 0x21, 0x0a, + 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, + 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, + 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, + 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, + 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_blockchainpb_statepb_statepb_proto_rawDescOnce sync.Once + file_blockchainpb_statepb_statepb_proto_rawDescData = file_blockchainpb_statepb_statepb_proto_rawDesc +) + +func file_blockchainpb_statepb_statepb_proto_rawDescGZIP() []byte { + file_blockchainpb_statepb_statepb_proto_rawDescOnce.Do(func() { + file_blockchainpb_statepb_statepb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_statepb_statepb_proto_rawDescData) + }) + return file_blockchainpb_statepb_statepb_proto_rawDescData +} + +var file_blockchainpb_statepb_statepb_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_blockchainpb_statepb_statepb_proto_goTypes = []interface{}{ + (*State)(nil), // 0: statepb.State +} +var file_blockchainpb_statepb_statepb_proto_depIdxs = []int32{ + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] 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_blockchainpb_statepb_statepb_proto_init() } +func file_blockchainpb_statepb_statepb_proto_init() { + if File_blockchainpb_statepb_statepb_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_blockchainpb_statepb_statepb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*State); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_blockchainpb_statepb_statepb_proto_rawDesc, + NumEnums: 0, + NumMessages: 1, + NumExtensions: 0, + NumServices: 0, + }, + GoTypes: file_blockchainpb_statepb_statepb_proto_goTypes, + DependencyIndexes: file_blockchainpb_statepb_statepb_proto_depIdxs, + MessageInfos: file_blockchainpb_statepb_statepb_proto_msgTypes, + }.Build() + File_blockchainpb_statepb_statepb_proto = out.File + file_blockchainpb_statepb_statepb_proto_rawDesc = nil + file_blockchainpb_statepb_statepb_proto_goTypes = nil + file_blockchainpb_statepb_statepb_proto_depIdxs = nil +} diff --git a/pkg/pb/blockchainpb/statepb/types/types.mir.go b/pkg/pb/blockchainpb/statepb/types/types.mir.go new file mode 100644 index 000000000..c583fc3e4 --- /dev/null +++ b/pkg/pb/blockchainpb/statepb/types/types.mir.go @@ -0,0 +1,3 @@ +// Code generated by Mir codegen. DO NOT EDIT. + +package statepbtypes diff --git a/protos/blockchainpb/applicationpb/applicationpb.proto b/protos/blockchainpb/applicationpb/applicationpb.proto index c84335454..e978bf1c0 100644 --- a/protos/blockchainpb/applicationpb/applicationpb.proto +++ b/protos/blockchainpb/applicationpb/applicationpb.proto @@ -5,7 +5,6 @@ package applicationpb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb"; import "mir/codegen_extensions.proto"; -import "blockchainpb/blockchainpb.proto"; import "blockchainpb/payloadpb/payloadpb.proto"; message Event { @@ -15,9 +14,8 @@ message Event { option (mir.event_type) = true; NewHead new_head = 1; - RegisterBlock register_block = 2; - PayloadRequest payload_request = 3; - PayloadResponse payload_response = 4; + PayloadRequest payload_request = 2; + PayloadResponse payload_response = 3; } } @@ -28,12 +26,6 @@ message NewHead { uint64 head_id = 1; } -message RegisterBlock { - option (mir.event) = true; - - blockchainpb.Block block_id = 1; -} - message PayloadRequest { option (mir.event) = true; diff --git a/protos/blockchainpb/bcmpb/bcmpb.proto b/protos/blockchainpb/bcmpb/bcmpb.proto index 980b37d47..05cf8fe35 100644 --- a/protos/blockchainpb/bcmpb/bcmpb.proto +++ b/protos/blockchainpb/bcmpb/bcmpb.proto @@ -5,6 +5,7 @@ package bcmpb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb"; import "blockchainpb/blockchainpb.proto"; +import "blockchainpb/statepb/statepb.proto"; import "mir/codegen_extensions.proto"; @@ -14,13 +15,16 @@ message Event { oneof type { option (mir.event_type) = true; - NewBlock new_block = 1; - NewChain new_chain = 2; + NewBlock new_block = 1; + NewChain new_chain = 2; - GetBlockRequest get_block_request = 3; - GetBlockResponse get_block_response = 4; - GetChainRequest get_chain_request = 5; - GetChainResponse get_chain_response = 6; + GetBlockRequest get_block_request = 3; + GetBlockResponse get_block_response = 4; + GetChainRequest get_chain_request = 5; + GetChainResponse get_chain_response = 6; + GetHeadToCheckpointChainRequest get_head_to_checkpoint_chain_request = 7; + GetHeadToCheckpointChainResponse get_head_to_checkpoint_chain_response = 8; + RegisterCheckpoint register_checkpoint = 9; } } @@ -70,4 +74,25 @@ message GetChainResponse { string request_id = 1; bool success = 2; repeated blockchainpb.Block chain = 3; // sorted from oldest to youngest +} + +message GetHeadToCheckpointChainRequest { + option (mir.event) = true; + + string request_id = 1; + string source_module = 2 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.ModuleID"]; +} + +message GetHeadToCheckpointChainResponse { + option (mir.event) = true; + + string request_id = 1; + repeated blockchainpb.BlockInternal chain = 3; // from checkpoint to head +} + +message RegisterCheckpoint { + option (mir.event) = true; + + uint64 block_id = 1; + statepb.State state = 2; } \ No newline at end of file diff --git a/protos/blockchainpb/blockchainpb.proto b/protos/blockchainpb/blockchainpb.proto index 357e990ea..8d84ea8a1 100644 --- a/protos/blockchainpb/blockchainpb.proto +++ b/protos/blockchainpb/blockchainpb.proto @@ -5,6 +5,7 @@ package blockchainpb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb"; import "blockchainpb/payloadpb/payloadpb.proto"; +import "blockchainpb/statepb/statepb.proto"; message Blocktree { repeated Block blocks = 1; @@ -18,7 +19,12 @@ message Blockchain { message Block { uint64 block_id = 1; uint64 previous_block_id = 2; - payloadpb.Payload payload = 3; + payloadpb.Payload payload = 3; int64 timestamp = 4; } + +message BlockInternal { + Block block = 1; + statepb.State state = 2; +} \ No newline at end of file diff --git a/protos/blockchainpb/statepb/statepb.proto b/protos/blockchainpb/statepb/statepb.proto new file mode 100644 index 000000000..a84dd0549 --- /dev/null +++ b/protos/blockchainpb/statepb/statepb.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package statepb; + +option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb"; + +message State { + // application specific state + int64 counter = 1; +} \ No newline at end of file diff --git a/protos/generate.go b/protos/generate.go index 66cc351a8..de0f8d603 100644 --- a/protos/generate.go +++ b/protos/generate.go @@ -52,6 +52,7 @@ package protos //go:generate protoc-events blockchainpb/synchronizerpb/synchronizerpb.proto //go:generate protoc-events blockchainpb/applicationpb/applicationpb.proto //go:generate protoc-events blockchainpb/payloadpb/payloadpb.proto +//go:generate protoc-events blockchainpb/statepb/statepb.proto //go:generate protoc-events blockchainpb/interceptorpb/interceptorpb.proto // Build the custom code generators. @@ -91,6 +92,7 @@ package protos //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" +//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index 497633004..c36b5ce83 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -3,6 +3,7 @@ package application // app module import ( + "fmt" "math/rand" "github.com/filecoin-project/mir/pkg/dsl" @@ -10,15 +11,28 @@ import ( "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/pb/blockchainpb" applicationpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/dsl" + bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" "github.com/filecoin-project/mir/samples/blockchain/utils" ) +type localState struct { + head uint64 + state *statepb.State +} + type ApplicationModule struct { - m *dsl.Module - state *state - logger logging.Logger + m *dsl.Module + currentState *localState + logger logging.Logger +} + +func applyBlockToState(state *statepb.State, block *blockchainpb.Block) *statepb.State { + return &statepb.State{ + Counter: state.Counter + block.Payload.AddMinus, + } } func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { @@ -37,25 +51,47 @@ func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { return nil } -func (am *ApplicationModule) handleRegisterBlock(block *blockchainpb.Block) error { - am.state.applyBlock(block) - am.logger.Log(logging.LevelDebug, "Registered new block", "block_id", utils.FormatBlockId(block.BlockId)) +func (am *ApplicationModule) handleGetHeadToCheckpointChainResponse(requestID string, chain []*blockchainpb.BlockInternal) error { + // TODO: ignoring request ids rn - they are probably not even needed + am.logger.Log(logging.LevelInfo, "Received chain", "requestId", requestID, "chain", chain) + + if len(chain) == 0 { + am.logger.Log(logging.LevelError, "Received empty chain - this should not happen") + panic("Received empty chain - this should not happen") + } + + state := chain[0].State + blockId := chain[0].Block.BlockId + if state == nil { + am.logger.Log(logging.LevelError, "Received chain with empty checkpoint state - this should not happen") + panic("Received chain with empty checkpoint state - this should not happen") + } + + for _, block := range chain[1:] { + state = applyBlockToState(state, block.Block) + blockId = block.Block.BlockId + } + + bcmpbdsl.RegisterCheckpoint(*am.m, "bcm", blockId, state) + interceptorpbdsl.AppUpdate(*am.m, "devnull", state.Counter) + am.currentState = &localState{ + head: blockId, + state: state, + } + return nil } func (am *ApplicationModule) handleNewHead(head_id uint64) error { - previousState := am.state.getCurrentState() - am.state.setHead(head_id) - currentState := am.state.getCurrentState() - interceptorpbdsl.AppUpdate(*am.m, "devnull", currentState) - am.logger.Log(logging.LevelInfo, "Application state updated", "previousState", previousState, "currentState", currentState, "headId", utils.FormatBlockId(head_id)) + am.logger.Log(logging.LevelDebug, "Processing new head, sending request for state update", "headId", utils.FormatBlockId(head_id)) + bcmpbdsl.GetHeadToCheckpointChainRequest(*am.m, "bcm", fmt.Sprint(head_id), "application") return nil } func NewApplication(logger logging.Logger) modules.PassiveModule { m := dsl.NewModule("application") - am := &ApplicationModule{m: &m, state: initState(), logger: logger} + am := &ApplicationModule{m: &m, currentState: nil, logger: logger} dsl.UponInit(m, func() error { return nil @@ -63,7 +99,7 @@ func NewApplication(logger logging.Logger) modules.PassiveModule { applicationpbdsl.UponPayloadRequest(m, am.handlePayloadRequest) applicationpbdsl.UponNewHead(m, am.handleNewHead) - applicationpbdsl.UponRegisterBlock(m, am.handleRegisterBlock) + bcmpbdsl.UponGetHeadToCheckpointChainResponse(m, am.handleGetHeadToCheckpointChainResponse) return m } diff --git a/samples/blockchain/application/state.go b/samples/blockchain/application/state.go deleted file mode 100644 index f4feb1bf1..000000000 --- a/samples/blockchain/application/state.go +++ /dev/null @@ -1,41 +0,0 @@ -package application - -import "github.com/filecoin-project/mir/pkg/pb/blockchainpb" - -type state struct { - head uint64 - state map[uint64]int64 - headCounter int -} - -func initState() *state { - - initialState := state{ - head: 0, // referenced by genesis block - state: make(map[uint64]int64), - } - - initialState.state[0] = 0 - - return &initialState -} - -func (s *state) applyBlock(block *blockchainpb.Block) { - if _, ok := s.state[block.PreviousBlockId]; !ok { - panic("previous block not found") - } - - s.state[block.BlockId] = s.state[block.PreviousBlockId] + block.Payload.AddMinus - -} - -func (s *state) setHead(head uint64) { - if _, ok := s.state[head]; !ok { - panic("head not found") - } - s.head = head -} - -func (s *state) getCurrentState() int64 { - return s.state[s.head] -} diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 312fb723d..c7ca49533 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -14,6 +14,7 @@ import ( interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" minerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" t "github.com/filecoin-project/mir/pkg/types" "github.com/filecoin-project/mir/samples/blockchain/utils" @@ -26,20 +27,27 @@ var ( ) type bcmBlock struct { - block *blockchainpb.Block + block *blockchainpb.BlockInternal parent *bcmBlock depth uint64 scanCount uint64 } type bcmModule struct { - m *dsl.Module - blocks []bcmBlock // IMPORTANT: only to be used for visualization, not for actual block lookup - leaves map[uint64]*bcmBlock - head *bcmBlock - genesis *bcmBlock - blockCount uint64 - logger logging.Logger + m *dsl.Module + blocks []bcmBlock // IMPORTANT: only to be used for visualization, not for actual block lookup + leaves map[uint64]*bcmBlock + head *bcmBlock + genesis *bcmBlock + checkpoints map[uint64]*bcmBlock + blockCount uint64 + logger logging.Logger +} + +func (bcm *bcmModule) handleNewHead(newHead *blockchainpb.BlockInternal) { + bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.Block.BlockId)) + applicationpbdsl.NewHead(*bcm.m, "application", newHead.Block.BlockId) + minerpbdsl.NewHead(*bcm.m, "miner", newHead.Block.BlockId) } // traversalFunc should return true if traversal should continue, false otherwise @@ -92,7 +100,14 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { // return nil if it isn't there if parent, ok := bcm.leaves[parentId]; ok { bcm.logger.Log(logging.LevelDebug, "Found parend in leaves", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(parentId)) - blockNode := bcmBlock{block: block, parent: parent, depth: parent.depth + 1} + blockNode := bcmBlock{ + block: &blockchainpb.BlockInternal{ + Block: block, + State: nil, + }, + parent: parent, + depth: parent.depth + 1, + } bcm.blocks = append(bcm.blocks, blockNode) // replace leave delete(bcm.leaves, parentId) @@ -106,15 +121,23 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { if hit := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { // check if curr matches the block to be added - if so, ignore - if currBlock.block.BlockId == block.BlockId { + if currBlock.block.Block.BlockId == block.BlockId { bcm.logger.Log(logging.LevelDebug, "Block already in tree", "blockId", utils.FormatBlockId(block.BlockId)) return true, ErrNodeAlreadyInTree } // check if curr is parent - if currBlock.block.BlockId == parentId { + if currBlock.block.Block.BlockId == parentId { bcm.logger.Log(logging.LevelDebug, "Found parend in tree", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(parentId)) - blockNode := bcmBlock{block: block, parent: currBlock, depth: currBlock.depth + 1, scanCount: 0} + blockNode := bcmBlock{ + block: &blockchainpb.BlockInternal{ + Block: block, + State: nil, + }, + parent: currBlock, + depth: currBlock.depth + 1, + scanCount: 0, + } // add new leave bcm.leaves[block.BlockId] = &blockNode bcm.blocks = append(bcm.blocks, blockNode) @@ -136,14 +159,14 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { func (bcm *bcmModule) findBlock(blockId uint64) *bcmBlock { return bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { - if currBlock.block.BlockId == blockId { + if currBlock.block.Block.BlockId == blockId { return true, nil } return false, nil }) } -func (bcm *bcmModule) getHead() *blockchainpb.Block { +func (bcm *bcmModule) getHead() *blockchainpb.BlockInternal { return bcm.head.block } @@ -157,10 +180,10 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { bcm.logger.Log(logging.LevelDebug, "Block already in tree - ignore", "blockId", utils.FormatBlockId(block.BlockId)) case ErrParentNotFound: // ask synchronizer for help - leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.BlockId) + leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.Block.BlockId) bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", utils.FormatBlockId(block.BlockId), "leaves+genesis", utils.FormatBlockIdSlice(leavesPlusGenesis)) synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", block, leavesPlusGenesis) - // announce orphan block to interceptor + // announce orphan block to interceptor, just for the visualization interceptorpbdsl.NewOrphan(*bcm.m, "devnull", block) default: // some other error @@ -170,15 +193,9 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { return } - // register block in app - applicationpbdsl.RegisterBlock(*bcm.m, "application", block) - // if head changed, trigger get tpm to prep new block if newHead := bcm.getHead(); newHead != currentHead { - // new head - bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.BlockId)) - applicationpbdsl.NewHead(*bcm.m, "application", newHead.BlockId) - minerpbdsl.NewHead(*bcm.m, "miner", newHead.BlockId) + bcm.handleNewHead(newHead) } // send to chainviewer @@ -201,7 +218,7 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { bcm.logger.Log(logging.LevelDebug, "Block already in tree - ignore", "blockId", utils.FormatBlockId(block.BlockId)) case ErrParentNotFound: // ask synchronizer for help - leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.BlockId) + leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.Block.BlockId) bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", utils.FormatBlockId(block.BlockId), "leaves+genesis", utils.FormatBlockIdSlice(leavesPlusGenesis)) synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", block, leavesPlusGenesis) // announce orphan block to interceptor @@ -212,17 +229,11 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { panic(err) } } - // register block in app - applicationpbdsl.RegisterBlock(*bcm.m, "application", block) - } // if head changed, trigger get tpm to prep new block (only need to do this once) if newHead := bcm.getHead(); newHead != currentHead { - // new head - bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.BlockId)) - applicationpbdsl.NewHead(*bcm.m, "application", newHead.BlockId) - minerpbdsl.NewHead(*bcm.m, "miner", newHead.BlockId) + bcm.handleNewHead(newHead) } // send to chainviewer @@ -233,21 +244,66 @@ func (bcm *bcmModule) sendTreeUpdate() { blocks := func() []*blockchainpb.Block { blocks := make([]*blockchainpb.Block, 0, len(bcm.blocks)) for _, v := range bcm.blocks { - blocks = append(blocks, v.block) + blocks = append(blocks, v.block.Block) } return blocks }() leaves := func() []uint64 { leaves := make([]uint64, 0, len(bcm.leaves)) for _, v := range bcm.leaves { - leaves = append(leaves, v.block.BlockId) + leaves = append(leaves, v.block.Block.BlockId) } return leaves }() blockTree := blockchainpb.Blocktree{Blocks: blocks, Leaves: leaves} - interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", &blockTree, bcm.head.block.BlockId) + interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", &blockTree, bcm.head.block.Block.BlockId) +} + +func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, sourceModule t.ModuleID) error { + bcm.logger.Log(logging.LevelInfo, "Received get head to checkpoint chain request", "requestId", requestID) + chain := make([]*blockchainpb.BlockInternal, 0) + + // start with head + currentBlock := bcm.head + for currentBlock != nil { + chain = append(chain, currentBlock.block) + if _, ok := bcm.checkpoints[currentBlock.block.Block.BlockId]; ok { + break + } + currentBlock = currentBlock.parent + } + + for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 { + chain[i], chain[j] = chain[j], chain[i] + } + + bcmpbdsl.GetHeadToCheckpointChainResponse(*bcm.m, sourceModule, requestID, chain) + + return nil + +} + +func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepb.State) error { + bcm.logger.Log(logging.LevelInfo, "Received register checkpoint", "blockId", utils.FormatBlockId(block_id)) + if _, ok := bcm.checkpoints[block_id]; ok { + bcm.logger.Log(logging.LevelDebug, "Checkpoint already registered - ignore", "blockId", utils.FormatBlockId(block_id)) + return nil + } + + // find block + // should be a leave or a block really close + if hit := bcm.findBlock(block_id); hit == nil { + bcm.logger.Log(logging.LevelError, "Block not found - can't register checkpoint", "blockId", utils.FormatBlockId(block_id)) + panic("block not found when registering checkpoint") // should never happen + } else { + bcm.logger.Log(logging.LevelDebug, "Found block - registering checkpoint", "blockId", utils.FormatBlockId(block_id)) + bcm.checkpoints[block_id] = hit + hit.block.State = state + } + + return nil } func NewBCM(logger logging.Logger) modules.PassiveModule { @@ -263,27 +319,36 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { hash := utils.HashBlock(genesis) genesis.BlockId = hash - genesisBcm := bcmBlock{block: genesis, parent: nil, depth: 0} + genesisBcm := bcmBlock{ + block: &blockchainpb.BlockInternal{ + Block: genesis, + State: &statepb.State{ + Counter: 0, + }, + }, + parent: nil, + depth: 0, + } // init bcm genisis block bcm := bcmModule{ - m: &m, - blocks: []bcmBlock{genesisBcm}, - leaves: make(map[uint64]*bcmBlock), - head: &genesisBcm, - genesis: &genesisBcm, - blockCount: 1, - logger: logger, + m: &m, + blocks: []bcmBlock{genesisBcm}, + leaves: make(map[uint64]*bcmBlock), + head: &genesisBcm, + genesis: &genesisBcm, + checkpoints: make(map[uint64]*bcmBlock), + blockCount: 1, + logger: logger, } - // add genesis to leaves + // add genesis to leaves and checkpoints bcm.leaves[hash] = bcm.head + bcm.checkpoints[hash] = bcm.head dsl.UponInit(m, func() error { // start with genesis block - // setup app state accordingly (expects first block to have 0 as previous block id) - applicationpbdsl.RegisterBlock(m, "application", genesis) applicationpbdsl.NewHead(m, "application", hash) // start mining on genesis block minerpbdsl.NewHead(m, "miner", hash) @@ -314,7 +379,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { } bcm.logger.Log(logging.LevelDebug, "Found block in tree", "requestId", requestID) - bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block) + bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block.GetBlock()) return nil }) @@ -338,11 +403,11 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { bcm.logger.Log(logging.LevelDebug, "Found block in tree - building chain", "requestId", requestID) // initialize chain - chain = append(chain, currentBlock.block) + chain = append(chain, currentBlock.block.Block) for currentBlock.parent != nil { // if parent is in sourceBlockIds, stop - if _, ok := sourceBlockIdsMap[currentBlock.parent.block.BlockId]; ok { + if _, ok := sourceBlockIdsMap[currentBlock.parent.block.Block.BlockId]; ok { // chain build bcm.logger.Log(logging.LevelDebug, "Found link with sourceBlockIds - chain build", "requestId", requestID) // reversing chain to send it in the right order @@ -353,16 +418,20 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { return nil } - chain = append(chain, currentBlock.parent.block) + chain = append(chain, currentBlock.parent.block.Block) currentBlock = currentBlock.parent } // if we get here, we didn't find a link with sourceBlockIds bcm.logger.Log(logging.LevelDebug, "No link with sourceBlockIds - can't build chain", "requestId", requestID) bcmpbdsl.GetChainResponse(*bcm.m, sourceModule, requestID, false, nil) + return nil }) + bcmpbdsl.UponGetHeadToCheckpointChainRequest(m, bcm.handleGetHeadToCheckpointChainRequest) + bcmpbdsl.UponRegisterCheckpoint(m, bcm.handleRegisterCheckpoint) + return m } From f39c97a9e77806ded49430aa9beb3a7d1252dae0 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Thu, 7 Dec 2023 14:54:51 +0100 Subject: [PATCH 10/46] fixes with state update - needs cleanup --- samples/blockchain/bcm.go | 36 ++++++++++++++++++++++++++++++++---- samples/blockchain/main.go | 2 +- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index c7ca49533..c25f54513 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -4,6 +4,7 @@ package main import ( "errors" + "fmt" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" @@ -261,24 +262,51 @@ func (bcm *bcmModule) sendTreeUpdate() { interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", &blockTree, bcm.head.block.Block.BlockId) } +func formatBlockInts(blocks []*blockchainpb.BlockInternal) []string { + blockIds := make([]string, 0, len(blocks)) + for _, v := range blocks { + stateText := "no state" + if v.State != nil { + stateText = v.State.String() + } + blockIds = append(blockIds, fmt.Sprintf("%d - %s", v.Block.BlockId, stateText)) + } + return blockIds +} + func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, sourceModule t.ModuleID) error { - bcm.logger.Log(logging.LevelInfo, "Received get head to checkpoint chain request", "requestId", requestID) + // for debugging + // TODO: remove + checkpoints := make([]*blockchainpb.BlockInternal, 0, len(bcm.checkpoints)) + + for _, value := range bcm.checkpoints { + checkpoints = append(checkpoints, value.block) + } + + bcm.logger.Log(logging.LevelInfo, "Received get head to checkpoint chain request", "requestId", requestID, "checkpoints", formatBlockInts(checkpoints)) chain := make([]*blockchainpb.BlockInternal, 0) // start with head currentBlock := bcm.head for currentBlock != nil { - chain = append(chain, currentBlock.block) - if _, ok := bcm.checkpoints[currentBlock.block.Block.BlockId]; ok { + if cp, ok := bcm.checkpoints[currentBlock.block.Block.BlockId]; ok { + chain = append(chain, cp.block) break } + chain = append(chain, currentBlock.block) currentBlock = currentBlock.parent } + if currentBlock == nil { + bcm.logger.Log(logging.LevelError, "Cannot link up with checkpoints - this should not happen", "chain", chain) + panic("Cannot link up with checkpoints - this should not happen") + } + for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 { chain[i], chain[j] = chain[j], chain[i] } + bcm.logger.Log(logging.LevelDebug, "Sending chain for state comp", "requestId", requestID, "chain", formatBlockInts(chain), "checkpoint", formatBlockInts([]*blockchainpb.BlockInternal{currentBlock.block})) bcmpbdsl.GetHeadToCheckpointChainResponse(*bcm.m, sourceModule, requestID, chain) return nil @@ -299,8 +327,8 @@ func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepb.S panic("block not found when registering checkpoint") // should never happen } else { bcm.logger.Log(logging.LevelDebug, "Found block - registering checkpoint", "blockId", utils.FormatBlockId(block_id)) - bcm.checkpoints[block_id] = hit hit.block.State = state + bcm.checkpoints[block_id] = hit } return nil diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index c2cee68e7..6c30c81c5 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -74,7 +74,7 @@ func main() { mangler, err := eventmangler.NewModule( eventmangler.ModuleConfig{Self: "mangler", Dest: "transport", Timer: "timer"}, - &eventmangler.ModuleParams{MinDelay: time.Second / 1000, MaxDelay: 2 * time.Second, DropRate: 0.15}, + &eventmangler.ModuleParams{MinDelay: time.Second / 1000, MaxDelay: 1 * time.Second, DropRate: 0.05}, ) if err != nil { panic(err) From 46800f66c8f791062603a5dfef107e37734656ab Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Fri, 29 Dec 2023 15:36:22 +0100 Subject: [PATCH 11/46] msg payloads, delta --- .../applicationpb/applicationpb.pb.go | 436 +++++++++++++++--- .../applicationpb/applicationpb.pb.mir.go | 3 + .../applicationpb/dsl/emit.mir.go | 14 + .../applicationpb/dsl/upon.mir.go | 20 + .../applicationpb/events/events.mir.go | 51 ++ .../applicationpb/oneof_interfaces.mir.go | 12 + .../applicationpb/types/types.mir.go | 187 ++++++++ .../interceptorpb/dsl/emit.mir.go | 3 +- .../interceptorpb/dsl/upon.mir.go | 3 +- .../interceptorpb/events/events.mir.go | 3 +- .../interceptorpb/interceptorpb.pb.go | 87 ++-- .../interceptorpb/types/types.mir.go | 7 +- pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go | 31 +- pkg/pb/blockchainpb/statepb/statepb.pb.go | 25 +- pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go | 15 - pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go | 28 -- .../blockchainpb/tpmpb/events/events.mir.go | 24 - .../tpmpb/oneof_interfaces.mir.go | 14 - pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go | 240 ---------- pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go | 13 - pkg/pb/blockchainpb/tpmpb/types/types.mir.go | 115 ----- pkg/pb/eventpb/eventpb.pb.go | 240 +++++----- pkg/pb/eventpb/eventpb.pb.mir.go | 1 - pkg/pb/eventpb/oneof_interfaces.mir.go | 5 - pkg/pb/eventpb/types/types.mir.go | 101 ++-- .../applicationpb/applicationpb.proto | 39 +- .../interceptorpb/interceptorpb.proto | 3 +- protos/blockchainpb/payloadpb/payloadpb.proto | 3 +- protos/blockchainpb/statepb/statepb.proto | 2 +- protos/blockchainpb/tpmpb/tpmpb.proto | 24 - protos/eventpb/eventpb.proto | 8 +- protos/generate.go | 2 - samples/blockchain/application/application.go | 117 +++-- .../application/transactions/transactions.go | 100 ++++ samples/blockchain/bcm.go | 211 ++++++--- samples/blockchain/main.go | 2 +- samples/blockchain/tpm.go | 40 -- 37 files changed, 1273 insertions(+), 956 deletions(-) delete mode 100644 pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go delete mode 100644 pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go delete mode 100644 pkg/pb/blockchainpb/tpmpb/events/events.mir.go delete mode 100644 pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go delete mode 100644 pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go delete mode 100644 pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go delete mode 100644 pkg/pb/blockchainpb/tpmpb/types/types.mir.go delete mode 100644 protos/blockchainpb/tpmpb/tpmpb.proto create mode 100644 samples/blockchain/application/transactions/transactions.go delete mode 100644 samples/blockchain/tpm.go diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go index 3c692fe40..eafdee1a0 100644 --- a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go @@ -7,8 +7,9 @@ package applicationpb import ( - _ "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -31,8 +32,11 @@ type Event struct { // Types that are assignable to Type: // // *Event_NewHead + // *Event_VerifyBlockRequest + // *Event_VerifyBlockResponse // *Event_PayloadRequest // *Event_PayloadResponse + // *Event_ForkUpdate Type isEvent_Type `protobuf_oneof:"type"` } @@ -82,6 +86,20 @@ func (x *Event) GetNewHead() *NewHead { return nil } +func (x *Event) GetVerifyBlockRequest() *VerifyBlockRequest { + if x, ok := x.GetType().(*Event_VerifyBlockRequest); ok { + return x.VerifyBlockRequest + } + return nil +} + +func (x *Event) GetVerifyBlockResponse() *VerifyBlockResponse { + if x, ok := x.GetType().(*Event_VerifyBlockResponse); ok { + return x.VerifyBlockResponse + } + return nil +} + func (x *Event) GetPayloadRequest() *PayloadRequest { if x, ok := x.GetType().(*Event_PayloadRequest); ok { return x.PayloadRequest @@ -96,28 +114,56 @@ func (x *Event) GetPayloadResponse() *PayloadResponse { return nil } +func (x *Event) GetForkUpdate() *ForkUpdate { + if x, ok := x.GetType().(*Event_ForkUpdate); ok { + return x.ForkUpdate + } + return nil +} + type isEvent_Type interface { isEvent_Type() } type Event_NewHead struct { - NewHead *NewHead `protobuf:"bytes,1,opt,name=new_head,json=newHead,proto3,oneof"` + // Application-application events + NewHead *NewHead `protobuf:"bytes,10,opt,name=new_head,json=newHead,proto3,oneof"` +} + +type Event_VerifyBlockRequest struct { + VerifyBlockRequest *VerifyBlockRequest `protobuf:"bytes,11,opt,name=verify_block_request,json=verifyBlockRequest,proto3,oneof"` +} + +type Event_VerifyBlockResponse struct { + VerifyBlockResponse *VerifyBlockResponse `protobuf:"bytes,12,opt,name=verify_block_response,json=verifyBlockResponse,proto3,oneof"` } type Event_PayloadRequest struct { - PayloadRequest *PayloadRequest `protobuf:"bytes,2,opt,name=payload_request,json=payloadRequest,proto3,oneof"` + // Transaction management application events + PayloadRequest *PayloadRequest `protobuf:"bytes,20,opt,name=payload_request,json=payloadRequest,proto3,oneof"` } type Event_PayloadResponse struct { - PayloadResponse *PayloadResponse `protobuf:"bytes,3,opt,name=payload_response,json=payloadResponse,proto3,oneof"` + PayloadResponse *PayloadResponse `protobuf:"bytes,21,opt,name=payload_response,json=payloadResponse,proto3,oneof"` +} + +type Event_ForkUpdate struct { + ForkUpdate *ForkUpdate `protobuf:"bytes,22,opt,name=fork_update,json=forkUpdate,proto3,oneof"` } func (*Event_NewHead) isEvent_Type() {} +func (*Event_VerifyBlockRequest) isEvent_Type() {} + +func (*Event_VerifyBlockResponse) isEvent_Type() {} + func (*Event_PayloadRequest) isEvent_Type() {} func (*Event_PayloadResponse) isEvent_Type() {} +func (*Event_ForkUpdate) isEvent_Type() {} + +// Application-application events type NewHead struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -165,6 +211,180 @@ func (x *NewHead) GetHeadId() uint64 { return 0 } +type VerifyBlockRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Block *blockchainpb.Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` +} + +func (x *VerifyBlockRequest) Reset() { + *x = VerifyBlockRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VerifyBlockRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VerifyBlockRequest) ProtoMessage() {} + +func (x *VerifyBlockRequest) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] + 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 VerifyBlockRequest.ProtoReflect.Descriptor instead. +func (*VerifyBlockRequest) Descriptor() ([]byte, []int) { + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{2} +} + +func (x *VerifyBlockRequest) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *VerifyBlockRequest) GetBlock() *blockchainpb.Block { + if x != nil { + return x.Block + } + return nil +} + +type VerifyBlockResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Ok bool `protobuf:"varint,2,opt,name=ok,proto3" json:"ok,omitempty"` +} + +func (x *VerifyBlockResponse) Reset() { + *x = VerifyBlockResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *VerifyBlockResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*VerifyBlockResponse) ProtoMessage() {} + +func (x *VerifyBlockResponse) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] + 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 VerifyBlockResponse.ProtoReflect.Descriptor instead. +func (*VerifyBlockResponse) Descriptor() ([]byte, []int) { + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{3} +} + +func (x *VerifyBlockResponse) GetRequestId() uint64 { + if x != nil { + return x.RequestId + } + return 0 +} + +func (x *VerifyBlockResponse) GetOk() bool { + if x != nil { + return x.Ok + } + return false +} + +type ForkUpdate struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + RemovedChain *blockchainpb.Blockchain `protobuf:"bytes,1,opt,name=removed_chain,json=removedChain,proto3" json:"removed_chain,omitempty"` + AddedChain *blockchainpb.Blockchain `protobuf:"bytes,2,opt,name=added_chain,json=addedChain,proto3" json:"added_chain,omitempty"` + ForkState *statepb.State `protobuf:"bytes,3,opt,name=fork_state,json=forkState,proto3" json:"fork_state,omitempty"` +} + +func (x *ForkUpdate) Reset() { + *x = ForkUpdate{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ForkUpdate) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ForkUpdate) ProtoMessage() {} + +func (x *ForkUpdate) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_applicationpb_applicationpb_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 ForkUpdate.ProtoReflect.Descriptor instead. +func (*ForkUpdate) Descriptor() ([]byte, []int) { + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{4} +} + +func (x *ForkUpdate) GetRemovedChain() *blockchainpb.Blockchain { + if x != nil { + return x.RemovedChain + } + return nil +} + +func (x *ForkUpdate) GetAddedChain() *blockchainpb.Blockchain { + if x != nil { + return x.AddedChain + } + return nil +} + +func (x *ForkUpdate) GetForkState() *statepb.State { + if x != nil { + return x.ForkState + } + return nil +} + +// Transaction management application events type PayloadRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -176,7 +396,7 @@ type PayloadRequest struct { func (x *PayloadRequest) Reset() { *x = PayloadRequest{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -189,7 +409,7 @@ func (x *PayloadRequest) String() string { func (*PayloadRequest) ProtoMessage() {} func (x *PayloadRequest) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -202,7 +422,7 @@ func (x *PayloadRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PayloadRequest.ProtoReflect.Descriptor instead. func (*PayloadRequest) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{2} + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{5} } func (x *PayloadRequest) GetHeadId() uint64 { @@ -224,7 +444,7 @@ type PayloadResponse struct { func (x *PayloadResponse) Reset() { *x = PayloadResponse{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -237,7 +457,7 @@ func (x *PayloadResponse) String() string { func (*PayloadResponse) ProtoMessage() {} func (x *PayloadResponse) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -250,7 +470,7 @@ func (x *PayloadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PayloadResponse.ProtoReflect.Descriptor instead. func (*PayloadResponse) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{3} + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{6} } func (x *PayloadResponse) GetHeadId() uint64 { @@ -275,43 +495,83 @@ var file_blockchainpb_applicationpb_applicationpb_proto_rawDesc = []byte{ 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, - 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x26, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe7, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x12, 0x33, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x48, 0x00, 0x52, 0x07, 0x6e, 0x65, - 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x48, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x26, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd6, 0x03, 0x0a, 0x05, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x48, 0x00, + 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x55, 0x0a, 0x14, 0x76, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x12, 0x76, 0x65, + 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x58, 0x0a, 0x15, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x22, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, + 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x13, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0f, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x14, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, + 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x50, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, - 0x0e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x4b, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x70, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x3a, 0x04, 0x90, 0xa6, - 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, - 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, - 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, - 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, - 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, - 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0f, 0x50, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, + 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x3a, + 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, + 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, - 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x64, 0x0a, + 0x12, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x49, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, + 0xa6, 0x1d, 0x01, 0x22, 0x4a, 0x0a, 0x13, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, + 0xbb, 0x01, 0x0a, 0x0a, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x3d, + 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x52, + 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x39, 0x0a, + 0x0b, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, + 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x0a, 0x61, 0x64, + 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2d, 0x0a, 0x0a, 0x66, 0x6f, 0x72, 0x6b, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x09, 0x66, 0x6f, + 0x72, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x2f, 0x0a, + 0x0e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5e, + 0x0a, 0x0f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, + 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, + 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, + 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, + 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -326,24 +586,37 @@ func file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP() []byte { return file_blockchainpb_applicationpb_applicationpb_proto_rawDescData } -var file_blockchainpb_applicationpb_applicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_blockchainpb_applicationpb_applicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_blockchainpb_applicationpb_applicationpb_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: applicationpb.Event - (*NewHead)(nil), // 1: applicationpb.NewHead - (*PayloadRequest)(nil), // 2: applicationpb.PayloadRequest - (*PayloadResponse)(nil), // 3: applicationpb.PayloadResponse - (*payloadpb.Payload)(nil), // 4: payloadpb.Payload + (*Event)(nil), // 0: applicationpb.Event + (*NewHead)(nil), // 1: applicationpb.NewHead + (*VerifyBlockRequest)(nil), // 2: applicationpb.VerifyBlockRequest + (*VerifyBlockResponse)(nil), // 3: applicationpb.VerifyBlockResponse + (*ForkUpdate)(nil), // 4: applicationpb.ForkUpdate + (*PayloadRequest)(nil), // 5: applicationpb.PayloadRequest + (*PayloadResponse)(nil), // 6: applicationpb.PayloadResponse + (*blockchainpb.Block)(nil), // 7: blockchainpb.Block + (*blockchainpb.Blockchain)(nil), // 8: blockchainpb.Blockchain + (*statepb.State)(nil), // 9: statepb.State + (*payloadpb.Payload)(nil), // 10: payloadpb.Payload } var file_blockchainpb_applicationpb_applicationpb_proto_depIdxs = []int32{ - 1, // 0: applicationpb.Event.new_head:type_name -> applicationpb.NewHead - 2, // 1: applicationpb.Event.payload_request:type_name -> applicationpb.PayloadRequest - 3, // 2: applicationpb.Event.payload_response:type_name -> applicationpb.PayloadResponse - 4, // 3: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload - 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 + 1, // 0: applicationpb.Event.new_head:type_name -> applicationpb.NewHead + 2, // 1: applicationpb.Event.verify_block_request:type_name -> applicationpb.VerifyBlockRequest + 3, // 2: applicationpb.Event.verify_block_response:type_name -> applicationpb.VerifyBlockResponse + 5, // 3: applicationpb.Event.payload_request:type_name -> applicationpb.PayloadRequest + 6, // 4: applicationpb.Event.payload_response:type_name -> applicationpb.PayloadResponse + 4, // 5: applicationpb.Event.fork_update:type_name -> applicationpb.ForkUpdate + 7, // 6: applicationpb.VerifyBlockRequest.block:type_name -> blockchainpb.Block + 8, // 7: applicationpb.ForkUpdate.removed_chain:type_name -> blockchainpb.Blockchain + 8, // 8: applicationpb.ForkUpdate.added_chain:type_name -> blockchainpb.Blockchain + 9, // 9: applicationpb.ForkUpdate.fork_state:type_name -> statepb.State + 10, // 10: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_blockchainpb_applicationpb_applicationpb_proto_init() } @@ -377,7 +650,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*PayloadRequest); i { + switch v := v.(*VerifyBlockRequest); i { case 0: return &v.state case 1: @@ -389,6 +662,42 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*VerifyBlockResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ForkUpdate); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*PayloadRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PayloadResponse); i { case 0: return &v.state @@ -403,8 +712,11 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_NewHead)(nil), + (*Event_VerifyBlockRequest)(nil), + (*Event_VerifyBlockResponse)(nil), (*Event_PayloadRequest)(nil), (*Event_PayloadResponse)(nil), + (*Event_ForkUpdate)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -412,7 +724,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_applicationpb_applicationpb_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go index f14dcfd8a..141532059 100644 --- a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go @@ -9,7 +9,10 @@ import ( func (*Event) ReflectTypeOptions() []reflect.Type { return []reflect.Type{ reflect.TypeOf((*Event_NewHead)(nil)), + reflect.TypeOf((*Event_VerifyBlockRequest)(nil)), + reflect.TypeOf((*Event_VerifyBlockResponse)(nil)), reflect.TypeOf((*Event_PayloadRequest)(nil)), reflect.TypeOf((*Event_PayloadResponse)(nil)), + reflect.TypeOf((*Event_ForkUpdate)(nil)), } } diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go index e45621ecb..a7d8f919c 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go @@ -4,8 +4,10 @@ package applicationpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/events" payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" types "github.com/filecoin-project/mir/pkg/types" ) @@ -15,6 +17,14 @@ func NewHead(m dsl.Module, destModule types.ModuleID, headId uint64) { dsl.EmitMirEvent(m, events.NewHead(destModule, headId)) } +func VerifyBlockRequest(m dsl.Module, destModule types.ModuleID, requestId uint64, block *blockchainpb.Block) { + dsl.EmitMirEvent(m, events.VerifyBlockRequest(destModule, requestId, block)) +} + +func VerifyBlockResponse(m dsl.Module, destModule types.ModuleID, requestId uint64, ok bool) { + dsl.EmitMirEvent(m, events.VerifyBlockResponse(destModule, requestId, ok)) +} + func PayloadRequest(m dsl.Module, destModule types.ModuleID, headId uint64) { dsl.EmitMirEvent(m, events.PayloadRequest(destModule, headId)) } @@ -22,3 +32,7 @@ func PayloadRequest(m dsl.Module, destModule types.ModuleID, headId uint64) { func PayloadResponse(m dsl.Module, destModule types.ModuleID, headId uint64, payload *payloadpb.Payload) { dsl.EmitMirEvent(m, events.PayloadResponse(destModule, headId, payload)) } + +func ForkUpdate(m dsl.Module, destModule types.ModuleID, removedChain *blockchainpb.Blockchain, addedChain *blockchainpb.Blockchain, forkState *statepb.State) { + dsl.EmitMirEvent(m, events.ForkUpdate(destModule, removedChain, addedChain, forkState)) +} diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go index b02555b6a..6411ff716 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go @@ -4,8 +4,10 @@ package applicationpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" ) @@ -28,6 +30,18 @@ func UponNewHead(m dsl.Module, handler func(headId uint64) error) { }) } +func UponVerifyBlockRequest(m dsl.Module, handler func(requestId uint64, block *blockchainpb.Block) error) { + UponEvent[*types.Event_VerifyBlockRequest](m, func(ev *types.VerifyBlockRequest) error { + return handler(ev.RequestId, ev.Block) + }) +} + +func UponVerifyBlockResponse(m dsl.Module, handler func(requestId uint64, ok bool) error) { + UponEvent[*types.Event_VerifyBlockResponse](m, func(ev *types.VerifyBlockResponse) error { + return handler(ev.RequestId, ev.Ok) + }) +} + func UponPayloadRequest(m dsl.Module, handler func(headId uint64) error) { UponEvent[*types.Event_PayloadRequest](m, func(ev *types.PayloadRequest) error { return handler(ev.HeadId) @@ -39,3 +53,9 @@ func UponPayloadResponse(m dsl.Module, handler func(headId uint64, payload *payl return handler(ev.HeadId, ev.Payload) }) } + +func UponForkUpdate(m dsl.Module, handler func(removedChain *blockchainpb.Blockchain, addedChain *blockchainpb.Blockchain, forkState *statepb.State) error) { + UponEvent[*types.Event_ForkUpdate](m, func(ev *types.ForkUpdate) error { + return handler(ev.RemovedChain, ev.AddedChain, ev.ForkState) + }) +} diff --git a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go index 5cf745842..2a8b01ee1 100644 --- a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go @@ -3,8 +3,10 @@ package applicationpbevents import ( + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) @@ -24,6 +26,38 @@ func NewHead(destModule types.ModuleID, headId uint64) *types1.Event { } } +func VerifyBlockRequest(destModule types.ModuleID, requestId uint64, block *blockchainpb.Block) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Application{ + Application: &types2.Event{ + Type: &types2.Event_VerifyBlockRequest{ + VerifyBlockRequest: &types2.VerifyBlockRequest{ + RequestId: requestId, + Block: block, + }, + }, + }, + }, + } +} + +func VerifyBlockResponse(destModule types.ModuleID, requestId uint64, ok bool) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Application{ + Application: &types2.Event{ + Type: &types2.Event_VerifyBlockResponse{ + VerifyBlockResponse: &types2.VerifyBlockResponse{ + RequestId: requestId, + Ok: ok, + }, + }, + }, + }, + } +} + func PayloadRequest(destModule types.ModuleID, headId uint64) *types1.Event { return &types1.Event{ DestModule: destModule, @@ -54,3 +88,20 @@ func PayloadResponse(destModule types.ModuleID, headId uint64, payload *payloadp }, } } + +func ForkUpdate(destModule types.ModuleID, removedChain *blockchainpb.Blockchain, addedChain *blockchainpb.Blockchain, forkState *statepb.State) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Application{ + Application: &types2.Event{ + Type: &types2.Event_ForkUpdate{ + ForkUpdate: &types2.ForkUpdate{ + RemovedChain: removedChain, + AddedChain: addedChain, + ForkState: forkState, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go index 28b930c6c..b6f4cb63c 100644 --- a/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go @@ -13,6 +13,14 @@ func (w *Event_NewHead) Unwrap() *NewHead { return w.NewHead } +func (w *Event_VerifyBlockRequest) Unwrap() *VerifyBlockRequest { + return w.VerifyBlockRequest +} + +func (w *Event_VerifyBlockResponse) Unwrap() *VerifyBlockResponse { + return w.VerifyBlockResponse +} + func (w *Event_PayloadRequest) Unwrap() *PayloadRequest { return w.PayloadRequest } @@ -20,3 +28,7 @@ func (w *Event_PayloadRequest) Unwrap() *PayloadRequest { func (w *Event_PayloadResponse) Unwrap() *PayloadResponse { return w.PayloadResponse } + +func (w *Event_ForkUpdate) Unwrap() *ForkUpdate { + return w.ForkUpdate +} diff --git a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go index de90dd96d..c4f463bef 100644 --- a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go @@ -4,8 +4,10 @@ package applicationpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" applicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -31,10 +33,16 @@ func Event_TypeFromPb(pb applicationpb.Event_Type) Event_Type { switch pb := pb.(type) { case *applicationpb.Event_NewHead: return &Event_NewHead{NewHead: NewHeadFromPb(pb.NewHead)} + case *applicationpb.Event_VerifyBlockRequest: + return &Event_VerifyBlockRequest{VerifyBlockRequest: VerifyBlockRequestFromPb(pb.VerifyBlockRequest)} + case *applicationpb.Event_VerifyBlockResponse: + return &Event_VerifyBlockResponse{VerifyBlockResponse: VerifyBlockResponseFromPb(pb.VerifyBlockResponse)} case *applicationpb.Event_PayloadRequest: return &Event_PayloadRequest{PayloadRequest: PayloadRequestFromPb(pb.PayloadRequest)} case *applicationpb.Event_PayloadResponse: return &Event_PayloadResponse{PayloadResponse: PayloadResponseFromPb(pb.PayloadResponse)} + case *applicationpb.Event_ForkUpdate: + return &Event_ForkUpdate{ForkUpdate: ForkUpdateFromPb(pb.ForkUpdate)} } return nil } @@ -63,6 +71,54 @@ func (*Event_NewHead) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_NewHead]()} } +type Event_VerifyBlockRequest struct { + VerifyBlockRequest *VerifyBlockRequest +} + +func (*Event_VerifyBlockRequest) isEvent_Type() {} + +func (w *Event_VerifyBlockRequest) Unwrap() *VerifyBlockRequest { + return w.VerifyBlockRequest +} + +func (w *Event_VerifyBlockRequest) Pb() applicationpb.Event_Type { + if w == nil { + return nil + } + if w.VerifyBlockRequest == nil { + return &applicationpb.Event_VerifyBlockRequest{} + } + return &applicationpb.Event_VerifyBlockRequest{VerifyBlockRequest: (w.VerifyBlockRequest).Pb()} +} + +func (*Event_VerifyBlockRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_VerifyBlockRequest]()} +} + +type Event_VerifyBlockResponse struct { + VerifyBlockResponse *VerifyBlockResponse +} + +func (*Event_VerifyBlockResponse) isEvent_Type() {} + +func (w *Event_VerifyBlockResponse) Unwrap() *VerifyBlockResponse { + return w.VerifyBlockResponse +} + +func (w *Event_VerifyBlockResponse) Pb() applicationpb.Event_Type { + if w == nil { + return nil + } + if w.VerifyBlockResponse == nil { + return &applicationpb.Event_VerifyBlockResponse{} + } + return &applicationpb.Event_VerifyBlockResponse{VerifyBlockResponse: (w.VerifyBlockResponse).Pb()} +} + +func (*Event_VerifyBlockResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_VerifyBlockResponse]()} +} + type Event_PayloadRequest struct { PayloadRequest *PayloadRequest } @@ -111,6 +167,30 @@ func (*Event_PayloadResponse) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_PayloadResponse]()} } +type Event_ForkUpdate struct { + ForkUpdate *ForkUpdate +} + +func (*Event_ForkUpdate) isEvent_Type() {} + +func (w *Event_ForkUpdate) Unwrap() *ForkUpdate { + return w.ForkUpdate +} + +func (w *Event_ForkUpdate) Pb() applicationpb.Event_Type { + if w == nil { + return nil + } + if w.ForkUpdate == nil { + return &applicationpb.Event_ForkUpdate{} + } + return &applicationpb.Event_ForkUpdate{ForkUpdate: (w.ForkUpdate).Pb()} +} + +func (*Event_ForkUpdate) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_ForkUpdate]()} +} + func EventFromPb(pb *applicationpb.Event) *Event { if pb == nil { return nil @@ -167,6 +247,113 @@ func (*NewHead) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.NewHead]()} } +type VerifyBlockRequest struct { + RequestId uint64 + Block *blockchainpb.Block +} + +func VerifyBlockRequestFromPb(pb *applicationpb.VerifyBlockRequest) *VerifyBlockRequest { + if pb == nil { + return nil + } + return &VerifyBlockRequest{ + RequestId: pb.RequestId, + Block: pb.Block, + } +} + +func (m *VerifyBlockRequest) Pb() *applicationpb.VerifyBlockRequest { + if m == nil { + return nil + } + pbMessage := &applicationpb.VerifyBlockRequest{} + { + pbMessage.RequestId = m.RequestId + if m.Block != nil { + pbMessage.Block = m.Block + } + } + + return pbMessage +} + +func (*VerifyBlockRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.VerifyBlockRequest]()} +} + +type VerifyBlockResponse struct { + RequestId uint64 + Ok bool +} + +func VerifyBlockResponseFromPb(pb *applicationpb.VerifyBlockResponse) *VerifyBlockResponse { + if pb == nil { + return nil + } + return &VerifyBlockResponse{ + RequestId: pb.RequestId, + Ok: pb.Ok, + } +} + +func (m *VerifyBlockResponse) Pb() *applicationpb.VerifyBlockResponse { + if m == nil { + return nil + } + pbMessage := &applicationpb.VerifyBlockResponse{} + { + pbMessage.RequestId = m.RequestId + pbMessage.Ok = m.Ok + } + + return pbMessage +} + +func (*VerifyBlockResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.VerifyBlockResponse]()} +} + +type ForkUpdate struct { + RemovedChain *blockchainpb.Blockchain + AddedChain *blockchainpb.Blockchain + ForkState *statepb.State +} + +func ForkUpdateFromPb(pb *applicationpb.ForkUpdate) *ForkUpdate { + if pb == nil { + return nil + } + return &ForkUpdate{ + RemovedChain: pb.RemovedChain, + AddedChain: pb.AddedChain, + ForkState: pb.ForkState, + } +} + +func (m *ForkUpdate) Pb() *applicationpb.ForkUpdate { + if m == nil { + return nil + } + pbMessage := &applicationpb.ForkUpdate{} + { + if m.RemovedChain != nil { + pbMessage.RemovedChain = m.RemovedChain + } + if m.AddedChain != nil { + pbMessage.AddedChain = m.AddedChain + } + if m.ForkState != nil { + pbMessage.ForkState = m.ForkState + } + } + + return pbMessage +} + +func (*ForkUpdate) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.ForkUpdate]()} +} + type PayloadRequest struct { HeadId uint64 } diff --git a/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go index fb6cc6881..e0d7a51f0 100644 --- a/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go @@ -6,6 +6,7 @@ import ( dsl "github.com/filecoin-project/mir/pkg/dsl" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/events" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" types "github.com/filecoin-project/mir/pkg/types" ) @@ -19,6 +20,6 @@ func NewOrphan(m dsl.Module, destModule types.ModuleID, orphan *blockchainpb.Blo dsl.EmitMirEvent(m, events.NewOrphan(destModule, orphan)) } -func AppUpdate(m dsl.Module, destModule types.ModuleID, state int64) { +func AppUpdate(m dsl.Module, destModule types.ModuleID, state *statepb.State) { dsl.EmitMirEvent(m, events.AppUpdate(destModule, state)) } diff --git a/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go index 856125510..a722d0d69 100644 --- a/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go @@ -6,6 +6,7 @@ import ( dsl "github.com/filecoin-project/mir/pkg/dsl" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" ) @@ -34,7 +35,7 @@ func UponNewOrphan(m dsl.Module, handler func(orphan *blockchainpb.Block) error) }) } -func UponAppUpdate(m dsl.Module, handler func(state int64) error) { +func UponAppUpdate(m dsl.Module, handler func(state *statepb.State) error) { UponEvent[*types.Event_AppUpdate](m, func(ev *types.AppUpdate) error { return handler(ev.State) }) diff --git a/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go b/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go index 84a51075a..2b904bc8e 100644 --- a/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go @@ -5,6 +5,7 @@ package interceptorpbevents import ( blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) @@ -40,7 +41,7 @@ func NewOrphan(destModule types.ModuleID, orphan *blockchainpb.Block) *types1.Ev } } -func AppUpdate(destModule types.ModuleID, state int64) *types1.Event { +func AppUpdate(destModule types.ModuleID, state *statepb.State) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Bcinterceptor{ diff --git a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go index 5b88faed7..5c11ae09d 100644 --- a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go +++ b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go @@ -8,6 +8,7 @@ package interceptorpb import ( blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -224,7 +225,7 @@ type AppUpdate struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - State int64 `protobuf:"varint,1,opt,name=state,proto3" json:"state,omitempty"` + State *statepb.State `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` } func (x *AppUpdate) Reset() { @@ -259,11 +260,11 @@ func (*AppUpdate) Descriptor() ([]byte, []int) { return file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescGZIP(), []int{3} } -func (x *AppUpdate) GetState() int64 { +func (x *AppUpdate) GetState() *statepb.State { if x != nil { return x.State } - return 0 + return nil } var File_blockchainpb_interceptorpb_interceptorpb_proto protoreflect.FileDescriptor @@ -275,38 +276,42 @@ var file_blockchainpb_interceptorpb_interceptorpb_proto_rawDesc = []byte{ 0x12, 0x0d, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, - 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xcf, - 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0b, 0x74, 0x72, 0x65, 0x65, - 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, - 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x72, - 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x74, 0x72, 0x65, 0x65, - 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x6e, 0x65, 0x77, 0x5f, 0x6f, 0x72, - 0x70, 0x68, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x4f, 0x72, - 0x70, 0x68, 0x61, 0x6e, 0x48, 0x00, 0x52, 0x09, 0x6e, 0x65, 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, - 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x61, 0x70, 0x70, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, - 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, - 0x00, 0x52, 0x09, 0x61, 0x70, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x90, 0xa6, - 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, - 0x22, 0x58, 0x0a, 0x0a, 0x54, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2b, - 0x0a, 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x74, 0x72, 0x65, 0x65, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, - 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, - 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3e, 0x0a, 0x09, 0x4e, 0x65, - 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x6f, 0x72, 0x70, 0x68, 0x61, - 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x6f, 0x72, - 0x70, 0x68, 0x61, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x27, 0x0a, 0x09, 0x41, 0x70, - 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, - 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x1a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, + 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0xcf, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0b, + 0x74, 0x72, 0x65, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x19, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, + 0x62, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0a, + 0x74, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x6e, 0x65, + 0x77, 0x5f, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, + 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x4e, + 0x65, 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x48, 0x00, 0x52, 0x09, 0x6e, 0x65, 0x77, 0x4f, + 0x72, 0x70, 0x68, 0x61, 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x61, 0x70, 0x70, 0x5f, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x09, 0x61, 0x70, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, + 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x58, 0x0a, 0x0a, 0x54, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x12, + 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3e, + 0x0a, 0x09, 0x4e, 0x65, 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x6f, + 0x72, 0x70, 0x68, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x06, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x37, + 0x0a, 0x09, 0x41, 0x70, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x05, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, + 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -329,6 +334,7 @@ var file_blockchainpb_interceptorpb_interceptorpb_proto_goTypes = []interface{}{ (*AppUpdate)(nil), // 3: interceptorpb.AppUpdate (*blockchainpb.Blocktree)(nil), // 4: blockchainpb.Blocktree (*blockchainpb.Block)(nil), // 5: blockchainpb.Block + (*statepb.State)(nil), // 6: statepb.State } var file_blockchainpb_interceptorpb_interceptorpb_proto_depIdxs = []int32{ 1, // 0: interceptorpb.Event.tree_update:type_name -> interceptorpb.TreeUpdate @@ -336,11 +342,12 @@ var file_blockchainpb_interceptorpb_interceptorpb_proto_depIdxs = []int32{ 3, // 2: interceptorpb.Event.app_update:type_name -> interceptorpb.AppUpdate 4, // 3: interceptorpb.TreeUpdate.tree:type_name -> blockchainpb.Blocktree 5, // 4: interceptorpb.NewOrphan.orphan:type_name -> blockchainpb.Block - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] 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 + 6, // 5: interceptorpb.AppUpdate.state:type_name -> statepb.State + 6, // [6:6] is the sub-list for method output_type + 6, // [6:6] is the sub-list for method input_type + 6, // [6:6] is the sub-list for extension type_name + 6, // [6:6] is the sub-list for extension extendee + 0, // [0:6] is the sub-list for field type_name } func init() { file_blockchainpb_interceptorpb_interceptorpb_proto_init() } diff --git a/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go b/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go index a19db5818..9d5d1d6ba 100644 --- a/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go @@ -6,6 +6,7 @@ import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -204,7 +205,7 @@ func (*NewOrphan) MirReflect() mirreflect.Type { } type AppUpdate struct { - State int64 + State *statepb.State } func AppUpdateFromPb(pb *interceptorpb.AppUpdate) *AppUpdate { @@ -222,7 +223,9 @@ func (m *AppUpdate) Pb() *interceptorpb.AppUpdate { } pbMessage := &interceptorpb.AppUpdate{} { - pbMessage.State = m.State + if m.State != nil { + pbMessage.State = m.State + } } return pbMessage diff --git a/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go b/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go index aeb72e2a8..a83a00034 100644 --- a/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go +++ b/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go @@ -26,7 +26,8 @@ type Payload struct { unknownFields protoimpl.UnknownFields // application specific payload - AddMinus int64 `protobuf:"varint,1,opt,name=add_minus,json=addMinus,proto3" json:"add_minus,omitempty"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (x *Payload) Reset() { @@ -61,9 +62,16 @@ func (*Payload) Descriptor() ([]byte, []int) { return file_blockchainpb_payloadpb_payloadpb_proto_rawDescGZIP(), []int{0} } -func (x *Payload) GetAddMinus() int64 { +func (x *Payload) GetMessage() string { if x != nil { - return x.AddMinus + return x.Message + } + return "" +} + +func (x *Payload) GetTimestamp() int64 { + if x != nil { + return x.Timestamp } return 0 } @@ -74,14 +82,15 @@ var file_blockchainpb_payloadpb_payloadpb_proto_rawDesc = []byte{ 0x0a, 0x26, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x70, 0x62, 0x22, 0x26, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1b, - 0x0a, 0x09, 0x61, 0x64, 0x64, 0x5f, 0x6d, 0x69, 0x6e, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x03, 0x52, 0x08, 0x61, 0x64, 0x64, 0x4d, 0x69, 0x6e, 0x75, 0x73, 0x42, 0x3f, 0x5a, 0x3d, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, - 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x70, 0x62, 0x22, 0x41, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, + 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/pb/blockchainpb/statepb/statepb.pb.go b/pkg/pb/blockchainpb/statepb/statepb.pb.go index 6f2ac176e..ed79fb683 100644 --- a/pkg/pb/blockchainpb/statepb/statepb.pb.go +++ b/pkg/pb/blockchainpb/statepb/statepb.pb.go @@ -26,7 +26,7 @@ type State struct { unknownFields protoimpl.UnknownFields // application specific state - Counter int64 `protobuf:"varint,1,opt,name=counter,proto3" json:"counter,omitempty"` + MessageHistory []string `protobuf:"bytes,1,rep,name=message_history,json=messageHistory,proto3" json:"message_history,omitempty"` } func (x *State) Reset() { @@ -61,11 +61,11 @@ func (*State) Descriptor() ([]byte, []int) { return file_blockchainpb_statepb_statepb_proto_rawDescGZIP(), []int{0} } -func (x *State) GetCounter() int64 { +func (x *State) GetMessageHistory() []string { if x != nil { - return x.Counter + return x.MessageHistory } - return 0 + return nil } var File_blockchainpb_statepb_statepb_proto protoreflect.FileDescriptor @@ -73,14 +73,15 @@ var File_blockchainpb_statepb_statepb_proto protoreflect.FileDescriptor var file_blockchainpb_statepb_statepb_proto_rawDesc = []byte{ 0x0a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x22, 0x21, 0x0a, - 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, - 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x03, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, - 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, - 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, - 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x22, 0x30, 0x0a, + 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x42, + 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, + 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, + 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x62, 0x06, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go deleted file mode 100644 index aacc52b41..000000000 --- a/pkg/pb/blockchainpb/tpmpb/dsl/emit.mir.go +++ /dev/null @@ -1,15 +0,0 @@ -// Code generated by Mir codegen. DO NOT EDIT. - -package tpmpbdsl - -import ( - dsl "github.com/filecoin-project/mir/pkg/dsl" - events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/events" - types "github.com/filecoin-project/mir/pkg/types" -) - -// Module-specific dsl functions for emitting events. - -func NewBlockRequest(m dsl.Module, destModule types.ModuleID, headId uint64) { - dsl.EmitMirEvent(m, events.NewBlockRequest(destModule, headId)) -} diff --git a/pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go deleted file mode 100644 index e048f603c..000000000 --- a/pkg/pb/blockchainpb/tpmpb/dsl/upon.mir.go +++ /dev/null @@ -1,28 +0,0 @@ -// Code generated by Mir codegen. DO NOT EDIT. - -package tpmpbdsl - -import ( - dsl "github.com/filecoin-project/mir/pkg/dsl" - types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/types" - types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" -) - -// Module-specific dsl functions for processing events. - -func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func(ev *Ev) error) { - dsl.UponMirEvent[*types1.Event_Tpm](m, func(ev *types.Event) error { - w, ok := ev.Type.(W) - if !ok { - return nil - } - - return handler(w.Unwrap()) - }) -} - -func UponNewBlockRequest(m dsl.Module, handler func(headId uint64) error) { - UponEvent[*types.Event_NewBlockRequest](m, func(ev *types.NewBlockRequest) error { - return handler(ev.HeadId) - }) -} diff --git a/pkg/pb/blockchainpb/tpmpb/events/events.mir.go b/pkg/pb/blockchainpb/tpmpb/events/events.mir.go deleted file mode 100644 index 36abbfffa..000000000 --- a/pkg/pb/blockchainpb/tpmpb/events/events.mir.go +++ /dev/null @@ -1,24 +0,0 @@ -// Code generated by Mir codegen. DO NOT EDIT. - -package tpmpbevents - -import ( - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/types" - types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" - types "github.com/filecoin-project/mir/pkg/types" -) - -func NewBlockRequest(destModule types.ModuleID, headId uint64) *types1.Event { - return &types1.Event{ - DestModule: destModule, - Type: &types1.Event_Tpm{ - Tpm: &types2.Event{ - Type: &types2.Event_NewBlockRequest{ - NewBlockRequest: &types2.NewBlockRequest{ - HeadId: headId, - }, - }, - }, - }, - } -} diff --git a/pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go deleted file mode 100644 index 234641010..000000000 --- a/pkg/pb/blockchainpb/tpmpb/oneof_interfaces.mir.go +++ /dev/null @@ -1,14 +0,0 @@ -// Code generated by Mir codegen. DO NOT EDIT. - -package tpmpb - -type Event_Type = isEvent_Type - -type Event_TypeWrapper[T any] interface { - Event_Type - Unwrap() *T -} - -func (w *Event_NewBlockRequest) Unwrap() *NewBlockRequest { - return w.NewBlockRequest -} diff --git a/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go b/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go deleted file mode 100644 index 43d549f3a..000000000 --- a/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.go +++ /dev/null @@ -1,240 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// versions: -// protoc-gen-go v1.31.0 -// protoc v4.24.4 -// source: blockchainpb/tpmpb/tpmpb.proto - -package tpmpb - -import ( - _ "github.com/filecoin-project/mir/pkg/pb/mir" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" - reflect "reflect" - sync "sync" -) - -const ( - // Verify that this generated code is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) - // Verify that runtime/protoimpl is sufficiently up-to-date. - _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) -) - -type Event struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // Types that are assignable to Type: - // - // *Event_NewBlockRequest - Type isEvent_Type `protobuf_oneof:"type"` -} - -func (x *Event) Reset() { - *x = Event{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Event) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Event) ProtoMessage() {} - -func (x *Event) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[0] - 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 Event.ProtoReflect.Descriptor instead. -func (*Event) Descriptor() ([]byte, []int) { - return file_blockchainpb_tpmpb_tpmpb_proto_rawDescGZIP(), []int{0} -} - -func (m *Event) GetType() isEvent_Type { - if m != nil { - return m.Type - } - return nil -} - -func (x *Event) GetNewBlockRequest() *NewBlockRequest { - if x, ok := x.GetType().(*Event_NewBlockRequest); ok { - return x.NewBlockRequest - } - return nil -} - -type isEvent_Type interface { - isEvent_Type() -} - -type Event_NewBlockRequest struct { - NewBlockRequest *NewBlockRequest `protobuf:"bytes,1,opt,name=new_block_request,json=newBlockRequest,proto3,oneof"` -} - -func (*Event_NewBlockRequest) isEvent_Type() {} - -type NewBlockRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - // blockchainpb.Blockchain chain = 1; - HeadId uint64 `protobuf:"varint,1,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` -} - -func (x *NewBlockRequest) Reset() { - *x = NewBlockRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *NewBlockRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NewBlockRequest) ProtoMessage() {} - -func (x *NewBlockRequest) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[1] - 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 NewBlockRequest.ProtoReflect.Descriptor instead. -func (*NewBlockRequest) Descriptor() ([]byte, []int) { - return file_blockchainpb_tpmpb_tpmpb_proto_rawDescGZIP(), []int{1} -} - -func (x *NewBlockRequest) GetHeadId() uint64 { - if x != nil { - return x.HeadId - } - return 0 -} - -var File_blockchainpb_tpmpb_tpmpb_proto protoreflect.FileDescriptor - -var file_blockchainpb_tpmpb_tpmpb_proto_rawDesc = []byte{ - 0x0a, 0x1e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x74, - 0x70, 0x6d, 0x70, 0x62, 0x2f, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x12, 0x05, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, - 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x61, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x44, - 0x0a, 0x11, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x74, 0x70, 0x6d, 0x70, - 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x48, 0x00, 0x52, 0x0f, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x30, 0x0a, 0x0f, 0x4e, 0x65, 0x77, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, - 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, - 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, - 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, - 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, - 0x62, 0x2f, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} - -var ( - file_blockchainpb_tpmpb_tpmpb_proto_rawDescOnce sync.Once - file_blockchainpb_tpmpb_tpmpb_proto_rawDescData = file_blockchainpb_tpmpb_tpmpb_proto_rawDesc -) - -func file_blockchainpb_tpmpb_tpmpb_proto_rawDescGZIP() []byte { - file_blockchainpb_tpmpb_tpmpb_proto_rawDescOnce.Do(func() { - file_blockchainpb_tpmpb_tpmpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_tpmpb_tpmpb_proto_rawDescData) - }) - return file_blockchainpb_tpmpb_tpmpb_proto_rawDescData -} - -var file_blockchainpb_tpmpb_tpmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 2) -var file_blockchainpb_tpmpb_tpmpb_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: tpmpb.Event - (*NewBlockRequest)(nil), // 1: tpmpb.NewBlockRequest -} -var file_blockchainpb_tpmpb_tpmpb_proto_depIdxs = []int32{ - 1, // 0: tpmpb.Event.new_block_request:type_name -> tpmpb.NewBlockRequest - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name -} - -func init() { file_blockchainpb_tpmpb_tpmpb_proto_init() } -func file_blockchainpb_tpmpb_tpmpb_proto_init() { - if File_blockchainpb_tpmpb_tpmpb_proto != nil { - return - } - if !protoimpl.UnsafeEnabled { - file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Event); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NewBlockRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - } - file_blockchainpb_tpmpb_tpmpb_proto_msgTypes[0].OneofWrappers = []interface{}{ - (*Event_NewBlockRequest)(nil), - } - type x struct{} - out := protoimpl.TypeBuilder{ - File: protoimpl.DescBuilder{ - GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_blockchainpb_tpmpb_tpmpb_proto_rawDesc, - NumEnums: 0, - NumMessages: 2, - NumExtensions: 0, - NumServices: 0, - }, - GoTypes: file_blockchainpb_tpmpb_tpmpb_proto_goTypes, - DependencyIndexes: file_blockchainpb_tpmpb_tpmpb_proto_depIdxs, - MessageInfos: file_blockchainpb_tpmpb_tpmpb_proto_msgTypes, - }.Build() - File_blockchainpb_tpmpb_tpmpb_proto = out.File - file_blockchainpb_tpmpb_tpmpb_proto_rawDesc = nil - file_blockchainpb_tpmpb_tpmpb_proto_goTypes = nil - file_blockchainpb_tpmpb_tpmpb_proto_depIdxs = nil -} diff --git a/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go b/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go deleted file mode 100644 index 91ebfb31f..000000000 --- a/pkg/pb/blockchainpb/tpmpb/tpmpb.pb.mir.go +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by Mir codegen. DO NOT EDIT. - -package tpmpb - -import ( - reflect "reflect" -) - -func (*Event) ReflectTypeOptions() []reflect.Type { - return []reflect.Type{ - reflect.TypeOf((*Event_NewBlockRequest)(nil)), - } -} diff --git a/pkg/pb/blockchainpb/tpmpb/types/types.mir.go b/pkg/pb/blockchainpb/tpmpb/types/types.mir.go deleted file mode 100644 index 95dd7efbd..000000000 --- a/pkg/pb/blockchainpb/tpmpb/types/types.mir.go +++ /dev/null @@ -1,115 +0,0 @@ -// Code generated by Mir codegen. DO NOT EDIT. - -package tpmpbtypes - -import ( - mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" - reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" -) - -type Event struct { - Type Event_Type -} - -type Event_Type interface { - mirreflect.GeneratedType - isEvent_Type() - Pb() tpmpb.Event_Type -} - -type Event_TypeWrapper[T any] interface { - Event_Type - Unwrap() *T -} - -func Event_TypeFromPb(pb tpmpb.Event_Type) Event_Type { - if pb == nil { - return nil - } - switch pb := pb.(type) { - case *tpmpb.Event_NewBlockRequest: - return &Event_NewBlockRequest{NewBlockRequest: NewBlockRequestFromPb(pb.NewBlockRequest)} - } - return nil -} - -type Event_NewBlockRequest struct { - NewBlockRequest *NewBlockRequest -} - -func (*Event_NewBlockRequest) isEvent_Type() {} - -func (w *Event_NewBlockRequest) Unwrap() *NewBlockRequest { - return w.NewBlockRequest -} - -func (w *Event_NewBlockRequest) Pb() tpmpb.Event_Type { - if w == nil { - return nil - } - if w.NewBlockRequest == nil { - return &tpmpb.Event_NewBlockRequest{} - } - return &tpmpb.Event_NewBlockRequest{NewBlockRequest: (w.NewBlockRequest).Pb()} -} - -func (*Event_NewBlockRequest) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*tpmpb.Event_NewBlockRequest]()} -} - -func EventFromPb(pb *tpmpb.Event) *Event { - if pb == nil { - return nil - } - return &Event{ - Type: Event_TypeFromPb(pb.Type), - } -} - -func (m *Event) Pb() *tpmpb.Event { - if m == nil { - return nil - } - pbMessage := &tpmpb.Event{} - { - if m.Type != nil { - pbMessage.Type = (m.Type).Pb() - } - } - - return pbMessage -} - -func (*Event) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*tpmpb.Event]()} -} - -type NewBlockRequest struct { - HeadId uint64 -} - -func NewBlockRequestFromPb(pb *tpmpb.NewBlockRequest) *NewBlockRequest { - if pb == nil { - return nil - } - return &NewBlockRequest{ - HeadId: pb.HeadId, - } -} - -func (m *NewBlockRequest) Pb() *tpmpb.NewBlockRequest { - if m == nil { - return nil - } - pbMessage := &tpmpb.NewBlockRequest{} - { - pbMessage.HeadId = m.HeadId - } - - return pbMessage -} - -func (*NewBlockRequest) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*tpmpb.NewBlockRequest]()} -} diff --git a/pkg/pb/eventpb/eventpb.pb.go b/pkg/pb/eventpb/eventpb.pb.go index e0aa68824..03954714f 100644 --- a/pkg/pb/eventpb/eventpb.pb.go +++ b/pkg/pb/eventpb/eventpb.pb.go @@ -18,7 +18,6 @@ import ( interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" - tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" chkpvalidatorpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb/chkpvalidatorpb" cryptopb "github.com/filecoin-project/mir/pkg/pb/cryptopb" @@ -77,7 +76,6 @@ type Event struct { // *Event_PingPong // *Event_Bcm // *Event_Miner - // *Event_Tpm // *Event_Communication // *Event_Synchronizer // *Event_Application @@ -289,13 +287,6 @@ func (x *Event) GetMiner() *minerpb.Event { return nil } -func (x *Event) GetTpm() *tpmpb.Event { - if x, ok := x.GetType().(*Event_Tpm); ok { - return x.Tpm - } - return nil -} - func (x *Event) GetCommunication() *communicationpb.Event { if x, ok := x.GetType().(*Event_Communication); ok { return x.Communication @@ -445,20 +436,16 @@ type Event_Miner struct { Miner *minerpb.Event `protobuf:"bytes,202,opt,name=miner,proto3,oneof"` } -type Event_Tpm struct { - Tpm *tpmpb.Event `protobuf:"bytes,203,opt,name=tpm,proto3,oneof"` -} - type Event_Communication struct { - Communication *communicationpb.Event `protobuf:"bytes,204,opt,name=communication,proto3,oneof"` + Communication *communicationpb.Event `protobuf:"bytes,203,opt,name=communication,proto3,oneof"` } type Event_Synchronizer struct { - Synchronizer *synchronizerpb.Event `protobuf:"bytes,205,opt,name=synchronizer,proto3,oneof"` + Synchronizer *synchronizerpb.Event `protobuf:"bytes,204,opt,name=synchronizer,proto3,oneof"` } type Event_Application struct { - Application *applicationpb.Event `protobuf:"bytes,206,opt,name=application,proto3,oneof"` + Application *applicationpb.Event `protobuf:"bytes,205,opt,name=application,proto3,oneof"` } type Event_Bcinterceptor struct { @@ -521,8 +508,6 @@ func (*Event_Bcm) isEvent_Type() {} func (*Event_Miner) isEvent_Type() {} -func (*Event_Tpm) isEvent_Type() {} - func (*Event_Communication) isEvent_Type() {} func (*Event_Synchronizer) isEvent_Type() {} @@ -884,8 +869,6 @@ var file_eventpb_eventpb_proto_rawDesc = []byte{ 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x2f, 0x74, 0x70, 0x6d, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x32, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, @@ -900,7 +883,7 @@ var file_eventpb_eventpb_proto_rawDesc = []byte{ 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe1, 0x0c, 0x0a, 0x05, 0x45, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbe, 0x0c, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, @@ -969,93 +952,91 @@ var file_eventpb_eventpb_proto_rawDesc = []byte{ 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, 0x6d, 0x12, 0x27, 0x0a, 0x05, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x03, - 0x74, 0x70, 0x6d, 0x18, 0xcb, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x74, 0x70, 0x6d, - 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x74, 0x70, 0x6d, 0x12, - 0x3f, 0x0a, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0xcc, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, - 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, - 0x00, 0x52, 0x0d, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x12, 0x3c, 0x0a, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, - 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, - 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, - 0x52, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x12, 0x39, - 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xce, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x70, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x0d, 0x62, 0x63, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x18, 0xd2, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, - 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x63, 0x69, 0x6e, 0x74, - 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x45, 0x0a, 0x0d, 0x74, 0x65, 0x73, 0x74, - 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0xad, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, - 0x52, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, - 0x41, 0x0a, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x18, 0xae, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, - 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, - 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0xaf, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, - 0x65, 0x73, 0x74, 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, - 0x29, 0x0a, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x18, 0x90, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x04, - 0x90, 0xa6, 0x1d, 0x01, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x88, 0xa6, 0x1d, 0x01, - 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x0c, - 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xc6, 0x01, 0x0a, - 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x64, - 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x48, - 0x00, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2e, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, - 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, - 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x48, 0x00, - 0x52, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x47, 0x0a, 0x0f, 0x67, 0x61, 0x72, 0x62, - 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x48, - 0x00, 0x52, 0x0e, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, - 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, - 0x65, 0x6c, 0x61, 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, - 0x6f, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0d, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x52, 0x0a, 0x05, - 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, - 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, - 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, - 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, 0x54, 0x69, 0x6d, 0x65, 0x72, - 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x38, 0x0a, 0x10, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, - 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, - 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, - 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, - 0x65, 0x6c, 0x61, 0x79, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, - 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, - 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, - 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, - 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, - 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x8a, 0x01, 0x0a, 0x13, 0x54, 0x69, - 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, - 0x74, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0d, + 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xcb, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, + 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, + 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x18, 0xcc, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, + 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x73, + 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x0b, 0x61, + 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, + 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x0d, 0x62, 0x63, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x18, 0xd2, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, + 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x63, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, + 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x45, 0x0a, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0xad, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x74, + 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, + 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x18, 0xae, 0x02, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, + 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x12, + 0x2b, 0x0a, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0xaf, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x73, 0x74, + 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x04, + 0x6e, 0x65, 0x78, 0x74, 0x18, 0x90, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x04, 0x90, 0xa6, 0x1d, + 0x01, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x88, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x0c, 0x0a, 0x04, 0x49, + 0x6e, 0x69, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xc6, 0x01, 0x0a, 0x0a, 0x54, 0x69, + 0x6d, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, 0x05, + 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2e, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, + 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, 0x72, + 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x47, 0x0a, 0x0f, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, + 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, + 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0e, + 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x3a, 0x04, + 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, + 0x1d, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, + 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x64, + 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x73, 0x54, 0x6f, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, + 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, + 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, + 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, + 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x3a, 0x04, 0x98, + 0xa6, 0x1d, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, + 0x65, 0x61, 0x74, 0x12, 0x38, 0x0a, 0x10, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, + 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, + 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x65, + 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x52, 0x0a, + 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, + 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, + 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, + 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, + 0x79, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, + 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, - 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x8a, 0x01, 0x0a, 0x13, 0x54, 0x69, 0x6d, 0x65, 0x72, + 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x12, 0x6d, + 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, + 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, + 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, + 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, + 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, + 0xa6, 0x1d, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x65, 0x76, + 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1097,14 +1078,13 @@ var file_eventpb_eventpb_proto_goTypes = []interface{}{ (*pingpongpb.Event)(nil), // 22: pingpongpb.Event (*bcmpb.Event)(nil), // 23: bcmpb.Event (*minerpb.Event)(nil), // 24: minerpb.Event - (*tpmpb.Event)(nil), // 25: tpmpb.Event - (*communicationpb.Event)(nil), // 26: communicationpb.Event - (*synchronizerpb.Event)(nil), // 27: synchronizerpb.Event - (*applicationpb.Event)(nil), // 28: applicationpb.Event - (*interceptorpb.Event)(nil), // 29: interceptorpb.Event - (*wrapperspb.StringValue)(nil), // 30: google.protobuf.StringValue - (*wrapperspb.UInt64Value)(nil), // 31: google.protobuf.UInt64Value - (*testerpb.Tester)(nil), // 32: testerpb.Tester + (*communicationpb.Event)(nil), // 25: communicationpb.Event + (*synchronizerpb.Event)(nil), // 26: synchronizerpb.Event + (*applicationpb.Event)(nil), // 27: applicationpb.Event + (*interceptorpb.Event)(nil), // 28: interceptorpb.Event + (*wrapperspb.StringValue)(nil), // 29: google.protobuf.StringValue + (*wrapperspb.UInt64Value)(nil), // 30: google.protobuf.UInt64Value + (*testerpb.Tester)(nil), // 31: testerpb.Tester } var file_eventpb_eventpb_proto_depIdxs = []int32{ 1, // 0: eventpb.Event.init:type_name -> eventpb.Init @@ -1128,25 +1108,24 @@ var file_eventpb_eventpb_proto_depIdxs = []int32{ 22, // 18: eventpb.Event.ping_pong:type_name -> pingpongpb.Event 23, // 19: eventpb.Event.bcm:type_name -> bcmpb.Event 24, // 20: eventpb.Event.miner:type_name -> minerpb.Event - 25, // 21: eventpb.Event.tpm:type_name -> tpmpb.Event - 26, // 22: eventpb.Event.communication:type_name -> communicationpb.Event - 27, // 23: eventpb.Event.synchronizer:type_name -> synchronizerpb.Event - 28, // 24: eventpb.Event.application:type_name -> applicationpb.Event - 29, // 25: eventpb.Event.bcinterceptor:type_name -> interceptorpb.Event - 30, // 26: eventpb.Event.testingString:type_name -> google.protobuf.StringValue - 31, // 27: eventpb.Event.testingUint:type_name -> google.protobuf.UInt64Value - 32, // 28: eventpb.Event.tester:type_name -> testerpb.Tester - 0, // 29: eventpb.Event.next:type_name -> eventpb.Event - 3, // 30: eventpb.TimerEvent.delay:type_name -> eventpb.TimerDelay - 4, // 31: eventpb.TimerEvent.repeat:type_name -> eventpb.TimerRepeat - 5, // 32: eventpb.TimerEvent.garbage_collect:type_name -> eventpb.TimerGarbageCollect - 0, // 33: eventpb.TimerDelay.events_to_delay:type_name -> eventpb.Event - 0, // 34: eventpb.TimerRepeat.events_to_repeat:type_name -> eventpb.Event - 35, // [35:35] is the sub-list for method output_type - 35, // [35:35] is the sub-list for method input_type - 35, // [35:35] is the sub-list for extension type_name - 35, // [35:35] is the sub-list for extension extendee - 0, // [0:35] is the sub-list for field type_name + 25, // 21: eventpb.Event.communication:type_name -> communicationpb.Event + 26, // 22: eventpb.Event.synchronizer:type_name -> synchronizerpb.Event + 27, // 23: eventpb.Event.application:type_name -> applicationpb.Event + 28, // 24: eventpb.Event.bcinterceptor:type_name -> interceptorpb.Event + 29, // 25: eventpb.Event.testingString:type_name -> google.protobuf.StringValue + 30, // 26: eventpb.Event.testingUint:type_name -> google.protobuf.UInt64Value + 31, // 27: eventpb.Event.tester:type_name -> testerpb.Tester + 0, // 28: eventpb.Event.next:type_name -> eventpb.Event + 3, // 29: eventpb.TimerEvent.delay:type_name -> eventpb.TimerDelay + 4, // 30: eventpb.TimerEvent.repeat:type_name -> eventpb.TimerRepeat + 5, // 31: eventpb.TimerEvent.garbage_collect:type_name -> eventpb.TimerGarbageCollect + 0, // 32: eventpb.TimerDelay.events_to_delay:type_name -> eventpb.Event + 0, // 33: eventpb.TimerRepeat.events_to_repeat:type_name -> eventpb.Event + 34, // [34:34] is the sub-list for method output_type + 34, // [34:34] is the sub-list for method input_type + 34, // [34:34] is the sub-list for extension type_name + 34, // [34:34] is the sub-list for extension extendee + 0, // [0:34] is the sub-list for field type_name } func init() { file_eventpb_eventpb_proto_init() } @@ -1250,7 +1229,6 @@ func file_eventpb_eventpb_proto_init() { (*Event_PingPong)(nil), (*Event_Bcm)(nil), (*Event_Miner)(nil), - (*Event_Tpm)(nil), (*Event_Communication)(nil), (*Event_Synchronizer)(nil), (*Event_Application)(nil), diff --git a/pkg/pb/eventpb/eventpb.pb.mir.go b/pkg/pb/eventpb/eventpb.pb.mir.go index be4d2a265..b3dd73d86 100644 --- a/pkg/pb/eventpb/eventpb.pb.mir.go +++ b/pkg/pb/eventpb/eventpb.pb.mir.go @@ -29,7 +29,6 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_PingPong)(nil)), reflect.TypeOf((*Event_Bcm)(nil)), reflect.TypeOf((*Event_Miner)(nil)), - reflect.TypeOf((*Event_Tpm)(nil)), reflect.TypeOf((*Event_Communication)(nil)), reflect.TypeOf((*Event_Synchronizer)(nil)), reflect.TypeOf((*Event_Application)(nil)), diff --git a/pkg/pb/eventpb/oneof_interfaces.mir.go b/pkg/pb/eventpb/oneof_interfaces.mir.go index 18e800408..0cc24995e 100644 --- a/pkg/pb/eventpb/oneof_interfaces.mir.go +++ b/pkg/pb/eventpb/oneof_interfaces.mir.go @@ -14,7 +14,6 @@ import ( interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" - tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" chkpvalidatorpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb/chkpvalidatorpb" cryptopb "github.com/filecoin-project/mir/pkg/pb/cryptopb" @@ -122,10 +121,6 @@ func (w *Event_Miner) Unwrap() *minerpb.Event { return w.Miner } -func (w *Event_Tpm) Unwrap() *tpmpb.Event { - return w.Tpm -} - func (w *Event_Communication) Unwrap() *communicationpb.Event { return w.Communication } diff --git a/pkg/pb/eventpb/types/types.mir.go b/pkg/pb/eventpb/types/types.mir.go index 32a2cf2c7..be46f7266 100644 --- a/pkg/pb/eventpb/types/types.mir.go +++ b/pkg/pb/eventpb/types/types.mir.go @@ -4,19 +4,18 @@ package eventpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - types26 "github.com/filecoin-project/mir/codegen/model/types" + types25 "github.com/filecoin-project/mir/codegen/model/types" types13 "github.com/filecoin-project/mir/pkg/pb/apppb/types" types5 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/batchdbpb/types" types4 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/types" types6 "github.com/filecoin-project/mir/pkg/pb/batchfetcherpb/types" types2 "github.com/filecoin-project/mir/pkg/pb/bcbpb/types" - types23 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" + types22 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" types18 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" - types21 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" - types24 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" + types20 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types23 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" types19 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" - types22 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" - types20 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/types" + types21 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" types15 "github.com/filecoin-project/mir/pkg/pb/checkpointpb/chkpvalidatorpb/types" types8 "github.com/filecoin-project/mir/pkg/pb/checkpointpb/types" types12 "github.com/filecoin-project/mir/pkg/pb/cryptopb/types" @@ -28,11 +27,11 @@ import ( types16 "github.com/filecoin-project/mir/pkg/pb/ordererpb/pprepvalidatorpb/types" types11 "github.com/filecoin-project/mir/pkg/pb/ordererpb/types" types17 "github.com/filecoin-project/mir/pkg/pb/pingpongpb/types" - types25 "github.com/filecoin-project/mir/pkg/pb/testerpb/types" + types24 "github.com/filecoin-project/mir/pkg/pb/testerpb/types" types7 "github.com/filecoin-project/mir/pkg/pb/threshcryptopb/types" types14 "github.com/filecoin-project/mir/pkg/pb/transportpb/types" - types27 "github.com/filecoin-project/mir/pkg/timer/types" - types28 "github.com/filecoin-project/mir/pkg/trantor/types" + types26 "github.com/filecoin-project/mir/pkg/timer/types" + types27 "github.com/filecoin-project/mir/pkg/trantor/types" types "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" wrapperspb "google.golang.org/protobuf/types/known/wrapperspb" @@ -102,22 +101,20 @@ func Event_TypeFromPb(pb eventpb.Event_Type) Event_Type { return &Event_Bcm{Bcm: types18.EventFromPb(pb.Bcm)} case *eventpb.Event_Miner: return &Event_Miner{Miner: types19.EventFromPb(pb.Miner)} - case *eventpb.Event_Tpm: - return &Event_Tpm{Tpm: types20.EventFromPb(pb.Tpm)} case *eventpb.Event_Communication: - return &Event_Communication{Communication: types21.EventFromPb(pb.Communication)} + return &Event_Communication{Communication: types20.EventFromPb(pb.Communication)} case *eventpb.Event_Synchronizer: - return &Event_Synchronizer{Synchronizer: types22.EventFromPb(pb.Synchronizer)} + return &Event_Synchronizer{Synchronizer: types21.EventFromPb(pb.Synchronizer)} case *eventpb.Event_Application: - return &Event_Application{Application: types23.EventFromPb(pb.Application)} + return &Event_Application{Application: types22.EventFromPb(pb.Application)} case *eventpb.Event_Bcinterceptor: - return &Event_Bcinterceptor{Bcinterceptor: types24.EventFromPb(pb.Bcinterceptor)} + return &Event_Bcinterceptor{Bcinterceptor: types23.EventFromPb(pb.Bcinterceptor)} case *eventpb.Event_TestingString: return &Event_TestingString{TestingString: pb.TestingString} case *eventpb.Event_TestingUint: return &Event_TestingUint{TestingUint: pb.TestingUint} case *eventpb.Event_Tester: - return &Event_Tester{Tester: types25.TesterFromPb(pb.Tester)} + return &Event_Tester{Tester: types24.TesterFromPb(pb.Tester)} } return nil } @@ -626,37 +623,13 @@ func (*Event_Miner) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Miner]()} } -type Event_Tpm struct { - Tpm *types20.Event -} - -func (*Event_Tpm) isEvent_Type() {} - -func (w *Event_Tpm) Unwrap() *types20.Event { - return w.Tpm -} - -func (w *Event_Tpm) Pb() eventpb.Event_Type { - if w == nil { - return nil - } - if w.Tpm == nil { - return &eventpb.Event_Tpm{} - } - return &eventpb.Event_Tpm{Tpm: (w.Tpm).Pb()} -} - -func (*Event_Tpm) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Tpm]()} -} - type Event_Communication struct { - Communication *types21.Event + Communication *types20.Event } func (*Event_Communication) isEvent_Type() {} -func (w *Event_Communication) Unwrap() *types21.Event { +func (w *Event_Communication) Unwrap() *types20.Event { return w.Communication } @@ -675,12 +648,12 @@ func (*Event_Communication) MirReflect() mirreflect.Type { } type Event_Synchronizer struct { - Synchronizer *types22.Event + Synchronizer *types21.Event } func (*Event_Synchronizer) isEvent_Type() {} -func (w *Event_Synchronizer) Unwrap() *types22.Event { +func (w *Event_Synchronizer) Unwrap() *types21.Event { return w.Synchronizer } @@ -699,12 +672,12 @@ func (*Event_Synchronizer) MirReflect() mirreflect.Type { } type Event_Application struct { - Application *types23.Event + Application *types22.Event } func (*Event_Application) isEvent_Type() {} -func (w *Event_Application) Unwrap() *types23.Event { +func (w *Event_Application) Unwrap() *types22.Event { return w.Application } @@ -723,12 +696,12 @@ func (*Event_Application) MirReflect() mirreflect.Type { } type Event_Bcinterceptor struct { - Bcinterceptor *types24.Event + Bcinterceptor *types23.Event } func (*Event_Bcinterceptor) isEvent_Type() {} -func (w *Event_Bcinterceptor) Unwrap() *types24.Event { +func (w *Event_Bcinterceptor) Unwrap() *types23.Event { return w.Bcinterceptor } @@ -795,12 +768,12 @@ func (*Event_TestingUint) MirReflect() mirreflect.Type { } type Event_Tester struct { - Tester *types25.Tester + Tester *types24.Tester } func (*Event_Tester) isEvent_Type() {} -func (w *Event_Tester) Unwrap() *types25.Tester { +func (w *Event_Tester) Unwrap() *types24.Tester { return w.Tester } @@ -825,7 +798,7 @@ func EventFromPb(pb *eventpb.Event) *Event { return &Event{ DestModule: (types.ModuleID)(pb.DestModule), Type: Event_TypeFromPb(pb.Type), - Next: types26.ConvertSlice(pb.Next, func(t *eventpb.Event) *Event { + Next: types25.ConvertSlice(pb.Next, func(t *eventpb.Event) *Event { return EventFromPb(t) }), } @@ -841,7 +814,7 @@ func (m *Event) Pb() *eventpb.Event { if m.Type != nil { pbMessage.Type = (m.Type).Pb() } - pbMessage.Next = types26.ConvertSlice(m.Next, func(t *Event) *eventpb.Event { + pbMessage.Next = types25.ConvertSlice(m.Next, func(t *Event) *eventpb.Event { return (t).Pb() }) } @@ -1008,7 +981,7 @@ func (*TimerEvent) MirReflect() mirreflect.Type { type TimerDelay struct { EventsToDelay []*Event - Delay types27.Duration + Delay types26.Duration } func TimerDelayFromPb(pb *eventpb.TimerDelay) *TimerDelay { @@ -1016,10 +989,10 @@ func TimerDelayFromPb(pb *eventpb.TimerDelay) *TimerDelay { return nil } return &TimerDelay{ - EventsToDelay: types26.ConvertSlice(pb.EventsToDelay, func(t *eventpb.Event) *Event { + EventsToDelay: types25.ConvertSlice(pb.EventsToDelay, func(t *eventpb.Event) *Event { return EventFromPb(t) }), - Delay: (types27.Duration)(pb.Delay), + Delay: (types26.Duration)(pb.Delay), } } @@ -1029,7 +1002,7 @@ func (m *TimerDelay) Pb() *eventpb.TimerDelay { } pbMessage := &eventpb.TimerDelay{} { - pbMessage.EventsToDelay = types26.ConvertSlice(m.EventsToDelay, func(t *Event) *eventpb.Event { + pbMessage.EventsToDelay = types25.ConvertSlice(m.EventsToDelay, func(t *Event) *eventpb.Event { return (t).Pb() }) pbMessage.Delay = (uint64)(m.Delay) @@ -1044,8 +1017,8 @@ func (*TimerDelay) MirReflect() mirreflect.Type { type TimerRepeat struct { EventsToRepeat []*Event - Delay types27.Duration - RetentionIndex types28.RetentionIndex + Delay types26.Duration + RetentionIndex types27.RetentionIndex } func TimerRepeatFromPb(pb *eventpb.TimerRepeat) *TimerRepeat { @@ -1053,11 +1026,11 @@ func TimerRepeatFromPb(pb *eventpb.TimerRepeat) *TimerRepeat { return nil } return &TimerRepeat{ - EventsToRepeat: types26.ConvertSlice(pb.EventsToRepeat, func(t *eventpb.Event) *Event { + EventsToRepeat: types25.ConvertSlice(pb.EventsToRepeat, func(t *eventpb.Event) *Event { return EventFromPb(t) }), - Delay: (types27.Duration)(pb.Delay), - RetentionIndex: (types28.RetentionIndex)(pb.RetentionIndex), + Delay: (types26.Duration)(pb.Delay), + RetentionIndex: (types27.RetentionIndex)(pb.RetentionIndex), } } @@ -1067,7 +1040,7 @@ func (m *TimerRepeat) Pb() *eventpb.TimerRepeat { } pbMessage := &eventpb.TimerRepeat{} { - pbMessage.EventsToRepeat = types26.ConvertSlice(m.EventsToRepeat, func(t *Event) *eventpb.Event { + pbMessage.EventsToRepeat = types25.ConvertSlice(m.EventsToRepeat, func(t *Event) *eventpb.Event { return (t).Pb() }) pbMessage.Delay = (uint64)(m.Delay) @@ -1082,7 +1055,7 @@ func (*TimerRepeat) MirReflect() mirreflect.Type { } type TimerGarbageCollect struct { - RetentionIndex types28.RetentionIndex + RetentionIndex types27.RetentionIndex } func TimerGarbageCollectFromPb(pb *eventpb.TimerGarbageCollect) *TimerGarbageCollect { @@ -1090,7 +1063,7 @@ func TimerGarbageCollectFromPb(pb *eventpb.TimerGarbageCollect) *TimerGarbageCol return nil } return &TimerGarbageCollect{ - RetentionIndex: (types28.RetentionIndex)(pb.RetentionIndex), + RetentionIndex: (types27.RetentionIndex)(pb.RetentionIndex), } } diff --git a/protos/blockchainpb/applicationpb/applicationpb.proto b/protos/blockchainpb/applicationpb/applicationpb.proto index e978bf1c0..abd2d804b 100644 --- a/protos/blockchainpb/applicationpb/applicationpb.proto +++ b/protos/blockchainpb/applicationpb/applicationpb.proto @@ -6,6 +6,8 @@ option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applica import "mir/codegen_extensions.proto"; import "blockchainpb/payloadpb/payloadpb.proto"; +import "blockchainpb/statepb/statepb.proto"; +import "blockchainpb/blockchainpb.proto"; message Event { option (mir.event_class) = true; @@ -13,19 +15,48 @@ message Event { oneof type { option (mir.event_type) = true; - NewHead new_head = 1; - PayloadRequest payload_request = 2; - PayloadResponse payload_response = 3; - + // Application-application events + NewHead new_head = 10; + VerifyBlockRequest verify_block_request = 11; + VerifyBlockResponse verify_block_response = 12; + + // Transaction management application events + PayloadRequest payload_request = 20; + PayloadResponse payload_response = 21; + ForkUpdate fork_update = 22; } } +// Application-application events message NewHead { option (mir.event) = true; uint64 head_id = 1; } +message VerifyBlockRequest { + option (mir.event) = true; + + uint64 request_id = 1; + blockchainpb.Block block = 2; +} + +message VerifyBlockResponse { + option (mir.event) = true; + + uint64 request_id = 1; + bool ok = 2; +} + +message ForkUpdate { + option (mir.event) = true; + + blockchainpb.Blockchain removed_chain = 1; + blockchainpb.Blockchain added_chain = 2; + statepb.State fork_state = 3; +} + +// Transaction management application events message PayloadRequest { option (mir.event) = true; diff --git a/protos/blockchainpb/interceptorpb/interceptorpb.proto b/protos/blockchainpb/interceptorpb/interceptorpb.proto index e68550887..9e9ed37b3 100644 --- a/protos/blockchainpb/interceptorpb/interceptorpb.proto +++ b/protos/blockchainpb/interceptorpb/interceptorpb.proto @@ -5,6 +5,7 @@ package interceptorpb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb"; import "blockchainpb/blockchainpb.proto"; +import "blockchainpb/statepb/statepb.proto"; import "mir/codegen_extensions.proto"; message Event { @@ -35,5 +36,5 @@ message NewOrphan { message AppUpdate { option (mir.event) = true; - int64 state = 1; + statepb.State state = 1; } \ No newline at end of file diff --git a/protos/blockchainpb/payloadpb/payloadpb.proto b/protos/blockchainpb/payloadpb/payloadpb.proto index e18edf361..aedc14c95 100644 --- a/protos/blockchainpb/payloadpb/payloadpb.proto +++ b/protos/blockchainpb/payloadpb/payloadpb.proto @@ -6,5 +6,6 @@ option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payload message Payload { // application specific payload - int64 add_minus = 1; + string message = 1; + int64 timestamp = 2; } \ No newline at end of file diff --git a/protos/blockchainpb/statepb/statepb.proto b/protos/blockchainpb/statepb/statepb.proto index a84dd0549..4b393d37a 100644 --- a/protos/blockchainpb/statepb/statepb.proto +++ b/protos/blockchainpb/statepb/statepb.proto @@ -6,5 +6,5 @@ option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb message State { // application specific state - int64 counter = 1; + repeated string message_history = 1; } \ No newline at end of file diff --git a/protos/blockchainpb/tpmpb/tpmpb.proto b/protos/blockchainpb/tpmpb/tpmpb.proto deleted file mode 100644 index fce9e761d..000000000 --- a/protos/blockchainpb/tpmpb/tpmpb.proto +++ /dev/null @@ -1,24 +0,0 @@ -syntax = "proto3"; - -package tpmpb; - -option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb"; - -import "mir/codegen_extensions.proto"; - -message Event { - option (mir.event_class) = true; - - oneof type { - option (mir.event_type) = true; - - NewBlockRequest new_block_request = 1; - } -} - -message NewBlockRequest { - option (mir.event) = true; - - //blockchainpb.Blockchain chain = 1; - uint64 head_id = 1; -} diff --git a/protos/eventpb/eventpb.proto b/protos/eventpb/eventpb.proto index 2a21fce98..9f1f3870f 100644 --- a/protos/eventpb/eventpb.proto +++ b/protos/eventpb/eventpb.proto @@ -25,7 +25,6 @@ import "transportpb/transportpb.proto"; import "testerpb/testerpb.proto"; import "blockchainpb/bcmpb/bcmpb.proto"; import "blockchainpb/minerpb/minerpb.proto"; -import "blockchainpb/tpmpb/tpmpb.proto"; import "blockchainpb/communicationpb/communicationpb.proto"; import "blockchainpb/synchronizerpb/synchronizerpb.proto"; import "blockchainpb/applicationpb/applicationpb.proto"; @@ -73,10 +72,9 @@ message Event { // Events for blockchain bcmpb.Event bcm = 201; minerpb.Event miner = 202; - tpmpb.Event tpm = 203; - communicationpb.Event communication = 204; - synchronizerpb.Event synchronizer = 205; - applicationpb.Event application = 206; + communicationpb.Event communication = 203; + synchronizerpb.Event synchronizer = 204; + applicationpb.Event application = 205; // Events for blockchain interceptor interceptorpb.Event bcinterceptor = 210; diff --git a/protos/generate.go b/protos/generate.go index de0f8d603..e1206f55f 100644 --- a/protos/generate.go +++ b/protos/generate.go @@ -47,7 +47,6 @@ package protos //go:generate protoc-events blockchainpb/blockchainpb.proto //go:generate protoc-events blockchainpb/bcmpb/bcmpb.proto //go:generate protoc-events blockchainpb/minerpb/minerpb.proto -//go:generate protoc-events blockchainpb/tpmpb/tpmpb.proto //go:generate protoc-events blockchainpb/communicationpb/communicationpb.proto //go:generate protoc-events blockchainpb/synchronizerpb/synchronizerpb.proto //go:generate protoc-events blockchainpb/applicationpb/applicationpb.proto @@ -88,7 +87,6 @@ package protos //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" -//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index c36b5ce83..44b5f9226 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -3,9 +3,6 @@ package application // app module import ( - "fmt" - "math/rand" - "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" @@ -13,8 +10,8 @@ import ( applicationpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/dsl" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + "github.com/filecoin-project/mir/samples/blockchain/application/transactions" "github.com/filecoin-project/mir/samples/blockchain/utils" ) @@ -27,53 +24,80 @@ type ApplicationModule struct { m *dsl.Module currentState *localState logger logging.Logger + tm *transactions.TransactionManager + name string } +// application-application events + func applyBlockToState(state *statepb.State, block *blockchainpb.Block) *statepb.State { return &statepb.State{ - Counter: state.Counter + block.Payload.AddMinus, + MessageHistory: append(state.MessageHistory, block.Payload.Message), } } -func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { - // NOTE: reject request for head that doesn't match current head? - am.logger.Log(logging.LevelDebug, "Processing payload request", "headId", utils.FormatBlockId(head_id)) - - var summand int64 - if rand.Intn(2) == 0 { - summand = -1 - } else { - summand = 1 +// func (am *ApplicationModule) handleGetHeadToCheckpointChainResponse(requestID string, chain []*blockchainpb.BlockInternal) error { +// // TODO: ignoring request ids rn - they are probably not even needed +// am.logger.Log(logging.LevelInfo, "Received chain", "requestId", requestID, "chain", chain) + +// if len(chain) == 0 { +// am.logger.Log(logging.LevelError, "Received empty chain - this should not happen") +// panic("Received empty chain - this should not happen") +// } + +// state := chain[0].State +// blockId := chain[0].Block.BlockId +// if state == nil { +// am.logger.Log(logging.LevelError, "Received chain with empty checkpoint state - this should not happen") +// panic("Received chain with empty checkpoint state - this should not happen") +// } + +// for _, block := range chain[1:] { +// state = applyBlockToState(state, block.Block) +// blockId = block.Block.BlockId +// } + +// bcmpbdsl.RegisterCheckpoint(*am.m, "bcm", blockId, state) +// interceptorpbdsl.AppUpdate(*am.m, "devnull", state) +// am.currentState = &localState{ +// head: blockId, +// state: state, +// } + +// return nil +// } + +// func (am *ApplicationModule) handleNewHead(head_id uint64) error { +// am.logger.Log(logging.LevelDebug, "Processing new head, sending request for state update", "headId", utils.FormatBlockId(head_id)) +// bcmpbdsl.GetHeadToCheckpointChainRequest(*am.m, "bcm", fmt.Sprint(head_id), "application") +// return nil +// } + +func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain *blockchainpb.Blockchain, forkState *statepb.State) error { + am.logger.Log(logging.LevelInfo, "Processing fork update", "poolSize", am.tm.PoolSize()) + + // add "remove chain" transactions to pool + for _, block := range removedChain.GetBlocks() { + am.tm.AddPayload(block.Payload) } - applicationpbdsl.PayloadResponse(*am.m, "miner", head_id, &payloadpb.Payload{AddMinus: summand}) - - return nil -} - -func (am *ApplicationModule) handleGetHeadToCheckpointChainResponse(requestID string, chain []*blockchainpb.BlockInternal) error { - // TODO: ignoring request ids rn - they are probably not even needed - am.logger.Log(logging.LevelInfo, "Received chain", "requestId", requestID, "chain", chain) - - if len(chain) == 0 { - am.logger.Log(logging.LevelError, "Received empty chain - this should not happen") - panic("Received empty chain - this should not happen") + // remove "add chain" transactions from pool + for _, block := range addedChain.GetBlocks() { + am.tm.RemovePayload(block.Payload) } - state := chain[0].State - blockId := chain[0].Block.BlockId - if state == nil { - am.logger.Log(logging.LevelError, "Received chain with empty checkpoint state - this should not happen") - panic("Received chain with empty checkpoint state - this should not happen") + // apply state to fork state + state := forkState + for _, block := range addedChain.GetBlocks() { + state = applyBlockToState(state, block) } - for _, block := range chain[1:] { - state = applyBlockToState(state, block.Block) - blockId = block.Block.BlockId - } + am.logger.Log(logging.LevelInfo, "Pool after fork", "poolSize", am.tm.PoolSize()) + // register checkpoint + blockId := addedChain.GetBlocks()[len(addedChain.GetBlocks())-1].BlockId bcmpbdsl.RegisterCheckpoint(*am.m, "bcm", blockId, state) - interceptorpbdsl.AppUpdate(*am.m, "devnull", state.Counter) + interceptorpbdsl.AppUpdate(*am.m, "devnull", state) am.currentState = &localState{ head: blockId, state: state, @@ -82,24 +106,31 @@ func (am *ApplicationModule) handleGetHeadToCheckpointChainResponse(requestID st return nil } -func (am *ApplicationModule) handleNewHead(head_id uint64) error { - am.logger.Log(logging.LevelDebug, "Processing new head, sending request for state update", "headId", utils.FormatBlockId(head_id)) - bcmpbdsl.GetHeadToCheckpointChainRequest(*am.m, "bcm", fmt.Sprint(head_id), "application") +// transaction management events + +func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { + // NOTE: reject request for head that doesn't match current head? + am.logger.Log(logging.LevelDebug, "Processing payload request", "headId", utils.FormatBlockId(head_id)) + + payload := am.tm.GetPayload() + applicationpbdsl.PayloadResponse(*am.m, "miner", head_id, payload) + return nil } -func NewApplication(logger logging.Logger) modules.PassiveModule { +func NewApplication(logger logging.Logger, name string) modules.PassiveModule { m := dsl.NewModule("application") - am := &ApplicationModule{m: &m, currentState: nil, logger: logger} + am := &ApplicationModule{m: &m, currentState: nil, logger: logger, name: name, tm: transactions.New(name)} dsl.UponInit(m, func() error { return nil }) applicationpbdsl.UponPayloadRequest(m, am.handlePayloadRequest) - applicationpbdsl.UponNewHead(m, am.handleNewHead) - bcmpbdsl.UponGetHeadToCheckpointChainResponse(m, am.handleGetHeadToCheckpointChainResponse) + applicationpbdsl.UponForkUpdate(m, am.handleForkUpdate) + // applicationpbdsl.UponNewHead(m, am.handleNewHead) + // bcmpbdsl.UponGetHeadToCheckpointChainResponse(m, am.handleGetHeadToCheckpointChainResponse) return m } diff --git a/samples/blockchain/application/transactions/transactions.go b/samples/blockchain/application/transactions/transactions.go new file mode 100644 index 000000000..2da7790d3 --- /dev/null +++ b/samples/blockchain/application/transactions/transactions.go @@ -0,0 +1,100 @@ +package transactions + +import ( + "cmp" + "fmt" + "slices" + "time" + + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + "github.com/mitchellh/hashstructure" +) + +type transaction struct { + hash uint64 + payload *payloadpb.Payload +} + +type TransactionManager struct { + transactions []transaction // sorted by timestamp + name string + ownTransactionCounter uint64 +} + +func (tm *TransactionManager) PoolSize() int { + return len(tm.transactions) +} + +func New(name string) *TransactionManager { + return &TransactionManager{ + transactions: []transaction{}, + name: name, + ownTransactionCounter: 0, + } +} + +func (tm *TransactionManager) GetPayload() *payloadpb.Payload { + // return oldest transaction, where timestamp is a field in the payload + if len(tm.transactions) == 0 { + // TODO: this only makes sense for the test - remove it + payload := &payloadpb.Payload{ + Message: fmt.Sprintf("%s: %d", tm.name, tm.ownTransactionCounter), + Timestamp: time.Now().UnixNano(), + } + hash, err := hashstructure.Hash(payload, nil) + if err != nil { + // just panicing - only for testing anyways... + panic(fmt.Errorf("error hashing payload: %w", err)) + } + tm.transactions = append(tm.transactions, transaction{ + hash: hash, + payload: payload, + }) + tm.ownTransactionCounter++ + return payload + } + // sort by timestamp + // TODO: keep it sorted + slices.SortFunc(tm.transactions, func(i, j transaction) int { + return cmp.Compare[int64](i.payload.Timestamp, j.payload.Timestamp) + }) + return tm.transactions[0].payload +} + +func (tm *TransactionManager) AddPayload(payload *payloadpb.Payload) error { + hash, err := hashstructure.Hash(payload, nil) + if err != nil { + return err + } + + if slices.ContainsFunc(tm.transactions, func(t transaction) bool { + return t.hash == hash + }) { + // already exists, ignore + return nil + } + + transaction := transaction{ + hash: hash, + payload: payload, + } + + tm.transactions = append(tm.transactions, transaction) + + return nil +} + +func (tm *TransactionManager) RemovePayload(payload *payloadpb.Payload) error { + hash, err := hashstructure.Hash(payload, nil) + if err != nil { + return err + } + + // goes through all transactions and removes the one with the matching hash + // NOTE: consider adding (hash) index to improve performance - not important rn + tm.transactions = slices.DeleteFunc(tm.transactions, func(t transaction) bool { + return t.hash == hash + }) + + return nil +} diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index c25f54513..48d3226d6 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -5,6 +5,8 @@ package main import ( "errors" "fmt" + "slices" + "strings" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" @@ -25,6 +27,7 @@ import ( var ( ErrParentNotFound = errors.New("parent not found") ErrNodeAlreadyInTree = errors.New("node already in tree") + ErrRollback = errors.New("rollback - should never happen") ) type bcmBlock struct { @@ -35,20 +38,144 @@ type bcmBlock struct { } type bcmModule struct { - m *dsl.Module - blocks []bcmBlock // IMPORTANT: only to be used for visualization, not for actual block lookup - leaves map[uint64]*bcmBlock - head *bcmBlock - genesis *bcmBlock - checkpoints map[uint64]*bcmBlock - blockCount uint64 - logger logging.Logger + m *dsl.Module + blocks []bcmBlock // IMPORTANT: only to be used for visualization, not for actual block lookup + leaves map[uint64]*bcmBlock + head *bcmBlock + genesis *bcmBlock + checkpoints map[uint64]*bcmBlock + blockCount uint64 + currentScanCount uint64 + logger logging.Logger } -func (bcm *bcmModule) handleNewHead(newHead *blockchainpb.BlockInternal) { - bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.Block.BlockId)) - applicationpbdsl.NewHead(*bcm.m, "application", newHead.Block.BlockId) - minerpbdsl.NewHead(*bcm.m, "miner", newHead.Block.BlockId) +func (bcm *bcmModule) handleNewHeadSideEffects(newHead, oldHead *bcmBlock) { + bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.block.Block.BlockId)) + // compute delta + removeChain, addChain, err := bcm.computeDelta(oldHead, newHead) + + bcm.logger.Log(logging.LevelInfo, "Delta chains", "addChain", formatChain(addChain), "removeChain", formatChain(removeChain)) + + if err != nil { + // should never happen + bcm.logger.Log(logging.LevelError, "Error computing delta", "error", err) + panic(err) + } + + // get fork state + forkBlock, ok := bcm.checkpoints[addChain[0].BlockId] + if !ok { + bcm.logger.Log(logging.LevelError, "Failed to get fork block") + } + + forkState := forkBlock.block.State + + applicationpbdsl.ForkUpdate(*bcm.m, "application", &blockchainpb.Blockchain{ + Blocks: removeChain[1:], + }, &blockchainpb.Blockchain{ + Blocks: addChain[1:], + }, forkState) + // applicationpbdsl.NewHead(*bcm.m, "application", newHead.block.Block.BlockId) + minerpbdsl.NewHead(*bcm.m, "miner", newHead.block.Block.BlockId) +} + +func formatChain(chain []*blockchainpb.Block) string { + ids := make([]string, len(chain)) + for i, block := range chain { + ids[i] = fmt.Sprint(block.BlockId) + } + + return strings.Join(ids, ", ") +} + +func reverse[S ~[]T, T any](slice S) S { + slices.Reverse(slice) + return slice +} + +func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]*blockchainpb.Block, []*blockchainpb.Block, error) { + bcm.logger.Log(logging.LevelDebug, "Computing Delta", "removeHead", removeHead.block.Block.BlockId, "addHead", addHead.block.Block.BlockId) + + // short circuit for most simple cases + if removeHead.block.Block.GetBlockId() == addHead.block.Block.GetBlockId() { + // no delta + return []*blockchainpb.Block{addHead.block.GetBlock()}, []*blockchainpb.Block{addHead.block.GetBlock()}, nil + } else if addHead.block.Block.GetPreviousBlockId() == removeHead.block.Block.GetBlockId() { + // just appending + return []*blockchainpb.Block{removeHead.block.GetBlock()}, []*blockchainpb.Block{removeHead.block.GetBlock(), addHead.block.GetBlock()}, nil + } else if removeHead.block.Block.GetPreviousBlockId() == addHead.block.Block.GetBlockId() { + // rollbacks should never happen + return nil, nil, ErrRollback + } + + // traverse backwards from both heads until we find a common ancestor + // current ancecor is given when we find a block was already scanned by the other traversal + // this is the case if + initialScanCount := bcm.currentScanCount + + removeChain := make([]*blockchainpb.Block, 0) + addChain := make([]*blockchainpb.Block, 0) + + currRemove := removeHead + currAdd := addHead + + // TODO: consider weird cases like when they only have the genesis block in common + // at least one hasn't reached the end yet + for (currRemove != nil) || (currAdd != nil) { + bcm.logger.Log(logging.LevelDebug, "add", "chain", formatChain(addChain)) + bcm.logger.Log(logging.LevelDebug, "remove", "chain", formatChain(removeChain)) + + // handle remove step + if currRemove != nil { + + removeChain = append(removeChain, currRemove.block.GetBlock()) + + // check for intersection + if currRemove.scanCount > initialScanCount { + // remove chain intersects with add chain, remove chain is ok, add chain needs to be truncated + // find currRemove in addChain + currRemoveBlockId := currRemove.block.Block.GetBlockId() + index := slices.IndexFunc(addChain, func(i *blockchainpb.Block) bool { return i.BlockId == currRemoveBlockId }) + if index == -1 { + // should never happen + bcm.logger.Log(logging.LevelError, "Intersection trucation failed (remove intersection case) - this should never happen", "blockId", utils.FormatBlockId(currRemoveBlockId)) + panic("Intersection trucation failed (remove into add intersection case)") + } + return reverse(removeChain), reverse(addChain[:index+1]), nil + } + + currRemove.scanCount = initialScanCount + 1 + currRemove = currRemove.parent + } + + // handle add step + if currAdd != nil { + + addChain = append(addChain, currAdd.block.GetBlock()) + + // check for intersection + if currAdd.scanCount > initialScanCount { + // add chain intersects with remove chain, add chain is ok, remove chain needs to be truncated + // find addRemove in removeChain + currAddBlockId := currAdd.block.Block.GetBlockId() + index := slices.IndexFunc(removeChain, func(i *blockchainpb.Block) bool { return i.BlockId == currAddBlockId }) + if index == -1 { + // should never happen + bcm.logger.Log(logging.LevelError, "Intersection trucation failed (add intersection case) - this should never happen", "blockId", utils.FormatBlockId(currAddBlockId)) + panic("Intersection trucation failed (add into remove intersection case)") + } + return reverse(removeChain[:index+1]), reverse(addChain), nil + } + + currAdd.scanCount = initialScanCount + 1 + currAdd = currAdd.parent + } + + } + + // if we get here, we didn't find a common ancestor, this should never happen + bcm.logger.Log(logging.LevelError, "Intersection not found - this should never happen") + panic("intersection not found - this should never happen") } // traversalFunc should return true if traversal should continue, false otherwise @@ -56,13 +183,10 @@ func (bcm *bcmModule) handleNewHead(newHead *blockchainpb.BlockInternal) { // if it should continue but there are no more blocks, the function will return nil // if an error is thrown, the traversal is aborted and nil is returned func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) (bool, error)) *bcmBlock { - currentScanCount := uint64(0) // to mark nodes that have been scanned in this round + bcm.currentScanCount = bcm.currentScanCount + 1 queue := make([]*bcmBlock, 0) for _, v := range bcm.leaves { queue = append(queue, v) - if v.scanCount > currentScanCount { - currentScanCount = v.scanCount - } } for len(queue) > 0 { // pop from queue @@ -70,7 +194,7 @@ func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) queue = queue[1:] // mark as scanned - curr.scanCount = currentScanCount + 1 + curr.scanCount = bcm.currentScanCount if match, err := traversalFunc(curr); err != nil { return nil @@ -79,7 +203,7 @@ func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) } // add curr's parents to queue if it hasn't been scanned yet - if curr.parent != nil && curr.parent.scanCount <= currentScanCount { + if curr.parent != nil && curr.parent.scanCount <= bcm.currentScanCount { queue = append(queue, curr.parent) } } @@ -167,8 +291,8 @@ func (bcm *bcmModule) findBlock(blockId uint64) *bcmBlock { }) } -func (bcm *bcmModule) getHead() *blockchainpb.BlockInternal { - return bcm.head.block +func (bcm *bcmModule) getHead() *bcmBlock { + return bcm.head } func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { @@ -196,7 +320,7 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { // if head changed, trigger get tpm to prep new block if newHead := bcm.getHead(); newHead != currentHead { - bcm.handleNewHead(newHead) + bcm.handleNewHeadSideEffects(newHead, currentHead) } // send to chainviewer @@ -234,7 +358,7 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { // if head changed, trigger get tpm to prep new block (only need to do this once) if newHead := bcm.getHead(); newHead != currentHead { - bcm.handleNewHead(newHead) + bcm.handleNewHeadSideEffects(newHead, currentHead) } // send to chainviewer @@ -262,28 +386,7 @@ func (bcm *bcmModule) sendTreeUpdate() { interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", &blockTree, bcm.head.block.Block.BlockId) } -func formatBlockInts(blocks []*blockchainpb.BlockInternal) []string { - blockIds := make([]string, 0, len(blocks)) - for _, v := range blocks { - stateText := "no state" - if v.State != nil { - stateText = v.State.String() - } - blockIds = append(blockIds, fmt.Sprintf("%d - %s", v.Block.BlockId, stateText)) - } - return blockIds -} - func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, sourceModule t.ModuleID) error { - // for debugging - // TODO: remove - checkpoints := make([]*blockchainpb.BlockInternal, 0, len(bcm.checkpoints)) - - for _, value := range bcm.checkpoints { - checkpoints = append(checkpoints, value.block) - } - - bcm.logger.Log(logging.LevelInfo, "Received get head to checkpoint chain request", "requestId", requestID, "checkpoints", formatBlockInts(checkpoints)) chain := make([]*blockchainpb.BlockInternal, 0) // start with head @@ -306,7 +409,6 @@ func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, so chain[i], chain[j] = chain[j], chain[i] } - bcm.logger.Log(logging.LevelDebug, "Sending chain for state comp", "requestId", requestID, "chain", formatBlockInts(chain), "checkpoint", formatBlockInts([]*blockchainpb.BlockInternal{currentBlock.block})) bcmpbdsl.GetHeadToCheckpointChainResponse(*bcm.m, sourceModule, requestID, chain) return nil @@ -341,7 +443,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { genesis := &blockchainpb.Block{ BlockId: 0, PreviousBlockId: 0, - Payload: &payloadpb.Payload{AddMinus: 0}, + Payload: &payloadpb.Payload{Message: "GENESIS", Timestamp: 0}, Timestamp: 0, // unix 0 } @@ -351,7 +453,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { block: &blockchainpb.BlockInternal{ Block: genesis, State: &statepb.State{ - Counter: 0, + MessageHistory: []string{genesis.Payload.GetMessage()}, // TODO: this is ugly af - have application define genesis and base state }, }, parent: nil, @@ -360,14 +462,15 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { // init bcm genisis block bcm := bcmModule{ - m: &m, - blocks: []bcmBlock{genesisBcm}, - leaves: make(map[uint64]*bcmBlock), - head: &genesisBcm, - genesis: &genesisBcm, - checkpoints: make(map[uint64]*bcmBlock), - blockCount: 1, - logger: logger, + m: &m, + blocks: []bcmBlock{genesisBcm}, + leaves: make(map[uint64]*bcmBlock), + head: &genesisBcm, + genesis: &genesisBcm, + checkpoints: make(map[uint64]*bcmBlock), + blockCount: 1, + currentScanCount: 0, + logger: logger, } // add genesis to leaves and checkpoints diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index 6c30c81c5..de598d599 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -90,7 +90,7 @@ func main() { "miner": NewMiner(logging.Decorate(logger, "Miner:\t")), "communication": NewCommunication(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), // "tpm": NewTPM(logging.Decorate(logger, "TPM:\t")), - "application": application.NewApplication(logging.Decorate(logger, "App:\t")), + "application": application.NewApplication(logging.Decorate(logger, "App:\t"), string(ownNodeID)), "synchronizer": NewSynchronizer(ownNodeID, otherNodes, false, logging.Decorate(logger, "Sync:\t")), "timer": timer, "mangler": mangler, diff --git a/samples/blockchain/tpm.go b/samples/blockchain/tpm.go deleted file mode 100644 index 41c5a0787..000000000 --- a/samples/blockchain/tpm.go +++ /dev/null @@ -1,40 +0,0 @@ -// transaction pool manager - -package main - -import ( - "math/rand" - "time" - - "github.com/filecoin-project/mir/pkg/dsl" - "github.com/filecoin-project/mir/pkg/logging" - "github.com/filecoin-project/mir/pkg/modules" - minerdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" - tpmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/tpmpb/dsl" - "github.com/filecoin-project/mir/samples/blockchain/utils" -) - -func NewTPM(logger logging.Logger) modules.PassiveModule { - - m := dsl.NewModule("tpm") - r := rand.New(rand.NewSource(time.Now().UnixNano())) - - dsl.UponInit(m, func() error { - return nil - }) - - tpmpb.UponNewBlockRequest(m, func(headId uint64) error { - // just generating random payloads for now - // generate random string - logger.Log(logging.LevelInfo, "Processing block request", "headId", utils.FormatBlockId(headId)) - value := int64(r.Intn(10000)) - payload := &payloadpb.Payload{AddMinus: value} - - minerdsl.BlockRequest(m, "miner", headId, payload) - - return nil - }) - - return m -} From baa8fd17933eb26dfca6fed16fc7fee3bbb91d0f Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Mon, 8 Jan 2024 12:54:27 +0100 Subject: [PATCH 12/46] wip - to be reverted (unless I forget) --- .../applicationpb/applicationpb.pb.go | 202 +++++++++++++----- .../applicationpb/applicationpb.pb.mir.go | 1 + .../applicationpb/dsl/emit.mir.go | 4 + .../applicationpb/dsl/upon.mir.go | 6 + .../applicationpb/events/events.mir.go | 15 ++ .../applicationpb/oneof_interfaces.mir.go | 4 + .../applicationpb/types/types.mir.go | 55 +++++ .../applicationpb/applicationpb.proto | 11 + samples/blockchain/application/application.go | 104 ++++----- samples/blockchain/application/config.go | 7 + .../application/transactions/transactions.go | 35 ++- samples/blockchain/bcm.go | 27 +-- samples/blockchain/main.go | 47 ++-- samples/blockchain/run.sh | 23 ++ 14 files changed, 381 insertions(+), 160 deletions(-) create mode 100644 samples/blockchain/application/config.go create mode 100755 samples/blockchain/run.sh diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go index eafdee1a0..d26d5c7b4 100644 --- a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go @@ -37,6 +37,7 @@ type Event struct { // *Event_PayloadRequest // *Event_PayloadResponse // *Event_ForkUpdate + // *Event_MessageInput Type isEvent_Type `protobuf_oneof:"type"` } @@ -121,6 +122,13 @@ func (x *Event) GetForkUpdate() *ForkUpdate { return nil } +func (x *Event) GetMessageInput() *MessageInput { + if x, ok := x.GetType().(*Event_MessageInput); ok { + return x.MessageInput + } + return nil +} + type isEvent_Type interface { isEvent_Type() } @@ -151,6 +159,11 @@ type Event_ForkUpdate struct { ForkUpdate *ForkUpdate `protobuf:"bytes,22,opt,name=fork_update,json=forkUpdate,proto3,oneof"` } +type Event_MessageInput struct { + // Message input + MessageInput *MessageInput `protobuf:"bytes,30,opt,name=message_input,json=messageInput,proto3,oneof"` +} + func (*Event_NewHead) isEvent_Type() {} func (*Event_VerifyBlockRequest) isEvent_Type() {} @@ -163,6 +176,8 @@ func (*Event_PayloadResponse) isEvent_Type() {} func (*Event_ForkUpdate) isEvent_Type() {} +func (*Event_MessageInput) isEvent_Type() {} + // Application-application events type NewHead struct { state protoimpl.MessageState @@ -487,6 +502,53 @@ func (x *PayloadResponse) GetPayload() *payloadpb.Payload { return nil } +type MessageInput struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Text string `protobuf:"bytes,1,opt,name=text,proto3" json:"text,omitempty"` +} + +func (x *MessageInput) Reset() { + *x = MessageInput{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[7] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *MessageInput) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*MessageInput) ProtoMessage() {} + +func (x *MessageInput) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[7] + 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 MessageInput.ProtoReflect.Descriptor instead. +func (*MessageInput) Descriptor() ([]byte, []int) { + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{7} +} + +func (x *MessageInput) GetText() string { + if x != nil { + return x.Text + } + return "" +} + var File_blockchainpb_applicationpb_applicationpb_proto protoreflect.FileDescriptor var file_blockchainpb_applicationpb_applicationpb_proto_rawDesc = []byte{ @@ -502,7 +564,7 @@ var file_blockchainpb_applicationpb_applicationpb_proto_rawDesc = []byte{ 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd6, 0x03, 0x0a, 0x05, 0x45, + 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x04, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x48, 0x00, @@ -530,48 +592,55 @@ var file_blockchainpb_applicationpb_applicationpb_proto_rawDesc = []byte{ 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x3a, - 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, - 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x17, - 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x64, 0x0a, - 0x12, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x49, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, - 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, - 0xa6, 0x1d, 0x01, 0x22, 0x4a, 0x0a, 0x13, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, - 0xbb, 0x01, 0x0a, 0x0a, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x3d, - 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x52, - 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x39, 0x0a, - 0x0b, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, - 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x0a, 0x61, 0x64, - 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2d, 0x0a, 0x0a, 0x66, 0x6f, 0x72, 0x6b, - 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x09, 0x66, 0x6f, - 0x72, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x2f, 0x0a, - 0x0e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5e, - 0x0a, 0x0f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, - 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, - 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, - 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, - 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, + 0x42, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, + 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, + 0x01, 0x22, 0x64, 0x0a, 0x12, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x4a, 0x0a, 0x13, 0x56, 0x65, 0x72, 0x69, 0x66, + 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, + 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x0e, 0x0a, + 0x02, 0x6f, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x3a, 0x04, 0x98, + 0xa6, 0x1d, 0x01, 0x22, 0xbb, 0x01, 0x0a, 0x0a, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, + 0x6e, 0x12, 0x39, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x52, 0x0a, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2d, 0x0a, 0x0a, + 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x52, 0x09, 0x66, 0x6f, 0x72, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, + 0x01, 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, + 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x0c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, + 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, + 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -586,7 +655,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP() []byte { return file_blockchainpb_applicationpb_applicationpb_proto_rawDescData } -var file_blockchainpb_applicationpb_applicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_blockchainpb_applicationpb_applicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_blockchainpb_applicationpb_applicationpb_proto_goTypes = []interface{}{ (*Event)(nil), // 0: applicationpb.Event (*NewHead)(nil), // 1: applicationpb.NewHead @@ -595,10 +664,11 @@ var file_blockchainpb_applicationpb_applicationpb_proto_goTypes = []interface{}{ (*ForkUpdate)(nil), // 4: applicationpb.ForkUpdate (*PayloadRequest)(nil), // 5: applicationpb.PayloadRequest (*PayloadResponse)(nil), // 6: applicationpb.PayloadResponse - (*blockchainpb.Block)(nil), // 7: blockchainpb.Block - (*blockchainpb.Blockchain)(nil), // 8: blockchainpb.Blockchain - (*statepb.State)(nil), // 9: statepb.State - (*payloadpb.Payload)(nil), // 10: payloadpb.Payload + (*MessageInput)(nil), // 7: applicationpb.MessageInput + (*blockchainpb.Block)(nil), // 8: blockchainpb.Block + (*blockchainpb.Blockchain)(nil), // 9: blockchainpb.Blockchain + (*statepb.State)(nil), // 10: statepb.State + (*payloadpb.Payload)(nil), // 11: payloadpb.Payload } var file_blockchainpb_applicationpb_applicationpb_proto_depIdxs = []int32{ 1, // 0: applicationpb.Event.new_head:type_name -> applicationpb.NewHead @@ -607,16 +677,17 @@ var file_blockchainpb_applicationpb_applicationpb_proto_depIdxs = []int32{ 5, // 3: applicationpb.Event.payload_request:type_name -> applicationpb.PayloadRequest 6, // 4: applicationpb.Event.payload_response:type_name -> applicationpb.PayloadResponse 4, // 5: applicationpb.Event.fork_update:type_name -> applicationpb.ForkUpdate - 7, // 6: applicationpb.VerifyBlockRequest.block:type_name -> blockchainpb.Block - 8, // 7: applicationpb.ForkUpdate.removed_chain:type_name -> blockchainpb.Blockchain - 8, // 8: applicationpb.ForkUpdate.added_chain:type_name -> blockchainpb.Blockchain - 9, // 9: applicationpb.ForkUpdate.fork_state:type_name -> statepb.State - 10, // 10: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 7, // 6: applicationpb.Event.message_input:type_name -> applicationpb.MessageInput + 8, // 7: applicationpb.VerifyBlockRequest.block:type_name -> blockchainpb.Block + 9, // 8: applicationpb.ForkUpdate.removed_chain:type_name -> blockchainpb.Blockchain + 9, // 9: applicationpb.ForkUpdate.added_chain:type_name -> blockchainpb.Blockchain + 10, // 10: applicationpb.ForkUpdate.fork_state:type_name -> statepb.State + 11, // 11: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload + 12, // [12:12] is the sub-list for method output_type + 12, // [12:12] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_blockchainpb_applicationpb_applicationpb_proto_init() } @@ -709,6 +780,18 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { return nil } } + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*MessageInput); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_NewHead)(nil), @@ -717,6 +800,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { (*Event_PayloadRequest)(nil), (*Event_PayloadResponse)(nil), (*Event_ForkUpdate)(nil), + (*Event_MessageInput)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -724,7 +808,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_applicationpb_applicationpb_proto_rawDesc, NumEnums: 0, - NumMessages: 7, + NumMessages: 8, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go index 141532059..dab135fdf 100644 --- a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go @@ -14,5 +14,6 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_PayloadRequest)(nil)), reflect.TypeOf((*Event_PayloadResponse)(nil)), reflect.TypeOf((*Event_ForkUpdate)(nil)), + reflect.TypeOf((*Event_MessageInput)(nil)), } } diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go index a7d8f919c..306b6ef06 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go @@ -36,3 +36,7 @@ func PayloadResponse(m dsl.Module, destModule types.ModuleID, headId uint64, pay func ForkUpdate(m dsl.Module, destModule types.ModuleID, removedChain *blockchainpb.Blockchain, addedChain *blockchainpb.Blockchain, forkState *statepb.State) { dsl.EmitMirEvent(m, events.ForkUpdate(destModule, removedChain, addedChain, forkState)) } + +func MessageInput(m dsl.Module, destModule types.ModuleID, text string) { + dsl.EmitMirEvent(m, events.MessageInput(destModule, text)) +} diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go index 6411ff716..49942909c 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go @@ -59,3 +59,9 @@ func UponForkUpdate(m dsl.Module, handler func(removedChain *blockchainpb.Blockc return handler(ev.RemovedChain, ev.AddedChain, ev.ForkState) }) } + +func UponMessageInput(m dsl.Module, handler func(text string) error) { + UponEvent[*types.Event_MessageInput](m, func(ev *types.MessageInput) error { + return handler(ev.Text) + }) +} diff --git a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go index 2a8b01ee1..a68c6efc7 100644 --- a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go @@ -105,3 +105,18 @@ func ForkUpdate(destModule types.ModuleID, removedChain *blockchainpb.Blockchain }, } } + +func MessageInput(destModule types.ModuleID, text string) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Application{ + Application: &types2.Event{ + Type: &types2.Event_MessageInput{ + MessageInput: &types2.MessageInput{ + Text: text, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go index b6f4cb63c..1eaed08ae 100644 --- a/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go @@ -32,3 +32,7 @@ func (w *Event_PayloadResponse) Unwrap() *PayloadResponse { func (w *Event_ForkUpdate) Unwrap() *ForkUpdate { return w.ForkUpdate } + +func (w *Event_MessageInput) Unwrap() *MessageInput { + return w.MessageInput +} diff --git a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go index c4f463bef..8ebd67daf 100644 --- a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go @@ -43,6 +43,8 @@ func Event_TypeFromPb(pb applicationpb.Event_Type) Event_Type { return &Event_PayloadResponse{PayloadResponse: PayloadResponseFromPb(pb.PayloadResponse)} case *applicationpb.Event_ForkUpdate: return &Event_ForkUpdate{ForkUpdate: ForkUpdateFromPb(pb.ForkUpdate)} + case *applicationpb.Event_MessageInput: + return &Event_MessageInput{MessageInput: MessageInputFromPb(pb.MessageInput)} } return nil } @@ -191,6 +193,30 @@ func (*Event_ForkUpdate) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_ForkUpdate]()} } +type Event_MessageInput struct { + MessageInput *MessageInput +} + +func (*Event_MessageInput) isEvent_Type() {} + +func (w *Event_MessageInput) Unwrap() *MessageInput { + return w.MessageInput +} + +func (w *Event_MessageInput) Pb() applicationpb.Event_Type { + if w == nil { + return nil + } + if w.MessageInput == nil { + return &applicationpb.Event_MessageInput{} + } + return &applicationpb.Event_MessageInput{MessageInput: (w.MessageInput).Pb()} +} + +func (*Event_MessageInput) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_MessageInput]()} +} + func EventFromPb(pb *applicationpb.Event) *Event { if pb == nil { return nil @@ -416,3 +442,32 @@ func (m *PayloadResponse) Pb() *applicationpb.PayloadResponse { func (*PayloadResponse) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.PayloadResponse]()} } + +type MessageInput struct { + Text string +} + +func MessageInputFromPb(pb *applicationpb.MessageInput) *MessageInput { + if pb == nil { + return nil + } + return &MessageInput{ + Text: pb.Text, + } +} + +func (m *MessageInput) Pb() *applicationpb.MessageInput { + if m == nil { + return nil + } + pbMessage := &applicationpb.MessageInput{} + { + pbMessage.Text = m.Text + } + + return pbMessage +} + +func (*MessageInput) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.MessageInput]()} +} diff --git a/protos/blockchainpb/applicationpb/applicationpb.proto b/protos/blockchainpb/applicationpb/applicationpb.proto index abd2d804b..e59f2a181 100644 --- a/protos/blockchainpb/applicationpb/applicationpb.proto +++ b/protos/blockchainpb/applicationpb/applicationpb.proto @@ -24,6 +24,9 @@ message Event { PayloadRequest payload_request = 20; PayloadResponse payload_response = 21; ForkUpdate fork_update = 22; + + // Message input + MessageInput message_input = 30; } } @@ -69,3 +72,11 @@ message PayloadResponse { uint64 head_id = 1; payloadpb.Payload payload = 2; } + +// Message input events + +message MessageInput { + option (mir.event) = true; + + string text = 1; +} \ No newline at end of file diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index 44b5f9226..ae9fa714c 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -3,6 +3,9 @@ package application // app module import ( + "fmt" + "time" + "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" @@ -10,6 +13,7 @@ import ( applicationpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/dsl" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" "github.com/filecoin-project/mir/samples/blockchain/application/transactions" "github.com/filecoin-project/mir/samples/blockchain/utils" @@ -21,11 +25,12 @@ type localState struct { } type ApplicationModule struct { - m *dsl.Module - currentState *localState - logger logging.Logger - tm *transactions.TransactionManager - name string + m *dsl.Module + currentState *localState + logger logging.Logger + tm *transactions.TransactionManager + name string + openPayloadRequest bool // request id } // application-application events @@ -36,43 +41,6 @@ func applyBlockToState(state *statepb.State, block *blockchainpb.Block) *statepb } } -// func (am *ApplicationModule) handleGetHeadToCheckpointChainResponse(requestID string, chain []*blockchainpb.BlockInternal) error { -// // TODO: ignoring request ids rn - they are probably not even needed -// am.logger.Log(logging.LevelInfo, "Received chain", "requestId", requestID, "chain", chain) - -// if len(chain) == 0 { -// am.logger.Log(logging.LevelError, "Received empty chain - this should not happen") -// panic("Received empty chain - this should not happen") -// } - -// state := chain[0].State -// blockId := chain[0].Block.BlockId -// if state == nil { -// am.logger.Log(logging.LevelError, "Received chain with empty checkpoint state - this should not happen") -// panic("Received chain with empty checkpoint state - this should not happen") -// } - -// for _, block := range chain[1:] { -// state = applyBlockToState(state, block.Block) -// blockId = block.Block.BlockId -// } - -// bcmpbdsl.RegisterCheckpoint(*am.m, "bcm", blockId, state) -// interceptorpbdsl.AppUpdate(*am.m, "devnull", state) -// am.currentState = &localState{ -// head: blockId, -// state: state, -// } - -// return nil -// } - -// func (am *ApplicationModule) handleNewHead(head_id uint64) error { -// am.logger.Log(logging.LevelDebug, "Processing new head, sending request for state update", "headId", utils.FormatBlockId(head_id)) -// bcmpbdsl.GetHeadToCheckpointChainRequest(*am.m, "bcm", fmt.Sprint(head_id), "application") -// return nil -// } - func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain *blockchainpb.Blockchain, forkState *statepb.State) error { am.logger.Log(logging.LevelInfo, "Processing fork update", "poolSize", am.tm.PoolSize()) @@ -103,25 +71,52 @@ func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain *blockcha state: state, } + // print state + fmt.Printf("=== STATE ===\n") + for _, msg := range state.MessageHistory { + fmt.Println(msg) + } + fmt.Printf("=============\nEnter new message: \n") + return nil } // transaction management events +func (am *ApplicationModule) providePayload() error { + payload := am.tm.GetPayload() + if payload == nil { + // no payloads to provide, will respond as soon as new paylod is available + am.openPayloadRequest = true // set flag s.t. payload response will be sent as soon as there is a payload available + return nil + } + + applicationpbdsl.PayloadResponse(*am.m, "miner", am.currentState.head, payload) // not using head id anywhere so we can get rid of it + am.openPayloadRequest = false + + return nil +} + func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { // NOTE: reject request for head that doesn't match current head? am.logger.Log(logging.LevelDebug, "Processing payload request", "headId", utils.FormatBlockId(head_id)) - payload := am.tm.GetPayload() - applicationpbdsl.PayloadResponse(*am.m, "miner", head_id, payload) - - return nil + return am.providePayload() } func NewApplication(logger logging.Logger, name string) modules.PassiveModule { m := dsl.NewModule("application") - am := &ApplicationModule{m: &m, currentState: nil, logger: logger, name: name, tm: transactions.New(name)} + am := &ApplicationModule{ + m: &m, + currentState: &localState{ + head: 0, + state: InitialState, + }, + logger: logger, + name: name, + tm: transactions.New(name), + } dsl.UponInit(m, func() error { return nil @@ -129,8 +124,19 @@ func NewApplication(logger logging.Logger, name string) modules.PassiveModule { applicationpbdsl.UponPayloadRequest(m, am.handlePayloadRequest) applicationpbdsl.UponForkUpdate(m, am.handleForkUpdate) - // applicationpbdsl.UponNewHead(m, am.handleNewHead) - // bcmpbdsl.UponGetHeadToCheckpointChainResponse(m, am.handleGetHeadToCheckpointChainResponse) + + applicationpbdsl.UponMessageInput(m, func(text string) error { + am.tm.AddPayload(&payloadpb.Payload{ + Message: text, + Timestamp: time.Now().Unix(), + }) + + if am.openPayloadRequest { + return am.providePayload() + } + + return nil + }) return m } diff --git a/samples/blockchain/application/config.go b/samples/blockchain/application/config.go new file mode 100644 index 000000000..a2459d32e --- /dev/null +++ b/samples/blockchain/application/config.go @@ -0,0 +1,7 @@ +package application + +import "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + +var InitialState = &statepb.State{ + MessageHistory: []string{}, +} diff --git a/samples/blockchain/application/transactions/transactions.go b/samples/blockchain/application/transactions/transactions.go index 2da7790d3..1be5696cb 100644 --- a/samples/blockchain/application/transactions/transactions.go +++ b/samples/blockchain/application/transactions/transactions.go @@ -2,9 +2,7 @@ package transactions import ( "cmp" - "fmt" "slices" - "time" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" "github.com/mitchellh/hashstructure" @@ -36,22 +34,23 @@ func New(name string) *TransactionManager { func (tm *TransactionManager) GetPayload() *payloadpb.Payload { // return oldest transaction, where timestamp is a field in the payload if len(tm.transactions) == 0 { - // TODO: this only makes sense for the test - remove it - payload := &payloadpb.Payload{ - Message: fmt.Sprintf("%s: %d", tm.name, tm.ownTransactionCounter), - Timestamp: time.Now().UnixNano(), - } - hash, err := hashstructure.Hash(payload, nil) - if err != nil { - // just panicing - only for testing anyways... - panic(fmt.Errorf("error hashing payload: %w", err)) - } - tm.transactions = append(tm.transactions, transaction{ - hash: hash, - payload: payload, - }) - tm.ownTransactionCounter++ - return payload + return nil + // // TODO: this only makes sense for the test - remove it + // payload := &payloadpb.Payload{ + // Message: fmt.Sprintf("%s: %d", tm.name, tm.ownTransactionCounter), + // Timestamp: time.Now().UnixNano(), + // } + // hash, err := hashstructure.Hash(payload, nil) + // if err != nil { + // // just panicing - only for testing anyways... + // panic(fmt.Errorf("error hashing payload: %w", err)) + // } + // tm.transactions = append(tm.transactions, transaction{ + // hash: hash, + // payload: payload, + // }) + // tm.ownTransactionCounter++ + // return payload } // sort by timestamp // TODO: keep it sorted diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 48d3226d6..9cd66be81 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -4,9 +4,7 @@ package main import ( "errors" - "fmt" "slices" - "strings" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" @@ -16,10 +14,10 @@ import ( bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" minerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" t "github.com/filecoin-project/mir/pkg/types" + "github.com/filecoin-project/mir/samples/blockchain/application" "github.com/filecoin-project/mir/samples/blockchain/utils" "golang.org/x/exp/maps" ) @@ -51,11 +49,9 @@ type bcmModule struct { func (bcm *bcmModule) handleNewHeadSideEffects(newHead, oldHead *bcmBlock) { bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.block.Block.BlockId)) + // compute delta removeChain, addChain, err := bcm.computeDelta(oldHead, newHead) - - bcm.logger.Log(logging.LevelInfo, "Delta chains", "addChain", formatChain(addChain), "removeChain", formatChain(removeChain)) - if err != nil { // should never happen bcm.logger.Log(logging.LevelError, "Error computing delta", "error", err) @@ -79,15 +75,6 @@ func (bcm *bcmModule) handleNewHeadSideEffects(newHead, oldHead *bcmBlock) { minerpbdsl.NewHead(*bcm.m, "miner", newHead.block.Block.BlockId) } -func formatChain(chain []*blockchainpb.Block) string { - ids := make([]string, len(chain)) - for i, block := range chain { - ids[i] = fmt.Sprint(block.BlockId) - } - - return strings.Join(ids, ", ") -} - func reverse[S ~[]T, T any](slice S) S { slices.Reverse(slice) return slice @@ -122,8 +109,6 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* // TODO: consider weird cases like when they only have the genesis block in common // at least one hasn't reached the end yet for (currRemove != nil) || (currAdd != nil) { - bcm.logger.Log(logging.LevelDebug, "add", "chain", formatChain(addChain)) - bcm.logger.Log(logging.LevelDebug, "remove", "chain", formatChain(removeChain)) // handle remove step if currRemove != nil { @@ -443,18 +428,16 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { genesis := &blockchainpb.Block{ BlockId: 0, PreviousBlockId: 0, - Payload: &payloadpb.Payload{Message: "GENESIS", Timestamp: 0}, + Payload: nil, Timestamp: 0, // unix 0 } - hash := utils.HashBlock(genesis) + hash := uint64(0) // utils.HashBlock(genesis) genesis.BlockId = hash genesisBcm := bcmBlock{ block: &blockchainpb.BlockInternal{ Block: genesis, - State: &statepb.State{ - MessageHistory: []string{genesis.Payload.GetMessage()}, // TODO: this is ugly af - have application define genesis and base state - }, + State: application.InitialState, }, parent: nil, depth: 0, diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index de598d599..5627f03af 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -1,6 +1,7 @@ package main import ( + "bufio" "context" "fmt" "os" @@ -9,9 +10,11 @@ import ( "github.com/filecoin-project/mir" "github.com/filecoin-project/mir/pkg/eventmangler" + "github.com/filecoin-project/mir/pkg/events" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" "github.com/filecoin-project/mir/pkg/net/grpc" + applicationpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/events" "github.com/filecoin-project/mir/pkg/pb/eventpb" trantorpbtypes "github.com/filecoin-project/mir/pkg/pb/trantorpb/types" "github.com/filecoin-project/mir/pkg/timer" @@ -89,12 +92,11 @@ func main() { "bcm": NewBCM(logging.Decorate(logger, "BCM:\t")), "miner": NewMiner(logging.Decorate(logger, "Miner:\t")), "communication": NewCommunication(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), - // "tpm": NewTPM(logging.Decorate(logger, "TPM:\t")), - "application": application.NewApplication(logging.Decorate(logger, "App:\t"), string(ownNodeID)), - "synchronizer": NewSynchronizer(ownNodeID, otherNodes, false, logging.Decorate(logger, "Sync:\t")), - "timer": timer, - "mangler": mangler, - "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor + "application": application.NewApplication(logging.Decorate(logger, "App:\t"), string(ownNodeID)), + "synchronizer": NewSynchronizer(ownNodeID, otherNodes, false, logging.Decorate(logger, "Sync:\t")), + "timer": timer, + "mangler": mangler, + "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor }, wsinterceptor.NewWsInterceptor( func(e *eventpb.Event) bool { @@ -112,22 +114,43 @@ func main() { panic(err) } - // Run the node for 5 seconds. + ctx := context.Background() + nodeError := make(chan error) go func() { - nodeError <- node.Run(context.Background()) + nodeError <- node.Run(ctx) }() fmt.Println("Mir node running.") // block until nodeError receives an error // fmt.Printf("timer started\n") - fmt.Printf("Mir node stopped: %v\n", <-nodeError) + // fmt.Printf("Mir node stopped: %v\n", <-nodeError) + + // ================================================================================ + // Read chat messages from stdin and submit them as transactions. + // ================================================================================ - // Dead code below + scanner := bufio.NewScanner(os.Stdin) - // time.Sleep(5 * time.Second) - // fmt.Printf("timer up") + // Prompt for chat message input. + fmt.Println("Type in your messages and press 'Enter' to send.") + + // Read chat message from stdin. + for scanner.Scan() { + fmt.Println("Gimme more") + // Submit the chat message as transaction payload to the mempool module. + if err := node.InjectEvents(ctx, events.ListOf( + applicationpbevents.MessageInput("application", scanner.Text()).Pb(), + )); err != nil { + // Print error if occurred. + fmt.Println(err) + } + + } + if err := scanner.Err(); err != nil { + fmt.Printf("Error reading input: %v\n", err) + } // Stop the node. node.Stop() diff --git a/samples/blockchain/run.sh b/samples/blockchain/run.sh new file mode 100755 index 000000000..585d5a145 --- /dev/null +++ b/samples/blockchain/run.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +NODE_0_LOG="./node_0.log" +NODE_1_LOG="./node_1.log" +NODE_2_LOG="./node_2.log" +NODE_3_LOG="./node_3.log" + +rm -rf ./node_*.log + +tmux kill-session -t demo +tmux new-session -d -s demo \; \ + \ + split-window -t demo:0 -v \; \ + split-window -t demo:0.0 -h \; \ + split-window -t "demo:0.2" -h \; \ + \ + send-keys -t "demo:0.0" "go run . 4 0 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.1" "go run . 4 1 2>&1 | tee \"$NODE_1_LOG\"" Enter \; \ + send-keys -t "demo:0.2" "go run . 4 2 2>&1 | tee \"$NODE_2_LOG\"" Enter \; \ + send-keys -t "demo:0.3" "go run . 4 3 2>&1 | tee \"$NODE_3_LOG\"" Enter \; \ + attach-session -t "demo:0.0" +#!/usr/bin/env bash +set -e From 03872ac25e6fdb8e246b5c57f012d7950ab5500c Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sun, 14 Jan 2024 15:22:46 +0100 Subject: [PATCH 13/46] better init, drop app local state, some cleanup --- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go | 276 ++++++++++++------ pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go | 1 + pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go | 4 + pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go | 6 + .../blockchainpb/bcmpb/events/events.mir.go | 15 + .../bcmpb/oneof_interfaces.mir.go | 4 + pkg/pb/blockchainpb/bcmpb/types/types.mir.go | 57 ++++ protos/blockchainpb/bcmpb/bcmpb.proto | 10 +- samples/blockchain/application/application.go | 56 ++-- samples/blockchain/application/config.go | 7 - .../blockchain/application/config/config.go | 12 + .../application/transactions/transactions.go | 19 +- samples/blockchain/bcm.go | 85 ++++-- 13 files changed, 374 insertions(+), 178 deletions(-) delete mode 100644 samples/blockchain/application/config.go create mode 100644 samples/blockchain/application/config/config.go diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go index 074fbfa48..b66933e41 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go @@ -39,6 +39,7 @@ type Event struct { // *Event_GetHeadToCheckpointChainRequest // *Event_GetHeadToCheckpointChainResponse // *Event_RegisterCheckpoint + // *Event_InitBlockchain Type isEvent_Type `protobuf_oneof:"type"` } @@ -144,6 +145,13 @@ func (x *Event) GetRegisterCheckpoint() *RegisterCheckpoint { return nil } +func (x *Event) GetInitBlockchain() *InitBlockchain { + if x, ok := x.GetType().(*Event_InitBlockchain); ok { + return x.InitBlockchain + } + return nil +} + type isEvent_Type interface { isEvent_Type() } @@ -184,6 +192,10 @@ type Event_RegisterCheckpoint struct { RegisterCheckpoint *RegisterCheckpoint `protobuf:"bytes,9,opt,name=register_checkpoint,json=registerCheckpoint,proto3,oneof"` } +type Event_InitBlockchain struct { + InitBlockchain *InitBlockchain `protobuf:"bytes,100,opt,name=init_blockchain,json=initBlockchain,proto3,oneof"` +} + func (*Event_NewBlock) isEvent_Type() {} func (*Event_NewChain) isEvent_Type() {} @@ -202,6 +214,8 @@ func (*Event_GetHeadToCheckpointChainResponse) isEvent_Type() {} func (*Event_RegisterCheckpoint) isEvent_Type() {} +func (*Event_InitBlockchain) isEvent_Type() {} + type NewBlock struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -722,6 +736,53 @@ func (x *RegisterCheckpoint) GetState() *statepb.State { return nil } +type InitBlockchain struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + InitialState *statepb.State `protobuf:"bytes,1,opt,name=initial_state,json=initialState,proto3" json:"initial_state,omitempty"` +} + +func (x *InitBlockchain) Reset() { + *x = InitBlockchain{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *InitBlockchain) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InitBlockchain) ProtoMessage() {} + +func (x *InitBlockchain) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[10] + 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 InitBlockchain.ProtoReflect.Descriptor instead. +func (*InitBlockchain) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{10} +} + +func (x *InitBlockchain) GetInitialState() *statepb.State { + if x != nil { + return x.InitialState + } + return nil +} + var File_blockchainpb_bcmpb_bcmpb_proto protoreflect.FileDescriptor var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ @@ -733,7 +794,7 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdc, 0x05, 0x0a, 0x05, 0x45, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9e, 0x06, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, @@ -778,85 +839,94 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x12, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x77, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, - 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, - 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xae, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, - 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, - 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, - 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, - 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x78, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, - 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, - 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, - 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, - 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, - 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, + 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, + 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, + 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xae, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x78, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, + 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, + 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, - 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, - 0x22, 0xa3, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, - 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, - 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7a, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, - 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x05, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x74, - 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x98, 0xa6, - 0x1d, 0x01, 0x22, 0x5b, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, - 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, - 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, - 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, - 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, + 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, + 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, + 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, + 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x73, 0x3a, 0x04, + 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x22, 0xa3, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, + 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, + 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, + 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7a, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x48, + 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x05, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, + 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5b, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, + 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, + 0x01, 0x22, 0x4b, 0x0a, 0x0e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x0d, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x69, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, + 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, + 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, + 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -871,7 +941,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP() []byte { return file_blockchainpb_bcmpb_bcmpb_proto_rawDescData } -var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 10) +var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 11) var file_blockchainpb_bcmpb_bcmpb_proto_goTypes = []interface{}{ (*Event)(nil), // 0: bcmpb.Event (*NewBlock)(nil), // 1: bcmpb.NewBlock @@ -883,9 +953,10 @@ var file_blockchainpb_bcmpb_bcmpb_proto_goTypes = []interface{}{ (*GetHeadToCheckpointChainRequest)(nil), // 7: bcmpb.GetHeadToCheckpointChainRequest (*GetHeadToCheckpointChainResponse)(nil), // 8: bcmpb.GetHeadToCheckpointChainResponse (*RegisterCheckpoint)(nil), // 9: bcmpb.RegisterCheckpoint - (*blockchainpb.Block)(nil), // 10: blockchainpb.Block - (*blockchainpb.BlockInternal)(nil), // 11: blockchainpb.BlockInternal - (*statepb.State)(nil), // 12: statepb.State + (*InitBlockchain)(nil), // 10: bcmpb.InitBlockchain + (*blockchainpb.Block)(nil), // 11: blockchainpb.Block + (*blockchainpb.BlockInternal)(nil), // 12: blockchainpb.BlockInternal + (*statepb.State)(nil), // 13: statepb.State } var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ 1, // 0: bcmpb.Event.new_block:type_name -> bcmpb.NewBlock @@ -897,17 +968,19 @@ var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ 7, // 6: bcmpb.Event.get_head_to_checkpoint_chain_request:type_name -> bcmpb.GetHeadToCheckpointChainRequest 8, // 7: bcmpb.Event.get_head_to_checkpoint_chain_response:type_name -> bcmpb.GetHeadToCheckpointChainResponse 9, // 8: bcmpb.Event.register_checkpoint:type_name -> bcmpb.RegisterCheckpoint - 10, // 9: bcmpb.NewBlock.block:type_name -> blockchainpb.Block - 10, // 10: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block - 10, // 11: bcmpb.GetBlockResponse.block:type_name -> blockchainpb.Block - 10, // 12: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block - 11, // 13: bcmpb.GetHeadToCheckpointChainResponse.chain:type_name -> blockchainpb.BlockInternal - 12, // 14: bcmpb.RegisterCheckpoint.state:type_name -> statepb.State - 15, // [15:15] is the sub-list for method output_type - 15, // [15:15] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name + 10, // 9: bcmpb.Event.init_blockchain:type_name -> bcmpb.InitBlockchain + 11, // 10: bcmpb.NewBlock.block:type_name -> blockchainpb.Block + 11, // 11: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block + 11, // 12: bcmpb.GetBlockResponse.block:type_name -> blockchainpb.Block + 11, // 13: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block + 12, // 14: bcmpb.GetHeadToCheckpointChainResponse.chain:type_name -> blockchainpb.BlockInternal + 13, // 15: bcmpb.RegisterCheckpoint.state:type_name -> statepb.State + 13, // 16: bcmpb.InitBlockchain.initial_state:type_name -> statepb.State + 17, // [17:17] is the sub-list for method output_type + 17, // [17:17] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_blockchainpb_bcmpb_bcmpb_proto_init() } @@ -1036,6 +1109,18 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { return nil } } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*InitBlockchain); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_NewBlock)(nil), @@ -1047,6 +1132,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { (*Event_GetHeadToCheckpointChainRequest)(nil), (*Event_GetHeadToCheckpointChainResponse)(nil), (*Event_RegisterCheckpoint)(nil), + (*Event_InitBlockchain)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -1054,7 +1140,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_bcmpb_bcmpb_proto_rawDesc, NumEnums: 0, - NumMessages: 10, + NumMessages: 11, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go index b5c31b8fa..2977f948d 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go @@ -17,5 +17,6 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_GetHeadToCheckpointChainRequest)(nil)), reflect.TypeOf((*Event_GetHeadToCheckpointChainResponse)(nil)), reflect.TypeOf((*Event_RegisterCheckpoint)(nil)), + reflect.TypeOf((*Event_InitBlockchain)(nil)), } } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go index 3fad09bf3..dc4a02176 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go @@ -47,3 +47,7 @@ func GetHeadToCheckpointChainResponse(m dsl.Module, destModule types.ModuleID, r func RegisterCheckpoint(m dsl.Module, destModule types.ModuleID, blockId uint64, state *statepb.State) { dsl.EmitMirEvent(m, events.RegisterCheckpoint(destModule, blockId, state)) } + +func InitBlockchain(m dsl.Module, destModule types.ModuleID, initialState *statepb.State) { + dsl.EmitMirEvent(m, events.InitBlockchain(destModule, initialState)) +} diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go index 48c47ca0b..6c49fe51e 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go @@ -77,3 +77,9 @@ func UponRegisterCheckpoint(m dsl.Module, handler func(blockId uint64, state *st return handler(ev.BlockId, ev.State) }) } + +func UponInitBlockchain(m dsl.Module, handler func(initialState *statepb.State) error) { + UponEvent[*types.Event_InitBlockchain](m, func(ev *types.InitBlockchain) error { + return handler(ev.InitialState) + }) +} diff --git a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go index 4e03edff8..ad3777a67 100644 --- a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go @@ -156,3 +156,18 @@ func RegisterCheckpoint(destModule types.ModuleID, blockId uint64, state *statep }, } } + +func InitBlockchain(destModule types.ModuleID, initialState *statepb.State) *types1.Event { + return &types1.Event{ + DestModule: destModule, + Type: &types1.Event_Bcm{ + Bcm: &types2.Event{ + Type: &types2.Event_InitBlockchain{ + InitBlockchain: &types2.InitBlockchain{ + InitialState: initialState, + }, + }, + }, + }, + } +} diff --git a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go index b4ba75b65..eeaf28733 100644 --- a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go @@ -44,3 +44,7 @@ func (w *Event_GetHeadToCheckpointChainResponse) Unwrap() *GetHeadToCheckpointCh func (w *Event_RegisterCheckpoint) Unwrap() *RegisterCheckpoint { return w.RegisterCheckpoint } + +func (w *Event_InitBlockchain) Unwrap() *InitBlockchain { + return w.InitBlockchain +} diff --git a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go index 0f9c01228..db0c289fb 100644 --- a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go @@ -49,6 +49,8 @@ func Event_TypeFromPb(pb bcmpb.Event_Type) Event_Type { return &Event_GetHeadToCheckpointChainResponse{GetHeadToCheckpointChainResponse: GetHeadToCheckpointChainResponseFromPb(pb.GetHeadToCheckpointChainResponse)} case *bcmpb.Event_RegisterCheckpoint: return &Event_RegisterCheckpoint{RegisterCheckpoint: RegisterCheckpointFromPb(pb.RegisterCheckpoint)} + case *bcmpb.Event_InitBlockchain: + return &Event_InitBlockchain{InitBlockchain: InitBlockchainFromPb(pb.InitBlockchain)} } return nil } @@ -269,6 +271,30 @@ func (*Event_RegisterCheckpoint) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_RegisterCheckpoint]()} } +type Event_InitBlockchain struct { + InitBlockchain *InitBlockchain +} + +func (*Event_InitBlockchain) isEvent_Type() {} + +func (w *Event_InitBlockchain) Unwrap() *InitBlockchain { + return w.InitBlockchain +} + +func (w *Event_InitBlockchain) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.InitBlockchain == nil { + return &bcmpb.Event_InitBlockchain{} + } + return &bcmpb.Event_InitBlockchain{InitBlockchain: (w.InitBlockchain).Pb()} +} + +func (*Event_InitBlockchain) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_InitBlockchain]()} +} + func EventFromPb(pb *bcmpb.Event) *Event { if pb == nil { return nil @@ -598,3 +624,34 @@ func (m *RegisterCheckpoint) Pb() *bcmpb.RegisterCheckpoint { func (*RegisterCheckpoint) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.RegisterCheckpoint]()} } + +type InitBlockchain struct { + InitialState *statepb.State +} + +func InitBlockchainFromPb(pb *bcmpb.InitBlockchain) *InitBlockchain { + if pb == nil { + return nil + } + return &InitBlockchain{ + InitialState: pb.InitialState, + } +} + +func (m *InitBlockchain) Pb() *bcmpb.InitBlockchain { + if m == nil { + return nil + } + pbMessage := &bcmpb.InitBlockchain{} + { + if m.InitialState != nil { + pbMessage.InitialState = m.InitialState + } + } + + return pbMessage +} + +func (*InitBlockchain) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.InitBlockchain]()} +} diff --git a/protos/blockchainpb/bcmpb/bcmpb.proto b/protos/blockchainpb/bcmpb/bcmpb.proto index 05cf8fe35..d1c5dad0b 100644 --- a/protos/blockchainpb/bcmpb/bcmpb.proto +++ b/protos/blockchainpb/bcmpb/bcmpb.proto @@ -25,6 +25,8 @@ message Event { GetHeadToCheckpointChainRequest get_head_to_checkpoint_chain_request = 7; GetHeadToCheckpointChainResponse get_head_to_checkpoint_chain_response = 8; RegisterCheckpoint register_checkpoint = 9; + + InitBlockchain init_blockchain = 100; } } @@ -95,4 +97,10 @@ message RegisterCheckpoint { uint64 block_id = 1; statepb.State state = 2; -} \ No newline at end of file +} + +message InitBlockchain { + option (mir.event) = true; + + statepb.State initial_state = 1; +} diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index ae9fa714c..e9573f4d9 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -15,27 +15,25 @@ import ( interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + "github.com/filecoin-project/mir/samples/blockchain/application/config" "github.com/filecoin-project/mir/samples/blockchain/application/transactions" "github.com/filecoin-project/mir/samples/blockchain/utils" ) -type localState struct { - head uint64 - state *statepb.State -} - type ApplicationModule struct { - m *dsl.Module - currentState *localState - logger logging.Logger - tm *transactions.TransactionManager - name string - openPayloadRequest bool // request id + m *dsl.Module + logger logging.Logger + tm *transactions.TransactionManager + name string } // application-application events func applyBlockToState(state *statepb.State, block *blockchainpb.Block) *statepb.State { + // empty block, just skip + if block.Payload.Message == "" { + return state + } return &statepb.State{ MessageHistory: append(state.MessageHistory, block.Payload.Message), } @@ -66,10 +64,6 @@ func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain *blockcha blockId := addedChain.GetBlocks()[len(addedChain.GetBlocks())-1].BlockId bcmpbdsl.RegisterCheckpoint(*am.m, "bcm", blockId, state) interceptorpbdsl.AppUpdate(*am.m, "devnull", state) - am.currentState = &localState{ - head: blockId, - state: state, - } // print state fmt.Printf("=== STATE ===\n") @@ -83,42 +77,30 @@ func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain *blockcha // transaction management events -func (am *ApplicationModule) providePayload() error { +func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { + am.logger.Log(logging.LevelDebug, "Processing payload request", "headId", utils.FormatBlockId(head_id)) + payload := am.tm.GetPayload() - if payload == nil { - // no payloads to provide, will respond as soon as new paylod is available - am.openPayloadRequest = true // set flag s.t. payload response will be sent as soon as there is a payload available - return nil - } - applicationpbdsl.PayloadResponse(*am.m, "miner", am.currentState.head, payload) // not using head id anywhere so we can get rid of it - am.openPayloadRequest = false + applicationpbdsl.PayloadResponse(*am.m, "miner", head_id, payload) // not using head id anywhere so we can get rid of it return nil } -func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { - // NOTE: reject request for head that doesn't match current head? - am.logger.Log(logging.LevelDebug, "Processing payload request", "headId", utils.FormatBlockId(head_id)) - - return am.providePayload() -} - func NewApplication(logger logging.Logger, name string) modules.PassiveModule { m := dsl.NewModule("application") am := &ApplicationModule{ - m: &m, - currentState: &localState{ - head: 0, - state: InitialState, - }, + m: &m, logger: logger, name: name, tm: transactions.New(name), } dsl.UponInit(m, func() error { + // init blockchain + bcmpbdsl.InitBlockchain(*am.m, "bcm", config.InitialState) + return nil }) @@ -131,10 +113,6 @@ func NewApplication(logger logging.Logger, name string) modules.PassiveModule { Timestamp: time.Now().Unix(), }) - if am.openPayloadRequest { - return am.providePayload() - } - return nil }) diff --git a/samples/blockchain/application/config.go b/samples/blockchain/application/config.go deleted file mode 100644 index a2459d32e..000000000 --- a/samples/blockchain/application/config.go +++ /dev/null @@ -1,7 +0,0 @@ -package application - -import "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" - -var InitialState = &statepb.State{ - MessageHistory: []string{}, -} diff --git a/samples/blockchain/application/config/config.go b/samples/blockchain/application/config/config.go new file mode 100644 index 000000000..3cd4eeff6 --- /dev/null +++ b/samples/blockchain/application/config/config.go @@ -0,0 +1,12 @@ +package config + +import ( + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" +) + +var InitialState = &statepb.State{ + MessageHistory: []string{}, +} + +var EmptyPayload = &payloadpb.Payload{} diff --git a/samples/blockchain/application/transactions/transactions.go b/samples/blockchain/application/transactions/transactions.go index 1be5696cb..50ae7286e 100644 --- a/samples/blockchain/application/transactions/transactions.go +++ b/samples/blockchain/application/transactions/transactions.go @@ -5,6 +5,7 @@ import ( "slices" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + "github.com/filecoin-project/mir/samples/blockchain/application/config" "github.com/mitchellh/hashstructure" ) @@ -34,23 +35,7 @@ func New(name string) *TransactionManager { func (tm *TransactionManager) GetPayload() *payloadpb.Payload { // return oldest transaction, where timestamp is a field in the payload if len(tm.transactions) == 0 { - return nil - // // TODO: this only makes sense for the test - remove it - // payload := &payloadpb.Payload{ - // Message: fmt.Sprintf("%s: %d", tm.name, tm.ownTransactionCounter), - // Timestamp: time.Now().UnixNano(), - // } - // hash, err := hashstructure.Hash(payload, nil) - // if err != nil { - // // just panicing - only for testing anyways... - // panic(fmt.Errorf("error hashing payload: %w", err)) - // } - // tm.transactions = append(tm.transactions, transaction{ - // hash: hash, - // payload: payload, - // }) - // tm.ownTransactionCounter++ - // return payload + return config.EmptyPayload } // sort by timestamp // TODO: keep it sorted diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 9cd66be81..5a99ae210 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -17,7 +17,7 @@ import ( "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" t "github.com/filecoin-project/mir/pkg/types" - "github.com/filecoin-project/mir/samples/blockchain/application" + "github.com/filecoin-project/mir/samples/blockchain/application/config" "github.com/filecoin-project/mir/samples/blockchain/utils" "golang.org/x/exp/maps" ) @@ -26,6 +26,7 @@ var ( ErrParentNotFound = errors.New("parent not found") ErrNodeAlreadyInTree = errors.New("node already in tree") ErrRollback = errors.New("rollback - should never happen") + ErrUninitialized = errors.New("uninitialized - the application must initialize the blockchain first by sending it a initBlockchain message") ) type bcmBlock struct { @@ -45,6 +46,16 @@ type bcmModule struct { blockCount uint64 currentScanCount uint64 logger logging.Logger + initialized bool +} + +func (bcm *bcmModule) checkInitialization() error { + if !bcm.initialized { + bcm.logger.Log(logging.LevelError, ErrUninitialized.Error()) + return ErrUninitialized + } + + return nil } func (bcm *bcmModule) handleNewHeadSideEffects(newHead, oldHead *bcmBlock) { @@ -197,7 +208,7 @@ func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) // TODO: might need to add some "cleanup" to handle very old leaves in case this grows too much func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { - bcm.logger.Log(logging.LevelInfo, "Adding block...", "blockId", utils.FormatBlockId(block.BlockId)) + bcm.logger.Log(logging.LevelInfo, "Adding block...", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(block.PreviousBlockId)) // check if block is already in the leaves, reject if so if _, ok := bcm.leaves[block.BlockId]; ok { @@ -231,6 +242,7 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { if hit := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { // check if curr matches the block to be added - if so, ignore + bcm.logger.Log(logging.LevelDebug, "Traversing - checking block", "blockId", utils.FormatBlockId(block.BlockId), "currBlockId", utils.FormatBlockId(currBlock.block.Block.BlockId)) if currBlock.block.Block.BlockId == block.BlockId { bcm.logger.Log(logging.LevelDebug, "Block already in tree", "blockId", utils.FormatBlockId(block.BlockId)) return true, ErrNodeAlreadyInTree @@ -372,6 +384,10 @@ func (bcm *bcmModule) sendTreeUpdate() { } func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, sourceModule t.ModuleID) error { + if err := bcm.checkInitialization(); err != nil { + return err + } + chain := make([]*blockchainpb.BlockInternal, 0) // start with head @@ -402,7 +418,11 @@ func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, so func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepb.State) error { bcm.logger.Log(logging.LevelInfo, "Received register checkpoint", "blockId", utils.FormatBlockId(block_id)) + if err := bcm.checkInitialization(); err != nil { + return err + } if _, ok := bcm.checkpoints[block_id]; ok { + bcm.logger.Log(logging.LevelDebug, "Checkpoint already registered - ignore", "blockId", utils.FormatBlockId(block_id)) return nil } @@ -421,8 +441,8 @@ func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepb.S return nil } -func NewBCM(logger logging.Logger) modules.PassiveModule { - m := dsl.NewModule("bcm") +func (bcm *bcmModule) handleInitBlockchain(initialState *statepb.State) error { + // initialize blockchain // making up a genisis block genesis := &blockchainpb.Block{ @@ -432,57 +452,79 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { Timestamp: 0, // unix 0 } - hash := uint64(0) // utils.HashBlock(genesis) + hash := utils.HashBlock(genesis) //uint64(0) genesis.BlockId = hash genesisBcm := bcmBlock{ block: &blockchainpb.BlockInternal{ Block: genesis, - State: application.InitialState, + State: config.InitialState, }, parent: nil, depth: 0, } + bcm.head = &genesisBcm + bcm.genesis = &genesisBcm + + bcm.blocks = []bcmBlock{genesisBcm} + bcm.leaves[hash] = &genesisBcm + bcm.checkpoints[hash] = &genesisBcm + + bcm.initialized = true + + // initialized, start operations + + bcm.logger.Log(logging.LevelInfo, "Initialized blockchain") + + applicationpbdsl.NewHead(*bcm.m, "application", hash) + minerpbdsl.NewHead(*bcm.m, "miner", hash) + + return nil +} + +func NewBCM(logger logging.Logger) modules.PassiveModule { + m := dsl.NewModule("bcm") + // init bcm genisis block bcm := bcmModule{ m: &m, - blocks: []bcmBlock{genesisBcm}, + blocks: nil, // will be initialized in handleInitBlockchain, triggered by application leaves: make(map[uint64]*bcmBlock), - head: &genesisBcm, - genesis: &genesisBcm, + head: nil, // will be initialized in handleInitBlockchain, triggered by application + genesis: nil, // will be initialized in handleInitBlockchain, triggered by application checkpoints: make(map[uint64]*bcmBlock), blockCount: 1, currentScanCount: 0, logger: logger, + initialized: false, } - // add genesis to leaves and checkpoints - bcm.leaves[hash] = bcm.head - bcm.checkpoints[hash] = bcm.head - dsl.UponInit(m, func() error { - - // start with genesis block - applicationpbdsl.NewHead(m, "application", hash) - // start mining on genesis block - minerpbdsl.NewHead(m, "miner", hash) - return nil }) bcmpbdsl.UponNewBlock(m, func(block *blockchainpb.Block) error { + if err := bcm.checkInitialization(); err != nil { + return err + } bcm.handleNewBlock(block) return nil }) bcmpbdsl.UponNewChain(m, func(blocks []*blockchainpb.Block) error { bcm.logger.Log(logging.LevelInfo, "Received chain from synchronizer") + if err := bcm.checkInitialization(); err != nil { + return err + } bcm.handleNewChain(blocks) return nil }) bcmpbdsl.UponGetBlockRequest(m, func(requestID string, sourceModule t.ModuleID, blockID uint64) error { bcm.logger.Log(logging.LevelInfo, "Received get block request", "requestId", requestID, "sourceModule", sourceModule) + if err := bcm.checkInitialization(); err != nil { + return err + } // check if block is in tree hit := bcm.findBlock(blockID) @@ -500,6 +542,9 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { bcmpbdsl.UponGetChainRequest(m, func(requestID string, sourceModule t.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error { bcm.logger.Log(logging.LevelInfo, "Received get chain request", "requestId", requestID, "sourceModule", sourceModule, "endBlockId", utils.FormatBlockId(endBlockId), "sourceBlockIds", utils.FormatBlockIdSlice(sourceBlockIds)) + if err := bcm.checkInitialization(); err != nil { + return err + } chain := make([]*blockchainpb.Block, 0) // for easier lookup... sourceBlockIdsMap := make(map[uint64]uint64) @@ -547,5 +592,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { bcmpbdsl.UponGetHeadToCheckpointChainRequest(m, bcm.handleGetHeadToCheckpointChainRequest) bcmpbdsl.UponRegisterCheckpoint(m, bcm.handleRegisterCheckpoint) + bcmpbdsl.UponInitBlockchain(m, bcm.handleInitBlockchain) + return m } From d4410e940d4a4e597dc9f72385066a22174ba7f6 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Mon, 15 Jan 2024 22:22:40 +0100 Subject: [PATCH 14/46] make mir type structs, add timestamp sender to payload, todo - check blocks for ordering --- .../applicationpb/dsl/emit.mir.go | 12 +- .../applicationpb/dsl/upon.mir.go | 12 +- .../applicationpb/events/events.mir.go | 12 +- .../applicationpb/types/types.mir.go | 36 ++-- pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go | 18 +- pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go | 26 +-- .../blockchainpb/bcmpb/events/events.mir.go | 128 +++++++-------- pkg/pb/blockchainpb/bcmpb/types/types.mir.go | 72 ++++---- pkg/pb/blockchainpb/blockchainpb.pb.go | 62 +++---- .../communicationpb/dsl/emit.mir.go | 4 +- .../communicationpb/dsl/messages.mir.go | 4 +- .../communicationpb/dsl/upon.mir.go | 4 +- .../communicationpb/events/events.mir.go | 18 +- .../communicationpb/msgs/msgs.mir.go | 18 +- .../communicationpb/types/types.mir.go | 14 +- .../interceptorpb/dsl/emit.mir.go | 10 +- .../interceptorpb/dsl/upon.mir.go | 10 +- .../interceptorpb/events/events.mir.go | 44 ++--- .../interceptorpb/types/types.mir.go | 22 +-- pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go | 4 +- pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go | 4 +- .../blockchainpb/minerpb/events/events.mir.go | 30 ++-- .../blockchainpb/minerpb/types/types.mir.go | 8 +- pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go | 35 +++- .../blockchainpb/payloadpb/types/types.mir.go | 42 +++++ pkg/pb/blockchainpb/statepb/statepb.pb.go | 130 +++++++++++++-- .../blockchainpb/statepb/types/types.mir.go | 76 +++++++++ .../synchronizerpb/dsl/emit.mir.go | 4 +- .../synchronizerpb/dsl/messages.mir.go | 4 +- .../synchronizerpb/dsl/upon.mir.go | 4 +- .../synchronizerpb/events/events.mir.go | 18 +- .../synchronizerpb/msgs/msgs.mir.go | 4 +- .../synchronizerpb/types/types.mir.go | 18 +- pkg/pb/blockchainpb/types/types.mir.go | 154 ++++++++++++++++++ protos/blockchainpb/blockchainpb.proto | 10 ++ protos/blockchainpb/payloadpb/payloadpb.proto | 5 + protos/blockchainpb/statepb/statepb.proto | 14 +- .../proto_converter_tmp_83059537/generator.go | 40 +++++ samples/blockchain/application/application.go | 69 ++++++-- .../blockchain/application/config/config.go | 10 +- .../application/transactions/transactions.go | 23 +-- samples/blockchain/bcm.go | 72 ++++---- samples/blockchain/communication.go | 6 +- samples/blockchain/main.go | 2 +- samples/blockchain/miner.go | 12 +- samples/blockchain/synchronizer.go | 10 +- samples/blockchain/utils/hash.go | 6 +- 47 files changed, 920 insertions(+), 420 deletions(-) create mode 100644 protos/proto_converter_tmp_83059537/generator.go diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go index 306b6ef06..8b56a037d 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go @@ -4,10 +4,10 @@ package applicationpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/events" - payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types "github.com/filecoin-project/mir/pkg/types" ) @@ -17,7 +17,7 @@ func NewHead(m dsl.Module, destModule types.ModuleID, headId uint64) { dsl.EmitMirEvent(m, events.NewHead(destModule, headId)) } -func VerifyBlockRequest(m dsl.Module, destModule types.ModuleID, requestId uint64, block *blockchainpb.Block) { +func VerifyBlockRequest(m dsl.Module, destModule types.ModuleID, requestId uint64, block *types1.Block) { dsl.EmitMirEvent(m, events.VerifyBlockRequest(destModule, requestId, block)) } @@ -29,11 +29,11 @@ func PayloadRequest(m dsl.Module, destModule types.ModuleID, headId uint64) { dsl.EmitMirEvent(m, events.PayloadRequest(destModule, headId)) } -func PayloadResponse(m dsl.Module, destModule types.ModuleID, headId uint64, payload *payloadpb.Payload) { +func PayloadResponse(m dsl.Module, destModule types.ModuleID, headId uint64, payload *types2.Payload) { dsl.EmitMirEvent(m, events.PayloadResponse(destModule, headId, payload)) } -func ForkUpdate(m dsl.Module, destModule types.ModuleID, removedChain *blockchainpb.Blockchain, addedChain *blockchainpb.Blockchain, forkState *statepb.State) { +func ForkUpdate(m dsl.Module, destModule types.ModuleID, removedChain *types1.Blockchain, addedChain *types1.Blockchain, forkState *types3.State) { dsl.EmitMirEvent(m, events.ForkUpdate(destModule, removedChain, addedChain, forkState)) } diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go index 49942909c..edb886702 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go @@ -4,10 +4,10 @@ package applicationpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" - payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types4 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" ) @@ -30,7 +30,7 @@ func UponNewHead(m dsl.Module, handler func(headId uint64) error) { }) } -func UponVerifyBlockRequest(m dsl.Module, handler func(requestId uint64, block *blockchainpb.Block) error) { +func UponVerifyBlockRequest(m dsl.Module, handler func(requestId uint64, block *types2.Block) error) { UponEvent[*types.Event_VerifyBlockRequest](m, func(ev *types.VerifyBlockRequest) error { return handler(ev.RequestId, ev.Block) }) @@ -48,13 +48,13 @@ func UponPayloadRequest(m dsl.Module, handler func(headId uint64) error) { }) } -func UponPayloadResponse(m dsl.Module, handler func(headId uint64, payload *payloadpb.Payload) error) { +func UponPayloadResponse(m dsl.Module, handler func(headId uint64, payload *types3.Payload) error) { UponEvent[*types.Event_PayloadResponse](m, func(ev *types.PayloadResponse) error { return handler(ev.HeadId, ev.Payload) }) } -func UponForkUpdate(m dsl.Module, handler func(removedChain *blockchainpb.Blockchain, addedChain *blockchainpb.Blockchain, forkState *statepb.State) error) { +func UponForkUpdate(m dsl.Module, handler func(removedChain *types2.Blockchain, addedChain *types2.Blockchain, forkState *types4.State) error) { UponEvent[*types.Event_ForkUpdate](m, func(ev *types.ForkUpdate) error { return handler(ev.RemovedChain, ev.AddedChain, ev.ForkState) }) diff --git a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go index a68c6efc7..8f5f1fc76 100644 --- a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go @@ -3,10 +3,10 @@ package applicationpbevents import ( - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" - payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + types4 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types5 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) @@ -26,7 +26,7 @@ func NewHead(destModule types.ModuleID, headId uint64) *types1.Event { } } -func VerifyBlockRequest(destModule types.ModuleID, requestId uint64, block *blockchainpb.Block) *types1.Event { +func VerifyBlockRequest(destModule types.ModuleID, requestId uint64, block *types3.Block) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Application{ @@ -73,7 +73,7 @@ func PayloadRequest(destModule types.ModuleID, headId uint64) *types1.Event { } } -func PayloadResponse(destModule types.ModuleID, headId uint64, payload *payloadpb.Payload) *types1.Event { +func PayloadResponse(destModule types.ModuleID, headId uint64, payload *types4.Payload) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Application{ @@ -89,7 +89,7 @@ func PayloadResponse(destModule types.ModuleID, headId uint64, payload *payloadp } } -func ForkUpdate(destModule types.ModuleID, removedChain *blockchainpb.Blockchain, addedChain *blockchainpb.Blockchain, forkState *statepb.State) *types1.Event { +func ForkUpdate(destModule types.ModuleID, removedChain *types3.Blockchain, addedChain *types3.Blockchain, forkState *types5.State) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Application{ diff --git a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go index 8ebd67daf..488857e99 100644 --- a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go @@ -4,10 +4,10 @@ package applicationpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" applicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" - payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -275,7 +275,7 @@ func (*NewHead) MirReflect() mirreflect.Type { type VerifyBlockRequest struct { RequestId uint64 - Block *blockchainpb.Block + Block *types.Block } func VerifyBlockRequestFromPb(pb *applicationpb.VerifyBlockRequest) *VerifyBlockRequest { @@ -284,7 +284,7 @@ func VerifyBlockRequestFromPb(pb *applicationpb.VerifyBlockRequest) *VerifyBlock } return &VerifyBlockRequest{ RequestId: pb.RequestId, - Block: pb.Block, + Block: types.BlockFromPb(pb.Block), } } @@ -296,7 +296,7 @@ func (m *VerifyBlockRequest) Pb() *applicationpb.VerifyBlockRequest { { pbMessage.RequestId = m.RequestId if m.Block != nil { - pbMessage.Block = m.Block + pbMessage.Block = (m.Block).Pb() } } @@ -340,9 +340,9 @@ func (*VerifyBlockResponse) MirReflect() mirreflect.Type { } type ForkUpdate struct { - RemovedChain *blockchainpb.Blockchain - AddedChain *blockchainpb.Blockchain - ForkState *statepb.State + RemovedChain *types.Blockchain + AddedChain *types.Blockchain + ForkState *types1.State } func ForkUpdateFromPb(pb *applicationpb.ForkUpdate) *ForkUpdate { @@ -350,9 +350,9 @@ func ForkUpdateFromPb(pb *applicationpb.ForkUpdate) *ForkUpdate { return nil } return &ForkUpdate{ - RemovedChain: pb.RemovedChain, - AddedChain: pb.AddedChain, - ForkState: pb.ForkState, + RemovedChain: types.BlockchainFromPb(pb.RemovedChain), + AddedChain: types.BlockchainFromPb(pb.AddedChain), + ForkState: types1.StateFromPb(pb.ForkState), } } @@ -363,13 +363,13 @@ func (m *ForkUpdate) Pb() *applicationpb.ForkUpdate { pbMessage := &applicationpb.ForkUpdate{} { if m.RemovedChain != nil { - pbMessage.RemovedChain = m.RemovedChain + pbMessage.RemovedChain = (m.RemovedChain).Pb() } if m.AddedChain != nil { - pbMessage.AddedChain = m.AddedChain + pbMessage.AddedChain = (m.AddedChain).Pb() } if m.ForkState != nil { - pbMessage.ForkState = m.ForkState + pbMessage.ForkState = (m.ForkState).Pb() } } @@ -411,7 +411,7 @@ func (*PayloadRequest) MirReflect() mirreflect.Type { type PayloadResponse struct { HeadId uint64 - Payload *payloadpb.Payload + Payload *types2.Payload } func PayloadResponseFromPb(pb *applicationpb.PayloadResponse) *PayloadResponse { @@ -420,7 +420,7 @@ func PayloadResponseFromPb(pb *applicationpb.PayloadResponse) *PayloadResponse { } return &PayloadResponse{ HeadId: pb.HeadId, - Payload: pb.Payload, + Payload: types2.PayloadFromPb(pb.Payload), } } @@ -432,7 +432,7 @@ func (m *PayloadResponse) Pb() *applicationpb.PayloadResponse { { pbMessage.HeadId = m.HeadId if m.Payload != nil { - pbMessage.Payload = m.Payload + pbMessage.Payload = (m.Payload).Pb() } } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go index dc4a02176..37633b6c0 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go @@ -4,19 +4,19 @@ package bcmpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/events" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types "github.com/filecoin-project/mir/pkg/types" ) // Module-specific dsl functions for emitting events. -func NewBlock(m dsl.Module, destModule types.ModuleID, block *blockchainpb.Block) { +func NewBlock(m dsl.Module, destModule types.ModuleID, block *types1.Block) { dsl.EmitMirEvent(m, events.NewBlock(destModule, block)) } -func NewChain(m dsl.Module, destModule types.ModuleID, blocks []*blockchainpb.Block) { +func NewChain(m dsl.Module, destModule types.ModuleID, blocks []*types1.Block) { dsl.EmitMirEvent(m, events.NewChain(destModule, blocks)) } @@ -24,7 +24,7 @@ func GetBlockRequest(m dsl.Module, destModule types.ModuleID, requestId string, dsl.EmitMirEvent(m, events.GetBlockRequest(destModule, requestId, sourceModule, blockId)) } -func GetBlockResponse(m dsl.Module, destModule types.ModuleID, requestId string, found bool, block *blockchainpb.Block) { +func GetBlockResponse(m dsl.Module, destModule types.ModuleID, requestId string, found bool, block *types1.Block) { dsl.EmitMirEvent(m, events.GetBlockResponse(destModule, requestId, found, block)) } @@ -32,7 +32,7 @@ func GetChainRequest(m dsl.Module, destModule types.ModuleID, requestId string, dsl.EmitMirEvent(m, events.GetChainRequest(destModule, requestId, sourceModule, endBlockId, sourceBlockIds)) } -func GetChainResponse(m dsl.Module, destModule types.ModuleID, requestId string, success bool, chain []*blockchainpb.Block) { +func GetChainResponse(m dsl.Module, destModule types.ModuleID, requestId string, success bool, chain []*types1.Block) { dsl.EmitMirEvent(m, events.GetChainResponse(destModule, requestId, success, chain)) } @@ -40,14 +40,14 @@ func GetHeadToCheckpointChainRequest(m dsl.Module, destModule types.ModuleID, re dsl.EmitMirEvent(m, events.GetHeadToCheckpointChainRequest(destModule, requestId, sourceModule)) } -func GetHeadToCheckpointChainResponse(m dsl.Module, destModule types.ModuleID, requestId string, chain []*blockchainpb.BlockInternal) { +func GetHeadToCheckpointChainResponse(m dsl.Module, destModule types.ModuleID, requestId string, chain []*types1.BlockInternal) { dsl.EmitMirEvent(m, events.GetHeadToCheckpointChainResponse(destModule, requestId, chain)) } -func RegisterCheckpoint(m dsl.Module, destModule types.ModuleID, blockId uint64, state *statepb.State) { +func RegisterCheckpoint(m dsl.Module, destModule types.ModuleID, blockId uint64, state *types2.State) { dsl.EmitMirEvent(m, events.RegisterCheckpoint(destModule, blockId, state)) } -func InitBlockchain(m dsl.Module, destModule types.ModuleID, initialState *statepb.State) { +func InitBlockchain(m dsl.Module, destModule types.ModuleID, initialState *types2.State) { dsl.EmitMirEvent(m, events.InitBlockchain(destModule, initialState)) } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go index 6c49fe51e..5075ed42b 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go @@ -4,11 +4,11 @@ package bcmpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + types4 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" - types2 "github.com/filecoin-project/mir/pkg/types" + types3 "github.com/filecoin-project/mir/pkg/types" ) // Module-specific dsl functions for processing events. @@ -24,61 +24,61 @@ func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func }) } -func UponNewBlock(m dsl.Module, handler func(block *blockchainpb.Block) error) { +func UponNewBlock(m dsl.Module, handler func(block *types2.Block) error) { UponEvent[*types.Event_NewBlock](m, func(ev *types.NewBlock) error { return handler(ev.Block) }) } -func UponNewChain(m dsl.Module, handler func(blocks []*blockchainpb.Block) error) { +func UponNewChain(m dsl.Module, handler func(blocks []*types2.Block) error) { UponEvent[*types.Event_NewChain](m, func(ev *types.NewChain) error { return handler(ev.Blocks) }) } -func UponGetBlockRequest(m dsl.Module, handler func(requestId string, sourceModule types2.ModuleID, blockId uint64) error) { +func UponGetBlockRequest(m dsl.Module, handler func(requestId string, sourceModule types3.ModuleID, blockId uint64) error) { UponEvent[*types.Event_GetBlockRequest](m, func(ev *types.GetBlockRequest) error { return handler(ev.RequestId, ev.SourceModule, ev.BlockId) }) } -func UponGetBlockResponse(m dsl.Module, handler func(requestId string, found bool, block *blockchainpb.Block) error) { +func UponGetBlockResponse(m dsl.Module, handler func(requestId string, found bool, block *types2.Block) error) { UponEvent[*types.Event_GetBlockResponse](m, func(ev *types.GetBlockResponse) error { return handler(ev.RequestId, ev.Found, ev.Block) }) } -func UponGetChainRequest(m dsl.Module, handler func(requestId string, sourceModule types2.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error) { +func UponGetChainRequest(m dsl.Module, handler func(requestId string, sourceModule types3.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error) { UponEvent[*types.Event_GetChainRequest](m, func(ev *types.GetChainRequest) error { return handler(ev.RequestId, ev.SourceModule, ev.EndBlockId, ev.SourceBlockIds) }) } -func UponGetChainResponse(m dsl.Module, handler func(requestId string, success bool, chain []*blockchainpb.Block) error) { +func UponGetChainResponse(m dsl.Module, handler func(requestId string, success bool, chain []*types2.Block) error) { UponEvent[*types.Event_GetChainResponse](m, func(ev *types.GetChainResponse) error { return handler(ev.RequestId, ev.Success, ev.Chain) }) } -func UponGetHeadToCheckpointChainRequest(m dsl.Module, handler func(requestId string, sourceModule types2.ModuleID) error) { +func UponGetHeadToCheckpointChainRequest(m dsl.Module, handler func(requestId string, sourceModule types3.ModuleID) error) { UponEvent[*types.Event_GetHeadToCheckpointChainRequest](m, func(ev *types.GetHeadToCheckpointChainRequest) error { return handler(ev.RequestId, ev.SourceModule) }) } -func UponGetHeadToCheckpointChainResponse(m dsl.Module, handler func(requestId string, chain []*blockchainpb.BlockInternal) error) { +func UponGetHeadToCheckpointChainResponse(m dsl.Module, handler func(requestId string, chain []*types2.BlockInternal) error) { UponEvent[*types.Event_GetHeadToCheckpointChainResponse](m, func(ev *types.GetHeadToCheckpointChainResponse) error { return handler(ev.RequestId, ev.Chain) }) } -func UponRegisterCheckpoint(m dsl.Module, handler func(blockId uint64, state *statepb.State) error) { +func UponRegisterCheckpoint(m dsl.Module, handler func(blockId uint64, state *types4.State) error) { UponEvent[*types.Event_RegisterCheckpoint](m, func(ev *types.RegisterCheckpoint) error { return handler(ev.BlockId, ev.State) }) } -func UponInitBlockchain(m dsl.Module, handler func(initialState *statepb.State) error) { +func UponInitBlockchain(m dsl.Module, handler func(initialState *types4.State) error) { UponEvent[*types.Event_InitBlockchain](m, func(ev *types.InitBlockchain) error { return handler(ev.InitialState) }) diff --git a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go index ad3777a67..76db3e824 100644 --- a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go @@ -3,20 +3,20 @@ package bcmpbevents import ( - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" - types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" + types4 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) -func NewBlock(destModule types.ModuleID, block *blockchainpb.Block) *types1.Event { - return &types1.Event{ +func NewBlock(destModule types.ModuleID, block *types1.Block) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcm{ - Bcm: &types2.Event{ - Type: &types2.Event_NewBlock{ - NewBlock: &types2.NewBlock{ + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_NewBlock{ + NewBlock: &types3.NewBlock{ Block: block, }, }, @@ -25,13 +25,13 @@ func NewBlock(destModule types.ModuleID, block *blockchainpb.Block) *types1.Even } } -func NewChain(destModule types.ModuleID, blocks []*blockchainpb.Block) *types1.Event { - return &types1.Event{ +func NewChain(destModule types.ModuleID, blocks []*types1.Block) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcm{ - Bcm: &types2.Event{ - Type: &types2.Event_NewChain{ - NewChain: &types2.NewChain{ + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_NewChain{ + NewChain: &types3.NewChain{ Blocks: blocks, }, }, @@ -40,13 +40,13 @@ func NewChain(destModule types.ModuleID, blocks []*blockchainpb.Block) *types1.E } } -func GetBlockRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID, blockId uint64) *types1.Event { - return &types1.Event{ +func GetBlockRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID, blockId uint64) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcm{ - Bcm: &types2.Event{ - Type: &types2.Event_GetBlockRequest{ - GetBlockRequest: &types2.GetBlockRequest{ + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_GetBlockRequest{ + GetBlockRequest: &types3.GetBlockRequest{ RequestId: requestId, SourceModule: sourceModule, BlockId: blockId, @@ -57,13 +57,13 @@ func GetBlockRequest(destModule types.ModuleID, requestId string, sourceModule t } } -func GetBlockResponse(destModule types.ModuleID, requestId string, found bool, block *blockchainpb.Block) *types1.Event { - return &types1.Event{ +func GetBlockResponse(destModule types.ModuleID, requestId string, found bool, block *types1.Block) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcm{ - Bcm: &types2.Event{ - Type: &types2.Event_GetBlockResponse{ - GetBlockResponse: &types2.GetBlockResponse{ + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_GetBlockResponse{ + GetBlockResponse: &types3.GetBlockResponse{ RequestId: requestId, Found: found, Block: block, @@ -74,13 +74,13 @@ func GetBlockResponse(destModule types.ModuleID, requestId string, found bool, b } } -func GetChainRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID, endBlockId uint64, sourceBlockIds []uint64) *types1.Event { - return &types1.Event{ +func GetChainRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID, endBlockId uint64, sourceBlockIds []uint64) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcm{ - Bcm: &types2.Event{ - Type: &types2.Event_GetChainRequest{ - GetChainRequest: &types2.GetChainRequest{ + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_GetChainRequest{ + GetChainRequest: &types3.GetChainRequest{ RequestId: requestId, SourceModule: sourceModule, EndBlockId: endBlockId, @@ -92,13 +92,13 @@ func GetChainRequest(destModule types.ModuleID, requestId string, sourceModule t } } -func GetChainResponse(destModule types.ModuleID, requestId string, success bool, chain []*blockchainpb.Block) *types1.Event { - return &types1.Event{ +func GetChainResponse(destModule types.ModuleID, requestId string, success bool, chain []*types1.Block) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcm{ - Bcm: &types2.Event{ - Type: &types2.Event_GetChainResponse{ - GetChainResponse: &types2.GetChainResponse{ + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_GetChainResponse{ + GetChainResponse: &types3.GetChainResponse{ RequestId: requestId, Success: success, Chain: chain, @@ -109,13 +109,13 @@ func GetChainResponse(destModule types.ModuleID, requestId string, success bool, } } -func GetHeadToCheckpointChainRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID) *types1.Event { - return &types1.Event{ +func GetHeadToCheckpointChainRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcm{ - Bcm: &types2.Event{ - Type: &types2.Event_GetHeadToCheckpointChainRequest{ - GetHeadToCheckpointChainRequest: &types2.GetHeadToCheckpointChainRequest{ + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_GetHeadToCheckpointChainRequest{ + GetHeadToCheckpointChainRequest: &types3.GetHeadToCheckpointChainRequest{ RequestId: requestId, SourceModule: sourceModule, }, @@ -125,13 +125,13 @@ func GetHeadToCheckpointChainRequest(destModule types.ModuleID, requestId string } } -func GetHeadToCheckpointChainResponse(destModule types.ModuleID, requestId string, chain []*blockchainpb.BlockInternal) *types1.Event { - return &types1.Event{ +func GetHeadToCheckpointChainResponse(destModule types.ModuleID, requestId string, chain []*types1.BlockInternal) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcm{ - Bcm: &types2.Event{ - Type: &types2.Event_GetHeadToCheckpointChainResponse{ - GetHeadToCheckpointChainResponse: &types2.GetHeadToCheckpointChainResponse{ + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_GetHeadToCheckpointChainResponse{ + GetHeadToCheckpointChainResponse: &types3.GetHeadToCheckpointChainResponse{ RequestId: requestId, Chain: chain, }, @@ -141,13 +141,13 @@ func GetHeadToCheckpointChainResponse(destModule types.ModuleID, requestId strin } } -func RegisterCheckpoint(destModule types.ModuleID, blockId uint64, state *statepb.State) *types1.Event { - return &types1.Event{ +func RegisterCheckpoint(destModule types.ModuleID, blockId uint64, state *types4.State) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcm{ - Bcm: &types2.Event{ - Type: &types2.Event_RegisterCheckpoint{ - RegisterCheckpoint: &types2.RegisterCheckpoint{ + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_RegisterCheckpoint{ + RegisterCheckpoint: &types3.RegisterCheckpoint{ BlockId: blockId, State: state, }, @@ -157,13 +157,13 @@ func RegisterCheckpoint(destModule types.ModuleID, blockId uint64, state *statep } } -func InitBlockchain(destModule types.ModuleID, initialState *statepb.State) *types1.Event { - return &types1.Event{ +func InitBlockchain(destModule types.ModuleID, initialState *types4.State) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcm{ - Bcm: &types2.Event{ - Type: &types2.Event_InitBlockchain{ - InitBlockchain: &types2.InitBlockchain{ + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_InitBlockchain{ + InitBlockchain: &types3.InitBlockchain{ InitialState: initialState, }, }, diff --git a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go index db0c289fb..580f9b4f6 100644 --- a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go @@ -4,10 +4,12 @@ package bcmpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + types1 "github.com/filecoin-project/mir/codegen/model/types" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" - types "github.com/filecoin-project/mir/pkg/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + types2 "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -323,7 +325,7 @@ func (*Event) MirReflect() mirreflect.Type { } type NewBlock struct { - Block *blockchainpb.Block + Block *types.Block } func NewBlockFromPb(pb *bcmpb.NewBlock) *NewBlock { @@ -331,7 +333,7 @@ func NewBlockFromPb(pb *bcmpb.NewBlock) *NewBlock { return nil } return &NewBlock{ - Block: pb.Block, + Block: types.BlockFromPb(pb.Block), } } @@ -342,7 +344,7 @@ func (m *NewBlock) Pb() *bcmpb.NewBlock { pbMessage := &bcmpb.NewBlock{} { if m.Block != nil { - pbMessage.Block = m.Block + pbMessage.Block = (m.Block).Pb() } } @@ -354,7 +356,7 @@ func (*NewBlock) MirReflect() mirreflect.Type { } type NewChain struct { - Blocks []*blockchainpb.Block + Blocks []*types.Block } func NewChainFromPb(pb *bcmpb.NewChain) *NewChain { @@ -362,7 +364,9 @@ func NewChainFromPb(pb *bcmpb.NewChain) *NewChain { return nil } return &NewChain{ - Blocks: pb.Blocks, + Blocks: types1.ConvertSlice(pb.Blocks, func(t *blockchainpb.Block) *types.Block { + return types.BlockFromPb(t) + }), } } @@ -372,7 +376,9 @@ func (m *NewChain) Pb() *bcmpb.NewChain { } pbMessage := &bcmpb.NewChain{} { - pbMessage.Blocks = m.Blocks + pbMessage.Blocks = types1.ConvertSlice(m.Blocks, func(t *types.Block) *blockchainpb.Block { + return (t).Pb() + }) } return pbMessage @@ -384,7 +390,7 @@ func (*NewChain) MirReflect() mirreflect.Type { type GetBlockRequest struct { RequestId string - SourceModule types.ModuleID + SourceModule types2.ModuleID BlockId uint64 } @@ -394,7 +400,7 @@ func GetBlockRequestFromPb(pb *bcmpb.GetBlockRequest) *GetBlockRequest { } return &GetBlockRequest{ RequestId: pb.RequestId, - SourceModule: (types.ModuleID)(pb.SourceModule), + SourceModule: (types2.ModuleID)(pb.SourceModule), BlockId: pb.BlockId, } } @@ -420,7 +426,7 @@ func (*GetBlockRequest) MirReflect() mirreflect.Type { type GetBlockResponse struct { RequestId string Found bool - Block *blockchainpb.Block + Block *types.Block } func GetBlockResponseFromPb(pb *bcmpb.GetBlockResponse) *GetBlockResponse { @@ -430,7 +436,7 @@ func GetBlockResponseFromPb(pb *bcmpb.GetBlockResponse) *GetBlockResponse { return &GetBlockResponse{ RequestId: pb.RequestId, Found: pb.Found, - Block: pb.Block, + Block: types.BlockFromPb(pb.Block), } } @@ -443,7 +449,7 @@ func (m *GetBlockResponse) Pb() *bcmpb.GetBlockResponse { pbMessage.RequestId = m.RequestId pbMessage.Found = m.Found if m.Block != nil { - pbMessage.Block = m.Block + pbMessage.Block = (m.Block).Pb() } } @@ -456,7 +462,7 @@ func (*GetBlockResponse) MirReflect() mirreflect.Type { type GetChainRequest struct { RequestId string - SourceModule types.ModuleID + SourceModule types2.ModuleID EndBlockId uint64 SourceBlockIds []uint64 } @@ -467,7 +473,7 @@ func GetChainRequestFromPb(pb *bcmpb.GetChainRequest) *GetChainRequest { } return &GetChainRequest{ RequestId: pb.RequestId, - SourceModule: (types.ModuleID)(pb.SourceModule), + SourceModule: (types2.ModuleID)(pb.SourceModule), EndBlockId: pb.EndBlockId, SourceBlockIds: pb.SourceBlockIds, } @@ -495,7 +501,7 @@ func (*GetChainRequest) MirReflect() mirreflect.Type { type GetChainResponse struct { RequestId string Success bool - Chain []*blockchainpb.Block + Chain []*types.Block } func GetChainResponseFromPb(pb *bcmpb.GetChainResponse) *GetChainResponse { @@ -505,7 +511,9 @@ func GetChainResponseFromPb(pb *bcmpb.GetChainResponse) *GetChainResponse { return &GetChainResponse{ RequestId: pb.RequestId, Success: pb.Success, - Chain: pb.Chain, + Chain: types1.ConvertSlice(pb.Chain, func(t *blockchainpb.Block) *types.Block { + return types.BlockFromPb(t) + }), } } @@ -517,7 +525,9 @@ func (m *GetChainResponse) Pb() *bcmpb.GetChainResponse { { pbMessage.RequestId = m.RequestId pbMessage.Success = m.Success - pbMessage.Chain = m.Chain + pbMessage.Chain = types1.ConvertSlice(m.Chain, func(t *types.Block) *blockchainpb.Block { + return (t).Pb() + }) } return pbMessage @@ -529,7 +539,7 @@ func (*GetChainResponse) MirReflect() mirreflect.Type { type GetHeadToCheckpointChainRequest struct { RequestId string - SourceModule types.ModuleID + SourceModule types2.ModuleID } func GetHeadToCheckpointChainRequestFromPb(pb *bcmpb.GetHeadToCheckpointChainRequest) *GetHeadToCheckpointChainRequest { @@ -538,7 +548,7 @@ func GetHeadToCheckpointChainRequestFromPb(pb *bcmpb.GetHeadToCheckpointChainReq } return &GetHeadToCheckpointChainRequest{ RequestId: pb.RequestId, - SourceModule: (types.ModuleID)(pb.SourceModule), + SourceModule: (types2.ModuleID)(pb.SourceModule), } } @@ -561,7 +571,7 @@ func (*GetHeadToCheckpointChainRequest) MirReflect() mirreflect.Type { type GetHeadToCheckpointChainResponse struct { RequestId string - Chain []*blockchainpb.BlockInternal + Chain []*types.BlockInternal } func GetHeadToCheckpointChainResponseFromPb(pb *bcmpb.GetHeadToCheckpointChainResponse) *GetHeadToCheckpointChainResponse { @@ -570,7 +580,9 @@ func GetHeadToCheckpointChainResponseFromPb(pb *bcmpb.GetHeadToCheckpointChainRe } return &GetHeadToCheckpointChainResponse{ RequestId: pb.RequestId, - Chain: pb.Chain, + Chain: types1.ConvertSlice(pb.Chain, func(t *blockchainpb.BlockInternal) *types.BlockInternal { + return types.BlockInternalFromPb(t) + }), } } @@ -581,7 +593,9 @@ func (m *GetHeadToCheckpointChainResponse) Pb() *bcmpb.GetHeadToCheckpointChainR pbMessage := &bcmpb.GetHeadToCheckpointChainResponse{} { pbMessage.RequestId = m.RequestId - pbMessage.Chain = m.Chain + pbMessage.Chain = types1.ConvertSlice(m.Chain, func(t *types.BlockInternal) *blockchainpb.BlockInternal { + return (t).Pb() + }) } return pbMessage @@ -593,7 +607,7 @@ func (*GetHeadToCheckpointChainResponse) MirReflect() mirreflect.Type { type RegisterCheckpoint struct { BlockId uint64 - State *statepb.State + State *types3.State } func RegisterCheckpointFromPb(pb *bcmpb.RegisterCheckpoint) *RegisterCheckpoint { @@ -602,7 +616,7 @@ func RegisterCheckpointFromPb(pb *bcmpb.RegisterCheckpoint) *RegisterCheckpoint } return &RegisterCheckpoint{ BlockId: pb.BlockId, - State: pb.State, + State: types3.StateFromPb(pb.State), } } @@ -614,7 +628,7 @@ func (m *RegisterCheckpoint) Pb() *bcmpb.RegisterCheckpoint { { pbMessage.BlockId = m.BlockId if m.State != nil { - pbMessage.State = m.State + pbMessage.State = (m.State).Pb() } } @@ -626,7 +640,7 @@ func (*RegisterCheckpoint) MirReflect() mirreflect.Type { } type InitBlockchain struct { - InitialState *statepb.State + InitialState *types3.State } func InitBlockchainFromPb(pb *bcmpb.InitBlockchain) *InitBlockchain { @@ -634,7 +648,7 @@ func InitBlockchainFromPb(pb *bcmpb.InitBlockchain) *InitBlockchain { return nil } return &InitBlockchain{ - InitialState: pb.InitialState, + InitialState: types3.StateFromPb(pb.InitialState), } } @@ -645,7 +659,7 @@ func (m *InitBlockchain) Pb() *bcmpb.InitBlockchain { pbMessage := &bcmpb.InitBlockchain{} { if m.InitialState != nil { - pbMessage.InitialState = m.InitialState + pbMessage.InitialState = (m.InitialState).Pb() } } diff --git a/pkg/pb/blockchainpb/blockchainpb.pb.go b/pkg/pb/blockchainpb/blockchainpb.pb.go index 9e44ec90d..535e7d062 100644 --- a/pkg/pb/blockchainpb/blockchainpb.pb.go +++ b/pkg/pb/blockchainpb/blockchainpb.pb.go @@ -9,6 +9,7 @@ package blockchainpb import ( payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -256,40 +257,43 @@ var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ 0x0a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0c, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x1a, - 0x26, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, - 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x50, 0x0a, 0x09, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, - 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x22, 0x39, 0x0a, - 0x0a, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, - 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, - 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x22, 0x60, 0x0a, 0x0d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, + 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, + 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x26, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x09, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x04, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, + 0x01, 0x22, 0x3f, 0x0a, 0x0a, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, + 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x80, 0xa6, + 0x1d, 0x01, 0x22, 0xa0, 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, + 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, + 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, + 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x66, 0x0a, 0x0d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, - 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x35, 0x5a, + 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, + 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, + 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/pb/blockchainpb/communicationpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/communicationpb/dsl/emit.mir.go index 7d47bebf8..81c447f5f 100644 --- a/pkg/pb/blockchainpb/communicationpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/communicationpb/dsl/emit.mir.go @@ -4,13 +4,13 @@ package communicationpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/events" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types "github.com/filecoin-project/mir/pkg/types" ) // Module-specific dsl functions for emitting events. -func NewBlock(m dsl.Module, destModule types.ModuleID, block *blockchainpb.Block) { +func NewBlock(m dsl.Module, destModule types.ModuleID, block *types1.Block) { dsl.EmitMirEvent(m, events.NewBlock(destModule, block)) } diff --git a/pkg/pb/blockchainpb/communicationpb/dsl/messages.mir.go b/pkg/pb/blockchainpb/communicationpb/dsl/messages.mir.go index 53f1cfbd2..10a2f2e7e 100644 --- a/pkg/pb/blockchainpb/communicationpb/dsl/messages.mir.go +++ b/pkg/pb/blockchainpb/communicationpb/dsl/messages.mir.go @@ -4,8 +4,8 @@ package communicationpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" dsl1 "github.com/filecoin-project/mir/pkg/pb/messagepb/dsl" types2 "github.com/filecoin-project/mir/pkg/pb/messagepb/types" types1 "github.com/filecoin-project/mir/pkg/types" @@ -24,7 +24,7 @@ func UponMessageReceived[W types.Message_TypeWrapper[M], M any](m dsl.Module, ha }) } -func UponNewBlockMessageReceived(m dsl.Module, handler func(from types1.NodeID, block *blockchainpb.Block) error) { +func UponNewBlockMessageReceived(m dsl.Module, handler func(from types1.NodeID, block *types3.Block) error) { UponMessageReceived[*types.Message_NewBlock](m, func(from types1.NodeID, msg *types.NewBlockMessage) error { return handler(from, msg.Block) }) diff --git a/pkg/pb/blockchainpb/communicationpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/communicationpb/dsl/upon.mir.go index 976afedfe..3dcf3561c 100644 --- a/pkg/pb/blockchainpb/communicationpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/communicationpb/dsl/upon.mir.go @@ -4,8 +4,8 @@ package communicationpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" ) @@ -22,7 +22,7 @@ func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func }) } -func UponNewBlock(m dsl.Module, handler func(block *blockchainpb.Block) error) { +func UponNewBlock(m dsl.Module, handler func(block *types2.Block) error) { UponEvent[*types.Event_NewBlock](m, func(ev *types.NewBlock) error { return handler(ev.Block) }) diff --git a/pkg/pb/blockchainpb/communicationpb/events/events.mir.go b/pkg/pb/blockchainpb/communicationpb/events/events.mir.go index a79e7352c..355742e8c 100644 --- a/pkg/pb/blockchainpb/communicationpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/communicationpb/events/events.mir.go @@ -3,19 +3,19 @@ package communicationpbevents import ( - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" - types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) -func NewBlock(destModule types.ModuleID, block *blockchainpb.Block) *types1.Event { - return &types1.Event{ +func NewBlock(destModule types.ModuleID, block *types1.Block) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Communication{ - Communication: &types2.Event{ - Type: &types2.Event_NewBlock{ - NewBlock: &types2.NewBlock{ + Type: &types2.Event_Communication{ + Communication: &types3.Event{ + Type: &types3.Event_NewBlock{ + NewBlock: &types3.NewBlock{ Block: block, }, }, diff --git a/pkg/pb/blockchainpb/communicationpb/msgs/msgs.mir.go b/pkg/pb/blockchainpb/communicationpb/msgs/msgs.mir.go index 9a4f1c8e5..53627c694 100644 --- a/pkg/pb/blockchainpb/communicationpb/msgs/msgs.mir.go +++ b/pkg/pb/blockchainpb/communicationpb/msgs/msgs.mir.go @@ -3,19 +3,19 @@ package communicationpbmsgs import ( - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" - types1 "github.com/filecoin-project/mir/pkg/pb/messagepb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/messagepb/types" types "github.com/filecoin-project/mir/pkg/types" ) -func NewBlockMessage(destModule types.ModuleID, block *blockchainpb.Block) *types1.Message { - return &types1.Message{ +func NewBlockMessage(destModule types.ModuleID, block *types1.Block) *types2.Message { + return &types2.Message{ DestModule: destModule, - Type: &types1.Message_Communicationpb{ - Communicationpb: &types2.Message{ - Type: &types2.Message_NewBlock{ - NewBlock: &types2.NewBlockMessage{ + Type: &types2.Message_Communicationpb{ + Communicationpb: &types3.Message{ + Type: &types3.Message_NewBlock{ + NewBlock: &types3.NewBlockMessage{ Block: block, }, }, diff --git a/pkg/pb/blockchainpb/communicationpb/types/types.mir.go b/pkg/pb/blockchainpb/communicationpb/types/types.mir.go index 74b4afe28..fc82a6134 100644 --- a/pkg/pb/blockchainpb/communicationpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/communicationpb/types/types.mir.go @@ -4,8 +4,8 @@ package communicationpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -87,7 +87,7 @@ func (*Event) MirReflect() mirreflect.Type { } type NewBlock struct { - Block *blockchainpb.Block + Block *types.Block } func NewBlockFromPb(pb *communicationpb.NewBlock) *NewBlock { @@ -95,7 +95,7 @@ func NewBlockFromPb(pb *communicationpb.NewBlock) *NewBlock { return nil } return &NewBlock{ - Block: pb.Block, + Block: types.BlockFromPb(pb.Block), } } @@ -106,7 +106,7 @@ func (m *NewBlock) Pb() *communicationpb.NewBlock { pbMessage := &communicationpb.NewBlock{} { if m.Block != nil { - pbMessage.Block = m.Block + pbMessage.Block = (m.Block).Pb() } } @@ -195,7 +195,7 @@ func (*Message) MirReflect() mirreflect.Type { } type NewBlockMessage struct { - Block *blockchainpb.Block + Block *types.Block } func NewBlockMessageFromPb(pb *communicationpb.NewBlockMessage) *NewBlockMessage { @@ -203,7 +203,7 @@ func NewBlockMessageFromPb(pb *communicationpb.NewBlockMessage) *NewBlockMessage return nil } return &NewBlockMessage{ - Block: pb.Block, + Block: types.BlockFromPb(pb.Block), } } @@ -214,7 +214,7 @@ func (m *NewBlockMessage) Pb() *communicationpb.NewBlockMessage { pbMessage := &communicationpb.NewBlockMessage{} { if m.Block != nil { - pbMessage.Block = m.Block + pbMessage.Block = (m.Block).Pb() } } diff --git a/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go index e0d7a51f0..5add921d0 100644 --- a/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go @@ -4,22 +4,22 @@ package interceptorpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/events" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types "github.com/filecoin-project/mir/pkg/types" ) // Module-specific dsl functions for emitting events. -func TreeUpdate(m dsl.Module, destModule types.ModuleID, tree *blockchainpb.Blocktree, headId uint64) { +func TreeUpdate(m dsl.Module, destModule types.ModuleID, tree *types1.Blocktree, headId uint64) { dsl.EmitMirEvent(m, events.TreeUpdate(destModule, tree, headId)) } -func NewOrphan(m dsl.Module, destModule types.ModuleID, orphan *blockchainpb.Block) { +func NewOrphan(m dsl.Module, destModule types.ModuleID, orphan *types1.Block) { dsl.EmitMirEvent(m, events.NewOrphan(destModule, orphan)) } -func AppUpdate(m dsl.Module, destModule types.ModuleID, state *statepb.State) { +func AppUpdate(m dsl.Module, destModule types.ModuleID, state *types2.State) { dsl.EmitMirEvent(m, events.AppUpdate(destModule, state)) } diff --git a/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go index a722d0d69..787b81d4e 100644 --- a/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go @@ -4,9 +4,9 @@ package interceptorpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" ) @@ -23,19 +23,19 @@ func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func }) } -func UponTreeUpdate(m dsl.Module, handler func(tree *blockchainpb.Blocktree, headId uint64) error) { +func UponTreeUpdate(m dsl.Module, handler func(tree *types2.Blocktree, headId uint64) error) { UponEvent[*types.Event_TreeUpdate](m, func(ev *types.TreeUpdate) error { return handler(ev.Tree, ev.HeadId) }) } -func UponNewOrphan(m dsl.Module, handler func(orphan *blockchainpb.Block) error) { +func UponNewOrphan(m dsl.Module, handler func(orphan *types2.Block) error) { UponEvent[*types.Event_NewOrphan](m, func(ev *types.NewOrphan) error { return handler(ev.Orphan) }) } -func UponAppUpdate(m dsl.Module, handler func(state *statepb.State) error) { +func UponAppUpdate(m dsl.Module, handler func(state *types3.State) error) { UponEvent[*types.Event_AppUpdate](m, func(ev *types.AppUpdate) error { return handler(ev.State) }) diff --git a/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go b/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go index 2b904bc8e..32b8ded15 100644 --- a/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go @@ -3,20 +3,20 @@ package interceptorpbevents import ( - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" - types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" + types4 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) -func TreeUpdate(destModule types.ModuleID, tree *blockchainpb.Blocktree, headId uint64) *types1.Event { - return &types1.Event{ +func TreeUpdate(destModule types.ModuleID, tree *types1.Blocktree, headId uint64) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcinterceptor{ - Bcinterceptor: &types2.Event{ - Type: &types2.Event_TreeUpdate{ - TreeUpdate: &types2.TreeUpdate{ + Type: &types2.Event_Bcinterceptor{ + Bcinterceptor: &types3.Event{ + Type: &types3.Event_TreeUpdate{ + TreeUpdate: &types3.TreeUpdate{ Tree: tree, HeadId: headId, }, @@ -26,13 +26,13 @@ func TreeUpdate(destModule types.ModuleID, tree *blockchainpb.Blocktree, headId } } -func NewOrphan(destModule types.ModuleID, orphan *blockchainpb.Block) *types1.Event { - return &types1.Event{ +func NewOrphan(destModule types.ModuleID, orphan *types1.Block) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcinterceptor{ - Bcinterceptor: &types2.Event{ - Type: &types2.Event_NewOrphan{ - NewOrphan: &types2.NewOrphan{ + Type: &types2.Event_Bcinterceptor{ + Bcinterceptor: &types3.Event{ + Type: &types3.Event_NewOrphan{ + NewOrphan: &types3.NewOrphan{ Orphan: orphan, }, }, @@ -41,13 +41,13 @@ func NewOrphan(destModule types.ModuleID, orphan *blockchainpb.Block) *types1.Ev } } -func AppUpdate(destModule types.ModuleID, state *statepb.State) *types1.Event { - return &types1.Event{ +func AppUpdate(destModule types.ModuleID, state *types4.State) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Bcinterceptor{ - Bcinterceptor: &types2.Event{ - Type: &types2.Event_AppUpdate{ - AppUpdate: &types2.AppUpdate{ + Type: &types2.Event_Bcinterceptor{ + Bcinterceptor: &types3.Event{ + Type: &types3.Event_AppUpdate{ + AppUpdate: &types3.AppUpdate{ State: state, }, }, diff --git a/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go b/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go index 9d5d1d6ba..39d50b292 100644 --- a/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go @@ -4,9 +4,9 @@ package interceptorpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -140,7 +140,7 @@ func (*Event) MirReflect() mirreflect.Type { } type TreeUpdate struct { - Tree *blockchainpb.Blocktree + Tree *types.Blocktree HeadId uint64 } @@ -149,7 +149,7 @@ func TreeUpdateFromPb(pb *interceptorpb.TreeUpdate) *TreeUpdate { return nil } return &TreeUpdate{ - Tree: pb.Tree, + Tree: types.BlocktreeFromPb(pb.Tree), HeadId: pb.HeadId, } } @@ -161,7 +161,7 @@ func (m *TreeUpdate) Pb() *interceptorpb.TreeUpdate { pbMessage := &interceptorpb.TreeUpdate{} { if m.Tree != nil { - pbMessage.Tree = m.Tree + pbMessage.Tree = (m.Tree).Pb() } pbMessage.HeadId = m.HeadId } @@ -174,7 +174,7 @@ func (*TreeUpdate) MirReflect() mirreflect.Type { } type NewOrphan struct { - Orphan *blockchainpb.Block + Orphan *types.Block } func NewOrphanFromPb(pb *interceptorpb.NewOrphan) *NewOrphan { @@ -182,7 +182,7 @@ func NewOrphanFromPb(pb *interceptorpb.NewOrphan) *NewOrphan { return nil } return &NewOrphan{ - Orphan: pb.Orphan, + Orphan: types.BlockFromPb(pb.Orphan), } } @@ -193,7 +193,7 @@ func (m *NewOrphan) Pb() *interceptorpb.NewOrphan { pbMessage := &interceptorpb.NewOrphan{} { if m.Orphan != nil { - pbMessage.Orphan = m.Orphan + pbMessage.Orphan = (m.Orphan).Pb() } } @@ -205,7 +205,7 @@ func (*NewOrphan) MirReflect() mirreflect.Type { } type AppUpdate struct { - State *statepb.State + State *types1.State } func AppUpdateFromPb(pb *interceptorpb.AppUpdate) *AppUpdate { @@ -213,7 +213,7 @@ func AppUpdateFromPb(pb *interceptorpb.AppUpdate) *AppUpdate { return nil } return &AppUpdate{ - State: pb.State, + State: types1.StateFromPb(pb.State), } } @@ -224,7 +224,7 @@ func (m *AppUpdate) Pb() *interceptorpb.AppUpdate { pbMessage := &interceptorpb.AppUpdate{} { if m.State != nil { - pbMessage.State = m.State + pbMessage.State = (m.State).Pb() } } diff --git a/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go index 303c00c92..abd98a1f7 100644 --- a/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/minerpb/dsl/emit.mir.go @@ -5,13 +5,13 @@ package minerpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/events" - payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" types "github.com/filecoin-project/mir/pkg/types" ) // Module-specific dsl functions for emitting events. -func BlockRequest(m dsl.Module, destModule types.ModuleID, headId uint64, payload *payloadpb.Payload) { +func BlockRequest(m dsl.Module, destModule types.ModuleID, headId uint64, payload *types1.Payload) { dsl.EmitMirEvent(m, events.BlockRequest(destModule, headId, payload)) } diff --git a/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go index 84805450c..676701e40 100644 --- a/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/minerpb/dsl/upon.mir.go @@ -5,7 +5,7 @@ package minerpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" - payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" ) @@ -22,7 +22,7 @@ func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func }) } -func UponBlockRequest(m dsl.Module, handler func(headId uint64, payload *payloadpb.Payload) error) { +func UponBlockRequest(m dsl.Module, handler func(headId uint64, payload *types2.Payload) error) { UponEvent[*types.Event_BlockRequest](m, func(ev *types.BlockRequest) error { return handler(ev.HeadId, ev.Payload) }) diff --git a/pkg/pb/blockchainpb/minerpb/events/events.mir.go b/pkg/pb/blockchainpb/minerpb/events/events.mir.go index 13e930775..b8c58c99b 100644 --- a/pkg/pb/blockchainpb/minerpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/minerpb/events/events.mir.go @@ -3,19 +3,19 @@ package minerpbevents import ( - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" - payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" - types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) -func BlockRequest(destModule types.ModuleID, headId uint64, payload *payloadpb.Payload) *types1.Event { - return &types1.Event{ +func BlockRequest(destModule types.ModuleID, headId uint64, payload *types1.Payload) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Miner{ - Miner: &types2.Event{ - Type: &types2.Event_BlockRequest{ - BlockRequest: &types2.BlockRequest{ + Type: &types2.Event_Miner{ + Miner: &types3.Event{ + Type: &types3.Event_BlockRequest{ + BlockRequest: &types3.BlockRequest{ HeadId: headId, Payload: payload, }, @@ -25,13 +25,13 @@ func BlockRequest(destModule types.ModuleID, headId uint64, payload *payloadpb.P } } -func NewHead(destModule types.ModuleID, headId uint64) *types1.Event { - return &types1.Event{ +func NewHead(destModule types.ModuleID, headId uint64) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Miner{ - Miner: &types2.Event{ - Type: &types2.Event_NewHead{ - NewHead: &types2.NewHead{ + Type: &types2.Event_Miner{ + Miner: &types3.Event{ + Type: &types3.Event_NewHead{ + NewHead: &types3.NewHead{ HeadId: headId, }, }, diff --git a/pkg/pb/blockchainpb/minerpb/types/types.mir.go b/pkg/pb/blockchainpb/minerpb/types/types.mir.go index 055f98415..9c299b5d3 100644 --- a/pkg/pb/blockchainpb/minerpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/minerpb/types/types.mir.go @@ -5,7 +5,7 @@ package minerpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" - payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -114,7 +114,7 @@ func (*Event) MirReflect() mirreflect.Type { type BlockRequest struct { HeadId uint64 - Payload *payloadpb.Payload + Payload *types.Payload } func BlockRequestFromPb(pb *minerpb.BlockRequest) *BlockRequest { @@ -123,7 +123,7 @@ func BlockRequestFromPb(pb *minerpb.BlockRequest) *BlockRequest { } return &BlockRequest{ HeadId: pb.HeadId, - Payload: pb.Payload, + Payload: types.PayloadFromPb(pb.Payload), } } @@ -135,7 +135,7 @@ func (m *BlockRequest) Pb() *minerpb.BlockRequest { { pbMessage.HeadId = m.HeadId if m.Payload != nil { - pbMessage.Payload = m.Payload + pbMessage.Payload = (m.Payload).Pb() } } diff --git a/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go b/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go index a83a00034..70c7d309f 100644 --- a/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go +++ b/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go @@ -7,6 +7,7 @@ package payloadpb import ( + _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -28,6 +29,7 @@ type Payload struct { // application specific payload Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Sender string `protobuf:"bytes,3,opt,name=sender,proto3" json:"sender,omitempty"` } func (x *Payload) Reset() { @@ -76,21 +78,36 @@ func (x *Payload) GetTimestamp() int64 { return 0 } +func (x *Payload) GetSender() string { + if x != nil { + return x.Sender + } + return "" +} + var File_blockchainpb_payloadpb_payloadpb_proto protoreflect.FileDescriptor var file_blockchainpb_payloadpb_payloadpb_proto_rawDesc = []byte{ 0x0a, 0x26, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x70, 0x62, 0x22, 0x41, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, - 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, - 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, - 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, - 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x70, 0x62, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, + 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0x95, 0x01, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, 0x0a, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x4c, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x34, 0x82, 0xa6, 0x1d, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, + 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, + 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x52, 0x06, 0x73, 0x65, 0x6e, + 0x64, 0x65, 0x72, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, + 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/pkg/pb/blockchainpb/payloadpb/types/types.mir.go b/pkg/pb/blockchainpb/payloadpb/types/types.mir.go index 83cd966c3..e4d3833c5 100644 --- a/pkg/pb/blockchainpb/payloadpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/payloadpb/types/types.mir.go @@ -1,3 +1,45 @@ // Code generated by Mir codegen. DO NOT EDIT. package payloadpbtypes + +import ( + mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + types "github.com/filecoin-project/mir/pkg/types" + reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" +) + +type Payload struct { + Message string + Timestamp int64 + Sender types.NodeID +} + +func PayloadFromPb(pb *payloadpb.Payload) *Payload { + if pb == nil { + return nil + } + return &Payload{ + Message: pb.Message, + Timestamp: pb.Timestamp, + Sender: (types.NodeID)(pb.Sender), + } +} + +func (m *Payload) Pb() *payloadpb.Payload { + if m == nil { + return nil + } + pbMessage := &payloadpb.Payload{} + { + pbMessage.Message = m.Message + pbMessage.Timestamp = m.Timestamp + pbMessage.Sender = (string)(m.Sender) + } + + return pbMessage +} + +func (*Payload) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*payloadpb.Payload]()} +} diff --git a/pkg/pb/blockchainpb/statepb/statepb.pb.go b/pkg/pb/blockchainpb/statepb/statepb.pb.go index ed79fb683..e3e271099 100644 --- a/pkg/pb/blockchainpb/statepb/statepb.pb.go +++ b/pkg/pb/blockchainpb/statepb/statepb.pb.go @@ -7,6 +7,7 @@ package statepb import ( + _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" @@ -26,7 +27,8 @@ type State struct { unknownFields protoimpl.UnknownFields // application specific state - MessageHistory []string `protobuf:"bytes,1,rep,name=message_history,json=messageHistory,proto3" json:"message_history,omitempty"` + MessageHistory []string `protobuf:"bytes,1,rep,name=message_history,json=messageHistory,proto3" json:"message_history,omitempty"` + LastSentTimestamps []*LastSentTimestamp `protobuf:"bytes,2,rep,name=last_sent_timestamps,json=lastSentTimestamps,proto3" json:"last_sent_timestamps,omitempty"` } func (x *State) Reset() { @@ -68,20 +70,98 @@ func (x *State) GetMessageHistory() []string { return nil } +func (x *State) GetLastSentTimestamps() []*LastSentTimestamp { + if x != nil { + return x.LastSentTimestamps + } + return nil +} + +type LastSentTimestamp struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` +} + +func (x *LastSentTimestamp) Reset() { + *x = LastSentTimestamp{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_statepb_statepb_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LastSentTimestamp) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LastSentTimestamp) ProtoMessage() {} + +func (x *LastSentTimestamp) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_statepb_statepb_proto_msgTypes[1] + 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 LastSentTimestamp.ProtoReflect.Descriptor instead. +func (*LastSentTimestamp) Descriptor() ([]byte, []int) { + return file_blockchainpb_statepb_statepb_proto_rawDescGZIP(), []int{1} +} + +func (x *LastSentTimestamp) GetNodeId() string { + if x != nil { + return x.NodeId + } + return "" +} + +func (x *LastSentTimestamp) GetTimestamp() int64 { + if x != nil { + return x.Timestamp + } + return 0 +} + var File_blockchainpb_statepb_statepb_proto protoreflect.FileDescriptor var file_blockchainpb_statepb_statepb_proto_rawDesc = []byte{ 0x0a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x22, 0x30, 0x0a, - 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x42, - 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, - 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, - 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x62, 0x06, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x1a, 0x1c, 0x6d, + 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x84, 0x01, 0x0a, 0x05, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x4c, + 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, + 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x3a, 0x04, 0x80, 0xa6, + 0x1d, 0x01, 0x22, 0x86, 0x01, 0x0a, 0x11, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, 0x54, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x4d, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, + 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x34, 0x82, 0xa6, 0x1d, 0x30, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, + 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x52, + 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, + 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -96,16 +176,18 @@ func file_blockchainpb_statepb_statepb_proto_rawDescGZIP() []byte { return file_blockchainpb_statepb_statepb_proto_rawDescData } -var file_blockchainpb_statepb_statepb_proto_msgTypes = make([]protoimpl.MessageInfo, 1) +var file_blockchainpb_statepb_statepb_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_blockchainpb_statepb_statepb_proto_goTypes = []interface{}{ - (*State)(nil), // 0: statepb.State + (*State)(nil), // 0: statepb.State + (*LastSentTimestamp)(nil), // 1: statepb.LastSentTimestamp } var file_blockchainpb_statepb_statepb_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] 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 + 1, // 0: statepb.State.last_sent_timestamps:type_name -> statepb.LastSentTimestamp + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_blockchainpb_statepb_statepb_proto_init() } @@ -126,6 +208,18 @@ func file_blockchainpb_statepb_statepb_proto_init() { return nil } } + file_blockchainpb_statepb_statepb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LastSentTimestamp); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -133,7 +227,7 @@ func file_blockchainpb_statepb_statepb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_statepb_statepb_proto_rawDesc, NumEnums: 0, - NumMessages: 1, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/statepb/types/types.mir.go b/pkg/pb/blockchainpb/statepb/types/types.mir.go index c583fc3e4..c26623e7c 100644 --- a/pkg/pb/blockchainpb/statepb/types/types.mir.go +++ b/pkg/pb/blockchainpb/statepb/types/types.mir.go @@ -1,3 +1,79 @@ // Code generated by Mir codegen. DO NOT EDIT. package statepbtypes + +import ( + mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + types "github.com/filecoin-project/mir/codegen/model/types" + statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + types1 "github.com/filecoin-project/mir/pkg/types" + reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" +) + +type State struct { + MessageHistory []string + LastSentTimestamps []*LastSentTimestamp +} + +func StateFromPb(pb *statepb.State) *State { + if pb == nil { + return nil + } + return &State{ + MessageHistory: pb.MessageHistory, + LastSentTimestamps: types.ConvertSlice(pb.LastSentTimestamps, func(t *statepb.LastSentTimestamp) *LastSentTimestamp { + return LastSentTimestampFromPb(t) + }), + } +} + +func (m *State) Pb() *statepb.State { + if m == nil { + return nil + } + pbMessage := &statepb.State{} + { + pbMessage.MessageHistory = m.MessageHistory + pbMessage.LastSentTimestamps = types.ConvertSlice(m.LastSentTimestamps, func(t *LastSentTimestamp) *statepb.LastSentTimestamp { + return (t).Pb() + }) + } + + return pbMessage +} + +func (*State) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*statepb.State]()} +} + +type LastSentTimestamp struct { + NodeId types1.NodeID + Timestamp int64 +} + +func LastSentTimestampFromPb(pb *statepb.LastSentTimestamp) *LastSentTimestamp { + if pb == nil { + return nil + } + return &LastSentTimestamp{ + NodeId: (types1.NodeID)(pb.NodeId), + Timestamp: pb.Timestamp, + } +} + +func (m *LastSentTimestamp) Pb() *statepb.LastSentTimestamp { + if m == nil { + return nil + } + pbMessage := &statepb.LastSentTimestamp{} + { + pbMessage.NodeId = (string)(m.NodeId) + pbMessage.Timestamp = m.Timestamp + } + + return pbMessage +} + +func (*LastSentTimestamp) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*statepb.LastSentTimestamp]()} +} diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go index 3f4cd734a..dc1cee8e6 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go @@ -4,13 +4,13 @@ package synchronizerpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/events" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types "github.com/filecoin-project/mir/pkg/types" ) // Module-specific dsl functions for emitting events. -func SyncRequest(m dsl.Module, destModule types.ModuleID, orphanBlock *blockchainpb.Block, leaveIds []uint64) { +func SyncRequest(m dsl.Module, destModule types.ModuleID, orphanBlock *types1.Block, leaveIds []uint64) { dsl.EmitMirEvent(m, events.SyncRequest(destModule, orphanBlock, leaveIds)) } diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go index f83bd996b..075e9a670 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go @@ -4,8 +4,8 @@ package synchronizerpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" dsl1 "github.com/filecoin-project/mir/pkg/pb/messagepb/dsl" types2 "github.com/filecoin-project/mir/pkg/pb/messagepb/types" types1 "github.com/filecoin-project/mir/pkg/types" @@ -30,7 +30,7 @@ func UponChainRequestReceived(m dsl.Module, handler func(from types1.NodeID, req }) } -func UponChainResponseReceived(m dsl.Module, handler func(from types1.NodeID, requestId string, found bool, chain []*blockchainpb.Block) error) { +func UponChainResponseReceived(m dsl.Module, handler func(from types1.NodeID, requestId string, found bool, chain []*types3.Block) error) { UponMessageReceived[*types.Message_ChainResponse](m, func(from types1.NodeID, msg *types.ChainResponse) error { return handler(from, msg.RequestId, msg.Found, msg.Chain) }) diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go index d3b0790b8..c2d66c8f8 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go @@ -4,8 +4,8 @@ package synchronizerpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" ) @@ -22,7 +22,7 @@ func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func }) } -func UponSyncRequest(m dsl.Module, handler func(orphanBlock *blockchainpb.Block, leaveIds []uint64) error) { +func UponSyncRequest(m dsl.Module, handler func(orphanBlock *types2.Block, leaveIds []uint64) error) { UponEvent[*types.Event_SyncRequest](m, func(ev *types.SyncRequest) error { return handler(ev.OrphanBlock, ev.LeaveIds) }) diff --git a/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go b/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go index a5f5b99f4..b94a578af 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go @@ -3,19 +3,19 @@ package synchronizerpbevents import ( - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" - types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) -func SyncRequest(destModule types.ModuleID, orphanBlock *blockchainpb.Block, leaveIds []uint64) *types1.Event { - return &types1.Event{ +func SyncRequest(destModule types.ModuleID, orphanBlock *types1.Block, leaveIds []uint64) *types2.Event { + return &types2.Event{ DestModule: destModule, - Type: &types1.Event_Synchronizer{ - Synchronizer: &types2.Event{ - Type: &types2.Event_SyncRequest{ - SyncRequest: &types2.SyncRequest{ + Type: &types2.Event_Synchronizer{ + Synchronizer: &types3.Event{ + Type: &types3.Event_SyncRequest{ + SyncRequest: &types3.SyncRequest{ OrphanBlock: orphanBlock, LeaveIds: leaveIds, }, diff --git a/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go b/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go index 1ec14adf2..bff86f95e 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go @@ -3,8 +3,8 @@ package synchronizerpbmsgs import ( - blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/messagepb/types" types "github.com/filecoin-project/mir/pkg/types" ) @@ -26,7 +26,7 @@ func ChainRequest(destModule types.ModuleID, requestId string, blockId uint64, l } } -func ChainResponse(destModule types.ModuleID, requestId string, found bool, chain []*blockchainpb.Block) *types1.Message { +func ChainResponse(destModule types.ModuleID, requestId string, found bool, chain []*types3.Block) *types1.Message { return &types1.Message{ DestModule: destModule, Type: &types1.Message_Synchronizer{ diff --git a/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go index 18db20e04..a5dcfee8b 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go @@ -4,8 +4,10 @@ package synchronizerpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + types1 "github.com/filecoin-project/mir/codegen/model/types" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -87,7 +89,7 @@ func (*Event) MirReflect() mirreflect.Type { } type SyncRequest struct { - OrphanBlock *blockchainpb.Block + OrphanBlock *types.Block LeaveIds []uint64 } @@ -96,7 +98,7 @@ func SyncRequestFromPb(pb *synchronizerpb.SyncRequest) *SyncRequest { return nil } return &SyncRequest{ - OrphanBlock: pb.OrphanBlock, + OrphanBlock: types.BlockFromPb(pb.OrphanBlock), LeaveIds: pb.LeaveIds, } } @@ -108,7 +110,7 @@ func (m *SyncRequest) Pb() *synchronizerpb.SyncRequest { pbMessage := &synchronizerpb.SyncRequest{} { if m.OrphanBlock != nil { - pbMessage.OrphanBlock = m.OrphanBlock + pbMessage.OrphanBlock = (m.OrphanBlock).Pb() } pbMessage.LeaveIds = m.LeaveIds } @@ -261,7 +263,7 @@ func (*ChainRequest) MirReflect() mirreflect.Type { type ChainResponse struct { RequestId string Found bool - Chain []*blockchainpb.Block + Chain []*types.Block } func ChainResponseFromPb(pb *synchronizerpb.ChainResponse) *ChainResponse { @@ -271,7 +273,9 @@ func ChainResponseFromPb(pb *synchronizerpb.ChainResponse) *ChainResponse { return &ChainResponse{ RequestId: pb.RequestId, Found: pb.Found, - Chain: pb.Chain, + Chain: types1.ConvertSlice(pb.Chain, func(t *blockchainpb.Block) *types.Block { + return types.BlockFromPb(t) + }), } } @@ -283,7 +287,9 @@ func (m *ChainResponse) Pb() *synchronizerpb.ChainResponse { { pbMessage.RequestId = m.RequestId pbMessage.Found = m.Found - pbMessage.Chain = m.Chain + pbMessage.Chain = types1.ConvertSlice(m.Chain, func(t *types.Block) *blockchainpb.Block { + return (t).Pb() + }) } return pbMessage diff --git a/pkg/pb/blockchainpb/types/types.mir.go b/pkg/pb/blockchainpb/types/types.mir.go index fe15cb316..6804e94a0 100644 --- a/pkg/pb/blockchainpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/types/types.mir.go @@ -1,3 +1,157 @@ // Code generated by Mir codegen. DO NOT EDIT. package blockchainpbtypes + +import ( + mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + types "github.com/filecoin-project/mir/codegen/model/types" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" +) + +type Blocktree struct { + Blocks []*Block + Leaves []uint64 +} + +func BlocktreeFromPb(pb *blockchainpb.Blocktree) *Blocktree { + if pb == nil { + return nil + } + return &Blocktree{ + Blocks: types.ConvertSlice(pb.Blocks, func(t *blockchainpb.Block) *Block { + return BlockFromPb(t) + }), + Leaves: pb.Leaves, + } +} + +func (m *Blocktree) Pb() *blockchainpb.Blocktree { + if m == nil { + return nil + } + pbMessage := &blockchainpb.Blocktree{} + { + pbMessage.Blocks = types.ConvertSlice(m.Blocks, func(t *Block) *blockchainpb.Block { + return (t).Pb() + }) + pbMessage.Leaves = m.Leaves + } + + return pbMessage +} + +func (*Blocktree) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*blockchainpb.Blocktree]()} +} + +type Blockchain struct { + Blocks []*Block +} + +func BlockchainFromPb(pb *blockchainpb.Blockchain) *Blockchain { + if pb == nil { + return nil + } + return &Blockchain{ + Blocks: types.ConvertSlice(pb.Blocks, func(t *blockchainpb.Block) *Block { + return BlockFromPb(t) + }), + } +} + +func (m *Blockchain) Pb() *blockchainpb.Blockchain { + if m == nil { + return nil + } + pbMessage := &blockchainpb.Blockchain{} + { + pbMessage.Blocks = types.ConvertSlice(m.Blocks, func(t *Block) *blockchainpb.Block { + return (t).Pb() + }) + } + + return pbMessage +} + +func (*Blockchain) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*blockchainpb.Blockchain]()} +} + +type Block struct { + BlockId uint64 + PreviousBlockId uint64 + Payload *types1.Payload + Timestamp int64 +} + +func BlockFromPb(pb *blockchainpb.Block) *Block { + if pb == nil { + return nil + } + return &Block{ + BlockId: pb.BlockId, + PreviousBlockId: pb.PreviousBlockId, + Payload: types1.PayloadFromPb(pb.Payload), + Timestamp: pb.Timestamp, + } +} + +func (m *Block) Pb() *blockchainpb.Block { + if m == nil { + return nil + } + pbMessage := &blockchainpb.Block{} + { + pbMessage.BlockId = m.BlockId + pbMessage.PreviousBlockId = m.PreviousBlockId + if m.Payload != nil { + pbMessage.Payload = (m.Payload).Pb() + } + pbMessage.Timestamp = m.Timestamp + } + + return pbMessage +} + +func (*Block) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*blockchainpb.Block]()} +} + +type BlockInternal struct { + Block *Block + State *types2.State +} + +func BlockInternalFromPb(pb *blockchainpb.BlockInternal) *BlockInternal { + if pb == nil { + return nil + } + return &BlockInternal{ + Block: BlockFromPb(pb.Block), + State: types2.StateFromPb(pb.State), + } +} + +func (m *BlockInternal) Pb() *blockchainpb.BlockInternal { + if m == nil { + return nil + } + pbMessage := &blockchainpb.BlockInternal{} + { + if m.Block != nil { + pbMessage.Block = (m.Block).Pb() + } + if m.State != nil { + pbMessage.State = (m.State).Pb() + } + } + + return pbMessage +} + +func (*BlockInternal) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*blockchainpb.BlockInternal]()} +} diff --git a/protos/blockchainpb/blockchainpb.proto b/protos/blockchainpb/blockchainpb.proto index 8d84ea8a1..b7c900acc 100644 --- a/protos/blockchainpb/blockchainpb.proto +++ b/protos/blockchainpb/blockchainpb.proto @@ -4,19 +4,27 @@ package blockchainpb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb"; +import "mir/codegen_extensions.proto"; + import "blockchainpb/payloadpb/payloadpb.proto"; import "blockchainpb/statepb/statepb.proto"; message Blocktree { + option (mir.struct) = true; + repeated Block blocks = 1; repeated uint64 leaves = 2; } message Blockchain { + option (mir.struct) = true; + repeated Block blocks = 1; // ordered, no forks -> the 'current' chain } message Block { + option (mir.struct) = true; + uint64 block_id = 1; uint64 previous_block_id = 2; payloadpb.Payload payload = 3; @@ -25,6 +33,8 @@ message Block { } message BlockInternal { + option (mir.struct) = true; + Block block = 1; statepb.State state = 2; } \ No newline at end of file diff --git a/protos/blockchainpb/payloadpb/payloadpb.proto b/protos/blockchainpb/payloadpb/payloadpb.proto index aedc14c95..067f382db 100644 --- a/protos/blockchainpb/payloadpb/payloadpb.proto +++ b/protos/blockchainpb/payloadpb/payloadpb.proto @@ -4,8 +4,13 @@ package payloadpb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb"; +import "mir/codegen_extensions.proto"; + message Payload { + option (mir.struct) = true; + // application specific payload string message = 1; int64 timestamp = 2; + string sender = 3 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.NodeID"]; } \ No newline at end of file diff --git a/protos/blockchainpb/statepb/statepb.proto b/protos/blockchainpb/statepb/statepb.proto index 4b393d37a..efab977a6 100644 --- a/protos/blockchainpb/statepb/statepb.proto +++ b/protos/blockchainpb/statepb/statepb.proto @@ -4,7 +4,19 @@ package statepb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb"; +import "mir/codegen_extensions.proto"; + message State { + option (mir.struct) = true; + // application specific state - repeated string message_history = 1; + repeated string message_history = 1; + repeated LastSentTimestamp last_sent_timestamps = 2; +} + +message LastSentTimestamp { + option (mir.struct) = true; + + string node_id = 1 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.NodeID"]; + int64 timestamp = 2; } \ No newline at end of file diff --git a/protos/proto_converter_tmp_83059537/generator.go b/protos/proto_converter_tmp_83059537/generator.go new file mode 100644 index 000000000..3223d160b --- /dev/null +++ b/protos/proto_converter_tmp_83059537/generator.go @@ -0,0 +1,40 @@ + +package main + +import ( + "log" + "os" + "reflect" + + generator_ "github.com/filecoin-project/mir/codegen/generators/mir-std-gen/generator" + pkg_ "github.com/filecoin-project/mir/pkg/pb/pingpongpb" +) + +func main() { + var generator generator_.CombinedGenerator + err := generator.Run( + []reflect.Type{ + + reflect.TypeOf((*pkg_.Event)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Event_PingTime)(nil)).Elem(), + + reflect.TypeOf((*pkg_.PingTime)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Message)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Message_Ping)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Message_Pong)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Ping)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Pong)(nil)).Elem(), + + }) + + if err != nil { + log.Printf("Error: %v\n", err) + os.Exit(2) + } +} diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index e9573f4d9..cbb3b9ff2 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -9,12 +9,13 @@ import ( "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb" applicationpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/dsl" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + statepbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + t "github.com/filecoin-project/mir/pkg/types" "github.com/filecoin-project/mir/samples/blockchain/application/config" "github.com/filecoin-project/mir/samples/blockchain/application/transactions" "github.com/filecoin-project/mir/samples/blockchain/utils" @@ -24,44 +25,67 @@ type ApplicationModule struct { m *dsl.Module logger logging.Logger tm *transactions.TransactionManager - name string + nodeID t.NodeID } // application-application events -func applyBlockToState(state *statepb.State, block *blockchainpb.Block) *statepb.State { +func applyBlockToState(state *statepbtypes.State, block *blockchainpbtypes.Block) *statepbtypes.State { + sender := block.Payload.Sender + timeStamps := state.LastSentTimestamps + msgHistory := state.MessageHistory + + for i, lt := range timeStamps { + if lt.NodeId == sender { + if lt.Timestamp > block.Payload.Timestamp { + panic("invalid ordering - there is a block that should never have been accepted") + } + + // remove old timestamp, if it exists + timeStamps[i] = timeStamps[len(state.LastSentTimestamps)-1] + timeStamps = timeStamps[:len(state.LastSentTimestamps)-1] + } + } + + timeStamps = append(timeStamps, &statepbtypes.LastSentTimestamp{ + NodeId: sender, + Timestamp: block.Payload.Timestamp, + }) + // empty block, just skip - if block.Payload.Message == "" { - return state + if block.Payload.Message != "" { + msgHistory = append(msgHistory, block.Payload.Message) } - return &statepb.State{ - MessageHistory: append(state.MessageHistory, block.Payload.Message), + + return &statepbtypes.State{ + MessageHistory: msgHistory, + LastSentTimestamps: timeStamps, } } -func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain *blockchainpb.Blockchain, forkState *statepb.State) error { +func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain *blockchainpbtypes.Blockchain, forkState *statepbtypes.State) error { am.logger.Log(logging.LevelInfo, "Processing fork update", "poolSize", am.tm.PoolSize()) // add "remove chain" transactions to pool - for _, block := range removedChain.GetBlocks() { + for _, block := range removedChain.Blocks { am.tm.AddPayload(block.Payload) } // remove "add chain" transactions from pool - for _, block := range addedChain.GetBlocks() { + for _, block := range addedChain.Blocks { am.tm.RemovePayload(block.Payload) } // apply state to fork state state := forkState - for _, block := range addedChain.GetBlocks() { + for _, block := range addedChain.Blocks { state = applyBlockToState(state, block) } am.logger.Log(logging.LevelInfo, "Pool after fork", "poolSize", am.tm.PoolSize()) // register checkpoint - blockId := addedChain.GetBlocks()[len(addedChain.GetBlocks())-1].BlockId + blockId := addedChain.Blocks[len(addedChain.Blocks)-1].BlockId bcmpbdsl.RegisterCheckpoint(*am.m, "bcm", blockId, state) interceptorpbdsl.AppUpdate(*am.m, "devnull", state) @@ -82,19 +106,27 @@ func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { payload := am.tm.GetPayload() + if payload == nil { + payload = &payloadpbtypes.Payload{ + Message: "", + Timestamp: time.Now().Unix(), + Sender: am.nodeID, + } + } + applicationpbdsl.PayloadResponse(*am.m, "miner", head_id, payload) // not using head id anywhere so we can get rid of it return nil } -func NewApplication(logger logging.Logger, name string) modules.PassiveModule { +func NewApplication(logger logging.Logger, nodeID t.NodeID) modules.PassiveModule { m := dsl.NewModule("application") am := &ApplicationModule{ m: &m, logger: logger, - name: name, - tm: transactions.New(name), + nodeID: nodeID, + tm: transactions.New(), } dsl.UponInit(m, func() error { @@ -108,9 +140,10 @@ func NewApplication(logger logging.Logger, name string) modules.PassiveModule { applicationpbdsl.UponForkUpdate(m, am.handleForkUpdate) applicationpbdsl.UponMessageInput(m, func(text string) error { - am.tm.AddPayload(&payloadpb.Payload{ + am.tm.AddPayload(&payloadpbtypes.Payload{ Message: text, Timestamp: time.Now().Unix(), + Sender: am.nodeID, }) return nil diff --git a/samples/blockchain/application/config/config.go b/samples/blockchain/application/config/config.go index 3cd4eeff6..c9fd207fb 100644 --- a/samples/blockchain/application/config/config.go +++ b/samples/blockchain/application/config/config.go @@ -1,12 +1,10 @@ package config import ( - "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + statepbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" ) -var InitialState = &statepb.State{ - MessageHistory: []string{}, +var InitialState = &statepbtypes.State{ + MessageHistory: []string{}, + LastSentTimestamps: []*statepbtypes.LastSentTimestamp{}, } - -var EmptyPayload = &payloadpb.Payload{} diff --git a/samples/blockchain/application/transactions/transactions.go b/samples/blockchain/application/transactions/transactions.go index 50ae7286e..6cf8f0d7d 100644 --- a/samples/blockchain/application/transactions/transactions.go +++ b/samples/blockchain/application/transactions/transactions.go @@ -4,38 +4,33 @@ import ( "cmp" "slices" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" - "github.com/filecoin-project/mir/samples/blockchain/application/config" + payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" "github.com/mitchellh/hashstructure" ) type transaction struct { hash uint64 - payload *payloadpb.Payload + payload *payloadpbtypes.Payload } type TransactionManager struct { - transactions []transaction // sorted by timestamp - name string - ownTransactionCounter uint64 + transactions []transaction // sorted by timestamp } func (tm *TransactionManager) PoolSize() int { return len(tm.transactions) } -func New(name string) *TransactionManager { +func New() *TransactionManager { return &TransactionManager{ - transactions: []transaction{}, - name: name, - ownTransactionCounter: 0, + transactions: []transaction{}, } } -func (tm *TransactionManager) GetPayload() *payloadpb.Payload { +func (tm *TransactionManager) GetPayload() *payloadpbtypes.Payload { // return oldest transaction, where timestamp is a field in the payload if len(tm.transactions) == 0 { - return config.EmptyPayload + return nil } // sort by timestamp // TODO: keep it sorted @@ -45,7 +40,7 @@ func (tm *TransactionManager) GetPayload() *payloadpb.Payload { return tm.transactions[0].payload } -func (tm *TransactionManager) AddPayload(payload *payloadpb.Payload) error { +func (tm *TransactionManager) AddPayload(payload *payloadpbtypes.Payload) error { hash, err := hashstructure.Hash(payload, nil) if err != nil { return err @@ -68,7 +63,7 @@ func (tm *TransactionManager) AddPayload(payload *payloadpb.Payload) error { return nil } -func (tm *TransactionManager) RemovePayload(payload *payloadpb.Payload) error { +func (tm *TransactionManager) RemovePayload(payload *payloadpbtypes.Payload) error { hash, err := hashstructure.Hash(payload, nil) if err != nil { return err diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 5a99ae210..06886b8b0 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -9,13 +9,13 @@ import ( "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb" applicationpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/dsl" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" minerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" + statepbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" + blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" t "github.com/filecoin-project/mir/pkg/types" "github.com/filecoin-project/mir/samples/blockchain/application/config" "github.com/filecoin-project/mir/samples/blockchain/utils" @@ -30,7 +30,7 @@ var ( ) type bcmBlock struct { - block *blockchainpb.BlockInternal + block *blockchainpbtypes.BlockInternal parent *bcmBlock depth uint64 scanCount uint64 @@ -77,9 +77,9 @@ func (bcm *bcmModule) handleNewHeadSideEffects(newHead, oldHead *bcmBlock) { forkState := forkBlock.block.State - applicationpbdsl.ForkUpdate(*bcm.m, "application", &blockchainpb.Blockchain{ + applicationpbdsl.ForkUpdate(*bcm.m, "application", &blockchainpbtypes.Blockchain{ Blocks: removeChain[1:], - }, &blockchainpb.Blockchain{ + }, &blockchainpbtypes.Blockchain{ Blocks: addChain[1:], }, forkState) // applicationpbdsl.NewHead(*bcm.m, "application", newHead.block.Block.BlockId) @@ -91,17 +91,17 @@ func reverse[S ~[]T, T any](slice S) S { return slice } -func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]*blockchainpb.Block, []*blockchainpb.Block, error) { +func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]*blockchainpbtypes.Block, []*blockchainpbtypes.Block, error) { bcm.logger.Log(logging.LevelDebug, "Computing Delta", "removeHead", removeHead.block.Block.BlockId, "addHead", addHead.block.Block.BlockId) // short circuit for most simple cases - if removeHead.block.Block.GetBlockId() == addHead.block.Block.GetBlockId() { + if removeHead.block.Block.BlockId == addHead.block.Block.BlockId { // no delta - return []*blockchainpb.Block{addHead.block.GetBlock()}, []*blockchainpb.Block{addHead.block.GetBlock()}, nil - } else if addHead.block.Block.GetPreviousBlockId() == removeHead.block.Block.GetBlockId() { + return []*blockchainpbtypes.Block{addHead.block.Block}, []*blockchainpbtypes.Block{addHead.block.Block}, nil + } else if addHead.block.Block.PreviousBlockId == removeHead.block.Block.BlockId { // just appending - return []*blockchainpb.Block{removeHead.block.GetBlock()}, []*blockchainpb.Block{removeHead.block.GetBlock(), addHead.block.GetBlock()}, nil - } else if removeHead.block.Block.GetPreviousBlockId() == addHead.block.Block.GetBlockId() { + return []*blockchainpbtypes.Block{removeHead.block.Block}, []*blockchainpbtypes.Block{removeHead.block.Block, addHead.block.Block}, nil + } else if removeHead.block.Block.PreviousBlockId == addHead.block.Block.BlockId { // rollbacks should never happen return nil, nil, ErrRollback } @@ -111,8 +111,8 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* // this is the case if initialScanCount := bcm.currentScanCount - removeChain := make([]*blockchainpb.Block, 0) - addChain := make([]*blockchainpb.Block, 0) + removeChain := make([]*blockchainpbtypes.Block, 0) + addChain := make([]*blockchainpbtypes.Block, 0) currRemove := removeHead currAdd := addHead @@ -124,14 +124,14 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* // handle remove step if currRemove != nil { - removeChain = append(removeChain, currRemove.block.GetBlock()) + removeChain = append(removeChain, currRemove.block.Block) // check for intersection if currRemove.scanCount > initialScanCount { // remove chain intersects with add chain, remove chain is ok, add chain needs to be truncated // find currRemove in addChain - currRemoveBlockId := currRemove.block.Block.GetBlockId() - index := slices.IndexFunc(addChain, func(i *blockchainpb.Block) bool { return i.BlockId == currRemoveBlockId }) + currRemoveBlockId := currRemove.block.Block.BlockId + index := slices.IndexFunc(addChain, func(i *blockchainpbtypes.Block) bool { return i.BlockId == currRemoveBlockId }) if index == -1 { // should never happen bcm.logger.Log(logging.LevelError, "Intersection trucation failed (remove intersection case) - this should never happen", "blockId", utils.FormatBlockId(currRemoveBlockId)) @@ -147,14 +147,14 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* // handle add step if currAdd != nil { - addChain = append(addChain, currAdd.block.GetBlock()) + addChain = append(addChain, currAdd.block.Block) // check for intersection if currAdd.scanCount > initialScanCount { // add chain intersects with remove chain, add chain is ok, remove chain needs to be truncated // find addRemove in removeChain - currAddBlockId := currAdd.block.Block.GetBlockId() - index := slices.IndexFunc(removeChain, func(i *blockchainpb.Block) bool { return i.BlockId == currAddBlockId }) + currAddBlockId := currAdd.block.Block.BlockId + index := slices.IndexFunc(removeChain, func(i *blockchainpbtypes.Block) bool { return i.BlockId == currAddBlockId }) if index == -1 { // should never happen bcm.logger.Log(logging.LevelError, "Intersection trucation failed (add intersection case) - this should never happen", "blockId", utils.FormatBlockId(currAddBlockId)) @@ -207,7 +207,7 @@ func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) } // TODO: might need to add some "cleanup" to handle very old leaves in case this grows too much -func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { +func (bcm *bcmModule) addBlock(block *blockchainpbtypes.Block) error { bcm.logger.Log(logging.LevelInfo, "Adding block...", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(block.PreviousBlockId)) // check if block is already in the leaves, reject if so @@ -222,7 +222,7 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { if parent, ok := bcm.leaves[parentId]; ok { bcm.logger.Log(logging.LevelDebug, "Found parend in leaves", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(parentId)) blockNode := bcmBlock{ - block: &blockchainpb.BlockInternal{ + block: &blockchainpbtypes.BlockInternal{ Block: block, State: nil, }, @@ -252,7 +252,7 @@ func (bcm *bcmModule) addBlock(block *blockchainpb.Block) error { if currBlock.block.Block.BlockId == parentId { bcm.logger.Log(logging.LevelDebug, "Found parend in tree", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(parentId)) blockNode := bcmBlock{ - block: &blockchainpb.BlockInternal{ + block: &blockchainpbtypes.BlockInternal{ Block: block, State: nil, }, @@ -292,7 +292,7 @@ func (bcm *bcmModule) getHead() *bcmBlock { return bcm.head } -func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { +func (bcm *bcmModule) handleNewBlock(block *blockchainpbtypes.Block) { currentHead := bcm.getHead() // insert block if err := bcm.addBlock(block); err != nil { @@ -324,7 +324,7 @@ func (bcm *bcmModule) handleNewBlock(block *blockchainpb.Block) { bcm.sendTreeUpdate() } -func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { +func (bcm *bcmModule) handleNewChain(blocks []*blockchainpbtypes.Block) { currentHead := bcm.getHead() blockIds := make([]uint64, 0, len(blocks)) for _, v := range blocks { @@ -363,8 +363,8 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpb.Block) { } func (bcm *bcmModule) sendTreeUpdate() { - blocks := func() []*blockchainpb.Block { - blocks := make([]*blockchainpb.Block, 0, len(bcm.blocks)) + blocks := func() []*blockchainpbtypes.Block { + blocks := make([]*blockchainpbtypes.Block, 0, len(bcm.blocks)) for _, v := range bcm.blocks { blocks = append(blocks, v.block.Block) } @@ -378,7 +378,7 @@ func (bcm *bcmModule) sendTreeUpdate() { return leaves }() - blockTree := blockchainpb.Blocktree{Blocks: blocks, Leaves: leaves} + blockTree := blockchainpbtypes.Blocktree{Blocks: blocks, Leaves: leaves} interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", &blockTree, bcm.head.block.Block.BlockId) } @@ -388,7 +388,7 @@ func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, so return err } - chain := make([]*blockchainpb.BlockInternal, 0) + chain := make([]*blockchainpbtypes.BlockInternal, 0) // start with head currentBlock := bcm.head @@ -416,7 +416,7 @@ func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, so } -func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepb.State) error { +func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepbtypes.State) error { bcm.logger.Log(logging.LevelInfo, "Received register checkpoint", "blockId", utils.FormatBlockId(block_id)) if err := bcm.checkInitialization(); err != nil { return err @@ -441,11 +441,11 @@ func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepb.S return nil } -func (bcm *bcmModule) handleInitBlockchain(initialState *statepb.State) error { +func (bcm *bcmModule) handleInitBlockchain(initialState *statepbtypes.State) error { // initialize blockchain // making up a genisis block - genesis := &blockchainpb.Block{ + genesis := &blockchainpbtypes.Block{ BlockId: 0, PreviousBlockId: 0, Payload: nil, @@ -455,7 +455,7 @@ func (bcm *bcmModule) handleInitBlockchain(initialState *statepb.State) error { hash := utils.HashBlock(genesis) //uint64(0) genesis.BlockId = hash genesisBcm := bcmBlock{ - block: &blockchainpb.BlockInternal{ + block: &blockchainpbtypes.BlockInternal{ Block: genesis, State: config.InitialState, }, @@ -503,7 +503,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { return nil }) - bcmpbdsl.UponNewBlock(m, func(block *blockchainpb.Block) error { + bcmpbdsl.UponNewBlock(m, func(block *blockchainpbtypes.Block) error { if err := bcm.checkInitialization(); err != nil { return err } @@ -511,7 +511,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { return nil }) - bcmpbdsl.UponNewChain(m, func(blocks []*blockchainpb.Block) error { + bcmpbdsl.UponNewChain(m, func(blocks []*blockchainpbtypes.Block) error { bcm.logger.Log(logging.LevelInfo, "Received chain from synchronizer") if err := bcm.checkInitialization(); err != nil { return err @@ -535,7 +535,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { } bcm.logger.Log(logging.LevelDebug, "Found block in tree", "requestId", requestID) - bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block.GetBlock()) + bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block.Block) return nil }) @@ -545,7 +545,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { if err := bcm.checkInitialization(); err != nil { return err } - chain := make([]*blockchainpb.Block, 0) + chain := make([]*blockchainpbtypes.Block, 0) // for easier lookup... sourceBlockIdsMap := make(map[uint64]uint64) for _, v := range sourceBlockIds { diff --git a/samples/blockchain/communication.go b/samples/blockchain/communication.go index 0a2db85d0..fe437bd9e 100644 --- a/samples/blockchain/communication.go +++ b/samples/blockchain/communication.go @@ -6,10 +6,10 @@ import ( "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" communicationpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/dsl" communicationpbmsgs "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/msgs" + blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" t "github.com/filecoin-project/mir/pkg/types" "github.com/filecoin-project/mir/samples/blockchain/utils" @@ -22,7 +22,7 @@ func NewCommunication(otherNodes []t.NodeID, mangle bool, logger logging.Logger) return nil }) - communicationpbdsl.UponNewBlock(m, func(block *blockchainpb.Block) error { + communicationpbdsl.UponNewBlock(m, func(block *blockchainpbtypes.Block) error { // take the block and send it to all other nodes logger.Log(logging.LevelDebug, "broadcasting block", "blockId", utils.FormatBlockId(block.BlockId), "manlge", mangle) @@ -39,7 +39,7 @@ func NewCommunication(otherNodes []t.NodeID, mangle bool, logger logging.Logger) return nil }) - communicationpbdsl.UponNewBlockMessageReceived(m, func(from t.NodeID, block *blockchainpb.Block) error { + communicationpbdsl.UponNewBlockMessageReceived(m, func(from t.NodeID, block *blockchainpbtypes.Block) error { logger.Log(logging.LevelDebug, "new block received", "blockId", utils.FormatBlockId(block.BlockId)) bcmpbdsl.NewBlock(m, "bcm", block) diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index 5627f03af..b74fd02df 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -92,7 +92,7 @@ func main() { "bcm": NewBCM(logging.Decorate(logger, "BCM:\t")), "miner": NewMiner(logging.Decorate(logger, "Miner:\t")), "communication": NewCommunication(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), - "application": application.NewApplication(logging.Decorate(logger, "App:\t"), string(ownNodeID)), + "application": application.NewApplication(logging.Decorate(logger, "App:\t"), ownNodeID), "synchronizer": NewSynchronizer(ownNodeID, otherNodes, false, logging.Decorate(logger, "Sync:\t")), "timer": timer, "mangler": mangler, diff --git a/samples/blockchain/miner.go b/samples/blockchain/miner.go index 4df4d0bb2..6aa38bb08 100644 --- a/samples/blockchain/miner.go +++ b/samples/blockchain/miner.go @@ -8,13 +8,13 @@ import ( "github.com/filecoin-project/mir/pkg/events" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" applicationpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/events" bcmpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/events" communicationpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/events" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" "github.com/filecoin-project/mir/pkg/pb/eventpb" "github.com/filecoin-project/mir/samples/blockchain/utils" "github.com/go-errors/errors" @@ -27,7 +27,7 @@ const ( type blockRequest struct { HeadId uint64 - Payload *payloadpb.Payload + Payload *payloadpbtypes.Payload } type minerModule struct { @@ -58,7 +58,7 @@ func (m *minerModule) ApplyEvents(context context.Context, eventList *events.Eve case *eventpb.Event_Application: switch e := e.Application.Type.(type) { case *applicationpb.Event_PayloadResponse: - m.blockRequests <- blockRequest{e.PayloadResponse.GetHeadId(), e.PayloadResponse.GetPayload()} + m.blockRequests <- blockRequest{e.PayloadResponse.HeadId, payloadpbtypes.PayloadFromPb(e.PayloadResponse.Payload)} return nil default: return errors.Errorf("unknown event: %T", e) @@ -66,7 +66,7 @@ func (m *minerModule) ApplyEvents(context context.Context, eventList *events.Eve case *eventpb.Event_Miner: switch e := e.Miner.Type.(type) { case *minerpb.Event_NewHead: - m.eventsOut <- events.ListOf(applicationpbevents.PayloadRequest("application", e.NewHead.GetHeadId()).Pb()) + m.eventsOut <- events.ListOf(applicationpbevents.PayloadRequest("application", e.NewHead.HeadId).Pb()) default: return errors.Errorf("unknown miner event: %T", e) } @@ -93,7 +93,7 @@ func (m *minerModule) mineWorkerManager() { m.logger.Log(logging.LevelDebug, "Mining aborted", "headId", utils.FormatBlockId(blockRequest.HeadId)) return case <-time.After(delay): - block := &blockchainpb.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: time.Now().Unix()} + block := &blockchainpbtypes.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: time.Now().Unix()} hash, err := hashstructure.Hash(block, nil) if err != nil { m.logger.Log(logging.LevelError, "Failed to hash block", "error", err) diff --git a/samples/blockchain/synchronizer.go b/samples/blockchain/synchronizer.go index f65e420c5..27e9e9086 100644 --- a/samples/blockchain/synchronizer.go +++ b/samples/blockchain/synchronizer.go @@ -10,10 +10,10 @@ import ( "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" - "github.com/filecoin-project/mir/pkg/pb/blockchainpb" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" synchronizerpbmsgs "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/msgs" + blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" t "github.com/filecoin-project/mir/pkg/types" "github.com/filecoin-project/mir/samples/blockchain/utils" @@ -56,7 +56,7 @@ type syncRequest struct { nodesContacted []int // index of node in otherNodes } -func (sm *synchronizerModule) registerSyncRequest(block *blockchainpb.Block, leaves []uint64) (string, error) { +func (sm *synchronizerModule) registerSyncRequest(block *blockchainpbtypes.Block, leaves []uint64) (string, error) { requestId := fmt.Sprint(block.BlockId) + "-" + string(sm.nodeID) // check if request already exists if _, ok := sm.syncRequests[requestId]; ok { @@ -102,7 +102,7 @@ func (sm *synchronizerModule) contactNextNode(requestID string) error { return nil } -func (sm *synchronizerModule) handleSyncRequest(orphanBlock *blockchainpb.Block, leaveIds []uint64) error { +func (sm *synchronizerModule) handleSyncRequest(orphanBlock *blockchainpbtypes.Block, leaveIds []uint64) error { // register request requestId, err := sm.registerSyncRequest(orphanBlock, leaveIds) @@ -127,7 +127,7 @@ func (sm *synchronizerModule) handleSyncRequest(orphanBlock *blockchainpb.Block, return nil } -func (sm *synchronizerModule) handleChainResponseReceived(from t.NodeID, requestID string, found bool, chain []*blockchainpb.Block) error { +func (sm *synchronizerModule) handleChainResponseReceived(from t.NodeID, requestID string, found bool, chain []*blockchainpbtypes.Block) error { // check if request exists request, ok := sm.syncRequests[requestID] if !ok { @@ -190,7 +190,7 @@ func (sm *synchronizerModule) handleChainRequestReceived(from t.NodeID, requestI } -func (sm *synchronizerModule) handleGetChainResponse(requestID string, found bool, chain []*blockchainpb.Block) error { +func (sm *synchronizerModule) handleGetChainResponse(requestID string, found bool, chain []*blockchainpbtypes.Block) error { request, ok := sm.getRequests[requestID] if !ok { sm.externaLogger.Log(logging.LevelError, "Unknown get block request", "requestId", requestID, "mangle", sm.mangle) diff --git a/samples/blockchain/utils/hash.go b/samples/blockchain/utils/hash.go index 7528b9675..8561ab9e4 100644 --- a/samples/blockchain/utils/hash.go +++ b/samples/blockchain/utils/hash.go @@ -1,12 +1,12 @@ package utils import ( - "github.com/filecoin-project/mir/pkg/pb/blockchainpb" + blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" "github.com/mitchellh/hashstructure" ) -func HashBlock(block *blockchainpb.Block) uint64 { - hashBlock := &blockchainpb.Block{BlockId: 0, PreviousBlockId: block.PreviousBlockId, Payload: block.Payload, Timestamp: block.Timestamp} +func HashBlock(block *blockchainpbtypes.Block) uint64 { + hashBlock := &blockchainpbtypes.Block{BlockId: 0, PreviousBlockId: block.PreviousBlockId, Payload: block.Payload, Timestamp: block.Timestamp} hash, err := hashstructure.Hash(hashBlock, nil) if err != nil { panic(err) From ade03b57bb58a75fc37ba9100bc4d2bc777634a8 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sun, 28 Jan 2024 17:30:37 +0100 Subject: [PATCH 15/46] app logic val, bunch of fixes --- .../applicationpb/applicationpb.pb.go | 295 ++++++++++-------- .../applicationpb/dsl/emit.mir.go | 20 +- .../applicationpb/dsl/upon.mir.go | 24 +- .../applicationpb/events/events.mir.go | 33 +- .../applicationpb/oneof_interfaces.mir.go | 4 +- .../applicationpb/types/types.mir.go | 111 ++++--- .../applicationpb/applicationpb.proto | 23 +- samples/blockchain/application/application.go | 46 ++- samples/blockchain/bcm.go | 191 ++++++------ samples/blockchain/run.sh | 8 +- .../blockchain/wsinterceptor/wsinterceptor.go | 2 +- 11 files changed, 427 insertions(+), 330 deletions(-) diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go index d26d5c7b4..202017a39 100644 --- a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go @@ -87,14 +87,14 @@ func (x *Event) GetNewHead() *NewHead { return nil } -func (x *Event) GetVerifyBlockRequest() *VerifyBlockRequest { +func (x *Event) GetVerifyBlockRequest() *VerifyBlocksRequest { if x, ok := x.GetType().(*Event_VerifyBlockRequest); ok { return x.VerifyBlockRequest } return nil } -func (x *Event) GetVerifyBlockResponse() *VerifyBlockResponse { +func (x *Event) GetVerifyBlockResponse() *VerifyBlocksResponse { if x, ok := x.GetType().(*Event_VerifyBlockResponse); ok { return x.VerifyBlockResponse } @@ -139,11 +139,11 @@ type Event_NewHead struct { } type Event_VerifyBlockRequest struct { - VerifyBlockRequest *VerifyBlockRequest `protobuf:"bytes,11,opt,name=verify_block_request,json=verifyBlockRequest,proto3,oneof"` + VerifyBlockRequest *VerifyBlocksRequest `protobuf:"bytes,11,opt,name=verify_block_request,json=verifyBlockRequest,proto3,oneof"` } type Event_VerifyBlockResponse struct { - VerifyBlockResponse *VerifyBlockResponse `protobuf:"bytes,12,opt,name=verify_block_response,json=verifyBlockResponse,proto3,oneof"` + VerifyBlockResponse *VerifyBlocksResponse `protobuf:"bytes,12,opt,name=verify_block_response,json=verifyBlockResponse,proto3,oneof"` } type Event_PayloadRequest struct { @@ -226,17 +226,18 @@ func (x *NewHead) GetHeadId() uint64 { return 0 } -type VerifyBlockRequest struct { +type VerifyBlocksRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - Block *blockchainpb.Block `protobuf:"bytes,2,opt,name=block,proto3" json:"block,omitempty"` + CheckpointState *statepb.State `protobuf:"bytes,1,opt,name=checkpoint_state,json=checkpointState,proto3" json:"checkpoint_state,omitempty"` + ChainCheckpointToStart []*blockchainpb.Block `protobuf:"bytes,2,rep,name=chain_checkpoint_to_start,json=chainCheckpointToStart,proto3" json:"chain_checkpoint_to_start,omitempty"` + ChainToVerify []*blockchainpb.Block `protobuf:"bytes,3,rep,name=chain_to_verify,json=chainToVerify,proto3" json:"chain_to_verify,omitempty"` } -func (x *VerifyBlockRequest) Reset() { - *x = VerifyBlockRequest{} +func (x *VerifyBlocksRequest) Reset() { + *x = VerifyBlocksRequest{} if protoimpl.UnsafeEnabled { mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -244,13 +245,13 @@ func (x *VerifyBlockRequest) Reset() { } } -func (x *VerifyBlockRequest) String() string { +func (x *VerifyBlocksRequest) String() string { return protoimpl.X.MessageStringOf(x) } -func (*VerifyBlockRequest) ProtoMessage() {} +func (*VerifyBlocksRequest) ProtoMessage() {} -func (x *VerifyBlockRequest) ProtoReflect() protoreflect.Message { +func (x *VerifyBlocksRequest) ProtoReflect() protoreflect.Message { mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -262,36 +263,42 @@ func (x *VerifyBlockRequest) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use VerifyBlockRequest.ProtoReflect.Descriptor instead. -func (*VerifyBlockRequest) Descriptor() ([]byte, []int) { +// Deprecated: Use VerifyBlocksRequest.ProtoReflect.Descriptor instead. +func (*VerifyBlocksRequest) Descriptor() ([]byte, []int) { return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{2} } -func (x *VerifyBlockRequest) GetRequestId() uint64 { +func (x *VerifyBlocksRequest) GetCheckpointState() *statepb.State { if x != nil { - return x.RequestId + return x.CheckpointState } - return 0 + return nil } -func (x *VerifyBlockRequest) GetBlock() *blockchainpb.Block { +func (x *VerifyBlocksRequest) GetChainCheckpointToStart() []*blockchainpb.Block { if x != nil { - return x.Block + return x.ChainCheckpointToStart } return nil } -type VerifyBlockResponse struct { +func (x *VerifyBlocksRequest) GetChainToVerify() []*blockchainpb.Block { + if x != nil { + return x.ChainToVerify + } + return nil +} + +type VerifyBlocksResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId uint64 `protobuf:"varint,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - Ok bool `protobuf:"varint,2,opt,name=ok,proto3" json:"ok,omitempty"` + VerifiedBlocks []*blockchainpb.Block `protobuf:"bytes,1,rep,name=verified_blocks,json=verifiedBlocks,proto3" json:"verified_blocks,omitempty"` } -func (x *VerifyBlockResponse) Reset() { - *x = VerifyBlockResponse{} +func (x *VerifyBlocksResponse) Reset() { + *x = VerifyBlocksResponse{} if protoimpl.UnsafeEnabled { mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -299,13 +306,13 @@ func (x *VerifyBlockResponse) Reset() { } } -func (x *VerifyBlockResponse) String() string { +func (x *VerifyBlocksResponse) String() string { return protoimpl.X.MessageStringOf(x) } -func (*VerifyBlockResponse) ProtoMessage() {} +func (*VerifyBlocksResponse) ProtoMessage() {} -func (x *VerifyBlockResponse) ProtoReflect() protoreflect.Message { +func (x *VerifyBlocksResponse) ProtoReflect() protoreflect.Message { mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -317,23 +324,16 @@ func (x *VerifyBlockResponse) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use VerifyBlockResponse.ProtoReflect.Descriptor instead. -func (*VerifyBlockResponse) Descriptor() ([]byte, []int) { +// Deprecated: Use VerifyBlocksResponse.ProtoReflect.Descriptor instead. +func (*VerifyBlocksResponse) Descriptor() ([]byte, []int) { return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{3} } -func (x *VerifyBlockResponse) GetRequestId() uint64 { - if x != nil { - return x.RequestId - } - return 0 -} - -func (x *VerifyBlockResponse) GetOk() bool { +func (x *VerifyBlocksResponse) GetVerifiedBlocks() []*blockchainpb.Block { if x != nil { - return x.Ok + return x.VerifiedBlocks } - return false + return nil } type ForkUpdate struct { @@ -341,9 +341,10 @@ type ForkUpdate struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RemovedChain *blockchainpb.Blockchain `protobuf:"bytes,1,opt,name=removed_chain,json=removedChain,proto3" json:"removed_chain,omitempty"` - AddedChain *blockchainpb.Blockchain `protobuf:"bytes,2,opt,name=added_chain,json=addedChain,proto3" json:"added_chain,omitempty"` - ForkState *statepb.State `protobuf:"bytes,3,opt,name=fork_state,json=forkState,proto3" json:"fork_state,omitempty"` + RemovedChain *blockchainpb.Blockchain `protobuf:"bytes,1,opt,name=removed_chain,json=removedChain,proto3" json:"removed_chain,omitempty"` + AddedChain *blockchainpb.Blockchain `protobuf:"bytes,2,opt,name=added_chain,json=addedChain,proto3" json:"added_chain,omitempty"` + CheckpointToForkRoot *blockchainpb.Blockchain `protobuf:"bytes,3,opt,name=checkpoint_to_fork_root,json=checkpointToForkRoot,proto3" json:"checkpoint_to_fork_root,omitempty"` + CheckpointState *statepb.State `protobuf:"bytes,4,opt,name=checkpoint_state,json=checkpointState,proto3" json:"checkpoint_state,omitempty"` } func (x *ForkUpdate) Reset() { @@ -392,9 +393,16 @@ func (x *ForkUpdate) GetAddedChain() *blockchainpb.Blockchain { return nil } -func (x *ForkUpdate) GetForkState() *statepb.State { +func (x *ForkUpdate) GetCheckpointToForkRoot() *blockchainpb.Blockchain { + if x != nil { + return x.CheckpointToForkRoot + } + return nil +} + +func (x *ForkUpdate) GetCheckpointState() *statepb.State { if x != nil { - return x.ForkState + return x.CheckpointState } return nil } @@ -564,83 +572,98 @@ var file_blockchainpb_applicationpb_applicationpb_proto_rawDesc = []byte{ 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x04, 0x0a, 0x05, 0x45, + 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9c, 0x04, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x48, 0x00, - 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x55, 0x0a, 0x14, 0x76, 0x65, 0x72, + 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x56, 0x0a, 0x14, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, + 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x12, 0x76, 0x65, - 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x58, 0x0a, 0x15, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x22, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, - 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x13, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0f, 0x70, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x14, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, - 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, - 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x50, - 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, - 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, - 0x42, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, - 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, - 0x70, 0x75, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, 0x48, 0x65, - 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x12, 0x76, + 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x59, 0x0a, 0x15, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x23, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x13, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0f, + 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, + 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x48, 0x00, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x75, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, + 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, 0x70, + 0x75, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x49, 0x6e, 0x70, 0x75, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, + 0x49, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, + 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, + 0x48, 0x65, 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, + 0xa6, 0x1d, 0x01, 0x22, 0xe3, 0x01, 0x0a, 0x13, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x10, 0x63, + 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x19, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x74, + 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x16, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, + 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, + 0x74, 0x6f, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x6f, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5a, 0x0a, 0x14, 0x56, 0x65, 0x72, + 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x3c, 0x0a, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x0e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, + 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x98, 0x02, 0x0a, 0x0a, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x12, 0x39, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x4f, + 0x0a, 0x17, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, + 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x14, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x6f, 0x46, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, + 0x39, 0x0a, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, + 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, - 0x01, 0x22, 0x64, 0x0a, 0x12, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x4a, 0x0a, 0x13, 0x56, 0x65, 0x72, 0x69, 0x66, - 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x0e, 0x0a, - 0x02, 0x6f, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x3a, 0x04, 0x98, - 0xa6, 0x1d, 0x01, 0x22, 0xbb, 0x01, 0x0a, 0x0a, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x12, 0x39, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x52, 0x0a, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2d, 0x0a, 0x0a, - 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x09, 0x66, 0x6f, 0x72, 0x6b, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, - 0x01, 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, - 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, - 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, - 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x0c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, - 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, - 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x01, 0x22, 0x5e, 0x0a, 0x0f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, 0x0a, + 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, + 0x01, 0x22, 0x28, 0x0a, 0x0c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, + 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x04, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, + 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -659,35 +682,39 @@ var file_blockchainpb_applicationpb_applicationpb_proto_msgTypes = make([]protoi var file_blockchainpb_applicationpb_applicationpb_proto_goTypes = []interface{}{ (*Event)(nil), // 0: applicationpb.Event (*NewHead)(nil), // 1: applicationpb.NewHead - (*VerifyBlockRequest)(nil), // 2: applicationpb.VerifyBlockRequest - (*VerifyBlockResponse)(nil), // 3: applicationpb.VerifyBlockResponse + (*VerifyBlocksRequest)(nil), // 2: applicationpb.VerifyBlocksRequest + (*VerifyBlocksResponse)(nil), // 3: applicationpb.VerifyBlocksResponse (*ForkUpdate)(nil), // 4: applicationpb.ForkUpdate (*PayloadRequest)(nil), // 5: applicationpb.PayloadRequest (*PayloadResponse)(nil), // 6: applicationpb.PayloadResponse (*MessageInput)(nil), // 7: applicationpb.MessageInput - (*blockchainpb.Block)(nil), // 8: blockchainpb.Block - (*blockchainpb.Blockchain)(nil), // 9: blockchainpb.Blockchain - (*statepb.State)(nil), // 10: statepb.State + (*statepb.State)(nil), // 8: statepb.State + (*blockchainpb.Block)(nil), // 9: blockchainpb.Block + (*blockchainpb.Blockchain)(nil), // 10: blockchainpb.Blockchain (*payloadpb.Payload)(nil), // 11: payloadpb.Payload } var file_blockchainpb_applicationpb_applicationpb_proto_depIdxs = []int32{ 1, // 0: applicationpb.Event.new_head:type_name -> applicationpb.NewHead - 2, // 1: applicationpb.Event.verify_block_request:type_name -> applicationpb.VerifyBlockRequest - 3, // 2: applicationpb.Event.verify_block_response:type_name -> applicationpb.VerifyBlockResponse + 2, // 1: applicationpb.Event.verify_block_request:type_name -> applicationpb.VerifyBlocksRequest + 3, // 2: applicationpb.Event.verify_block_response:type_name -> applicationpb.VerifyBlocksResponse 5, // 3: applicationpb.Event.payload_request:type_name -> applicationpb.PayloadRequest 6, // 4: applicationpb.Event.payload_response:type_name -> applicationpb.PayloadResponse 4, // 5: applicationpb.Event.fork_update:type_name -> applicationpb.ForkUpdate 7, // 6: applicationpb.Event.message_input:type_name -> applicationpb.MessageInput - 8, // 7: applicationpb.VerifyBlockRequest.block:type_name -> blockchainpb.Block - 9, // 8: applicationpb.ForkUpdate.removed_chain:type_name -> blockchainpb.Blockchain - 9, // 9: applicationpb.ForkUpdate.added_chain:type_name -> blockchainpb.Blockchain - 10, // 10: applicationpb.ForkUpdate.fork_state:type_name -> statepb.State - 11, // 11: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload - 12, // [12:12] is the sub-list for method output_type - 12, // [12:12] is the sub-list for method input_type - 12, // [12:12] is the sub-list for extension type_name - 12, // [12:12] is the sub-list for extension extendee - 0, // [0:12] is the sub-list for field type_name + 8, // 7: applicationpb.VerifyBlocksRequest.checkpoint_state:type_name -> statepb.State + 9, // 8: applicationpb.VerifyBlocksRequest.chain_checkpoint_to_start:type_name -> blockchainpb.Block + 9, // 9: applicationpb.VerifyBlocksRequest.chain_to_verify:type_name -> blockchainpb.Block + 9, // 10: applicationpb.VerifyBlocksResponse.verified_blocks:type_name -> blockchainpb.Block + 10, // 11: applicationpb.ForkUpdate.removed_chain:type_name -> blockchainpb.Blockchain + 10, // 12: applicationpb.ForkUpdate.added_chain:type_name -> blockchainpb.Blockchain + 10, // 13: applicationpb.ForkUpdate.checkpoint_to_fork_root:type_name -> blockchainpb.Blockchain + 8, // 14: applicationpb.ForkUpdate.checkpoint_state:type_name -> statepb.State + 11, // 15: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload + 16, // [16:16] is the sub-list for method output_type + 16, // [16:16] is the sub-list for method input_type + 16, // [16:16] is the sub-list for extension type_name + 16, // [16:16] is the sub-list for extension extendee + 0, // [0:16] is the sub-list for field type_name } func init() { file_blockchainpb_applicationpb_applicationpb_proto_init() } @@ -721,7 +748,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyBlockRequest); i { + switch v := v.(*VerifyBlocksRequest); i { case 0: return &v.state case 1: @@ -733,7 +760,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyBlockResponse); i { + switch v := v.(*VerifyBlocksResponse); i { case 0: return &v.state case 1: diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go index 8b56a037d..e1eb5cfa7 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go @@ -5,9 +5,9 @@ package applicationpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/events" - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" - types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" - types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types "github.com/filecoin-project/mir/pkg/types" ) @@ -17,24 +17,24 @@ func NewHead(m dsl.Module, destModule types.ModuleID, headId uint64) { dsl.EmitMirEvent(m, events.NewHead(destModule, headId)) } -func VerifyBlockRequest(m dsl.Module, destModule types.ModuleID, requestId uint64, block *types1.Block) { - dsl.EmitMirEvent(m, events.VerifyBlockRequest(destModule, requestId, block)) +func VerifyBlocksRequest(m dsl.Module, destModule types.ModuleID, checkpointState *types1.State, chainCheckpointToStart []*types2.Block, chainToVerify []*types2.Block) { + dsl.EmitMirEvent(m, events.VerifyBlocksRequest(destModule, checkpointState, chainCheckpointToStart, chainToVerify)) } -func VerifyBlockResponse(m dsl.Module, destModule types.ModuleID, requestId uint64, ok bool) { - dsl.EmitMirEvent(m, events.VerifyBlockResponse(destModule, requestId, ok)) +func VerifyBlocksResponse(m dsl.Module, destModule types.ModuleID, verifiedBlocks []*types2.Block) { + dsl.EmitMirEvent(m, events.VerifyBlocksResponse(destModule, verifiedBlocks)) } func PayloadRequest(m dsl.Module, destModule types.ModuleID, headId uint64) { dsl.EmitMirEvent(m, events.PayloadRequest(destModule, headId)) } -func PayloadResponse(m dsl.Module, destModule types.ModuleID, headId uint64, payload *types2.Payload) { +func PayloadResponse(m dsl.Module, destModule types.ModuleID, headId uint64, payload *types3.Payload) { dsl.EmitMirEvent(m, events.PayloadResponse(destModule, headId, payload)) } -func ForkUpdate(m dsl.Module, destModule types.ModuleID, removedChain *types1.Blockchain, addedChain *types1.Blockchain, forkState *types3.State) { - dsl.EmitMirEvent(m, events.ForkUpdate(destModule, removedChain, addedChain, forkState)) +func ForkUpdate(m dsl.Module, destModule types.ModuleID, removedChain *types2.Blockchain, addedChain *types2.Blockchain, checkpointToForkRoot *types2.Blockchain, checkpointState *types1.State) { + dsl.EmitMirEvent(m, events.ForkUpdate(destModule, removedChain, addedChain, checkpointToForkRoot, checkpointState)) } func MessageInput(m dsl.Module, destModule types.ModuleID, text string) { diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go index edb886702..8a8669017 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go @@ -5,9 +5,9 @@ package applicationpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" - types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" - types4 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + types4 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" ) @@ -30,15 +30,15 @@ func UponNewHead(m dsl.Module, handler func(headId uint64) error) { }) } -func UponVerifyBlockRequest(m dsl.Module, handler func(requestId uint64, block *types2.Block) error) { - UponEvent[*types.Event_VerifyBlockRequest](m, func(ev *types.VerifyBlockRequest) error { - return handler(ev.RequestId, ev.Block) +func UponVerifyBlocksRequest(m dsl.Module, handler func(checkpointState *types2.State, chainCheckpointToStart []*types3.Block, chainToVerify []*types3.Block) error) { + UponEvent[*types.Event_VerifyBlockRequest](m, func(ev *types.VerifyBlocksRequest) error { + return handler(ev.CheckpointState, ev.ChainCheckpointToStart, ev.ChainToVerify) }) } -func UponVerifyBlockResponse(m dsl.Module, handler func(requestId uint64, ok bool) error) { - UponEvent[*types.Event_VerifyBlockResponse](m, func(ev *types.VerifyBlockResponse) error { - return handler(ev.RequestId, ev.Ok) +func UponVerifyBlocksResponse(m dsl.Module, handler func(verifiedBlocks []*types3.Block) error) { + UponEvent[*types.Event_VerifyBlockResponse](m, func(ev *types.VerifyBlocksResponse) error { + return handler(ev.VerifiedBlocks) }) } @@ -48,15 +48,15 @@ func UponPayloadRequest(m dsl.Module, handler func(headId uint64) error) { }) } -func UponPayloadResponse(m dsl.Module, handler func(headId uint64, payload *types3.Payload) error) { +func UponPayloadResponse(m dsl.Module, handler func(headId uint64, payload *types4.Payload) error) { UponEvent[*types.Event_PayloadResponse](m, func(ev *types.PayloadResponse) error { return handler(ev.HeadId, ev.Payload) }) } -func UponForkUpdate(m dsl.Module, handler func(removedChain *types2.Blockchain, addedChain *types2.Blockchain, forkState *types4.State) error) { +func UponForkUpdate(m dsl.Module, handler func(removedChain *types3.Blockchain, addedChain *types3.Blockchain, checkpointToForkRoot *types3.Blockchain, checkpointState *types2.State) error) { UponEvent[*types.Event_ForkUpdate](m, func(ev *types.ForkUpdate) error { - return handler(ev.RemovedChain, ev.AddedChain, ev.ForkState) + return handler(ev.RemovedChain, ev.AddedChain, ev.CheckpointToForkRoot, ev.CheckpointState) }) } diff --git a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go index 8f5f1fc76..dad67ef50 100644 --- a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go @@ -4,9 +4,9 @@ package applicationpbevents import ( types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" - types4 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" - types5 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" - types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + types5 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types4 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) @@ -26,15 +26,16 @@ func NewHead(destModule types.ModuleID, headId uint64) *types1.Event { } } -func VerifyBlockRequest(destModule types.ModuleID, requestId uint64, block *types3.Block) *types1.Event { +func VerifyBlocksRequest(destModule types.ModuleID, checkpointState *types3.State, chainCheckpointToStart []*types4.Block, chainToVerify []*types4.Block) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Application{ Application: &types2.Event{ Type: &types2.Event_VerifyBlockRequest{ - VerifyBlockRequest: &types2.VerifyBlockRequest{ - RequestId: requestId, - Block: block, + VerifyBlockRequest: &types2.VerifyBlocksRequest{ + CheckpointState: checkpointState, + ChainCheckpointToStart: chainCheckpointToStart, + ChainToVerify: chainToVerify, }, }, }, @@ -42,15 +43,14 @@ func VerifyBlockRequest(destModule types.ModuleID, requestId uint64, block *type } } -func VerifyBlockResponse(destModule types.ModuleID, requestId uint64, ok bool) *types1.Event { +func VerifyBlocksResponse(destModule types.ModuleID, verifiedBlocks []*types4.Block) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Application{ Application: &types2.Event{ Type: &types2.Event_VerifyBlockResponse{ - VerifyBlockResponse: &types2.VerifyBlockResponse{ - RequestId: requestId, - Ok: ok, + VerifyBlockResponse: &types2.VerifyBlocksResponse{ + VerifiedBlocks: verifiedBlocks, }, }, }, @@ -73,7 +73,7 @@ func PayloadRequest(destModule types.ModuleID, headId uint64) *types1.Event { } } -func PayloadResponse(destModule types.ModuleID, headId uint64, payload *types4.Payload) *types1.Event { +func PayloadResponse(destModule types.ModuleID, headId uint64, payload *types5.Payload) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Application{ @@ -89,16 +89,17 @@ func PayloadResponse(destModule types.ModuleID, headId uint64, payload *types4.P } } -func ForkUpdate(destModule types.ModuleID, removedChain *types3.Blockchain, addedChain *types3.Blockchain, forkState *types5.State) *types1.Event { +func ForkUpdate(destModule types.ModuleID, removedChain *types4.Blockchain, addedChain *types4.Blockchain, checkpointToForkRoot *types4.Blockchain, checkpointState *types3.State) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Application{ Application: &types2.Event{ Type: &types2.Event_ForkUpdate{ ForkUpdate: &types2.ForkUpdate{ - RemovedChain: removedChain, - AddedChain: addedChain, - ForkState: forkState, + RemovedChain: removedChain, + AddedChain: addedChain, + CheckpointToForkRoot: checkpointToForkRoot, + CheckpointState: checkpointState, }, }, }, diff --git a/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go index 1eaed08ae..b003a79de 100644 --- a/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go @@ -13,11 +13,11 @@ func (w *Event_NewHead) Unwrap() *NewHead { return w.NewHead } -func (w *Event_VerifyBlockRequest) Unwrap() *VerifyBlockRequest { +func (w *Event_VerifyBlockRequest) Unwrap() *VerifyBlocksRequest { return w.VerifyBlockRequest } -func (w *Event_VerifyBlockResponse) Unwrap() *VerifyBlockResponse { +func (w *Event_VerifyBlockResponse) Unwrap() *VerifyBlocksResponse { return w.VerifyBlockResponse } diff --git a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go index 488857e99..27e8117a7 100644 --- a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go @@ -4,10 +4,12 @@ package applicationpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + types2 "github.com/filecoin-project/mir/codegen/model/types" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" applicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" - types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" - types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -34,9 +36,9 @@ func Event_TypeFromPb(pb applicationpb.Event_Type) Event_Type { case *applicationpb.Event_NewHead: return &Event_NewHead{NewHead: NewHeadFromPb(pb.NewHead)} case *applicationpb.Event_VerifyBlockRequest: - return &Event_VerifyBlockRequest{VerifyBlockRequest: VerifyBlockRequestFromPb(pb.VerifyBlockRequest)} + return &Event_VerifyBlockRequest{VerifyBlockRequest: VerifyBlocksRequestFromPb(pb.VerifyBlockRequest)} case *applicationpb.Event_VerifyBlockResponse: - return &Event_VerifyBlockResponse{VerifyBlockResponse: VerifyBlockResponseFromPb(pb.VerifyBlockResponse)} + return &Event_VerifyBlockResponse{VerifyBlockResponse: VerifyBlocksResponseFromPb(pb.VerifyBlockResponse)} case *applicationpb.Event_PayloadRequest: return &Event_PayloadRequest{PayloadRequest: PayloadRequestFromPb(pb.PayloadRequest)} case *applicationpb.Event_PayloadResponse: @@ -74,12 +76,12 @@ func (*Event_NewHead) MirReflect() mirreflect.Type { } type Event_VerifyBlockRequest struct { - VerifyBlockRequest *VerifyBlockRequest + VerifyBlockRequest *VerifyBlocksRequest } func (*Event_VerifyBlockRequest) isEvent_Type() {} -func (w *Event_VerifyBlockRequest) Unwrap() *VerifyBlockRequest { +func (w *Event_VerifyBlockRequest) Unwrap() *VerifyBlocksRequest { return w.VerifyBlockRequest } @@ -98,12 +100,12 @@ func (*Event_VerifyBlockRequest) MirReflect() mirreflect.Type { } type Event_VerifyBlockResponse struct { - VerifyBlockResponse *VerifyBlockResponse + VerifyBlockResponse *VerifyBlocksResponse } func (*Event_VerifyBlockResponse) isEvent_Type() {} -func (w *Event_VerifyBlockResponse) Unwrap() *VerifyBlockResponse { +func (w *Event_VerifyBlockResponse) Unwrap() *VerifyBlocksResponse { return w.VerifyBlockResponse } @@ -273,76 +275,89 @@ func (*NewHead) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.NewHead]()} } -type VerifyBlockRequest struct { - RequestId uint64 - Block *types.Block +type VerifyBlocksRequest struct { + CheckpointState *types.State + ChainCheckpointToStart []*types1.Block + ChainToVerify []*types1.Block } -func VerifyBlockRequestFromPb(pb *applicationpb.VerifyBlockRequest) *VerifyBlockRequest { +func VerifyBlocksRequestFromPb(pb *applicationpb.VerifyBlocksRequest) *VerifyBlocksRequest { if pb == nil { return nil } - return &VerifyBlockRequest{ - RequestId: pb.RequestId, - Block: types.BlockFromPb(pb.Block), + return &VerifyBlocksRequest{ + CheckpointState: types.StateFromPb(pb.CheckpointState), + ChainCheckpointToStart: types2.ConvertSlice(pb.ChainCheckpointToStart, func(t *blockchainpb.Block) *types1.Block { + return types1.BlockFromPb(t) + }), + ChainToVerify: types2.ConvertSlice(pb.ChainToVerify, func(t *blockchainpb.Block) *types1.Block { + return types1.BlockFromPb(t) + }), } } -func (m *VerifyBlockRequest) Pb() *applicationpb.VerifyBlockRequest { +func (m *VerifyBlocksRequest) Pb() *applicationpb.VerifyBlocksRequest { if m == nil { return nil } - pbMessage := &applicationpb.VerifyBlockRequest{} + pbMessage := &applicationpb.VerifyBlocksRequest{} { - pbMessage.RequestId = m.RequestId - if m.Block != nil { - pbMessage.Block = (m.Block).Pb() + if m.CheckpointState != nil { + pbMessage.CheckpointState = (m.CheckpointState).Pb() } + pbMessage.ChainCheckpointToStart = types2.ConvertSlice(m.ChainCheckpointToStart, func(t *types1.Block) *blockchainpb.Block { + return (t).Pb() + }) + pbMessage.ChainToVerify = types2.ConvertSlice(m.ChainToVerify, func(t *types1.Block) *blockchainpb.Block { + return (t).Pb() + }) } return pbMessage } -func (*VerifyBlockRequest) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.VerifyBlockRequest]()} +func (*VerifyBlocksRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.VerifyBlocksRequest]()} } -type VerifyBlockResponse struct { - RequestId uint64 - Ok bool +type VerifyBlocksResponse struct { + VerifiedBlocks []*types1.Block } -func VerifyBlockResponseFromPb(pb *applicationpb.VerifyBlockResponse) *VerifyBlockResponse { +func VerifyBlocksResponseFromPb(pb *applicationpb.VerifyBlocksResponse) *VerifyBlocksResponse { if pb == nil { return nil } - return &VerifyBlockResponse{ - RequestId: pb.RequestId, - Ok: pb.Ok, + return &VerifyBlocksResponse{ + VerifiedBlocks: types2.ConvertSlice(pb.VerifiedBlocks, func(t *blockchainpb.Block) *types1.Block { + return types1.BlockFromPb(t) + }), } } -func (m *VerifyBlockResponse) Pb() *applicationpb.VerifyBlockResponse { +func (m *VerifyBlocksResponse) Pb() *applicationpb.VerifyBlocksResponse { if m == nil { return nil } - pbMessage := &applicationpb.VerifyBlockResponse{} + pbMessage := &applicationpb.VerifyBlocksResponse{} { - pbMessage.RequestId = m.RequestId - pbMessage.Ok = m.Ok + pbMessage.VerifiedBlocks = types2.ConvertSlice(m.VerifiedBlocks, func(t *types1.Block) *blockchainpb.Block { + return (t).Pb() + }) } return pbMessage } -func (*VerifyBlockResponse) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.VerifyBlockResponse]()} +func (*VerifyBlocksResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.VerifyBlocksResponse]()} } type ForkUpdate struct { - RemovedChain *types.Blockchain - AddedChain *types.Blockchain - ForkState *types1.State + RemovedChain *types1.Blockchain + AddedChain *types1.Blockchain + CheckpointToForkRoot *types1.Blockchain + CheckpointState *types.State } func ForkUpdateFromPb(pb *applicationpb.ForkUpdate) *ForkUpdate { @@ -350,9 +365,10 @@ func ForkUpdateFromPb(pb *applicationpb.ForkUpdate) *ForkUpdate { return nil } return &ForkUpdate{ - RemovedChain: types.BlockchainFromPb(pb.RemovedChain), - AddedChain: types.BlockchainFromPb(pb.AddedChain), - ForkState: types1.StateFromPb(pb.ForkState), + RemovedChain: types1.BlockchainFromPb(pb.RemovedChain), + AddedChain: types1.BlockchainFromPb(pb.AddedChain), + CheckpointToForkRoot: types1.BlockchainFromPb(pb.CheckpointToForkRoot), + CheckpointState: types.StateFromPb(pb.CheckpointState), } } @@ -368,8 +384,11 @@ func (m *ForkUpdate) Pb() *applicationpb.ForkUpdate { if m.AddedChain != nil { pbMessage.AddedChain = (m.AddedChain).Pb() } - if m.ForkState != nil { - pbMessage.ForkState = (m.ForkState).Pb() + if m.CheckpointToForkRoot != nil { + pbMessage.CheckpointToForkRoot = (m.CheckpointToForkRoot).Pb() + } + if m.CheckpointState != nil { + pbMessage.CheckpointState = (m.CheckpointState).Pb() } } @@ -411,7 +430,7 @@ func (*PayloadRequest) MirReflect() mirreflect.Type { type PayloadResponse struct { HeadId uint64 - Payload *types2.Payload + Payload *types3.Payload } func PayloadResponseFromPb(pb *applicationpb.PayloadResponse) *PayloadResponse { @@ -420,7 +439,7 @@ func PayloadResponseFromPb(pb *applicationpb.PayloadResponse) *PayloadResponse { } return &PayloadResponse{ HeadId: pb.HeadId, - Payload: types2.PayloadFromPb(pb.Payload), + Payload: types3.PayloadFromPb(pb.Payload), } } diff --git a/protos/blockchainpb/applicationpb/applicationpb.proto b/protos/blockchainpb/applicationpb/applicationpb.proto index e59f2a181..c449d147a 100644 --- a/protos/blockchainpb/applicationpb/applicationpb.proto +++ b/protos/blockchainpb/applicationpb/applicationpb.proto @@ -17,8 +17,8 @@ message Event { // Application-application events NewHead new_head = 10; - VerifyBlockRequest verify_block_request = 11; - VerifyBlockResponse verify_block_response = 12; + VerifyBlocksRequest verify_block_request = 11; + VerifyBlocksResponse verify_block_response = 12; // Transaction management application events PayloadRequest payload_request = 20; @@ -37,26 +37,27 @@ message NewHead { uint64 head_id = 1; } -message VerifyBlockRequest { +message VerifyBlocksRequest { option (mir.event) = true; - uint64 request_id = 1; - blockchainpb.Block block = 2; + statepb.State checkpoint_state = 1; + repeated blockchainpb.Block chain_checkpoint_to_start = 2; + repeated blockchainpb.Block chain_to_verify = 3; } -message VerifyBlockResponse { +message VerifyBlocksResponse { option (mir.event) = true; - uint64 request_id = 1; - bool ok = 2; + repeated blockchainpb.Block verified_blocks = 1; } message ForkUpdate { option (mir.event) = true; - blockchainpb.Blockchain removed_chain = 1; - blockchainpb.Blockchain added_chain = 2; - statepb.State fork_state = 3; + blockchainpb.Blockchain removed_chain = 1; + blockchainpb.Blockchain added_chain = 2; + blockchainpb.Blockchain checkpoint_to_fork_root = 3; + statepb.State checkpoint_state = 4; } // Transaction management application events diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index cbb3b9ff2..a47ebd605 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -63,7 +63,7 @@ func applyBlockToState(state *statepbtypes.State, block *blockchainpbtypes.Block } } -func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain *blockchainpbtypes.Blockchain, forkState *statepbtypes.State) error { +func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain, chain *blockchainpbtypes.Blockchain, checkpointState *statepbtypes.State) error { am.logger.Log(logging.LevelInfo, "Processing fork update", "poolSize", am.tm.PoolSize()) // add "remove chain" transactions to pool @@ -76,8 +76,12 @@ func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain *blockcha am.tm.RemovePayload(block.Payload) } - // apply state to fork state - state := forkState + state := checkpointState + // compute state at fork roo + for _, block := range chain.Blocks { + state = applyBlockToState(state, block) + } + // compute state at new head for _, block := range addedChain.Blocks { state = applyBlockToState(state, block) } @@ -99,6 +103,41 @@ func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain *blockcha return nil } +func (am *ApplicationModule) handleVerifyBlocksRequest(checkpointState *statepbtypes.State, chainCheckpointToStart, chainToVerify []*blockchainpbtypes.Block) error { + am.logger.Log(logging.LevelDebug, "Processing verify block request") + + timeStamps := checkpointState.LastSentTimestamps + + chain := append(chainCheckpointToStart, chainToVerify...) // chainCheckpointToStart wouldn't need to be verified, but we need it to get the timestamps + + for _, block := range chain { + // verify block + for i, lt := range timeStamps { + if lt.NodeId == block.Payload.Sender { + if lt.Timestamp > block.Payload.Timestamp { + // block in chain invalid, don't respond + return nil + } + + // remove old timestamp, if it exists + timeStamps[i] = timeStamps[len(timeStamps)-1] + timeStamps = timeStamps[:len(timeStamps)-1] + } + + // add new timestamp + timeStamps = append(timeStamps, &statepbtypes.LastSentTimestamp{ + NodeId: block.Payload.Sender, + Timestamp: block.Payload.Timestamp, + }) + } + } + + // if no blocks are invalid, responds with chain + applicationpbdsl.VerifyBlocksResponse(*am.m, "bcm", chainToVerify) + + return nil +} + // transaction management events func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { @@ -138,6 +177,7 @@ func NewApplication(logger logging.Logger, nodeID t.NodeID) modules.PassiveModul applicationpbdsl.UponPayloadRequest(m, am.handlePayloadRequest) applicationpbdsl.UponForkUpdate(m, am.handleForkUpdate) + applicationpbdsl.UponVerifyBlocksRequest(m, am.handleVerifyBlocksRequest) applicationpbdsl.UponMessageInput(m, func(text string) error { am.tm.AddPayload(&payloadpbtypes.Payload{ diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 06886b8b0..3e78036a5 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -13,6 +13,7 @@ import ( bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" minerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/dsl" + payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" statepbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" @@ -27,6 +28,7 @@ var ( ErrNodeAlreadyInTree = errors.New("node already in tree") ErrRollback = errors.New("rollback - should never happen") ErrUninitialized = errors.New("uninitialized - the application must initialize the blockchain first by sending it a initBlockchain message") + ErrNoHit = errors.New("no hit") ) type bcmBlock struct { @@ -58,7 +60,7 @@ func (bcm *bcmModule) checkInitialization() error { return nil } -func (bcm *bcmModule) handleNewHeadSideEffects(newHead, oldHead *bcmBlock) { +func (bcm *bcmModule) handleNewHead(newHead, oldHead *bcmBlock) { bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.block.Block.BlockId)) // compute delta @@ -70,19 +72,25 @@ func (bcm *bcmModule) handleNewHeadSideEffects(newHead, oldHead *bcmBlock) { } // get fork state - forkBlock, ok := bcm.checkpoints[addChain[0].BlockId] - if !ok { - bcm.logger.Log(logging.LevelError, "Failed to get fork block") + // TODO: fork might not happen right at checkpoint... find segment, add segment to fork update + forkRoot, err := bcm.findBlock(addChain[0].BlockId) + if err != nil { + bcm.logger.Log(logging.LevelError, "Failed to get fork parent - this should not happen", "blockId", utils.FormatBlockId(addChain[0].BlockId)) + panic(err) } + ibChain := bcm.getChainFromCheckpointToBlock(forkRoot) + checkpointState := ibChain[0].State // ib chain starts with checkpont, checkpoints have explicit state + chain := make([]*blockchainpbtypes.Block, 0, len(ibChain)) - forkState := forkBlock.block.State + for _, v := range ibChain { + chain = append(chain, v.Block) + } applicationpbdsl.ForkUpdate(*bcm.m, "application", &blockchainpbtypes.Blockchain{ Blocks: removeChain[1:], }, &blockchainpbtypes.Blockchain{ Blocks: addChain[1:], - }, forkState) - // applicationpbdsl.NewHead(*bcm.m, "application", newHead.block.Block.BlockId) + }, &blockchainpbtypes.Blockchain{Blocks: chain}, checkpointState) minerpbdsl.NewHead(*bcm.m, "miner", newHead.block.Block.BlockId) } @@ -178,7 +186,7 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* // if it shouldn't continue, the function will return the current block // if it should continue but there are no more blocks, the function will return nil // if an error is thrown, the traversal is aborted and nil is returned -func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) (bool, error)) *bcmBlock { +func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) (bool, error)) (*bcmBlock, error) { bcm.currentScanCount = bcm.currentScanCount + 1 queue := make([]*bcmBlock, 0) for _, v := range bcm.leaves { @@ -193,9 +201,9 @@ func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) curr.scanCount = bcm.currentScanCount if match, err := traversalFunc(curr); err != nil { - return nil + return nil, err } else if match { - return curr + return curr, nil } // add curr's parents to queue if it hasn't been scanned yet @@ -203,7 +211,7 @@ func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) queue = append(queue, curr.parent) } } - return nil + return nil, ErrNoHit } // TODO: might need to add some "cleanup" to handle very old leaves in case this grows too much @@ -240,9 +248,8 @@ func (bcm *bcmModule) addBlock(block *blockchainpbtypes.Block) error { return nil } - if hit := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { + if _, err := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { // check if curr matches the block to be added - if so, ignore - bcm.logger.Log(logging.LevelDebug, "Traversing - checking block", "blockId", utils.FormatBlockId(block.BlockId), "currBlockId", utils.FormatBlockId(currBlock.block.Block.BlockId)) if currBlock.block.Block.BlockId == block.BlockId { bcm.logger.Log(logging.LevelDebug, "Block already in tree", "blockId", utils.FormatBlockId(block.BlockId)) return true, ErrNodeAlreadyInTree @@ -271,15 +278,15 @@ func (bcm *bcmModule) addBlock(block *blockchainpbtypes.Block) error { } return false, nil - }); hit == nil { - bcm.logger.Log(logging.LevelInfo, "Couldn't find parent", "blockId", utils.FormatBlockId(block.BlockId)) - return ErrParentNotFound + }); err != nil { + bcm.logger.Log(logging.LevelInfo, "Couldn't find parent", "blockId", utils.FormatBlockId(block.BlockId), "error", err) + return err } return nil } -func (bcm *bcmModule) findBlock(blockId uint64) *bcmBlock { +func (bcm *bcmModule) findBlock(blockId uint64) (*bcmBlock, error) { return bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { if currBlock.block.Block.BlockId == blockId { return true, nil @@ -288,49 +295,34 @@ func (bcm *bcmModule) findBlock(blockId uint64) *bcmBlock { }) } -func (bcm *bcmModule) getHead() *bcmBlock { - return bcm.head -} +// first send off to application to verify that payloads are valid, then check that everything links together +func (bcm *bcmModule) processNewChain(blocks []*blockchainpbtypes.Block) error { + // take blocks, compute chain to link up with checkpoint, send req to app -func (bcm *bcmModule) handleNewBlock(block *blockchainpbtypes.Block) { - currentHead := bcm.getHead() - // insert block - if err := bcm.addBlock(block); err != nil { - switch err { - case ErrNodeAlreadyInTree: - // ignore - bcm.logger.Log(logging.LevelDebug, "Block already in tree - ignore", "blockId", utils.FormatBlockId(block.BlockId)) - case ErrParentNotFound: - // ask synchronizer for help - leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.Block.BlockId) - bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", utils.FormatBlockId(block.BlockId), "leaves+genesis", utils.FormatBlockIdSlice(leavesPlusGenesis)) - synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", block, leavesPlusGenesis) - // announce orphan block to interceptor, just for the visualization - interceptorpbdsl.NewOrphan(*bcm.m, "devnull", block) - default: - // some other error - bcm.logger.Log(logging.LevelError, "Unexpected error adding block", "blockId", utils.FormatBlockId(block.BlockId), "error", err) - panic(err) - } - return + checkpointState := statepbtypes.State{} + checkpointToParent := []*blockchainpbtypes.Block{} + + // find parent + parent, _ := bcm.findBlock(blocks[0].PreviousBlockId) + if parent == nil { + bcm.logger.Log(logging.LevelError, "Parent not found. Could be invalid chain/block or we are missing a part. Sending sync req to sync end of chain.", "blockId", utils.FormatBlockId(blocks[0].BlockId)) + leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.Block.BlockId) + bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", utils.FormatBlockId(blocks[len(blocks)-1].BlockId), "leaves+genesis", utils.FormatBlockIdSlice(leavesPlusGenesis)) + synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", blocks[len(blocks)-1], leavesPlusGenesis) + return nil } - // if head changed, trigger get tpm to prep new block - if newHead := bcm.getHead(); newHead != currentHead { - bcm.handleNewHeadSideEffects(newHead, currentHead) + ibChain := bcm.getChainFromCheckpointToBlock(parent) + for _, v := range ibChain { + checkpointToParent = append(checkpointToParent, v.Block) } - // send to chainviewer - bcm.sendTreeUpdate() + applicationpbdsl.VerifyBlocksRequest(*bcm.m, "application", &checkpointState, checkpointToParent, blocks) + return nil } -func (bcm *bcmModule) handleNewChain(blocks []*blockchainpbtypes.Block) { - currentHead := bcm.getHead() - blockIds := make([]uint64, 0, len(blocks)) - for _, v := range blocks { - blockIds = append(blockIds, v.BlockId) - } - bcm.logger.Log(logging.LevelDebug, "Adding new chain", "chain", utils.FormatBlockIdSlice(blockIds)) +func (bcm *bcmModule) handleVerifyBlocksResponse(blocks []*blockchainpbtypes.Block) error { + currentHead := bcm.head // insert block for _, block := range blocks { if err := bcm.addBlock(block); err != nil { @@ -339,12 +331,9 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpbtypes.Block) { // ignore bcm.logger.Log(logging.LevelDebug, "Block already in tree - ignore", "blockId", utils.FormatBlockId(block.BlockId)) case ErrParentNotFound: - // ask synchronizer for help - leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.Block.BlockId) - bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", utils.FormatBlockId(block.BlockId), "leaves+genesis", utils.FormatBlockIdSlice(leavesPlusGenesis)) - synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", block, leavesPlusGenesis) - // announce orphan block to interceptor - interceptorpbdsl.NewOrphan(*bcm.m, "devnull", block) + // this should not happen + bcm.logger.Log(logging.LevelError, "Parent not found - this should not happen", "blockId", utils.FormatBlockId(block.BlockId)) + panic(err) default: // some other error bcm.logger.Log(logging.LevelError, "Unexpected error adding block", "blockId", utils.FormatBlockId(block.BlockId), "error", err) @@ -354,12 +343,30 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpbtypes.Block) { } // if head changed, trigger get tpm to prep new block (only need to do this once) - if newHead := bcm.getHead(); newHead != currentHead { - bcm.handleNewHeadSideEffects(newHead, currentHead) + if newHead := bcm.head; newHead != currentHead { + bcm.handleNewHead(newHead, currentHead) } // send to chainviewer bcm.sendTreeUpdate() + + return nil +} + +func (bcm *bcmModule) handleNewChain(blocks []*blockchainpbtypes.Block) error { + // just for logging + blockIds := make([]uint64, 0, len(blocks)) + for _, v := range blocks { + blockIds = append(blockIds, v.BlockId) + } + bcm.logger.Log(logging.LevelDebug, "Adding new chain", "chain", utils.FormatBlockIdSlice(blockIds)) + + return bcm.processNewChain(blocks) +} + +func (bcm *bcmModule) handleNewBlock(block *blockchainpbtypes.Block) error { + bcm.logger.Log(logging.LevelDebug, "Adding new block", "blockId", utils.FormatBlockId(block.BlockId)) + return bcm.processNewChain([]*blockchainpbtypes.Block{block}) } func (bcm *bcmModule) sendTreeUpdate() { @@ -383,31 +390,35 @@ func (bcm *bcmModule) sendTreeUpdate() { interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", &blockTree, bcm.head.block.Block.BlockId) } -func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, sourceModule t.ModuleID) error { - if err := bcm.checkInitialization(); err != nil { - return err - } +func (bcm *bcmModule) getChainFromCheckpointToBlock(block *bcmBlock) []*blockchainpbtypes.BlockInternal { chain := make([]*blockchainpbtypes.BlockInternal, 0) - // start with head - currentBlock := bcm.head + currentBlock := block for currentBlock != nil { - if cp, ok := bcm.checkpoints[currentBlock.block.Block.BlockId]; ok { - chain = append(chain, cp.block) - break - } chain = append(chain, currentBlock.block) + if _, ok := bcm.checkpoints[currentBlock.block.Block.BlockId]; ok { + for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 { + chain[i], chain[j] = chain[j], chain[i] + } + + return chain + } currentBlock = currentBlock.parent } - if currentBlock == nil { - bcm.logger.Log(logging.LevelError, "Cannot link up with checkpoints - this should not happen", "chain", chain) - panic("Cannot link up with checkpoints - this should not happen") + bcm.logger.Log(logging.LevelWarn, "Cannot link up with checkpoints", "source", utils.FormatBlockId(block.block.Block.BlockId), "chain", chain) + return nil +} + +func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, sourceModule t.ModuleID) error { + if err := bcm.checkInitialization(); err != nil { + return err } - for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 { - chain[i], chain[j] = chain[j], chain[i] + chain := bcm.getChainFromCheckpointToBlock(bcm.head) + if chain == nil { + bcm.logger.Log(logging.LevelError, "Cannot link head up with checkpoints", "head", utils.FormatBlockId(bcm.head.block.Block.BlockId)) } bcmpbdsl.GetHeadToCheckpointChainResponse(*bcm.m, sourceModule, requestID, chain) @@ -429,8 +440,8 @@ func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepbty // find block // should be a leave or a block really close - if hit := bcm.findBlock(block_id); hit == nil { - bcm.logger.Log(logging.LevelError, "Block not found - can't register checkpoint", "blockId", utils.FormatBlockId(block_id)) + if hit, err := bcm.findBlock(block_id); err != nil { + bcm.logger.Log(logging.LevelError, "Block not found - can't register checkpoint", "blockId", utils.FormatBlockId(block_id), "error", err) panic("block not found when registering checkpoint") // should never happen } else { bcm.logger.Log(logging.LevelDebug, "Found block - registering checkpoint", "blockId", utils.FormatBlockId(block_id)) @@ -448,7 +459,7 @@ func (bcm *bcmModule) handleInitBlockchain(initialState *statepbtypes.State) err genesis := &blockchainpbtypes.Block{ BlockId: 0, PreviousBlockId: 0, - Payload: nil, + Payload: &payloadpbtypes.Payload{}, Timestamp: 0, // unix 0 } @@ -475,8 +486,6 @@ func (bcm *bcmModule) handleInitBlockchain(initialState *statepbtypes.State) err // initialized, start operations bcm.logger.Log(logging.LevelInfo, "Initialized blockchain") - - applicationpbdsl.NewHead(*bcm.m, "application", hash) minerpbdsl.NewHead(*bcm.m, "miner", hash) return nil @@ -507,8 +516,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { if err := bcm.checkInitialization(); err != nil { return err } - bcm.handleNewBlock(block) - return nil + return bcm.handleNewBlock(block) }) bcmpbdsl.UponNewChain(m, func(blocks []*blockchainpbtypes.Block) error { @@ -516,8 +524,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { if err := bcm.checkInitialization(); err != nil { return err } - bcm.handleNewChain(blocks) - return nil + return bcm.handleNewChain(blocks) }) bcmpbdsl.UponGetBlockRequest(m, func(requestID string, sourceModule t.ModuleID, blockID uint64) error { @@ -526,10 +533,10 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { return err } // check if block is in tree - hit := bcm.findBlock(blockID) + hit, err := bcm.findBlock(blockID) - if hit == nil { - bcm.logger.Log(logging.LevelDebug, "Block not found", "requestId", requestID) + if err != nil { + bcm.logger.Log(logging.LevelDebug, "Block not found", "requestId", requestID, "error", err) bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) return nil } @@ -551,10 +558,10 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { for _, v := range sourceBlockIds { sourceBlockIdsMap[v] = v } - currentBlock := bcm.findBlock(endBlockId) + currentBlock, err := bcm.findBlock(endBlockId) - if currentBlock == nil { - bcm.logger.Log(logging.LevelDebug, "Block not found - can't build chain", "requestId", requestID) + if err != nil { + bcm.logger.Log(logging.LevelDebug, "Block not found - can't build chain", "requestId", requestID, "error", err) bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) return nil } @@ -594,5 +601,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { bcmpbdsl.UponInitBlockchain(m, bcm.handleInitBlockchain) + applicationpbdsl.UponVerifyBlocksResponse(m, bcm.handleVerifyBlocksResponse) + return m } diff --git a/samples/blockchain/run.sh b/samples/blockchain/run.sh index 585d5a145..c44baea33 100755 --- a/samples/blockchain/run.sh +++ b/samples/blockchain/run.sh @@ -14,10 +14,10 @@ tmux new-session -d -s demo \; \ split-window -t demo:0.0 -h \; \ split-window -t "demo:0.2" -h \; \ \ - send-keys -t "demo:0.0" "go run . 4 0 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ - send-keys -t "demo:0.1" "go run . 4 1 2>&1 | tee \"$NODE_1_LOG\"" Enter \; \ - send-keys -t "demo:0.2" "go run . 4 2 2>&1 | tee \"$NODE_2_LOG\"" Enter \; \ - send-keys -t "demo:0.3" "go run . 4 3 2>&1 | tee \"$NODE_3_LOG\"" Enter \; \ + send-keys -t "demo:0.0" "go run . 4 0 true 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.1" "go run . 4 1 true 2>&1 | tee \"$NODE_1_LOG\"" Enter \; \ + send-keys -t "demo:0.2" "go run . 4 2 true 2>&1 | tee \"$NODE_2_LOG\"" Enter \; \ + send-keys -t "demo:0.3" "go run . 4 3 true 2>&1 | tee \"$NODE_3_LOG\"" Enter \; \ attach-session -t "demo:0.0" #!/usr/bin/env bash set -e diff --git a/samples/blockchain/wsinterceptor/wsinterceptor.go b/samples/blockchain/wsinterceptor/wsinterceptor.go index f82e38ee7..d68be0142 100644 --- a/samples/blockchain/wsinterceptor/wsinterceptor.go +++ b/samples/blockchain/wsinterceptor/wsinterceptor.go @@ -1,4 +1,4 @@ -package wsInterceptor +package wsinterceptor import ( "github.com/filecoin-project/mir/pkg/events" From 8b0272de919d171100e862f4d0dd91d0f3706b5d Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Mon, 29 Jan 2024 21:56:32 +0100 Subject: [PATCH 16/46] get rid of block internal --- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go | 85 +++++----- pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go | 4 +- pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go | 4 +- .../blockchainpb/bcmpb/events/events.mir.go | 7 +- pkg/pb/blockchainpb/bcmpb/types/types.mir.go | 15 +- pkg/pb/blockchainpb/blockchainpb.pb.go | 147 ++++-------------- pkg/pb/blockchainpb/types/types.mir.go | 37 ----- protos/blockchainpb/bcmpb/bcmpb.proto | 5 +- protos/blockchainpb/blockchainpb.proto | 8 - .../generator.go | 50 ++++++ samples/blockchain/bcm.go | 111 ++++++------- 11 files changed, 199 insertions(+), 274 deletions(-) create mode 100644 protos/proto_converter_tmp_3294919200/generator.go diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go index b66933e41..0959b7797 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go @@ -631,8 +631,9 @@ type GetHeadToCheckpointChainResponse struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - Chain []*blockchainpb.BlockInternal `protobuf:"bytes,3,rep,name=chain,proto3" json:"chain,omitempty"` // from checkpoint to head + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + Chain []*blockchainpb.Block `protobuf:"bytes,2,rep,name=chain,proto3" json:"chain,omitempty"` // from checkpoint to head + CheckpointState *statepb.State `protobuf:"bytes,3,opt,name=checkpoint_state,json=checkpointState,proto3" json:"checkpoint_state,omitempty"` } func (x *GetHeadToCheckpointChainResponse) Reset() { @@ -674,13 +675,20 @@ func (x *GetHeadToCheckpointChainResponse) GetRequestId() string { return "" } -func (x *GetHeadToCheckpointChainResponse) GetChain() []*blockchainpb.BlockInternal { +func (x *GetHeadToCheckpointChainResponse) GetChain() []*blockchainpb.Block { if x != nil { return x.Chain } return nil } +func (x *GetHeadToCheckpointChainResponse) GetCheckpointState() *statepb.State { + if x != nil { + return x.CheckpointState + } + return nil +} + type RegisterCheckpoint struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -903,30 +911,33 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7a, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x48, - 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x31, 0x0a, 0x05, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, - 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, - 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5b, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, - 0x01, 0x22, 0x4b, 0x0a, 0x0e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x0d, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x69, 0x6e, 0x69, 0x74, - 0x69, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, - 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, - 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, - 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x6c, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xad, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, + 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, + 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x05, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x39, 0x0a, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5b, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, + 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, + 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x4b, 0x0a, 0x0e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x0d, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, + 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, + 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -955,8 +966,7 @@ var file_blockchainpb_bcmpb_bcmpb_proto_goTypes = []interface{}{ (*RegisterCheckpoint)(nil), // 9: bcmpb.RegisterCheckpoint (*InitBlockchain)(nil), // 10: bcmpb.InitBlockchain (*blockchainpb.Block)(nil), // 11: blockchainpb.Block - (*blockchainpb.BlockInternal)(nil), // 12: blockchainpb.BlockInternal - (*statepb.State)(nil), // 13: statepb.State + (*statepb.State)(nil), // 12: statepb.State } var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ 1, // 0: bcmpb.Event.new_block:type_name -> bcmpb.NewBlock @@ -973,14 +983,15 @@ var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ 11, // 11: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block 11, // 12: bcmpb.GetBlockResponse.block:type_name -> blockchainpb.Block 11, // 13: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block - 12, // 14: bcmpb.GetHeadToCheckpointChainResponse.chain:type_name -> blockchainpb.BlockInternal - 13, // 15: bcmpb.RegisterCheckpoint.state:type_name -> statepb.State - 13, // 16: bcmpb.InitBlockchain.initial_state:type_name -> statepb.State - 17, // [17:17] is the sub-list for method output_type - 17, // [17:17] is the sub-list for method input_type - 17, // [17:17] is the sub-list for extension type_name - 17, // [17:17] is the sub-list for extension extendee - 0, // [0:17] is the sub-list for field type_name + 11, // 14: bcmpb.GetHeadToCheckpointChainResponse.chain:type_name -> blockchainpb.Block + 12, // 15: bcmpb.GetHeadToCheckpointChainResponse.checkpoint_state:type_name -> statepb.State + 12, // 16: bcmpb.RegisterCheckpoint.state:type_name -> statepb.State + 12, // 17: bcmpb.InitBlockchain.initial_state:type_name -> statepb.State + 18, // [18:18] is the sub-list for method output_type + 18, // [18:18] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_blockchainpb_bcmpb_bcmpb_proto_init() } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go index 37633b6c0..8d4558023 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go @@ -40,8 +40,8 @@ func GetHeadToCheckpointChainRequest(m dsl.Module, destModule types.ModuleID, re dsl.EmitMirEvent(m, events.GetHeadToCheckpointChainRequest(destModule, requestId, sourceModule)) } -func GetHeadToCheckpointChainResponse(m dsl.Module, destModule types.ModuleID, requestId string, chain []*types1.BlockInternal) { - dsl.EmitMirEvent(m, events.GetHeadToCheckpointChainResponse(destModule, requestId, chain)) +func GetHeadToCheckpointChainResponse(m dsl.Module, destModule types.ModuleID, requestId string, chain []*types1.Block, checkpointState *types2.State) { + dsl.EmitMirEvent(m, events.GetHeadToCheckpointChainResponse(destModule, requestId, chain, checkpointState)) } func RegisterCheckpoint(m dsl.Module, destModule types.ModuleID, blockId uint64, state *types2.State) { diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go index 5075ed42b..fedeef406 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go @@ -66,9 +66,9 @@ func UponGetHeadToCheckpointChainRequest(m dsl.Module, handler func(requestId st }) } -func UponGetHeadToCheckpointChainResponse(m dsl.Module, handler func(requestId string, chain []*types2.BlockInternal) error) { +func UponGetHeadToCheckpointChainResponse(m dsl.Module, handler func(requestId string, chain []*types2.Block, checkpointState *types4.State) error) { UponEvent[*types.Event_GetHeadToCheckpointChainResponse](m, func(ev *types.GetHeadToCheckpointChainResponse) error { - return handler(ev.RequestId, ev.Chain) + return handler(ev.RequestId, ev.Chain, ev.CheckpointState) }) } diff --git a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go index 76db3e824..e2a7e8072 100644 --- a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go @@ -125,15 +125,16 @@ func GetHeadToCheckpointChainRequest(destModule types.ModuleID, requestId string } } -func GetHeadToCheckpointChainResponse(destModule types.ModuleID, requestId string, chain []*types1.BlockInternal) *types2.Event { +func GetHeadToCheckpointChainResponse(destModule types.ModuleID, requestId string, chain []*types1.Block, checkpointState *types4.State) *types2.Event { return &types2.Event{ DestModule: destModule, Type: &types2.Event_Bcm{ Bcm: &types3.Event{ Type: &types3.Event_GetHeadToCheckpointChainResponse{ GetHeadToCheckpointChainResponse: &types3.GetHeadToCheckpointChainResponse{ - RequestId: requestId, - Chain: chain, + RequestId: requestId, + Chain: chain, + CheckpointState: checkpointState, }, }, }, diff --git a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go index 580f9b4f6..9ab0e7022 100644 --- a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go @@ -570,8 +570,9 @@ func (*GetHeadToCheckpointChainRequest) MirReflect() mirreflect.Type { } type GetHeadToCheckpointChainResponse struct { - RequestId string - Chain []*types.BlockInternal + RequestId string + Chain []*types.Block + CheckpointState *types3.State } func GetHeadToCheckpointChainResponseFromPb(pb *bcmpb.GetHeadToCheckpointChainResponse) *GetHeadToCheckpointChainResponse { @@ -580,9 +581,10 @@ func GetHeadToCheckpointChainResponseFromPb(pb *bcmpb.GetHeadToCheckpointChainRe } return &GetHeadToCheckpointChainResponse{ RequestId: pb.RequestId, - Chain: types1.ConvertSlice(pb.Chain, func(t *blockchainpb.BlockInternal) *types.BlockInternal { - return types.BlockInternalFromPb(t) + Chain: types1.ConvertSlice(pb.Chain, func(t *blockchainpb.Block) *types.Block { + return types.BlockFromPb(t) }), + CheckpointState: types3.StateFromPb(pb.CheckpointState), } } @@ -593,9 +595,12 @@ func (m *GetHeadToCheckpointChainResponse) Pb() *bcmpb.GetHeadToCheckpointChainR pbMessage := &bcmpb.GetHeadToCheckpointChainResponse{} { pbMessage.RequestId = m.RequestId - pbMessage.Chain = types1.ConvertSlice(m.Chain, func(t *types.BlockInternal) *blockchainpb.BlockInternal { + pbMessage.Chain = types1.ConvertSlice(m.Chain, func(t *types.Block) *blockchainpb.Block { return (t).Pb() }) + if m.CheckpointState != nil { + pbMessage.CheckpointState = (m.CheckpointState).Pb() + } } return pbMessage diff --git a/pkg/pb/blockchainpb/blockchainpb.pb.go b/pkg/pb/blockchainpb/blockchainpb.pb.go index 535e7d062..e87d898f2 100644 --- a/pkg/pb/blockchainpb/blockchainpb.pb.go +++ b/pkg/pb/blockchainpb/blockchainpb.pb.go @@ -8,7 +8,6 @@ package blockchainpb import ( payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" - statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -196,61 +195,6 @@ func (x *Block) GetTimestamp() int64 { return 0 } -type BlockInternal struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Block *Block `protobuf:"bytes,1,opt,name=block,proto3" json:"block,omitempty"` - State *statepb.State `protobuf:"bytes,2,opt,name=state,proto3" json:"state,omitempty"` -} - -func (x *BlockInternal) Reset() { - *x = BlockInternal{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *BlockInternal) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*BlockInternal) ProtoMessage() {} - -func (x *BlockInternal) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[3] - 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 BlockInternal.ProtoReflect.Descriptor instead. -func (*BlockInternal) Descriptor() ([]byte, []int) { - return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{3} -} - -func (x *BlockInternal) GetBlock() *Block { - if x != nil { - return x.Block - } - return nil -} - -func (x *BlockInternal) GetState() *statepb.State { - if x != nil { - return x.State - } - return nil -} - var File_blockchainpb_blockchainpb_proto protoreflect.FileDescriptor var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ @@ -261,39 +205,30 @@ var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x26, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x09, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x04, 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, - 0x01, 0x22, 0x3f, 0x0a, 0x0a, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, - 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x80, 0xa6, - 0x1d, 0x01, 0x22, 0xa0, 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, - 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x04, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, - 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, - 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x66, 0x0a, 0x0d, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x6e, - 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, - 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x35, 0x5a, - 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, - 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x72, + 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, + 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, + 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3f, 0x0a, + 0x0a, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0xa0, + 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, + 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, + 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1c, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, 0x04, 0x80, 0xa6, 0x1d, + 0x01, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, + 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, + 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -308,26 +243,22 @@ func file_blockchainpb_blockchainpb_proto_rawDescGZIP() []byte { return file_blockchainpb_blockchainpb_proto_rawDescData } -var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_blockchainpb_blockchainpb_proto_goTypes = []interface{}{ (*Blocktree)(nil), // 0: blockchainpb.Blocktree (*Blockchain)(nil), // 1: blockchainpb.Blockchain (*Block)(nil), // 2: blockchainpb.Block - (*BlockInternal)(nil), // 3: blockchainpb.BlockInternal - (*payloadpb.Payload)(nil), // 4: payloadpb.Payload - (*statepb.State)(nil), // 5: statepb.State + (*payloadpb.Payload)(nil), // 3: payloadpb.Payload } var file_blockchainpb_blockchainpb_proto_depIdxs = []int32{ 2, // 0: blockchainpb.Blocktree.blocks:type_name -> blockchainpb.Block 2, // 1: blockchainpb.Blockchain.blocks:type_name -> blockchainpb.Block - 4, // 2: blockchainpb.Block.payload:type_name -> payloadpb.Payload - 2, // 3: blockchainpb.BlockInternal.block:type_name -> blockchainpb.Block - 5, // 4: blockchainpb.BlockInternal.state:type_name -> statepb.State - 5, // [5:5] is the sub-list for method output_type - 5, // [5:5] 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 + 3, // 2: blockchainpb.Block.payload:type_name -> payloadpb.Payload + 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 } func init() { file_blockchainpb_blockchainpb_proto_init() } @@ -372,18 +303,6 @@ func file_blockchainpb_blockchainpb_proto_init() { return nil } } - file_blockchainpb_blockchainpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*BlockInternal); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } } type x struct{} out := protoimpl.TypeBuilder{ @@ -391,7 +310,7 @@ func file_blockchainpb_blockchainpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_blockchainpb_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/types/types.mir.go b/pkg/pb/blockchainpb/types/types.mir.go index 6804e94a0..62432c861 100644 --- a/pkg/pb/blockchainpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/types/types.mir.go @@ -7,7 +7,6 @@ import ( types "github.com/filecoin-project/mir/codegen/model/types" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -119,39 +118,3 @@ func (m *Block) Pb() *blockchainpb.Block { func (*Block) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*blockchainpb.Block]()} } - -type BlockInternal struct { - Block *Block - State *types2.State -} - -func BlockInternalFromPb(pb *blockchainpb.BlockInternal) *BlockInternal { - if pb == nil { - return nil - } - return &BlockInternal{ - Block: BlockFromPb(pb.Block), - State: types2.StateFromPb(pb.State), - } -} - -func (m *BlockInternal) Pb() *blockchainpb.BlockInternal { - if m == nil { - return nil - } - pbMessage := &blockchainpb.BlockInternal{} - { - if m.Block != nil { - pbMessage.Block = (m.Block).Pb() - } - if m.State != nil { - pbMessage.State = (m.State).Pb() - } - } - - return pbMessage -} - -func (*BlockInternal) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*blockchainpb.BlockInternal]()} -} diff --git a/protos/blockchainpb/bcmpb/bcmpb.proto b/protos/blockchainpb/bcmpb/bcmpb.proto index d1c5dad0b..06875640c 100644 --- a/protos/blockchainpb/bcmpb/bcmpb.proto +++ b/protos/blockchainpb/bcmpb/bcmpb.proto @@ -88,8 +88,9 @@ message GetHeadToCheckpointChainRequest { message GetHeadToCheckpointChainResponse { option (mir.event) = true; - string request_id = 1; - repeated blockchainpb.BlockInternal chain = 3; // from checkpoint to head + string request_id = 1; + repeated blockchainpb.Block chain = 2; // from checkpoint to head + statepb.State checkpoint_state = 3; } message RegisterCheckpoint { diff --git a/protos/blockchainpb/blockchainpb.proto b/protos/blockchainpb/blockchainpb.proto index b7c900acc..a19911dfd 100644 --- a/protos/blockchainpb/blockchainpb.proto +++ b/protos/blockchainpb/blockchainpb.proto @@ -7,7 +7,6 @@ option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb"; import "mir/codegen_extensions.proto"; import "blockchainpb/payloadpb/payloadpb.proto"; -import "blockchainpb/statepb/statepb.proto"; message Blocktree { option (mir.struct) = true; @@ -30,11 +29,4 @@ message Block { payloadpb.Payload payload = 3; int64 timestamp = 4; -} - -message BlockInternal { - option (mir.struct) = true; - - Block block = 1; - statepb.State state = 2; } \ No newline at end of file diff --git a/protos/proto_converter_tmp_3294919200/generator.go b/protos/proto_converter_tmp_3294919200/generator.go new file mode 100644 index 000000000..ee8d5ba94 --- /dev/null +++ b/protos/proto_converter_tmp_3294919200/generator.go @@ -0,0 +1,50 @@ + +package main + +import ( + "log" + "os" + "reflect" + + generator_ "github.com/filecoin-project/mir/codegen/generators/mir-std-gen/generator" + pkg_ "github.com/filecoin-project/mir/pkg/pb/hasherpb" +) + +func main() { + var generator generator_.CombinedGenerator + err := generator.Run( + []reflect.Type{ + + reflect.TypeOf((*pkg_.Event)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Event_Request)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Event_Result)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Event_RequestOne)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Event_ResultOne)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Request)(nil)).Elem(), + + reflect.TypeOf((*pkg_.Result)(nil)).Elem(), + + reflect.TypeOf((*pkg_.RequestOne)(nil)).Elem(), + + reflect.TypeOf((*pkg_.ResultOne)(nil)).Elem(), + + reflect.TypeOf((*pkg_.HashOrigin)(nil)).Elem(), + + reflect.TypeOf((*pkg_.HashOrigin_ContextStore)(nil)).Elem(), + + reflect.TypeOf((*pkg_.HashOrigin_Dsl)(nil)).Elem(), + + reflect.TypeOf((*pkg_.HashData)(nil)).Elem(), + + }) + + if err != nil { + log.Printf("Error: %v\n", err) + os.Exit(2) + } +} diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 3e78036a5..9936433cd 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -32,7 +32,8 @@ var ( ) type bcmBlock struct { - block *blockchainpbtypes.BlockInternal + block *blockchainpbtypes.Block + state *statepbtypes.State parent *bcmBlock depth uint64 scanCount uint64 @@ -61,7 +62,7 @@ func (bcm *bcmModule) checkInitialization() error { } func (bcm *bcmModule) handleNewHead(newHead, oldHead *bcmBlock) { - bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.block.Block.BlockId)) + bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.block.BlockId)) // compute delta removeChain, addChain, err := bcm.computeDelta(oldHead, newHead) @@ -78,20 +79,15 @@ func (bcm *bcmModule) handleNewHead(newHead, oldHead *bcmBlock) { bcm.logger.Log(logging.LevelError, "Failed to get fork parent - this should not happen", "blockId", utils.FormatBlockId(addChain[0].BlockId)) panic(err) } - ibChain := bcm.getChainFromCheckpointToBlock(forkRoot) - checkpointState := ibChain[0].State // ib chain starts with checkpont, checkpoints have explicit state - chain := make([]*blockchainpbtypes.Block, 0, len(ibChain)) - - for _, v := range ibChain { - chain = append(chain, v.Block) - } + chain, checkpointState := bcm.getChainFromCheckpointToBlock(forkRoot) applicationpbdsl.ForkUpdate(*bcm.m, "application", &blockchainpbtypes.Blockchain{ Blocks: removeChain[1:], }, &blockchainpbtypes.Blockchain{ Blocks: addChain[1:], }, &blockchainpbtypes.Blockchain{Blocks: chain}, checkpointState) - minerpbdsl.NewHead(*bcm.m, "miner", newHead.block.Block.BlockId) + + minerpbdsl.NewHead(*bcm.m, "miner", newHead.block.BlockId) } func reverse[S ~[]T, T any](slice S) S { @@ -100,16 +96,16 @@ func reverse[S ~[]T, T any](slice S) S { } func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]*blockchainpbtypes.Block, []*blockchainpbtypes.Block, error) { - bcm.logger.Log(logging.LevelDebug, "Computing Delta", "removeHead", removeHead.block.Block.BlockId, "addHead", addHead.block.Block.BlockId) + bcm.logger.Log(logging.LevelDebug, "Computing Delta", "removeHead", removeHead.block.BlockId, "addHead", addHead.block.BlockId) // short circuit for most simple cases - if removeHead.block.Block.BlockId == addHead.block.Block.BlockId { + if removeHead.block.BlockId == addHead.block.BlockId { // no delta - return []*blockchainpbtypes.Block{addHead.block.Block}, []*blockchainpbtypes.Block{addHead.block.Block}, nil - } else if addHead.block.Block.PreviousBlockId == removeHead.block.Block.BlockId { + return []*blockchainpbtypes.Block{addHead.block}, []*blockchainpbtypes.Block{addHead.block}, nil + } else if addHead.block.PreviousBlockId == removeHead.block.BlockId { // just appending - return []*blockchainpbtypes.Block{removeHead.block.Block}, []*blockchainpbtypes.Block{removeHead.block.Block, addHead.block.Block}, nil - } else if removeHead.block.Block.PreviousBlockId == addHead.block.Block.BlockId { + return []*blockchainpbtypes.Block{removeHead.block}, []*blockchainpbtypes.Block{removeHead.block, addHead.block}, nil + } else if removeHead.block.PreviousBlockId == addHead.block.BlockId { // rollbacks should never happen return nil, nil, ErrRollback } @@ -132,13 +128,13 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* // handle remove step if currRemove != nil { - removeChain = append(removeChain, currRemove.block.Block) + removeChain = append(removeChain, currRemove.block) // check for intersection if currRemove.scanCount > initialScanCount { // remove chain intersects with add chain, remove chain is ok, add chain needs to be truncated // find currRemove in addChain - currRemoveBlockId := currRemove.block.Block.BlockId + currRemoveBlockId := currRemove.block.BlockId index := slices.IndexFunc(addChain, func(i *blockchainpbtypes.Block) bool { return i.BlockId == currRemoveBlockId }) if index == -1 { // should never happen @@ -155,13 +151,13 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* // handle add step if currAdd != nil { - addChain = append(addChain, currAdd.block.Block) + addChain = append(addChain, currAdd.block) // check for intersection if currAdd.scanCount > initialScanCount { // add chain intersects with remove chain, add chain is ok, remove chain needs to be truncated // find addRemove in removeChain - currAddBlockId := currAdd.block.Block.BlockId + currAddBlockId := currAdd.block.BlockId index := slices.IndexFunc(removeChain, func(i *blockchainpbtypes.Block) bool { return i.BlockId == currAddBlockId }) if index == -1 { // should never happen @@ -230,10 +226,7 @@ func (bcm *bcmModule) addBlock(block *blockchainpbtypes.Block) error { if parent, ok := bcm.leaves[parentId]; ok { bcm.logger.Log(logging.LevelDebug, "Found parend in leaves", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(parentId)) blockNode := bcmBlock{ - block: &blockchainpbtypes.BlockInternal{ - Block: block, - State: nil, - }, + block: block, parent: parent, depth: parent.depth + 1, } @@ -250,19 +243,16 @@ func (bcm *bcmModule) addBlock(block *blockchainpbtypes.Block) error { if _, err := bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { // check if curr matches the block to be added - if so, ignore - if currBlock.block.Block.BlockId == block.BlockId { + if currBlock.block.BlockId == block.BlockId { bcm.logger.Log(logging.LevelDebug, "Block already in tree", "blockId", utils.FormatBlockId(block.BlockId)) return true, ErrNodeAlreadyInTree } // check if curr is parent - if currBlock.block.Block.BlockId == parentId { + if currBlock.block.BlockId == parentId { bcm.logger.Log(logging.LevelDebug, "Found parend in tree", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(parentId)) blockNode := bcmBlock{ - block: &blockchainpbtypes.BlockInternal{ - Block: block, - State: nil, - }, + block: block, parent: currBlock, depth: currBlock.depth + 1, scanCount: 0, @@ -288,7 +278,7 @@ func (bcm *bcmModule) addBlock(block *blockchainpbtypes.Block) error { func (bcm *bcmModule) findBlock(blockId uint64) (*bcmBlock, error) { return bcm.blockTreeTraversal(func(currBlock *bcmBlock) (bool, error) { - if currBlock.block.Block.BlockId == blockId { + if currBlock.block.BlockId == blockId { return true, nil } return false, nil @@ -297,27 +287,18 @@ func (bcm *bcmModule) findBlock(blockId uint64) (*bcmBlock, error) { // first send off to application to verify that payloads are valid, then check that everything links together func (bcm *bcmModule) processNewChain(blocks []*blockchainpbtypes.Block) error { - // take blocks, compute chain to link up with checkpoint, send req to app - - checkpointState := statepbtypes.State{} - checkpointToParent := []*blockchainpbtypes.Block{} - - // find parent parent, _ := bcm.findBlock(blocks[0].PreviousBlockId) if parent == nil { bcm.logger.Log(logging.LevelError, "Parent not found. Could be invalid chain/block or we are missing a part. Sending sync req to sync end of chain.", "blockId", utils.FormatBlockId(blocks[0].BlockId)) - leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.Block.BlockId) + leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.BlockId) bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", utils.FormatBlockId(blocks[len(blocks)-1].BlockId), "leaves+genesis", utils.FormatBlockIdSlice(leavesPlusGenesis)) synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", blocks[len(blocks)-1], leavesPlusGenesis) return nil } - ibChain := bcm.getChainFromCheckpointToBlock(parent) - for _, v := range ibChain { - checkpointToParent = append(checkpointToParent, v.Block) - } + checkpointToParent, checkpointState := bcm.getChainFromCheckpointToBlock(parent) - applicationpbdsl.VerifyBlocksRequest(*bcm.m, "application", &checkpointState, checkpointToParent, blocks) + applicationpbdsl.VerifyBlocksRequest(*bcm.m, "application", checkpointState, checkpointToParent, blocks) return nil } @@ -373,42 +354,42 @@ func (bcm *bcmModule) sendTreeUpdate() { blocks := func() []*blockchainpbtypes.Block { blocks := make([]*blockchainpbtypes.Block, 0, len(bcm.blocks)) for _, v := range bcm.blocks { - blocks = append(blocks, v.block.Block) + blocks = append(blocks, v.block) } return blocks }() leaves := func() []uint64 { leaves := make([]uint64, 0, len(bcm.leaves)) for _, v := range bcm.leaves { - leaves = append(leaves, v.block.Block.BlockId) + leaves = append(leaves, v.block.BlockId) } return leaves }() blockTree := blockchainpbtypes.Blocktree{Blocks: blocks, Leaves: leaves} - interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", &blockTree, bcm.head.block.Block.BlockId) + interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", &blockTree, bcm.head.block.BlockId) } -func (bcm *bcmModule) getChainFromCheckpointToBlock(block *bcmBlock) []*blockchainpbtypes.BlockInternal { +func (bcm *bcmModule) getChainFromCheckpointToBlock(block *bcmBlock) ([]*blockchainpbtypes.Block, *statepbtypes.State) { - chain := make([]*blockchainpbtypes.BlockInternal, 0) + chain := make([]*blockchainpbtypes.Block, 0) currentBlock := block for currentBlock != nil { chain = append(chain, currentBlock.block) - if _, ok := bcm.checkpoints[currentBlock.block.Block.BlockId]; ok { + if bcmBlock, ok := bcm.checkpoints[currentBlock.block.BlockId]; ok { for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 { chain[i], chain[j] = chain[j], chain[i] } - return chain + return chain, bcmBlock.state } currentBlock = currentBlock.parent } - bcm.logger.Log(logging.LevelWarn, "Cannot link up with checkpoints", "source", utils.FormatBlockId(block.block.Block.BlockId), "chain", chain) - return nil + bcm.logger.Log(logging.LevelWarn, "Cannot link up with checkpoints", "source", utils.FormatBlockId(block.block.BlockId), "chain", chain) + return nil, nil } func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, sourceModule t.ModuleID) error { @@ -416,12 +397,16 @@ func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, so return err } - chain := bcm.getChainFromCheckpointToBlock(bcm.head) + chain, checkpointState := bcm.getChainFromCheckpointToBlock(bcm.head) if chain == nil { - bcm.logger.Log(logging.LevelError, "Cannot link head up with checkpoints", "head", utils.FormatBlockId(bcm.head.block.Block.BlockId)) + bcm.logger.Log(logging.LevelError, "Cannot link head up with checkpoints", "head", utils.FormatBlockId(bcm.head.block.BlockId)) + return nil + } else if checkpointState == nil { + bcm.logger.Log(logging.LevelError, "Linked head up with checkpoint but state is missing - this should not happen", "head", utils.FormatBlockId(bcm.head.block.BlockId)) + panic(errors.New("Linked head up with checkpoint but state is missing - this should not happen")) } - bcmpbdsl.GetHeadToCheckpointChainResponse(*bcm.m, sourceModule, requestID, chain) + bcmpbdsl.GetHeadToCheckpointChainResponse(*bcm.m, sourceModule, requestID, chain, checkpointState) return nil @@ -445,7 +430,7 @@ func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepbty panic("block not found when registering checkpoint") // should never happen } else { bcm.logger.Log(logging.LevelDebug, "Found block - registering checkpoint", "blockId", utils.FormatBlockId(block_id)) - hit.block.State = state + hit.state = state bcm.checkpoints[block_id] = hit } @@ -466,10 +451,8 @@ func (bcm *bcmModule) handleInitBlockchain(initialState *statepbtypes.State) err hash := utils.HashBlock(genesis) //uint64(0) genesis.BlockId = hash genesisBcm := bcmBlock{ - block: &blockchainpbtypes.BlockInternal{ - Block: genesis, - State: config.InitialState, - }, + block: genesis, + state: config.InitialState, parent: nil, depth: 0, } @@ -542,7 +525,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { } bcm.logger.Log(logging.LevelDebug, "Found block in tree", "requestId", requestID) - bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block.Block) + bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block) return nil }) @@ -569,11 +552,11 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { bcm.logger.Log(logging.LevelDebug, "Found block in tree - building chain", "requestId", requestID) // initialize chain - chain = append(chain, currentBlock.block.Block) + chain = append(chain, currentBlock.block) for currentBlock.parent != nil { // if parent is in sourceBlockIds, stop - if _, ok := sourceBlockIdsMap[currentBlock.parent.block.Block.BlockId]; ok { + if _, ok := sourceBlockIdsMap[currentBlock.parent.block.BlockId]; ok { // chain build bcm.logger.Log(logging.LevelDebug, "Found link with sourceBlockIds - chain build", "requestId", requestID) // reversing chain to send it in the right order @@ -584,7 +567,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { return nil } - chain = append(chain, currentBlock.parent.block.Block) + chain = append(chain, currentBlock.parent.block) currentBlock = currentBlock.parent } From 1b58e484cb6568c2c4aec0e715b15db17e64958d Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Tue, 30 Jan 2024 22:09:02 +0100 Subject: [PATCH 17/46] switch to proto timestamps, add test script --- pkg/pb/blockchainpb/blockchainpb.pb.go | 85 ++++++++++--------- pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go | 58 +++++++------ .../blockchainpb/payloadpb/types/types.mir.go | 7 +- pkg/pb/blockchainpb/statepb/statepb.pb.go | 74 ++++++++-------- .../blockchainpb/statepb/types/types.mir.go | 7 +- pkg/pb/blockchainpb/types/types.mir.go | 7 +- protos/blockchainpb/blockchainpb.proto | 10 +-- protos/blockchainpb/payloadpb/payloadpb.proto | 8 +- protos/blockchainpb/statepb/statepb.proto | 5 +- samples/blockchain/application/application.go | 17 ++-- .../application/transactions/transactions.go | 3 +- samples/blockchain/bcm.go | 4 +- samples/blockchain/miner.go | 3 +- samples/blockchain/test.sh | 32 +++++++ 14 files changed, 195 insertions(+), 125 deletions(-) create mode 100755 samples/blockchain/test.sh diff --git a/pkg/pb/blockchainpb/blockchainpb.pb.go b/pkg/pb/blockchainpb/blockchainpb.pb.go index e87d898f2..4c705520d 100644 --- a/pkg/pb/blockchainpb/blockchainpb.pb.go +++ b/pkg/pb/blockchainpb/blockchainpb.pb.go @@ -11,6 +11,7 @@ import ( _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -129,10 +130,10 @@ type Block struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - BlockId uint64 `protobuf:"varint,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` - PreviousBlockId uint64 `protobuf:"varint,2,opt,name=previous_block_id,json=previousBlockId,proto3" json:"previous_block_id,omitempty"` - Payload *payloadpb.Payload `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` - Timestamp int64 `protobuf:"varint,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + BlockId uint64 `protobuf:"varint,1,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + PreviousBlockId uint64 `protobuf:"varint,2,opt,name=previous_block_id,json=previousBlockId,proto3" json:"previous_block_id,omitempty"` + Payload *payloadpb.Payload `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (x *Block) Reset() { @@ -188,11 +189,11 @@ func (x *Block) GetPayload() *payloadpb.Payload { return nil } -func (x *Block) GetTimestamp() int64 { +func (x *Block) GetTimestamp() *timestamppb.Timestamp { if x != nil { return x.Timestamp } - return 0 + return nil } var File_blockchainpb_blockchainpb_proto protoreflect.FileDescriptor @@ -205,30 +206,34 @@ var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x26, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x72, - 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, - 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, - 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, - 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3f, 0x0a, - 0x0a, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0xa0, - 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, - 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, - 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x1c, 0x0a, - 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x03, - 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, 0x04, 0x80, 0xa6, 0x1d, - 0x01, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, - 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, + 0x72, 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, + 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, + 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3f, + 0x0a, 0x0a, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, + 0xbc, 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, + 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, + 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x38, + 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x35, + 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, + 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, + 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -245,20 +250,22 @@ func file_blockchainpb_blockchainpb_proto_rawDescGZIP() []byte { var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_blockchainpb_blockchainpb_proto_goTypes = []interface{}{ - (*Blocktree)(nil), // 0: blockchainpb.Blocktree - (*Blockchain)(nil), // 1: blockchainpb.Blockchain - (*Block)(nil), // 2: blockchainpb.Block - (*payloadpb.Payload)(nil), // 3: payloadpb.Payload + (*Blocktree)(nil), // 0: blockchainpb.Blocktree + (*Blockchain)(nil), // 1: blockchainpb.Blockchain + (*Block)(nil), // 2: blockchainpb.Block + (*payloadpb.Payload)(nil), // 3: payloadpb.Payload + (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp } var file_blockchainpb_blockchainpb_proto_depIdxs = []int32{ 2, // 0: blockchainpb.Blocktree.blocks:type_name -> blockchainpb.Block 2, // 1: blockchainpb.Blockchain.blocks:type_name -> blockchainpb.Block 3, // 2: blockchainpb.Block.payload:type_name -> payloadpb.Payload - 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 + 4, // 3: blockchainpb.Block.timestamp:type_name -> google.protobuf.Timestamp + 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_blockchainpb_blockchainpb_proto_init() } diff --git a/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go b/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go index 70c7d309f..b850787d3 100644 --- a/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go +++ b/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go @@ -10,6 +10,7 @@ import ( _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -27,9 +28,9 @@ type Payload struct { unknownFields protoimpl.UnknownFields // application specific payload - Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` - Sender string `protobuf:"bytes,3,opt,name=sender,proto3" json:"sender,omitempty"` + Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + Sender string `protobuf:"bytes,3,opt,name=sender,proto3" json:"sender,omitempty"` } func (x *Payload) Reset() { @@ -71,11 +72,11 @@ func (x *Payload) GetMessage() string { return "" } -func (x *Payload) GetTimestamp() int64 { +func (x *Payload) GetTimestamp() *timestamppb.Timestamp { if x != nil { return x.Timestamp } - return 0 + return nil } func (x *Payload) GetSender() string { @@ -93,21 +94,24 @@ var file_blockchainpb_payloadpb_payloadpb_proto_rawDesc = []byte{ 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x09, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0x95, 0x01, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, 0x0a, - 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x4c, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x34, 0x82, 0xa6, 0x1d, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, - 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x52, 0x06, 0x73, 0x65, 0x6e, - 0x64, 0x65, 0x72, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, - 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, - 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, - 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0xb1, 0x01, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x18, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x12, 0x4c, 0x0a, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x34, 0x82, 0xa6, 0x1d, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x52, 0x06, 0x73, 0x65, 0x6e, 0x64, 0x65, 0x72, + 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x3f, 0x5a, 0x3d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, + 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -124,14 +128,16 @@ func file_blockchainpb_payloadpb_payloadpb_proto_rawDescGZIP() []byte { var file_blockchainpb_payloadpb_payloadpb_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_blockchainpb_payloadpb_payloadpb_proto_goTypes = []interface{}{ - (*Payload)(nil), // 0: payloadpb.Payload + (*Payload)(nil), // 0: payloadpb.Payload + (*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp } var file_blockchainpb_payloadpb_payloadpb_proto_depIdxs = []int32{ - 0, // [0:0] is the sub-list for method output_type - 0, // [0:0] 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 + 1, // 0: payloadpb.Payload.timestamp:type_name -> google.protobuf.Timestamp + 1, // [1:1] is the sub-list for method output_type + 1, // [1:1] is the sub-list for method input_type + 1, // [1:1] is the sub-list for extension type_name + 1, // [1:1] is the sub-list for extension extendee + 0, // [0:1] is the sub-list for field type_name } func init() { file_blockchainpb_payloadpb_payloadpb_proto_init() } diff --git a/pkg/pb/blockchainpb/payloadpb/types/types.mir.go b/pkg/pb/blockchainpb/payloadpb/types/types.mir.go index e4d3833c5..8568db1f3 100644 --- a/pkg/pb/blockchainpb/payloadpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/payloadpb/types/types.mir.go @@ -7,11 +7,12 @@ import ( payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" types "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" ) type Payload struct { Message string - Timestamp int64 + Timestamp *timestamppb.Timestamp Sender types.NodeID } @@ -33,7 +34,9 @@ func (m *Payload) Pb() *payloadpb.Payload { pbMessage := &payloadpb.Payload{} { pbMessage.Message = m.Message - pbMessage.Timestamp = m.Timestamp + if m.Timestamp != nil { + pbMessage.Timestamp = m.Timestamp + } pbMessage.Sender = (string)(m.Sender) } diff --git a/pkg/pb/blockchainpb/statepb/statepb.pb.go b/pkg/pb/blockchainpb/statepb/statepb.pb.go index e3e271099..deba7c845 100644 --- a/pkg/pb/blockchainpb/statepb/statepb.pb.go +++ b/pkg/pb/blockchainpb/statepb/statepb.pb.go @@ -10,6 +10,7 @@ import ( _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" ) @@ -82,8 +83,8 @@ type LastSentTimestamp struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` - Timestamp int64 `protobuf:"varint,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + NodeId string `protobuf:"bytes,1,opt,name=node_id,json=nodeId,proto3" json:"node_id,omitempty"` + Timestamp *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"` } func (x *LastSentTimestamp) Reset() { @@ -125,11 +126,11 @@ func (x *LastSentTimestamp) GetNodeId() string { return "" } -func (x *LastSentTimestamp) GetTimestamp() int64 { +func (x *LastSentTimestamp) GetTimestamp() *timestamppb.Timestamp { if x != nil { return x.Timestamp } - return 0 + return nil } var File_blockchainpb_statepb_statepb_proto protoreflect.FileDescriptor @@ -139,29 +140,32 @@ var file_blockchainpb_statepb_statepb_proto_rawDesc = []byte{ 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x84, 0x01, 0x0a, 0x05, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, - 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x4c, - 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, - 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x3a, 0x04, 0x80, 0xa6, - 0x1d, 0x01, 0x22, 0x86, 0x01, 0x0a, 0x11, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x4d, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, - 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x34, 0x82, 0xa6, 0x1d, 0x30, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, - 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x52, - 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, - 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, - 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x84, 0x01, 0x0a, + 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, + 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, + 0x4c, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x53, + 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x3a, 0x04, 0x80, + 0xa6, 0x1d, 0x01, 0x22, 0xa2, 0x01, 0x0a, 0x11, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x4d, 0x0a, 0x07, 0x6e, 0x6f, 0x64, + 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x34, 0x82, 0xa6, 0x1d, 0x30, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, + 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, + 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, + 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, + 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -178,16 +182,18 @@ func file_blockchainpb_statepb_statepb_proto_rawDescGZIP() []byte { var file_blockchainpb_statepb_statepb_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_blockchainpb_statepb_statepb_proto_goTypes = []interface{}{ - (*State)(nil), // 0: statepb.State - (*LastSentTimestamp)(nil), // 1: statepb.LastSentTimestamp + (*State)(nil), // 0: statepb.State + (*LastSentTimestamp)(nil), // 1: statepb.LastSentTimestamp + (*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp } var file_blockchainpb_statepb_statepb_proto_depIdxs = []int32{ 1, // 0: statepb.State.last_sent_timestamps:type_name -> statepb.LastSentTimestamp - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 2, // 1: statepb.LastSentTimestamp.timestamp:type_name -> google.protobuf.Timestamp + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_blockchainpb_statepb_statepb_proto_init() } diff --git a/pkg/pb/blockchainpb/statepb/types/types.mir.go b/pkg/pb/blockchainpb/statepb/types/types.mir.go index c26623e7c..308fb8ad0 100644 --- a/pkg/pb/blockchainpb/statepb/types/types.mir.go +++ b/pkg/pb/blockchainpb/statepb/types/types.mir.go @@ -8,6 +8,7 @@ import ( statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" types1 "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" ) type State struct { @@ -48,7 +49,7 @@ func (*State) MirReflect() mirreflect.Type { type LastSentTimestamp struct { NodeId types1.NodeID - Timestamp int64 + Timestamp *timestamppb.Timestamp } func LastSentTimestampFromPb(pb *statepb.LastSentTimestamp) *LastSentTimestamp { @@ -68,7 +69,9 @@ func (m *LastSentTimestamp) Pb() *statepb.LastSentTimestamp { pbMessage := &statepb.LastSentTimestamp{} { pbMessage.NodeId = (string)(m.NodeId) - pbMessage.Timestamp = m.Timestamp + if m.Timestamp != nil { + pbMessage.Timestamp = m.Timestamp + } } return pbMessage diff --git a/pkg/pb/blockchainpb/types/types.mir.go b/pkg/pb/blockchainpb/types/types.mir.go index 62432c861..46aa247e2 100644 --- a/pkg/pb/blockchainpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/types/types.mir.go @@ -8,6 +8,7 @@ import ( blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" + timestamppb "google.golang.org/protobuf/types/known/timestamppb" ) type Blocktree struct { @@ -83,7 +84,7 @@ type Block struct { BlockId uint64 PreviousBlockId uint64 Payload *types1.Payload - Timestamp int64 + Timestamp *timestamppb.Timestamp } func BlockFromPb(pb *blockchainpb.Block) *Block { @@ -109,7 +110,9 @@ func (m *Block) Pb() *blockchainpb.Block { if m.Payload != nil { pbMessage.Payload = (m.Payload).Pb() } - pbMessage.Timestamp = m.Timestamp + if m.Timestamp != nil { + pbMessage.Timestamp = m.Timestamp + } } return pbMessage diff --git a/protos/blockchainpb/blockchainpb.proto b/protos/blockchainpb/blockchainpb.proto index a19911dfd..e7014cfa8 100644 --- a/protos/blockchainpb/blockchainpb.proto +++ b/protos/blockchainpb/blockchainpb.proto @@ -5,8 +5,8 @@ package blockchainpb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb"; import "mir/codegen_extensions.proto"; - import "blockchainpb/payloadpb/payloadpb.proto"; +import "google/protobuf/timestamp.proto"; message Blocktree { option (mir.struct) = true; @@ -24,9 +24,9 @@ message Blockchain { message Block { option (mir.struct) = true; - uint64 block_id = 1; - uint64 previous_block_id = 2; - payloadpb.Payload payload = 3; + uint64 block_id = 1; + uint64 previous_block_id = 2; + payloadpb.Payload payload = 3; - int64 timestamp = 4; + google.protobuf.Timestamp timestamp = 4; } \ No newline at end of file diff --git a/protos/blockchainpb/payloadpb/payloadpb.proto b/protos/blockchainpb/payloadpb/payloadpb.proto index 067f382db..655249195 100644 --- a/protos/blockchainpb/payloadpb/payloadpb.proto +++ b/protos/blockchainpb/payloadpb/payloadpb.proto @@ -5,12 +5,14 @@ package payloadpb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb"; import "mir/codegen_extensions.proto"; +import "google/protobuf/timestamp.proto"; + message Payload { option (mir.struct) = true; // application specific payload - string message = 1; - int64 timestamp = 2; - string sender = 3 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.NodeID"]; + string message = 1; + google.protobuf.Timestamp timestamp = 2; + string sender = 3 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.NodeID"]; } \ No newline at end of file diff --git a/protos/blockchainpb/statepb/statepb.proto b/protos/blockchainpb/statepb/statepb.proto index efab977a6..187661352 100644 --- a/protos/blockchainpb/statepb/statepb.proto +++ b/protos/blockchainpb/statepb/statepb.proto @@ -5,6 +5,7 @@ package statepb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb"; import "mir/codegen_extensions.proto"; +import "google/protobuf/timestamp.proto"; message State { option (mir.struct) = true; @@ -17,6 +18,6 @@ message State { message LastSentTimestamp { option (mir.struct) = true; - string node_id = 1 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.NodeID"]; - int64 timestamp = 2; + string node_id = 1 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.NodeID"]; + google.protobuf.Timestamp timestamp = 2; } \ No newline at end of file diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index a47ebd605..9cf6f3c5a 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -4,7 +4,6 @@ package application import ( "fmt" - "time" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" @@ -19,6 +18,7 @@ import ( "github.com/filecoin-project/mir/samples/blockchain/application/config" "github.com/filecoin-project/mir/samples/blockchain/application/transactions" "github.com/filecoin-project/mir/samples/blockchain/utils" + "google.golang.org/protobuf/types/known/timestamppb" ) type ApplicationModule struct { @@ -35,9 +35,11 @@ func applyBlockToState(state *statepbtypes.State, block *blockchainpbtypes.Block timeStamps := state.LastSentTimestamps msgHistory := state.MessageHistory + // why is this necessary? it should be verified already for i, lt := range timeStamps { if lt.NodeId == sender { - if lt.Timestamp > block.Payload.Timestamp { + ltTimestamp := lt.Timestamp.AsTime() + if ltTimestamp.After(block.Payload.Timestamp.AsTime()) { panic("invalid ordering - there is a block that should never have been accepted") } @@ -78,7 +80,8 @@ func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain, chain *b state := checkpointState // compute state at fork roo - for _, block := range chain.Blocks { + // skip first as checkpoint state already 'included' its payload + for _, block := range chain.Blocks[1:] { state = applyBlockToState(state, block) } // compute state at new head @@ -111,10 +114,12 @@ func (am *ApplicationModule) handleVerifyBlocksRequest(checkpointState *statepbt chain := append(chainCheckpointToStart, chainToVerify...) // chainCheckpointToStart wouldn't need to be verified, but we need it to get the timestamps for _, block := range chain { + blockTs := block.Payload.Timestamp.AsTime() // verify block for i, lt := range timeStamps { + ltTimestamp := lt.Timestamp.AsTime() if lt.NodeId == block.Payload.Sender { - if lt.Timestamp > block.Payload.Timestamp { + if ltTimestamp.After(blockTs) { // block in chain invalid, don't respond return nil } @@ -148,7 +153,7 @@ func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { if payload == nil { payload = &payloadpbtypes.Payload{ Message: "", - Timestamp: time.Now().Unix(), + Timestamp: timestamppb.Now(), Sender: am.nodeID, } } @@ -182,7 +187,7 @@ func NewApplication(logger logging.Logger, nodeID t.NodeID) modules.PassiveModul applicationpbdsl.UponMessageInput(m, func(text string) error { am.tm.AddPayload(&payloadpbtypes.Payload{ Message: text, - Timestamp: time.Now().Unix(), + Timestamp: timestamppb.Now(), Sender: am.nodeID, }) diff --git a/samples/blockchain/application/transactions/transactions.go b/samples/blockchain/application/transactions/transactions.go index 6cf8f0d7d..1da1645c9 100644 --- a/samples/blockchain/application/transactions/transactions.go +++ b/samples/blockchain/application/transactions/transactions.go @@ -1,7 +1,6 @@ package transactions import ( - "cmp" "slices" payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" @@ -35,7 +34,7 @@ func (tm *TransactionManager) GetPayload() *payloadpbtypes.Payload { // sort by timestamp // TODO: keep it sorted slices.SortFunc(tm.transactions, func(i, j transaction) int { - return cmp.Compare[int64](i.payload.Timestamp, j.payload.Timestamp) + return i.payload.Timestamp.AsTime().Compare(j.payload.Timestamp.AsTime()) }) return tm.transactions[0].payload } diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 9936433cd..957dad1e8 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -5,6 +5,7 @@ package main import ( "errors" "slices" + "time" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" @@ -21,6 +22,7 @@ import ( "github.com/filecoin-project/mir/samples/blockchain/application/config" "github.com/filecoin-project/mir/samples/blockchain/utils" "golang.org/x/exp/maps" + "google.golang.org/protobuf/types/known/timestamppb" ) var ( @@ -445,7 +447,7 @@ func (bcm *bcmModule) handleInitBlockchain(initialState *statepbtypes.State) err BlockId: 0, PreviousBlockId: 0, Payload: &payloadpbtypes.Payload{}, - Timestamp: 0, // unix 0 + Timestamp: timestamppb.New(time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC)), // unix 0 } hash := utils.HashBlock(genesis) //uint64(0) diff --git a/samples/blockchain/miner.go b/samples/blockchain/miner.go index 6aa38bb08..84054b529 100644 --- a/samples/blockchain/miner.go +++ b/samples/blockchain/miner.go @@ -19,6 +19,7 @@ import ( "github.com/filecoin-project/mir/samples/blockchain/utils" "github.com/go-errors/errors" "github.com/mitchellh/hashstructure" + "google.golang.org/protobuf/types/known/timestamppb" ) const ( @@ -93,7 +94,7 @@ func (m *minerModule) mineWorkerManager() { m.logger.Log(logging.LevelDebug, "Mining aborted", "headId", utils.FormatBlockId(blockRequest.HeadId)) return case <-time.After(delay): - block := &blockchainpbtypes.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: time.Now().Unix()} + block := &blockchainpbtypes.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: timestamppb.Now()} hash, err := hashstructure.Hash(block, nil) if err != nil { m.logger.Log(logging.LevelError, "Failed to hash block", "error", err) diff --git a/samples/blockchain/test.sh b/samples/blockchain/test.sh new file mode 100755 index 000000000..56f3bbac4 --- /dev/null +++ b/samples/blockchain/test.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +NODE_0_LOG="./node_0.log" +NODE_1_LOG="./node_1.log" +NODE_2_LOG="./node_2.log" +NODE_3_LOG="./node_3.log" + +rm -rf ./node_*.log + +tmux kill-session -t demo +tmux new-session -d -s demo \; \ + \ + split-window -t demo:0 -v \; \ + split-window -t demo:0.0 -h \; \ + split-window -t "demo:0.2" -h \; \ + \ + send-keys -t "demo:0.0" "go run . 4 0 true 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.1" "go run . 4 1 true 2>&1 | tee \"$NODE_1_LOG\"" Enter \; \ + send-keys -t "demo:0.2" "go run . 4 2 true 2>&1 | tee \"$NODE_2_LOG\"" Enter \; \ + send-keys -t "demo:0.3" "go run . 4 3 true 2>&1 | tee \"$NODE_3_LOG\"" Enter \; + +sleep 5 # wait for it to start up +for i in {1..15}; do sleep 1; + tmux send-keys -t "demo:0.0" "0-$i" Enter \;; + tmux send-keys -t "demo:0.1" "1-$i" Enter \;; + tmux send-keys -t "demo:0.2" "2-$i" Enter \;; + tmux send-keys -t "demo:0.3" "3-$i" Enter \;; +done + +tmux attach-session -t "demo:0.0" +#!/usr/bin/env bash +set -e \ No newline at end of file From 67ed4b8f40fff357493752ccf3fd89e3f3e81a3d Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Wed, 31 Jan 2024 00:48:34 +0100 Subject: [PATCH 18/46] get rid of blockchain -> repeated blocks --- .../applicationpb/applicationpb.pb.go | 112 ++++++++------- .../applicationpb/dsl/emit.mir.go | 2 +- .../applicationpb/dsl/upon.mir.go | 2 +- .../applicationpb/events/events.mir.go | 2 +- .../applicationpb/types/types.mir.go | 38 ++--- pkg/pb/blockchainpb/blockchainpb.pb.go | 131 +++++------------- pkg/pb/blockchainpb/types/types.mir.go | 33 ----- .../applicationpb/applicationpb.proto | 8 +- protos/blockchainpb/blockchainpb.proto | 6 - samples/blockchain/application/application.go | 12 +- samples/blockchain/bcm.go | 13 +- 11 files changed, 129 insertions(+), 230 deletions(-) diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go index 202017a39..3d1f2bb47 100644 --- a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go @@ -341,10 +341,10 @@ type ForkUpdate struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RemovedChain *blockchainpb.Blockchain `protobuf:"bytes,1,opt,name=removed_chain,json=removedChain,proto3" json:"removed_chain,omitempty"` - AddedChain *blockchainpb.Blockchain `protobuf:"bytes,2,opt,name=added_chain,json=addedChain,proto3" json:"added_chain,omitempty"` - CheckpointToForkRoot *blockchainpb.Blockchain `protobuf:"bytes,3,opt,name=checkpoint_to_fork_root,json=checkpointToForkRoot,proto3" json:"checkpoint_to_fork_root,omitempty"` - CheckpointState *statepb.State `protobuf:"bytes,4,opt,name=checkpoint_state,json=checkpointState,proto3" json:"checkpoint_state,omitempty"` + RemovedChain []*blockchainpb.Block `protobuf:"bytes,1,rep,name=removed_chain,json=removedChain,proto3" json:"removed_chain,omitempty"` + AddedChain []*blockchainpb.Block `protobuf:"bytes,2,rep,name=added_chain,json=addedChain,proto3" json:"added_chain,omitempty"` + CheckpointToForkRoot []*blockchainpb.Block `protobuf:"bytes,3,rep,name=checkpoint_to_fork_root,json=checkpointToForkRoot,proto3" json:"checkpoint_to_fork_root,omitempty"` + CheckpointState *statepb.State `protobuf:"bytes,4,opt,name=checkpoint_state,json=checkpointState,proto3" json:"checkpoint_state,omitempty"` } func (x *ForkUpdate) Reset() { @@ -379,21 +379,21 @@ func (*ForkUpdate) Descriptor() ([]byte, []int) { return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{4} } -func (x *ForkUpdate) GetRemovedChain() *blockchainpb.Blockchain { +func (x *ForkUpdate) GetRemovedChain() []*blockchainpb.Block { if x != nil { return x.RemovedChain } return nil } -func (x *ForkUpdate) GetAddedChain() *blockchainpb.Blockchain { +func (x *ForkUpdate) GetAddedChain() []*blockchainpb.Block { if x != nil { return x.AddedChain } return nil } -func (x *ForkUpdate) GetCheckpointToForkRoot() *blockchainpb.Blockchain { +func (x *ForkUpdate) GetCheckpointToForkRoot() []*blockchainpb.Block { if x != nil { return x.CheckpointToForkRoot } @@ -629,41 +629,40 @@ var file_blockchainpb_applicationpb_applicationpb_proto_rawDesc = []byte{ 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, - 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x98, 0x02, 0x0a, 0x0a, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x12, 0x3d, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, + 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x89, 0x02, 0x0a, 0x0a, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x12, 0x38, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, - 0x61, 0x69, 0x6e, 0x12, 0x39, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x4f, - 0x0a, 0x17, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, - 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x14, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x6f, 0x46, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, - 0x39, 0x0a, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, - 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, - 0x01, 0x22, 0x5e, 0x0a, 0x0f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, 0x0a, - 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, - 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, - 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, - 0x01, 0x22, 0x28, 0x0a, 0x0c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, - 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x04, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, - 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x34, + 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, + 0x68, 0x61, 0x69, 0x6e, 0x12, 0x4a, 0x0a, 0x17, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, + 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x14, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x6f, 0x46, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x6f, 0x74, + 0x12, 0x39, 0x0a, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, + 0x01, 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, + 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x0c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, + 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, + 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, + 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -680,18 +679,17 @@ func file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP() []byte { var file_blockchainpb_applicationpb_applicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_blockchainpb_applicationpb_applicationpb_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: applicationpb.Event - (*NewHead)(nil), // 1: applicationpb.NewHead - (*VerifyBlocksRequest)(nil), // 2: applicationpb.VerifyBlocksRequest - (*VerifyBlocksResponse)(nil), // 3: applicationpb.VerifyBlocksResponse - (*ForkUpdate)(nil), // 4: applicationpb.ForkUpdate - (*PayloadRequest)(nil), // 5: applicationpb.PayloadRequest - (*PayloadResponse)(nil), // 6: applicationpb.PayloadResponse - (*MessageInput)(nil), // 7: applicationpb.MessageInput - (*statepb.State)(nil), // 8: statepb.State - (*blockchainpb.Block)(nil), // 9: blockchainpb.Block - (*blockchainpb.Blockchain)(nil), // 10: blockchainpb.Blockchain - (*payloadpb.Payload)(nil), // 11: payloadpb.Payload + (*Event)(nil), // 0: applicationpb.Event + (*NewHead)(nil), // 1: applicationpb.NewHead + (*VerifyBlocksRequest)(nil), // 2: applicationpb.VerifyBlocksRequest + (*VerifyBlocksResponse)(nil), // 3: applicationpb.VerifyBlocksResponse + (*ForkUpdate)(nil), // 4: applicationpb.ForkUpdate + (*PayloadRequest)(nil), // 5: applicationpb.PayloadRequest + (*PayloadResponse)(nil), // 6: applicationpb.PayloadResponse + (*MessageInput)(nil), // 7: applicationpb.MessageInput + (*statepb.State)(nil), // 8: statepb.State + (*blockchainpb.Block)(nil), // 9: blockchainpb.Block + (*payloadpb.Payload)(nil), // 10: payloadpb.Payload } var file_blockchainpb_applicationpb_applicationpb_proto_depIdxs = []int32{ 1, // 0: applicationpb.Event.new_head:type_name -> applicationpb.NewHead @@ -705,11 +703,11 @@ var file_blockchainpb_applicationpb_applicationpb_proto_depIdxs = []int32{ 9, // 8: applicationpb.VerifyBlocksRequest.chain_checkpoint_to_start:type_name -> blockchainpb.Block 9, // 9: applicationpb.VerifyBlocksRequest.chain_to_verify:type_name -> blockchainpb.Block 9, // 10: applicationpb.VerifyBlocksResponse.verified_blocks:type_name -> blockchainpb.Block - 10, // 11: applicationpb.ForkUpdate.removed_chain:type_name -> blockchainpb.Blockchain - 10, // 12: applicationpb.ForkUpdate.added_chain:type_name -> blockchainpb.Blockchain - 10, // 13: applicationpb.ForkUpdate.checkpoint_to_fork_root:type_name -> blockchainpb.Blockchain + 9, // 11: applicationpb.ForkUpdate.removed_chain:type_name -> blockchainpb.Block + 9, // 12: applicationpb.ForkUpdate.added_chain:type_name -> blockchainpb.Block + 9, // 13: applicationpb.ForkUpdate.checkpoint_to_fork_root:type_name -> blockchainpb.Block 8, // 14: applicationpb.ForkUpdate.checkpoint_state:type_name -> statepb.State - 11, // 15: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload + 10, // 15: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload 16, // [16:16] is the sub-list for method output_type 16, // [16:16] is the sub-list for method input_type 16, // [16:16] is the sub-list for extension type_name diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go index e1eb5cfa7..ed42ac9d3 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go @@ -33,7 +33,7 @@ func PayloadResponse(m dsl.Module, destModule types.ModuleID, headId uint64, pay dsl.EmitMirEvent(m, events.PayloadResponse(destModule, headId, payload)) } -func ForkUpdate(m dsl.Module, destModule types.ModuleID, removedChain *types2.Blockchain, addedChain *types2.Blockchain, checkpointToForkRoot *types2.Blockchain, checkpointState *types1.State) { +func ForkUpdate(m dsl.Module, destModule types.ModuleID, removedChain []*types2.Block, addedChain []*types2.Block, checkpointToForkRoot []*types2.Block, checkpointState *types1.State) { dsl.EmitMirEvent(m, events.ForkUpdate(destModule, removedChain, addedChain, checkpointToForkRoot, checkpointState)) } diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go index 8a8669017..211b480ae 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go @@ -54,7 +54,7 @@ func UponPayloadResponse(m dsl.Module, handler func(headId uint64, payload *type }) } -func UponForkUpdate(m dsl.Module, handler func(removedChain *types3.Blockchain, addedChain *types3.Blockchain, checkpointToForkRoot *types3.Blockchain, checkpointState *types2.State) error) { +func UponForkUpdate(m dsl.Module, handler func(removedChain []*types3.Block, addedChain []*types3.Block, checkpointToForkRoot []*types3.Block, checkpointState *types2.State) error) { UponEvent[*types.Event_ForkUpdate](m, func(ev *types.ForkUpdate) error { return handler(ev.RemovedChain, ev.AddedChain, ev.CheckpointToForkRoot, ev.CheckpointState) }) diff --git a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go index dad67ef50..adf5a64ee 100644 --- a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go @@ -89,7 +89,7 @@ func PayloadResponse(destModule types.ModuleID, headId uint64, payload *types5.P } } -func ForkUpdate(destModule types.ModuleID, removedChain *types4.Blockchain, addedChain *types4.Blockchain, checkpointToForkRoot *types4.Blockchain, checkpointState *types3.State) *types1.Event { +func ForkUpdate(destModule types.ModuleID, removedChain []*types4.Block, addedChain []*types4.Block, checkpointToForkRoot []*types4.Block, checkpointState *types3.State) *types1.Event { return &types1.Event{ DestModule: destModule, Type: &types1.Event_Application{ diff --git a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go index 27e8117a7..1f8fa5908 100644 --- a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go @@ -354,9 +354,9 @@ func (*VerifyBlocksResponse) MirReflect() mirreflect.Type { } type ForkUpdate struct { - RemovedChain *types1.Blockchain - AddedChain *types1.Blockchain - CheckpointToForkRoot *types1.Blockchain + RemovedChain []*types1.Block + AddedChain []*types1.Block + CheckpointToForkRoot []*types1.Block CheckpointState *types.State } @@ -365,10 +365,16 @@ func ForkUpdateFromPb(pb *applicationpb.ForkUpdate) *ForkUpdate { return nil } return &ForkUpdate{ - RemovedChain: types1.BlockchainFromPb(pb.RemovedChain), - AddedChain: types1.BlockchainFromPb(pb.AddedChain), - CheckpointToForkRoot: types1.BlockchainFromPb(pb.CheckpointToForkRoot), - CheckpointState: types.StateFromPb(pb.CheckpointState), + RemovedChain: types2.ConvertSlice(pb.RemovedChain, func(t *blockchainpb.Block) *types1.Block { + return types1.BlockFromPb(t) + }), + AddedChain: types2.ConvertSlice(pb.AddedChain, func(t *blockchainpb.Block) *types1.Block { + return types1.BlockFromPb(t) + }), + CheckpointToForkRoot: types2.ConvertSlice(pb.CheckpointToForkRoot, func(t *blockchainpb.Block) *types1.Block { + return types1.BlockFromPb(t) + }), + CheckpointState: types.StateFromPb(pb.CheckpointState), } } @@ -378,15 +384,15 @@ func (m *ForkUpdate) Pb() *applicationpb.ForkUpdate { } pbMessage := &applicationpb.ForkUpdate{} { - if m.RemovedChain != nil { - pbMessage.RemovedChain = (m.RemovedChain).Pb() - } - if m.AddedChain != nil { - pbMessage.AddedChain = (m.AddedChain).Pb() - } - if m.CheckpointToForkRoot != nil { - pbMessage.CheckpointToForkRoot = (m.CheckpointToForkRoot).Pb() - } + pbMessage.RemovedChain = types2.ConvertSlice(m.RemovedChain, func(t *types1.Block) *blockchainpb.Block { + return (t).Pb() + }) + pbMessage.AddedChain = types2.ConvertSlice(m.AddedChain, func(t *types1.Block) *blockchainpb.Block { + return (t).Pb() + }) + pbMessage.CheckpointToForkRoot = types2.ConvertSlice(m.CheckpointToForkRoot, func(t *types1.Block) *blockchainpb.Block { + return (t).Pb() + }) if m.CheckpointState != nil { pbMessage.CheckpointState = (m.CheckpointState).Pb() } diff --git a/pkg/pb/blockchainpb/blockchainpb.pb.go b/pkg/pb/blockchainpb/blockchainpb.pb.go index 4c705520d..33944fc62 100644 --- a/pkg/pb/blockchainpb/blockchainpb.pb.go +++ b/pkg/pb/blockchainpb/blockchainpb.pb.go @@ -78,53 +78,6 @@ func (x *Blocktree) GetLeaves() []uint64 { return nil } -type Blockchain struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Blocks []*Block `protobuf:"bytes,1,rep,name=blocks,proto3" json:"blocks,omitempty"` // ordered, no forks -> the 'current' chain -} - -func (x *Blockchain) Reset() { - *x = Blockchain{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Blockchain) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Blockchain) ProtoMessage() {} - -func (x *Blockchain) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[1] - 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 Blockchain.ProtoReflect.Descriptor instead. -func (*Blockchain) Descriptor() ([]byte, []int) { - return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{1} -} - -func (x *Blockchain) GetBlocks() []*Block { - if x != nil { - return x.Blocks - } - return nil -} - type Block struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -139,7 +92,7 @@ type Block struct { func (x *Block) Reset() { *x = Block{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[2] + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -152,7 +105,7 @@ func (x *Block) String() string { func (*Block) ProtoMessage() {} func (x *Block) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[2] + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -165,7 +118,7 @@ func (x *Block) ProtoReflect() protoreflect.Message { // Deprecated: Use Block.ProtoReflect.Descriptor instead. func (*Block) Descriptor() ([]byte, []int) { - return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{2} + return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{1} } func (x *Block) GetBlockId() uint64 { @@ -213,27 +166,23 @@ var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, - 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3f, - 0x0a, 0x0a, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, - 0xbc, 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, - 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, - 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, - 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, - 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x38, - 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, - 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x35, - 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, - 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, - 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0xbc, + 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, + 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, + 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x38, 0x0a, + 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x35, 0x5a, + 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, + 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, + 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -248,24 +197,22 @@ func file_blockchainpb_blockchainpb_proto_rawDescGZIP() []byte { return file_blockchainpb_blockchainpb_proto_rawDescData } -var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 3) +var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 2) var file_blockchainpb_blockchainpb_proto_goTypes = []interface{}{ (*Blocktree)(nil), // 0: blockchainpb.Blocktree - (*Blockchain)(nil), // 1: blockchainpb.Blockchain - (*Block)(nil), // 2: blockchainpb.Block - (*payloadpb.Payload)(nil), // 3: payloadpb.Payload - (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp + (*Block)(nil), // 1: blockchainpb.Block + (*payloadpb.Payload)(nil), // 2: payloadpb.Payload + (*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp } var file_blockchainpb_blockchainpb_proto_depIdxs = []int32{ - 2, // 0: blockchainpb.Blocktree.blocks:type_name -> blockchainpb.Block - 2, // 1: blockchainpb.Blockchain.blocks:type_name -> blockchainpb.Block - 3, // 2: blockchainpb.Block.payload:type_name -> payloadpb.Payload - 4, // 3: blockchainpb.Block.timestamp:type_name -> google.protobuf.Timestamp - 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 + 1, // 0: blockchainpb.Blocktree.blocks:type_name -> blockchainpb.Block + 2, // 1: blockchainpb.Block.payload:type_name -> payloadpb.Payload + 3, // 2: blockchainpb.Block.timestamp:type_name -> google.protobuf.Timestamp + 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 } func init() { file_blockchainpb_blockchainpb_proto_init() } @@ -287,18 +234,6 @@ func file_blockchainpb_blockchainpb_proto_init() { } } file_blockchainpb_blockchainpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Blockchain); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_blockchainpb_blockchainpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Block); i { case 0: return &v.state @@ -317,7 +252,7 @@ func file_blockchainpb_blockchainpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_blockchainpb_proto_rawDesc, NumEnums: 0, - NumMessages: 3, + NumMessages: 2, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/types/types.mir.go b/pkg/pb/blockchainpb/types/types.mir.go index 46aa247e2..4c6e0f61f 100644 --- a/pkg/pb/blockchainpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/types/types.mir.go @@ -47,39 +47,6 @@ func (*Blocktree) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*blockchainpb.Blocktree]()} } -type Blockchain struct { - Blocks []*Block -} - -func BlockchainFromPb(pb *blockchainpb.Blockchain) *Blockchain { - if pb == nil { - return nil - } - return &Blockchain{ - Blocks: types.ConvertSlice(pb.Blocks, func(t *blockchainpb.Block) *Block { - return BlockFromPb(t) - }), - } -} - -func (m *Blockchain) Pb() *blockchainpb.Blockchain { - if m == nil { - return nil - } - pbMessage := &blockchainpb.Blockchain{} - { - pbMessage.Blocks = types.ConvertSlice(m.Blocks, func(t *Block) *blockchainpb.Block { - return (t).Pb() - }) - } - - return pbMessage -} - -func (*Blockchain) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*blockchainpb.Blockchain]()} -} - type Block struct { BlockId uint64 PreviousBlockId uint64 diff --git a/protos/blockchainpb/applicationpb/applicationpb.proto b/protos/blockchainpb/applicationpb/applicationpb.proto index c449d147a..f048f8ab5 100644 --- a/protos/blockchainpb/applicationpb/applicationpb.proto +++ b/protos/blockchainpb/applicationpb/applicationpb.proto @@ -54,10 +54,10 @@ message VerifyBlocksResponse { message ForkUpdate { option (mir.event) = true; - blockchainpb.Blockchain removed_chain = 1; - blockchainpb.Blockchain added_chain = 2; - blockchainpb.Blockchain checkpoint_to_fork_root = 3; - statepb.State checkpoint_state = 4; + repeated blockchainpb.Block removed_chain = 1; + repeated blockchainpb.Block added_chain = 2; + repeated blockchainpb.Block checkpoint_to_fork_root = 3; + statepb.State checkpoint_state = 4; } // Transaction management application events diff --git a/protos/blockchainpb/blockchainpb.proto b/protos/blockchainpb/blockchainpb.proto index e7014cfa8..4d92b2dfe 100644 --- a/protos/blockchainpb/blockchainpb.proto +++ b/protos/blockchainpb/blockchainpb.proto @@ -15,12 +15,6 @@ message Blocktree { repeated uint64 leaves = 2; } -message Blockchain { - option (mir.struct) = true; - - repeated Block blocks = 1; // ordered, no forks -> the 'current' chain -} - message Block { option (mir.struct) = true; diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index 9cf6f3c5a..b572b6de0 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -65,34 +65,34 @@ func applyBlockToState(state *statepbtypes.State, block *blockchainpbtypes.Block } } -func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain, chain *blockchainpbtypes.Blockchain, checkpointState *statepbtypes.State) error { +func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain, checkpointToForkRootChain []*blockchainpbtypes.Block, checkpointState *statepbtypes.State) error { am.logger.Log(logging.LevelInfo, "Processing fork update", "poolSize", am.tm.PoolSize()) // add "remove chain" transactions to pool - for _, block := range removedChain.Blocks { + for _, block := range removedChain { am.tm.AddPayload(block.Payload) } // remove "add chain" transactions from pool - for _, block := range addedChain.Blocks { + for _, block := range addedChain { am.tm.RemovePayload(block.Payload) } state := checkpointState // compute state at fork roo // skip first as checkpoint state already 'included' its payload - for _, block := range chain.Blocks[1:] { + for _, block := range checkpointToForkRootChain[1:] { state = applyBlockToState(state, block) } // compute state at new head - for _, block := range addedChain.Blocks { + for _, block := range addedChain { state = applyBlockToState(state, block) } am.logger.Log(logging.LevelInfo, "Pool after fork", "poolSize", am.tm.PoolSize()) // register checkpoint - blockId := addedChain.Blocks[len(addedChain.Blocks)-1].BlockId + blockId := addedChain[len(addedChain)-1].BlockId bcmpbdsl.RegisterCheckpoint(*am.m, "bcm", blockId, state) interceptorpbdsl.AppUpdate(*am.m, "devnull", state) diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 957dad1e8..64c23945f 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -81,13 +81,13 @@ func (bcm *bcmModule) handleNewHead(newHead, oldHead *bcmBlock) { bcm.logger.Log(logging.LevelError, "Failed to get fork parent - this should not happen", "blockId", utils.FormatBlockId(addChain[0].BlockId)) panic(err) } - chain, checkpointState := bcm.getChainFromCheckpointToBlock(forkRoot) + checkpointToForkRootChain, checkpointState := bcm.getChainFromCheckpointToBlock(forkRoot) - applicationpbdsl.ForkUpdate(*bcm.m, "application", &blockchainpbtypes.Blockchain{ - Blocks: removeChain[1:], - }, &blockchainpbtypes.Blockchain{ - Blocks: addChain[1:], - }, &blockchainpbtypes.Blockchain{Blocks: chain}, checkpointState) + applicationpbdsl.ForkUpdate(*bcm.m, "application", + removeChain[1:], + addChain[1:], + checkpointToForkRootChain, + checkpointState) minerpbdsl.NewHead(*bcm.m, "miner", newHead.block.BlockId) } @@ -487,7 +487,6 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { head: nil, // will be initialized in handleInitBlockchain, triggered by application genesis: nil, // will be initialized in handleInitBlockchain, triggered by application checkpoints: make(map[uint64]*bcmBlock), - blockCount: 1, currentScanCount: 0, logger: logger, initialized: false, From b05f30db6977264d1cc08d8b193477fa2eca39c6 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Wed, 31 Jan 2024 01:10:47 +0100 Subject: [PATCH 19/46] some cleanup --- .../interceptorpb/dsl/emit.mir.go | 12 +- .../interceptorpb/dsl/upon.mir.go | 14 +- .../interceptorpb/events/events.mir.go | 25 +-- .../interceptorpb/interceptorpb.pb.go | 205 +++++------------- .../interceptorpb/interceptorpb.pb.mir.go | 3 +- .../interceptorpb/oneof_interfaces.mir.go | 8 +- .../interceptorpb/types/types.mir.go | 117 +++------- .../interceptorpb/interceptorpb.proto | 17 +- samples/blockchain/application/application.go | 2 +- samples/blockchain/bcm.go | 16 +- .../blockchain/wsinterceptor/wsinterceptor.go | 2 +- 11 files changed, 117 insertions(+), 304 deletions(-) diff --git a/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go index 5add921d0..e5531e0ec 100644 --- a/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/dsl/emit.mir.go @@ -12,14 +12,10 @@ import ( // Module-specific dsl functions for emitting events. -func TreeUpdate(m dsl.Module, destModule types.ModuleID, tree *types1.Blocktree, headId uint64) { - dsl.EmitMirEvent(m, events.TreeUpdate(destModule, tree, headId)) +func TreeUpdate(m dsl.Module, destModule types.ModuleID, blocks []*types1.Block, headId uint64) { + dsl.EmitMirEvent(m, events.TreeUpdate(destModule, blocks, headId)) } -func NewOrphan(m dsl.Module, destModule types.ModuleID, orphan *types1.Block) { - dsl.EmitMirEvent(m, events.NewOrphan(destModule, orphan)) -} - -func AppUpdate(m dsl.Module, destModule types.ModuleID, state *types2.State) { - dsl.EmitMirEvent(m, events.AppUpdate(destModule, state)) +func StateUpdate(m dsl.Module, destModule types.ModuleID, state *types2.State) { + dsl.EmitMirEvent(m, events.StateUpdate(destModule, state)) } diff --git a/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go index 787b81d4e..32f0e598b 100644 --- a/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/dsl/upon.mir.go @@ -23,20 +23,14 @@ func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func }) } -func UponTreeUpdate(m dsl.Module, handler func(tree *types2.Blocktree, headId uint64) error) { +func UponTreeUpdate(m dsl.Module, handler func(blocks []*types2.Block, headId uint64) error) { UponEvent[*types.Event_TreeUpdate](m, func(ev *types.TreeUpdate) error { - return handler(ev.Tree, ev.HeadId) + return handler(ev.Blocks, ev.HeadId) }) } -func UponNewOrphan(m dsl.Module, handler func(orphan *types2.Block) error) { - UponEvent[*types.Event_NewOrphan](m, func(ev *types.NewOrphan) error { - return handler(ev.Orphan) - }) -} - -func UponAppUpdate(m dsl.Module, handler func(state *types3.State) error) { - UponEvent[*types.Event_AppUpdate](m, func(ev *types.AppUpdate) error { +func UponStateUpdate(m dsl.Module, handler func(state *types3.State) error) { + UponEvent[*types.Event_StateUpdate](m, func(ev *types.StateUpdate) error { return handler(ev.State) }) } diff --git a/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go b/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go index 32b8ded15..3a3b5f357 100644 --- a/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/events/events.mir.go @@ -10,14 +10,14 @@ import ( types "github.com/filecoin-project/mir/pkg/types" ) -func TreeUpdate(destModule types.ModuleID, tree *types1.Blocktree, headId uint64) *types2.Event { +func TreeUpdate(destModule types.ModuleID, blocks []*types1.Block, headId uint64) *types2.Event { return &types2.Event{ DestModule: destModule, Type: &types2.Event_Bcinterceptor{ Bcinterceptor: &types3.Event{ Type: &types3.Event_TreeUpdate{ TreeUpdate: &types3.TreeUpdate{ - Tree: tree, + Blocks: blocks, HeadId: headId, }, }, @@ -26,28 +26,13 @@ func TreeUpdate(destModule types.ModuleID, tree *types1.Blocktree, headId uint64 } } -func NewOrphan(destModule types.ModuleID, orphan *types1.Block) *types2.Event { +func StateUpdate(destModule types.ModuleID, state *types4.State) *types2.Event { return &types2.Event{ DestModule: destModule, Type: &types2.Event_Bcinterceptor{ Bcinterceptor: &types3.Event{ - Type: &types3.Event_NewOrphan{ - NewOrphan: &types3.NewOrphan{ - Orphan: orphan, - }, - }, - }, - }, - } -} - -func AppUpdate(destModule types.ModuleID, state *types4.State) *types2.Event { - return &types2.Event{ - DestModule: destModule, - Type: &types2.Event_Bcinterceptor{ - Bcinterceptor: &types3.Event{ - Type: &types3.Event_AppUpdate{ - AppUpdate: &types3.AppUpdate{ + Type: &types3.Event_StateUpdate{ + StateUpdate: &types3.StateUpdate{ State: state, }, }, diff --git a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go index 5c11ae09d..4fa5c54ce 100644 --- a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go +++ b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go @@ -31,8 +31,7 @@ type Event struct { // Types that are assignable to Type: // // *Event_TreeUpdate - // *Event_NewOrphan - // *Event_AppUpdate + // *Event_StateUpdate Type isEvent_Type `protobuf_oneof:"type"` } @@ -82,16 +81,9 @@ func (x *Event) GetTreeUpdate() *TreeUpdate { return nil } -func (x *Event) GetNewOrphan() *NewOrphan { - if x, ok := x.GetType().(*Event_NewOrphan); ok { - return x.NewOrphan - } - return nil -} - -func (x *Event) GetAppUpdate() *AppUpdate { - if x, ok := x.GetType().(*Event_AppUpdate); ok { - return x.AppUpdate +func (x *Event) GetStateUpdate() *StateUpdate { + if x, ok := x.GetType().(*Event_StateUpdate); ok { + return x.StateUpdate } return nil } @@ -104,27 +96,21 @@ type Event_TreeUpdate struct { TreeUpdate *TreeUpdate `protobuf:"bytes,1,opt,name=tree_update,json=treeUpdate,proto3,oneof"` } -type Event_NewOrphan struct { - NewOrphan *NewOrphan `protobuf:"bytes,2,opt,name=new_orphan,json=newOrphan,proto3,oneof"` -} - -type Event_AppUpdate struct { - AppUpdate *AppUpdate `protobuf:"bytes,3,opt,name=app_update,json=appUpdate,proto3,oneof"` +type Event_StateUpdate struct { + StateUpdate *StateUpdate `protobuf:"bytes,2,opt,name=state_update,json=stateUpdate,proto3,oneof"` } func (*Event_TreeUpdate) isEvent_Type() {} -func (*Event_NewOrphan) isEvent_Type() {} - -func (*Event_AppUpdate) isEvent_Type() {} +func (*Event_StateUpdate) isEvent_Type() {} type TreeUpdate struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Tree *blockchainpb.Blocktree `protobuf:"bytes,1,opt,name=tree,proto3" json:"tree,omitempty"` - HeadId uint64 `protobuf:"varint,2,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` + Blocks []*blockchainpb.Block `protobuf:"bytes,1,rep,name=blocks,proto3" json:"blocks,omitempty"` + HeadId uint64 `protobuf:"varint,2,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` } func (x *TreeUpdate) Reset() { @@ -159,9 +145,9 @@ func (*TreeUpdate) Descriptor() ([]byte, []int) { return file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescGZIP(), []int{1} } -func (x *TreeUpdate) GetTree() *blockchainpb.Blocktree { +func (x *TreeUpdate) GetBlocks() []*blockchainpb.Block { if x != nil { - return x.Tree + return x.Blocks } return nil } @@ -173,16 +159,16 @@ func (x *TreeUpdate) GetHeadId() uint64 { return 0 } -type NewOrphan struct { +type StateUpdate struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Orphan *blockchainpb.Block `protobuf:"bytes,1,opt,name=orphan,proto3" json:"orphan,omitempty"` + State *statepb.State `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` } -func (x *NewOrphan) Reset() { - *x = NewOrphan{} +func (x *StateUpdate) Reset() { + *x = StateUpdate{} if protoimpl.UnsafeEnabled { mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -190,13 +176,13 @@ func (x *NewOrphan) Reset() { } } -func (x *NewOrphan) String() string { +func (x *StateUpdate) String() string { return protoimpl.X.MessageStringOf(x) } -func (*NewOrphan) ProtoMessage() {} +func (*StateUpdate) ProtoMessage() {} -func (x *NewOrphan) ProtoReflect() protoreflect.Message { +func (x *StateUpdate) ProtoReflect() protoreflect.Message { mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -208,59 +194,12 @@ func (x *NewOrphan) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use NewOrphan.ProtoReflect.Descriptor instead. -func (*NewOrphan) Descriptor() ([]byte, []int) { +// Deprecated: Use StateUpdate.ProtoReflect.Descriptor instead. +func (*StateUpdate) Descriptor() ([]byte, []int) { return file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescGZIP(), []int{2} } -func (x *NewOrphan) GetOrphan() *blockchainpb.Block { - if x != nil { - return x.Orphan - } - return nil -} - -type AppUpdate struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - State *statepb.State `protobuf:"bytes,1,opt,name=state,proto3" json:"state,omitempty"` -} - -func (x *AppUpdate) Reset() { - *x = AppUpdate{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *AppUpdate) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*AppUpdate) ProtoMessage() {} - -func (x *AppUpdate) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[3] - 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 AppUpdate.ProtoReflect.Descriptor instead. -func (*AppUpdate) Descriptor() ([]byte, []int) { - return file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescGZIP(), []int{3} -} - -func (x *AppUpdate) GetState() *statepb.State { +func (x *StateUpdate) GetState() *statepb.State { if x != nil { return x.State } @@ -280,38 +219,31 @@ var file_blockchainpb_interceptorpb_interceptorpb_proto_rawDesc = []byte{ 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0xcf, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0b, + 0x74, 0x6f, 0x22, 0x9a, 0x01, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x3c, 0x0a, 0x0b, 0x74, 0x72, 0x65, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0a, - 0x74, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x0a, 0x6e, 0x65, - 0x77, 0x5f, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x4e, - 0x65, 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x48, 0x00, 0x52, 0x09, 0x6e, 0x65, 0x77, 0x4f, - 0x72, 0x70, 0x68, 0x61, 0x6e, 0x12, 0x39, 0x0a, 0x0a, 0x61, 0x70, 0x70, 0x5f, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x41, 0x70, 0x70, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x09, 0x61, 0x70, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, - 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x58, 0x0a, 0x0a, 0x54, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x12, 0x2b, 0x0a, 0x04, 0x74, 0x72, 0x65, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x17, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, 0x72, 0x65, 0x65, 0x52, 0x04, 0x74, 0x72, 0x65, 0x65, 0x12, - 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3e, - 0x0a, 0x09, 0x4e, 0x65, 0x77, 0x4f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x6f, - 0x72, 0x70, 0x68, 0x61, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x06, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x37, - 0x0a, 0x09, 0x41, 0x70, 0x70, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x05, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, - 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, - 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x3f, 0x0a, 0x0c, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1a, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0b, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x90, 0xa6, 0x1d, + 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, + 0x58, 0x0a, 0x0a, 0x54, 0x72, 0x65, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x2b, 0x0a, + 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, + 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, + 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, + 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x39, 0x0a, 0x0b, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, + 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, + 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( @@ -326,28 +258,24 @@ func file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescGZIP() []byte { return file_blockchainpb_interceptorpb_interceptorpb_proto_rawDescData } -var file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_blockchainpb_interceptorpb_interceptorpb_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: interceptorpb.Event - (*TreeUpdate)(nil), // 1: interceptorpb.TreeUpdate - (*NewOrphan)(nil), // 2: interceptorpb.NewOrphan - (*AppUpdate)(nil), // 3: interceptorpb.AppUpdate - (*blockchainpb.Blocktree)(nil), // 4: blockchainpb.Blocktree - (*blockchainpb.Block)(nil), // 5: blockchainpb.Block - (*statepb.State)(nil), // 6: statepb.State + (*Event)(nil), // 0: interceptorpb.Event + (*TreeUpdate)(nil), // 1: interceptorpb.TreeUpdate + (*StateUpdate)(nil), // 2: interceptorpb.StateUpdate + (*blockchainpb.Block)(nil), // 3: blockchainpb.Block + (*statepb.State)(nil), // 4: statepb.State } var file_blockchainpb_interceptorpb_interceptorpb_proto_depIdxs = []int32{ 1, // 0: interceptorpb.Event.tree_update:type_name -> interceptorpb.TreeUpdate - 2, // 1: interceptorpb.Event.new_orphan:type_name -> interceptorpb.NewOrphan - 3, // 2: interceptorpb.Event.app_update:type_name -> interceptorpb.AppUpdate - 4, // 3: interceptorpb.TreeUpdate.tree:type_name -> blockchainpb.Blocktree - 5, // 4: interceptorpb.NewOrphan.orphan:type_name -> blockchainpb.Block - 6, // 5: interceptorpb.AppUpdate.state:type_name -> statepb.State - 6, // [6:6] is the sub-list for method output_type - 6, // [6:6] is the sub-list for method input_type - 6, // [6:6] is the sub-list for extension type_name - 6, // [6:6] is the sub-list for extension extendee - 0, // [0:6] is the sub-list for field type_name + 2, // 1: interceptorpb.Event.state_update:type_name -> interceptorpb.StateUpdate + 3, // 2: interceptorpb.TreeUpdate.blocks:type_name -> blockchainpb.Block + 4, // 3: interceptorpb.StateUpdate.state:type_name -> statepb.State + 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_blockchainpb_interceptorpb_interceptorpb_proto_init() } @@ -381,19 +309,7 @@ func file_blockchainpb_interceptorpb_interceptorpb_proto_init() { } } file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NewOrphan); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*AppUpdate); i { + switch v := v.(*StateUpdate); i { case 0: return &v.state case 1: @@ -407,8 +323,7 @@ func file_blockchainpb_interceptorpb_interceptorpb_proto_init() { } file_blockchainpb_interceptorpb_interceptorpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_TreeUpdate)(nil), - (*Event_NewOrphan)(nil), - (*Event_AppUpdate)(nil), + (*Event_StateUpdate)(nil), } type x struct{} out := protoimpl.TypeBuilder{ @@ -416,7 +331,7 @@ func file_blockchainpb_interceptorpb_interceptorpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_interceptorpb_interceptorpb_proto_rawDesc, NumEnums: 0, - NumMessages: 4, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go index a1da29437..dcf3cb82a 100644 --- a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.mir.go @@ -9,7 +9,6 @@ import ( func (*Event) ReflectTypeOptions() []reflect.Type { return []reflect.Type{ reflect.TypeOf((*Event_TreeUpdate)(nil)), - reflect.TypeOf((*Event_NewOrphan)(nil)), - reflect.TypeOf((*Event_AppUpdate)(nil)), + reflect.TypeOf((*Event_StateUpdate)(nil)), } } diff --git a/pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go index c3d7bb4ab..4a4417002 100644 --- a/pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/oneof_interfaces.mir.go @@ -13,10 +13,6 @@ func (w *Event_TreeUpdate) Unwrap() *TreeUpdate { return w.TreeUpdate } -func (w *Event_NewOrphan) Unwrap() *NewOrphan { - return w.NewOrphan -} - -func (w *Event_AppUpdate) Unwrap() *AppUpdate { - return w.AppUpdate +func (w *Event_StateUpdate) Unwrap() *StateUpdate { + return w.StateUpdate } diff --git a/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go b/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go index 39d50b292..8942f527e 100644 --- a/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/interceptorpb/types/types.mir.go @@ -4,8 +4,10 @@ package interceptorpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" + types1 "github.com/filecoin-project/mir/codegen/model/types" + blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" - types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -32,10 +34,8 @@ func Event_TypeFromPb(pb interceptorpb.Event_Type) Event_Type { switch pb := pb.(type) { case *interceptorpb.Event_TreeUpdate: return &Event_TreeUpdate{TreeUpdate: TreeUpdateFromPb(pb.TreeUpdate)} - case *interceptorpb.Event_NewOrphan: - return &Event_NewOrphan{NewOrphan: NewOrphanFromPb(pb.NewOrphan)} - case *interceptorpb.Event_AppUpdate: - return &Event_AppUpdate{AppUpdate: AppUpdateFromPb(pb.AppUpdate)} + case *interceptorpb.Event_StateUpdate: + return &Event_StateUpdate{StateUpdate: StateUpdateFromPb(pb.StateUpdate)} } return nil } @@ -64,52 +64,28 @@ func (*Event_TreeUpdate) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.Event_TreeUpdate]()} } -type Event_NewOrphan struct { - NewOrphan *NewOrphan +type Event_StateUpdate struct { + StateUpdate *StateUpdate } -func (*Event_NewOrphan) isEvent_Type() {} +func (*Event_StateUpdate) isEvent_Type() {} -func (w *Event_NewOrphan) Unwrap() *NewOrphan { - return w.NewOrphan +func (w *Event_StateUpdate) Unwrap() *StateUpdate { + return w.StateUpdate } -func (w *Event_NewOrphan) Pb() interceptorpb.Event_Type { +func (w *Event_StateUpdate) Pb() interceptorpb.Event_Type { if w == nil { return nil } - if w.NewOrphan == nil { - return &interceptorpb.Event_NewOrphan{} + if w.StateUpdate == nil { + return &interceptorpb.Event_StateUpdate{} } - return &interceptorpb.Event_NewOrphan{NewOrphan: (w.NewOrphan).Pb()} + return &interceptorpb.Event_StateUpdate{StateUpdate: (w.StateUpdate).Pb()} } -func (*Event_NewOrphan) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.Event_NewOrphan]()} -} - -type Event_AppUpdate struct { - AppUpdate *AppUpdate -} - -func (*Event_AppUpdate) isEvent_Type() {} - -func (w *Event_AppUpdate) Unwrap() *AppUpdate { - return w.AppUpdate -} - -func (w *Event_AppUpdate) Pb() interceptorpb.Event_Type { - if w == nil { - return nil - } - if w.AppUpdate == nil { - return &interceptorpb.Event_AppUpdate{} - } - return &interceptorpb.Event_AppUpdate{AppUpdate: (w.AppUpdate).Pb()} -} - -func (*Event_AppUpdate) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.Event_AppUpdate]()} +func (*Event_StateUpdate) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.Event_StateUpdate]()} } func EventFromPb(pb *interceptorpb.Event) *Event { @@ -140,7 +116,7 @@ func (*Event) MirReflect() mirreflect.Type { } type TreeUpdate struct { - Tree *types.Blocktree + Blocks []*types.Block HeadId uint64 } @@ -149,7 +125,9 @@ func TreeUpdateFromPb(pb *interceptorpb.TreeUpdate) *TreeUpdate { return nil } return &TreeUpdate{ - Tree: types.BlocktreeFromPb(pb.Tree), + Blocks: types1.ConvertSlice(pb.Blocks, func(t *blockchainpb.Block) *types.Block { + return types.BlockFromPb(t) + }), HeadId: pb.HeadId, } } @@ -160,9 +138,9 @@ func (m *TreeUpdate) Pb() *interceptorpb.TreeUpdate { } pbMessage := &interceptorpb.TreeUpdate{} { - if m.Tree != nil { - pbMessage.Tree = (m.Tree).Pb() - } + pbMessage.Blocks = types1.ConvertSlice(m.Blocks, func(t *types.Block) *blockchainpb.Block { + return (t).Pb() + }) pbMessage.HeadId = m.HeadId } @@ -173,55 +151,24 @@ func (*TreeUpdate) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.TreeUpdate]()} } -type NewOrphan struct { - Orphan *types.Block -} - -func NewOrphanFromPb(pb *interceptorpb.NewOrphan) *NewOrphan { - if pb == nil { - return nil - } - return &NewOrphan{ - Orphan: types.BlockFromPb(pb.Orphan), - } -} - -func (m *NewOrphan) Pb() *interceptorpb.NewOrphan { - if m == nil { - return nil - } - pbMessage := &interceptorpb.NewOrphan{} - { - if m.Orphan != nil { - pbMessage.Orphan = (m.Orphan).Pb() - } - } - - return pbMessage -} - -func (*NewOrphan) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.NewOrphan]()} -} - -type AppUpdate struct { - State *types1.State +type StateUpdate struct { + State *types2.State } -func AppUpdateFromPb(pb *interceptorpb.AppUpdate) *AppUpdate { +func StateUpdateFromPb(pb *interceptorpb.StateUpdate) *StateUpdate { if pb == nil { return nil } - return &AppUpdate{ - State: types1.StateFromPb(pb.State), + return &StateUpdate{ + State: types2.StateFromPb(pb.State), } } -func (m *AppUpdate) Pb() *interceptorpb.AppUpdate { +func (m *StateUpdate) Pb() *interceptorpb.StateUpdate { if m == nil { return nil } - pbMessage := &interceptorpb.AppUpdate{} + pbMessage := &interceptorpb.StateUpdate{} { if m.State != nil { pbMessage.State = (m.State).Pb() @@ -231,6 +178,6 @@ func (m *AppUpdate) Pb() *interceptorpb.AppUpdate { return pbMessage } -func (*AppUpdate) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.AppUpdate]()} +func (*StateUpdate) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*interceptorpb.StateUpdate]()} } diff --git a/protos/blockchainpb/interceptorpb/interceptorpb.proto b/protos/blockchainpb/interceptorpb/interceptorpb.proto index 9e9ed37b3..e5f963c83 100644 --- a/protos/blockchainpb/interceptorpb/interceptorpb.proto +++ b/protos/blockchainpb/interceptorpb/interceptorpb.proto @@ -14,26 +14,19 @@ message Event { oneof type { option (mir.event_type) = true; - TreeUpdate tree_update = 1; - NewOrphan new_orphan = 2; - AppUpdate app_update = 3; + TreeUpdate tree_update = 1; + StateUpdate state_update = 2; } } message TreeUpdate { option (mir.event) = true; - blockchainpb.Blocktree tree = 1; - uint64 head_id = 2; + repeated blockchainpb.Block blocks = 1; + uint64 head_id = 2; } -message NewOrphan { - option (mir.event) = true; - - blockchainpb.Block orphan = 1; -} - -message AppUpdate { +message StateUpdate { option (mir.event) = true; statepb.State state = 1; diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index b572b6de0..523116af7 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -94,7 +94,7 @@ func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain, checkpoi // register checkpoint blockId := addedChain[len(addedChain)-1].BlockId bcmpbdsl.RegisterCheckpoint(*am.m, "bcm", blockId, state) - interceptorpbdsl.AppUpdate(*am.m, "devnull", state) + interceptorpbdsl.StateUpdate(*am.m, "devnull", state) // print state fmt.Printf("=== STATE ===\n") diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 64c23945f..aea85fef4 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -48,7 +48,6 @@ type bcmModule struct { head *bcmBlock genesis *bcmBlock checkpoints map[uint64]*bcmBlock - blockCount uint64 currentScanCount uint64 logger logging.Logger initialized bool @@ -239,7 +238,6 @@ func (bcm *bcmModule) addBlock(block *blockchainpbtypes.Block) error { if blockNode.depth > bcm.head.depth { // update head only if new block is deeper, i.e. is the longest chain bcm.head = &blockNode } - bcm.blockCount++ return nil } @@ -265,7 +263,6 @@ func (bcm *bcmModule) addBlock(block *blockchainpbtypes.Block) error { if blockNode.depth > bcm.head.depth { // update head only if new block is deeper, i.e. is the longest chain bcm.head = &blockNode } - bcm.blockCount++ return true, nil } @@ -360,17 +357,8 @@ func (bcm *bcmModule) sendTreeUpdate() { } return blocks }() - leaves := func() []uint64 { - leaves := make([]uint64, 0, len(bcm.leaves)) - for _, v := range bcm.leaves { - leaves = append(leaves, v.block.BlockId) - } - return leaves - }() - - blockTree := blockchainpbtypes.Blocktree{Blocks: blocks, Leaves: leaves} - interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", &blockTree, bcm.head.block.BlockId) + interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", blocks, bcm.head.block.BlockId) } func (bcm *bcmModule) getChainFromCheckpointToBlock(block *bcmBlock) ([]*blockchainpbtypes.Block, *statepbtypes.State) { @@ -405,7 +393,7 @@ func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, so return nil } else if checkpointState == nil { bcm.logger.Log(logging.LevelError, "Linked head up with checkpoint but state is missing - this should not happen", "head", utils.FormatBlockId(bcm.head.block.BlockId)) - panic(errors.New("Linked head up with checkpoint but state is missing - this should not happen")) + panic(errors.New("linked head up with checkpoint but state is missing - this should not happen")) } bcmpbdsl.GetHeadToCheckpointChainResponse(*bcm.m, sourceModule, requestID, chain, checkpointState) diff --git a/samples/blockchain/wsinterceptor/wsinterceptor.go b/samples/blockchain/wsinterceptor/wsinterceptor.go index d68be0142..f82e38ee7 100644 --- a/samples/blockchain/wsinterceptor/wsinterceptor.go +++ b/samples/blockchain/wsinterceptor/wsinterceptor.go @@ -1,4 +1,4 @@ -package wsinterceptor +package wsInterceptor import ( "github.com/filecoin-project/mir/pkg/events" From 3fc28e44fcf91d7bb7d668e2b26f7b56c10316bb Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Wed, 31 Jan 2024 10:18:07 +0100 Subject: [PATCH 20/46] drop get block request --- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go | 515 +++++------------- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go | 2 - pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go | 8 - pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go | 12 - .../blockchainpb/bcmpb/events/events.mir.go | 34 -- .../bcmpb/oneof_interfaces.mir.go | 8 - pkg/pb/blockchainpb/bcmpb/types/types.mir.go | 124 ----- protos/blockchainpb/bcmpb/bcmpb.proto | 19 - samples/blockchain/bcm.go | 40 +- 9 files changed, 171 insertions(+), 591 deletions(-) diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go index 0959b7797..e61e6c615 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go @@ -32,8 +32,6 @@ type Event struct { // // *Event_NewBlock // *Event_NewChain - // *Event_GetBlockRequest - // *Event_GetBlockResponse // *Event_GetChainRequest // *Event_GetChainResponse // *Event_GetHeadToCheckpointChainRequest @@ -96,20 +94,6 @@ func (x *Event) GetNewChain() *NewChain { return nil } -func (x *Event) GetGetBlockRequest() *GetBlockRequest { - if x, ok := x.GetType().(*Event_GetBlockRequest); ok { - return x.GetBlockRequest - } - return nil -} - -func (x *Event) GetGetBlockResponse() *GetBlockResponse { - if x, ok := x.GetType().(*Event_GetBlockResponse); ok { - return x.GetBlockResponse - } - return nil -} - func (x *Event) GetGetChainRequest() *GetChainRequest { if x, ok := x.GetType().(*Event_GetChainRequest); ok { return x.GetChainRequest @@ -164,14 +148,6 @@ type Event_NewChain struct { NewChain *NewChain `protobuf:"bytes,2,opt,name=new_chain,json=newChain,proto3,oneof"` } -type Event_GetBlockRequest struct { - GetBlockRequest *GetBlockRequest `protobuf:"bytes,3,opt,name=get_block_request,json=getBlockRequest,proto3,oneof"` -} - -type Event_GetBlockResponse struct { - GetBlockResponse *GetBlockResponse `protobuf:"bytes,4,opt,name=get_block_response,json=getBlockResponse,proto3,oneof"` -} - type Event_GetChainRequest struct { GetChainRequest *GetChainRequest `protobuf:"bytes,5,opt,name=get_chain_request,json=getChainRequest,proto3,oneof"` } @@ -200,10 +176,6 @@ func (*Event_NewBlock) isEvent_Type() {} func (*Event_NewChain) isEvent_Type() {} -func (*Event_GetBlockRequest) isEvent_Type() {} - -func (*Event_GetBlockResponse) isEvent_Type() {} - func (*Event_GetChainRequest) isEvent_Type() {} func (*Event_GetChainResponse) isEvent_Type() {} @@ -311,132 +283,6 @@ func (x *NewChain) GetBlocks() []*blockchainpb.Block { return nil } -type GetBlockRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - SourceModule string `protobuf:"bytes,2,opt,name=source_module,json=sourceModule,proto3" json:"source_module,omitempty"` - BlockId uint64 `protobuf:"varint,3,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` -} - -func (x *GetBlockRequest) Reset() { - *x = GetBlockRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[3] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetBlockRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetBlockRequest) ProtoMessage() {} - -func (x *GetBlockRequest) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[3] - 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 GetBlockRequest.ProtoReflect.Descriptor instead. -func (*GetBlockRequest) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{3} -} - -func (x *GetBlockRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -func (x *GetBlockRequest) GetSourceModule() string { - if x != nil { - return x.SourceModule - } - return "" -} - -func (x *GetBlockRequest) GetBlockId() uint64 { - if x != nil { - return x.BlockId - } - return 0 -} - -type GetBlockResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - Found bool `protobuf:"varint,2,opt,name=found,proto3" json:"found,omitempty"` - Block *blockchainpb.Block `protobuf:"bytes,3,opt,name=block,proto3" json:"block,omitempty"` -} - -func (x *GetBlockResponse) Reset() { - *x = GetBlockResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[4] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetBlockResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetBlockResponse) ProtoMessage() {} - -func (x *GetBlockResponse) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_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 GetBlockResponse.ProtoReflect.Descriptor instead. -func (*GetBlockResponse) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{4} -} - -func (x *GetBlockResponse) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -func (x *GetBlockResponse) GetFound() bool { - if x != nil { - return x.Found - } - return false -} - -func (x *GetBlockResponse) GetBlock() *blockchainpb.Block { - if x != nil { - return x.Block - } - return nil -} - type GetChainRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -451,7 +297,7 @@ type GetChainRequest struct { func (x *GetChainRequest) Reset() { *x = GetChainRequest{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -464,7 +310,7 @@ func (x *GetChainRequest) String() string { func (*GetChainRequest) ProtoMessage() {} func (x *GetChainRequest) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -477,7 +323,7 @@ func (x *GetChainRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetChainRequest.ProtoReflect.Descriptor instead. func (*GetChainRequest) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{5} + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{3} } func (x *GetChainRequest) GetRequestId() string { @@ -521,7 +367,7 @@ type GetChainResponse struct { func (x *GetChainResponse) Reset() { *x = GetChainResponse{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -534,7 +380,7 @@ func (x *GetChainResponse) String() string { func (*GetChainResponse) ProtoMessage() {} func (x *GetChainResponse) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -547,7 +393,7 @@ func (x *GetChainResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetChainResponse.ProtoReflect.Descriptor instead. func (*GetChainResponse) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{6} + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{4} } func (x *GetChainResponse) GetRequestId() string { @@ -583,7 +429,7 @@ type GetHeadToCheckpointChainRequest struct { func (x *GetHeadToCheckpointChainRequest) Reset() { *x = GetHeadToCheckpointChainRequest{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -596,7 +442,7 @@ func (x *GetHeadToCheckpointChainRequest) String() string { func (*GetHeadToCheckpointChainRequest) ProtoMessage() {} func (x *GetHeadToCheckpointChainRequest) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -609,7 +455,7 @@ func (x *GetHeadToCheckpointChainRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetHeadToCheckpointChainRequest.ProtoReflect.Descriptor instead. func (*GetHeadToCheckpointChainRequest) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{7} + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{5} } func (x *GetHeadToCheckpointChainRequest) GetRequestId() string { @@ -639,7 +485,7 @@ type GetHeadToCheckpointChainResponse struct { func (x *GetHeadToCheckpointChainResponse) Reset() { *x = GetHeadToCheckpointChainResponse{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -652,7 +498,7 @@ func (x *GetHeadToCheckpointChainResponse) String() string { func (*GetHeadToCheckpointChainResponse) ProtoMessage() {} func (x *GetHeadToCheckpointChainResponse) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -665,7 +511,7 @@ func (x *GetHeadToCheckpointChainResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetHeadToCheckpointChainResponse.ProtoReflect.Descriptor instead. func (*GetHeadToCheckpointChainResponse) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{8} + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{6} } func (x *GetHeadToCheckpointChainResponse) GetRequestId() string { @@ -701,7 +547,7 @@ type RegisterCheckpoint struct { func (x *RegisterCheckpoint) Reset() { *x = RegisterCheckpoint{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[9] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -714,7 +560,7 @@ func (x *RegisterCheckpoint) String() string { func (*RegisterCheckpoint) ProtoMessage() {} func (x *RegisterCheckpoint) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[9] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -727,7 +573,7 @@ func (x *RegisterCheckpoint) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterCheckpoint.ProtoReflect.Descriptor instead. func (*RegisterCheckpoint) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{9} + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{7} } func (x *RegisterCheckpoint) GetBlockId() uint64 { @@ -755,7 +601,7 @@ type InitBlockchain struct { func (x *InitBlockchain) Reset() { *x = InitBlockchain{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[10] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -768,7 +614,7 @@ func (x *InitBlockchain) String() string { func (*InitBlockchain) ProtoMessage() {} func (x *InitBlockchain) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[10] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -781,7 +627,7 @@ func (x *InitBlockchain) ProtoReflect() protoreflect.Message { // Deprecated: Use InitBlockchain.ProtoReflect.Descriptor instead. func (*InitBlockchain) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{10} + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{8} } func (x *InitBlockchain) GetInitialState() *statepb.State { @@ -802,142 +648,114 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9e, 0x06, 0x0a, 0x05, 0x45, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8f, 0x05, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x12, 0x44, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x16, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x67, 0x65, 0x74, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x12, 0x67, 0x65, - 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, - 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, - 0x00, 0x52, 0x10, 0x67, 0x65, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x44, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, - 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x12, 0x67, 0x65, 0x74, - 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, - 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, - 0x52, 0x10, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x77, 0x0a, 0x24, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, - 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x26, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, - 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x1f, 0x67, 0x65, 0x74, 0x48, - 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x7a, 0x0a, 0x25, 0x67, - 0x65, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x62, 0x63, 0x6d, - 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x20, 0x67, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x13, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x09, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x48, - 0x00, 0x52, 0x12, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, - 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, - 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xae, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, - 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, - 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x78, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, + 0x68, 0x61, 0x69, 0x6e, 0x12, 0x44, 0x0a, 0x11, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x16, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0f, 0x67, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x47, 0x0a, 0x12, 0x67, 0x65, + 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, + 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, + 0x00, 0x52, 0x10, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x24, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, + 0x74, 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x26, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, + 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, + 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x1f, 0x67, 0x65, 0x74, + 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x7a, 0x0a, 0x25, + 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, + 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x62, 0x63, + 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, + 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x20, 0x67, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, + 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x13, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, + 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x52, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x48, 0x00, 0x52, 0x12, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x15, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, + 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, + 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, - 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, - 0x64, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, - 0x1d, 0x01, 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, - 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, - 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, - 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, - 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, - 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x73, 0x3a, 0x04, - 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, - 0x73, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, - 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x98, 0xa6, - 0x1d, 0x01, 0x22, 0xa3, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, - 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, - 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, - 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, - 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xad, 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, + 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x6e, 0x64, 0x5f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, + 0x65, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, + 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x49, 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x47, 0x65, + 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, + 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, + 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, + 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xa3, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, - 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x29, 0x0a, 0x05, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x39, 0x0a, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5b, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, - 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, - 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, - 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x4b, 0x0a, 0x0e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x0d, 0x69, 0x6e, 0x69, 0x74, 0x69, - 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, - 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, - 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, - 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, + 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, + 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, + 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xad, + 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, + 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x49, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x39, 0x0a, + 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, + 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5b, + 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, + 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x4b, 0x0a, 0x0e, 0x49, + 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x33, 0x0a, + 0x0d, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, + 0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, + 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, + 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -952,46 +770,41 @@ func file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP() []byte { return file_blockchainpb_bcmpb_bcmpb_proto_rawDescData } -var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 11) +var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_blockchainpb_bcmpb_bcmpb_proto_goTypes = []interface{}{ (*Event)(nil), // 0: bcmpb.Event (*NewBlock)(nil), // 1: bcmpb.NewBlock (*NewChain)(nil), // 2: bcmpb.NewChain - (*GetBlockRequest)(nil), // 3: bcmpb.GetBlockRequest - (*GetBlockResponse)(nil), // 4: bcmpb.GetBlockResponse - (*GetChainRequest)(nil), // 5: bcmpb.GetChainRequest - (*GetChainResponse)(nil), // 6: bcmpb.GetChainResponse - (*GetHeadToCheckpointChainRequest)(nil), // 7: bcmpb.GetHeadToCheckpointChainRequest - (*GetHeadToCheckpointChainResponse)(nil), // 8: bcmpb.GetHeadToCheckpointChainResponse - (*RegisterCheckpoint)(nil), // 9: bcmpb.RegisterCheckpoint - (*InitBlockchain)(nil), // 10: bcmpb.InitBlockchain - (*blockchainpb.Block)(nil), // 11: blockchainpb.Block - (*statepb.State)(nil), // 12: statepb.State + (*GetChainRequest)(nil), // 3: bcmpb.GetChainRequest + (*GetChainResponse)(nil), // 4: bcmpb.GetChainResponse + (*GetHeadToCheckpointChainRequest)(nil), // 5: bcmpb.GetHeadToCheckpointChainRequest + (*GetHeadToCheckpointChainResponse)(nil), // 6: bcmpb.GetHeadToCheckpointChainResponse + (*RegisterCheckpoint)(nil), // 7: bcmpb.RegisterCheckpoint + (*InitBlockchain)(nil), // 8: bcmpb.InitBlockchain + (*blockchainpb.Block)(nil), // 9: blockchainpb.Block + (*statepb.State)(nil), // 10: statepb.State } var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ 1, // 0: bcmpb.Event.new_block:type_name -> bcmpb.NewBlock 2, // 1: bcmpb.Event.new_chain:type_name -> bcmpb.NewChain - 3, // 2: bcmpb.Event.get_block_request:type_name -> bcmpb.GetBlockRequest - 4, // 3: bcmpb.Event.get_block_response:type_name -> bcmpb.GetBlockResponse - 5, // 4: bcmpb.Event.get_chain_request:type_name -> bcmpb.GetChainRequest - 6, // 5: bcmpb.Event.get_chain_response:type_name -> bcmpb.GetChainResponse - 7, // 6: bcmpb.Event.get_head_to_checkpoint_chain_request:type_name -> bcmpb.GetHeadToCheckpointChainRequest - 8, // 7: bcmpb.Event.get_head_to_checkpoint_chain_response:type_name -> bcmpb.GetHeadToCheckpointChainResponse - 9, // 8: bcmpb.Event.register_checkpoint:type_name -> bcmpb.RegisterCheckpoint - 10, // 9: bcmpb.Event.init_blockchain:type_name -> bcmpb.InitBlockchain - 11, // 10: bcmpb.NewBlock.block:type_name -> blockchainpb.Block - 11, // 11: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block - 11, // 12: bcmpb.GetBlockResponse.block:type_name -> blockchainpb.Block - 11, // 13: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block - 11, // 14: bcmpb.GetHeadToCheckpointChainResponse.chain:type_name -> blockchainpb.Block - 12, // 15: bcmpb.GetHeadToCheckpointChainResponse.checkpoint_state:type_name -> statepb.State - 12, // 16: bcmpb.RegisterCheckpoint.state:type_name -> statepb.State - 12, // 17: bcmpb.InitBlockchain.initial_state:type_name -> statepb.State - 18, // [18:18] is the sub-list for method output_type - 18, // [18:18] is the sub-list for method input_type - 18, // [18:18] is the sub-list for extension type_name - 18, // [18:18] is the sub-list for extension extendee - 0, // [0:18] is the sub-list for field type_name + 3, // 2: bcmpb.Event.get_chain_request:type_name -> bcmpb.GetChainRequest + 4, // 3: bcmpb.Event.get_chain_response:type_name -> bcmpb.GetChainResponse + 5, // 4: bcmpb.Event.get_head_to_checkpoint_chain_request:type_name -> bcmpb.GetHeadToCheckpointChainRequest + 6, // 5: bcmpb.Event.get_head_to_checkpoint_chain_response:type_name -> bcmpb.GetHeadToCheckpointChainResponse + 7, // 6: bcmpb.Event.register_checkpoint:type_name -> bcmpb.RegisterCheckpoint + 8, // 7: bcmpb.Event.init_blockchain:type_name -> bcmpb.InitBlockchain + 9, // 8: bcmpb.NewBlock.block:type_name -> blockchainpb.Block + 9, // 9: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block + 9, // 10: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block + 9, // 11: bcmpb.GetHeadToCheckpointChainResponse.chain:type_name -> blockchainpb.Block + 10, // 12: bcmpb.GetHeadToCheckpointChainResponse.checkpoint_state:type_name -> statepb.State + 10, // 13: bcmpb.RegisterCheckpoint.state:type_name -> statepb.State + 10, // 14: bcmpb.InitBlockchain.initial_state:type_name -> statepb.State + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_blockchainpb_bcmpb_bcmpb_proto_init() } @@ -1037,30 +850,6 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { } } file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBlockRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetBlockResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetChainRequest); i { case 0: return &v.state @@ -1072,7 +861,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { return nil } } - file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetChainResponse); i { case 0: return &v.state @@ -1084,7 +873,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { return nil } } - file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetHeadToCheckpointChainRequest); i { case 0: return &v.state @@ -1096,7 +885,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { return nil } } - file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*GetHeadToCheckpointChainResponse); i { case 0: return &v.state @@ -1108,7 +897,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { return nil } } - file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RegisterCheckpoint); i { case 0: return &v.state @@ -1120,7 +909,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { return nil } } - file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InitBlockchain); i { case 0: return &v.state @@ -1136,8 +925,6 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_NewBlock)(nil), (*Event_NewChain)(nil), - (*Event_GetBlockRequest)(nil), - (*Event_GetBlockResponse)(nil), (*Event_GetChainRequest)(nil), (*Event_GetChainResponse)(nil), (*Event_GetHeadToCheckpointChainRequest)(nil), @@ -1151,7 +938,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_bcmpb_bcmpb_proto_rawDesc, NumEnums: 0, - NumMessages: 11, + NumMessages: 9, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go index 2977f948d..2b384fed7 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go @@ -10,8 +10,6 @@ func (*Event) ReflectTypeOptions() []reflect.Type { return []reflect.Type{ reflect.TypeOf((*Event_NewBlock)(nil)), reflect.TypeOf((*Event_NewChain)(nil)), - reflect.TypeOf((*Event_GetBlockRequest)(nil)), - reflect.TypeOf((*Event_GetBlockResponse)(nil)), reflect.TypeOf((*Event_GetChainRequest)(nil)), reflect.TypeOf((*Event_GetChainResponse)(nil)), reflect.TypeOf((*Event_GetHeadToCheckpointChainRequest)(nil)), diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go index 8d4558023..b81f6fa71 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go @@ -20,14 +20,6 @@ func NewChain(m dsl.Module, destModule types.ModuleID, blocks []*types1.Block) { dsl.EmitMirEvent(m, events.NewChain(destModule, blocks)) } -func GetBlockRequest(m dsl.Module, destModule types.ModuleID, requestId string, sourceModule types.ModuleID, blockId uint64) { - dsl.EmitMirEvent(m, events.GetBlockRequest(destModule, requestId, sourceModule, blockId)) -} - -func GetBlockResponse(m dsl.Module, destModule types.ModuleID, requestId string, found bool, block *types1.Block) { - dsl.EmitMirEvent(m, events.GetBlockResponse(destModule, requestId, found, block)) -} - func GetChainRequest(m dsl.Module, destModule types.ModuleID, requestId string, sourceModule types.ModuleID, endBlockId uint64, sourceBlockIds []uint64) { dsl.EmitMirEvent(m, events.GetChainRequest(destModule, requestId, sourceModule, endBlockId, sourceBlockIds)) } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go index fedeef406..75d4898c2 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go @@ -36,18 +36,6 @@ func UponNewChain(m dsl.Module, handler func(blocks []*types2.Block) error) { }) } -func UponGetBlockRequest(m dsl.Module, handler func(requestId string, sourceModule types3.ModuleID, blockId uint64) error) { - UponEvent[*types.Event_GetBlockRequest](m, func(ev *types.GetBlockRequest) error { - return handler(ev.RequestId, ev.SourceModule, ev.BlockId) - }) -} - -func UponGetBlockResponse(m dsl.Module, handler func(requestId string, found bool, block *types2.Block) error) { - UponEvent[*types.Event_GetBlockResponse](m, func(ev *types.GetBlockResponse) error { - return handler(ev.RequestId, ev.Found, ev.Block) - }) -} - func UponGetChainRequest(m dsl.Module, handler func(requestId string, sourceModule types3.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error) { UponEvent[*types.Event_GetChainRequest](m, func(ev *types.GetChainRequest) error { return handler(ev.RequestId, ev.SourceModule, ev.EndBlockId, ev.SourceBlockIds) diff --git a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go index e2a7e8072..03c6019e3 100644 --- a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go @@ -40,40 +40,6 @@ func NewChain(destModule types.ModuleID, blocks []*types1.Block) *types2.Event { } } -func GetBlockRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID, blockId uint64) *types2.Event { - return &types2.Event{ - DestModule: destModule, - Type: &types2.Event_Bcm{ - Bcm: &types3.Event{ - Type: &types3.Event_GetBlockRequest{ - GetBlockRequest: &types3.GetBlockRequest{ - RequestId: requestId, - SourceModule: sourceModule, - BlockId: blockId, - }, - }, - }, - }, - } -} - -func GetBlockResponse(destModule types.ModuleID, requestId string, found bool, block *types1.Block) *types2.Event { - return &types2.Event{ - DestModule: destModule, - Type: &types2.Event_Bcm{ - Bcm: &types3.Event{ - Type: &types3.Event_GetBlockResponse{ - GetBlockResponse: &types3.GetBlockResponse{ - RequestId: requestId, - Found: found, - Block: block, - }, - }, - }, - }, - } -} - func GetChainRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID, endBlockId uint64, sourceBlockIds []uint64) *types2.Event { return &types2.Event{ DestModule: destModule, diff --git a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go index eeaf28733..d67bc0451 100644 --- a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go @@ -17,14 +17,6 @@ func (w *Event_NewChain) Unwrap() *NewChain { return w.NewChain } -func (w *Event_GetBlockRequest) Unwrap() *GetBlockRequest { - return w.GetBlockRequest -} - -func (w *Event_GetBlockResponse) Unwrap() *GetBlockResponse { - return w.GetBlockResponse -} - func (w *Event_GetChainRequest) Unwrap() *GetChainRequest { return w.GetChainRequest } diff --git a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go index 9ab0e7022..706ab7e7a 100644 --- a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go @@ -37,10 +37,6 @@ func Event_TypeFromPb(pb bcmpb.Event_Type) Event_Type { return &Event_NewBlock{NewBlock: NewBlockFromPb(pb.NewBlock)} case *bcmpb.Event_NewChain: return &Event_NewChain{NewChain: NewChainFromPb(pb.NewChain)} - case *bcmpb.Event_GetBlockRequest: - return &Event_GetBlockRequest{GetBlockRequest: GetBlockRequestFromPb(pb.GetBlockRequest)} - case *bcmpb.Event_GetBlockResponse: - return &Event_GetBlockResponse{GetBlockResponse: GetBlockResponseFromPb(pb.GetBlockResponse)} case *bcmpb.Event_GetChainRequest: return &Event_GetChainRequest{GetChainRequest: GetChainRequestFromPb(pb.GetChainRequest)} case *bcmpb.Event_GetChainResponse: @@ -105,54 +101,6 @@ func (*Event_NewChain) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_NewChain]()} } -type Event_GetBlockRequest struct { - GetBlockRequest *GetBlockRequest -} - -func (*Event_GetBlockRequest) isEvent_Type() {} - -func (w *Event_GetBlockRequest) Unwrap() *GetBlockRequest { - return w.GetBlockRequest -} - -func (w *Event_GetBlockRequest) Pb() bcmpb.Event_Type { - if w == nil { - return nil - } - if w.GetBlockRequest == nil { - return &bcmpb.Event_GetBlockRequest{} - } - return &bcmpb.Event_GetBlockRequest{GetBlockRequest: (w.GetBlockRequest).Pb()} -} - -func (*Event_GetBlockRequest) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetBlockRequest]()} -} - -type Event_GetBlockResponse struct { - GetBlockResponse *GetBlockResponse -} - -func (*Event_GetBlockResponse) isEvent_Type() {} - -func (w *Event_GetBlockResponse) Unwrap() *GetBlockResponse { - return w.GetBlockResponse -} - -func (w *Event_GetBlockResponse) Pb() bcmpb.Event_Type { - if w == nil { - return nil - } - if w.GetBlockResponse == nil { - return &bcmpb.Event_GetBlockResponse{} - } - return &bcmpb.Event_GetBlockResponse{GetBlockResponse: (w.GetBlockResponse).Pb()} -} - -func (*Event_GetBlockResponse) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetBlockResponse]()} -} - type Event_GetChainRequest struct { GetChainRequest *GetChainRequest } @@ -388,78 +336,6 @@ func (*NewChain) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.NewChain]()} } -type GetBlockRequest struct { - RequestId string - SourceModule types2.ModuleID - BlockId uint64 -} - -func GetBlockRequestFromPb(pb *bcmpb.GetBlockRequest) *GetBlockRequest { - if pb == nil { - return nil - } - return &GetBlockRequest{ - RequestId: pb.RequestId, - SourceModule: (types2.ModuleID)(pb.SourceModule), - BlockId: pb.BlockId, - } -} - -func (m *GetBlockRequest) Pb() *bcmpb.GetBlockRequest { - if m == nil { - return nil - } - pbMessage := &bcmpb.GetBlockRequest{} - { - pbMessage.RequestId = m.RequestId - pbMessage.SourceModule = (string)(m.SourceModule) - pbMessage.BlockId = m.BlockId - } - - return pbMessage -} - -func (*GetBlockRequest) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetBlockRequest]()} -} - -type GetBlockResponse struct { - RequestId string - Found bool - Block *types.Block -} - -func GetBlockResponseFromPb(pb *bcmpb.GetBlockResponse) *GetBlockResponse { - if pb == nil { - return nil - } - return &GetBlockResponse{ - RequestId: pb.RequestId, - Found: pb.Found, - Block: types.BlockFromPb(pb.Block), - } -} - -func (m *GetBlockResponse) Pb() *bcmpb.GetBlockResponse { - if m == nil { - return nil - } - pbMessage := &bcmpb.GetBlockResponse{} - { - pbMessage.RequestId = m.RequestId - pbMessage.Found = m.Found - if m.Block != nil { - pbMessage.Block = (m.Block).Pb() - } - } - - return pbMessage -} - -func (*GetBlockResponse) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetBlockResponse]()} -} - type GetChainRequest struct { RequestId string SourceModule types2.ModuleID diff --git a/protos/blockchainpb/bcmpb/bcmpb.proto b/protos/blockchainpb/bcmpb/bcmpb.proto index 06875640c..260d5cedc 100644 --- a/protos/blockchainpb/bcmpb/bcmpb.proto +++ b/protos/blockchainpb/bcmpb/bcmpb.proto @@ -18,8 +18,6 @@ message Event { NewBlock new_block = 1; NewChain new_chain = 2; - GetBlockRequest get_block_request = 3; - GetBlockResponse get_block_response = 4; GetChainRequest get_chain_request = 5; GetChainResponse get_chain_response = 6; GetHeadToCheckpointChainRequest get_head_to_checkpoint_chain_request = 7; @@ -43,23 +41,6 @@ message NewChain { repeated blockchainpb.Block blocks = 1; } -message GetBlockRequest { - option (mir.event) = true; - - string request_id = 1; - string source_module = 2 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.ModuleID"]; - - uint64 block_id = 3; -} - -message GetBlockResponse { - option (mir.event) = true; - - string request_id = 1; - bool found = 2; - blockchainpb.Block block = 3; -} - message GetChainRequest { option (mir.event) = true; diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index aea85fef4..4722459d8 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -499,25 +499,25 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { return bcm.handleNewChain(blocks) }) - bcmpbdsl.UponGetBlockRequest(m, func(requestID string, sourceModule t.ModuleID, blockID uint64) error { - bcm.logger.Log(logging.LevelInfo, "Received get block request", "requestId", requestID, "sourceModule", sourceModule) - if err := bcm.checkInitialization(); err != nil { - return err - } - // check if block is in tree - hit, err := bcm.findBlock(blockID) - - if err != nil { - bcm.logger.Log(logging.LevelDebug, "Block not found", "requestId", requestID, "error", err) - bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) - return nil - } - - bcm.logger.Log(logging.LevelDebug, "Found block in tree", "requestId", requestID) - bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block) - - return nil - }) + // bcmpbdsl.UponGetBlockRequest(m, func(requestID string, sourceModule t.ModuleID, blockID uint64) error { + // bcm.logger.Log(logging.LevelInfo, "Received get block request", "requestId", requestID, "sourceModule", sourceModule) + // if err := bcm.checkInitialization(); err != nil { + // return err + // } + // // check if block is in tree + // hit, err := bcm.findBlock(blockID) + + // if err != nil { + // bcm.logger.Log(logging.LevelDebug, "Block not found", "requestId", requestID, "error", err) + // bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) + // return nil + // } + + // bcm.logger.Log(logging.LevelDebug, "Found block in tree", "requestId", requestID) + // bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block) + + // return nil + // }) bcmpbdsl.UponGetChainRequest(m, func(requestID string, sourceModule t.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error { bcm.logger.Log(logging.LevelInfo, "Received get chain request", "requestId", requestID, "sourceModule", sourceModule, "endBlockId", utils.FormatBlockId(endBlockId), "sourceBlockIds", utils.FormatBlockIdSlice(sourceBlockIds)) @@ -534,7 +534,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { if err != nil { bcm.logger.Log(logging.LevelDebug, "Block not found - can't build chain", "requestId", requestID, "error", err) - bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) + bcmpbdsl.GetChainResponse(*bcm.m, sourceModule, requestID, false, nil) return nil } From 3d32d8383fa5432032ed5b8c322ab1804cd64e86 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Wed, 31 Jan 2024 16:41:48 +0100 Subject: [PATCH 21/46] add miner (node) id to blocks --- pkg/pb/blockchainpb/blockchainpb.pb.go | 27 +++++++++++++++++++------- pkg/pb/blockchainpb/types/types.mir.go | 4 ++++ protos/blockchainpb/blockchainpb.proto | 1 + samples/blockchain/bcm.go | 1 + samples/blockchain/main.go | 6 +++--- samples/blockchain/miner.go | 7 +++++-- samples/blockchain/utils/hash.go | 3 ++- 7 files changed, 36 insertions(+), 13 deletions(-) diff --git a/pkg/pb/blockchainpb/blockchainpb.pb.go b/pkg/pb/blockchainpb/blockchainpb.pb.go index 33944fc62..78f1fd46a 100644 --- a/pkg/pb/blockchainpb/blockchainpb.pb.go +++ b/pkg/pb/blockchainpb/blockchainpb.pb.go @@ -87,6 +87,7 @@ type Block struct { PreviousBlockId uint64 `protobuf:"varint,2,opt,name=previous_block_id,json=previousBlockId,proto3" json:"previous_block_id,omitempty"` Payload *payloadpb.Payload `protobuf:"bytes,3,opt,name=payload,proto3" json:"payload,omitempty"` Timestamp *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=timestamp,proto3" json:"timestamp,omitempty"` + MinerId string `protobuf:"bytes,5,opt,name=miner_id,json=minerId,proto3" json:"miner_id,omitempty"` } func (x *Block) Reset() { @@ -149,6 +150,13 @@ func (x *Block) GetTimestamp() *timestamppb.Timestamp { return nil } +func (x *Block) GetMinerId() string { + if x != nil { + return x.MinerId + } + return "" +} + var File_blockchainpb_blockchainpb_proto protoreflect.FileDescriptor var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ @@ -166,8 +174,8 @@ var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, - 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0xbc, - 0x01, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, + 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x8d, + 0x02, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, @@ -178,11 +186,16 @@ var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x35, 0x5a, - 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, - 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x4f, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x65, 0x72, + 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x34, 0x82, 0xa6, 0x1d, 0x30, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, + 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x52, + 0x07, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x35, + 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, + 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, + 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/pb/blockchainpb/types/types.mir.go b/pkg/pb/blockchainpb/types/types.mir.go index 4c6e0f61f..f223d920f 100644 --- a/pkg/pb/blockchainpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/types/types.mir.go @@ -7,6 +7,7 @@ import ( types "github.com/filecoin-project/mir/codegen/model/types" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types2 "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" timestamppb "google.golang.org/protobuf/types/known/timestamppb" ) @@ -52,6 +53,7 @@ type Block struct { PreviousBlockId uint64 Payload *types1.Payload Timestamp *timestamppb.Timestamp + MinerId types2.NodeID } func BlockFromPb(pb *blockchainpb.Block) *Block { @@ -63,6 +65,7 @@ func BlockFromPb(pb *blockchainpb.Block) *Block { PreviousBlockId: pb.PreviousBlockId, Payload: types1.PayloadFromPb(pb.Payload), Timestamp: pb.Timestamp, + MinerId: (types2.NodeID)(pb.MinerId), } } @@ -80,6 +83,7 @@ func (m *Block) Pb() *blockchainpb.Block { if m.Timestamp != nil { pbMessage.Timestamp = m.Timestamp } + pbMessage.MinerId = (string)(m.MinerId) } return pbMessage diff --git a/protos/blockchainpb/blockchainpb.proto b/protos/blockchainpb/blockchainpb.proto index 4d92b2dfe..cffdf51d7 100644 --- a/protos/blockchainpb/blockchainpb.proto +++ b/protos/blockchainpb/blockchainpb.proto @@ -23,4 +23,5 @@ message Block { payloadpb.Payload payload = 3; google.protobuf.Timestamp timestamp = 4; + string miner_id = 5 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.NodeID"]; } \ No newline at end of file diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 4722459d8..21382c241 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -436,6 +436,7 @@ func (bcm *bcmModule) handleInitBlockchain(initialState *statepbtypes.State) err PreviousBlockId: 0, Payload: &payloadpbtypes.Payload{}, Timestamp: timestamppb.New(time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC)), // unix 0 + MinerId: "", // no node mined this block, leaving node id empty } hash := utils.HashBlock(genesis) //uint64(0) diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index b74fd02df..38ee30e4a 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -20,7 +20,7 @@ import ( "github.com/filecoin-project/mir/pkg/timer" t "github.com/filecoin-project/mir/pkg/types" application "github.com/filecoin-project/mir/samples/blockchain/application" - wsinterceptor "github.com/filecoin-project/mir/samples/blockchain/wsInterceptor" + "github.com/filecoin-project/mir/samples/blockchain/wsInterceptor" ) func main() { @@ -90,7 +90,7 @@ func main() { map[t.ModuleID]modules.Module{ "transport": transport, "bcm": NewBCM(logging.Decorate(logger, "BCM:\t")), - "miner": NewMiner(logging.Decorate(logger, "Miner:\t")), + "miner": NewMiner(ownNodeID, logging.Decorate(logger, "Miner:\t")), "communication": NewCommunication(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), "application": application.NewApplication(logging.Decorate(logger, "App:\t"), ownNodeID), "synchronizer": NewSynchronizer(ownNodeID, otherNodes, false, logging.Decorate(logger, "Sync:\t")), @@ -98,7 +98,7 @@ func main() { "mangler": mangler, "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor }, - wsinterceptor.NewWsInterceptor( + wsInterceptor.NewWsInterceptor( func(e *eventpb.Event) bool { switch e.Type.(type) { case *eventpb.Event_Bcinterceptor: diff --git a/samples/blockchain/miner.go b/samples/blockchain/miner.go index 84054b529..92b74a500 100644 --- a/samples/blockchain/miner.go +++ b/samples/blockchain/miner.go @@ -16,6 +16,7 @@ import ( payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" "github.com/filecoin-project/mir/pkg/pb/eventpb" + t "github.com/filecoin-project/mir/pkg/types" "github.com/filecoin-project/mir/samples/blockchain/utils" "github.com/go-errors/errors" "github.com/mitchellh/hashstructure" @@ -32,13 +33,15 @@ type blockRequest struct { } type minerModule struct { + nodeID t.NodeID // id of this node blockRequests chan blockRequest eventsOut chan *events.EventList logger logging.Logger } -func NewMiner(logger logging.Logger) modules.ActiveModule { +func NewMiner(nodeID t.NodeID, logger logging.Logger) modules.ActiveModule { return &minerModule{ + nodeID: nodeID, blockRequests: make(chan blockRequest), eventsOut: make(chan *events.EventList), logger: logger, @@ -94,7 +97,7 @@ func (m *minerModule) mineWorkerManager() { m.logger.Log(logging.LevelDebug, "Mining aborted", "headId", utils.FormatBlockId(blockRequest.HeadId)) return case <-time.After(delay): - block := &blockchainpbtypes.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: timestamppb.Now()} + block := &blockchainpbtypes.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: timestamppb.Now(), MinerId: m.nodeID} hash, err := hashstructure.Hash(block, nil) if err != nil { m.logger.Log(logging.LevelError, "Failed to hash block", "error", err) diff --git a/samples/blockchain/utils/hash.go b/samples/blockchain/utils/hash.go index 8561ab9e4..1b020d85e 100644 --- a/samples/blockchain/utils/hash.go +++ b/samples/blockchain/utils/hash.go @@ -6,7 +6,8 @@ import ( ) func HashBlock(block *blockchainpbtypes.Block) uint64 { - hashBlock := &blockchainpbtypes.Block{BlockId: 0, PreviousBlockId: block.PreviousBlockId, Payload: block.Payload, Timestamp: block.Timestamp} + hashBlock := *block + hashBlock.BlockId = 0 hash, err := hashstructure.Hash(hashBlock, nil) if err != nil { panic(err) From 7d6f91202226105dcd22063d10732ecec7b151d7 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Wed, 31 Jan 2024 22:39:41 +0100 Subject: [PATCH 22/46] wip clean up and commenting/documenting --- samples/blockchain/bcm.go | 294 +++++++++++++++-------------- samples/blockchain/main.go | 2 +- samples/blockchain/miner.go | 56 ++++-- samples/blockchain/utils/slices.go | 9 + 4 files changed, 200 insertions(+), 161 deletions(-) create mode 100644 samples/blockchain/utils/slices.go diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 21382c241..2bdec3387 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -53,6 +53,11 @@ type bcmModule struct { initialized bool } +/** + * Helper Functions + */ + +// checkInitialization checks whether the blockchain has been initialized and returns an error if not func (bcm *bcmModule) checkInitialization() error { if !bcm.initialized { bcm.logger.Log(logging.LevelError, ErrUninitialized.Error()) @@ -62,10 +67,49 @@ func (bcm *bcmModule) checkInitialization() error { return nil } +// The blockTreeTraversal function traverses the block tree starting from the leaves and applying the traversalFunc to each block. +// It will stop when the traversalFunc returns true and return the current block for which the traversalFunc returned true. +// If the traversalFunc never returns true, the function will return nil and ErrNoHit. +// if the traversalFunc returns an error, the function will return nil and the error. +func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) (bool, error)) (*bcmBlock, error) { + bcm.currentScanCount = bcm.currentScanCount + 1 + queue := make([]*bcmBlock, 0) + + for _, v := range bcm.leaves { + queue = append(queue, v) + } + + for len(queue) > 0 { + // pop from queue + curr := queue[0] + queue = queue[1:] + + // mark as scanned + curr.scanCount = bcm.currentScanCount + + // check whether current block matches + if match, err := traversalFunc(curr); err != nil { + return nil, err + } else if match { + return curr, nil + } + + // add curr's parents to queue if it exist hasn't been scanned yet + // if curr.parent == nil, we are at the genesis block and can stop + if curr.parent != nil && curr.parent.scanCount <= bcm.currentScanCount { + queue = append(queue, curr.parent) + } + } + + return nil, ErrNoHit +} + func (bcm *bcmModule) handleNewHead(newHead, oldHead *bcmBlock) { bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.block.BlockId)) + // TODO: short circuit if newHead is oldHead's parent - // compute delta + // compute delta where removeChain is the chain leading to the old head and addChain is the chain leading to the new head + // the first block in each chain is the common ancestor (fork root) removeChain, addChain, err := bcm.computeDelta(oldHead, newHead) if err != nil { // should never happen @@ -91,11 +135,6 @@ func (bcm *bcmModule) handleNewHead(newHead, oldHead *bcmBlock) { minerpbdsl.NewHead(*bcm.m, "miner", newHead.block.BlockId) } -func reverse[S ~[]T, T any](slice S) S { - slices.Reverse(slice) - return slice -} - func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]*blockchainpbtypes.Block, []*blockchainpbtypes.Block, error) { bcm.logger.Log(logging.LevelDebug, "Computing Delta", "removeHead", removeHead.block.BlockId, "addHead", addHead.block.BlockId) @@ -107,7 +146,7 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* // just appending return []*blockchainpbtypes.Block{removeHead.block}, []*blockchainpbtypes.Block{removeHead.block, addHead.block}, nil } else if removeHead.block.PreviousBlockId == addHead.block.BlockId { - // rollbacks should never happen + // rollbacks should never happen, this only checks for the most simple case return nil, nil, ErrRollback } @@ -142,7 +181,7 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* bcm.logger.Log(logging.LevelError, "Intersection trucation failed (remove intersection case) - this should never happen", "blockId", utils.FormatBlockId(currRemoveBlockId)) panic("Intersection trucation failed (remove into add intersection case)") } - return reverse(removeChain), reverse(addChain[:index+1]), nil + return utils.Reverse(removeChain), utils.Reverse(addChain[:index+1]), nil } currRemove.scanCount = initialScanCount + 1 @@ -165,7 +204,7 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* bcm.logger.Log(logging.LevelError, "Intersection trucation failed (add intersection case) - this should never happen", "blockId", utils.FormatBlockId(currAddBlockId)) panic("Intersection trucation failed (add into remove intersection case)") } - return reverse(removeChain[:index+1]), reverse(addChain), nil + return utils.Reverse(removeChain[:index+1]), utils.Reverse(addChain), nil } currAdd.scanCount = initialScanCount + 1 @@ -179,38 +218,6 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* panic("intersection not found - this should never happen") } -// traversalFunc should return true if traversal should continue, false otherwise -// if it shouldn't continue, the function will return the current block -// if it should continue but there are no more blocks, the function will return nil -// if an error is thrown, the traversal is aborted and nil is returned -func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) (bool, error)) (*bcmBlock, error) { - bcm.currentScanCount = bcm.currentScanCount + 1 - queue := make([]*bcmBlock, 0) - for _, v := range bcm.leaves { - queue = append(queue, v) - } - for len(queue) > 0 { - // pop from queue - curr := queue[0] - queue = queue[1:] - - // mark as scanned - curr.scanCount = bcm.currentScanCount - - if match, err := traversalFunc(curr); err != nil { - return nil, err - } else if match { - return curr, nil - } - - // add curr's parents to queue if it hasn't been scanned yet - if curr.parent != nil && curr.parent.scanCount <= bcm.currentScanCount { - queue = append(queue, curr.parent) - } - } - return nil, ErrNoHit -} - // TODO: might need to add some "cleanup" to handle very old leaves in case this grows too much func (bcm *bcmModule) addBlock(block *blockchainpbtypes.Block) error { bcm.logger.Log(logging.LevelInfo, "Adding block...", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(block.PreviousBlockId)) @@ -301,6 +308,44 @@ func (bcm *bcmModule) processNewChain(blocks []*blockchainpbtypes.Block) error { return nil } +func (bcm *bcmModule) getChainFromCheckpointToBlock(block *bcmBlock) ([]*blockchainpbtypes.Block, *statepbtypes.State) { + + chain := make([]*blockchainpbtypes.Block, 0) + + currentBlock := block + for currentBlock != nil { + chain = append(chain, currentBlock.block) + if bcmBlock, ok := bcm.checkpoints[currentBlock.block.BlockId]; ok { + return utils.Reverse(chain), bcmBlock.state + } + currentBlock = currentBlock.parent + } + + bcm.logger.Log(logging.LevelWarn, "Cannot link up with checkpoints", "source", utils.FormatBlockId(block.block.BlockId), "chain", chain) + return nil, nil +} + +/** + * For visualization only, not relevant for the actual operation + */ + +// Sends all blocks to the interceptor for visualization +func (bcm *bcmModule) sendTreeUpdate() { + blocks := func() []*blockchainpbtypes.Block { + blocks := make([]*blockchainpbtypes.Block, 0, len(bcm.blocks)) + for _, v := range bcm.blocks { + blocks = append(blocks, v.block) + } + return blocks + }() + + interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", blocks, bcm.head.block.BlockId) +} + +/** + * Mir Event Handlers + */ + func (bcm *bcmModule) handleVerifyBlocksResponse(blocks []*blockchainpbtypes.Block) error { currentHead := bcm.head // insert block @@ -333,53 +378,20 @@ func (bcm *bcmModule) handleVerifyBlocksResponse(blocks []*blockchainpbtypes.Blo return nil } -func (bcm *bcmModule) handleNewChain(blocks []*blockchainpbtypes.Block) error { - // just for logging - blockIds := make([]uint64, 0, len(blocks)) - for _, v := range blocks { - blockIds = append(blockIds, v.BlockId) - } - bcm.logger.Log(logging.LevelDebug, "Adding new chain", "chain", utils.FormatBlockIdSlice(blockIds)) - - return bcm.processNewChain(blocks) -} - func (bcm *bcmModule) handleNewBlock(block *blockchainpbtypes.Block) error { bcm.logger.Log(logging.LevelDebug, "Adding new block", "blockId", utils.FormatBlockId(block.BlockId)) return bcm.processNewChain([]*blockchainpbtypes.Block{block}) } -func (bcm *bcmModule) sendTreeUpdate() { - blocks := func() []*blockchainpbtypes.Block { - blocks := make([]*blockchainpbtypes.Block, 0, len(bcm.blocks)) - for _, v := range bcm.blocks { - blocks = append(blocks, v.block) - } - return blocks - }() - - interceptorpbdsl.TreeUpdate(*bcm.m, "devnull", blocks, bcm.head.block.BlockId) -} - -func (bcm *bcmModule) getChainFromCheckpointToBlock(block *bcmBlock) ([]*blockchainpbtypes.Block, *statepbtypes.State) { - - chain := make([]*blockchainpbtypes.Block, 0) - - currentBlock := block - for currentBlock != nil { - chain = append(chain, currentBlock.block) - if bcmBlock, ok := bcm.checkpoints[currentBlock.block.BlockId]; ok { - for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 { - chain[i], chain[j] = chain[j], chain[i] - } - - return chain, bcmBlock.state - } - currentBlock = currentBlock.parent +func (bcm *bcmModule) handleNewChain(blocks []*blockchainpbtypes.Block) error { + if len(blocks) == 0 { + bcm.logger.Log(logging.LevelError, "Received empty chain") + return nil } - bcm.logger.Log(logging.LevelWarn, "Cannot link up with checkpoints", "source", utils.FormatBlockId(block.block.BlockId), "chain", chain) - return nil, nil + bcm.logger.Log(logging.LevelDebug, "Adding new chain", "first block", utils.FormatBlockId(blocks[0].BlockId), "second block", utils.FormatBlockId(blocks[len(blocks)-1].BlockId)) + + return bcm.processNewChain(blocks) } func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, sourceModule t.ModuleID) error { @@ -427,6 +439,53 @@ func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepbty return nil } +func (bcm *bcmModule) handleGetChainRequest(requestID string, sourceModule t.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error { + bcm.logger.Log(logging.LevelInfo, "Received get chain request", "requestId", requestID, "sourceModule", sourceModule, "endBlockId", utils.FormatBlockId(endBlockId), "sourceBlockIds", utils.FormatBlockIdSlice(sourceBlockIds)) + + chain := make([]*blockchainpbtypes.Block, 0) + // for easier lookup... + sourceBlockIdsMap := make(map[uint64]uint64) + for _, v := range sourceBlockIds { + sourceBlockIdsMap[v] = v + } + currentBlock, err := bcm.findBlock(endBlockId) + + if err != nil { + bcm.logger.Log(logging.LevelDebug, "Block not found - can't build chain", "requestId", requestID, "error", err) + bcmpbdsl.GetChainResponse(*bcm.m, sourceModule, requestID, false, nil) + return nil + } + + bcm.logger.Log(logging.LevelDebug, "Found block in tree - building chain", "requestId", requestID) + + // initialize chain + chain = append(chain, currentBlock.block) + + for currentBlock.parent != nil { + // if parent is in sourceBlockIds, stop + if _, ok := sourceBlockIdsMap[currentBlock.parent.block.BlockId]; ok { + // chain build + bcm.logger.Log(logging.LevelDebug, "Found link with sourceBlockIds - chain build", "requestId", requestID) + // reversing chain to send it in the right order + for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 { + chain[i], chain[j] = chain[j], chain[i] + } + bcmpbdsl.GetChainResponse(*bcm.m, sourceModule, requestID, true, chain) + return nil + } + + chain = append(chain, currentBlock.parent.block) + currentBlock = currentBlock.parent + } + + // if we get here, we didn't find a link with sourceBlockIds + bcm.logger.Log(logging.LevelDebug, "No link with sourceBlockIds - can't build chain", "requestId", requestID) + bcmpbdsl.GetChainResponse(*bcm.m, sourceModule, requestID, false, nil) + + return nil + +} + func (bcm *bcmModule) handleInitBlockchain(initialState *statepbtypes.State) error { // initialize blockchain @@ -465,6 +524,10 @@ func (bcm *bcmModule) handleInitBlockchain(initialState *statepbtypes.State) err return nil } +/** + * Module Definition + */ + func NewBCM(logger logging.Logger) modules.PassiveModule { m := dsl.NewModule("bcm") @@ -500,76 +563,21 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { return bcm.handleNewChain(blocks) }) - // bcmpbdsl.UponGetBlockRequest(m, func(requestID string, sourceModule t.ModuleID, blockID uint64) error { - // bcm.logger.Log(logging.LevelInfo, "Received get block request", "requestId", requestID, "sourceModule", sourceModule) - // if err := bcm.checkInitialization(); err != nil { - // return err - // } - // // check if block is in tree - // hit, err := bcm.findBlock(blockID) - - // if err != nil { - // bcm.logger.Log(logging.LevelDebug, "Block not found", "requestId", requestID, "error", err) - // bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, false, nil) - // return nil - // } - - // bcm.logger.Log(logging.LevelDebug, "Found block in tree", "requestId", requestID) - // bcmpbdsl.GetBlockResponse(*bcm.m, sourceModule, requestID, true, hit.block) - - // return nil - // }) - bcmpbdsl.UponGetChainRequest(m, func(requestID string, sourceModule t.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error { - bcm.logger.Log(logging.LevelInfo, "Received get chain request", "requestId", requestID, "sourceModule", sourceModule, "endBlockId", utils.FormatBlockId(endBlockId), "sourceBlockIds", utils.FormatBlockIdSlice(sourceBlockIds)) if err := bcm.checkInitialization(); err != nil { return err } - chain := make([]*blockchainpbtypes.Block, 0) - // for easier lookup... - sourceBlockIdsMap := make(map[uint64]uint64) - for _, v := range sourceBlockIds { - sourceBlockIdsMap[v] = v - } - currentBlock, err := bcm.findBlock(endBlockId) - - if err != nil { - bcm.logger.Log(logging.LevelDebug, "Block not found - can't build chain", "requestId", requestID, "error", err) - bcmpbdsl.GetChainResponse(*bcm.m, sourceModule, requestID, false, nil) - return nil - } - - bcm.logger.Log(logging.LevelDebug, "Found block in tree - building chain", "requestId", requestID) - - // initialize chain - chain = append(chain, currentBlock.block) - - for currentBlock.parent != nil { - // if parent is in sourceBlockIds, stop - if _, ok := sourceBlockIdsMap[currentBlock.parent.block.BlockId]; ok { - // chain build - bcm.logger.Log(logging.LevelDebug, "Found link with sourceBlockIds - chain build", "requestId", requestID) - // reversing chain to send it in the right order - for i, j := 0, len(chain)-1; i < j; i, j = i+1, j-1 { - chain[i], chain[j] = chain[j], chain[i] - } - bcmpbdsl.GetChainResponse(*bcm.m, sourceModule, requestID, true, chain) - return nil - } + return bcm.handleGetChainRequest(requestID, sourceModule, endBlockId, sourceBlockIds) + }) - chain = append(chain, currentBlock.parent.block) - currentBlock = currentBlock.parent + bcmpbdsl.UponGetHeadToCheckpointChainRequest(m, func(requestId string, sourceModule t.ModuleID) error { + bcm.logger.Log(logging.LevelTrace, "Received get head to checkpoint chain request") + if err := bcm.checkInitialization(); err != nil { + return err } - - // if we get here, we didn't find a link with sourceBlockIds - bcm.logger.Log(logging.LevelDebug, "No link with sourceBlockIds - can't build chain", "requestId", requestID) - bcmpbdsl.GetChainResponse(*bcm.m, sourceModule, requestID, false, nil) - - return nil - + return bcm.handleGetHeadToCheckpointChainRequest(requestId, sourceModule) }) - bcmpbdsl.UponGetHeadToCheckpointChainRequest(m, bcm.handleGetHeadToCheckpointChainRequest) bcmpbdsl.UponRegisterCheckpoint(m, bcm.handleRegisterCheckpoint) bcmpbdsl.UponInitBlockchain(m, bcm.handleInitBlockchain) diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index 38ee30e4a..51ea733c0 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -90,7 +90,7 @@ func main() { map[t.ModuleID]modules.Module{ "transport": transport, "bcm": NewBCM(logging.Decorate(logger, "BCM:\t")), - "miner": NewMiner(ownNodeID, logging.Decorate(logger, "Miner:\t")), + "miner": NewMiner(ownNodeID, 0.2, logging.Decorate(logger, "Miner:\t")), "communication": NewCommunication(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), "application": application.NewApplication(logging.Decorate(logger, "App:\t"), ownNodeID), "synchronizer": NewSynchronizer(ownNodeID, otherNodes, false, logging.Decorate(logger, "Sync:\t")), diff --git a/samples/blockchain/miner.go b/samples/blockchain/miner.go index 92b74a500..5e7c5971e 100644 --- a/samples/blockchain/miner.go +++ b/samples/blockchain/miner.go @@ -23,28 +23,46 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) -const ( - EXPONENTIAL_MINUTE_FACTOR = 0.2 -) +/** + * Miner module + * ============ + * + * The miner module is responsible for mining new blocks. + * It simulates the process of mining a block by waiting for a random amount of time and then broadcasting the mined block. + * This random amount of time is sampled from an exponential distribution with a mean of `expMinuteFactor` minutes. + * The mining is orchestrated by a separate goroutine (mineWorkerManager), so that the miner module can continue to receive and process events. + * + * The operation of the miner module at a high level is as follows: + * 1. When it is notified of a new head (NewHead event), it prepares to mine the next block by sending a PayloadRequest event to the application module. + * If it is already mining a block, it aborts the ongoing mining operation. + * 2. When it receives the PayloadResponse containing a payload for the next block, it starts mining a new block with the received payload. + * 3. When it mines a new block, it broadcasts it to all other modules by sending a NewBlock message to the broadcast module. + * It also shares the block with the blockchain manager module (BCM) by sending a NewBlock event to it. + */ +/** + * Request for a new block to be mined. + */ type blockRequest struct { - HeadId uint64 - Payload *payloadpbtypes.Payload + HeadId uint64 // id of the head of the blockchain (parent of the new block) + Payload *payloadpbtypes.Payload // payload for the new block } type minerModule struct { - nodeID t.NodeID // id of this node - blockRequests chan blockRequest - eventsOut chan *events.EventList - logger logging.Logger + nodeID t.NodeID // id of this node + expMinuteFactor float64 // factor for exponential distribution for random mining duration + blockRequests chan blockRequest // channel to send block requests to mineWorker + eventsOut chan *events.EventList // channel to send events to other modules + logger logging.Logger } -func NewMiner(nodeID t.NodeID, logger logging.Logger) modules.ActiveModule { +func NewMiner(nodeID t.NodeID, expMinuteFactor float64, logger logging.Logger) modules.ActiveModule { return &minerModule{ - nodeID: nodeID, - blockRequests: make(chan blockRequest), - eventsOut: make(chan *events.EventList), - logger: logger, + nodeID: nodeID, + expMinuteFactor: expMinuteFactor, + blockRequests: make(chan blockRequest), + eventsOut: make(chan *events.EventList), + logger: logger, } } @@ -86,17 +104,21 @@ func (m *minerModule) mineWorkerManager() { ctx, cancel := context.WithCancel(context.Background()) for { - blockRequest := <-m.blockRequests + blockRequest := <-m.blockRequests // blocking until a new block request is received cancel() // abort ongoing mining ctx, cancel = context.WithCancel(context.Background()) // new context for new mining m.logger.Log(logging.LevelInfo, "New mining operation", "headId", utils.FormatBlockId(blockRequest.HeadId)) + + // spawn new goroutine to mine the requested block go func() { - delay := time.Duration(rand.ExpFloat64() * float64(time.Minute) * EXPONENTIAL_MINUTE_FACTOR) + delay := time.Duration(rand.ExpFloat64() * float64(time.Minute) * m.expMinuteFactor) select { case <-ctx.Done(): + // Mining aborted. Do nothing. m.logger.Log(logging.LevelDebug, "Mining aborted", "headId", utils.FormatBlockId(blockRequest.HeadId)) return case <-time.After(delay): + // Mining completed. Create block and broadcast. block := &blockchainpbtypes.Block{BlockId: 0, PreviousBlockId: blockRequest.HeadId, Payload: blockRequest.Payload, Timestamp: timestamppb.Now(), MinerId: m.nodeID} hash, err := hashstructure.Hash(block, nil) if err != nil { @@ -104,7 +126,7 @@ func (m *minerModule) mineWorkerManager() { panic(err) } block.BlockId = hash - // Block mined! Broadcast to blockchain and send event to bcm. + // Send block to BCM and broadcast module m.logger.Log(logging.LevelInfo, "Block mined", "headId", utils.FormatBlockId(hash), "parentId", utils.FormatBlockId(blockRequest.HeadId)) m.eventsOut <- events.ListOf(bcmpbevents.NewBlock("bcm", block).Pb(), communicationpbevents.NewBlock("communication", block).Pb()) return diff --git a/samples/blockchain/utils/slices.go b/samples/blockchain/utils/slices.go new file mode 100644 index 000000000..9ce59cff3 --- /dev/null +++ b/samples/blockchain/utils/slices.go @@ -0,0 +1,9 @@ +package utils + +import "slices" + +// Reverse reverses the order of the elements in a slice and returns the result +func Reverse[S ~[]T, T any](slice S) S { + slices.Reverse(slice) + return slice +} From 820f3820d8dd5e473574359e5e00563fd4fb8bfc Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Thu, 1 Feb 2024 10:29:08 +0100 Subject: [PATCH 23/46] more comments, cleanup --- samples/blockchain/application/application.go | 70 ++++++++++++--- .../application/payloads/payloads.go | 90 +++++++++++++++++++ .../application/transactions/transactions.go | 78 ---------------- 3 files changed, 146 insertions(+), 92 deletions(-) create mode 100644 samples/blockchain/application/payloads/payloads.go delete mode 100644 samples/blockchain/application/transactions/transactions.go diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index 523116af7..4cf78fed6 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -16,26 +16,56 @@ import ( blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" t "github.com/filecoin-project/mir/pkg/types" "github.com/filecoin-project/mir/samples/blockchain/application/config" - "github.com/filecoin-project/mir/samples/blockchain/application/transactions" + "github.com/filecoin-project/mir/samples/blockchain/application/payloads" "github.com/filecoin-project/mir/samples/blockchain/utils" "google.golang.org/protobuf/types/known/timestamppb" ) +/** + * Application module + * ================== + * + * The application module is reponsible for performing the actual application logic and to interact with users or other applications. + * It does not hold any state, but instead relies on the blockchain manager module (BCM) to store the state. + * However, the application needs to compute the state. + * Also, the application module is responsible for providing payloads for new blocks. + * + * The application module must perform the following tasks: + * 1. Initialize the blockchain by sending it the initial state in an InitBlockchain event to the BCM. + * 2. When it receives a PayloadRequest event, it must provide a payload for the next block. This payload can be empty. + * 3. When it receives a ForkUpdate event, it must compute the state at the new head of the blockchain. + * This state is then registered with the BCM by sending it a RegisterCheckpoint event. + * A checkpoint is a block stored by the BCM that has a state stored with it. + * 4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at a application level and respond with a VerifyBlocksResponse event. + * Whether or not not the blocks link together correctly is verified by the BCM. + * + * This application module implements a simple chat application. + * It takes new messages from the user (MessageInput event) and combines them with a sender id and "sent" timestamp as payloads. + * These payloads are stored in the payload manager (see applicaion/payloads/payloads.go). + * The state is the list of all messages that have been sent and timestamps for when each sender last sent a message. + * At the application level, a chain is valid if the timestamps are monotonically increasing for each sender. + */ + type ApplicationModule struct { m *dsl.Module + pm *payloads.PayloadManager // store for payloads + nodeID t.NodeID // to set sender in payload logger logging.Logger - tm *transactions.TransactionManager - nodeID t.NodeID } // application-application events +/** +* Helper functions + */ + +// helper function that applies a block's payload to the given state, thereby computing the new state func applyBlockToState(state *statepbtypes.State, block *blockchainpbtypes.Block) *statepbtypes.State { sender := block.Payload.Sender timeStamps := state.LastSentTimestamps msgHistory := state.MessageHistory - // why is this necessary? it should be verified already + // TODO: is this necessary? it should be verified already for i, lt := range timeStamps { if lt.NodeId == sender { ltTimestamp := lt.Timestamp.AsTime() @@ -65,17 +95,25 @@ func applyBlockToState(state *statepbtypes.State, block *blockchainpbtypes.Block } } +/** +* Mir event handlers + */ + +// TODO: rename - fork is confusing + +// Handler called by BCM when the head changes. +// It handles cases where the head changes because the canonical chain was extended as well as cases where the canonical chain changes because of a fork. func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain, checkpointToForkRootChain []*blockchainpbtypes.Block, checkpointState *statepbtypes.State) error { - am.logger.Log(logging.LevelInfo, "Processing fork update", "poolSize", am.tm.PoolSize()) + am.logger.Log(logging.LevelInfo, "Processing fork update", "poolSize", am.pm.PoolSize()) // add "remove chain" transactions to pool for _, block := range removedChain { - am.tm.AddPayload(block.Payload) + am.pm.AddPayload(block.Payload) } // remove "add chain" transactions from pool for _, block := range addedChain { - am.tm.RemovePayload(block.Payload) + am.pm.RemovePayload(block.Payload) } state := checkpointState @@ -89,7 +127,7 @@ func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain, checkpoi state = applyBlockToState(state, block) } - am.logger.Log(logging.LevelInfo, "Pool after fork", "poolSize", am.tm.PoolSize()) + am.logger.Log(logging.LevelInfo, "Pool after fork", "poolSize", am.pm.PoolSize()) // register checkpoint blockId := addedChain[len(addedChain)-1].BlockId @@ -106,6 +144,9 @@ func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain, checkpoi return nil } +// Handler called by BCM when a chain needs to be verified. +// The checks are purely at the application level. +// In this implementation, it simply checks that the timestamps are monotonically increasing for each sender. func (am *ApplicationModule) handleVerifyBlocksRequest(checkpointState *statepbtypes.State, chainCheckpointToStart, chainToVerify []*blockchainpbtypes.Block) error { am.logger.Log(logging.LevelDebug, "Processing verify block request") @@ -143,13 +184,14 @@ func (am *ApplicationModule) handleVerifyBlocksRequest(checkpointState *statepbt return nil } -// transaction management events - +// Handler called by the miner when it needs a payload for the next block. func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { am.logger.Log(logging.LevelDebug, "Processing payload request", "headId", utils.FormatBlockId(head_id)) - payload := am.tm.GetPayload() + payload := am.pm.GetPayload() + // if no payload is available, create empty payload + // this is important to ensure that all nodes are always mining new blocks if payload == nil { payload = &payloadpbtypes.Payload{ Message: "", @@ -158,7 +200,7 @@ func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { } } - applicationpbdsl.PayloadResponse(*am.m, "miner", head_id, payload) // not using head id anywhere so we can get rid of it + applicationpbdsl.PayloadResponse(*am.m, "miner", head_id, payload) return nil } @@ -170,7 +212,7 @@ func NewApplication(logger logging.Logger, nodeID t.NodeID) modules.PassiveModul m: &m, logger: logger, nodeID: nodeID, - tm: transactions.New(), + pm: payloads.New(), } dsl.UponInit(m, func() error { @@ -185,7 +227,7 @@ func NewApplication(logger logging.Logger, nodeID t.NodeID) modules.PassiveModul applicationpbdsl.UponVerifyBlocksRequest(m, am.handleVerifyBlocksRequest) applicationpbdsl.UponMessageInput(m, func(text string) error { - am.tm.AddPayload(&payloadpbtypes.Payload{ + am.pm.AddPayload(&payloadpbtypes.Payload{ Message: text, Timestamp: timestamppb.Now(), Sender: am.nodeID, diff --git a/samples/blockchain/application/payloads/payloads.go b/samples/blockchain/application/payloads/payloads.go new file mode 100644 index 000000000..51f0d6066 --- /dev/null +++ b/samples/blockchain/application/payloads/payloads.go @@ -0,0 +1,90 @@ +package payloads + +import ( + "slices" + + payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + "github.com/mitchellh/hashstructure" +) + +/** + * Payload manager + * =============== + * + * The payload manager is part of the application and responsible for managing payloads. + * It keeps track of all incoming payloads and provides the application with the oldest payload. + * If no payloads are available, it returns nil. + * The application can then use this payload to create a new block. + * Payloads can be added and removed from the payload manager. + * Equivalence of payloads is determined by their hash. + */ + +type storedPayload struct { + hash uint64 + payload *payloadpbtypes.Payload +} + +type PayloadManager struct { + payloads []storedPayload // sorted by timestamp +} + +func (tm *PayloadManager) PoolSize() int { + return len(tm.payloads) +} + +func New() *PayloadManager { + return &PayloadManager{ + payloads: []storedPayload{}, + } +} + +func (tm *PayloadManager) GetPayload() *payloadpbtypes.Payload { + // return oldest payload, where timestamp is a field in the payload + if len(tm.payloads) == 0 { + return nil + } + // sort by timestamp + // TODO: keep it sorted + slices.SortFunc(tm.payloads, func(i, j storedPayload) int { + return i.payload.Timestamp.AsTime().Compare(j.payload.Timestamp.AsTime()) + }) + return tm.payloads[0].payload +} + +func (tm *PayloadManager) AddPayload(payload *payloadpbtypes.Payload) error { + hash, err := hashstructure.Hash(payload, nil) + if err != nil { + return err + } + + if slices.ContainsFunc(tm.payloads, func(t storedPayload) bool { + return t.hash == hash + }) { + // already exists, ignore + return nil + } + + storedPaylod := storedPayload{ + hash: hash, + payload: payload, + } + + tm.payloads = append(tm.payloads, storedPaylod) + + return nil +} + +func (tm *PayloadManager) RemovePayload(payload *payloadpbtypes.Payload) error { + hash, err := hashstructure.Hash(payload, nil) + if err != nil { + return err + } + + // goes through all storedPayloads and removes the one with the matching hash + // NOTE: consider adding (hash) index to improve performance - not important rn + tm.payloads = slices.DeleteFunc(tm.payloads, func(t storedPayload) bool { + return t.hash == hash + }) + + return nil +} diff --git a/samples/blockchain/application/transactions/transactions.go b/samples/blockchain/application/transactions/transactions.go deleted file mode 100644 index 1da1645c9..000000000 --- a/samples/blockchain/application/transactions/transactions.go +++ /dev/null @@ -1,78 +0,0 @@ -package transactions - -import ( - "slices" - - payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" - "github.com/mitchellh/hashstructure" -) - -type transaction struct { - hash uint64 - payload *payloadpbtypes.Payload -} - -type TransactionManager struct { - transactions []transaction // sorted by timestamp -} - -func (tm *TransactionManager) PoolSize() int { - return len(tm.transactions) -} - -func New() *TransactionManager { - return &TransactionManager{ - transactions: []transaction{}, - } -} - -func (tm *TransactionManager) GetPayload() *payloadpbtypes.Payload { - // return oldest transaction, where timestamp is a field in the payload - if len(tm.transactions) == 0 { - return nil - } - // sort by timestamp - // TODO: keep it sorted - slices.SortFunc(tm.transactions, func(i, j transaction) int { - return i.payload.Timestamp.AsTime().Compare(j.payload.Timestamp.AsTime()) - }) - return tm.transactions[0].payload -} - -func (tm *TransactionManager) AddPayload(payload *payloadpbtypes.Payload) error { - hash, err := hashstructure.Hash(payload, nil) - if err != nil { - return err - } - - if slices.ContainsFunc(tm.transactions, func(t transaction) bool { - return t.hash == hash - }) { - // already exists, ignore - return nil - } - - transaction := transaction{ - hash: hash, - payload: payload, - } - - tm.transactions = append(tm.transactions, transaction) - - return nil -} - -func (tm *TransactionManager) RemovePayload(payload *payloadpbtypes.Payload) error { - hash, err := hashstructure.Hash(payload, nil) - if err != nil { - return err - } - - // goes through all transactions and removes the one with the matching hash - // NOTE: consider adding (hash) index to improve performance - not important rn - tm.transactions = slices.DeleteFunc(tm.transactions, func(t transaction) bool { - return t.hash == hash - }) - - return nil -} From efaabee274b7d09b7fad7202c6acd00f58a938ae Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Fri, 2 Feb 2024 13:18:36 +0100 Subject: [PATCH 24/46] comments, cleanup & communication -> broadcast --- .../broadcastpb.pb.go} | 159 ++++---- .../broadcastpb.pb.mir.go} | 2 +- .../dsl/emit.mir.go | 4 +- .../dsl/messages.mir.go | 6 +- .../dsl/upon.mir.go | 6 +- .../events/events.mir.go | 8 +- .../msgs/msgs.mir.go | 8 +- .../oneof_interfaces.mir.go | 2 +- .../types/types.mir.go | 64 ++-- .../synchronizerpb/dsl/emit.mir.go | 4 +- .../synchronizerpb/dsl/messages.mir.go | 4 +- .../synchronizerpb/dsl/upon.mir.go | 4 +- .../synchronizerpb/events/events.mir.go | 6 +- .../synchronizerpb/msgs/msgs.mir.go | 8 +- .../synchronizerpb/synchronizerpb.pb.go | 85 ++--- .../synchronizerpb/types/types.mir.go | 24 +- pkg/pb/eventpb/eventpb.pb.go | 349 +++++++++--------- pkg/pb/eventpb/eventpb.pb.mir.go | 2 +- pkg/pb/eventpb/oneof_interfaces.mir.go | 6 +- pkg/pb/eventpb/types/types.mir.go | 28 +- pkg/pb/messagepb/messagepb.pb.go | 137 ++++--- pkg/pb/messagepb/messagepb.pb.mir.go | 2 +- pkg/pb/messagepb/oneof_interfaces.mir.go | 6 +- pkg/pb/messagepb/types/types.mir.go | 28 +- .../broadcastpb.proto} | 4 +- .../synchronizerpb/synchronizerpb.proto | 8 +- protos/eventpb/eventpb.proto | 4 +- protos/generate.go | 4 +- protos/messagepb/messagepb.proto | 4 +- samples/blockchain/application/application.go | 6 +- .../blockchain/application/config/config.go | 10 - samples/blockchain/bcm.go | 9 +- .../{communication.go => broadcast.go} | 16 +- samples/blockchain/main.go | 18 +- samples/blockchain/miner.go | 6 +- samples/blockchain/synchronizer.go | 136 ++++--- 36 files changed, 596 insertions(+), 581 deletions(-) rename pkg/pb/blockchainpb/{communicationpb/communicationpb.pb.go => broadcastpb/broadcastpb.pb.go} (52%) rename pkg/pb/blockchainpb/{communicationpb/communicationpb.pb.mir.go => broadcastpb/broadcastpb.pb.mir.go} (93%) rename pkg/pb/blockchainpb/{communicationpb => broadcastpb}/dsl/emit.mir.go (78%) rename pkg/pb/blockchainpb/{communicationpb => broadcastpb}/dsl/messages.mir.go (80%) rename pkg/pb/blockchainpb/{communicationpb => broadcastpb}/dsl/upon.mir.go (77%) rename pkg/pb/blockchainpb/{communicationpb => broadcastpb}/events/events.mir.go (72%) rename pkg/pb/blockchainpb/{communicationpb => broadcastpb}/msgs/msgs.mir.go (72%) rename pkg/pb/blockchainpb/{communicationpb => broadcastpb}/oneof_interfaces.mir.go (94%) rename pkg/pb/blockchainpb/{communicationpb => broadcastpb}/types/types.mir.go (58%) rename protos/blockchainpb/{communicationpb/communicationpb.proto => broadcastpb/broadcastpb.proto} (92%) delete mode 100644 samples/blockchain/application/config/config.go rename samples/blockchain/{communication.go => broadcast.go} (57%) diff --git a/pkg/pb/blockchainpb/communicationpb/communicationpb.pb.go b/pkg/pb/blockchainpb/broadcastpb/broadcastpb.pb.go similarity index 52% rename from pkg/pb/blockchainpb/communicationpb/communicationpb.pb.go rename to pkg/pb/blockchainpb/broadcastpb/broadcastpb.pb.go index 3fb2e3242..8ca803459 100644 --- a/pkg/pb/blockchainpb/communicationpb/communicationpb.pb.go +++ b/pkg/pb/blockchainpb/broadcastpb/broadcastpb.pb.go @@ -2,9 +2,9 @@ // versions: // protoc-gen-go v1.31.0 // protoc v4.24.4 -// source: blockchainpb/communicationpb/communicationpb.proto +// source: blockchainpb/broadcastpb/broadcastpb.proto -package communicationpb +package broadcastpb import ( blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" @@ -37,7 +37,7 @@ type Event struct { func (x *Event) Reset() { *x = Event{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[0] + mi := &file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -50,7 +50,7 @@ func (x *Event) String() string { func (*Event) ProtoMessage() {} func (x *Event) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[0] + mi := &file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -63,7 +63,7 @@ func (x *Event) ProtoReflect() protoreflect.Message { // Deprecated: Use Event.ProtoReflect.Descriptor instead. func (*Event) Descriptor() ([]byte, []int) { - return file_blockchainpb_communicationpb_communicationpb_proto_rawDescGZIP(), []int{0} + return file_blockchainpb_broadcastpb_broadcastpb_proto_rawDescGZIP(), []int{0} } func (m *Event) GetType() isEvent_Type { @@ -101,7 +101,7 @@ type NewBlock struct { func (x *NewBlock) Reset() { *x = NewBlock{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[1] + mi := &file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -114,7 +114,7 @@ func (x *NewBlock) String() string { func (*NewBlock) ProtoMessage() {} func (x *NewBlock) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[1] + mi := &file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -127,7 +127,7 @@ func (x *NewBlock) ProtoReflect() protoreflect.Message { // Deprecated: Use NewBlock.ProtoReflect.Descriptor instead. func (*NewBlock) Descriptor() ([]byte, []int) { - return file_blockchainpb_communicationpb_communicationpb_proto_rawDescGZIP(), []int{1} + return file_blockchainpb_broadcastpb_broadcastpb_proto_rawDescGZIP(), []int{1} } func (x *NewBlock) GetBlock() *blockchainpb.Block { @@ -151,7 +151,7 @@ type Message struct { func (x *Message) Reset() { *x = Message{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[2] + mi := &file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -164,7 +164,7 @@ func (x *Message) String() string { func (*Message) ProtoMessage() {} func (x *Message) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[2] + mi := &file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -177,7 +177,7 @@ func (x *Message) ProtoReflect() protoreflect.Message { // Deprecated: Use Message.ProtoReflect.Descriptor instead. func (*Message) Descriptor() ([]byte, []int) { - return file_blockchainpb_communicationpb_communicationpb_proto_rawDescGZIP(), []int{2} + return file_blockchainpb_broadcastpb_broadcastpb_proto_rawDescGZIP(), []int{2} } func (m *Message) GetType() isMessage_Type { @@ -215,7 +215,7 @@ type NewBlockMessage struct { func (x *NewBlockMessage) Reset() { *x = NewBlockMessage{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[3] + mi := &file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -228,7 +228,7 @@ func (x *NewBlockMessage) String() string { func (*NewBlockMessage) ProtoMessage() {} func (x *NewBlockMessage) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[3] + mi := &file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -241,7 +241,7 @@ func (x *NewBlockMessage) ProtoReflect() protoreflect.Message { // Deprecated: Use NewBlockMessage.ProtoReflect.Descriptor instead. func (*NewBlockMessage) Descriptor() ([]byte, []int) { - return file_blockchainpb_communicationpb_communicationpb_proto_rawDescGZIP(), []int{3} + return file_blockchainpb_broadcastpb_broadcastpb_proto_rawDescGZIP(), []int{3} } func (x *NewBlockMessage) GetBlock() *blockchainpb.Block { @@ -251,71 +251,70 @@ func (x *NewBlockMessage) GetBlock() *blockchainpb.Block { return nil } -var File_blockchainpb_communicationpb_communicationpb_proto protoreflect.FileDescriptor - -var file_blockchainpb_communicationpb_communicationpb_proto_rawDesc = []byte{ - 0x0a, 0x32, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x63, - 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, - 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, - 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, - 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x55, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x38, 0x0a, 0x09, 0x6e, - 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, +var File_blockchainpb_broadcastpb_broadcastpb_proto protoreflect.FileDescriptor + +var file_blockchainpb_broadcastpb_broadcastpb_proto_rawDesc = []byte{ + 0x0a, 0x2a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, + 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x70, 0x62, 0x2f, 0x62, 0x72, 0x6f, 0x61, 0x64, + 0x63, 0x61, 0x73, 0x74, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0b, 0x62, 0x72, + 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x70, 0x62, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, + 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, + 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, + 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x51, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, + 0x34, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x12, 0x3f, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x20, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, - 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x3a, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, - 0x12, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x22, 0x42, 0x0a, 0x0f, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, - 0x63, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0xd0, 0xe4, 0x1d, 0x01, 0x42, 0x45, 0x5a, 0x43, 0x67, 0x69, - 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, - 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, - 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, - 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, - 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5a, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x3b, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, + 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, + 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0xc8, 0xe4, + 0x1d, 0x01, 0x22, 0x42, 0x0a, 0x0f, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x4d, 0x65, + 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x3a, 0x04, 0xd0, 0xe4, 0x1d, 0x01, 0x42, 0x41, 0x5a, 0x3f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, + 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x72, + 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x33, } var ( - file_blockchainpb_communicationpb_communicationpb_proto_rawDescOnce sync.Once - file_blockchainpb_communicationpb_communicationpb_proto_rawDescData = file_blockchainpb_communicationpb_communicationpb_proto_rawDesc + file_blockchainpb_broadcastpb_broadcastpb_proto_rawDescOnce sync.Once + file_blockchainpb_broadcastpb_broadcastpb_proto_rawDescData = file_blockchainpb_broadcastpb_broadcastpb_proto_rawDesc ) -func file_blockchainpb_communicationpb_communicationpb_proto_rawDescGZIP() []byte { - file_blockchainpb_communicationpb_communicationpb_proto_rawDescOnce.Do(func() { - file_blockchainpb_communicationpb_communicationpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_communicationpb_communicationpb_proto_rawDescData) +func file_blockchainpb_broadcastpb_broadcastpb_proto_rawDescGZIP() []byte { + file_blockchainpb_broadcastpb_broadcastpb_proto_rawDescOnce.Do(func() { + file_blockchainpb_broadcastpb_broadcastpb_proto_rawDescData = protoimpl.X.CompressGZIP(file_blockchainpb_broadcastpb_broadcastpb_proto_rawDescData) }) - return file_blockchainpb_communicationpb_communicationpb_proto_rawDescData + return file_blockchainpb_broadcastpb_broadcastpb_proto_rawDescData } -var file_blockchainpb_communicationpb_communicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 4) -var file_blockchainpb_communicationpb_communicationpb_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: communicationpb.Event - (*NewBlock)(nil), // 1: communicationpb.NewBlock - (*Message)(nil), // 2: communicationpb.Message - (*NewBlockMessage)(nil), // 3: communicationpb.NewBlockMessage +var file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_blockchainpb_broadcastpb_broadcastpb_proto_goTypes = []interface{}{ + (*Event)(nil), // 0: broadcastpb.Event + (*NewBlock)(nil), // 1: broadcastpb.NewBlock + (*Message)(nil), // 2: broadcastpb.Message + (*NewBlockMessage)(nil), // 3: broadcastpb.NewBlockMessage (*blockchainpb.Block)(nil), // 4: blockchainpb.Block } -var file_blockchainpb_communicationpb_communicationpb_proto_depIdxs = []int32{ - 1, // 0: communicationpb.Event.new_block:type_name -> communicationpb.NewBlock - 4, // 1: communicationpb.NewBlock.block:type_name -> blockchainpb.Block - 3, // 2: communicationpb.Message.new_block:type_name -> communicationpb.NewBlockMessage - 4, // 3: communicationpb.NewBlockMessage.block:type_name -> blockchainpb.Block +var file_blockchainpb_broadcastpb_broadcastpb_proto_depIdxs = []int32{ + 1, // 0: broadcastpb.Event.new_block:type_name -> broadcastpb.NewBlock + 4, // 1: broadcastpb.NewBlock.block:type_name -> blockchainpb.Block + 3, // 2: broadcastpb.Message.new_block:type_name -> broadcastpb.NewBlockMessage + 4, // 3: broadcastpb.NewBlockMessage.block:type_name -> blockchainpb.Block 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 @@ -323,13 +322,13 @@ var file_blockchainpb_communicationpb_communicationpb_proto_depIdxs = []int32{ 0, // [0:4] is the sub-list for field type_name } -func init() { file_blockchainpb_communicationpb_communicationpb_proto_init() } -func file_blockchainpb_communicationpb_communicationpb_proto_init() { - if File_blockchainpb_communicationpb_communicationpb_proto != nil { +func init() { file_blockchainpb_broadcastpb_broadcastpb_proto_init() } +func file_blockchainpb_broadcastpb_broadcastpb_proto_init() { + if File_blockchainpb_broadcastpb_broadcastpb_proto != nil { return } if !protoimpl.UnsafeEnabled { - file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Event); i { case 0: return &v.state @@ -341,7 +340,7 @@ func file_blockchainpb_communicationpb_communicationpb_proto_init() { return nil } } - file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NewBlock); i { case 0: return &v.state @@ -353,7 +352,7 @@ func file_blockchainpb_communicationpb_communicationpb_proto_init() { return nil } } - file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Message); i { case 0: return &v.state @@ -365,7 +364,7 @@ func file_blockchainpb_communicationpb_communicationpb_proto_init() { return nil } } - file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*NewBlockMessage); i { case 0: return &v.state @@ -378,28 +377,28 @@ func file_blockchainpb_communicationpb_communicationpb_proto_init() { } } } - file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[0].OneofWrappers = []interface{}{ + file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[0].OneofWrappers = []interface{}{ (*Event_NewBlock)(nil), } - file_blockchainpb_communicationpb_communicationpb_proto_msgTypes[2].OneofWrappers = []interface{}{ + file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes[2].OneofWrappers = []interface{}{ (*Message_NewBlock)(nil), } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_blockchainpb_communicationpb_communicationpb_proto_rawDesc, + RawDescriptor: file_blockchainpb_broadcastpb_broadcastpb_proto_rawDesc, NumEnums: 0, NumMessages: 4, NumExtensions: 0, NumServices: 0, }, - GoTypes: file_blockchainpb_communicationpb_communicationpb_proto_goTypes, - DependencyIndexes: file_blockchainpb_communicationpb_communicationpb_proto_depIdxs, - MessageInfos: file_blockchainpb_communicationpb_communicationpb_proto_msgTypes, + GoTypes: file_blockchainpb_broadcastpb_broadcastpb_proto_goTypes, + DependencyIndexes: file_blockchainpb_broadcastpb_broadcastpb_proto_depIdxs, + MessageInfos: file_blockchainpb_broadcastpb_broadcastpb_proto_msgTypes, }.Build() - File_blockchainpb_communicationpb_communicationpb_proto = out.File - file_blockchainpb_communicationpb_communicationpb_proto_rawDesc = nil - file_blockchainpb_communicationpb_communicationpb_proto_goTypes = nil - file_blockchainpb_communicationpb_communicationpb_proto_depIdxs = nil + File_blockchainpb_broadcastpb_broadcastpb_proto = out.File + file_blockchainpb_broadcastpb_broadcastpb_proto_rawDesc = nil + file_blockchainpb_broadcastpb_broadcastpb_proto_goTypes = nil + file_blockchainpb_broadcastpb_broadcastpb_proto_depIdxs = nil } diff --git a/pkg/pb/blockchainpb/communicationpb/communicationpb.pb.mir.go b/pkg/pb/blockchainpb/broadcastpb/broadcastpb.pb.mir.go similarity index 93% rename from pkg/pb/blockchainpb/communicationpb/communicationpb.pb.mir.go rename to pkg/pb/blockchainpb/broadcastpb/broadcastpb.pb.mir.go index 1eabded66..8edb387d7 100644 --- a/pkg/pb/blockchainpb/communicationpb/communicationpb.pb.mir.go +++ b/pkg/pb/blockchainpb/broadcastpb/broadcastpb.pb.mir.go @@ -1,6 +1,6 @@ // Code generated by Mir codegen. DO NOT EDIT. -package communicationpb +package broadcastpb import ( reflect "reflect" diff --git a/pkg/pb/blockchainpb/communicationpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/broadcastpb/dsl/emit.mir.go similarity index 78% rename from pkg/pb/blockchainpb/communicationpb/dsl/emit.mir.go rename to pkg/pb/blockchainpb/broadcastpb/dsl/emit.mir.go index 81c447f5f..8d917b5b6 100644 --- a/pkg/pb/blockchainpb/communicationpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/broadcastpb/dsl/emit.mir.go @@ -1,10 +1,10 @@ // Code generated by Mir codegen. DO NOT EDIT. -package communicationpbdsl +package broadcastpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/events" + events "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb/events" types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types "github.com/filecoin-project/mir/pkg/types" ) diff --git a/pkg/pb/blockchainpb/communicationpb/dsl/messages.mir.go b/pkg/pb/blockchainpb/broadcastpb/dsl/messages.mir.go similarity index 80% rename from pkg/pb/blockchainpb/communicationpb/dsl/messages.mir.go rename to pkg/pb/blockchainpb/broadcastpb/dsl/messages.mir.go index 10a2f2e7e..9864811b8 100644 --- a/pkg/pb/blockchainpb/communicationpb/dsl/messages.mir.go +++ b/pkg/pb/blockchainpb/broadcastpb/dsl/messages.mir.go @@ -1,10 +1,10 @@ // Code generated by Mir codegen. DO NOT EDIT. -package communicationpbdsl +package broadcastpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb/types" types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" dsl1 "github.com/filecoin-project/mir/pkg/pb/messagepb/dsl" types2 "github.com/filecoin-project/mir/pkg/pb/messagepb/types" @@ -14,7 +14,7 @@ import ( // Module-specific dsl functions for processing net messages. func UponMessageReceived[W types.Message_TypeWrapper[M], M any](m dsl.Module, handler func(from types1.NodeID, msg *M) error) { - dsl1.UponMessageReceived[*types2.Message_Communicationpb](m, func(from types1.NodeID, msg *types.Message) error { + dsl1.UponMessageReceived[*types2.Message_Broadcast](m, func(from types1.NodeID, msg *types.Message) error { w, ok := msg.Type.(W) if !ok { return nil diff --git a/pkg/pb/blockchainpb/communicationpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/broadcastpb/dsl/upon.mir.go similarity index 77% rename from pkg/pb/blockchainpb/communicationpb/dsl/upon.mir.go rename to pkg/pb/blockchainpb/broadcastpb/dsl/upon.mir.go index 3dcf3561c..0c844498d 100644 --- a/pkg/pb/blockchainpb/communicationpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/broadcastpb/dsl/upon.mir.go @@ -1,10 +1,10 @@ // Code generated by Mir codegen. DO NOT EDIT. -package communicationpbdsl +package broadcastpbdsl import ( dsl "github.com/filecoin-project/mir/pkg/dsl" - types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb/types" types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" ) @@ -12,7 +12,7 @@ import ( // Module-specific dsl functions for processing events. func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func(ev *Ev) error) { - dsl.UponMirEvent[*types1.Event_Communication](m, func(ev *types.Event) error { + dsl.UponMirEvent[*types1.Event_Broadcast](m, func(ev *types.Event) error { w, ok := ev.Type.(W) if !ok { return nil diff --git a/pkg/pb/blockchainpb/communicationpb/events/events.mir.go b/pkg/pb/blockchainpb/broadcastpb/events/events.mir.go similarity index 72% rename from pkg/pb/blockchainpb/communicationpb/events/events.mir.go rename to pkg/pb/blockchainpb/broadcastpb/events/events.mir.go index 355742e8c..2c2ff13d9 100644 --- a/pkg/pb/blockchainpb/communicationpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/broadcastpb/events/events.mir.go @@ -1,9 +1,9 @@ // Code generated by Mir codegen. DO NOT EDIT. -package communicationpbevents +package broadcastpbevents import ( - types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types2 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" @@ -12,8 +12,8 @@ import ( func NewBlock(destModule types.ModuleID, block *types1.Block) *types2.Event { return &types2.Event{ DestModule: destModule, - Type: &types2.Event_Communication{ - Communication: &types3.Event{ + Type: &types2.Event_Broadcast{ + Broadcast: &types3.Event{ Type: &types3.Event_NewBlock{ NewBlock: &types3.NewBlock{ Block: block, diff --git a/pkg/pb/blockchainpb/communicationpb/msgs/msgs.mir.go b/pkg/pb/blockchainpb/broadcastpb/msgs/msgs.mir.go similarity index 72% rename from pkg/pb/blockchainpb/communicationpb/msgs/msgs.mir.go rename to pkg/pb/blockchainpb/broadcastpb/msgs/msgs.mir.go index 53627c694..29929236d 100644 --- a/pkg/pb/blockchainpb/communicationpb/msgs/msgs.mir.go +++ b/pkg/pb/blockchainpb/broadcastpb/msgs/msgs.mir.go @@ -1,9 +1,9 @@ // Code generated by Mir codegen. DO NOT EDIT. -package communicationpbmsgs +package broadcastpbmsgs import ( - types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" types2 "github.com/filecoin-project/mir/pkg/pb/messagepb/types" types "github.com/filecoin-project/mir/pkg/types" @@ -12,8 +12,8 @@ import ( func NewBlockMessage(destModule types.ModuleID, block *types1.Block) *types2.Message { return &types2.Message{ DestModule: destModule, - Type: &types2.Message_Communicationpb{ - Communicationpb: &types3.Message{ + Type: &types2.Message_Broadcast{ + Broadcast: &types3.Message{ Type: &types3.Message_NewBlock{ NewBlock: &types3.NewBlockMessage{ Block: block, diff --git a/pkg/pb/blockchainpb/communicationpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/broadcastpb/oneof_interfaces.mir.go similarity index 94% rename from pkg/pb/blockchainpb/communicationpb/oneof_interfaces.mir.go rename to pkg/pb/blockchainpb/broadcastpb/oneof_interfaces.mir.go index 1664d6a4a..9d4f562ad 100644 --- a/pkg/pb/blockchainpb/communicationpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/broadcastpb/oneof_interfaces.mir.go @@ -1,6 +1,6 @@ // Code generated by Mir codegen. DO NOT EDIT. -package communicationpb +package broadcastpb type Event_Type = isEvent_Type diff --git a/pkg/pb/blockchainpb/communicationpb/types/types.mir.go b/pkg/pb/blockchainpb/broadcastpb/types/types.mir.go similarity index 58% rename from pkg/pb/blockchainpb/communicationpb/types/types.mir.go rename to pkg/pb/blockchainpb/broadcastpb/types/types.mir.go index fc82a6134..eeda98930 100644 --- a/pkg/pb/blockchainpb/communicationpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/broadcastpb/types/types.mir.go @@ -1,10 +1,10 @@ // Code generated by Mir codegen. DO NOT EDIT. -package communicationpbtypes +package broadcastpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + broadcastpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb" types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" ) @@ -16,7 +16,7 @@ type Event struct { type Event_Type interface { mirreflect.GeneratedType isEvent_Type() - Pb() communicationpb.Event_Type + Pb() broadcastpb.Event_Type } type Event_TypeWrapper[T any] interface { @@ -24,12 +24,12 @@ type Event_TypeWrapper[T any] interface { Unwrap() *T } -func Event_TypeFromPb(pb communicationpb.Event_Type) Event_Type { +func Event_TypeFromPb(pb broadcastpb.Event_Type) Event_Type { if pb == nil { return nil } switch pb := pb.(type) { - case *communicationpb.Event_NewBlock: + case *broadcastpb.Event_NewBlock: return &Event_NewBlock{NewBlock: NewBlockFromPb(pb.NewBlock)} } return nil @@ -45,21 +45,21 @@ func (w *Event_NewBlock) Unwrap() *NewBlock { return w.NewBlock } -func (w *Event_NewBlock) Pb() communicationpb.Event_Type { +func (w *Event_NewBlock) Pb() broadcastpb.Event_Type { if w == nil { return nil } if w.NewBlock == nil { - return &communicationpb.Event_NewBlock{} + return &broadcastpb.Event_NewBlock{} } - return &communicationpb.Event_NewBlock{NewBlock: (w.NewBlock).Pb()} + return &broadcastpb.Event_NewBlock{NewBlock: (w.NewBlock).Pb()} } func (*Event_NewBlock) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.Event_NewBlock]()} + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*broadcastpb.Event_NewBlock]()} } -func EventFromPb(pb *communicationpb.Event) *Event { +func EventFromPb(pb *broadcastpb.Event) *Event { if pb == nil { return nil } @@ -68,11 +68,11 @@ func EventFromPb(pb *communicationpb.Event) *Event { } } -func (m *Event) Pb() *communicationpb.Event { +func (m *Event) Pb() *broadcastpb.Event { if m == nil { return nil } - pbMessage := &communicationpb.Event{} + pbMessage := &broadcastpb.Event{} { if m.Type != nil { pbMessage.Type = (m.Type).Pb() @@ -83,14 +83,14 @@ func (m *Event) Pb() *communicationpb.Event { } func (*Event) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.Event]()} + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*broadcastpb.Event]()} } type NewBlock struct { Block *types.Block } -func NewBlockFromPb(pb *communicationpb.NewBlock) *NewBlock { +func NewBlockFromPb(pb *broadcastpb.NewBlock) *NewBlock { if pb == nil { return nil } @@ -99,11 +99,11 @@ func NewBlockFromPb(pb *communicationpb.NewBlock) *NewBlock { } } -func (m *NewBlock) Pb() *communicationpb.NewBlock { +func (m *NewBlock) Pb() *broadcastpb.NewBlock { if m == nil { return nil } - pbMessage := &communicationpb.NewBlock{} + pbMessage := &broadcastpb.NewBlock{} { if m.Block != nil { pbMessage.Block = (m.Block).Pb() @@ -114,7 +114,7 @@ func (m *NewBlock) Pb() *communicationpb.NewBlock { } func (*NewBlock) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.NewBlock]()} + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*broadcastpb.NewBlock]()} } type Message struct { @@ -124,7 +124,7 @@ type Message struct { type Message_Type interface { mirreflect.GeneratedType isMessage_Type() - Pb() communicationpb.Message_Type + Pb() broadcastpb.Message_Type } type Message_TypeWrapper[T any] interface { @@ -132,12 +132,12 @@ type Message_TypeWrapper[T any] interface { Unwrap() *T } -func Message_TypeFromPb(pb communicationpb.Message_Type) Message_Type { +func Message_TypeFromPb(pb broadcastpb.Message_Type) Message_Type { if pb == nil { return nil } switch pb := pb.(type) { - case *communicationpb.Message_NewBlock: + case *broadcastpb.Message_NewBlock: return &Message_NewBlock{NewBlock: NewBlockMessageFromPb(pb.NewBlock)} } return nil @@ -153,21 +153,21 @@ func (w *Message_NewBlock) Unwrap() *NewBlockMessage { return w.NewBlock } -func (w *Message_NewBlock) Pb() communicationpb.Message_Type { +func (w *Message_NewBlock) Pb() broadcastpb.Message_Type { if w == nil { return nil } if w.NewBlock == nil { - return &communicationpb.Message_NewBlock{} + return &broadcastpb.Message_NewBlock{} } - return &communicationpb.Message_NewBlock{NewBlock: (w.NewBlock).Pb()} + return &broadcastpb.Message_NewBlock{NewBlock: (w.NewBlock).Pb()} } func (*Message_NewBlock) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.Message_NewBlock]()} + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*broadcastpb.Message_NewBlock]()} } -func MessageFromPb(pb *communicationpb.Message) *Message { +func MessageFromPb(pb *broadcastpb.Message) *Message { if pb == nil { return nil } @@ -176,11 +176,11 @@ func MessageFromPb(pb *communicationpb.Message) *Message { } } -func (m *Message) Pb() *communicationpb.Message { +func (m *Message) Pb() *broadcastpb.Message { if m == nil { return nil } - pbMessage := &communicationpb.Message{} + pbMessage := &broadcastpb.Message{} { if m.Type != nil { pbMessage.Type = (m.Type).Pb() @@ -191,14 +191,14 @@ func (m *Message) Pb() *communicationpb.Message { } func (*Message) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.Message]()} + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*broadcastpb.Message]()} } type NewBlockMessage struct { Block *types.Block } -func NewBlockMessageFromPb(pb *communicationpb.NewBlockMessage) *NewBlockMessage { +func NewBlockMessageFromPb(pb *broadcastpb.NewBlockMessage) *NewBlockMessage { if pb == nil { return nil } @@ -207,11 +207,11 @@ func NewBlockMessageFromPb(pb *communicationpb.NewBlockMessage) *NewBlockMessage } } -func (m *NewBlockMessage) Pb() *communicationpb.NewBlockMessage { +func (m *NewBlockMessage) Pb() *broadcastpb.NewBlockMessage { if m == nil { return nil } - pbMessage := &communicationpb.NewBlockMessage{} + pbMessage := &broadcastpb.NewBlockMessage{} { if m.Block != nil { pbMessage.Block = (m.Block).Pb() @@ -222,5 +222,5 @@ func (m *NewBlockMessage) Pb() *communicationpb.NewBlockMessage { } func (*NewBlockMessage) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*communicationpb.NewBlockMessage]()} + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*broadcastpb.NewBlockMessage]()} } diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go index dc1cee8e6..8ff753256 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/emit.mir.go @@ -11,6 +11,6 @@ import ( // Module-specific dsl functions for emitting events. -func SyncRequest(m dsl.Module, destModule types.ModuleID, orphanBlock *types1.Block, leaveIds []uint64) { - dsl.EmitMirEvent(m, events.SyncRequest(destModule, orphanBlock, leaveIds)) +func SyncRequest(m dsl.Module, destModule types.ModuleID, orphanBlock *types1.Block, leaveNodeIds []uint64) { + dsl.EmitMirEvent(m, events.SyncRequest(destModule, orphanBlock, leaveNodeIds)) } diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go index 075e9a670..f7b614d7e 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/messages.mir.go @@ -24,9 +24,9 @@ func UponMessageReceived[W types.Message_TypeWrapper[M], M any](m dsl.Module, ha }) } -func UponChainRequestReceived(m dsl.Module, handler func(from types1.NodeID, requestId string, blockId uint64, leaveIds []uint64) error) { +func UponChainRequestReceived(m dsl.Module, handler func(from types1.NodeID, requestId string, blockId uint64, leaveNodeIds []uint64) error) { UponMessageReceived[*types.Message_ChainRequest](m, func(from types1.NodeID, msg *types.ChainRequest) error { - return handler(from, msg.RequestId, msg.BlockId, msg.LeaveIds) + return handler(from, msg.RequestId, msg.BlockId, msg.LeaveNodeIds) }) } diff --git a/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go index c2d66c8f8..043306635 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/dsl/upon.mir.go @@ -22,8 +22,8 @@ func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func }) } -func UponSyncRequest(m dsl.Module, handler func(orphanBlock *types2.Block, leaveIds []uint64) error) { +func UponSyncRequest(m dsl.Module, handler func(orphanBlock *types2.Block, leaveNodeIds []uint64) error) { UponEvent[*types.Event_SyncRequest](m, func(ev *types.SyncRequest) error { - return handler(ev.OrphanBlock, ev.LeaveIds) + return handler(ev.OrphanBlock, ev.LeaveNodeIds) }) } diff --git a/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go b/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go index b94a578af..f41dadfb4 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/events/events.mir.go @@ -9,15 +9,15 @@ import ( types "github.com/filecoin-project/mir/pkg/types" ) -func SyncRequest(destModule types.ModuleID, orphanBlock *types1.Block, leaveIds []uint64) *types2.Event { +func SyncRequest(destModule types.ModuleID, orphanBlock *types1.Block, leaveNodeIds []uint64) *types2.Event { return &types2.Event{ DestModule: destModule, Type: &types2.Event_Synchronizer{ Synchronizer: &types3.Event{ Type: &types3.Event_SyncRequest{ SyncRequest: &types3.SyncRequest{ - OrphanBlock: orphanBlock, - LeaveIds: leaveIds, + OrphanBlock: orphanBlock, + LeaveNodeIds: leaveNodeIds, }, }, }, diff --git a/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go b/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go index bff86f95e..058045720 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/msgs/msgs.mir.go @@ -9,16 +9,16 @@ import ( types "github.com/filecoin-project/mir/pkg/types" ) -func ChainRequest(destModule types.ModuleID, requestId string, blockId uint64, leaveIds []uint64) *types1.Message { +func ChainRequest(destModule types.ModuleID, requestId string, blockId uint64, leaveNodeIds []uint64) *types1.Message { return &types1.Message{ DestModule: destModule, Type: &types1.Message_Synchronizer{ Synchronizer: &types2.Message{ Type: &types2.Message_ChainRequest{ ChainRequest: &types2.ChainRequest{ - RequestId: requestId, - BlockId: blockId, - LeaveIds: leaveIds, + RequestId: requestId, + BlockId: blockId, + LeaveNodeIds: leaveNodeIds, }, }, }, diff --git a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go index c145b283b..6e7d53a86 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go +++ b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go @@ -95,8 +95,8 @@ type SyncRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - OrphanBlock *blockchainpb.Block `protobuf:"bytes,1,opt,name=orphan_block,json=orphanBlock,proto3" json:"orphan_block,omitempty"` - LeaveIds []uint64 `protobuf:"varint,2,rep,packed,name=leave_ids,json=leaveIds,proto3" json:"leave_ids,omitempty"` + OrphanBlock *blockchainpb.Block `protobuf:"bytes,1,opt,name=orphan_block,json=orphanBlock,proto3" json:"orphan_block,omitempty"` + LeaveNodeIds []uint64 `protobuf:"varint,2,rep,packed,name=leave_node_ids,json=leaveNodeIds,proto3" json:"leave_node_ids,omitempty"` } func (x *SyncRequest) Reset() { @@ -138,9 +138,9 @@ func (x *SyncRequest) GetOrphanBlock() *blockchainpb.Block { return nil } -func (x *SyncRequest) GetLeaveIds() []uint64 { +func (x *SyncRequest) GetLeaveNodeIds() []uint64 { if x != nil { - return x.LeaveIds + return x.LeaveNodeIds } return nil } @@ -231,9 +231,9 @@ type ChainRequest struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - BlockId uint64 `protobuf:"varint,2,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` - LeaveIds []uint64 `protobuf:"varint,3,rep,packed,name=leave_ids,json=leaveIds,proto3" json:"leave_ids,omitempty"` + RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` + BlockId uint64 `protobuf:"varint,2,opt,name=block_id,json=blockId,proto3" json:"block_id,omitempty"` + LeaveNodeIds []uint64 `protobuf:"varint,3,rep,packed,name=leave_node_ids,json=leaveNodeIds,proto3" json:"leave_node_ids,omitempty"` } func (x *ChainRequest) Reset() { @@ -282,9 +282,9 @@ func (x *ChainRequest) GetBlockId() uint64 { return 0 } -func (x *ChainRequest) GetLeaveIds() []uint64 { +func (x *ChainRequest) GetLeaveNodeIds() []uint64 { if x != nil { - return x.LeaveIds + return x.LeaveNodeIds } return nil } @@ -370,44 +370,45 @@ var file_blockchainpb_synchronizerpb_synchronizerpb_proto_rawDesc = []byte{ 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x73, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, - 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x68, + 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x71, 0x0a, 0x0b, 0x53, 0x79, 0x6e, 0x63, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x36, 0x0a, 0x0c, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0b, 0x6f, 0x72, 0x70, 0x68, 0x61, 0x6e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x69, - 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x49, - 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xaa, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x79, - 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x0e, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x48, 0x00, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x3a, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, - 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x22, 0x6b, 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x6e, + 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0c, 0x6c, + 0x65, 0x61, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, + 0x01, 0x22, 0xaa, 0x01, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x43, 0x0a, + 0x0d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, + 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x12, 0x46, 0x0a, 0x0e, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x73, 0x79, 0x6e, + 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x43, 0x68, 0x61, 0x69, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x3a, 0x04, 0xc8, 0xe4, 0x1d, 0x01, + 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x22, 0x74, + 0x0a, 0x0c, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, + 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, + 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x0e, 0x6c, 0x65, 0x61, 0x76, + 0x65, 0x5f, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x04, + 0x52, 0x0c, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x73, 0x3a, 0x04, + 0xd0, 0xe4, 0x1d, 0x01, 0x22, 0x75, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x49, 0x64, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, - 0x1b, 0x0a, 0x09, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x04, 0x52, 0x08, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x49, 0x64, 0x73, 0x3a, 0x04, 0xd0, 0xe4, - 0x1d, 0x01, 0x22, 0x75, 0x0a, 0x0d, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x3a, 0x04, 0xd0, 0xe4, 0x1d, 0x01, 0x42, 0x44, 0x5a, 0x42, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, - 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, - 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, - 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x73, 0x74, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x05, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0xd0, 0xe4, 0x1d, 0x01, 0x42, 0x44, 0x5a, 0x42, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, + 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, + 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, + 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go index a5dcfee8b..9316ff3fd 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/synchronizerpb/types/types.mir.go @@ -89,8 +89,8 @@ func (*Event) MirReflect() mirreflect.Type { } type SyncRequest struct { - OrphanBlock *types.Block - LeaveIds []uint64 + OrphanBlock *types.Block + LeaveNodeIds []uint64 } func SyncRequestFromPb(pb *synchronizerpb.SyncRequest) *SyncRequest { @@ -98,8 +98,8 @@ func SyncRequestFromPb(pb *synchronizerpb.SyncRequest) *SyncRequest { return nil } return &SyncRequest{ - OrphanBlock: types.BlockFromPb(pb.OrphanBlock), - LeaveIds: pb.LeaveIds, + OrphanBlock: types.BlockFromPb(pb.OrphanBlock), + LeaveNodeIds: pb.LeaveNodeIds, } } @@ -112,7 +112,7 @@ func (m *SyncRequest) Pb() *synchronizerpb.SyncRequest { if m.OrphanBlock != nil { pbMessage.OrphanBlock = (m.OrphanBlock).Pb() } - pbMessage.LeaveIds = m.LeaveIds + pbMessage.LeaveNodeIds = m.LeaveNodeIds } return pbMessage @@ -226,9 +226,9 @@ func (*Message) MirReflect() mirreflect.Type { } type ChainRequest struct { - RequestId string - BlockId uint64 - LeaveIds []uint64 + RequestId string + BlockId uint64 + LeaveNodeIds []uint64 } func ChainRequestFromPb(pb *synchronizerpb.ChainRequest) *ChainRequest { @@ -236,9 +236,9 @@ func ChainRequestFromPb(pb *synchronizerpb.ChainRequest) *ChainRequest { return nil } return &ChainRequest{ - RequestId: pb.RequestId, - BlockId: pb.BlockId, - LeaveIds: pb.LeaveIds, + RequestId: pb.RequestId, + BlockId: pb.BlockId, + LeaveNodeIds: pb.LeaveNodeIds, } } @@ -250,7 +250,7 @@ func (m *ChainRequest) Pb() *synchronizerpb.ChainRequest { { pbMessage.RequestId = m.RequestId pbMessage.BlockId = m.BlockId - pbMessage.LeaveIds = m.LeaveIds + pbMessage.LeaveNodeIds = m.LeaveNodeIds } return pbMessage diff --git a/pkg/pb/eventpb/eventpb.pb.go b/pkg/pb/eventpb/eventpb.pb.go index 03954714f..a5c654c2d 100644 --- a/pkg/pb/eventpb/eventpb.pb.go +++ b/pkg/pb/eventpb/eventpb.pb.go @@ -14,7 +14,7 @@ import ( bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" applicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" - communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + broadcastpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb" interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" @@ -76,7 +76,7 @@ type Event struct { // *Event_PingPong // *Event_Bcm // *Event_Miner - // *Event_Communication + // *Event_Broadcast // *Event_Synchronizer // *Event_Application // *Event_Bcinterceptor @@ -287,9 +287,9 @@ func (x *Event) GetMiner() *minerpb.Event { return nil } -func (x *Event) GetCommunication() *communicationpb.Event { - if x, ok := x.GetType().(*Event_Communication); ok { - return x.Communication +func (x *Event) GetBroadcast() *broadcastpb.Event { + if x, ok := x.GetType().(*Event_Broadcast); ok { + return x.Broadcast } return nil } @@ -436,8 +436,8 @@ type Event_Miner struct { Miner *minerpb.Event `protobuf:"bytes,202,opt,name=miner,proto3,oneof"` } -type Event_Communication struct { - Communication *communicationpb.Event `protobuf:"bytes,203,opt,name=communication,proto3,oneof"` +type Event_Broadcast struct { + Broadcast *broadcastpb.Event `protobuf:"bytes,203,opt,name=broadcast,proto3,oneof"` } type Event_Synchronizer struct { @@ -508,7 +508,7 @@ func (*Event_Bcm) isEvent_Type() {} func (*Event_Miner) isEvent_Type() {} -func (*Event_Communication) isEvent_Type() {} +func (*Event_Broadcast) isEvent_Type() {} func (*Event_Synchronizer) isEvent_Type() {} @@ -869,174 +869,173 @@ var file_eventpb_eventpb_proto_rawDesc = []byte{ 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x32, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, - 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, - 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, - 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, - 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, - 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xbe, 0x0c, 0x0a, 0x05, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, - 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, - 0x44, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a, - 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, - 0x69, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, - 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x05, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x12, - 0x29, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, - 0x48, 0x00, 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, 0x12, 0x20, 0x0a, 0x03, 0x62, 0x63, - 0x62, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x63, 0x62, 0x70, 0x62, 0x2e, - 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, 0x62, 0x12, 0x2c, 0x0a, 0x07, - 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, - 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, - 0x00, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x12, 0x3b, 0x0a, 0x0c, 0x61, 0x76, - 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x70, - 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, - 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, 0x2d, 0x0a, 0x08, 0x62, 0x61, 0x74, 0x63, 0x68, - 0x5f, 0x64, 0x62, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x62, 0x61, 0x74, 0x63, - 0x68, 0x64, 0x62, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x62, - 0x61, 0x74, 0x63, 0x68, 0x44, 0x62, 0x12, 0x3c, 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, - 0x66, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, - 0x62, 0x61, 0x74, 0x63, 0x68, 0x66, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x62, 0x61, 0x74, 0x63, 0x68, 0x46, 0x65, 0x74, - 0x63, 0x68, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x0d, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x63, - 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x68, - 0x72, 0x65, 0x73, 0x68, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x43, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x12, 0x35, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x63, - 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2c, 0x0a, 0x07, 0x66, 0x61, 0x63, - 0x74, 0x6f, 0x72, 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x66, 0x61, 0x63, - 0x74, 0x6f, 0x72, 0x79, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, - 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x20, 0x0a, 0x03, 0x69, 0x73, 0x73, 0x18, 0x13, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x69, 0x73, 0x73, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x69, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, - 0x6f, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x12, 0x20, 0x0a, 0x03, 0x61, 0x70, 0x70, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0c, 0x2e, 0x61, 0x70, 0x70, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, - 0x03, 0x61, 0x70, 0x70, 0x12, 0x32, 0x0a, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, - 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, - 0x6f, 0x72, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x74, - 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x68, 0x6b, 0x70, - 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x16, 0x2e, 0x63, 0x68, 0x6b, 0x70, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, - 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x63, 0x68, 0x6b, 0x70, - 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x12, 0x42, 0x0a, 0x0f, 0x70, 0x70, 0x72, - 0x65, 0x70, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x61, 0x64, 0x74, 0x6f, 0x72, 0x18, 0x19, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x70, 0x72, 0x65, 0x70, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, - 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, - 0x70, 0x72, 0x65, 0x70, 0x56, 0x61, 0x6c, 0x69, 0x61, 0x64, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, - 0x09, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, 0x6e, 0x67, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, - 0x12, 0x21, 0x0a, 0x03, 0x62, 0x63, 0x6d, 0x18, 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, - 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, - 0x62, 0x63, 0x6d, 0x12, 0x27, 0x0a, 0x05, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x18, 0xca, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, - 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x05, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x12, 0x3f, 0x0a, 0x0d, - 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xcb, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, - 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, - 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x18, 0xcc, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, - 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x73, - 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x0b, 0x61, - 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, - 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x0d, 0x62, 0x63, 0x69, 0x6e, 0x74, 0x65, - 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x18, 0xd2, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0d, 0x62, 0x63, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, - 0x65, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x45, 0x0a, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x18, 0xad, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x74, - 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, - 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x18, 0xae, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, - 0x48, 0x00, 0x52, 0x0b, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x12, - 0x2b, 0x0a, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0xaf, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x10, 0x2e, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x73, 0x74, - 0x65, 0x72, 0x48, 0x00, 0x52, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x04, - 0x6e, 0x65, 0x78, 0x74, 0x18, 0x90, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x04, 0x90, 0xa6, 0x1d, - 0x01, 0x52, 0x04, 0x6e, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x88, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, - 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x0c, 0x0a, 0x04, 0x49, - 0x6e, 0x69, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xc6, 0x01, 0x0a, 0x0a, 0x54, 0x69, - 0x6d, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, - 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, 0x05, - 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x2e, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, 0x72, - 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x47, 0x0a, 0x0f, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, - 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, - 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, - 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0e, - 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x3a, 0x04, - 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, - 0x1d, 0x01, 0x22, 0x9e, 0x01, 0x0a, 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, - 0x79, 0x12, 0x36, 0x0a, 0x0f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x64, - 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, - 0x74, 0x73, 0x54, 0x6f, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x70, 0x62, + 0x2f, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x1a, 0x30, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, + 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2f, + 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, + 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x70, 0x62, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, + 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, + 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x22, 0xb2, 0x0c, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x0b, + 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x74, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0d, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x49, 0x6e, + 0x69, 0x74, 0x48, 0x00, 0x52, 0x04, 0x69, 0x6e, 0x69, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x74, 0x69, + 0x6d, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, + 0x52, 0x05, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x06, 0x68, 0x61, 0x73, 0x68, 0x65, + 0x72, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x68, 0x61, 0x73, 0x68, 0x65, 0x72, + 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x06, 0x68, 0x61, 0x73, 0x68, + 0x65, 0x72, 0x12, 0x20, 0x0a, 0x03, 0x62, 0x63, 0x62, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0c, 0x2e, 0x62, 0x63, 0x62, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, + 0x03, 0x62, 0x63, 0x62, 0x12, 0x2c, 0x0a, 0x07, 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x18, + 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x70, + 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x6d, 0x65, 0x6d, 0x70, 0x6f, + 0x6f, 0x6c, 0x12, 0x3b, 0x0a, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, + 0x74, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x61, 0x76, 0x61, 0x69, 0x6c, + 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, + 0x00, 0x52, 0x0c, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x12, + 0x2d, 0x0a, 0x08, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x64, 0x62, 0x18, 0x0e, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x10, 0x2e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x64, 0x62, 0x70, 0x62, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x62, 0x61, 0x74, 0x63, 0x68, 0x44, 0x62, 0x12, 0x3c, + 0x0a, 0x0d, 0x62, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, + 0x0f, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x61, 0x74, 0x63, 0x68, 0x66, 0x65, 0x74, + 0x63, 0x68, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, + 0x62, 0x61, 0x74, 0x63, 0x68, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x3c, 0x0a, 0x0d, + 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x10, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x74, 0x68, 0x72, 0x65, 0x73, 0x68, 0x63, 0x72, 0x79, 0x70, + 0x74, 0x6f, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x74, 0x68, + 0x72, 0x65, 0x73, 0x68, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x12, 0x35, 0x0a, 0x0a, 0x63, 0x68, + 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, + 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x12, 0x2c, 0x0a, 0x07, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x12, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x70, 0x62, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x66, 0x61, 0x63, 0x74, 0x6f, 0x72, 0x79, 0x12, + 0x20, 0x0a, 0x03, 0x69, 0x73, 0x73, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x69, + 0x73, 0x73, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x69, 0x73, + 0x73, 0x12, 0x2c, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x18, 0x14, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, + 0x29, 0x0a, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x0f, 0x2e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x48, 0x00, 0x52, 0x06, 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0x12, 0x20, 0x0a, 0x03, 0x61, 0x70, + 0x70, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x61, 0x70, 0x70, 0x70, 0x62, 0x2e, + 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x61, 0x70, 0x70, 0x12, 0x32, 0x0a, 0x09, + 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x17, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, + 0x12, 0x3f, 0x0a, 0x0e, 0x63, 0x68, 0x6b, 0x70, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, + 0x6f, 0x72, 0x18, 0x18, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x63, 0x68, 0x6b, 0x70, 0x76, + 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x48, 0x00, 0x52, 0x0d, 0x63, 0x68, 0x6b, 0x70, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, + 0x72, 0x12, 0x42, 0x0a, 0x0f, 0x70, 0x70, 0x72, 0x65, 0x70, 0x5f, 0x76, 0x61, 0x6c, 0x69, 0x61, + 0x64, 0x74, 0x6f, 0x72, 0x18, 0x19, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x70, 0x72, + 0x65, 0x70, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x70, 0x72, 0x65, 0x70, 0x56, 0x61, 0x6c, 0x69, + 0x61, 0x64, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x09, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x70, 0x6f, + 0x6e, 0x67, 0x18, 0xc8, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x69, 0x6e, 0x67, + 0x70, 0x6f, 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x08, + 0x70, 0x69, 0x6e, 0x67, 0x50, 0x6f, 0x6e, 0x67, 0x12, 0x21, 0x0a, 0x03, 0x62, 0x63, 0x6d, 0x18, + 0xc9, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0c, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, 0x6d, 0x12, 0x27, 0x0a, 0x05, 0x6d, + 0x69, 0x6e, 0x65, 0x72, 0x18, 0xca, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x6d, 0x69, + 0x6e, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x05, 0x6d, + 0x69, 0x6e, 0x65, 0x72, 0x12, 0x33, 0x0a, 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, + 0x74, 0x18, 0xcb, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x62, 0x72, 0x6f, 0x61, 0x64, + 0x63, 0x61, 0x73, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x09, + 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x12, 0x3c, 0x0a, 0x0c, 0x73, 0x79, 0x6e, + 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x18, 0xcc, 0x01, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x15, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, + 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, + 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x12, 0x39, 0x0a, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0xcd, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x0b, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x12, 0x3d, 0x0a, 0x0d, 0x62, 0x63, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, + 0x74, 0x6f, 0x72, 0x18, 0xd2, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x69, 0x6e, 0x74, + 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, 0x72, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x48, 0x00, 0x52, 0x0d, 0x62, 0x63, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x63, 0x65, 0x70, 0x74, 0x6f, + 0x72, 0x12, 0x45, 0x0a, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x18, 0xad, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x69, + 0x6e, 0x67, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x69, + 0x6e, 0x67, 0x53, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x12, 0x41, 0x0a, 0x0b, 0x74, 0x65, 0x73, 0x74, + 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x18, 0xae, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x55, 0x49, 0x6e, 0x74, 0x36, 0x34, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x48, 0x00, 0x52, 0x0b, + 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x55, 0x69, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x06, 0x74, + 0x65, 0x73, 0x74, 0x65, 0x72, 0x18, 0xaf, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x74, + 0x65, 0x73, 0x74, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x54, 0x65, 0x73, 0x74, 0x65, 0x72, 0x48, 0x00, + 0x52, 0x06, 0x74, 0x65, 0x73, 0x74, 0x65, 0x72, 0x12, 0x29, 0x0a, 0x04, 0x6e, 0x65, 0x78, 0x74, + 0x18, 0x90, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x42, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x52, 0x04, 0x6e, + 0x65, 0x78, 0x74, 0x3a, 0x04, 0x88, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x0c, 0x0a, 0x04, 0x49, 0x6e, 0x69, 0x74, 0x3a, + 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xc6, 0x01, 0x0a, 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2b, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, + 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x48, 0x00, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, + 0x79, 0x12, 0x2e, 0x0a, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x14, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, + 0x72, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x48, 0x00, 0x52, 0x06, 0x72, 0x65, 0x70, 0x65, 0x61, + 0x74, 0x12, 0x47, 0x0a, 0x0f, 0x67, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x5f, 0x63, 0x6f, 0x6c, + 0x6c, 0x65, 0x63, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, + 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x67, 0x61, 0x72, 0x62, + 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, + 0x42, 0x0c, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x9e, + 0x01, 0x0a, 0x0a, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x36, 0x0a, + 0x0f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x64, 0x65, 0x6c, 0x61, 0x79, + 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, + 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0d, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, + 0x44, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, + 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, + 0x90, 0x02, 0x0a, 0x0b, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, + 0x38, 0x0a, 0x10, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, 0x5f, 0x72, 0x65, 0x70, + 0x65, 0x61, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x65, 0x76, 0x65, 0x6e, + 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x65, 0x76, 0x65, 0x6e, 0x74, + 0x73, 0x54, 0x6f, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x52, 0x0a, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x44, 0x75, - 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x3a, 0x04, 0x98, - 0xa6, 0x1d, 0x01, 0x22, 0x90, 0x02, 0x0a, 0x0b, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x52, 0x65, 0x70, - 0x65, 0x61, 0x74, 0x12, 0x38, 0x0a, 0x10, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x5f, 0x74, 0x6f, - 0x5f, 0x72, 0x65, 0x70, 0x65, 0x61, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, - 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x52, 0x0e, 0x65, - 0x76, 0x65, 0x6e, 0x74, 0x73, 0x54, 0x6f, 0x52, 0x65, 0x70, 0x65, 0x61, 0x74, 0x12, 0x52, 0x0a, - 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x3c, 0x82, 0xa6, - 0x1d, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, - 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, - 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, - 0x79, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, - 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, - 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, - 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x8a, 0x01, 0x0a, 0x13, 0x54, 0x69, 0x6d, 0x65, 0x72, - 0x47, 0x61, 0x72, 0x62, 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x12, 0x6d, - 0x0a, 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, - 0x78, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, - 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, - 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, - 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, - 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, - 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, - 0xa6, 0x1d, 0x01, 0x42, 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, - 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x65, 0x76, - 0x65, 0x6e, 0x74, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x05, 0x64, 0x65, 0x6c, 0x61, 0x79, 0x12, 0x6d, 0x0a, + 0x0f, 0x72, 0x65, 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, + 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, + 0x74, 0x72, 0x61, 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, + 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, + 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x22, 0x8a, 0x01, 0x0a, 0x13, 0x54, 0x69, 0x6d, 0x65, 0x72, 0x47, 0x61, 0x72, 0x62, + 0x61, 0x67, 0x65, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x12, 0x6d, 0x0a, 0x0f, 0x72, 0x65, + 0x74, 0x65, 0x6e, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xa6, 0x1d, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x72, 0x61, + 0x6e, 0x74, 0x6f, 0x72, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x52, 0x65, 0x74, 0x65, 0x6e, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x52, 0x0e, 0x72, 0x65, 0x74, 0x65, 0x6e, + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x64, 0x65, 0x78, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, + 0x30, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, + 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, + 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x70, + 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1078,7 +1077,7 @@ var file_eventpb_eventpb_proto_goTypes = []interface{}{ (*pingpongpb.Event)(nil), // 22: pingpongpb.Event (*bcmpb.Event)(nil), // 23: bcmpb.Event (*minerpb.Event)(nil), // 24: minerpb.Event - (*communicationpb.Event)(nil), // 25: communicationpb.Event + (*broadcastpb.Event)(nil), // 25: broadcastpb.Event (*synchronizerpb.Event)(nil), // 26: synchronizerpb.Event (*applicationpb.Event)(nil), // 27: applicationpb.Event (*interceptorpb.Event)(nil), // 28: interceptorpb.Event @@ -1108,7 +1107,7 @@ var file_eventpb_eventpb_proto_depIdxs = []int32{ 22, // 18: eventpb.Event.ping_pong:type_name -> pingpongpb.Event 23, // 19: eventpb.Event.bcm:type_name -> bcmpb.Event 24, // 20: eventpb.Event.miner:type_name -> minerpb.Event - 25, // 21: eventpb.Event.communication:type_name -> communicationpb.Event + 25, // 21: eventpb.Event.broadcast:type_name -> broadcastpb.Event 26, // 22: eventpb.Event.synchronizer:type_name -> synchronizerpb.Event 27, // 23: eventpb.Event.application:type_name -> applicationpb.Event 28, // 24: eventpb.Event.bcinterceptor:type_name -> interceptorpb.Event @@ -1229,7 +1228,7 @@ func file_eventpb_eventpb_proto_init() { (*Event_PingPong)(nil), (*Event_Bcm)(nil), (*Event_Miner)(nil), - (*Event_Communication)(nil), + (*Event_Broadcast)(nil), (*Event_Synchronizer)(nil), (*Event_Application)(nil), (*Event_Bcinterceptor)(nil), diff --git a/pkg/pb/eventpb/eventpb.pb.mir.go b/pkg/pb/eventpb/eventpb.pb.mir.go index b3dd73d86..f11c03e13 100644 --- a/pkg/pb/eventpb/eventpb.pb.mir.go +++ b/pkg/pb/eventpb/eventpb.pb.mir.go @@ -29,7 +29,7 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_PingPong)(nil)), reflect.TypeOf((*Event_Bcm)(nil)), reflect.TypeOf((*Event_Miner)(nil)), - reflect.TypeOf((*Event_Communication)(nil)), + reflect.TypeOf((*Event_Broadcast)(nil)), reflect.TypeOf((*Event_Synchronizer)(nil)), reflect.TypeOf((*Event_Application)(nil)), reflect.TypeOf((*Event_Bcinterceptor)(nil)), diff --git a/pkg/pb/eventpb/oneof_interfaces.mir.go b/pkg/pb/eventpb/oneof_interfaces.mir.go index 0cc24995e..ce2ad0918 100644 --- a/pkg/pb/eventpb/oneof_interfaces.mir.go +++ b/pkg/pb/eventpb/oneof_interfaces.mir.go @@ -10,7 +10,7 @@ import ( bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" applicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" bcmpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" - communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + broadcastpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb" interceptorpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb" minerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" @@ -121,8 +121,8 @@ func (w *Event_Miner) Unwrap() *minerpb.Event { return w.Miner } -func (w *Event_Communication) Unwrap() *communicationpb.Event { - return w.Communication +func (w *Event_Broadcast) Unwrap() *broadcastpb.Event { + return w.Broadcast } func (w *Event_Synchronizer) Unwrap() *synchronizerpb.Event { diff --git a/pkg/pb/eventpb/types/types.mir.go b/pkg/pb/eventpb/types/types.mir.go index be46f7266..e71d8c8de 100644 --- a/pkg/pb/eventpb/types/types.mir.go +++ b/pkg/pb/eventpb/types/types.mir.go @@ -12,7 +12,7 @@ import ( types2 "github.com/filecoin-project/mir/pkg/pb/bcbpb/types" types22 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" types18 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/types" - types20 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types20 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb/types" types23 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/types" types19 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb/types" types21 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" @@ -101,8 +101,8 @@ func Event_TypeFromPb(pb eventpb.Event_Type) Event_Type { return &Event_Bcm{Bcm: types18.EventFromPb(pb.Bcm)} case *eventpb.Event_Miner: return &Event_Miner{Miner: types19.EventFromPb(pb.Miner)} - case *eventpb.Event_Communication: - return &Event_Communication{Communication: types20.EventFromPb(pb.Communication)} + case *eventpb.Event_Broadcast: + return &Event_Broadcast{Broadcast: types20.EventFromPb(pb.Broadcast)} case *eventpb.Event_Synchronizer: return &Event_Synchronizer{Synchronizer: types21.EventFromPb(pb.Synchronizer)} case *eventpb.Event_Application: @@ -623,28 +623,28 @@ func (*Event_Miner) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Miner]()} } -type Event_Communication struct { - Communication *types20.Event +type Event_Broadcast struct { + Broadcast *types20.Event } -func (*Event_Communication) isEvent_Type() {} +func (*Event_Broadcast) isEvent_Type() {} -func (w *Event_Communication) Unwrap() *types20.Event { - return w.Communication +func (w *Event_Broadcast) Unwrap() *types20.Event { + return w.Broadcast } -func (w *Event_Communication) Pb() eventpb.Event_Type { +func (w *Event_Broadcast) Pb() eventpb.Event_Type { if w == nil { return nil } - if w.Communication == nil { - return &eventpb.Event_Communication{} + if w.Broadcast == nil { + return &eventpb.Event_Broadcast{} } - return &eventpb.Event_Communication{Communication: (w.Communication).Pb()} + return &eventpb.Event_Broadcast{Broadcast: (w.Broadcast).Pb()} } -func (*Event_Communication) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Communication]()} +func (*Event_Broadcast) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*eventpb.Event_Broadcast]()} } type Event_Synchronizer struct { diff --git a/pkg/pb/messagepb/messagepb.pb.go b/pkg/pb/messagepb/messagepb.pb.go index cef15e9a6..b4fefafc4 100644 --- a/pkg/pb/messagepb/messagepb.pb.go +++ b/pkg/pb/messagepb/messagepb.pb.go @@ -14,7 +14,7 @@ package messagepb import ( mscpb "github.com/filecoin-project/mir/pkg/pb/availabilitypb/mscpb" bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" - communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + broadcastpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb" synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" isspb "github.com/filecoin-project/mir/pkg/pb/isspb" @@ -49,7 +49,7 @@ type Message struct { // *Message_Pingpong // *Message_Checkpoint // *Message_Orderer - // *Message_Communicationpb + // *Message_Broadcast // *Message_Synchronizer Type isMessage_Type `protobuf_oneof:"type"` } @@ -142,9 +142,9 @@ func (x *Message) GetOrderer() *ordererpb.Message { return nil } -func (x *Message) GetCommunicationpb() *communicationpb.Message { - if x, ok := x.GetType().(*Message_Communicationpb); ok { - return x.Communicationpb +func (x *Message) GetBroadcast() *broadcastpb.Message { + if x, ok := x.GetType().(*Message_Broadcast); ok { + return x.Broadcast } return nil } @@ -184,9 +184,9 @@ type Message_Orderer struct { Orderer *ordererpb.Message `protobuf:"bytes,7,opt,name=orderer,proto3,oneof"` } -type Message_Communicationpb struct { +type Message_Broadcast struct { // Messages for blockchain - Communicationpb *communicationpb.Message `protobuf:"bytes,8,opt,name=communicationpb,proto3,oneof"` + Broadcast *broadcastpb.Message `protobuf:"bytes,8,opt,name=broadcast,proto3,oneof"` } type Message_Synchronizer struct { @@ -205,7 +205,7 @@ func (*Message_Checkpoint) isMessage_Type() {} func (*Message_Orderer) isMessage_Type() {} -func (*Message_Communicationpb) isMessage_Type() {} +func (*Message_Broadcast) isMessage_Type() {} func (*Message_Synchronizer) isMessage_Type() {} @@ -224,56 +224,55 @@ var file_messagepb_messagepb_proto_rawDesc = []byte{ 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x70, - 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x32, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, - 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, - 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, - 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6e, 0x65, 0x74, - 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, - 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb2, 0x04, 0x0a, 0x07, 0x4d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x57, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, - 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x49, 0x44, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x25, - 0x0a, 0x03, 0x69, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69, 0x73, - 0x73, 0x70, 0x62, 0x2e, 0x49, 0x53, 0x53, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, - 0x52, 0x03, 0x69, 0x73, 0x73, 0x12, 0x22, 0x0a, 0x03, 0x62, 0x63, 0x62, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x63, 0x62, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, - 0x67, 0x65, 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, 0x62, 0x12, 0x4e, 0x0a, 0x12, 0x6d, 0x75, 0x6c, - 0x74, 0x69, 0x73, 0x69, 0x67, 0x5f, 0x63, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, - 0x6c, 0x69, 0x74, 0x79, 0x70, 0x62, 0x2e, 0x6d, 0x73, 0x63, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x11, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x73, 0x69, 0x67, - 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x12, 0x31, 0x0a, 0x08, 0x70, 0x69, 0x6e, - 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x69, - 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x48, 0x00, 0x52, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x12, 0x37, 0x0a, 0x0a, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x15, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, - 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x6f, 0x72, - 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x44, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, - 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, - 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x63, 0x6f, 0x6d, 0x6d, - 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x12, 0x3d, 0x0a, 0x0c, 0x73, - 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, - 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x79, - 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x3a, 0x04, 0xc0, 0xe4, 0x1d, 0x01, - 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x32, - 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, - 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, - 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x2a, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x70, + 0x62, 0x2f, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x70, 0x62, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x30, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, + 0x62, 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, + 0x2f, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, + 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6e, 0x65, 0x74, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, + 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x22, 0xa2, 0x04, 0x0a, 0x07, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x57, 0x0a, + 0x0b, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, + 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, + 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0a, 0x64, 0x65, 0x73, 0x74, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x03, 0x69, 0x73, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x69, 0x73, 0x73, 0x70, 0x62, 0x2e, 0x49, 0x53, 0x53, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x03, 0x69, 0x73, 0x73, 0x12, 0x22, 0x0a, + 0x03, 0x62, 0x63, 0x62, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x62, 0x63, 0x62, + 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x03, 0x62, 0x63, + 0x62, 0x12, 0x4e, 0x0a, 0x12, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x73, 0x69, 0x67, 0x5f, 0x63, 0x6f, + 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x61, 0x76, 0x61, 0x69, 0x6c, 0x61, 0x62, 0x69, 0x6c, 0x69, 0x74, 0x79, 0x70, 0x62, 0x2e, 0x6d, + 0x73, 0x63, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x11, + 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x73, 0x69, 0x67, 0x43, 0x6f, 0x6c, 0x6c, 0x65, 0x63, 0x74, 0x6f, + 0x72, 0x12, 0x31, 0x0a, 0x08, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x69, 0x6e, 0x67, 0x70, 0x6f, 0x6e, 0x67, 0x70, 0x62, + 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x08, 0x70, 0x69, 0x6e, 0x67, + 0x70, 0x6f, 0x6e, 0x67, 0x12, 0x37, 0x0a, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, + 0x00, 0x52, 0x0a, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x2e, 0x0a, + 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, + 0x2e, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x48, 0x00, 0x52, 0x07, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x65, 0x72, 0x12, 0x34, 0x0a, + 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x14, 0x2e, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, 0x61, 0x73, 0x74, 0x70, 0x62, 0x2e, 0x4d, + 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x00, 0x52, 0x09, 0x62, 0x72, 0x6f, 0x61, 0x64, 0x63, + 0x61, 0x73, 0x74, 0x12, 0x3d, 0x0a, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, + 0x7a, 0x65, 0x72, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x73, 0x79, 0x6e, 0x63, + 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, 0x65, 0x72, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x48, 0x00, 0x52, 0x0c, 0x73, 0x79, 0x6e, 0x63, 0x68, 0x72, 0x6f, 0x6e, 0x69, 0x7a, + 0x65, 0x72, 0x3a, 0x04, 0xc0, 0xe4, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, + 0x12, 0x04, 0xc8, 0xe4, 0x1d, 0x01, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, + 0x2f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -290,15 +289,15 @@ func file_messagepb_messagepb_proto_rawDescGZIP() []byte { var file_messagepb_messagepb_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_messagepb_messagepb_proto_goTypes = []interface{}{ - (*Message)(nil), // 0: messagepb.Message - (*isspb.ISSMessage)(nil), // 1: isspb.ISSMessage - (*bcbpb.Message)(nil), // 2: bcbpb.Message - (*mscpb.Message)(nil), // 3: availabilitypb.mscpb.Message - (*pingpongpb.Message)(nil), // 4: pingpongpb.Message - (*checkpointpb.Message)(nil), // 5: checkpointpb.Message - (*ordererpb.Message)(nil), // 6: ordererpb.Message - (*communicationpb.Message)(nil), // 7: communicationpb.Message - (*synchronizerpb.Message)(nil), // 8: synchronizerpb.Message + (*Message)(nil), // 0: messagepb.Message + (*isspb.ISSMessage)(nil), // 1: isspb.ISSMessage + (*bcbpb.Message)(nil), // 2: bcbpb.Message + (*mscpb.Message)(nil), // 3: availabilitypb.mscpb.Message + (*pingpongpb.Message)(nil), // 4: pingpongpb.Message + (*checkpointpb.Message)(nil), // 5: checkpointpb.Message + (*ordererpb.Message)(nil), // 6: ordererpb.Message + (*broadcastpb.Message)(nil), // 7: broadcastpb.Message + (*synchronizerpb.Message)(nil), // 8: synchronizerpb.Message } var file_messagepb_messagepb_proto_depIdxs = []int32{ 1, // 0: messagepb.Message.iss:type_name -> isspb.ISSMessage @@ -307,7 +306,7 @@ var file_messagepb_messagepb_proto_depIdxs = []int32{ 4, // 3: messagepb.Message.pingpong:type_name -> pingpongpb.Message 5, // 4: messagepb.Message.checkpoint:type_name -> checkpointpb.Message 6, // 5: messagepb.Message.orderer:type_name -> ordererpb.Message - 7, // 6: messagepb.Message.communicationpb:type_name -> communicationpb.Message + 7, // 6: messagepb.Message.broadcast:type_name -> broadcastpb.Message 8, // 7: messagepb.Message.synchronizer:type_name -> synchronizerpb.Message 8, // [8:8] is the sub-list for method output_type 8, // [8:8] is the sub-list for method input_type @@ -342,7 +341,7 @@ func file_messagepb_messagepb_proto_init() { (*Message_Pingpong)(nil), (*Message_Checkpoint)(nil), (*Message_Orderer)(nil), - (*Message_Communicationpb)(nil), + (*Message_Broadcast)(nil), (*Message_Synchronizer)(nil), } type x struct{} diff --git a/pkg/pb/messagepb/messagepb.pb.mir.go b/pkg/pb/messagepb/messagepb.pb.mir.go index f6532da0e..74be9d932 100644 --- a/pkg/pb/messagepb/messagepb.pb.mir.go +++ b/pkg/pb/messagepb/messagepb.pb.mir.go @@ -14,7 +14,7 @@ func (*Message) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Message_Pingpong)(nil)), reflect.TypeOf((*Message_Checkpoint)(nil)), reflect.TypeOf((*Message_Orderer)(nil)), - reflect.TypeOf((*Message_Communicationpb)(nil)), + reflect.TypeOf((*Message_Broadcast)(nil)), reflect.TypeOf((*Message_Synchronizer)(nil)), } } diff --git a/pkg/pb/messagepb/oneof_interfaces.mir.go b/pkg/pb/messagepb/oneof_interfaces.mir.go index fd232dc47..182d68e0f 100644 --- a/pkg/pb/messagepb/oneof_interfaces.mir.go +++ b/pkg/pb/messagepb/oneof_interfaces.mir.go @@ -5,7 +5,7 @@ package messagepb import ( mscpb "github.com/filecoin-project/mir/pkg/pb/availabilitypb/mscpb" bcbpb "github.com/filecoin-project/mir/pkg/pb/bcbpb" - communicationpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" + broadcastpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb" synchronizerpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" checkpointpb "github.com/filecoin-project/mir/pkg/pb/checkpointpb" isspb "github.com/filecoin-project/mir/pkg/pb/isspb" @@ -44,8 +44,8 @@ func (w *Message_Orderer) Unwrap() *ordererpb.Message { return w.Orderer } -func (w *Message_Communicationpb) Unwrap() *communicationpb.Message { - return w.Communicationpb +func (w *Message_Broadcast) Unwrap() *broadcastpb.Message { + return w.Broadcast } func (w *Message_Synchronizer) Unwrap() *synchronizerpb.Message { diff --git a/pkg/pb/messagepb/types/types.mir.go b/pkg/pb/messagepb/types/types.mir.go index fad742b87..6497f709a 100644 --- a/pkg/pb/messagepb/types/types.mir.go +++ b/pkg/pb/messagepb/types/types.mir.go @@ -6,7 +6,7 @@ import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" types3 "github.com/filecoin-project/mir/pkg/pb/availabilitypb/mscpb/types" types2 "github.com/filecoin-project/mir/pkg/pb/bcbpb/types" - types7 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/types" + types7 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb/types" types8 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/types" types5 "github.com/filecoin-project/mir/pkg/pb/checkpointpb/types" types1 "github.com/filecoin-project/mir/pkg/pb/isspb/types" @@ -50,8 +50,8 @@ func Message_TypeFromPb(pb messagepb.Message_Type) Message_Type { return &Message_Checkpoint{Checkpoint: types5.MessageFromPb(pb.Checkpoint)} case *messagepb.Message_Orderer: return &Message_Orderer{Orderer: types6.MessageFromPb(pb.Orderer)} - case *messagepb.Message_Communicationpb: - return &Message_Communicationpb{Communicationpb: types7.MessageFromPb(pb.Communicationpb)} + case *messagepb.Message_Broadcast: + return &Message_Broadcast{Broadcast: types7.MessageFromPb(pb.Broadcast)} case *messagepb.Message_Synchronizer: return &Message_Synchronizer{Synchronizer: types8.MessageFromPb(pb.Synchronizer)} } @@ -202,28 +202,28 @@ func (*Message_Orderer) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*messagepb.Message_Orderer]()} } -type Message_Communicationpb struct { - Communicationpb *types7.Message +type Message_Broadcast struct { + Broadcast *types7.Message } -func (*Message_Communicationpb) isMessage_Type() {} +func (*Message_Broadcast) isMessage_Type() {} -func (w *Message_Communicationpb) Unwrap() *types7.Message { - return w.Communicationpb +func (w *Message_Broadcast) Unwrap() *types7.Message { + return w.Broadcast } -func (w *Message_Communicationpb) Pb() messagepb.Message_Type { +func (w *Message_Broadcast) Pb() messagepb.Message_Type { if w == nil { return nil } - if w.Communicationpb == nil { - return &messagepb.Message_Communicationpb{} + if w.Broadcast == nil { + return &messagepb.Message_Broadcast{} } - return &messagepb.Message_Communicationpb{Communicationpb: (w.Communicationpb).Pb()} + return &messagepb.Message_Broadcast{Broadcast: (w.Broadcast).Pb()} } -func (*Message_Communicationpb) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*messagepb.Message_Communicationpb]()} +func (*Message_Broadcast) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*messagepb.Message_Broadcast]()} } type Message_Synchronizer struct { diff --git a/protos/blockchainpb/communicationpb/communicationpb.proto b/protos/blockchainpb/broadcastpb/broadcastpb.proto similarity index 92% rename from protos/blockchainpb/communicationpb/communicationpb.proto rename to protos/blockchainpb/broadcastpb/broadcastpb.proto index 88e8bda0f..e73651120 100644 --- a/protos/blockchainpb/communicationpb/communicationpb.proto +++ b/protos/blockchainpb/broadcastpb/broadcastpb.proto @@ -1,8 +1,8 @@ syntax = "proto3"; -package communicationpb; +package broadcastpb; -option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb"; +option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb"; import "blockchainpb/blockchainpb.proto"; diff --git a/protos/blockchainpb/synchronizerpb/synchronizerpb.proto b/protos/blockchainpb/synchronizerpb/synchronizerpb.proto index 6210c1266..5812bd0c8 100644 --- a/protos/blockchainpb/synchronizerpb/synchronizerpb.proto +++ b/protos/blockchainpb/synchronizerpb/synchronizerpb.proto @@ -23,7 +23,7 @@ message SyncRequest { option (mir.event) = true; blockchainpb.Block orphan_block = 1; - repeated uint64 leave_ids = 2; + repeated uint64 leave_node_ids = 2; } message Message { @@ -40,9 +40,9 @@ message Message { message ChainRequest { option (net.message) = true; - string request_id = 1; - uint64 block_id = 2; - repeated uint64 leave_ids = 3; + string request_id = 1; + uint64 block_id = 2; + repeated uint64 leave_node_ids = 3; } message ChainResponse { diff --git a/protos/eventpb/eventpb.proto b/protos/eventpb/eventpb.proto index 9f1f3870f..6706544f4 100644 --- a/protos/eventpb/eventpb.proto +++ b/protos/eventpb/eventpb.proto @@ -25,7 +25,7 @@ import "transportpb/transportpb.proto"; import "testerpb/testerpb.proto"; import "blockchainpb/bcmpb/bcmpb.proto"; import "blockchainpb/minerpb/minerpb.proto"; -import "blockchainpb/communicationpb/communicationpb.proto"; +import "blockchainpb/broadcastpb/broadcastpb.proto"; import "blockchainpb/synchronizerpb/synchronizerpb.proto"; import "blockchainpb/applicationpb/applicationpb.proto"; import "blockchainpb/interceptorpb/interceptorpb.proto"; @@ -72,7 +72,7 @@ message Event { // Events for blockchain bcmpb.Event bcm = 201; minerpb.Event miner = 202; - communicationpb.Event communication = 203; + broadcastpb.Event broadcast = 203; synchronizerpb.Event synchronizer = 204; applicationpb.Event application = 205; diff --git a/protos/generate.go b/protos/generate.go index e1206f55f..0edea1d97 100644 --- a/protos/generate.go +++ b/protos/generate.go @@ -47,7 +47,7 @@ package protos //go:generate protoc-events blockchainpb/blockchainpb.proto //go:generate protoc-events blockchainpb/bcmpb/bcmpb.proto //go:generate protoc-events blockchainpb/minerpb/minerpb.proto -//go:generate protoc-events blockchainpb/communicationpb/communicationpb.proto +//go:generate protoc-events blockchainpb/broadcastpb/broadcastpb.proto //go:generate protoc-events blockchainpb/synchronizerpb/synchronizerpb.proto //go:generate protoc-events blockchainpb/applicationpb/applicationpb.proto //go:generate protoc-events blockchainpb/payloadpb/payloadpb.proto @@ -87,7 +87,7 @@ package protos //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" -//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb" +//go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" //go:generate std-gen "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" diff --git a/protos/messagepb/messagepb.proto b/protos/messagepb/messagepb.proto index 6c58fba0e..82873e8e8 100644 --- a/protos/messagepb/messagepb.proto +++ b/protos/messagepb/messagepb.proto @@ -16,7 +16,7 @@ import "availabilitypb/mscpb/mscpb.proto"; import "pingpongpb/pingpongpb.proto"; import "checkpointpb/checkpointpb.proto"; import "ordererpb/ordererpb.proto"; -import "blockchainpb/communicationpb/communicationpb.proto"; +import "blockchainpb/broadcastpb/broadcastpb.proto"; import "blockchainpb/synchronizerpb/synchronizerpb.proto"; import "mir/codegen_extensions.proto"; @@ -38,7 +38,7 @@ message Message { ordererpb.Message orderer = 7; // Messages for blockchain - communicationpb.Message communicationpb = 8; + broadcastpb.Message broadcast = 8; synchronizerpb.Message synchronizer = 9; } diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index 4cf78fed6..84ff27764 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -15,7 +15,6 @@ import ( statepbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" t "github.com/filecoin-project/mir/pkg/types" - "github.com/filecoin-project/mir/samples/blockchain/application/config" "github.com/filecoin-project/mir/samples/blockchain/application/payloads" "github.com/filecoin-project/mir/samples/blockchain/utils" "google.golang.org/protobuf/types/known/timestamppb" @@ -217,7 +216,10 @@ func NewApplication(logger logging.Logger, nodeID t.NodeID) modules.PassiveModul dsl.UponInit(m, func() error { // init blockchain - bcmpbdsl.InitBlockchain(*am.m, "bcm", config.InitialState) + bcmpbdsl.InitBlockchain(*am.m, "bcm", &statepbtypes.State{ + MessageHistory: []string{}, + LastSentTimestamps: []*statepbtypes.LastSentTimestamp{}, + }) return nil }) diff --git a/samples/blockchain/application/config/config.go b/samples/blockchain/application/config/config.go deleted file mode 100644 index c9fd207fb..000000000 --- a/samples/blockchain/application/config/config.go +++ /dev/null @@ -1,10 +0,0 @@ -package config - -import ( - statepbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" -) - -var InitialState = &statepbtypes.State{ - MessageHistory: []string{}, - LastSentTimestamps: []*statepbtypes.LastSentTimestamp{}, -} diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 2bdec3387..c59751ce8 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -19,7 +19,6 @@ import ( synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" t "github.com/filecoin-project/mir/pkg/types" - "github.com/filecoin-project/mir/samples/blockchain/application/config" "github.com/filecoin-project/mir/samples/blockchain/utils" "golang.org/x/exp/maps" "google.golang.org/protobuf/types/known/timestamppb" @@ -296,9 +295,9 @@ func (bcm *bcmModule) processNewChain(blocks []*blockchainpbtypes.Block) error { parent, _ := bcm.findBlock(blocks[0].PreviousBlockId) if parent == nil { bcm.logger.Log(logging.LevelError, "Parent not found. Could be invalid chain/block or we are missing a part. Sending sync req to sync end of chain.", "blockId", utils.FormatBlockId(blocks[0].BlockId)) - leavesPlusGenesis := append(maps.Keys(bcm.leaves), bcm.genesis.block.BlockId) - bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", utils.FormatBlockId(blocks[len(blocks)-1].BlockId), "leaves+genesis", utils.FormatBlockIdSlice(leavesPlusGenesis)) - synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", blocks[len(blocks)-1], leavesPlusGenesis) + connectionNodeIDs := append(maps.Keys(bcm.leaves), bcm.genesis.block.BlockId) + bcm.logger.Log(logging.LevelDebug, "Sending sync request", "blockId", utils.FormatBlockId(blocks[len(blocks)-1].BlockId), "leaves+genesis", utils.FormatBlockIdSlice(connectionNodeIDs)) + synchronizerpbdsl.SyncRequest(*bcm.m, "synchronizer", blocks[len(blocks)-1], connectionNodeIDs) return nil } @@ -502,7 +501,7 @@ func (bcm *bcmModule) handleInitBlockchain(initialState *statepbtypes.State) err genesis.BlockId = hash genesisBcm := bcmBlock{ block: genesis, - state: config.InitialState, + state: initialState, parent: nil, depth: 0, } diff --git a/samples/blockchain/communication.go b/samples/blockchain/broadcast.go similarity index 57% rename from samples/blockchain/communication.go rename to samples/blockchain/broadcast.go index fe437bd9e..ad74a2e00 100644 --- a/samples/blockchain/communication.go +++ b/samples/blockchain/broadcast.go @@ -7,22 +7,22 @@ import ( "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" - communicationpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/dsl" - communicationpbmsgs "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/msgs" + broadcastpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb/dsl" + broadcastpbmsgs "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb/msgs" blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" t "github.com/filecoin-project/mir/pkg/types" "github.com/filecoin-project/mir/samples/blockchain/utils" ) -func NewCommunication(otherNodes []t.NodeID, mangle bool, logger logging.Logger) modules.PassiveModule { - m := dsl.NewModule("communication") +func NewBroadcast(otherNodes []t.NodeID, mangle bool, logger logging.Logger) modules.PassiveModule { + m := dsl.NewModule("broadcast") dsl.UponInit(m, func() error { return nil }) - communicationpbdsl.UponNewBlock(m, func(block *blockchainpbtypes.Block) error { + broadcastpbdsl.UponNewBlock(m, func(block *blockchainpbtypes.Block) error { // take the block and send it to all other nodes logger.Log(logging.LevelDebug, "broadcasting block", "blockId", utils.FormatBlockId(block.BlockId), "manlge", mangle) @@ -30,16 +30,16 @@ func NewCommunication(otherNodes []t.NodeID, mangle bool, logger logging.Logger) if mangle { // send via mangles for _, node := range otherNodes { - transportpbdsl.SendMessage(m, "mangler", communicationpbmsgs.NewBlockMessage("communication", block), []t.NodeID{node}) + transportpbdsl.SendMessage(m, "mangler", broadcastpbmsgs.NewBlockMessage("broadcast", block), []t.NodeID{node}) } } else { - transportpbdsl.SendMessage(m, "transport", communicationpbmsgs.NewBlockMessage("communication", block), otherNodes) + transportpbdsl.SendMessage(m, "transport", broadcastpbmsgs.NewBlockMessage("broadcast", block), otherNodes) } return nil }) - communicationpbdsl.UponNewBlockMessageReceived(m, func(from t.NodeID, block *blockchainpbtypes.Block) error { + broadcastpbdsl.UponNewBlockMessageReceived(m, func(from t.NodeID, block *blockchainpbtypes.Block) error { logger.Log(logging.LevelDebug, "new block received", "blockId", utils.FormatBlockId(block.BlockId)) bcmpbdsl.NewBlock(m, "bcm", block) diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index 51ea733c0..ccb4340e9 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -88,15 +88,15 @@ func main() { ownNodeID, mir.DefaultNodeConfig(), map[t.ModuleID]modules.Module{ - "transport": transport, - "bcm": NewBCM(logging.Decorate(logger, "BCM:\t")), - "miner": NewMiner(ownNodeID, 0.2, logging.Decorate(logger, "Miner:\t")), - "communication": NewCommunication(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), - "application": application.NewApplication(logging.Decorate(logger, "App:\t"), ownNodeID), - "synchronizer": NewSynchronizer(ownNodeID, otherNodes, false, logging.Decorate(logger, "Sync:\t")), - "timer": timer, - "mangler": mangler, - "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor + "transport": transport, + "bcm": NewBCM(logging.Decorate(logger, "BCM:\t")), + "miner": NewMiner(ownNodeID, 0.2, logging.Decorate(logger, "Miner:\t")), + "broadcast": NewBroadcast(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), + "application": application.NewApplication(logging.Decorate(logger, "App:\t"), ownNodeID), + "synchronizer": NewSynchronizer(ownNodeID, otherNodes, logging.Decorate(logger, "Sync:\t")), + "timer": timer, + "mangler": mangler, + "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor }, wsInterceptor.NewWsInterceptor( func(e *eventpb.Event) bool { diff --git a/samples/blockchain/miner.go b/samples/blockchain/miner.go index 5e7c5971e..de19c890c 100644 --- a/samples/blockchain/miner.go +++ b/samples/blockchain/miner.go @@ -11,7 +11,7 @@ import ( "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb" applicationpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/events" bcmpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/events" - communicationpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/communicationpb/events" + broadcastpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/broadcastpb/events" "github.com/filecoin-project/mir/pkg/pb/blockchainpb/minerpb" payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" @@ -127,8 +127,8 @@ func (m *minerModule) mineWorkerManager() { } block.BlockId = hash // Send block to BCM and broadcast module - m.logger.Log(logging.LevelInfo, "Block mined", "headId", utils.FormatBlockId(hash), "parentId", utils.FormatBlockId(blockRequest.HeadId)) - m.eventsOut <- events.ListOf(bcmpbevents.NewBlock("bcm", block).Pb(), communicationpbevents.NewBlock("communication", block).Pb()) + m.logger.Log(logging.LevelInfo, "Block mined", "blockId", utils.FormatBlockId(hash), "parentId", utils.FormatBlockId(blockRequest.HeadId)) + m.eventsOut <- events.ListOf(bcmpbevents.NewBlock("bcm", block).Pb(), broadcastpbevents.NewBlock("broadcast", block).Pb()) return } }() diff --git a/samples/blockchain/synchronizer.go b/samples/blockchain/synchronizer.go index 27e9e9086..93d2a4e5c 100644 --- a/samples/blockchain/synchronizer.go +++ b/samples/blockchain/synchronizer.go @@ -19,9 +19,33 @@ import ( "github.com/filecoin-project/mir/samples/blockchain/utils" ) -const ( - STRIKE_THRESHOLD = 2 -) +/** + * Synchronizer module + * =================== + * + * The synchronizer module assists the blockchain manager (BCM) in resolving cases whe BCM receives an orphan block. + * That is, a block that cannot be linked to the blockchain because the blockchain does not contain the block that the orphan block is linked to. + * To do this, the synchronizer module communicates with other nodes to get the missing blocks. + * + * Terminology: + * - internal sync request: a request to synchronize a chain segment that was initiated by this node + * - external sync request: a request to synchronize a chain segment that was initiated by another node + * + * The synchronizer module performs the following tasks: + * For internal sync requests: + * 1. When it receives a SyncRequest event, it must register the request and send a ChainRequest message to another node. + * It asks one node after another. + * 2. When it receives a successful ChainResponse message, it sends the blockchain manager (BCM) the chain fixing the missing bit with a Chain event. + * It then deletes the request. + * 3. When it receives a failed ChainResponse message, it sends a ChainRequest message to the next node. + * If there are no more nodes to ask, it deletes the request. + * + * For external sync requests: + * 1. When it receives a ChainRequest message, it must register the request and send a GetChainRequest event to the BCM. + * 2. The BCM will respond with a GetChainResponse event. The synchronizer then responds to the node that sent the ChainRequest message with a ChainResponse message. + * + * IMPORTANT: This module assumes that all other nodes resppond to requests and that no messages are lost. + */ var ( ErrRequestAlreadyExists = errors.New("request already exists") @@ -34,36 +58,41 @@ var ( // rn it would just get stuck on such a node... type synchronizerModule struct { - m *dsl.Module - nodeID t.NodeID // id of this node - syncRequests map[string]*syncRequest - getRequests map[string]*getRequest - otherNodes []t.NodeID // we remove nodes that we presume have crashed (see strikeList) // TODO: add a timeout to 're-add' them? exponential backoff? - // stikeList map[t.NodeID]int // a node gets a strike whenever it failed to handle a request, after STRIKE_THRESHOLD strikes it is removed from the otherNodes. Set to 0 if it handles a request successfully - mangle bool - internalLogger logging.Logger - externaLogger logging.Logger + m *dsl.Module + nodeID t.NodeID // id of this node + syncRequests map[string]*syncRequest // map to keep track of sync requests - for internal sync requests + getRequests map[string]*getRequest // map to keep track of get requests - for external sync request + otherNodes []t.NodeID // ids of all other nodes + internalLogger logging.Logger // logger for internal sync requests + externaLogger logging.Logger // logger for external sync requests } /************************* - * Outgoing sync requests + * Internal sync requests *************************/ +// struct to keep track of sync requests +// it hold all information needed to send sync requests to other nodes and keeps track of which nodes have already been contacted type syncRequest struct { - requestID string - blockID uint64 - leaveIDs []uint64 - nodesContacted []int // index of node in otherNodes + requestID string // id of the request, used as key in syncRequests map + blockID uint64 // id of the blocks that cannot be linked to the blockchain + connectionNodeIDs []uint64 // ids of the block + nodesContacted []int // index of node in otherNodes } -func (sm *synchronizerModule) registerSyncRequest(block *blockchainpbtypes.Block, leaves []uint64) (string, error) { +/** + * Helper functions for internal requests + */ + +// registers a sync request +func (sm *synchronizerModule) registerSyncRequest(block *blockchainpbtypes.Block, connectionNodeIDs []uint64) (string, error) { requestId := fmt.Sprint(block.BlockId) + "-" + string(sm.nodeID) // check if request already exists if _, ok := sm.syncRequests[requestId]; ok { return "", ErrRequestAlreadyExists } - sm.syncRequests[requestId] = &syncRequest{requestID: requestId, blockID: block.BlockId, leaveIDs: leaves, nodesContacted: []int{}} + sm.syncRequests[requestId] = &syncRequest{requestID: requestId, blockID: block.BlockId, connectionNodeIDs: connectionNodeIDs, nodesContacted: []int{}} return requestId, nil } @@ -84,43 +113,43 @@ func (sm *synchronizerModule) contactNextNode(requestID string) error { } else { // get next node - // NOTE: stupid to keep an array of indices but it's more flexible in case I want to change the way I pick the next node nextNodeIndex = (request.nodesContacted[len(request.nodesContacted)-1] + 1) % len(sm.otherNodes) } request.nodesContacted = append(request.nodesContacted, nextNodeIndex) nextNode := sm.otherNodes[nextNodeIndex] - sm.internalLogger.Log(logging.LevelDebug, "asking node for block", "block", utils.FormatBlockId(request.blockID), "node", nextNode, "mangle", sm.mangle) + sm.internalLogger.Log(logging.LevelDebug, "asking node for block", "block", utils.FormatBlockId(request.blockID), "node", nextNode) + // send request - if sm.mangle { - transportpbdsl.SendMessage(*sm.m, "mangler", synchronizerpbmsgs.ChainRequest("synchronizer", requestID, request.blockID, request.leaveIDs), []t.NodeID{nextNode}) - } else { - transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.ChainRequest("synchronizer", requestID, request.blockID, request.leaveIDs), []t.NodeID{nextNode}) - } + transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.ChainRequest("synchronizer", requestID, request.blockID, request.connectionNodeIDs), []t.NodeID{nextNode}) return nil } -func (sm *synchronizerModule) handleSyncRequest(orphanBlock *blockchainpbtypes.Block, leaveIds []uint64) error { +/** + * Mir handlers for internal requests + */ + +func (sm *synchronizerModule) handleSyncRequest(orphanBlock *blockchainpbtypes.Block, connectionNodeIDs []uint64) error { // register request - requestId, err := sm.registerSyncRequest(orphanBlock, leaveIds) + requestId, err := sm.registerSyncRequest(orphanBlock, connectionNodeIDs) if err != nil { switch err { case ErrRequestAlreadyExists: // ignore request - sm.internalLogger.Log(logging.LevelDebug, "sync request already exists", "orphanBlock", utils.FormatBlockId(orphanBlock.BlockId), "leaveIds", utils.FormatBlockIdSlice(leaveIds)) + sm.internalLogger.Log(logging.LevelDebug, "sync request already exists", "orphanBlock", utils.FormatBlockId(orphanBlock.BlockId), "connectionNodeIDs", utils.FormatBlockIdSlice(connectionNodeIDs)) return nil default: - sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", utils.FormatBlockId(orphanBlock.BlockId), "leaveIds", utils.FormatBlockIdSlice(leaveIds)) + sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", utils.FormatBlockId(orphanBlock.BlockId), "connectionNodeIDs", utils.FormatBlockIdSlice(connectionNodeIDs)) return err } } if err := sm.contactNextNode(requestId); err != nil { // could check for 'ErrNoMoreNodes' here but there should always be at least one node - sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", utils.FormatBlockId(orphanBlock.BlockId), "leaveIds", utils.FormatBlockIdSlice(leaveIds)) + sm.internalLogger.Log(logging.LevelError, "sync registration failed", "error", err.Error(), "orphanBlock", utils.FormatBlockId(orphanBlock.BlockId), "connectionNodeIDs", utils.FormatBlockIdSlice(connectionNodeIDs)) return err } @@ -136,7 +165,7 @@ func (sm *synchronizerModule) handleChainResponseReceived(from t.NodeID, request } if !found { - // check whether the response came fromt he last node we contacted + // check whether the response came from he last node we contacted // NOTE: this is getting messy if sm.otherNodes[request.nodesContacted[len(request.nodesContacted)-1]] != from { // there is still a request out in the ether - don't send another one @@ -145,13 +174,11 @@ func (sm *synchronizerModule) handleChainResponseReceived(from t.NodeID, request } // send request to the next node if err := sm.contactNextNode(requestID); err == ErrNoMoreNodes { - sm.internalLogger.Log(logging.LevelError, "no more nodes to contact - forgetting request - shouldn't happen if not mangling", "error", err.Error(), "requestId", requestID, "mangle", sm.mangle) + sm.internalLogger.Log(logging.LevelError, "no more nodes to contact - forgetting request - shouldn't happen ", "error", err.Error(), "requestId", requestID) delete(sm.syncRequests, requestID) - if !sm.mangle { - panic(err) // NOTE: this should never happen as long as we don't start mangling - } } else if err != nil { - sm.internalLogger.Log(logging.LevelError, "Unexpected error contacting next node", "error", err.Error(), "requestId", requestID, "mangle", sm.mangle) + sm.internalLogger.Log(logging.LevelError, "Unexpected error contacting next node", "error", err.Error(), "requestId", requestID) + // TODO: should we actually fail here? panic(err) } @@ -166,17 +193,21 @@ func (sm *synchronizerModule) handleChainResponseReceived(from t.NodeID, request } /************************* - * Outgoing sync requests + * External sync requests *************************/ type getRequest struct { requestID string from t.NodeID } -func (sm *synchronizerModule) handleChainRequestReceived(from t.NodeID, requestID string, blockID uint64, leaveIds []uint64) error { +/** + * Mir handlers for external requests + */ + +func (sm *synchronizerModule) handleChainRequestReceived(from t.NodeID, requestID string, blockID uint64, connectionNodeIDs []uint64) error { // check if request is already being processed if _, ok := sm.getRequests[requestID]; ok { - sm.externaLogger.Log(logging.LevelError, "Get block request already exists", "from", from, "requestId", requestID, "mangle", sm.mangle) + sm.externaLogger.Log(logging.LevelError, "Get block request already exists", "from", from, "requestId", requestID) return nil } @@ -184,7 +215,7 @@ func (sm *synchronizerModule) handleChainRequestReceived(from t.NodeID, requestI sm.getRequests[requestID] = &getRequest{requestID: requestID, from: from} // send get request to blockchain manager - bcmpbdsl.GetChainRequest(*sm.m, "bcm", requestID, "synchronizer", blockID, leaveIds) + bcmpbdsl.GetChainRequest(*sm.m, "bcm", requestID, "synchronizer", blockID, connectionNodeIDs) return nil @@ -193,18 +224,14 @@ func (sm *synchronizerModule) handleChainRequestReceived(from t.NodeID, requestI func (sm *synchronizerModule) handleGetChainResponse(requestID string, found bool, chain []*blockchainpbtypes.Block) error { request, ok := sm.getRequests[requestID] if !ok { - sm.externaLogger.Log(logging.LevelError, "Unknown get block request", "requestId", requestID, "mangle", sm.mangle) + sm.externaLogger.Log(logging.LevelError, "Unknown get block request", "requestId", requestID) return ErrUnkownGetBlockRequest } - sm.externaLogger.Log(logging.LevelInfo, "Responsing to block request", "requestId", requestID, "found", found, "mangle", sm.mangle) + sm.externaLogger.Log(logging.LevelInfo, "Responsing to block request", "requestId", requestID, "found", found) // respond to sync request - if sm.mangle { - transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.ChainResponse("synchronizer", requestID, found, chain), []t.NodeID{request.from}) - } else { - transportpbdsl.SendMessage(*sm.m, "mangler", synchronizerpbmsgs.ChainResponse("synchronizer", requestID, found, chain), []t.NodeID{request.from}) - } + transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.ChainResponse("synchronizer", requestID, found, chain), []t.NodeID{request.from}) // delete request delete(sm.getRequests, requestID) @@ -212,7 +239,7 @@ func (sm *synchronizerModule) handleGetChainResponse(requestID string, found boo return nil } -func NewSynchronizer(nodeID t.NodeID, otherNodes []t.NodeID, mangle bool, logger logging.Logger) modules.PassiveModule { +func NewSynchronizer(nodeID t.NodeID, otherNodes []t.NodeID, logger logging.Logger) modules.PassiveModule { m := dsl.NewModule("synchronizer") var sm = synchronizerModule{ m: &m, @@ -220,7 +247,6 @@ func NewSynchronizer(nodeID t.NodeID, otherNodes []t.NodeID, mangle bool, logger getRequests: make(map[string]*getRequest), syncRequests: make(map[string]*syncRequest), otherNodes: otherNodes, - mangle: mangle, internalLogger: logging.Decorate(logger, "Intern - "), externaLogger: logging.Decorate(logger, "External - "), } @@ -229,13 +255,13 @@ func NewSynchronizer(nodeID t.NodeID, otherNodes []t.NodeID, mangle bool, logger return nil }) - // outgoing sync requests - synchronizerpbdsl.UponSyncRequest(m, sm.handleSyncRequest) - synchronizerpbdsl.UponChainResponseReceived(m, sm.handleChainResponseReceived) - - // for incoming sync requests + // internal sync requests synchronizerpbdsl.UponChainRequestReceived(m, sm.handleChainRequestReceived) bcmpbdsl.UponGetChainResponse(m, sm.handleGetChainResponse) + // external sync requests + synchronizerpbdsl.UponSyncRequest(m, sm.handleSyncRequest) + synchronizerpbdsl.UponChainResponseReceived(m, sm.handleChainResponseReceived) + return m } From 9c3a865c36c9c07fbda7424c1c47b6bb529e73eb Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Fri, 2 Feb 2024 13:37:57 +0100 Subject: [PATCH 25/46] cleanup + comments --- samples/blockchain/broadcast.go | 11 ++++++++- .../wssserver.go => wsServer.go/wsServer.go} | 13 +++++++++- .../blockchain/wsinterceptor/wsinterceptor.go | 24 +++++++++++++++---- 3 files changed, 42 insertions(+), 6 deletions(-) rename samples/blockchain/wsinterceptor/{wsserver.go/wssserver.go => wsServer.go/wsServer.go} (88%) diff --git a/samples/blockchain/broadcast.go b/samples/blockchain/broadcast.go index ad74a2e00..8d8e8d51c 100644 --- a/samples/blockchain/broadcast.go +++ b/samples/blockchain/broadcast.go @@ -15,6 +15,15 @@ import ( "github.com/filecoin-project/mir/samples/blockchain/utils" ) +/** +* Broadcast module +* ================= +* +* The broadcast module is responsible for broadcasting new blocks to all other nodes. +* It either does this directly via the transport module or via the mangler (parameter mangle). +* If the mangler is used, messages might will be dropped and delayed. + */ + func NewBroadcast(otherNodes []t.NodeID, mangle bool, logger logging.Logger) modules.PassiveModule { m := dsl.NewModule("broadcast") @@ -28,7 +37,7 @@ func NewBroadcast(otherNodes []t.NodeID, mangle bool, logger logging.Logger) mod logger.Log(logging.LevelDebug, "broadcasting block", "blockId", utils.FormatBlockId(block.BlockId), "manlge", mangle) if mangle { - // send via mangles + // send via mangler for _, node := range otherNodes { transportpbdsl.SendMessage(m, "mangler", broadcastpbmsgs.NewBlockMessage("broadcast", block), []t.NodeID{node}) } diff --git a/samples/blockchain/wsinterceptor/wsserver.go/wssserver.go b/samples/blockchain/wsinterceptor/wsServer.go/wsServer.go similarity index 88% rename from samples/blockchain/wsinterceptor/wsserver.go/wssserver.go rename to samples/blockchain/wsinterceptor/wsServer.go/wsServer.go index a0f67d42c..7d4c3aefd 100644 --- a/samples/blockchain/wsinterceptor/wsserver.go/wssserver.go +++ b/samples/blockchain/wsinterceptor/wsServer.go/wsServer.go @@ -1,4 +1,4 @@ -package wsserver +package wsServer import ( "fmt" @@ -11,6 +11,17 @@ import ( "github.com/gorilla/websocket" ) +/** + * Websocket server + * ================= + * + * A simple websocket server which accepts connections and sends messages to all connected clients. + * The messages are sent to the server through a channel. + * + * Note: This is a very shotty implementation. For example, it crashes if a client disconnects. + * It is only intended for debugging purposes. + */ + type WsMessage struct { MessageType int Payload []byte diff --git a/samples/blockchain/wsinterceptor/wsinterceptor.go b/samples/blockchain/wsinterceptor/wsinterceptor.go index f82e38ee7..c543c2328 100644 --- a/samples/blockchain/wsinterceptor/wsinterceptor.go +++ b/samples/blockchain/wsinterceptor/wsinterceptor.go @@ -4,14 +4,30 @@ import ( "github.com/filecoin-project/mir/pkg/events" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/pb/eventpb" - "github.com/filecoin-project/mir/samples/blockchain/wsinterceptor/wsserver.go" + wsServer "github.com/filecoin-project/mir/samples/blockchain/wsinterceptor/wsServer.go" "google.golang.org/protobuf/proto" ) +/** + * Websocket interceptor + * ===================== + * + * The websocket interceptor intercepts all events and sends them to a websocket server. + * Any connected client can then receive these events by subscribing to the websocket server. + * + * The interceptor proto defines events which are specificly intended for the interceptor and not used by the actual blockchain. + * Since these events don't have a destination module, they are sent to the "devnull" module. + * However, all events are intercepted and sent to the websocket server. The interceptor proto is simply for "extra" events. + * + * The interceptor proto defines two such events: + * - TreeUpdate: This event is sent by the blockchain manager (BCM) when the blockchain is updated. It contains all blocks in the blockchain and the id of the new head. + * - StateUpdate: This event is sent by the application when it computes the state for the newest head of the blockchain. + */ + type eventFilterFn func(*eventpb.Event) bool type wsInterceptor struct { - server *wsserver.WsServer + server *wsServer.WsServer eventFilter eventFilterFn logger logging.Logger } @@ -24,7 +40,7 @@ func (i *wsInterceptor) Intercept(events *events.EventList) error { // log? return err } - i.server.SendChan <- wsserver.WsMessage{ + i.server.SendChan <- wsServer.WsMessage{ MessageType: 2, Payload: []byte(payload), } @@ -35,7 +51,7 @@ func (i *wsInterceptor) Intercept(events *events.EventList) error { } func NewWsInterceptor(eventFilter eventFilterFn, port int, logger logging.Logger) *wsInterceptor { - server := wsserver.NewWsServer(port, logger) + server := wsServer.NewWsServer(port, logger) logger.Log(logging.LevelInfo, "Starting websocker interceptor server") go server.StartServers() return &wsInterceptor{ From bd94cfbde062ffcefc80510a1ff6abb349426572 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sat, 3 Feb 2024 20:46:35 +0100 Subject: [PATCH 26/46] add flag parsing to chat app --- samples/blockchain/main.go | 132 +++++++++++++++++++++++++------------ samples/blockchain/run.sh | 10 +-- samples/blockchain/test.sh | 10 +-- 3 files changed, 102 insertions(+), 50 deletions(-) diff --git a/samples/blockchain/main.go b/samples/blockchain/main.go index ccb4340e9..2cb369f73 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain/main.go @@ -3,7 +3,9 @@ package main import ( "bufio" "context" + "flag" "fmt" + "math" "os" "strconv" "time" @@ -23,35 +25,76 @@ import ( "github.com/filecoin-project/mir/samples/blockchain/wsInterceptor" ) -func main() { - fmt.Println("Starting blockchain") - - logger := logging.ConsoleDebugLogger +// Flags +var disableMangle = flag.Bool("disableMangle", false, "Disable mangling of messages") +var dropRate = flag.Float64("dropRate", 0.05, "The rate at which to drop messages") +var minDelay = flag.Float64("minDelay", 0.001, "The minimum delay to add to messages [seconds]") +var maxDelay = flag.Float64("maxDelay", 1, "The minimum delay to add to messages [seconds]") +var numberOfNodes = flag.Int("numberOfNodes", -1, "The number of nodes in the network [1, inf] REQUIRED") +var nodeID = flag.Int("nodeID", -1, "The ID of the node [0, numberOfNodes-1] REQUIRED") - numberOfNodes, err := strconv.Atoi(os.Args[1]) - if err != nil { - panic(err) +func main() { + // Parse command line flags + flag.Parse() + + requiredFlags := []string{"numberOfNodes", "nodeID"} + flagsSet := make(map[string]bool) + missedRequiredFlags := false + + // Check which flags are set + flag.Visit(func(f *flag.Flag) { + flagsSet[f.Name] = true + }) + + // Check for missed required flags + for _, f := range requiredFlags { + if !flagsSet[f] { + fmt.Printf("Flag %s is required\n", f) + missedRequiredFlags = true + } } - idInput := os.Args[2] - ownID, err := strconv.Atoi(idInput) - if err != nil { - panic(err) + + // Exit if missing any required flag + if missedRequiredFlags { + os.Exit(2) } - ownNodeID := t.NodeID(idInput) - mangle := false - if len(os.Args) >= 4 { - mangle, err = strconv.ParseBool(os.Args[3]) - if err != nil { - panic(err) - } + // Check for valid flag values + if *numberOfNodes < 1 { + fmt.Printf("Number of nodes must be greater than 0.\n") + os.Exit(2) + } else if *nodeID < 0 || *nodeID >= *numberOfNodes { + fmt.Printf("Node ID must be between 0 and numberOfNodes-1.\n") + os.Exit(2) + } else if *minDelay < 0 { + fmt.Printf("Minimum delay must be greater than or equal to 0.\n") + os.Exit(2) + } else if *maxDelay < 0 { + fmt.Printf("Maximum delay must be greater than or equal to 0.\n") + os.Exit(2) + } else if *minDelay > *maxDelay { + fmt.Printf("Minimum delay must be less than or equal to maximum delay.\n") + os.Exit(2) + } else if *dropRate < 0 || *dropRate > 1 { + fmt.Printf("Drop rate must be between 0 and 1.\n") + os.Exit(2) + } else if *disableMangle && (flagsSet["dropRate"] || flagsSet["minDelay"] || flagsSet["maxDelay"]) { + fmt.Printf("WARNING: Settings for drop rate, minimum delay, and maximum delay are ignored when mangling is disabled.\n") } - nodes := make(map[t.NodeID]*trantorpbtypes.NodeIdentity, numberOfNodes) - allNodeIds := make([]t.NodeID, numberOfNodes) - otherNodes := make([]t.NodeID, numberOfNodes-1) + ownNodeID := t.NodeID(strconv.Itoa(*nodeID)) + + // Setting up node + fmt.Println("Starting longest chain consensus protocol...") + + logger := logging.ConsoleDebugLogger + + // determine "other" nodes for this node + nodes := make(map[t.NodeID]*trantorpbtypes.NodeIdentity, *numberOfNodes) + allNodeIds := make([]t.NodeID, *numberOfNodes) + otherNodes := make([]t.NodeID, *numberOfNodes-1) otherNodesIndex := 0 - for i := 0; i < numberOfNodes; i++ { + for i := 0; i < *numberOfNodes; i++ { nodeIdStr := strconv.Itoa(i) nodeId := t.NodeID(nodeIdStr) allNodeIds[i] = nodeId @@ -63,7 +106,7 @@ func main() { } membership := &trantorpbtypes.Membership{Nodes: nodes} - // Instantiate network trnasport module and establish connections. + // Instantiate network transport module and establish connections. transport, err := grpc.NewTransport(ownNodeID, membership.Nodes[ownNodeID].Addr, logging.ConsoleInfoLogger) if err != nil { panic(err) @@ -73,31 +116,36 @@ func main() { } transport.Connect(membership) - timer := timer.New() + modules := map[t.ModuleID]modules.Module{ + "transport": transport, + "bcm": NewBCM(logging.Decorate(logger, "BCM:\t")), + "miner": NewMiner(ownNodeID, 0.2, logging.Decorate(logger, "Miner:\t")), + "broadcast": NewBroadcast(otherNodes, !*disableMangle, logging.Decorate(logger, "Comm:\t")), + "application": application.NewApplication(logging.Decorate(logger, "App:\t"), ownNodeID), + "synchronizer": NewSynchronizer(ownNodeID, otherNodes, logging.Decorate(logger, "Sync:\t")), + "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor + } + + if !*disableMangle { + minDelayDuration := time.Duration(int64(math.Round(*minDelay * float64(time.Second)))) + maxDelayDuration := time.Duration(int64(math.Round(*maxDelay * float64(time.Second)))) + manglerModule, err := eventmangler.NewModule( + eventmangler.ModuleConfig{Self: "mangler", Dest: "transport", Timer: "timer"}, + &eventmangler.ModuleParams{MinDelay: minDelayDuration, MaxDelay: maxDelayDuration, DropRate: float32(*dropRate)}, + ) + if err != nil { + panic(err) + } - mangler, err := eventmangler.NewModule( - eventmangler.ModuleConfig{Self: "mangler", Dest: "transport", Timer: "timer"}, - &eventmangler.ModuleParams{MinDelay: time.Second / 1000, MaxDelay: 1 * time.Second, DropRate: 0.05}, - ) - if err != nil { - panic(err) + modules["timer"] = timer.New() + modules["mangler"] = manglerModule } // Instantiate Mir node. node, err := mir.NewNode( ownNodeID, mir.DefaultNodeConfig(), - map[t.ModuleID]modules.Module{ - "transport": transport, - "bcm": NewBCM(logging.Decorate(logger, "BCM:\t")), - "miner": NewMiner(ownNodeID, 0.2, logging.Decorate(logger, "Miner:\t")), - "broadcast": NewBroadcast(otherNodes, mangle, logging.Decorate(logger, "Comm:\t")), - "application": application.NewApplication(logging.Decorate(logger, "App:\t"), ownNodeID), - "synchronizer": NewSynchronizer(ownNodeID, otherNodes, logging.Decorate(logger, "Sync:\t")), - "timer": timer, - "mangler": mangler, - "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor - }, + modules, wsInterceptor.NewWsInterceptor( func(e *eventpb.Event) bool { switch e.Type.(type) { @@ -107,7 +155,7 @@ func main() { return false } }, - 8080+ownID, + 8080+*nodeID, logging.Decorate(logger, "WSInter:\t"), )) if err != nil { diff --git a/samples/blockchain/run.sh b/samples/blockchain/run.sh index c44baea33..6feb9f20b 100755 --- a/samples/blockchain/run.sh +++ b/samples/blockchain/run.sh @@ -1,5 +1,7 @@ #!/bin/bash +CONFIG="-dropRate 0.05 -minDelay 0.02 -maxDelay 1" + NODE_0_LOG="./node_0.log" NODE_1_LOG="./node_1.log" NODE_2_LOG="./node_2.log" @@ -14,10 +16,10 @@ tmux new-session -d -s demo \; \ split-window -t demo:0.0 -h \; \ split-window -t "demo:0.2" -h \; \ \ - send-keys -t "demo:0.0" "go run . 4 0 true 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ - send-keys -t "demo:0.1" "go run . 4 1 true 2>&1 | tee \"$NODE_1_LOG\"" Enter \; \ - send-keys -t "demo:0.2" "go run . 4 2 true 2>&1 | tee \"$NODE_2_LOG\"" Enter \; \ - send-keys -t "demo:0.3" "go run . 4 3 true 2>&1 | tee \"$NODE_3_LOG\"" Enter \; \ + send-keys -t "demo:0.0" "go run . -numberOfNodes 4 -nodeID 0 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.1" "go run . -numberOfNodes 4 -nodeID 1 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.2" "go run . -numberOfNodes 4 -nodeID 2 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.3" "go run . -numberOfNodes 4 -nodeID 3 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ attach-session -t "demo:0.0" #!/usr/bin/env bash set -e diff --git a/samples/blockchain/test.sh b/samples/blockchain/test.sh index 56f3bbac4..d567f6558 100755 --- a/samples/blockchain/test.sh +++ b/samples/blockchain/test.sh @@ -1,5 +1,7 @@ #!/bin/bash +CONFIG="-dropRate 0.05 -minDelay 0.01 -maxDelay 1" + NODE_0_LOG="./node_0.log" NODE_1_LOG="./node_1.log" NODE_2_LOG="./node_2.log" @@ -14,10 +16,10 @@ tmux new-session -d -s demo \; \ split-window -t demo:0.0 -h \; \ split-window -t "demo:0.2" -h \; \ \ - send-keys -t "demo:0.0" "go run . 4 0 true 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ - send-keys -t "demo:0.1" "go run . 4 1 true 2>&1 | tee \"$NODE_1_LOG\"" Enter \; \ - send-keys -t "demo:0.2" "go run . 4 2 true 2>&1 | tee \"$NODE_2_LOG\"" Enter \; \ - send-keys -t "demo:0.3" "go run . 4 3 true 2>&1 | tee \"$NODE_3_LOG\"" Enter \; + send-keys -t "demo:0.0" "go run . -numberOfNodes 4 -nodeID 0 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.1" "go run . -numberOfNodes 4 -nodeID 1 $CONFIG 2>&1 | tee \"$NODE_1_LOG\"" Enter \; \ + send-keys -t "demo:0.2" "go run . -numberOfNodes 4 -nodeID 2 $CONFIG 2>&1 | tee \"$NODE_2_LOG\"" Enter \; \ + send-keys -t "demo:0.3" "go run . -numberOfNodes 4 -nodeID 3 $CONFIG 2>&1 | tee \"$NODE_3_LOG\"" Enter \; sleep 5 # wait for it to start up for i in {1..15}; do sleep 1; From 93400dfdaccd8bf23bf174e1f98c6676936a89e2 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sat, 3 Feb 2024 20:48:00 +0100 Subject: [PATCH 27/46] fix stuck chain bug --- samples/blockchain/application/application.go | 32 ++++++++----------- 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index 84ff27764..9135c3e0e 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -4,6 +4,7 @@ package application import ( "fmt" + "time" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" @@ -147,34 +148,27 @@ func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain, checkpoi // The checks are purely at the application level. // In this implementation, it simply checks that the timestamps are monotonically increasing for each sender. func (am *ApplicationModule) handleVerifyBlocksRequest(checkpointState *statepbtypes.State, chainCheckpointToStart, chainToVerify []*blockchainpbtypes.Block) error { - am.logger.Log(logging.LevelDebug, "Processing verify block request") + am.logger.Log(logging.LevelDebug, "Processing verify blocks request", "checkpoint", utils.FormatBlockId(chainCheckpointToStart[0].BlockId), "first block to verify", utils.FormatBlockId(chainToVerify[0].BlockId), "last block to verify", utils.FormatBlockId(chainToVerify[len(chainToVerify)-1].BlockId)) - timeStamps := checkpointState.LastSentTimestamps + timestampMap := make(map[t.NodeID]time.Time) + for _, lt := range checkpointState.LastSentTimestamps { + timestampMap[lt.NodeId] = lt.Timestamp.AsTime() + } chain := append(chainCheckpointToStart, chainToVerify...) // chainCheckpointToStart wouldn't need to be verified, but we need it to get the timestamps for _, block := range chain { blockTs := block.Payload.Timestamp.AsTime() // verify block - for i, lt := range timeStamps { - ltTimestamp := lt.Timestamp.AsTime() - if lt.NodeId == block.Payload.Sender { - if ltTimestamp.After(blockTs) { - // block in chain invalid, don't respond - return nil - } - - // remove old timestamp, if it exists - timeStamps[i] = timeStamps[len(timeStamps)-1] - timeStamps = timeStamps[:len(timeStamps)-1] + if ts, ok := timestampMap[block.Payload.Sender]; ok { + if ts.After(blockTs) { + // block in chain invalid, don't respond + return nil } - - // add new timestamp - timeStamps = append(timeStamps, &statepbtypes.LastSentTimestamp{ - NodeId: block.Payload.Sender, - Timestamp: block.Payload.Timestamp, - }) } + + // update timestamp + timestampMap[block.Payload.Sender] = blockTs } // if no blocks are invalid, responds with chain From 687027f724384994a75238f07ec7870d7b511ea8 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sat, 3 Feb 2024 20:48:15 +0100 Subject: [PATCH 28/46] more comments, more cleanup --- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go | 388 ++++-------------- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go | 2 - pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go | 8 - pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go | 12 - .../blockchainpb/bcmpb/events/events.mir.go | 33 -- .../bcmpb/oneof_interfaces.mir.go | 8 - pkg/pb/blockchainpb/bcmpb/types/types.mir.go | 125 ------ protos/blockchainpb/bcmpb/bcmpb.proto | 19 +- samples/blockchain/bcm.go | 57 ++- 9 files changed, 116 insertions(+), 536 deletions(-) diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go index e61e6c615..af4e36179 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go @@ -34,8 +34,6 @@ type Event struct { // *Event_NewChain // *Event_GetChainRequest // *Event_GetChainResponse - // *Event_GetHeadToCheckpointChainRequest - // *Event_GetHeadToCheckpointChainResponse // *Event_RegisterCheckpoint // *Event_InitBlockchain Type isEvent_Type `protobuf_oneof:"type"` @@ -108,20 +106,6 @@ func (x *Event) GetGetChainResponse() *GetChainResponse { return nil } -func (x *Event) GetGetHeadToCheckpointChainRequest() *GetHeadToCheckpointChainRequest { - if x, ok := x.GetType().(*Event_GetHeadToCheckpointChainRequest); ok { - return x.GetHeadToCheckpointChainRequest - } - return nil -} - -func (x *Event) GetGetHeadToCheckpointChainResponse() *GetHeadToCheckpointChainResponse { - if x, ok := x.GetType().(*Event_GetHeadToCheckpointChainResponse); ok { - return x.GetHeadToCheckpointChainResponse - } - return nil -} - func (x *Event) GetRegisterCheckpoint() *RegisterCheckpoint { if x, ok := x.GetType().(*Event_RegisterCheckpoint); ok { return x.RegisterCheckpoint @@ -156,16 +140,8 @@ type Event_GetChainResponse struct { GetChainResponse *GetChainResponse `protobuf:"bytes,6,opt,name=get_chain_response,json=getChainResponse,proto3,oneof"` } -type Event_GetHeadToCheckpointChainRequest struct { - GetHeadToCheckpointChainRequest *GetHeadToCheckpointChainRequest `protobuf:"bytes,7,opt,name=get_head_to_checkpoint_chain_request,json=getHeadToCheckpointChainRequest,proto3,oneof"` -} - -type Event_GetHeadToCheckpointChainResponse struct { - GetHeadToCheckpointChainResponse *GetHeadToCheckpointChainResponse `protobuf:"bytes,8,opt,name=get_head_to_checkpoint_chain_response,json=getHeadToCheckpointChainResponse,proto3,oneof"` -} - type Event_RegisterCheckpoint struct { - RegisterCheckpoint *RegisterCheckpoint `protobuf:"bytes,9,opt,name=register_checkpoint,json=registerCheckpoint,proto3,oneof"` + RegisterCheckpoint *RegisterCheckpoint `protobuf:"bytes,7,opt,name=register_checkpoint,json=registerCheckpoint,proto3,oneof"` } type Event_InitBlockchain struct { @@ -180,10 +156,6 @@ func (*Event_GetChainRequest) isEvent_Type() {} func (*Event_GetChainResponse) isEvent_Type() {} -func (*Event_GetHeadToCheckpointChainRequest) isEvent_Type() {} - -func (*Event_GetHeadToCheckpointChainResponse) isEvent_Type() {} - func (*Event_RegisterCheckpoint) isEvent_Type() {} func (*Event_InitBlockchain) isEvent_Type() {} @@ -417,124 +389,6 @@ func (x *GetChainResponse) GetChain() []*blockchainpb.Block { return nil } -type GetHeadToCheckpointChainRequest struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - SourceModule string `protobuf:"bytes,2,opt,name=source_module,json=sourceModule,proto3" json:"source_module,omitempty"` -} - -func (x *GetHeadToCheckpointChainRequest) Reset() { - *x = GetHeadToCheckpointChainRequest{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetHeadToCheckpointChainRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetHeadToCheckpointChainRequest) ProtoMessage() {} - -func (x *GetHeadToCheckpointChainRequest) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] - 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 GetHeadToCheckpointChainRequest.ProtoReflect.Descriptor instead. -func (*GetHeadToCheckpointChainRequest) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{5} -} - -func (x *GetHeadToCheckpointChainRequest) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -func (x *GetHeadToCheckpointChainRequest) GetSourceModule() string { - if x != nil { - return x.SourceModule - } - return "" -} - -type GetHeadToCheckpointChainResponse struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - RequestId string `protobuf:"bytes,1,opt,name=request_id,json=requestId,proto3" json:"request_id,omitempty"` - Chain []*blockchainpb.Block `protobuf:"bytes,2,rep,name=chain,proto3" json:"chain,omitempty"` // from checkpoint to head - CheckpointState *statepb.State `protobuf:"bytes,3,opt,name=checkpoint_state,json=checkpointState,proto3" json:"checkpoint_state,omitempty"` -} - -func (x *GetHeadToCheckpointChainResponse) Reset() { - *x = GetHeadToCheckpointChainResponse{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *GetHeadToCheckpointChainResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*GetHeadToCheckpointChainResponse) ProtoMessage() {} - -func (x *GetHeadToCheckpointChainResponse) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] - 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 GetHeadToCheckpointChainResponse.ProtoReflect.Descriptor instead. -func (*GetHeadToCheckpointChainResponse) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{6} -} - -func (x *GetHeadToCheckpointChainResponse) GetRequestId() string { - if x != nil { - return x.RequestId - } - return "" -} - -func (x *GetHeadToCheckpointChainResponse) GetChain() []*blockchainpb.Block { - if x != nil { - return x.Chain - } - return nil -} - -func (x *GetHeadToCheckpointChainResponse) GetCheckpointState() *statepb.State { - if x != nil { - return x.CheckpointState - } - return nil -} - type RegisterCheckpoint struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -547,7 +401,7 @@ type RegisterCheckpoint struct { func (x *RegisterCheckpoint) Reset() { *x = RegisterCheckpoint{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -560,7 +414,7 @@ func (x *RegisterCheckpoint) String() string { func (*RegisterCheckpoint) ProtoMessage() {} func (x *RegisterCheckpoint) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -573,7 +427,7 @@ func (x *RegisterCheckpoint) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterCheckpoint.ProtoReflect.Descriptor instead. func (*RegisterCheckpoint) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{7} + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{5} } func (x *RegisterCheckpoint) GetBlockId() uint64 { @@ -601,7 +455,7 @@ type InitBlockchain struct { func (x *InitBlockchain) Reset() { *x = InitBlockchain{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -614,7 +468,7 @@ func (x *InitBlockchain) String() string { func (*InitBlockchain) ProtoMessage() {} func (x *InitBlockchain) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -627,7 +481,7 @@ func (x *InitBlockchain) ProtoReflect() protoreflect.Message { // Deprecated: Use InitBlockchain.ProtoReflect.Descriptor instead. func (*InitBlockchain) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{8} + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{6} } func (x *InitBlockchain) GetInitialState() *statepb.State { @@ -648,7 +502,7 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8f, 0x05, 0x0a, 0x05, 0x45, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x03, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, @@ -664,98 +518,62 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x10, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x77, 0x0a, 0x24, 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, - 0x74, 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x26, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, - 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, - 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x1f, 0x67, 0x65, 0x74, - 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x7a, 0x0a, 0x25, - 0x67, 0x65, 0x74, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x72, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x62, 0x63, - 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, - 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x20, 0x67, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, - 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x13, 0x72, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x65, 0x72, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, - 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x52, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x48, 0x00, 0x52, 0x12, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x62, - 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x15, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, - 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, - 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, - 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x6e, 0x64, 0x5f, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, - 0x65, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, - 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, - 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x6c, 0x6f, 0x63, - 0x6b, 0x49, 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x47, 0x65, - 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, - 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, - 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, - 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xa3, 0x01, 0x0a, 0x1f, 0x47, 0x65, 0x74, - 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, - 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, - 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, - 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, - 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, - 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, - 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xad, - 0x01, 0x0a, 0x20, 0x47, 0x65, 0x74, 0x48, 0x65, 0x61, 0x64, 0x54, 0x6f, 0x43, 0x68, 0x65, 0x63, - 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, - 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x49, 0x64, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, + 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x13, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x5f, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, + 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x12, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x63, 0x6d, + 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, + 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, + 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, + 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x98, + 0xa6, 0x1d, 0x01, 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, + 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, + 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, + 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, + 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, + 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x73, 0x3a, + 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, + 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, - 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x39, 0x0a, - 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, - 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, - 0x69, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5b, - 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, - 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, - 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x4b, 0x0a, 0x0e, 0x49, - 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x33, 0x0a, - 0x0d, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, - 0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x61, - 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, - 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, - 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x98, + 0xa6, 0x1d, 0x01, 0x22, 0x5b, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, + 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, + 0x63, 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, + 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, + 0x22, 0x4b, 0x0a, 0x0e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x0d, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, + 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x69, 0x6e, 0x69, 0x74, 0x69, + 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, + 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, + 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, + 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -770,41 +588,35 @@ func file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP() []byte { return file_blockchainpb_bcmpb_bcmpb_proto_rawDescData } -var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 9) +var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_blockchainpb_bcmpb_bcmpb_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: bcmpb.Event - (*NewBlock)(nil), // 1: bcmpb.NewBlock - (*NewChain)(nil), // 2: bcmpb.NewChain - (*GetChainRequest)(nil), // 3: bcmpb.GetChainRequest - (*GetChainResponse)(nil), // 4: bcmpb.GetChainResponse - (*GetHeadToCheckpointChainRequest)(nil), // 5: bcmpb.GetHeadToCheckpointChainRequest - (*GetHeadToCheckpointChainResponse)(nil), // 6: bcmpb.GetHeadToCheckpointChainResponse - (*RegisterCheckpoint)(nil), // 7: bcmpb.RegisterCheckpoint - (*InitBlockchain)(nil), // 8: bcmpb.InitBlockchain - (*blockchainpb.Block)(nil), // 9: blockchainpb.Block - (*statepb.State)(nil), // 10: statepb.State + (*Event)(nil), // 0: bcmpb.Event + (*NewBlock)(nil), // 1: bcmpb.NewBlock + (*NewChain)(nil), // 2: bcmpb.NewChain + (*GetChainRequest)(nil), // 3: bcmpb.GetChainRequest + (*GetChainResponse)(nil), // 4: bcmpb.GetChainResponse + (*RegisterCheckpoint)(nil), // 5: bcmpb.RegisterCheckpoint + (*InitBlockchain)(nil), // 6: bcmpb.InitBlockchain + (*blockchainpb.Block)(nil), // 7: blockchainpb.Block + (*statepb.State)(nil), // 8: statepb.State } var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ 1, // 0: bcmpb.Event.new_block:type_name -> bcmpb.NewBlock 2, // 1: bcmpb.Event.new_chain:type_name -> bcmpb.NewChain 3, // 2: bcmpb.Event.get_chain_request:type_name -> bcmpb.GetChainRequest 4, // 3: bcmpb.Event.get_chain_response:type_name -> bcmpb.GetChainResponse - 5, // 4: bcmpb.Event.get_head_to_checkpoint_chain_request:type_name -> bcmpb.GetHeadToCheckpointChainRequest - 6, // 5: bcmpb.Event.get_head_to_checkpoint_chain_response:type_name -> bcmpb.GetHeadToCheckpointChainResponse - 7, // 6: bcmpb.Event.register_checkpoint:type_name -> bcmpb.RegisterCheckpoint - 8, // 7: bcmpb.Event.init_blockchain:type_name -> bcmpb.InitBlockchain - 9, // 8: bcmpb.NewBlock.block:type_name -> blockchainpb.Block - 9, // 9: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block - 9, // 10: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block - 9, // 11: bcmpb.GetHeadToCheckpointChainResponse.chain:type_name -> blockchainpb.Block - 10, // 12: bcmpb.GetHeadToCheckpointChainResponse.checkpoint_state:type_name -> statepb.State - 10, // 13: bcmpb.RegisterCheckpoint.state:type_name -> statepb.State - 10, // 14: bcmpb.InitBlockchain.initial_state:type_name -> statepb.State - 15, // [15:15] is the sub-list for method output_type - 15, // [15:15] is the sub-list for method input_type - 15, // [15:15] is the sub-list for extension type_name - 15, // [15:15] is the sub-list for extension extendee - 0, // [0:15] is the sub-list for field type_name + 5, // 4: bcmpb.Event.register_checkpoint:type_name -> bcmpb.RegisterCheckpoint + 6, // 5: bcmpb.Event.init_blockchain:type_name -> bcmpb.InitBlockchain + 7, // 6: bcmpb.NewBlock.block:type_name -> blockchainpb.Block + 7, // 7: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block + 7, // 8: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block + 8, // 9: bcmpb.RegisterCheckpoint.state:type_name -> statepb.State + 8, // 10: bcmpb.InitBlockchain.initial_state:type_name -> statepb.State + 11, // [11:11] is the sub-list for method output_type + 11, // [11:11] is the sub-list for method input_type + 11, // [11:11] is the sub-list for extension type_name + 11, // [11:11] is the sub-list for extension extendee + 0, // [0:11] is the sub-list for field type_name } func init() { file_blockchainpb_bcmpb_bcmpb_proto_init() } @@ -874,30 +686,6 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { } } file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetHeadToCheckpointChainRequest); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*GetHeadToCheckpointChainResponse); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*RegisterCheckpoint); i { case 0: return &v.state @@ -909,7 +697,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { return nil } } - file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InitBlockchain); i { case 0: return &v.state @@ -927,8 +715,6 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { (*Event_NewChain)(nil), (*Event_GetChainRequest)(nil), (*Event_GetChainResponse)(nil), - (*Event_GetHeadToCheckpointChainRequest)(nil), - (*Event_GetHeadToCheckpointChainResponse)(nil), (*Event_RegisterCheckpoint)(nil), (*Event_InitBlockchain)(nil), } @@ -938,7 +724,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_bcmpb_bcmpb_proto_rawDesc, NumEnums: 0, - NumMessages: 9, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go index 2b384fed7..fa04aa9f4 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go @@ -12,8 +12,6 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_NewChain)(nil)), reflect.TypeOf((*Event_GetChainRequest)(nil)), reflect.TypeOf((*Event_GetChainResponse)(nil)), - reflect.TypeOf((*Event_GetHeadToCheckpointChainRequest)(nil)), - reflect.TypeOf((*Event_GetHeadToCheckpointChainResponse)(nil)), reflect.TypeOf((*Event_RegisterCheckpoint)(nil)), reflect.TypeOf((*Event_InitBlockchain)(nil)), } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go index b81f6fa71..21c82a57c 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go @@ -28,14 +28,6 @@ func GetChainResponse(m dsl.Module, destModule types.ModuleID, requestId string, dsl.EmitMirEvent(m, events.GetChainResponse(destModule, requestId, success, chain)) } -func GetHeadToCheckpointChainRequest(m dsl.Module, destModule types.ModuleID, requestId string, sourceModule types.ModuleID) { - dsl.EmitMirEvent(m, events.GetHeadToCheckpointChainRequest(destModule, requestId, sourceModule)) -} - -func GetHeadToCheckpointChainResponse(m dsl.Module, destModule types.ModuleID, requestId string, chain []*types1.Block, checkpointState *types2.State) { - dsl.EmitMirEvent(m, events.GetHeadToCheckpointChainResponse(destModule, requestId, chain, checkpointState)) -} - func RegisterCheckpoint(m dsl.Module, destModule types.ModuleID, blockId uint64, state *types2.State) { dsl.EmitMirEvent(m, events.RegisterCheckpoint(destModule, blockId, state)) } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go index 75d4898c2..611e97369 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go @@ -48,18 +48,6 @@ func UponGetChainResponse(m dsl.Module, handler func(requestId string, success b }) } -func UponGetHeadToCheckpointChainRequest(m dsl.Module, handler func(requestId string, sourceModule types3.ModuleID) error) { - UponEvent[*types.Event_GetHeadToCheckpointChainRequest](m, func(ev *types.GetHeadToCheckpointChainRequest) error { - return handler(ev.RequestId, ev.SourceModule) - }) -} - -func UponGetHeadToCheckpointChainResponse(m dsl.Module, handler func(requestId string, chain []*types2.Block, checkpointState *types4.State) error) { - UponEvent[*types.Event_GetHeadToCheckpointChainResponse](m, func(ev *types.GetHeadToCheckpointChainResponse) error { - return handler(ev.RequestId, ev.Chain, ev.CheckpointState) - }) -} - func UponRegisterCheckpoint(m dsl.Module, handler func(blockId uint64, state *types4.State) error) { UponEvent[*types.Event_RegisterCheckpoint](m, func(ev *types.RegisterCheckpoint) error { return handler(ev.BlockId, ev.State) diff --git a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go index 03c6019e3..b07018fde 100644 --- a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go @@ -75,39 +75,6 @@ func GetChainResponse(destModule types.ModuleID, requestId string, success bool, } } -func GetHeadToCheckpointChainRequest(destModule types.ModuleID, requestId string, sourceModule types.ModuleID) *types2.Event { - return &types2.Event{ - DestModule: destModule, - Type: &types2.Event_Bcm{ - Bcm: &types3.Event{ - Type: &types3.Event_GetHeadToCheckpointChainRequest{ - GetHeadToCheckpointChainRequest: &types3.GetHeadToCheckpointChainRequest{ - RequestId: requestId, - SourceModule: sourceModule, - }, - }, - }, - }, - } -} - -func GetHeadToCheckpointChainResponse(destModule types.ModuleID, requestId string, chain []*types1.Block, checkpointState *types4.State) *types2.Event { - return &types2.Event{ - DestModule: destModule, - Type: &types2.Event_Bcm{ - Bcm: &types3.Event{ - Type: &types3.Event_GetHeadToCheckpointChainResponse{ - GetHeadToCheckpointChainResponse: &types3.GetHeadToCheckpointChainResponse{ - RequestId: requestId, - Chain: chain, - CheckpointState: checkpointState, - }, - }, - }, - }, - } -} - func RegisterCheckpoint(destModule types.ModuleID, blockId uint64, state *types4.State) *types2.Event { return &types2.Event{ DestModule: destModule, diff --git a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go index d67bc0451..28edad5e7 100644 --- a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go @@ -25,14 +25,6 @@ func (w *Event_GetChainResponse) Unwrap() *GetChainResponse { return w.GetChainResponse } -func (w *Event_GetHeadToCheckpointChainRequest) Unwrap() *GetHeadToCheckpointChainRequest { - return w.GetHeadToCheckpointChainRequest -} - -func (w *Event_GetHeadToCheckpointChainResponse) Unwrap() *GetHeadToCheckpointChainResponse { - return w.GetHeadToCheckpointChainResponse -} - func (w *Event_RegisterCheckpoint) Unwrap() *RegisterCheckpoint { return w.RegisterCheckpoint } diff --git a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go index 706ab7e7a..22e6c6770 100644 --- a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go @@ -41,10 +41,6 @@ func Event_TypeFromPb(pb bcmpb.Event_Type) Event_Type { return &Event_GetChainRequest{GetChainRequest: GetChainRequestFromPb(pb.GetChainRequest)} case *bcmpb.Event_GetChainResponse: return &Event_GetChainResponse{GetChainResponse: GetChainResponseFromPb(pb.GetChainResponse)} - case *bcmpb.Event_GetHeadToCheckpointChainRequest: - return &Event_GetHeadToCheckpointChainRequest{GetHeadToCheckpointChainRequest: GetHeadToCheckpointChainRequestFromPb(pb.GetHeadToCheckpointChainRequest)} - case *bcmpb.Event_GetHeadToCheckpointChainResponse: - return &Event_GetHeadToCheckpointChainResponse{GetHeadToCheckpointChainResponse: GetHeadToCheckpointChainResponseFromPb(pb.GetHeadToCheckpointChainResponse)} case *bcmpb.Event_RegisterCheckpoint: return &Event_RegisterCheckpoint{RegisterCheckpoint: RegisterCheckpointFromPb(pb.RegisterCheckpoint)} case *bcmpb.Event_InitBlockchain: @@ -149,54 +145,6 @@ func (*Event_GetChainResponse) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetChainResponse]()} } -type Event_GetHeadToCheckpointChainRequest struct { - GetHeadToCheckpointChainRequest *GetHeadToCheckpointChainRequest -} - -func (*Event_GetHeadToCheckpointChainRequest) isEvent_Type() {} - -func (w *Event_GetHeadToCheckpointChainRequest) Unwrap() *GetHeadToCheckpointChainRequest { - return w.GetHeadToCheckpointChainRequest -} - -func (w *Event_GetHeadToCheckpointChainRequest) Pb() bcmpb.Event_Type { - if w == nil { - return nil - } - if w.GetHeadToCheckpointChainRequest == nil { - return &bcmpb.Event_GetHeadToCheckpointChainRequest{} - } - return &bcmpb.Event_GetHeadToCheckpointChainRequest{GetHeadToCheckpointChainRequest: (w.GetHeadToCheckpointChainRequest).Pb()} -} - -func (*Event_GetHeadToCheckpointChainRequest) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetHeadToCheckpointChainRequest]()} -} - -type Event_GetHeadToCheckpointChainResponse struct { - GetHeadToCheckpointChainResponse *GetHeadToCheckpointChainResponse -} - -func (*Event_GetHeadToCheckpointChainResponse) isEvent_Type() {} - -func (w *Event_GetHeadToCheckpointChainResponse) Unwrap() *GetHeadToCheckpointChainResponse { - return w.GetHeadToCheckpointChainResponse -} - -func (w *Event_GetHeadToCheckpointChainResponse) Pb() bcmpb.Event_Type { - if w == nil { - return nil - } - if w.GetHeadToCheckpointChainResponse == nil { - return &bcmpb.Event_GetHeadToCheckpointChainResponse{} - } - return &bcmpb.Event_GetHeadToCheckpointChainResponse{GetHeadToCheckpointChainResponse: (w.GetHeadToCheckpointChainResponse).Pb()} -} - -func (*Event_GetHeadToCheckpointChainResponse) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetHeadToCheckpointChainResponse]()} -} - type Event_RegisterCheckpoint struct { RegisterCheckpoint *RegisterCheckpoint } @@ -413,79 +361,6 @@ func (*GetChainResponse) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetChainResponse]()} } -type GetHeadToCheckpointChainRequest struct { - RequestId string - SourceModule types2.ModuleID -} - -func GetHeadToCheckpointChainRequestFromPb(pb *bcmpb.GetHeadToCheckpointChainRequest) *GetHeadToCheckpointChainRequest { - if pb == nil { - return nil - } - return &GetHeadToCheckpointChainRequest{ - RequestId: pb.RequestId, - SourceModule: (types2.ModuleID)(pb.SourceModule), - } -} - -func (m *GetHeadToCheckpointChainRequest) Pb() *bcmpb.GetHeadToCheckpointChainRequest { - if m == nil { - return nil - } - pbMessage := &bcmpb.GetHeadToCheckpointChainRequest{} - { - pbMessage.RequestId = m.RequestId - pbMessage.SourceModule = (string)(m.SourceModule) - } - - return pbMessage -} - -func (*GetHeadToCheckpointChainRequest) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetHeadToCheckpointChainRequest]()} -} - -type GetHeadToCheckpointChainResponse struct { - RequestId string - Chain []*types.Block - CheckpointState *types3.State -} - -func GetHeadToCheckpointChainResponseFromPb(pb *bcmpb.GetHeadToCheckpointChainResponse) *GetHeadToCheckpointChainResponse { - if pb == nil { - return nil - } - return &GetHeadToCheckpointChainResponse{ - RequestId: pb.RequestId, - Chain: types1.ConvertSlice(pb.Chain, func(t *blockchainpb.Block) *types.Block { - return types.BlockFromPb(t) - }), - CheckpointState: types3.StateFromPb(pb.CheckpointState), - } -} - -func (m *GetHeadToCheckpointChainResponse) Pb() *bcmpb.GetHeadToCheckpointChainResponse { - if m == nil { - return nil - } - pbMessage := &bcmpb.GetHeadToCheckpointChainResponse{} - { - pbMessage.RequestId = m.RequestId - pbMessage.Chain = types1.ConvertSlice(m.Chain, func(t *types.Block) *blockchainpb.Block { - return (t).Pb() - }) - if m.CheckpointState != nil { - pbMessage.CheckpointState = (m.CheckpointState).Pb() - } - } - - return pbMessage -} - -func (*GetHeadToCheckpointChainResponse) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetHeadToCheckpointChainResponse]()} -} - type RegisterCheckpoint struct { BlockId uint64 State *types3.State diff --git a/protos/blockchainpb/bcmpb/bcmpb.proto b/protos/blockchainpb/bcmpb/bcmpb.proto index 260d5cedc..8f23dc126 100644 --- a/protos/blockchainpb/bcmpb/bcmpb.proto +++ b/protos/blockchainpb/bcmpb/bcmpb.proto @@ -20,9 +20,7 @@ message Event { GetChainRequest get_chain_request = 5; GetChainResponse get_chain_response = 6; - GetHeadToCheckpointChainRequest get_head_to_checkpoint_chain_request = 7; - GetHeadToCheckpointChainResponse get_head_to_checkpoint_chain_response = 8; - RegisterCheckpoint register_checkpoint = 9; + RegisterCheckpoint register_checkpoint = 7; InitBlockchain init_blockchain = 100; } @@ -59,21 +57,6 @@ message GetChainResponse { repeated blockchainpb.Block chain = 3; // sorted from oldest to youngest } -message GetHeadToCheckpointChainRequest { - option (mir.event) = true; - - string request_id = 1; - string source_module = 2 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.ModuleID"]; -} - -message GetHeadToCheckpointChainResponse { - option (mir.event) = true; - - string request_id = 1; - repeated blockchainpb.Block chain = 2; // from checkpoint to head - statepb.State checkpoint_state = 3; -} - message RegisterCheckpoint { option (mir.event) = true; diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index c59751ce8..2e2938f41 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -24,6 +24,31 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) +/** + * Blockchain Manager Module (BCM) + * =============================== + * + * The blockchain manager module is responsible for managing the blockchain. + * It keeps track of all blocks and links them together to form a tree. + * In particular, it keeps track of the head of the blockchain, all leaves and so-called checkpoints. + * A checkpoint is a block stored by the BCM that has a state stored with it. + * Technically, checkpoints are not necessary as the state can be computed from the blocks. + * However, it is convenient to not have to recompute the state from the genesis block every time it is needed. + * + * The BCM must perform the following tasks: + * 1. Initialize the blockchain by receiving an InitBlockchain event from the application module which contains the initial state that is associated with the genesis block. + * 2. Add new blocks to the blockchain. If a block is added that has a parent that is not in the blockchain, the BCM requests the missing block from the synchronizer. + * Blocks that are missing their parent are called orphans. + * All blocks added to the blockchain are verified in two steps: + * - It has the application module verify that the payloads are valid given the chain that the block is part of. + * - The BCM must verify that the blocks link together correctly. + * Additionally, it sends a TreeUpdate event to the interceptor module. This is solely for debugging/visualization purposes and not necessary for the operation of the blockchain. + * 3. Register checkpoints when receiving a RegisterCheckpoint event from the application module. + * 4. It must provide the synchronizer with chains when requested. This is to resolve orphan blocks in other nodes. + * 5. When the head changes, it sends a ForkUpdate event to the application module. This event contains all information necessary for the application to compute the state at the new head + * as well as information about which payloads are now part of the canonical (i.e., longest) and which ones are no longer part of the canonical chain. + */ + var ( ErrParentNotFound = errors.New("parent not found") ErrNodeAlreadyInTree = errors.New("node already in tree") @@ -346,6 +371,8 @@ func (bcm *bcmModule) sendTreeUpdate() { */ func (bcm *bcmModule) handleVerifyBlocksResponse(blocks []*blockchainpbtypes.Block) error { + bcm.logger.Log(logging.LevelDebug, "Received verify blocks response", "first block", utils.FormatBlockId(blocks[0].BlockId), "last block", utils.FormatBlockId(blocks[len(blocks)-1].BlockId)) + currentHead := bcm.head // insert block for _, block := range blocks { @@ -388,31 +415,11 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpbtypes.Block) error { return nil } - bcm.logger.Log(logging.LevelDebug, "Adding new chain", "first block", utils.FormatBlockId(blocks[0].BlockId), "second block", utils.FormatBlockId(blocks[len(blocks)-1].BlockId)) + bcm.logger.Log(logging.LevelDebug, "Adding new chain", "first block", utils.FormatBlockId(blocks[0].BlockId), "last block", utils.FormatBlockId(blocks[len(blocks)-1].BlockId)) return bcm.processNewChain(blocks) } -func (bcm *bcmModule) handleGetHeadToCheckpointChainRequest(requestID string, sourceModule t.ModuleID) error { - if err := bcm.checkInitialization(); err != nil { - return err - } - - chain, checkpointState := bcm.getChainFromCheckpointToBlock(bcm.head) - if chain == nil { - bcm.logger.Log(logging.LevelError, "Cannot link head up with checkpoints", "head", utils.FormatBlockId(bcm.head.block.BlockId)) - return nil - } else if checkpointState == nil { - bcm.logger.Log(logging.LevelError, "Linked head up with checkpoint but state is missing - this should not happen", "head", utils.FormatBlockId(bcm.head.block.BlockId)) - panic(errors.New("linked head up with checkpoint but state is missing - this should not happen")) - } - - bcmpbdsl.GetHeadToCheckpointChainResponse(*bcm.m, sourceModule, requestID, chain, checkpointState) - - return nil - -} - func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepbtypes.State) error { bcm.logger.Log(logging.LevelInfo, "Received register checkpoint", "blockId", utils.FormatBlockId(block_id)) if err := bcm.checkInitialization(); err != nil { @@ -569,14 +576,6 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { return bcm.handleGetChainRequest(requestID, sourceModule, endBlockId, sourceBlockIds) }) - bcmpbdsl.UponGetHeadToCheckpointChainRequest(m, func(requestId string, sourceModule t.ModuleID) error { - bcm.logger.Log(logging.LevelTrace, "Received get head to checkpoint chain request") - if err := bcm.checkInitialization(); err != nil { - return err - } - return bcm.handleGetHeadToCheckpointChainRequest(requestId, sourceModule) - }) - bcmpbdsl.UponRegisterCheckpoint(m, bcm.handleRegisterCheckpoint) bcmpbdsl.UponInitBlockchain(m, bcm.handleInitBlockchain) From 0e0d4bd2060f1ef6a57b36b0775333dc30c1f0fc Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sat, 3 Feb 2024 20:48:30 +0100 Subject: [PATCH 29/46] start read me --- samples/blockchain/README.md | 209 +++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 samples/blockchain/README.md diff --git a/samples/blockchain/README.md b/samples/blockchain/README.md new file mode 100644 index 000000000..2cc9d276a --- /dev/null +++ b/samples/blockchain/README.md @@ -0,0 +1,209 @@ +# Longest Chain Consensus + +LCC is a modular implementation of a longest chain consensus protocol modelling proof-of-work. + +It provides the core elements with only the actual buisiness logic to be implemented by an application module. + +It consists of the following core modules: + +- **Blockchain Management Module (BCM)** + + It forms the core of the system and is responsible for managing the blockchain. + +- **Miner Module** + + Mines new blocks simulating proof-of-work. + +- **Synchronizer Module** + + Resolves issues when new blocks are to be added to the blockchain but their parent blocks are unknown to this node's BCM. + +- **Broadcast Module** + +Broadcasts newly mined blocks to all other nodes. +It can also simulate network delay and dropped messages. + +and additional modules: + +- **gRPC Transport Module** + +Used for communication between nodes. + +- **Event Mangler** + +Used by the broadcast module to simulate network delay and dropped messages. + +- **Timer Module** + +Used by the Miner to simulate proof-of-work. + +Further, it also allows includes an interceptor which intercepts all communication between different modules and allows for visualization/debugging tools to consume this communication via a websocket connection. +An example of how to use the information provided the interception can be found [here](https://github.com/komplexon3/longest-chain-project). + +(TODO - fix link!) + +## Architecture + +```mermaid +flowchart TB; + +subgraph Longest Chain Consensus +BCM[Blockchain Management \n Module] +Miner[Miner \n Module] +Synchronizer[Synchronizer \n Module] +Broadcast[Broadcast \n Module] +Transport[Transport \n Module] + +end + +Application[Application \n Module] + + +Broadcast <--> Transport + +Miner <--> Application +Miner <--> Broadcast + +BCM <--> Miner +BCM <--> Application +BCM <--> Synchronizer + +Synchronizer <--> Transport +``` + +```mermaid +sequenceDiagram + Broadcast ->> BCM: NewBlock + Note over BCM: Check that the block can be connected to the block tree + opt if it cannot connect the chain + BCM ->> Synchronizer: SyncRequest + Note over Synchronizer: Get missing blocks from other nodes, details omitted + Synchronizer ->> BCM: SyncResponse + end + BCM ->> Application: VerifyChainRequest + Note over Application: Verify that payloads are valid + Application ->> BCM: VerifyChainRespose + Note over BCM: Add blocks of chain to block tree, verify that they link together + + + opt if head changes + BCM ->> Miner: NewHead + Note over Miner: Abort current mining operation, prepare to mine on new head + Miner ->> Application: PayloadRequest + Application ->> Miner: PayloadResponse + Note over Miner: Start mining on new head + BCM ->> Application: ForkUpdate + Note over Application: Compute state for new head + Application ->> BCM: RegisterCheckpoint + end +``` + +### Blockchain Management Module (BCM) + +The blockchain manager module is responsible for managing the blockchain. +It keeps track of all blocks and links them together to form a tree. +In particular, it keeps track of the head of the blockchain, all leaves and so-called checkpoints. +A checkpoint is a block stored by the BCM that has a state stored with it. +Technically, checkpoints are not necessary as the state can be computed from the blocks. +However, it is convenient to not have to recompute the state from the genesis block every time it is needed. +The BCM must perform the following tasks: + +1. Initialize the blockchain by receiving an InitBlockchain event from the application module which contains the initial state that is associated with the genesis block. +2. Add new blocks to the blockchain. If a block is added that has a parent that is not in the blockchain, the BCM requests the missing block from the synchronizer. + Blocks that are missing their parent are called orphans. + All blocks added to the blockchain are verified in two steps: + - It has the application module verify that the payloads are valid given the chain that the block is part of. + - The BCM must verify that the blocks link together correctly. + Additionally, it sends a TreeUpdate event to the interceptor module. This is solely for debugging/visualization purposes and not necessary for the operation of the blockchain. +3. Register checkpoints when receiving a RegisterCheckpoint event from the application module. +4. It must provide the synchronizer with chains when requested. This is to resolve orphan blocks in other nodes. +5. When the head changes, it sends a ForkUpdate event to the application module. This event contains all information necessary for the application to compute the state at the new head + as well as information about which payloads are now part of the canonical (i.e., longest) and which ones are no longer part of the canonical chain. + +### Miner Module + +The miner module is responsible for mining new blocks. +It simulates the process of mining a block by waiting for a random amount of time and then broadcasting the mined block. +This random amount of time is sampled from an exponential distribution with a mean of `expMinuteFactor` minutes. +The mining is orchestrated by a separate goroutine (mineWorkerManager), so that the miner module can continue to receive and process events. +The operation of the miner module at a high level is as follows: + +1. When it is notified of a new head (NewHead event), it prepares to mine the next block by sending a PayloadRequest event to the application module. + If it is already mining a block, it aborts the ongoing mining operation. +2. When it receives the PayloadResponse containing a payload for the next block, it starts mining a new block with the received payload. +3. When it mines a new block, it broadcasts it to all other modules by sending a NewBlock message to the broadcast module. + It also shares the block with the blockchain manager module (BCM) by sending a NewBlock event to it. + +### Broadcast Module + +The broadcast module is responsible for broadcasting new blocks to all other nodes. +It either does this directly via the transport module or via the mangler (parameter mangle). +If the mangler is used, messages might will be dropped and delayed. + +### Synchronizer Module + +The synchronizer module assists the blockchain manager (BCM) in resolving cases whe BCM receives an orphan block. +That is, a block that cannot be linked to the blockchain because the blockchain does not contain the block that the orphan block is linked to. +To do this, the synchronizer module communicates with other nodes to get the missing blocks. + +Terminology: + +- internal sync request: a request to synchronize a chain segment that was initiated by this node +- external sync request: a request to synchronize a chain segment that was initiated by another node + +The synchronizer module performs the following tasks: + +For internal sync requests: + +1. When it receives a SyncRequest event, it must register the request and send a ChainRequest message to another node. + It asks one node after another. +2. When it receives a successful ChainResponse message, it sends the blockchain manager (BCM) the chain fixing the missing bit with a Chain event. + It then deletes the request. +3. When it receives a failed ChainResponse message, it sends a ChainRequest message to the next node. + If there are no more nodes to ask, it deletes the request. + +For external sync requests: + +1. When it receives a ChainRequest message, it must register the request and send a GetChainRequest event to the BCM. +2. The BCM will respond with a GetChainResponse event. The synchronizer then responds to the node that sent the ChainRequest message with a ChainResponse message. + IMPORTANT: This module assumes that all other nodes resppond to requests and that no messages are lost. + +### Application Module + +The application module is reponsible for performing the actual application logic and to interact with users or other applications. +It does not hold any state, but instead relies on the blockchain manager module (BCM) to store the state. +However, the application needs to compute the state. +Also, the application module is responsible for providing payloads for new blocks. + +The application module must perform the following tasks: + +1. Initialize the blockchain by sending it the initial state in an InitBlockchain event to the BCM. +2. When it receives a PayloadRequest event, it must provide a payload for the next block. This payload can be empty. +3. When it receives a ForkUpdate event, it must compute the state at the new head of the blockchain. + This state is then registered with the BCM by sending it a RegisterCheckpoint event. + A checkpoint is a block stored by the BCM that has a state stored with it. +4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at a application level and respond with a VerifyBlocksResponse event. + Whether or not not the blocks link together correctly is verified by the BCM. + +This application module implements a simple chat application. +It takes new messages from the user (MessageInput event) and combines them with a sender id and "sent" timestamp as payloads. +These payloads are stored in the payload manager (see applicaion/payloads/payloads.go). +The state is the list of all messages that have been sent and timestamps for when each sender last sent a message. +At the application level, a chain is valid if the timestamps are monotonically increasing for each sender. + +### Websocket Interceptor + +The websocket interceptor intercepts all events and sends them to a websocket server. +Any connected client can then receive these events by subscribing to the websocket server. +The interceptor proto defines events which are specificly intended for the interceptor and not used by the actual blockchain. +Since these events don't have a destination module, they are sent to the "devnull" module. +However, all events are intercepted and sent to the websocket server. The interceptor proto is simply for "extra" events. + +The interceptor proto defines two such events: + +- TreeUpdate: This event is sent by the blockchain manager (BCM) when the blockchain is updated. It contains all blocks in the blockchain and the id of the new head. +- StateUpdate: This event is sent by the application when it computes the state for the newest head of the blockchain. + +## How to use + +## Example: Chat App From 1d4f854eb3c42495d11a93e9e962b17878f4d7b5 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sat, 3 Feb 2024 21:59:17 +0100 Subject: [PATCH 30/46] docs wip --- samples/blockchain/README.md | 108 ++++++++++++++++++----------------- 1 file changed, 56 insertions(+), 52 deletions(-) diff --git a/samples/blockchain/README.md b/samples/blockchain/README.md index 2cc9d276a..910b1c3f3 100644 --- a/samples/blockchain/README.md +++ b/samples/blockchain/README.md @@ -4,55 +4,50 @@ LCC is a modular implementation of a longest chain consensus protocol modelling It provides the core elements with only the actual buisiness logic to be implemented by an application module. -It consists of the following core modules: +## Architecture -- **Blockchain Management Module (BCM)** +The system consists of the following core modules: +- **Blockchain Management Module (BCM):** It forms the core of the system and is responsible for managing the blockchain. -- **Miner Module** - +- **Miner Module:** Mines new blocks simulating proof-of-work. -- **Synchronizer Module** - +- **Synchronizer Module:** Resolves issues when new blocks are to be added to the blockchain but their parent blocks are unknown to this node's BCM. -- **Broadcast Module** +- **Broadcast Module:** + Broadcasts newly mined blocks to all other nodes. + It can also simulate network delay and dropped messages. -Broadcasts newly mined blocks to all other nodes. -It can also simulate network delay and dropped messages. +and the following supporting modules: -and additional modules: +- **gRPC Transport Module:** + Used for communication between nodes. -- **gRPC Transport Module** +- **Event Mangler Module:** + Used by the broadcast module to simulate network delay and dropped messages. -Used for communication between nodes. +- **Timer Module:** + Used by the Miner to simulate proof-of-work. -- **Event Mangler** +Lastly, a user implemented **Application Module** handles all business logic. +In particular, it need compute the state of the blockchain, verify transactions and provide transactions to the miner. -Used by the broadcast module to simulate network delay and dropped messages. +The following drawing depicts the relationship between the different modules at a high level. -- **Timer Module** - -Used by the Miner to simulate proof-of-work. - -Further, it also allows includes an interceptor which intercepts all communication between different modules and allows for visualization/debugging tools to consume this communication via a websocket connection. -An example of how to use the information provided the interception can be found [here](https://github.com/komplexon3/longest-chain-project). - -(TODO - fix link!) - -## Architecture +**Note:** The event mangler module and the timer module were omitted for simplicity. ```mermaid -flowchart TB; +flowchart LR; subgraph Longest Chain Consensus BCM[Blockchain Management \n Module] Miner[Miner \n Module] Synchronizer[Synchronizer \n Module] +Transport[gRPC Transport \n Module] Broadcast[Broadcast \n Module] -Transport[Transport \n Module] end @@ -71,41 +66,20 @@ BCM <--> Synchronizer Synchronizer <--> Transport ``` -```mermaid -sequenceDiagram - Broadcast ->> BCM: NewBlock - Note over BCM: Check that the block can be connected to the block tree - opt if it cannot connect the chain - BCM ->> Synchronizer: SyncRequest - Note over Synchronizer: Get missing blocks from other nodes, details omitted - Synchronizer ->> BCM: SyncResponse - end - BCM ->> Application: VerifyChainRequest - Note over Application: Verify that payloads are valid - Application ->> BCM: VerifyChainRespose - Note over BCM: Add blocks of chain to block tree, verify that they link together - +Further, it also includes an interceptor which intercepts all communication between different modules and allows for visualization/debugging tools to consume this communication via a websocket connection. +An example of how to use the information provided the interception can be found [here](https://github.com/komplexon3/longest-chain-project). - opt if head changes - BCM ->> Miner: NewHead - Note over Miner: Abort current mining operation, prepare to mine on new head - Miner ->> Application: PayloadRequest - Application ->> Miner: PayloadResponse - Note over Miner: Start mining on new head - BCM ->> Application: ForkUpdate - Note over Application: Compute state for new head - Application ->> BCM: RegisterCheckpoint - end -``` +(TODO - fix link!) ### Blockchain Management Module (BCM) The blockchain manager module is responsible for managing the blockchain. It keeps track of all blocks and links them together to form a tree. -In particular, it keeps track of the head of the blockchain, all leaves and so-called checkpoints. +In particular, it keeps track of the head of the blockchain, all leaves, and so-called checkpoints. A checkpoint is a block stored by the BCM that has a state stored with it. Technically, checkpoints are not necessary as the state can be computed from the blocks. However, it is convenient to not have to recompute the state from the genesis block every time it is needed. + The BCM must perform the following tasks: 1. Initialize the blockchain by receiving an InitBlockchain event from the application module which contains the initial state that is associated with the genesis block. @@ -126,6 +100,7 @@ The miner module is responsible for mining new blocks. It simulates the process of mining a block by waiting for a random amount of time and then broadcasting the mined block. This random amount of time is sampled from an exponential distribution with a mean of `expMinuteFactor` minutes. The mining is orchestrated by a separate goroutine (mineWorkerManager), so that the miner module can continue to receive and process events. + The operation of the miner module at a high level is as follows: 1. When it is notified of a new head (NewHead event), it prepares to mine the next block by sending a PayloadRequest event to the application module. @@ -204,6 +179,35 @@ The interceptor proto defines two such events: - TreeUpdate: This event is sent by the blockchain manager (BCM) when the blockchain is updated. It contains all blocks in the blockchain and the id of the new head. - StateUpdate: This event is sent by the application when it computes the state for the newest head of the blockchain. +## Operation + +```mermaid +sequenceDiagram + Broadcast ->> BCM: NewBlock + Note over BCM: Check that the block can be connected to the block tree + opt if it cannot the block to the blocktree + BCM ->> Synchronizer: SyncRequest + Note over Synchronizer: Get missing blocks from other nodes, details omitted + Synchronizer ->> BCM: SyncResponse + end + BCM ->> Application: VerifyChainRequest + Note over Application: Verify that payloads are valid + Application ->> BCM: VerifyChainRespose + Note over BCM: Add blocks of chain to block tree, verify that they link together + + + opt if head changes + BCM ->> Miner: NewHead + Note over Miner: Abort current mining operation, prepare to mine on new head + Miner ->> Application: PayloadRequest + Application ->> Miner: PayloadResponse + Note over Miner: Start mining on new head + BCM ->> Application: ForkUpdate + Note over Application: Compute state for new head + Application ->> BCM: RegisterCheckpoint + end +``` + ## How to use ## Example: Chat App From 13e0e725af9cb175307fec1f344072bfe35d1dbb Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sun, 4 Feb 2024 00:45:09 +0100 Subject: [PATCH 31/46] docs wip --- samples/blockchain/README.md | 60 +++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/samples/blockchain/README.md b/samples/blockchain/README.md index 910b1c3f3..474ef94ba 100644 --- a/samples/blockchain/README.md +++ b/samples/blockchain/README.md @@ -183,12 +183,26 @@ The interceptor proto defines two such events: ```mermaid sequenceDiagram + Application ->> BCM: InitBlockchain + + BCM ->> Miner: NewHead + Miner ->> Application: PayloadRequest + Application ->> Miner: PayloadResponse + Note over Miner: Start mining + + loop + alt receiving new block from another node Broadcast ->> BCM: NewBlock + else this node's miner mined a new block + Miner ->> Broadcast: NewBlock + Miner ->> BCM: NewBlock + end + Note over BCM: Check that the block can be connected to the block tree opt if it cannot the block to the blocktree BCM ->> Synchronizer: SyncRequest Note over Synchronizer: Get missing blocks from other nodes, details omitted - Synchronizer ->> BCM: SyncResponse + Synchronizer ->> BCM: NewChain end BCM ->> Application: VerifyChainRequest Note over Application: Verify that payloads are valid @@ -197,17 +211,61 @@ sequenceDiagram opt if head changes + par BCM ->> Miner: NewHead Note over Miner: Abort current mining operation, prepare to mine on new head Miner ->> Application: PayloadRequest Application ->> Miner: PayloadResponse Note over Miner: Start mining on new head + and BCM ->> Application: ForkUpdate Note over Application: Compute state for new head Application ->> BCM: RegisterCheckpoint end + end + end +``` + +**Note**: In the sequence diagram above, the dotted boxes have a different meaning depending on the label in the top left corner: + +- loop: The sequence in the box repeates indefinitely or until the condition int the brackets holds. +- alt: The two boxes making up this box describe two alternative sequences. +- opt: The sequence in the box is optional. + It is performed if the condition in the boxes holds. +- par: The two boxes making up this box describe two sequences that are performed in parallel. + +```mermaid +sequenceDiagram + +box Initiator Node +participant BCM (I) +participant Synchronizer (I) +end + +box Other Node(s) +participant Synchronizer +participant BCM +end + +Note over BCM (I): Cannot connect block +BCM (I) ->> Synchronizer (I): SyncRequest + +loop: until successful, one node at a time +Synchronizer (I) ->> Synchronizer: ChainRequest +Synchronizer ->> BCM: GetChainRequest +BCM ->> Synchronizer: GetChainResponse (successful/unsuccessful) +Synchronizer ->> Synchronizer (I): ChainResponse (successful/unsuccessful) +end +Synchronizer (I) ->> BCM (I): NewChain ``` +### Glossary + +- **Blocktree**: + Tree of all nodes logically. +- **Head**: + Last block of the canonical (longest) chain. + ## How to use ## Example: Chat App From 8c7e59eb7cbc4d36fb95d9979552a52cfd5c9f4e Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sun, 4 Feb 2024 16:21:36 +0100 Subject: [PATCH 32/46] clean up... --- .../applicationpb/applicationpb.pb.go | 410 +++++++----------- .../applicationpb/applicationpb.pb.mir.go | 3 +- .../applicationpb/dsl/emit.mir.go | 8 +- .../applicationpb/dsl/upon.mir.go | 10 +- .../applicationpb/events/events.mir.go | 95 ++-- .../applicationpb/oneof_interfaces.mir.go | 8 +- .../applicationpb/types/types.mir.go | 95 +--- .../applicationpb/applicationpb.proto | 26 +- protos/blockchainpb/blockchainpb.proto | 7 - protos/blockchainpb/minerpb/minerpb.proto | 6 +- samples/blockchain/application/application.go | 7 +- samples/blockchain/bcm.go | 4 +- 12 files changed, 244 insertions(+), 435 deletions(-) diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go index 3d1f2bb47..c4a0695ef 100644 --- a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go @@ -31,12 +31,11 @@ type Event struct { // Types that are assignable to Type: // - // *Event_NewHead // *Event_VerifyBlockRequest // *Event_VerifyBlockResponse // *Event_PayloadRequest // *Event_PayloadResponse - // *Event_ForkUpdate + // *Event_HeadChange // *Event_MessageInput Type isEvent_Type `protobuf_oneof:"type"` } @@ -80,13 +79,6 @@ func (m *Event) GetType() isEvent_Type { return nil } -func (x *Event) GetNewHead() *NewHead { - if x, ok := x.GetType().(*Event_NewHead); ok { - return x.NewHead - } - return nil -} - func (x *Event) GetVerifyBlockRequest() *VerifyBlocksRequest { if x, ok := x.GetType().(*Event_VerifyBlockRequest); ok { return x.VerifyBlockRequest @@ -115,9 +107,9 @@ func (x *Event) GetPayloadResponse() *PayloadResponse { return nil } -func (x *Event) GetForkUpdate() *ForkUpdate { - if x, ok := x.GetType().(*Event_ForkUpdate); ok { - return x.ForkUpdate +func (x *Event) GetHeadChange() *HeadChange { + if x, ok := x.GetType().(*Event_HeadChange); ok { + return x.HeadChange } return nil } @@ -133,39 +125,31 @@ type isEvent_Type interface { isEvent_Type() } -type Event_NewHead struct { - // Application-application events - NewHead *NewHead `protobuf:"bytes,10,opt,name=new_head,json=newHead,proto3,oneof"` -} - type Event_VerifyBlockRequest struct { - VerifyBlockRequest *VerifyBlocksRequest `protobuf:"bytes,11,opt,name=verify_block_request,json=verifyBlockRequest,proto3,oneof"` + VerifyBlockRequest *VerifyBlocksRequest `protobuf:"bytes,1,opt,name=verify_block_request,json=verifyBlockRequest,proto3,oneof"` } type Event_VerifyBlockResponse struct { - VerifyBlockResponse *VerifyBlocksResponse `protobuf:"bytes,12,opt,name=verify_block_response,json=verifyBlockResponse,proto3,oneof"` + VerifyBlockResponse *VerifyBlocksResponse `protobuf:"bytes,2,opt,name=verify_block_response,json=verifyBlockResponse,proto3,oneof"` } type Event_PayloadRequest struct { - // Transaction management application events - PayloadRequest *PayloadRequest `protobuf:"bytes,20,opt,name=payload_request,json=payloadRequest,proto3,oneof"` + PayloadRequest *PayloadRequest `protobuf:"bytes,3,opt,name=payload_request,json=payloadRequest,proto3,oneof"` } type Event_PayloadResponse struct { - PayloadResponse *PayloadResponse `protobuf:"bytes,21,opt,name=payload_response,json=payloadResponse,proto3,oneof"` + PayloadResponse *PayloadResponse `protobuf:"bytes,4,opt,name=payload_response,json=payloadResponse,proto3,oneof"` } -type Event_ForkUpdate struct { - ForkUpdate *ForkUpdate `protobuf:"bytes,22,opt,name=fork_update,json=forkUpdate,proto3,oneof"` +type Event_HeadChange struct { + HeadChange *HeadChange `protobuf:"bytes,5,opt,name=head_change,json=headChange,proto3,oneof"` } type Event_MessageInput struct { // Message input - MessageInput *MessageInput `protobuf:"bytes,30,opt,name=message_input,json=messageInput,proto3,oneof"` + MessageInput *MessageInput `protobuf:"bytes,10,opt,name=message_input,json=messageInput,proto3,oneof"` } -func (*Event_NewHead) isEvent_Type() {} - func (*Event_VerifyBlockRequest) isEvent_Type() {} func (*Event_VerifyBlockResponse) isEvent_Type() {} @@ -174,58 +158,10 @@ func (*Event_PayloadRequest) isEvent_Type() {} func (*Event_PayloadResponse) isEvent_Type() {} -func (*Event_ForkUpdate) isEvent_Type() {} +func (*Event_HeadChange) isEvent_Type() {} func (*Event_MessageInput) isEvent_Type() {} -// Application-application events -type NewHead struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - HeadId uint64 `protobuf:"varint,1,opt,name=head_id,json=headId,proto3" json:"head_id,omitempty"` -} - -func (x *NewHead) Reset() { - *x = NewHead{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[1] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *NewHead) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*NewHead) ProtoMessage() {} - -func (x *NewHead) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[1] - 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 NewHead.ProtoReflect.Descriptor instead. -func (*NewHead) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{1} -} - -func (x *NewHead) GetHeadId() uint64 { - if x != nil { - return x.HeadId - } - return 0 -} - type VerifyBlocksRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -239,7 +175,7 @@ type VerifyBlocksRequest struct { func (x *VerifyBlocksRequest) Reset() { *x = VerifyBlocksRequest{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[1] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -252,7 +188,7 @@ func (x *VerifyBlocksRequest) String() string { func (*VerifyBlocksRequest) ProtoMessage() {} func (x *VerifyBlocksRequest) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[1] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -265,7 +201,7 @@ func (x *VerifyBlocksRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyBlocksRequest.ProtoReflect.Descriptor instead. func (*VerifyBlocksRequest) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{2} + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{1} } func (x *VerifyBlocksRequest) GetCheckpointState() *statepb.State { @@ -300,7 +236,7 @@ type VerifyBlocksResponse struct { func (x *VerifyBlocksResponse) Reset() { *x = VerifyBlocksResponse{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -313,7 +249,7 @@ func (x *VerifyBlocksResponse) String() string { func (*VerifyBlocksResponse) ProtoMessage() {} func (x *VerifyBlocksResponse) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -326,7 +262,7 @@ func (x *VerifyBlocksResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use VerifyBlocksResponse.ProtoReflect.Descriptor instead. func (*VerifyBlocksResponse) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{3} + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{2} } func (x *VerifyBlocksResponse) GetVerifiedBlocks() []*blockchainpb.Block { @@ -336,7 +272,7 @@ func (x *VerifyBlocksResponse) GetVerifiedBlocks() []*blockchainpb.Block { return nil } -type ForkUpdate struct { +type HeadChange struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields @@ -347,23 +283,23 @@ type ForkUpdate struct { CheckpointState *statepb.State `protobuf:"bytes,4,opt,name=checkpoint_state,json=checkpointState,proto3" json:"checkpoint_state,omitempty"` } -func (x *ForkUpdate) Reset() { - *x = ForkUpdate{} +func (x *HeadChange) Reset() { + *x = HeadChange{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } } -func (x *ForkUpdate) String() string { +func (x *HeadChange) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ForkUpdate) ProtoMessage() {} +func (*HeadChange) ProtoMessage() {} -func (x *ForkUpdate) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4] +func (x *HeadChange) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -374,40 +310,39 @@ func (x *ForkUpdate) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ForkUpdate.ProtoReflect.Descriptor instead. -func (*ForkUpdate) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{4} +// Deprecated: Use HeadChange.ProtoReflect.Descriptor instead. +func (*HeadChange) Descriptor() ([]byte, []int) { + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{3} } -func (x *ForkUpdate) GetRemovedChain() []*blockchainpb.Block { +func (x *HeadChange) GetRemovedChain() []*blockchainpb.Block { if x != nil { return x.RemovedChain } return nil } -func (x *ForkUpdate) GetAddedChain() []*blockchainpb.Block { +func (x *HeadChange) GetAddedChain() []*blockchainpb.Block { if x != nil { return x.AddedChain } return nil } -func (x *ForkUpdate) GetCheckpointToForkRoot() []*blockchainpb.Block { +func (x *HeadChange) GetCheckpointToForkRoot() []*blockchainpb.Block { if x != nil { return x.CheckpointToForkRoot } return nil } -func (x *ForkUpdate) GetCheckpointState() *statepb.State { +func (x *HeadChange) GetCheckpointState() *statepb.State { if x != nil { return x.CheckpointState } return nil } -// Transaction management application events type PayloadRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -419,7 +354,7 @@ type PayloadRequest struct { func (x *PayloadRequest) Reset() { *x = PayloadRequest{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[5] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -432,7 +367,7 @@ func (x *PayloadRequest) String() string { func (*PayloadRequest) ProtoMessage() {} func (x *PayloadRequest) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[5] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -445,7 +380,7 @@ func (x *PayloadRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PayloadRequest.ProtoReflect.Descriptor instead. func (*PayloadRequest) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{5} + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{4} } func (x *PayloadRequest) GetHeadId() uint64 { @@ -467,7 +402,7 @@ type PayloadResponse struct { func (x *PayloadResponse) Reset() { *x = PayloadResponse{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[6] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -480,7 +415,7 @@ func (x *PayloadResponse) String() string { func (*PayloadResponse) ProtoMessage() {} func (x *PayloadResponse) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[6] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[5] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -493,7 +428,7 @@ func (x *PayloadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PayloadResponse.ProtoReflect.Descriptor instead. func (*PayloadResponse) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{6} + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{5} } func (x *PayloadResponse) GetHeadId() uint64 { @@ -521,7 +456,7 @@ type MessageInput struct { func (x *MessageInput) Reset() { *x = MessageInput{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[7] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -534,7 +469,7 @@ func (x *MessageInput) String() string { func (*MessageInput) ProtoMessage() {} func (x *MessageInput) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[7] + mi := &file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[6] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -547,7 +482,7 @@ func (x *MessageInput) ProtoReflect() protoreflect.Message { // Deprecated: Use MessageInput.ProtoReflect.Descriptor instead. func (*MessageInput) Descriptor() ([]byte, []int) { - return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{7} + return file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP(), []int{6} } func (x *MessageInput) GetText() string { @@ -572,97 +507,91 @@ var file_blockchainpb_applicationpb_applicationpb_proto_rawDesc = []byte{ 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9c, 0x04, 0x0a, 0x05, 0x45, - 0x76, 0x65, 0x6e, 0x74, 0x12, 0x33, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x68, 0x65, 0x61, 0x64, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x48, 0x00, - 0x52, 0x07, 0x6e, 0x65, 0x77, 0x48, 0x65, 0x61, 0x64, 0x12, 0x56, 0x0a, 0x14, 0x76, 0x65, 0x72, - 0x69, 0x66, 0x79, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x12, 0x76, - 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x59, 0x0a, 0x15, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x23, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, - 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x13, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0f, - 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, - 0x14, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4b, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1e, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, - 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x48, 0x00, 0x52, 0x0f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0b, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x65, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x48, 0x00, 0x52, 0x0a, 0x66, 0x6f, 0x72, 0x6b, 0x55, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x12, 0x42, 0x0a, 0x0d, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, 0x70, - 0x75, 0x74, 0x18, 0x1e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x48, 0x00, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, - 0x49, 0x6e, 0x70, 0x75, 0x74, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, - 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x07, 0x4e, 0x65, 0x77, - 0x48, 0x65, 0x61, 0x64, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, - 0xa6, 0x1d, 0x01, 0x22, 0xe3, 0x01, 0x0a, 0x13, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x10, 0x63, - 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, - 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x19, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, - 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x73, 0x74, - 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x16, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, - 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, - 0x74, 0x6f, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, - 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x6f, 0x56, 0x65, 0x72, - 0x69, 0x66, 0x79, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5a, 0x0a, 0x14, 0x56, 0x65, 0x72, - 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x12, 0x3c, 0x0a, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x62, 0x6c, - 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, + 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xe7, 0x03, 0x0a, 0x05, 0x45, + 0x76, 0x65, 0x6e, 0x74, 0x12, 0x56, 0x0a, 0x14, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x22, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x70, 0x62, 0x2e, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, 0x00, 0x52, 0x12, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x59, 0x0a, 0x15, + 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x72, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x61, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x56, 0x65, 0x72, 0x69, + 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x48, 0x00, 0x52, 0x13, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x0f, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1d, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x48, + 0x00, 0x52, 0x0e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x4b, 0x0a, 0x10, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x61, 0x70, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, 0x52, 0x0f, 0x70, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3c, + 0x0a, 0x0b, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x70, 0x62, 0x2e, 0x48, 0x65, 0x61, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x48, 0x00, + 0x52, 0x0a, 0x68, 0x65, 0x61, 0x64, 0x43, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x42, 0x0a, 0x0d, + 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x5f, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x18, 0x0a, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x70, 0x62, 0x2e, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x48, 0x00, 0x52, 0x0c, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, 0x75, 0x74, + 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, + 0x80, 0xa6, 0x1d, 0x01, 0x22, 0xe3, 0x01, 0x0a, 0x13, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x42, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x39, 0x0a, 0x10, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, + 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x4e, 0x0a, 0x19, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x73, + 0x74, 0x61, 0x72, 0x74, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, - 0x0e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, - 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x89, 0x02, 0x0a, 0x0a, 0x46, 0x6f, 0x72, 0x6b, 0x55, 0x70, - 0x64, 0x61, 0x74, 0x65, 0x12, 0x38, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x5f, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, + 0x16, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, + 0x54, 0x6f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x12, 0x3b, 0x0a, 0x0f, 0x63, 0x68, 0x61, 0x69, 0x6e, + 0x5f, 0x74, 0x6f, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0d, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x6f, 0x56, 0x65, + 0x72, 0x69, 0x66, 0x79, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5a, 0x0a, 0x14, 0x56, 0x65, + 0x72, 0x69, 0x66, 0x79, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x3c, 0x0a, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x5f, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, - 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x34, - 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x65, 0x64, 0x43, - 0x68, 0x61, 0x69, 0x6e, 0x12, 0x4a, 0x0a, 0x17, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, - 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x14, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x6f, 0x46, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x6f, 0x74, - 0x12, 0x39, 0x0a, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, 0x73, - 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, - 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, - 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, - 0x01, 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, 0xa6, - 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, 0x2c, - 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, - 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, 0xa6, - 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x0c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, 0x70, - 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, 0x41, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, - 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, - 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x0e, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x65, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x73, + 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x89, 0x02, 0x0a, 0x0a, 0x48, 0x65, 0x61, 0x64, 0x43, + 0x68, 0x61, 0x6e, 0x67, 0x65, 0x12, 0x38, 0x0a, 0x0d, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, + 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, + 0x6b, 0x52, 0x0c, 0x72, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x64, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, + 0x34, 0x0a, 0x0b, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, + 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x0a, 0x61, 0x64, 0x64, 0x65, 0x64, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x4a, 0x0a, 0x17, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, + 0x69, 0x6e, 0x74, 0x5f, 0x74, 0x6f, 0x5f, 0x66, 0x6f, 0x72, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, + 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x14, 0x63, 0x68, 0x65, + 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x54, 0x6f, 0x46, 0x6f, 0x72, 0x6b, 0x52, 0x6f, 0x6f, + 0x74, 0x12, 0x39, 0x0a, 0x10, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, + 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, + 0x1d, 0x01, 0x22, 0x2f, 0x0a, 0x0e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x3a, 0x04, 0x98, + 0xa6, 0x1d, 0x01, 0x22, 0x5e, 0x0a, 0x0f, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x06, 0x68, 0x65, 0x61, 0x64, 0x49, 0x64, 0x12, + 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, + 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x04, 0x98, + 0xa6, 0x1d, 0x01, 0x22, 0x28, 0x0a, 0x0c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x49, 0x6e, + 0x70, 0x75, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, 0x78, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x04, 0x74, 0x65, 0x78, 0x74, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x43, 0x5a, + 0x41, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, + 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, + 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -677,42 +606,40 @@ func file_blockchainpb_applicationpb_applicationpb_proto_rawDescGZIP() []byte { return file_blockchainpb_applicationpb_applicationpb_proto_rawDescData } -var file_blockchainpb_applicationpb_applicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 8) +var file_blockchainpb_applicationpb_applicationpb_proto_msgTypes = make([]protoimpl.MessageInfo, 7) var file_blockchainpb_applicationpb_applicationpb_proto_goTypes = []interface{}{ (*Event)(nil), // 0: applicationpb.Event - (*NewHead)(nil), // 1: applicationpb.NewHead - (*VerifyBlocksRequest)(nil), // 2: applicationpb.VerifyBlocksRequest - (*VerifyBlocksResponse)(nil), // 3: applicationpb.VerifyBlocksResponse - (*ForkUpdate)(nil), // 4: applicationpb.ForkUpdate - (*PayloadRequest)(nil), // 5: applicationpb.PayloadRequest - (*PayloadResponse)(nil), // 6: applicationpb.PayloadResponse - (*MessageInput)(nil), // 7: applicationpb.MessageInput - (*statepb.State)(nil), // 8: statepb.State - (*blockchainpb.Block)(nil), // 9: blockchainpb.Block - (*payloadpb.Payload)(nil), // 10: payloadpb.Payload + (*VerifyBlocksRequest)(nil), // 1: applicationpb.VerifyBlocksRequest + (*VerifyBlocksResponse)(nil), // 2: applicationpb.VerifyBlocksResponse + (*HeadChange)(nil), // 3: applicationpb.HeadChange + (*PayloadRequest)(nil), // 4: applicationpb.PayloadRequest + (*PayloadResponse)(nil), // 5: applicationpb.PayloadResponse + (*MessageInput)(nil), // 6: applicationpb.MessageInput + (*statepb.State)(nil), // 7: statepb.State + (*blockchainpb.Block)(nil), // 8: blockchainpb.Block + (*payloadpb.Payload)(nil), // 9: payloadpb.Payload } var file_blockchainpb_applicationpb_applicationpb_proto_depIdxs = []int32{ - 1, // 0: applicationpb.Event.new_head:type_name -> applicationpb.NewHead - 2, // 1: applicationpb.Event.verify_block_request:type_name -> applicationpb.VerifyBlocksRequest - 3, // 2: applicationpb.Event.verify_block_response:type_name -> applicationpb.VerifyBlocksResponse - 5, // 3: applicationpb.Event.payload_request:type_name -> applicationpb.PayloadRequest - 6, // 4: applicationpb.Event.payload_response:type_name -> applicationpb.PayloadResponse - 4, // 5: applicationpb.Event.fork_update:type_name -> applicationpb.ForkUpdate - 7, // 6: applicationpb.Event.message_input:type_name -> applicationpb.MessageInput - 8, // 7: applicationpb.VerifyBlocksRequest.checkpoint_state:type_name -> statepb.State - 9, // 8: applicationpb.VerifyBlocksRequest.chain_checkpoint_to_start:type_name -> blockchainpb.Block - 9, // 9: applicationpb.VerifyBlocksRequest.chain_to_verify:type_name -> blockchainpb.Block - 9, // 10: applicationpb.VerifyBlocksResponse.verified_blocks:type_name -> blockchainpb.Block - 9, // 11: applicationpb.ForkUpdate.removed_chain:type_name -> blockchainpb.Block - 9, // 12: applicationpb.ForkUpdate.added_chain:type_name -> blockchainpb.Block - 9, // 13: applicationpb.ForkUpdate.checkpoint_to_fork_root:type_name -> blockchainpb.Block - 8, // 14: applicationpb.ForkUpdate.checkpoint_state:type_name -> statepb.State - 10, // 15: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload - 16, // [16:16] is the sub-list for method output_type - 16, // [16:16] is the sub-list for method input_type - 16, // [16:16] is the sub-list for extension type_name - 16, // [16:16] is the sub-list for extension extendee - 0, // [0:16] is the sub-list for field type_name + 1, // 0: applicationpb.Event.verify_block_request:type_name -> applicationpb.VerifyBlocksRequest + 2, // 1: applicationpb.Event.verify_block_response:type_name -> applicationpb.VerifyBlocksResponse + 4, // 2: applicationpb.Event.payload_request:type_name -> applicationpb.PayloadRequest + 5, // 3: applicationpb.Event.payload_response:type_name -> applicationpb.PayloadResponse + 3, // 4: applicationpb.Event.head_change:type_name -> applicationpb.HeadChange + 6, // 5: applicationpb.Event.message_input:type_name -> applicationpb.MessageInput + 7, // 6: applicationpb.VerifyBlocksRequest.checkpoint_state:type_name -> statepb.State + 8, // 7: applicationpb.VerifyBlocksRequest.chain_checkpoint_to_start:type_name -> blockchainpb.Block + 8, // 8: applicationpb.VerifyBlocksRequest.chain_to_verify:type_name -> blockchainpb.Block + 8, // 9: applicationpb.VerifyBlocksResponse.verified_blocks:type_name -> blockchainpb.Block + 8, // 10: applicationpb.HeadChange.removed_chain:type_name -> blockchainpb.Block + 8, // 11: applicationpb.HeadChange.added_chain:type_name -> blockchainpb.Block + 8, // 12: applicationpb.HeadChange.checkpoint_to_fork_root:type_name -> blockchainpb.Block + 7, // 13: applicationpb.HeadChange.checkpoint_state:type_name -> statepb.State + 9, // 14: applicationpb.PayloadResponse.payload:type_name -> payloadpb.Payload + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_blockchainpb_applicationpb_applicationpb_proto_init() } @@ -734,7 +661,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*NewHead); i { + switch v := v.(*VerifyBlocksRequest); i { case 0: return &v.state case 1: @@ -746,7 +673,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyBlocksRequest); i { + switch v := v.(*VerifyBlocksResponse); i { case 0: return &v.state case 1: @@ -758,7 +685,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*VerifyBlocksResponse); i { + switch v := v.(*HeadChange); i { case 0: return &v.state case 1: @@ -770,18 +697,6 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*ForkUpdate); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PayloadRequest); i { case 0: return &v.state @@ -793,7 +708,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { return nil } } - file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*PayloadResponse); i { case 0: return &v.state @@ -805,7 +720,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { return nil } } - file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*MessageInput); i { case 0: return &v.state @@ -819,12 +734,11 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { } } file_blockchainpb_applicationpb_applicationpb_proto_msgTypes[0].OneofWrappers = []interface{}{ - (*Event_NewHead)(nil), (*Event_VerifyBlockRequest)(nil), (*Event_VerifyBlockResponse)(nil), (*Event_PayloadRequest)(nil), (*Event_PayloadResponse)(nil), - (*Event_ForkUpdate)(nil), + (*Event_HeadChange)(nil), (*Event_MessageInput)(nil), } type x struct{} @@ -833,7 +747,7 @@ func file_blockchainpb_applicationpb_applicationpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_applicationpb_applicationpb_proto_rawDesc, NumEnums: 0, - NumMessages: 8, + NumMessages: 7, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go index dab135fdf..0f330839c 100644 --- a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.mir.go @@ -8,12 +8,11 @@ import ( func (*Event) ReflectTypeOptions() []reflect.Type { return []reflect.Type{ - reflect.TypeOf((*Event_NewHead)(nil)), reflect.TypeOf((*Event_VerifyBlockRequest)(nil)), reflect.TypeOf((*Event_VerifyBlockResponse)(nil)), reflect.TypeOf((*Event_PayloadRequest)(nil)), reflect.TypeOf((*Event_PayloadResponse)(nil)), - reflect.TypeOf((*Event_ForkUpdate)(nil)), + reflect.TypeOf((*Event_HeadChange)(nil)), reflect.TypeOf((*Event_MessageInput)(nil)), } } diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go index ed42ac9d3..a51bfebbf 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/emit.mir.go @@ -13,10 +13,6 @@ import ( // Module-specific dsl functions for emitting events. -func NewHead(m dsl.Module, destModule types.ModuleID, headId uint64) { - dsl.EmitMirEvent(m, events.NewHead(destModule, headId)) -} - func VerifyBlocksRequest(m dsl.Module, destModule types.ModuleID, checkpointState *types1.State, chainCheckpointToStart []*types2.Block, chainToVerify []*types2.Block) { dsl.EmitMirEvent(m, events.VerifyBlocksRequest(destModule, checkpointState, chainCheckpointToStart, chainToVerify)) } @@ -33,8 +29,8 @@ func PayloadResponse(m dsl.Module, destModule types.ModuleID, headId uint64, pay dsl.EmitMirEvent(m, events.PayloadResponse(destModule, headId, payload)) } -func ForkUpdate(m dsl.Module, destModule types.ModuleID, removedChain []*types2.Block, addedChain []*types2.Block, checkpointToForkRoot []*types2.Block, checkpointState *types1.State) { - dsl.EmitMirEvent(m, events.ForkUpdate(destModule, removedChain, addedChain, checkpointToForkRoot, checkpointState)) +func HeadChange(m dsl.Module, destModule types.ModuleID, removedChain []*types2.Block, addedChain []*types2.Block, checkpointToForkRoot []*types2.Block, checkpointState *types1.State) { + dsl.EmitMirEvent(m, events.HeadChange(destModule, removedChain, addedChain, checkpointToForkRoot, checkpointState)) } func MessageInput(m dsl.Module, destModule types.ModuleID, text string) { diff --git a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go index 211b480ae..1338ad39f 100644 --- a/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/dsl/upon.mir.go @@ -24,12 +24,6 @@ func UponEvent[W types.Event_TypeWrapper[Ev], Ev any](m dsl.Module, handler func }) } -func UponNewHead(m dsl.Module, handler func(headId uint64) error) { - UponEvent[*types.Event_NewHead](m, func(ev *types.NewHead) error { - return handler(ev.HeadId) - }) -} - func UponVerifyBlocksRequest(m dsl.Module, handler func(checkpointState *types2.State, chainCheckpointToStart []*types3.Block, chainToVerify []*types3.Block) error) { UponEvent[*types.Event_VerifyBlockRequest](m, func(ev *types.VerifyBlocksRequest) error { return handler(ev.CheckpointState, ev.ChainCheckpointToStart, ev.ChainToVerify) @@ -54,8 +48,8 @@ func UponPayloadResponse(m dsl.Module, handler func(headId uint64, payload *type }) } -func UponForkUpdate(m dsl.Module, handler func(removedChain []*types3.Block, addedChain []*types3.Block, checkpointToForkRoot []*types3.Block, checkpointState *types2.State) error) { - UponEvent[*types.Event_ForkUpdate](m, func(ev *types.ForkUpdate) error { +func UponHeadChange(m dsl.Module, handler func(removedChain []*types3.Block, addedChain []*types3.Block, checkpointToForkRoot []*types3.Block, checkpointState *types2.State) error) { + UponEvent[*types.Event_HeadChange](m, func(ev *types.HeadChange) error { return handler(ev.RemovedChain, ev.AddedChain, ev.CheckpointToForkRoot, ev.CheckpointState) }) } diff --git a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go index adf5a64ee..049c51844 100644 --- a/pkg/pb/blockchainpb/applicationpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/events/events.mir.go @@ -3,36 +3,21 @@ package applicationpbevents import ( - types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" + types4 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/types" types5 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" - types3 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" - types4 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" - types1 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" + types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + types2 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + types3 "github.com/filecoin-project/mir/pkg/pb/eventpb/types" types "github.com/filecoin-project/mir/pkg/types" ) -func NewHead(destModule types.ModuleID, headId uint64) *types1.Event { - return &types1.Event{ +func VerifyBlocksRequest(destModule types.ModuleID, checkpointState *types1.State, chainCheckpointToStart []*types2.Block, chainToVerify []*types2.Block) *types3.Event { + return &types3.Event{ DestModule: destModule, - Type: &types1.Event_Application{ - Application: &types2.Event{ - Type: &types2.Event_NewHead{ - NewHead: &types2.NewHead{ - HeadId: headId, - }, - }, - }, - }, - } -} - -func VerifyBlocksRequest(destModule types.ModuleID, checkpointState *types3.State, chainCheckpointToStart []*types4.Block, chainToVerify []*types4.Block) *types1.Event { - return &types1.Event{ - DestModule: destModule, - Type: &types1.Event_Application{ - Application: &types2.Event{ - Type: &types2.Event_VerifyBlockRequest{ - VerifyBlockRequest: &types2.VerifyBlocksRequest{ + Type: &types3.Event_Application{ + Application: &types4.Event{ + Type: &types4.Event_VerifyBlockRequest{ + VerifyBlockRequest: &types4.VerifyBlocksRequest{ CheckpointState: checkpointState, ChainCheckpointToStart: chainCheckpointToStart, ChainToVerify: chainToVerify, @@ -43,13 +28,13 @@ func VerifyBlocksRequest(destModule types.ModuleID, checkpointState *types3.Stat } } -func VerifyBlocksResponse(destModule types.ModuleID, verifiedBlocks []*types4.Block) *types1.Event { - return &types1.Event{ +func VerifyBlocksResponse(destModule types.ModuleID, verifiedBlocks []*types2.Block) *types3.Event { + return &types3.Event{ DestModule: destModule, - Type: &types1.Event_Application{ - Application: &types2.Event{ - Type: &types2.Event_VerifyBlockResponse{ - VerifyBlockResponse: &types2.VerifyBlocksResponse{ + Type: &types3.Event_Application{ + Application: &types4.Event{ + Type: &types4.Event_VerifyBlockResponse{ + VerifyBlockResponse: &types4.VerifyBlocksResponse{ VerifiedBlocks: verifiedBlocks, }, }, @@ -58,13 +43,13 @@ func VerifyBlocksResponse(destModule types.ModuleID, verifiedBlocks []*types4.Bl } } -func PayloadRequest(destModule types.ModuleID, headId uint64) *types1.Event { - return &types1.Event{ +func PayloadRequest(destModule types.ModuleID, headId uint64) *types3.Event { + return &types3.Event{ DestModule: destModule, - Type: &types1.Event_Application{ - Application: &types2.Event{ - Type: &types2.Event_PayloadRequest{ - PayloadRequest: &types2.PayloadRequest{ + Type: &types3.Event_Application{ + Application: &types4.Event{ + Type: &types4.Event_PayloadRequest{ + PayloadRequest: &types4.PayloadRequest{ HeadId: headId, }, }, @@ -73,13 +58,13 @@ func PayloadRequest(destModule types.ModuleID, headId uint64) *types1.Event { } } -func PayloadResponse(destModule types.ModuleID, headId uint64, payload *types5.Payload) *types1.Event { - return &types1.Event{ +func PayloadResponse(destModule types.ModuleID, headId uint64, payload *types5.Payload) *types3.Event { + return &types3.Event{ DestModule: destModule, - Type: &types1.Event_Application{ - Application: &types2.Event{ - Type: &types2.Event_PayloadResponse{ - PayloadResponse: &types2.PayloadResponse{ + Type: &types3.Event_Application{ + Application: &types4.Event{ + Type: &types4.Event_PayloadResponse{ + PayloadResponse: &types4.PayloadResponse{ HeadId: headId, Payload: payload, }, @@ -89,13 +74,13 @@ func PayloadResponse(destModule types.ModuleID, headId uint64, payload *types5.P } } -func ForkUpdate(destModule types.ModuleID, removedChain []*types4.Block, addedChain []*types4.Block, checkpointToForkRoot []*types4.Block, checkpointState *types3.State) *types1.Event { - return &types1.Event{ +func HeadChange(destModule types.ModuleID, removedChain []*types2.Block, addedChain []*types2.Block, checkpointToForkRoot []*types2.Block, checkpointState *types1.State) *types3.Event { + return &types3.Event{ DestModule: destModule, - Type: &types1.Event_Application{ - Application: &types2.Event{ - Type: &types2.Event_ForkUpdate{ - ForkUpdate: &types2.ForkUpdate{ + Type: &types3.Event_Application{ + Application: &types4.Event{ + Type: &types4.Event_HeadChange{ + HeadChange: &types4.HeadChange{ RemovedChain: removedChain, AddedChain: addedChain, CheckpointToForkRoot: checkpointToForkRoot, @@ -107,13 +92,13 @@ func ForkUpdate(destModule types.ModuleID, removedChain []*types4.Block, addedCh } } -func MessageInput(destModule types.ModuleID, text string) *types1.Event { - return &types1.Event{ +func MessageInput(destModule types.ModuleID, text string) *types3.Event { + return &types3.Event{ DestModule: destModule, - Type: &types1.Event_Application{ - Application: &types2.Event{ - Type: &types2.Event_MessageInput{ - MessageInput: &types2.MessageInput{ + Type: &types3.Event_Application{ + Application: &types4.Event{ + Type: &types4.Event_MessageInput{ + MessageInput: &types4.MessageInput{ Text: text, }, }, diff --git a/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go index b003a79de..0994da1e5 100644 --- a/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/oneof_interfaces.mir.go @@ -9,10 +9,6 @@ type Event_TypeWrapper[T any] interface { Unwrap() *T } -func (w *Event_NewHead) Unwrap() *NewHead { - return w.NewHead -} - func (w *Event_VerifyBlockRequest) Unwrap() *VerifyBlocksRequest { return w.VerifyBlockRequest } @@ -29,8 +25,8 @@ func (w *Event_PayloadResponse) Unwrap() *PayloadResponse { return w.PayloadResponse } -func (w *Event_ForkUpdate) Unwrap() *ForkUpdate { - return w.ForkUpdate +func (w *Event_HeadChange) Unwrap() *HeadChange { + return w.HeadChange } func (w *Event_MessageInput) Unwrap() *MessageInput { diff --git a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go index 1f8fa5908..8e9060202 100644 --- a/pkg/pb/blockchainpb/applicationpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/applicationpb/types/types.mir.go @@ -33,8 +33,6 @@ func Event_TypeFromPb(pb applicationpb.Event_Type) Event_Type { return nil } switch pb := pb.(type) { - case *applicationpb.Event_NewHead: - return &Event_NewHead{NewHead: NewHeadFromPb(pb.NewHead)} case *applicationpb.Event_VerifyBlockRequest: return &Event_VerifyBlockRequest{VerifyBlockRequest: VerifyBlocksRequestFromPb(pb.VerifyBlockRequest)} case *applicationpb.Event_VerifyBlockResponse: @@ -43,38 +41,14 @@ func Event_TypeFromPb(pb applicationpb.Event_Type) Event_Type { return &Event_PayloadRequest{PayloadRequest: PayloadRequestFromPb(pb.PayloadRequest)} case *applicationpb.Event_PayloadResponse: return &Event_PayloadResponse{PayloadResponse: PayloadResponseFromPb(pb.PayloadResponse)} - case *applicationpb.Event_ForkUpdate: - return &Event_ForkUpdate{ForkUpdate: ForkUpdateFromPb(pb.ForkUpdate)} + case *applicationpb.Event_HeadChange: + return &Event_HeadChange{HeadChange: HeadChangeFromPb(pb.HeadChange)} case *applicationpb.Event_MessageInput: return &Event_MessageInput{MessageInput: MessageInputFromPb(pb.MessageInput)} } return nil } -type Event_NewHead struct { - NewHead *NewHead -} - -func (*Event_NewHead) isEvent_Type() {} - -func (w *Event_NewHead) Unwrap() *NewHead { - return w.NewHead -} - -func (w *Event_NewHead) Pb() applicationpb.Event_Type { - if w == nil { - return nil - } - if w.NewHead == nil { - return &applicationpb.Event_NewHead{} - } - return &applicationpb.Event_NewHead{NewHead: (w.NewHead).Pb()} -} - -func (*Event_NewHead) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_NewHead]()} -} - type Event_VerifyBlockRequest struct { VerifyBlockRequest *VerifyBlocksRequest } @@ -171,28 +145,28 @@ func (*Event_PayloadResponse) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_PayloadResponse]()} } -type Event_ForkUpdate struct { - ForkUpdate *ForkUpdate +type Event_HeadChange struct { + HeadChange *HeadChange } -func (*Event_ForkUpdate) isEvent_Type() {} +func (*Event_HeadChange) isEvent_Type() {} -func (w *Event_ForkUpdate) Unwrap() *ForkUpdate { - return w.ForkUpdate +func (w *Event_HeadChange) Unwrap() *HeadChange { + return w.HeadChange } -func (w *Event_ForkUpdate) Pb() applicationpb.Event_Type { +func (w *Event_HeadChange) Pb() applicationpb.Event_Type { if w == nil { return nil } - if w.ForkUpdate == nil { - return &applicationpb.Event_ForkUpdate{} + if w.HeadChange == nil { + return &applicationpb.Event_HeadChange{} } - return &applicationpb.Event_ForkUpdate{ForkUpdate: (w.ForkUpdate).Pb()} + return &applicationpb.Event_HeadChange{HeadChange: (w.HeadChange).Pb()} } -func (*Event_ForkUpdate) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_ForkUpdate]()} +func (*Event_HeadChange) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event_HeadChange]()} } type Event_MessageInput struct { @@ -246,35 +220,6 @@ func (*Event) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.Event]()} } -type NewHead struct { - HeadId uint64 -} - -func NewHeadFromPb(pb *applicationpb.NewHead) *NewHead { - if pb == nil { - return nil - } - return &NewHead{ - HeadId: pb.HeadId, - } -} - -func (m *NewHead) Pb() *applicationpb.NewHead { - if m == nil { - return nil - } - pbMessage := &applicationpb.NewHead{} - { - pbMessage.HeadId = m.HeadId - } - - return pbMessage -} - -func (*NewHead) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.NewHead]()} -} - type VerifyBlocksRequest struct { CheckpointState *types.State ChainCheckpointToStart []*types1.Block @@ -353,18 +298,18 @@ func (*VerifyBlocksResponse) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.VerifyBlocksResponse]()} } -type ForkUpdate struct { +type HeadChange struct { RemovedChain []*types1.Block AddedChain []*types1.Block CheckpointToForkRoot []*types1.Block CheckpointState *types.State } -func ForkUpdateFromPb(pb *applicationpb.ForkUpdate) *ForkUpdate { +func HeadChangeFromPb(pb *applicationpb.HeadChange) *HeadChange { if pb == nil { return nil } - return &ForkUpdate{ + return &HeadChange{ RemovedChain: types2.ConvertSlice(pb.RemovedChain, func(t *blockchainpb.Block) *types1.Block { return types1.BlockFromPb(t) }), @@ -378,11 +323,11 @@ func ForkUpdateFromPb(pb *applicationpb.ForkUpdate) *ForkUpdate { } } -func (m *ForkUpdate) Pb() *applicationpb.ForkUpdate { +func (m *HeadChange) Pb() *applicationpb.HeadChange { if m == nil { return nil } - pbMessage := &applicationpb.ForkUpdate{} + pbMessage := &applicationpb.HeadChange{} { pbMessage.RemovedChain = types2.ConvertSlice(m.RemovedChain, func(t *types1.Block) *blockchainpb.Block { return (t).Pb() @@ -401,8 +346,8 @@ func (m *ForkUpdate) Pb() *applicationpb.ForkUpdate { return pbMessage } -func (*ForkUpdate) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.ForkUpdate]()} +func (*HeadChange) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*applicationpb.HeadChange]()} } type PayloadRequest struct { diff --git a/protos/blockchainpb/applicationpb/applicationpb.proto b/protos/blockchainpb/applicationpb/applicationpb.proto index f048f8ab5..b83368cc7 100644 --- a/protos/blockchainpb/applicationpb/applicationpb.proto +++ b/protos/blockchainpb/applicationpb/applicationpb.proto @@ -15,28 +15,17 @@ message Event { oneof type { option (mir.event_type) = true; - // Application-application events - NewHead new_head = 10; - VerifyBlocksRequest verify_block_request = 11; - VerifyBlocksResponse verify_block_response = 12; - - // Transaction management application events - PayloadRequest payload_request = 20; - PayloadResponse payload_response = 21; - ForkUpdate fork_update = 22; + VerifyBlocksRequest verify_block_request = 1; + VerifyBlocksResponse verify_block_response = 2; + PayloadRequest payload_request = 3; + PayloadResponse payload_response = 4; + HeadChange head_change = 5; // Message input - MessageInput message_input = 30; + MessageInput message_input = 10; } } -// Application-application events -message NewHead { - option (mir.event) = true; - - uint64 head_id = 1; -} - message VerifyBlocksRequest { option (mir.event) = true; @@ -51,7 +40,7 @@ message VerifyBlocksResponse { repeated blockchainpb.Block verified_blocks = 1; } -message ForkUpdate { +message HeadChange { option (mir.event) = true; repeated blockchainpb.Block removed_chain = 1; @@ -60,7 +49,6 @@ message ForkUpdate { statepb.State checkpoint_state = 4; } -// Transaction management application events message PayloadRequest { option (mir.event) = true; diff --git a/protos/blockchainpb/blockchainpb.proto b/protos/blockchainpb/blockchainpb.proto index cffdf51d7..cb73faaca 100644 --- a/protos/blockchainpb/blockchainpb.proto +++ b/protos/blockchainpb/blockchainpb.proto @@ -8,13 +8,6 @@ import "mir/codegen_extensions.proto"; import "blockchainpb/payloadpb/payloadpb.proto"; import "google/protobuf/timestamp.proto"; -message Blocktree { - option (mir.struct) = true; - - repeated Block blocks = 1; - repeated uint64 leaves = 2; -} - message Block { option (mir.struct) = true; diff --git a/protos/blockchainpb/minerpb/minerpb.proto b/protos/blockchainpb/minerpb/minerpb.proto index 5dd99191b..c72dc8edd 100644 --- a/protos/blockchainpb/minerpb/minerpb.proto +++ b/protos/blockchainpb/minerpb/minerpb.proto @@ -21,12 +21,12 @@ message Event { message BlockRequest { option (mir.event) = true; - uint64 head_id = 1; - payloadpb.Payload payload = 2; + uint64 head_id = 1; + payloadpb.Payload payload = 2; } message NewHead { option (mir.event) = true; - uint64 head_id = 1; + uint64 head_id = 1; } \ No newline at end of file diff --git a/samples/blockchain/application/application.go b/samples/blockchain/application/application.go index 9135c3e0e..9038017ca 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain/application/application.go @@ -33,7 +33,7 @@ import ( * The application module must perform the following tasks: * 1. Initialize the blockchain by sending it the initial state in an InitBlockchain event to the BCM. * 2. When it receives a PayloadRequest event, it must provide a payload for the next block. This payload can be empty. - * 3. When it receives a ForkUpdate event, it must compute the state at the new head of the blockchain. + * 3. When it receives a HeadChange event, it must compute the state at the new head of the blockchain. * This state is then registered with the BCM by sending it a RegisterCheckpoint event. * A checkpoint is a block stored by the BCM that has a state stored with it. * 4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at a application level and respond with a VerifyBlocksResponse event. @@ -65,7 +65,6 @@ func applyBlockToState(state *statepbtypes.State, block *blockchainpbtypes.Block timeStamps := state.LastSentTimestamps msgHistory := state.MessageHistory - // TODO: is this necessary? it should be verified already for i, lt := range timeStamps { if lt.NodeId == sender { ltTimestamp := lt.Timestamp.AsTime() @@ -103,7 +102,7 @@ func applyBlockToState(state *statepbtypes.State, block *blockchainpbtypes.Block // Handler called by BCM when the head changes. // It handles cases where the head changes because the canonical chain was extended as well as cases where the canonical chain changes because of a fork. -func (am *ApplicationModule) handleForkUpdate(removedChain, addedChain, checkpointToForkRootChain []*blockchainpbtypes.Block, checkpointState *statepbtypes.State) error { +func (am *ApplicationModule) handleHeadChange(removedChain, addedChain, checkpointToForkRootChain []*blockchainpbtypes.Block, checkpointState *statepbtypes.State) error { am.logger.Log(logging.LevelInfo, "Processing fork update", "poolSize", am.pm.PoolSize()) // add "remove chain" transactions to pool @@ -219,7 +218,7 @@ func NewApplication(logger logging.Logger, nodeID t.NodeID) modules.PassiveModul }) applicationpbdsl.UponPayloadRequest(m, am.handlePayloadRequest) - applicationpbdsl.UponForkUpdate(m, am.handleForkUpdate) + applicationpbdsl.UponHeadChange(m, am.handleHeadChange) applicationpbdsl.UponVerifyBlocksRequest(m, am.handleVerifyBlocksRequest) applicationpbdsl.UponMessageInput(m, func(text string) error { diff --git a/samples/blockchain/bcm.go b/samples/blockchain/bcm.go index 2e2938f41..cbc27611a 100644 --- a/samples/blockchain/bcm.go +++ b/samples/blockchain/bcm.go @@ -45,7 +45,7 @@ import ( * Additionally, it sends a TreeUpdate event to the interceptor module. This is solely for debugging/visualization purposes and not necessary for the operation of the blockchain. * 3. Register checkpoints when receiving a RegisterCheckpoint event from the application module. * 4. It must provide the synchronizer with chains when requested. This is to resolve orphan blocks in other nodes. - * 5. When the head changes, it sends a ForkUpdate event to the application module. This event contains all information necessary for the application to compute the state at the new head + * 5. When the head changes, it sends a HeadChange event to the application module. This event contains all information necessary for the application to compute the state at the new head * as well as information about which payloads are now part of the canonical (i.e., longest) and which ones are no longer part of the canonical chain. */ @@ -150,7 +150,7 @@ func (bcm *bcmModule) handleNewHead(newHead, oldHead *bcmBlock) { } checkpointToForkRootChain, checkpointState := bcm.getChainFromCheckpointToBlock(forkRoot) - applicationpbdsl.ForkUpdate(*bcm.m, "application", + applicationpbdsl.HeadChange(*bcm.m, "application", removeChain[1:], addChain[1:], checkpointToForkRootChain, From 6f8f58ec96780614604e6338d527f75452699539 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sun, 4 Feb 2024 16:21:44 +0100 Subject: [PATCH 33/46] more docs --- samples/blockchain/README.md | 146 +++++++++++++----- samples/blockchain/images/visualizer.png | Bin 0 -> 198200 bytes .../blockchain/images/visualizer_block.png | Bin 0 -> 50801 bytes samples/blockchain/images/visualizers.png | Bin 0 -> 750824 bytes 4 files changed, 107 insertions(+), 39 deletions(-) create mode 100644 samples/blockchain/images/visualizer.png create mode 100644 samples/blockchain/images/visualizer_block.png create mode 100644 samples/blockchain/images/visualizers.png diff --git a/samples/blockchain/README.md b/samples/blockchain/README.md index 474ef94ba..71fb30949 100644 --- a/samples/blockchain/README.md +++ b/samples/blockchain/README.md @@ -1,8 +1,22 @@ -# Longest Chain Consensus +# Longest-Chain Consensus -LCC is a modular implementation of a longest chain consensus protocol modelling proof-of-work. +Longest-Chain Consensus is a modular implementation of a longest-chain consensus protocol modeling proof-of-work. -It provides the core elements with only the actual buisiness logic to be implemented by an application module. +It provides the core elements with only the actual business logic to be implemented by an application module. + +## Goal + +The goal of this system is to provide a simple modular blockchain run on a fixed set of nodes. + +Each node has a set of core modules running the blockchain and an application module that runs the business logic and provides certain functionality to the core modules. + +The blocks making up the blockchain contain the following: + +- **block id:** Identifier of a block, computed by hashing the block with the block id set to 0. +- **previous block id:** Identifier of the predecessor block. +- **payload:** An application dependant payload. +- **timestamp:** Timestamp of when the block was mined. +- **miner id:** Identifier of the node that mined the block. ## Architecture @@ -32,8 +46,8 @@ and the following supporting modules: - **Timer Module:** Used by the Miner to simulate proof-of-work. -Lastly, a user implemented **Application Module** handles all business logic. -In particular, it need compute the state of the blockchain, verify transactions and provide transactions to the miner. +Lastly, a user-implemented **Application Module** handles all business logic. +In particular, it needs to compute the state of the blockchain, verify transactions and provide transactions to the miner. The following drawing depicts the relationship between the different modules at a high level. @@ -66,8 +80,8 @@ BCM <--> Synchronizer Synchronizer <--> Transport ``` -Further, it also includes an interceptor which intercepts all communication between different modules and allows for visualization/debugging tools to consume this communication via a websocket connection. -An example of how to use the information provided the interception can be found [here](https://github.com/komplexon3/longest-chain-project). +Further, it also includes an interceptor that intercepts all communication between different modules and allows for visualization/debugging tools to consume this communication via a websocket connection. +An example of how to use the information provided by the interception can be found [here](https://github.com/komplexon3/longest-chain-project). (TODO - fix link!) @@ -86,12 +100,15 @@ The BCM must perform the following tasks: 2. Add new blocks to the blockchain. If a block is added that has a parent that is not in the blockchain, the BCM requests the missing block from the synchronizer. Blocks that are missing their parent are called orphans. All blocks added to the blockchain are verified in two steps: + - It has the application module verify that the payloads are valid given the chain that the block is part of. - The BCM must verify that the blocks link together correctly. - Additionally, it sends a TreeUpdate event to the interceptor module. This is solely for debugging/visualization purposes and not necessary for the operation of the blockchain. + + Additionally, it sends a TreeUpdate event to the interceptor module. This is solely for debugging/visualization purposes and is not necessary for the operation of the blockchain. + 3. Register checkpoints when receiving a RegisterCheckpoint event from the application module. 4. It must provide the synchronizer with chains when requested. This is to resolve orphan blocks in other nodes. -5. When the head changes, it sends a ForkUpdate event to the application module. This event contains all information necessary for the application to compute the state at the new head +5. When the head changes, it sends a HeadChange event to the application module. This event contains all information necessary for the application to compute the state at the new head as well as information about which payloads are now part of the canonical (i.e., longest) and which ones are no longer part of the canonical chain. ### Miner Module @@ -99,7 +116,7 @@ The BCM must perform the following tasks: The miner module is responsible for mining new blocks. It simulates the process of mining a block by waiting for a random amount of time and then broadcasting the mined block. This random amount of time is sampled from an exponential distribution with a mean of `expMinuteFactor` minutes. -The mining is orchestrated by a separate goroutine (mineWorkerManager), so that the miner module can continue to receive and process events. +The mining is orchestrated by a separate goroutine (mineWorkerManager) so that the miner module can continue to receive and process events. The operation of the miner module at a high level is as follows: @@ -112,13 +129,13 @@ The operation of the miner module at a high level is as follows: ### Broadcast Module The broadcast module is responsible for broadcasting new blocks to all other nodes. -It either does this directly via the transport module or via the mangler (parameter mangle). -If the mangler is used, messages might will be dropped and delayed. +It either does this directly via the transport module or the mangler (parameter mangle). +If the mangler is used, messages might be dropped and delayed. ### Synchronizer Module -The synchronizer module assists the blockchain manager (BCM) in resolving cases whe BCM receives an orphan block. -That is, a block that cannot be linked to the blockchain because the blockchain does not contain the block that the orphan block is linked to. +The synchronizer module assists the blockchain manager (BCM) in resolving cases when BCM receives an orphan block. +That is a block that cannot be linked to the blockchain because the blockchain does not contain the block that the orphan block is linked to. To do this, the synchronizer module communicates with other nodes to get the missing blocks. Terminology: @@ -141,37 +158,38 @@ For external sync requests: 1. When it receives a ChainRequest message, it must register the request and send a GetChainRequest event to the BCM. 2. The BCM will respond with a GetChainResponse event. The synchronizer then responds to the node that sent the ChainRequest message with a ChainResponse message. - IMPORTANT: This module assumes that all other nodes resppond to requests and that no messages are lost. + +**IMPORTANT:** This module assumes that all other nodes respond to requests and that no messages are lost. ### Application Module -The application module is reponsible for performing the actual application logic and to interact with users or other applications. -It does not hold any state, but instead relies on the blockchain manager module (BCM) to store the state. +The application module is responsible for performing the actual application logic and interacting with users. +It does not hold any persistent state but instead relies on the blockchain manager module (BCM) to store the state. However, the application needs to compute the state. Also, the application module is responsible for providing payloads for new blocks. The application module must perform the following tasks: -1. Initialize the blockchain by sending it the initial state in an InitBlockchain event to the BCM. -2. When it receives a PayloadRequest event, it must provide a payload for the next block. This payload can be empty. -3. When it receives a ForkUpdate event, it must compute the state at the new head of the blockchain. +1. Initialize the blockchain by sending it to the initial state in an InitBlockchain event to the BCM. +2. When it receives a PayloadRequest event, it must provide a payload for the next block. + Even if no payloads are available, a payload must be provided, however, this payload can be empty. +3. When it receives a HeadChange event, it must compute the state at the new head of the blockchain. This state is then registered with the BCM by sending it a RegisterCheckpoint event. A checkpoint is a block stored by the BCM that has a state stored with it. -4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at a application level and respond with a VerifyBlocksResponse event. - Whether or not not the blocks link together correctly is verified by the BCM. +4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at an application level and respond with a VerifyBlocksResponse event. + Whether or not the blocks link together correctly is verified by the BCM. -This application module implements a simple chat application. -It takes new messages from the user (MessageInput event) and combines them with a sender id and "sent" timestamp as payloads. -These payloads are stored in the payload manager (see applicaion/payloads/payloads.go). -The state is the list of all messages that have been sent and timestamps for when each sender last sent a message. -At the application level, a chain is valid if the timestamps are monotonically increasing for each sender. +An example of such an application is the (lcc-chat-app)[link] that is in the samples directory. +As the name implies, it implements a simple chat application. + +[Note - different end] ### Websocket Interceptor The websocket interceptor intercepts all events and sends them to a websocket server. Any connected client can then receive these events by subscribing to the websocket server. -The interceptor proto defines events which are specificly intended for the interceptor and not used by the actual blockchain. -Since these events don't have a destination module, they are sent to the "devnull" module. +The interceptor proto defines events that are specifically intended for the interceptor and not used by the actual blockchain. +Since these events don't have a destination module, they are sent to the "null" module (a module that simply ignores all incoming events). However, all events are intercepted and sent to the websocket server. The interceptor proto is simply for "extra" events. The interceptor proto defines two such events: @@ -181,6 +199,14 @@ The interceptor proto defines two such events: ## Operation +**Note**: In the sequence diagrams, the boxes with a dotted outline have a different meaning depending on the label in the top left corner: + +- loop: The sequence in the box repeats indefinitely or until the condition in the brackets holds. +- alt: The two boxes making up this box describe two alternative sequences. +- opt: The sequence in the box is optional. + It is performed if the condition in the boxes holds. +- par: The two boxes making up this box describe two sequences that are performed in parallel. + ```mermaid sequenceDiagram Application ->> BCM: InitBlockchain @@ -218,7 +244,7 @@ sequenceDiagram Application ->> Miner: PayloadResponse Note over Miner: Start mining on new head and - BCM ->> Application: ForkUpdate + BCM ->> Application: HeadChange Note over Application: Compute state for new head Application ->> BCM: RegisterCheckpoint end @@ -226,14 +252,6 @@ sequenceDiagram end ``` -**Note**: In the sequence diagram above, the dotted boxes have a different meaning depending on the label in the top left corner: - -- loop: The sequence in the box repeates indefinitely or until the condition int the brackets holds. -- alt: The two boxes making up this box describe two alternative sequences. -- opt: The sequence in the box is optional. - It is performed if the condition in the boxes holds. -- par: The two boxes making up this box describe two sequences that are performed in parallel. - ```mermaid sequenceDiagram @@ -262,10 +280,60 @@ Synchronizer (I) ->> BCM (I): NewChain ### Glossary - **Blocktree**: - Tree of all nodes logically. + Tree of all blocks with each block logically connected to its predecessor. - **Head**: Last block of the canonical (longest) chain. ## How to use +In order to use the longest-chain consensus system, ... [add after restucture]. + ## Example: Chat App + +An example of how to use the longest-chain consensus is the Chat App [...here....]. +Users can enter input through standard input line by line and the system replicates all messages in the same order across all nodes. +It enforces that all messages sent from the same sender appear in the history in a monotonically increasing order of submission time. + +[Add note: node id ~=~ sender id] + +#### Payload + +In this application, the payloads consist of a message, the sender id (id of the sender node) and a submission timestamp. + +#### State + +The state consists of the message history and a collection of "last sent" timestamps, one for each sender/node, where the timestamp corresponds to the submission time of the last message of this sender. + +#### Applying Blocks + +When applying a block to a state, the message in the payload is appended to the message history and the "last sent" timestamp corresponding to the sender is updated. + +#### Verifying Blocks + +To verify a block, the application verifies that the submission timestamp + +#### Providing Payloads + +Each node's application keeps track of all messages that were submitted to it. +Additionally, if a fork happens and the branch changes, it + +### Visualization + +For this chat application, there exists an accompanying browser-based visualization tool that utilizes the aforementioned websocket interceptor. +The interface provides insight into the state of the different nodes. +In particular, it visualizes the block tree stored in every node, what node is the current head per node and the current message history (i.e., state corresponding to the current head). + +For each node, the visualizer displays the current state of each node. +In the box at the top, you see the state corresponding to the current head. +Right below it, you can see the id of the current head (truncated for readability) and the message part of the head's payload. +The biggest part of the window is filled with the block tree that is stored by the node. +It shows how the blocks are connected and the current head is marked with a red border. +To more easily compare the trees, each block's background color is dependent on its id. + +![visualizer](./images/visualizers.png) + +Every single blocks shows the following information: + +![visualizer block](./images/visualizer_block.png) + +## Conclusion diff --git a/samples/blockchain/images/visualizer.png b/samples/blockchain/images/visualizer.png new file mode 100644 index 0000000000000000000000000000000000000000..9eae71b808233af71b7178cccdfaf7241079f271 GIT binary patch literal 198200 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

#n-$!7NK z_W>t!K^cJ>hMQk3gh*plI=zaTaxg29KI%hMJ4gc?#Jgs3>-UI(WlW=f-j&oK_duE9 zYjGtq!rA;$0c@VT{{Sh%gK-{ww$Smzk0D`@_jA0V^NvR@>K_e5jF|Sj?AOhD2-7Xl z_!J4QH!Dk9iIY-SId(V-3+*yLYgrp*-0 zyHAN>BFUL`^6U1*5x^CvmlZ2b7!#(|WoQCAn=+mGg`D4BX;M|#6J5`=JF(osP5`_e zGMl7m=bL(zkHQ7j$BlL2{aPc-KXvPGoG zjq;AD23}(fiJG_obT#fMxVjV&94~Q;2Z?hfEnNUQbaI^dBCVvpgCnW##iniCkO*MUQ3+lSLpgyvX6Xo&ou0+Pjx4>L&}-q`7a?!2^#Q~kEA^v5m!XVV?XUp)w$m@$K@K`>7GRcX~jyoFzJip zcdw*NRkh$@Z@1I)^WqEp;L|%XnCEG+SAXpqAy6~C-v_NkaI795?o`@ndTY4JW$^61 zs(a-+EXgZ3IYmJsf0Fy|st@`g8ob5_ouE6Sbl{mNz}9!xvT@V(Q`b!1L;z1k{}?$%(Na} zIce8rHh$2(G2l|!X@T{Z%>JF%mCNAAM2Wc8h(`P?l7Y0e^}TqdL76pP;C@a#n5UAF zC^Dr(T8~_`tTF}J znsT@d_x`kU@k9Laa;?10K~aZqn5L2mECAy4ap;ZU`f-2)giz|&sX;^a=sOqVAhH4F z*1z{5KHkF~KkJ%ZAyx=nH0&XBLoa|K3S=tN*E5J$@K|-;b=NF8wbM;nHg5@eS8FR0 zIhY2{E&0$9lv`_0Fq4{!I|Sj!1dD#x|8Sk~C#;Pu!34a@e(;AI2o4C_dA z*!L^NrAGg@-~yEBKQ8GgcCw2R>nNXkEFWsB+DuBp3jye*dm?i2^*XY{>O_ZII$m zSPN(lLc#H;*exo4i0~$Scrt?PsIxg`t_sPgi#r{1@x(N(szq{XOHn;9{h~VC%*L&< zrbm_sxdUJP(x?S0X9<$kBHQup`0bajx!(w9F;jSn-w%W}W*P?r&m#%c5oj(E=%y5L zB$FZ4NqN$i=2_N8!0lfTmjHsKi54uM?BA}P?7R6@y#K@ASB6EowQVbcqKIGs2BipW zascUOBO)P4OC#MK!q6&;BA|35IW*D?ElPI{4Ba_&=eGuQi$42#|GdZd>-+w23>?h8 z;$G{@^SmyYMscxG-Jr%#Y@0ql+Tj~)Eu&ODI*c^urbzb9Eqf}XbSohMotdJb^lkD7 zu7BU^NQEBaLDqaGS9D+`m!|%(ZN|66+0FpYg3%xP;Q_Em*w;FroBuj>*rAU{Ko7~) z?;?Fv;aBB^D$Cq2{k;S=%75%u0lPs;V)vm#R)zskP;T2x)&dIw%>=s^SLkBjGw=5N z2HQPxMVD#Dl>RTn2N?Nuf2NbZFc6&xHl}!>WghSoOr5II;$DyW3u(vR7Z~RI9V?Xs z6$sj8vcWvXKQr%@AOIM(NvQvPs!*Yv)5i~6e6t%*X|oR&@fJTmI9k14AO37^!{vur zR(Tn`+UDgOC#=dJW)v0SHZqLMdOi{BQy8(?m@*3+rZdq^=*qP)rvu7sG0|gt&90^K z68)2?Oc)V`ROuKopo)n1V-7)R1vY$ZHF8jA97+uW2-`GP-;i-DTKOD5*L@b)91(|C znT1ZBA}=cc%(5BFc?)TOheD42RmqCtd}a9-Ba8Q!#D_~+HN|blO3Pl`W4{Wj93oQm z-bG(#G}5B6*a2XOMhkn8vM-BFnO-#j?-$UEnq8Bf+CQhUjOH3N`eeB&#>E}&SwHN4 zbSkyt7t8~V?E>s%0uy#IaAnUa?4Jm?%5|_6PmbP(lAjVU!bG8Jf#h@X7|ncT2ePht z!l}36iSQIr;|OR1{7ZjuD#fElE7jjYgGJKkw1s2NI^4s*?NO>6Fj*?Pn2kF$+)s_F zy_1k1_b64M?=G5+#*yOZdVpt?W#(TWQzEF8B^7=}v2GVi#@2u-ap}1zj_VK%Ip`ClITR@598fq6zAJR z(Rx4*y1Mb19DfvpS?uDc@IVVqJr$$Rb>8;?VImmBZ|LS1_usHC@M%|+gFLOoVQI=I zuIG7WjL$B7QL#i8OCEGk!bv(?5IWb!^g4p3>w4a3WNRAps~HJQo!D3&miPea$jM7N z_4daJ`f*c6IL;Ed^G}8h6B1O~)1&XW8! zjnX)T6YF^T9+p=@nT{e}nyEiHks}-)38nO5J0IvK#!Eu__jmzbVBOwU%=T-tV^3%8 zjhzFLmqzjYgY6mT{Xh$e*5@PScORIIw4qwRE!bA)7ld<*wU8OXl22$TXCDCYBKx@9 zbE#8T8?D>V5?Ky8rk^)WNe{9O!k9P`2l0*x$rvWf+x{^$@vF86R8zX|9*WG)sYQKD zOuU)OGZ%p^bw?g{yzxwF0P)(is~bvm>L7su1O>4GJLB>}p#lj<7Z&_pB9juwL+FQ1 z`)`M?>QQL{AaX98&%u+4%kh(E_7?5;Vn;teW#^zr_?xaR74?Z-Sd46KRtn{@10!jB z83gd@!T|}yI+cTMZ-4MhB<}%Hc^}Wy=#;sFHlZ+;V$JvXtKG#M`@G)ecU z8kZ7|vJp|=(NI@N4JN%$8(uLr|CICC0A$KPbJWeU0cyGbu}?2A8PYI;QUT}9rg~#D z;8N@dJ4Us%-UyAqL;1mziF^XCeqm;r%j1H7F5p`)1)R9qIbvxiZf?C=knU0xQ!)Cu z82Ll6H=%v-1?jgtrvy8Cwl8kL_KVb_D`p<;EEBl}%}Luoc8i&I1~lm_4SPp>)U!tO zZ5t*y_|TPme-A!!<)%?}3eQZQs#eI^v1o-~ewr4$F;&5<6MEG9o^XD5;S8vl<-*5T(1@1Fa{++JxMPznE?)ImtcU+%-z{XzoT5=0FQdYpf<{nk z;PT-2`;>LBTBG$~c#0xJ3-xG@3Yu%gKhZ+15mZwqsyn7I{kW#++Q)K!qX9Sh0)fLE zd84zAwP^Ou2&^&*or2vJ{f7C+t9HI>m;w7(21#F?Bvf8ry92CwL^5=L z@C5)YG&blJ&f7m!%Iv3|d+;pGsygT;+bH*s=UoR?gJwxDUO(jqp_hpr?2gncTlTdu z;>nQ4?(sLT6{Beh5OHu(>S8pvcprW=>$dwb;WfEZL7`2;4}SY1@Iz2iUQYkWm6I!) zjb9hVB}RBSrYf6}xB%O>%!NgDz9iUKtxnN|KdXs!cn2f87M=*}BK;NK+vLmuTo1<1 z5Oqdo?-kO1D|93a9O>Lkq0<$W`UKhcl&(pH4rvb}g`vWR#(3+VS%qe;^CMZl<;xKQcKV zX|-B3uZyO#&;PN#KLjsWR5tLJr{1H_?;n@%gci8wmbB;K^EF(^DrT;Ovcg-X4VK)|Ya@7q{ZN)34!}g1}nj_^wd>iZJ2V zV`Lq?kiHLr#F|7<^kUrzJ%$sY--`!6wto)==FH_kl-575<(WD_+jm!-O(*-0H+u3G zuswk4_}jh3|BuxT;e1O>c&sAky}q$2ULDO>5HmFVe{Azvt$jsl{%0 z$?4T7mEVgeWGIkWH#s}UN2gdvjW+S}H%)$|FGvT{r5_f8e^sLlerwaapPEXF(6a zi~cZ(1Sf4(?fz3}^uk+_tuvJqWtx3#`rSF)78((8q$e4#f{ z%N$(tDOdicQ_)Q@R(67*j_`Qwyt%3I?L6F{S)a;Mv?ac08v1*64=rU6tMP{O>QH6n z*;q5fpGRp5aGlJC462)cpG)H2qjN-M-ejg&(N$}^4NA|V1a|rQg&7t+DJYw`ro;}jq zT6D?TkCCpc`Jj5Aon29?$6f-Wx``zwEuHIg7UzUbMlK8Fjp$r}p8Ai8{9MJ)U){a2 zv?X~tlDvK%y&w129p<*&KQ#0_;H|g!$D*QV@;_Ef4vK(EiB<~hUi|rl<1Y#(;FPY^ z@DxxoYLxnq*d_>=_8fDnv~Hc%cxxCY+Whw)+V!8!TLTd#_SRf2s^6aXYc}qBU_jL= zncSx*^7o%F{`|F)7yb6u=tF-Vv_B;fn1g3$uwxhfji1~8OUfL7V)6sGc#RiF|5TXv z*95?)XODpJ&?9C0v8g{L2H5^cTJpT;#>(3{j{_m(gCV7v;!_PzYE7-clZE^rL<|_<}ca0z;Mp2TQiRRxXA9Fe$M)!K=3?93}pJGe8&{w4scV zjAW8-;a+3mGFy=i6Ih~mTxd^z@<4#<=V=xu0&RkS4|bxTL+*|t0H3s1F5edmq1az2 zh|ufHfxt^2QT`*ZfvvIpLh283ogG+-%%7jkk*FB4Z)Rp@&T(us1|r9%|DQU2K=yVx zh5lO=%rwQaX{$j#KEBmO9uwYz(Vx2)imti#Z-qA7dfPZ>;CCXz&zlLK`|&aWXV}Lv zx#qO^#fEr%u&}t4b@72JrsQA#9NbEmYHrjOS?RrO$xKi0>d_w#KIpKG6x@i_VinZF z_XtaFzL|g(f)+_fmq1*(B6|Io{L^~w@ji1q=e1I6$JJ%O$_elBiOi!dij*u1|A46& z1_p)P}q!Op#aERV^#ZIv@KpdT&(jFM`+x3>Qz zBTla32$=_pXRFTZgDT~N2f~ZRTSV6g^cD(XFu}#rdG`L-M4FCKm3bfE-ac9J*S`To z;rU8v=>6Xg8%WGm@a;o(Hd-w{;xKZmDwZ9@(H~7;&#@XO^#1T6D1fL5t%}KKXN_`n z5E?sstI6r-s>M&_Fu>?^I6dq=r^of{^nFI#A~OrOxK=T1d`KyWJ3QO2*OavI9aM?l znb_JAxSwHW_ObAV;w!LrqU_w@ z9QFVTzcDo}W}%ry%xfzzuceZB_>*fa_yR4}mZobLep3)0ZwX&am*vOt|I9hQj7G=os{$Pczhk^ZSI{%`GpjCWi~)y zJwH4X_A7-Bn5O&d9HP|*IaCp|jRu;^h2}@JOn*(s7aTdtbs!tDx?ymxR#Pn5d_$X!XkrMb`KHf@vz$A3l7@Bsn-v%me4X|Mwm>OPs@&jY46MxQ*z0-ET=^ zwz@O>E2W9?3dYE}hb|l)+5wbQEH&|L)W@R#t17we;XKh$>&NcjArmfC*=0$O6@#*Z zCiPl${;Jy#M3u@Gk`-6(pV z{V-xv`k*-E0err@>^${Umqp8+ed_kIlNS5n4GE-Cl(O+j zt>XMs0b0Wh&+h_}mL-ysWI9NMYW$@sfkGP((~P0XFA?J!CVu9h+!oaT;<#M#H{&hd z+GO|by9}_Stb_NwzNkj;(pMg?7m~YMX0{}P8)6*FwXq+U49RiPo!eOJSVZUz)um0Y z-kmPCKS@83O9I>Z&Fo8qzF$HHE!+e!z%aov=`P=5BqCkFA^2|Www-R07P92B8B1); zyAYXNcrM6uT-3ys!ojCZtff9?HY06uVDC+E{dLut&iox&8KD6`38ng@sD1ZukCY({ zk5mmxc=JXbRvo{e*9?xI@7%74nuR~2Js$WuOt1*DzC$>8DIrZS()&Yh9_o)T4O%?U8fKqUDb{KW(|-Tf zI$8jR6?c?>8i8E#&HP1g+uU)q1<#I_M zU>&2iPns*)c(@1;IvN|t~!RurIJK783qW?hhWE@(`+Ot4+m36H>( z9jZK0O+93?)s72O=qe-SJ~ZE%Mb=*Su-O@_YG5sb8sSgYH`@`nq?S?#4GibAys)5$W<~i( zyMq}q0cOmkL7vosK7*-f+Up&UN@C)UTM~ehQ-DmpUX^S5!>l{eIk7xv^K}qqYFz6W zVF7M%s7+g!QcxCK(?*&~?4aD4viw|yU_%I%Erg&sC`Y>g)?&&6`^CRvViTulGGS4* z#zZ+LG7vM?{<1}6GQ_t#bM5;ZrQ7Bkm4Qo6O%Y}@lE)QH&tRaL-0Do6f5gGIWworl zneiQ^@Gm4OSlVq|b+J^L6pQ}EeAp;bLQ+<+MAErVoZ8RLK%Jl5G1OiWv~>m=X0Zc4w#8#$AASND?d5tEv)S)U<-n2`_bc1DU{}a_ z*G9WA<&G`Uvvlo(2uI`H7vA1J>`}9487$U^-wm8+P3(%+7j;`^bNF| z7CkCc%dTiNP&u^RvUaVywfLm##DrQ_2G(?|yzTF+`zk6}*3?#}#yKOol@yzrwtjO_ zPgndiG@xv#yPHzl3|h4w8E+*$=vw=EG?$zi(WaTxkW@UKa}4( zUoy8jyO?5a=O=zE$t3W?H-LxN=UU zOsh2}uErK3oW~UrgeM-{7@Qi_B1e1h20Ao7*P~*zXvmC9>?ch!S=wW@oOQ6|?L-ro z9ED6B7EPqPyv0RnlB$CpBfw!;0v9k>r=;buf1+BZTa#pDUo0DYi_ox@nvXrdpGXBh zlpdcZ{LXlzYFo@mCsO@3GD25j=QdaG(^MB~-`<$!r>NJVFORmO>fSvGdHWXI!0ktU z$){-7pY#u>`zB_Sy%gSCDgS$Bh0AiPn*LGwP+UY2J6c*4o7QoWYO>BC5eqL&HW+0D zhTb`Gjpx4(*(Oe4$B{}S&w$DZO$(s*BQkfu`j)5Yp2cxT|63*hHU)$Jn==(Aa2VzaUyT4OnZtpT};+RqKV3F`v z!0sf3xD+?j_2Q^TVALfQNN?`b!}402-RpKyNIjRWZ3{yX_wu#gvPwDDH?Kf+WOmuH zC;j*E2`K)r*l#wkB5&-?Mi4P^Xy;eV4&`-qMWn5!1jlbf$1MVYtnxeXo&099Hx!Jv zX-IS|g2iSv7sobO^`rX37eC2L=WW~B|>TOw02zA297-#leFGh^`bQlQ(>+J(tD zA%l9{>MVau)l$UOfeO6T-0(f&JFdxm(;FID3e?@1^I2K@A7SK2SXjAzB7+$>Nq|}A zu?#}rB&4lj3cm*xTraZ1vDEJ%@3>r%vH7qVJ`6-?&Hs(!!RO43W z&Ie~XEWVO@Z`5r|d&zt4!6Te`Zkc>=<>0`~u6!4hCd2{_Q&)@CR$PbE3)*vW*C#os z_aD2K%-&Kzqtv_;hN>ay$Q*~U-gEd6MV*Yx+n&!gQVVRazhab3{fD3I6(i2%_etE= z?Ax<|BJtg*Hr}Az@{>Jw=CUOYihatDHP>BQke7H!M&EcUFA~e+;p@wo0y7MP!|m&v zKLmXH23uqtb-sDAtB`F*ah|kSfhVQ4iBF;NfjT*}O#7x%oMsJEe~oIr;1T zzDjn#BnVMgp?Oaj)CjlB&vA2$zd^(?&-Ce&DT|eM&SkP_@>~OhhJ3`-`{o*)Cfmo) z<&75F+yvA@T(zg}@015;3ZpLD2O6nN`8K|Zq=PKFJ_#{Ujyl@%z{``rVt~~AHa)X+ z$R&ugpK8_G67&3MeZ8ZSp_^wCum6x1I*$j(yIV25`H7cllE=$lR9GUJ*`JtTMbC1# z+vVsg0nF}35cSTdDjQ-2t6{wiDOhE3VF!@ZSxat8hxu+F3(es+Zgfs^I18ly^In=% zwYWDEcnlO}AYPIm#Ddo@>DQ-6&XPt&WfB<%b}t-6_bDL`Qi&64U?-V9$O51#0^-Zi7oQIS&)606L1!{R}2hyRRb#z~?gs^?Q z8h!LEL)LZQc)T-i)gmo~9!``mtcDfT+%QLBqIZ}YQ%9uYI=A?aWv9Q1dJ_IT1PUn4 z%w3F&S-2IlhfD_{af>L8LyJb6j>H2UMYWgPi_>n*8+%}F@vYejcURlGN=Ou@r{7RE z0z0-#dHUFGCg;WCJyK%j<$K0Di6 zw!QUkulaP+D!>3Fn#jzuAWJZ|B(AVk?5Ox7$`(xivbV7AX|~5f7!~Ia3*^anHQpf+ znd~etSiUCOy<-&om4dy4m-Aruqg_s-aq^mqU2KiZ?0W6wzUKp5p4M((bG^NYaku+6 zIFKIoc-q#*XXLbekI5Uu9FrSf<+Xd=#bYC8&r3OcCy%Ubk4?#8WHLFipj?y|$Vt86xOZt<)oTWH~6 zkrh=*)t1RDZ!{nmariCyL>S!mVe3~gCCpPONzxqH2sf8h*qNG{r=(zSZd){b+t%Tw z7LWD4yG0)nw`!8r(cz4l^YHO2$@-fHH&?ad9aBH_);$x}>}GnnC-MqHNv$CvzNjdE zyKz7Tb}X`9MF1^q2^~v6ZDgUFx3TU_alC2hl=dq;8n8oH#c8~q$rykxU9m{!BGSe% zVy5vT(5p4LjpSw1t<}3J6~v}+6X}~+MQHQ@(>ekfk(ioaW82%rsiU>c5phU9cUzQ; zZi&^*p@`Wq4Zw9WZEbB_lWLQ={YWk-FtEkR>J|}hXjg-9LJ@lyX_|T$@U*{Ln#*#+v;A1tnFz6>P7GNx34k`iR!(OU=@CfT*KE9&6ic%ZEq&)s3Fr zzr-;)eg}qJa#G!(171KQq%~;9aQyw?Vu{780q3>z%0+F?96FAU?*ju_B{ZTP8OQu< zaH_vgQcm1dj2auAbn)_4dD(|^-9&Z12~H{(aVfOqf@k*?gWM6P^7?Q!i%k3X!z`<} z5mbX;ly6_kjcS{u;`uhXrZj)vJ}ngWXz_!fdAOKXaa>}98T(O&e%OAFkfS41CT?C( zA5zTP@4e`RTYD@3KSMy>*fzJC^j|p~avv;~2Raiujn=w-IkP$X&6kUJ=Xg6S^X|#I zXuf!!qiYaxwBgciQ0=9%A$0MaN9ilmYT9mJElr6JV&q8Et@NyM$2ldWVUud4p#&2u zI)wnav!#+ICv$L1ak%ZrNO?ViNd zLV47u%uL3Zow971sTF6A$9B1|M!+~!gyjGk;d&>WFqR+O{$*47{_2Y1(39rTB zyqbqW8?sEJEN8c-R1zqV_AJZfL7Hh6%_nq#PHt`zs!zjY8?_J1voB~^Dk#?~`fBQs zWs+>vx677d)0t-%p4efjriADr2WIgcj9Y*@(a(;wgftZEg#vjm{RgjkJKyo{NfVMc zEz;6k*7nP5Bk`XIa`K%*Mrm`4dAOgjILb_T@*Qo?5)oREY*#Y-tUW;Z$y2wyKPl6O zO#`AOcQ-hdFP~CxwN$a}-<_Q9#@%*Yc;?wzOGT`v6vMv!u=>>zd=J+MW%?kmgni#; zsXtR;?@RJX>ZSd1wRJBLKw(!KPZ62~1#eoECbPxNr|YRopz5_@Q4a?5jRiDZbTbfp zM+y-@>ay)&G6>kkL37J8tu;!f4HT)P`->~{Doo-V7!F!p9#veNjaM(>@)O`HRRPBK zjoiqV7Af-!#!7n+cr@%y2*Ui?k?SJCzP?y0x32^lfJ^bE$v&uLQ>G*hK{_~r)D<9& zc8>h~;ry|cxQy;6rO_I3S}ctyXtl>#r=*#1paKGQ1FBx~CGRtpy{(BQfFVk-LVe`OAY-O z1gw`Bm54NS4Q?$9W|U=P8kUnIrmBSB(>JnZ|pv067J zP_Gm+bk}WY=N(^N=9HPDJfjH#c}&F|b)1I&jK(6dLoPJnvnwiF0@thpxt&+4UfSE1 zdAmo+{3EnyA8EOmI?fnKUr0oMo4fruLyEtOQQ)t67w5O&LJa2Z+uXW``^}_#u3IIq zuRV`$K#a3RVw-zlqdbOzv%Ax`d(_FGYk<<&sY!+E)l>%PzUaXB_Vpno$W!!?%^hr7 zd!)gT!dwq$Zla}aenrv!YA#D7G+9;vW=l?+SQiaHmn5XX7Ch(H4>M`@V#`w(*tgy( z)qa^-2G3V4FJC1^M+-MgyJV1j0zDX$#uB3l&<6Nr`8cy0?t)FtrjDB(L=`62#vEIvtzD`4=2rB$=v+zxLbUbq&mQ+JH zy-mJ)tFc3_iA)t}mA&JFcod*R04ud@Q>HQ~#G)qq`M7F zJeIk39yCcgn#l=SZB-T}<8(=_;#|^)a%Y$*Rb+41C-6*WN|R-0Rc|CXDVFnQS$rrL zx|SEqdjb~*amMA}@oJ9SqxY1vW530J(>!DmV`0(ejh=pJh~u2U%u<|lfk(X+!YLfh zxpS7`>779nLE5b~oCl;qVzWB?@Y=}zP7s7Q*@x>V1XJux%T-XX$XM~KbLa|s@H&4j z+l;E=S0Cw_MShjgI}E)usB%t8fhV+r=TNcOZB-pP{BHRmO9W<1j7NHOZtseXn|}PN z<kR6LFn; z?Fy0u>`%^)8lB01xf3j7Gr8?^F&e(zRUV)8IC^+PI?F^>XaIP-2GSzW->)A*$Q|tM zka8NlX_HXJlB5zkT8GVzYDNy1l(@ZoT2$S;9Ehac+oCPI49x_Teua6jo`$CT403qk z8DELg)`92{_i}lyEuHUt7v!AzEiOcK&A0X%gJGQgyLKsv2u9}(uB>S;G$;;J1(>_B zBBw~0dX6&sCykW({9`~-Ukt& z7zaqiU~T%7$%Y}deBrKeBzHeb!1#@&##g~G7^8pWsd>P%yQI<2* zf#2u12DvWxvrUx~5tZxS)JY8y7SU}>9<%&oBkZo&3AITLO_4^fYNM)31ZPOYWDSp2 zQ12=?WY1b`bVi6^#pRB(EC^iBBf4-N4x5g1-G#&KK1kouJDQz`+6WwLml495VH94K z*>9+vqi^2zt3y!g_fA*u{kUDnUmODV&fuNY(@l!pUt`%Xz(T?v zC^<%s_I?pt9evYzpCMyQLwkB1bgyu!MLzO;nW((krOv`h!})SY@GmCriY zSWj7F_>aM`9$mWhrz{?N+NTQLRMU}bDQJ1eEld!4xII4mDL_~YdE@P)fBwaB+HchzuTkmr=ECSKF2+oH;$iqp4b2dMAyxH z4XPF-o7}A_av_dQj^!%ouvbwtZ>i6~!@I6>Vpw9|O*Rteo*$G3H-k`IjQEbqJqLpl7v&>JxjY~1f z2=Q1^{uC2RQM}T}LT~ieM~q~#b6tg7MNg-jJ!aP)AvVYX(l90FE>7je@gi4P17{Y) zuE)^Sw~N8q-Dtn#zR?TNS47G)zPa6ykUsnNe(h!deavQo=dm}aa6bVPg7WXZWC4i~ z+xnyiR7XdjQ&VT1Z=?U+bC;Y+lXDVP_|tmH`%@jn^dvsW8*TK$Zq|3y)cZ&jx(OsF z_yvH3K`k>~p4;?R583*Qp_uD)8U<&LmR<{NC8ImDYGHX)9#&A%rN8C+{m7R6^|a#| z)e*Ad@eqxMc2VQn15yG4CLCSIEZvlS^Z7NeyGP9PMz`>z3=NmkyeOJQC)=CddNAVp z`G#b%v;+{I9=0ilD<1S!=v1L@U33-!*b!D#Nm)|e&|%iW5ueN6 z%>3k6A%pNum!&q@oxNy|C#^gE+YRZ%V%EB?Zg)qQ4rb6{z#moYFWH=ubm$6yu)$AR zOHifY7uc2?HLtp*qzUA6wNori)^CThN30>^BOV@rpl71B*=VA^I>r#;-2+g+8=l`o z%Bzt*J$zPOQ)bY$SZCva->+DqpLzeFl`Jsgks77a6~5u=`E(?6#~bw#+U5Q6{0g>x zmdgI{KoiXybj}=m+ED}NfW^H`1wf(^SEf&Fyc2OozJMifIUfTZ4(=B1*)0A}=CI$K z6{`{>o&APmU$s3#CEUBpB`4ldaxR;+WwjSVhp-PxQ{Oz|U#|LsH^u(9x@y_J^TBoV zjH8Uybr!CReX{+E)F3l)vs}1!$*H>>>I6d7o%1gft+aP=L!5!^Vg8jT8}bam1#qlfI3YA&Ld zo=_VJeU;1c3;9@IYqS#uG(G$GXuOgyW%bdjI|>XRUaR<-IO?v&Ol&3d>V}`!D+;FG zbAOPHf)>(M#%@-ZalU6;rQwAcF?uWY#(EZYetwkJGxuB!oeAqOTThoV6 z$&;b>fr<=;xyY3x>-GxbC!4LB<}buGLRX$u+9b@IYs-53z6~amDpC*6FC9A!bR&DA z88XiW@Ipz(jtIV>%@%tZ6I%R+@{Nd z!nisYxKhhDtOOA4-HagjEQ4@1KcrWWLX&*(Gl5pC{xjG|y!QA@hy<2wx022Xlq z&FPtVT$;>iVS5uj6~t?tWn7gbXB}5TqQ5RQaFois`VIwmGUnP8ZLP!@Y zik?E&OWal+79iU;WfqdUJ_oLICHu&7hsb34)Q@8|vN9#-$3PP)rcUO{& zrqmtOl3lI-K325&IQNIgXnvP7Lia2|+R1U^8(CwPbdjiUYh85^JieXQUGhv}E4nsS$J9(JZw6kHe@ zH?!i4mNJqnevViNIa4OgBy|hR4J)EzMO5wH+?CtN^xjx&L89)tHNn;vp6+pZ0Ct-A zKA1JzlD70UE6>eh@LVTcN=3xQqAPPwFj(Y!%#4>@m7}cF%wrfJUEajWw&C_6~H#8r%BcGLG8N5*RTelk1&A8#4z#4|$;i4QV*C+2JL1?d3KN2+>vly-?h1 zx31Cz@d2R)DRtGsx(}Rq)l%mAv;whWQ#1fr-6X zp_y^vBHL@CMzOE}5!CG~4^uiio)(3C_QqO#0*52NXWE;QnRAbs4lB9UIhszs#Jj@K z`9wM_RjVR7C&jqrZdC1u>WYqWgPB?W@;oxL6qboJrIK=*W3%H3*i6kFr}}rImIc1FlbqJXHSx*d2L(H z>vCP;QVv-F_drae^PEY+JU1;%YQM?#c($V?NQuavWphZ`-t^-{ik)oR;>D?H!lsVy zK{~pI@;0^IRS?fU28oN##Xgc^88*03K(o z>}n`L?N#JiLO^!uc<0p@qi<6PR8%z)!!LFL8T`ou;oXsSuASUnLxlsIJNeb>iJ?}w>}e!$kn932qll1= z)Pn#5`S%u8WA`wPMqESy^6FCgx#4`_h|NMgV2l*88!=vw+#pdwM!v*NL`vY%p&Vnn zhu=M|sC(@9#BTFqSJfkivd6FO$q+1bwMQfeKLWt7!hx)R3XMzujT7N1C5SS1X|Y>uSO}77oMLPx^A13S_KUh zP8GK3F&t`I?p_YCrJ5rmlQ!$zYpoYh$7xW3PUl@>u>g2TwRmpMvSxC%Kze&37psw#g zp6ao>>dS@9!Q}aP?lfCc%{#7%Yust1U|=u+tCNw;Iy#q>{sKc3!)l27(TqKumgGCfBXw$HqS-IOaqt6p7ojmP0cieZ z4dRh{TFy!PV*OVa{sN(&Ya8P!Fz4ps+cv_2uFNB|q#lMADARz#LDv<_Ou_+$;?goO zFA+pshp{bDs68r6_FI?Xk<_8>s(NO#D;b1gugkJ|ePJBej1?{ZN6~E4fj?=By9y(H zQ8=~`=e?^Nwd4olM$;VMWkL@@RQys&sB5~!j|t^oE1_T zngF2Hg#PYFyNW|b7C}&w!KeqG-Qe^@E9cZ;FZ^r@dbW4(X&oHhdyUQ7 z_{)Jre;UB*G2~I}e!oCGiB1Yx_vnhUjl?7y@^HxM1U#wz0Kjf$Tlvhi_fq>a=dfHg zH68ubm}Vq*;DqLmYCnAVDziDJp_R%_X%4wzbhm2*xD$t$ zI}Kb+`sXG>?nD<{kaa1ZT`SQD!h#+#Y)M5fZA^NX`*ioa4B0dy(lNesttbx;6=b9h z=J_?y9E^04)me$XjGXTNvMW3(G7}Q~%?M^sX+bQdn1S6=^D$#31B>9m(EDM*1Vu#U z#Vu^@!Q_yZ7n0%m1D$awVufNiJQJ?utu7LOHFD&chdhNeRK zBO*z84o+a^54!AU008dn66HZpMbT*XK*gd=$(Q;2N&1r6(|S@p(RLBRw{Bdr{ zfBAD3u@i?hhx1{V=1emTzQ>p*)VSw(@f_d!6<_@z#p--mX-BS2vI$pSD?$EsyV+65 z+Hz{w#U&$90rz~=q?31R?a~cM`$kk?rYxIn1vt4f_)5{pO;1K95{vf%OMVs2>mhg{ zMqEu~!>a~;8gdhAD*#X(a1^e`-Jfn9tARhK(Us|@DGreCpCc|^iHWxP^m#B}s4TsW zjWeSqC6Dazd@@i4B9j^R40Dw4Yjfg=95Ov2yz$#^jP4 zECAzwYqmfFo|iJPK&a6l9OPooxg?QkS}k%c51!ux`DZ4_Hu_VQt3Ww@itGaGTyvH$ zADLIDJhp7jG6{mFD$DkDmi|Wjofe87JR5O5bVL!s0lO_$YZIG6TinShvXTohhEG5Y zN1Bxr1KB#zmQmOdbkkhd13(5eXY`q$LGO+&M9#w5x5OWrhO2QbSUVQa;7-%zS8w@# z;ShVZ>8H>DJh?=;!cn1Q%PNJY%_mgFkyKHpfmW$SNp)_%E04(*H-y26-iCC~X@vBd zl96K0IE!;xq$^s#^a7s#l@iew&(m>+N@I+UVI>X@E$>=dH3pH6{v#TO;69hYbH55Jyw2C_iWwzhGx1(`4d0f43agBg&C(@ z&CeaA)stBX+4<5PDKGBZp0Z1_H8f>R_*M}jyq=g)w2@|-vN_3O5Cf!o+@azt6D}S< zqPuV>OLXeC2u-^q`utmn7@NF}4#W73xs+P)LWaN$@lkd#0ui$s7MzA;E6T`>igHL5 z5XN*FKG=8zY@?gg{VgN(g^?r1{?@4PfrD7d5!~UpLQ05>KJA|4gNeT6qFY4e0TDS+U2tYT1>?b8yXi6hmJ4r!XT!UArCP${egJP{0iQo( zEA|(64V|LHj1m;L{UmLpXmm+2IExttO$jFaM%Y*4_Hbctt^+IS33bPlai}>YqEWi; zYA#9D;AI`BU10|Gw@~qm-7+5p?waMzvMCy-8?7;Hs|@!ThMGqdTrVrJE?RlJzx-a) zabXj=U~r$Gwqe{+QySQjMl;-W4&k3OO3nxII0;X(8*t?2akd07UeGh2wdrzbM1b;v z_t4_oPHFb>S0m91^Oa*xBpH^|){)~j=SBGM464!lmd~rgto1XON?ZyNH|o*gO^V&> zb? z%RIX_Wz%Q3*h$e52T($1X7FhGD&xEK->xbCvasNRo!iE^33LCgDAIs2=eu!PjGMDe4d}UJgn(e1U%-^U1hGrrF6E-*K~1Ti@mg~G9q}Z09lpe z@!}&Oh;(ro=7D$864`)!in^+s0=ojr|J{`MAPOK3Kd0@fgpF-R$qG4cT&wT2k=o?^ z27R+$oT(rhw1h;gO08T|SIVDvN)Yq4P^Ug2n_TlgP5VDKj}Ea}jY}l6cD7ZmTZt-y zrLSZK1AJ#z2=K7^%6D2p;qDSpWT^4OmYGB9rOa{{%P;ejD*VIsgHsYc>iGA9z9!uD z3gRuEge1|3$%_U_=~YF)wvPS$&|&am*mb*&!}QW6LSn^=QW>6PsI2cDrpGl&zHREe zwIprt?m(iUzqKx`Y&K>Pe{aA`!6j39k-T-iYt@vt-V40MaZz%&g4 z*lJ2ui4a+YBILpyag+ywL4k)vJt~F>kH6n>Am+l{>(^u^6%&>WHmg!0p>>7`stVr< z?giOmi)M=`1Z=6LQ1RfIIhjkDK8N^Tw7!Xvb^}{A$tu7F+iltq4*>d%_uIFY@@1~B z36r!AkOxqIx_+0-b7-;qql9DA?bBj6wiW|d1s};)kyYsN4kYwGE^ycyq1MZDdo6|h z*eXvtZq#R-O&0grn%*1g9Tmp|8mohUgGL&p5owgiBw~qp|6HK zYQkt=M6~u;i}-ceOYO<3-;Fi;PxgPK7&TuA zR^)0VPGUbC+3%6D5y1*Spb5FcB|7C-+&(SA->D%vTaM!@g=eF;=PbvK&T!qmk6(U7 ziTC+Uo$P7HD0U0e^enb}jmY=SALG8o`w zb>ZNsHx^^%s-^Mq`@VbIVIMl2G)Wb6&y8+3%UzzXeMm;ljd2Ffp{4iOs~7W3AU^4anO&=Yk$pL` zHFB&{Xt)@p$|c$;kYW84ier0WXVoSAPzd$?bxD2~Pj?1eoOO-C_arn~gywM*Zc60x z5YwSQysO_xd3(-AqtynDVZ+XIU#hr`w;pvW!+nCxy^nd`(8EyEX5H(XCqof-M1jOJ z)S-gF4V$wBZxsbj2*&g1@}k_Wo?)jK3Y6(Tzdok`j+p6xlz{kk1k%Aj{QoDQZPEYz zK4|5($p2o>V;yvi6%CP>&l3kZ>xrovF>P&m&7o-MFVo=_I$g+ggpuw1Pe>iGI-Z`L zNtB?|0|+kV!c4_Y&{uM7On-1J;9nsAgrDbyPiNf)(dKF3{qcW}0LTFec{mI+gw&D^ z(~&t_CiM5EoY@4Z_stk0P5R)N1Jh*Q^)|zu1|@4s+IQr3h3>Y$+~8#i_sC9HtM*f! zc0ER}ZI8UX6ob*JpN7m$mYHf%r%R41kPQ##Zi2U+GU^2V01)5T;o-qasPE*=Vh5h#NBbL*YS;%g*k&Q+r>TOa%*EQZK&h=Riyb4p7^#KprYA&{1J-Vdin-=+{$!E?)- zIn@A9(Ep;YeodWT$YfoPUZ-Aj_$}*=ZpS1KqkQ`At=B|U6884#wF>6FPv=*NPw3Sr zi(3{2?zw=<`stSHb(p8nt+V{iynK8rnQ&T!j;3cPsS)R|8Vw`KphSs4)yyfgUl4|K z=hQ{_|7)nae=&5;)P* zTfb1ZXFRkBX6CN>K_1GR+~x@eh0^&|r~bhoJVRGQ>HfVMN-P-`Ap>m)p!9U!x=-1s zyR6kpBOnQh`%%#C`1s?29134$+o`Hjf<+fuS#9g~cOv*0_ezEt07mTDp1nfXd+gzn}H4^?rGu^*mp$HFC}TV()XGd7Q_|iX=6}*mqIPYt7sTN|W;S5WGSS762dEuaIlHg&fpwsdmj zC&lW7h@BE?ovM7P=8}?2ZHIBcO0`(c(T;~D>|NF*a8UM~2?$P3b^iNvs>Io=bNIWN=Z(xP~WAw z#EKHfot<4OaW5{jI${orJ^MRhY+cr2TZmL0eb02|SBXo>zlGQbJs9R^Ac@ z6>}?U8vcX+Y5l3gW;+oy`&1-Xg0GU5)yZ4<^l@Gmmo0XFK@o^D7I;msVfnLAElNl8faIh;41DmD zsfkoy_K}wU(M3pV>b3I8Kt`R7IU1OZ{0qnCK)E;I6|sCt=&(tyZ>n|YiqV-pT2NUm zoB-$ThlWgmj3?Wc=a*Sjb-5y}zFCI&lXhl)J}{W2B97=>DUx=IFGU?3tk78+wi(2@ zM$0|=vi@}j4gmqRkYADG!h|C!bu;CwR|~IRvHo~ukJb|9KSvpdACsQ7rSR3|Qc;Du zu2}I^f^ml}>o98(nH<+NJJ%I zm&UMW{F86H{sBPOK6F(V&_~U_Z;@OsyY)nnwQ=|j6Yw@-K6d}=i>b7RVWgGI59fDs3j3<}#Tt7yqHa2qz!O8NDcZnb zKFc#oNG+wZ=Oz849F%Uol;lt1ARC*w)D3XDDZ~LWFwUDNNrO;8r(ZNhbp6ID9Pii? zpeOSi>qk`q%)gm%2#&VA=+Po~Y&5SVpyoIRu3BmMn;ZtwF{o3(3epf~qPv*2X=%`< zf)~-X+vlkXuk!m%`}7*%(5&{kl0Tar+^tBQe(-=- z?yzWLxm`7nr$VILKh1Km?#7g=w%NzYS#l|LgOj}llU~3CoIQi^+el^)oE`pnv|hpy zy1WGkG4=(}j414ux^5BkVUUOC{zEC(g3arFda#M8B6;FWH*0dYB6hSB^9w+J`*^LZ zXBq4}nIZlCb+s=k85mZyH%)BR-nt*57?rLwvh$`?KA_?iL%{HJ+lpoLQo6R27v$h` zSJY~3Y@`Zb*qf@x>{~X#5H;!fo7{wawet$f`CDU_%U^ahG-GF9os&Efpw15P8hbnE zEu4@f$WiRY?m7#7ysefwW=9!1YKZ%l{X^@;q^|DK?5Hh&sxcDp5>)jlFo?u90du0a-{bD%ek$ovhyC+rUwcRMnf1C zb=Fn%k~MsXY!RWXJr7bqxtz-1e5VlfzgWgPy_~mVqIUVfqhDMY@cTw|40U78h1(Zi zIwI*g#8U9!87!NWrnOjLPG)`wNqW~by${Pfb&VD&^rWJ88)uU844}U;z=^7@&o_aF z=If;s=O6L{2k>7%;Gv&Bn@b`dFXY!(Oe<;FR#tYh4qA#-eWpPMsu}Zx+zJ=5n!>!K z%KBh%!r~d@dfmQ)#08AL%&WZ`?;deA?(H+ZgiV4zQz-PQ`J+6SqfXg!?F9*(goEQ* zxS+6o8&p~^HORg6`{!j`FI$U*aTgxw?rg6Zjo2s>*#)SvO{%F!m5L_PYkW)bUGJR= z?w&*0HOp{iX3DcJZ8^-v-+KAQQ(zR7eq}tMLJ|(303g~p_J&*Y ztG2&hj@-t>hFMr zK?cHrV96AL(*Ozcsn`*33n#Xd+V<=xO|)Koo(Uj>y%PE$Up=5)sR)Y6@Y%VKeZ>Sn za}o4Mt2h@c*#3~`r-0~7b@$f;jkuojNU`e_2fM3UQEO`g##+@AFQWfAa zzS9zwGTZTyFYUcMs34%I7)A$Z77&QW_PCE6GKy)xi8Ff8OrFOPBfD|OIg7d|e!vK{ z|MDQnlYu@aXSLHa?doFe-Iqx(CmVxm7U9-;#Zu5nQoyGIP0d_vcLJ4&`=!}ss%8Wn zir-TFf)}>MGXNnGfGMnQ@tC(DxH4+|JOH3z@zLZ-N*WT6XrjC6?=AArlG)?T21>%@ zo)+&(4CVBuTWYzP@8?+VW$qjAka_fKiWhk~Zw}+(%x6u?X*!9Y z7;)gA1Uy8t!l$VM#Haruu0Hz(6cUh+!hZkZG;Bq@=ZP^m{sbPP-mtc87AXWg`vq)t z1V&$4=juOVefrN@@RWOBpX#6f zj}L%2k1Jwz(~9P2-U8%g{FWq_eii?4{SlD9YeDtm;Lr8>n-H0!a-o*jr>~SvV=+Um7$#!N_2@2-^&5~|%5uq`+Ie_f<5$L;^vDM-yN;dlOF(U6F)?*r6dy+l)zk_z+E z>JENC7l;@^UI~Rp;2#x$Us)FnSTDPLrklD&CSp7?&TXuQ#6gYCYsmJl-Q*hj%U(T> zVAXK$_;Ba!{*o|SY)s*`*z~DQtIhQ0aFKHkQj$Hp8WfQhV{+uM-pmJWger3FS^SZe zdJ6t___(7+E7H(1L@?z5;m~ilxR^@hvTk^Xb(NfBFB_`c+v+nl8PpzHTfHv@TJV?3 z6u$WpUSB2rLxwGl3(R*quW~N}W_uzCoiivLCEdKWRP2Jp-KeCbq?6yu6$isMlAJ(* zRlSZig5%>OHn~(u^wIO%v+~7~yymt(=cj}YD_a%3f9P9@AEA;~6ye~@>4yH<=ik@= zLAnA@GoD<9nyr+nMYVxAhdwAcSjXSxZ%V267MKW79EVki6s-fX`YijRIdDp&Z)9|o zL5xosUW`TcJB?O12xL0vz&ZT)7=Yh*#12Ja?O`3stxPQ`8RGq$T%1Q3(xpNzy)zHS z-+$#Qkw*XGAO?n)?U2{C-~aQ4Iyg_gE^Mt=&m1FkRR}b%O#0$g;q|gZYB|cLbvTs+ zcgN-5r!v0=D3f_UGQG2`y>DXpRa7#$H@7`-u4}0|y1M4>Pye{|fy&@FhyoIi^KQ0F zRQf;ZtKaz6<*GYPrr_ZRZsjZ6n~DFcna|6({V2`I7+}yU93ZEpyn9k41Tu9Vygtbawlc~e;W!pT>^T%7 z*KKbXZ)W{iQ47v)BDVSCy#no1fOVA3Kl1HAN96BA^j-(_nKAnH;-_c*%qK`b_yWQI zKd(^Fh<*L~zVQpR-?Fiy2^Pos#+m0wmXcjHpseG9tOWjxu0DtO`KAB8eh!JfVr{dr zE}Hx2Y-R!FT{3v%H!I?qBXxY>`EMhPo`+W}4pPqhjOF;>R)yGM{T3O0hw>Og+tdrCkE>8p|76jZfJz&Ants+& z00({iyHH%H44G75VMm3UUR#xtBrn`#X9#*!)Vd#z{`-`!tAXkXPw9l{S;>QF+}!@o z!xt8&vFimVSaKUTLul`a*rY$H@Ye!BVlB9-^v;fj03)r^;&KC^jFt5hk)~`>Cc{PI zva*3Zn}5^cN~(Z!x%WTj2cU~V01>Z~x}B3#CEbBeN*{0ZpOgm}gA-k{xu#`%Mr8Pv zu5`=ySywJa4i1{_b^Za3XVbnYTf*4n>N_K=yFkR8vUioaM zuc-8>dsJVpSu(?9mN$H45!984Tha9V9zASDLyZ1iF9!bRo=WuIsgit?UH4S64P&7A z(*x99(HE`hKUP;5UC9*`dYBd`Ib=1*UO3Jp)V$lM260S`x@DX1T1rOwKoJp0FppQ( zeO8QHt7hf_5)L_s7_4#b-PL~_P3+qcQ!ZQR;6@Y$J-q^<)Ba=ccMtrc;^Js)9k$Q! zdB0tD<$p0KEQoQkgO5r0Rs7@6&d5TTcp?a2?aKT8UHz>lG`!XT3L?hfFby^0`9AZ& z*;xY)-CYsOAyal?QPBdEF=g_O>UE>-x_sFaDR?3d_K4)noO%8u;7cVPEtFf$UdCtF z`Pka`x|2*Od?r?7V?f2>=T?W9fVTE5eJn0XrxZ%^#w?Y+na_V|Q$Wq8$+tUtyff;8 z)!Yh`484&m8C(?&0iG1e9u__hZus*Iz7vKl(6TZNgxh0p=OJ?T1;q8qXuQgY8Ri>&%sMBj!en@>* zf1O8Vf#muz7&7FQx_z6u`Vn1qdFhi;hDl&aq@H)1@WP;uf`|VYR$D11vUd zm%7jQMp(ps55{}69t;Fz%CL*@c9|1j@`SnJ{gM8%~RKtKC{ zos8hH1%09tTAoT)PD$@`&zLo z8bvf!ucH}?KpwZ?H$p;X&T&RcnliLpaT*Ne7$p_fad(aPHT6Z}<}a3ak186=){;M} z-c83>FH+qboah?P_LWW3Z+Ti9OA>R@rucdJezk!_jY-*&oXouS)U4G3@tw6{z(?El zJ!HD*S0Mk63suZ!mHl^=w7znL({2ywABxhR!cV8J5$gc0;k5kyM)YQEP8JWle2>T| zpIyu7Oz&-5yrIqSk>l%`<;yMJZUhC3-*VQr*U8Hl)*QMLgkFnnWyG{*)$YYYAqqp1 z3J;uDq$r1;N5t+KNz9bQeRONHZ?nFZ`&?taTbF62{xSorxsgdAL9H4k2cxNFMztwf zanSDj5WC9#Xp`lsVP+t$JQKoRK$KiKq4~#`#d@%RqMmmos{oYZLB=6mAs)60v zFv$zB#bW!AUG0PFxOan3hr1G1N%Jaa$h-hBK!B4ZtFXb$mS@cu|H!(wVLrBEydAJk zO#7wPgu-b3%evb>`EPRMEbuq};R3)Er{&T$uuTu9&N6q?>}KSAbn*bwD3j6Czb7C6 z^?qK^#9LlwjM;n-XEj$$HFS6%k8XSGQ_O^$1Snhn-XqP!;5@-a>#z>nt!{Z`hwxd_Zg-3FJqyC|#oi1x*0^>xH_ z8ti1+;whmF{49j;dVSM%%b9#VcXcoF=eH3=dasncXq2 zUX~4yE3Y&07&c>g1bas}5~7ve)Y!i)jpYTlaxpMfE3#(y^cJr5(OPe%e>R}Uv#hNY znx7-%6cq6nW1ed1*=ouh8ohxlUrabh;X%V!BC7SXui4NLOm- z3HlhXt`HW$8{>v-wRfhTY`T5G(QQ5UVXy~*6km~z?Y)RyW3Bi>os9IxzRPa zK5WZzB4B-fUOWW4liI)+a$QQ&?zURVDA5qT2Ic;Nqp{3ZwN9U*`ap@rej$E?pD}BR z%ZtHHLe}1XyxfAEg6Nrj)1-vzM2NZ|pG)_=Fg~?f(!hw{66tr@n(fg=?DyJ2K9GJq*_OyT~VoF~YE3k&ApR z&7B%4q_3=li8%DjSSHixYlo%LA{Q@Yhfs;8tiSk_z-Rn6uQ%CNK+3_Y#5!uYkF!W@ z%!|it|Lbc<869m25BNbbUM42w;LCaHE^WtDL#Lq@5`Xbe^eXLAt|Pm6FYaXxKR0QPl4tlE=vht`{E-*%^o!dE}g5A zcD0X^b}9;k)}y@cGQ?@vbW8X3Ua{-z9{iPvqfO4s(`>Q#Hk#R!Q$Z%cZB%->g~8ND zZsvN1=O=m2+lm@53~vV?GyQ-p1PH<9{e!-qdNznpu{Wf5VtT|@nWnFWt<0i#*eoQ( zzLe#>>zK)-X_{X6T6!4K|d90qF&BUU1F-&I!mxSI5hyH{^Z_SggcAgk7e@ zTuRLoqX@4*aHw?{4Jrel6gh!BN;1!U2#jp{>N{xLo`(;wMBE;+J{ z&@v^9i_l6h%n@#yGf^#yj#?yUW9g#tt0`^FR=JxwFEKxoW3m`IOdwP|wzj9>S?5~X z?2IMP@q~CmNRYKU zD-=vj3Cmq;Uwlq^5?ox&*!l2S?3F}*a0Iif=LH(j4cV{a;J&c2{gOs?k70Qa#wifv z^7cyZEd~w)O;c)M(j$5+`^QlX*c1XJ6;bIp*rpe`SI4c2;@YVI|hob?nZ%8 z4~KF^k#LDQW61u#DE0mH-jRrp*whVPRHC-FLJ_8;h#QFu>XjSYUyaRLH0^&D5)g|+gB#z>N;hND!FEUec)eBaw!Ot&{%V)dtw zknZB36=43I6GzMMDP|wN|8Zx{uF3s8tlB;!amHBNVP6l#(z#bd!pP zsF-PFJ*gvdxs>m+zh8b{;<0|gehDTrUHf#Y-SX7BJrNtv%6mVRmDgTm^xeTBgvoA+ zZD%bmNOx!Ta#z9yNb>W?mO(#uxSFP}g-% z_syJGO71(!53Fi--EKP3yghG|Z1px`Rf3eqN^EF0n&yV^LjxYOf}SLOF|ls!NInu~GX*R@~mEf4N;~bRuL=gyT(Xe`1o{ z4@D}y^QT`i>6a$O;w@vB59_En;Ey*jt;vqqs?nmPk&d*Tcp*?-_#UVhne(PXhWF>7 z#u67`G!LR!&65-6doQN(lv6e&o6y{yzpFdKLnQL+8%mT)0K%8uj^V%=cqpnkCQGtb zay@WodzrNVxzSZFDoNwyn9(oXx}{bgqGE|22z{1vn6lm8xN}CA40`+ z$1k9!MLj;53vGai-I>u&l*ft2f8$s8=5jNWkv!EMCi2mO+2P?U^xMwEfp&udpC|lh z0JClyM};^K3L)NoE^9!VxJl&c+S5J)92@rTO1XqpDJjpiPXLe}q?7Sc_SO6V15uBKM0v`TX1n z{d$d_yILXKwRx}c8b@uOu&8Zp@M_XbRHe*B@<{wtVU1n(l&(*v@4@vx_U4-RC+hbd zqGtAw*edd)B@wx>h?!MTDJ7S2z#1Ul2_{*aP{uGF9o(~ul-EBESQ@3pxZ&dZud3MUf8Y8GI9aR8fOm@gN0A^ z#qOn!32_6jT+IX08HdOW!{;AsXy%Gk_rKa&RveJ*vE()Vo|7mWX+JN$lSQ!D#9Ltz zrq%!DklK@z=EMUL<3BPU?^NN+LNYGIvuK2F+^W3$7UDh-Lt4R^b{Qsp$;@Jd?cCQe zd~|!c_{}H9*;Nh+!34Z%_fnzZ8ll{c*B>qxHqNcw8?s)Ld7FD<_x4;XjdM9=$T#iz zj#?IRfs_X#)pStlWuGe>VxAK|ShJp@_#a7Wimx0)NBIpvj`Z3;p7#Uv^`*r}FK%A{ zk)U?PQznC-UohpZ66=<_4CB=z9{Hsi!{t$<-6v0mY)3isXmbLF(Nr-<(j+@rx(5`u zEhW%2C!0L?_=@9RPoa6{*6e?`D7-}DS5*z`6x)r~`nv-EgAe8|2T`xYU-l#9WT}T& zs|m&zqxiC77T6x{d4}Oo>e%#ZjN3&CR_^?)?`?0ehzoUd^bf5aIFzne*a#HV_hRg` zHhLPHT|VK`VEyc1$x?>#qe)&8-E#DT+ba*PZQedimhO{b^4N|b!WCGKC`2av!N0;{ z2zev~P~YsBgyI&1B!qY>^qul!_vdSmK1Hj=rp*gXxz(&EEsL7;JyLg3#V}&Gl1tju zGXG(aZ5%9t<-)+>#$KajF^<*9RXr4DOap?>u~yf{2XR{dQbC7fMEdFG#!uCieZ|zH zM^x@>YE;y%$U=Ght_8noZD-hcOm-};)Mo_sY61R3%7OZ|D8&M*}|HY(JOG=g+pcmD{Ey!*GF=nKfHSCwOEeLjS}a3K-93LD3k?tbIVI-EY$uMxRYAnO18YfWNg;V zXX=>nwnfz^4*S)rGGKa#`p1Tu`X4}7Q)Io!H>Cpl&0fhm<}0;l^DSAR7DqYtYs2t0 zN;Ot`BIU8mYN~oUP1d$<9C4D*K}WQ|1i^GW{i!%>7LRPBRpYF5oQu*yqcD}VfJF68 z^ZVEvD1t(bP5OGm&W?de(C%Z6@+KUVT(F(S>5ysQna_u`PsBReifU-24Cu;KrV=?n z7+Jp?QqTC}*1kzkrfPfg_e`@PM>$}f6KhmBU&{T|*-a}I6Blg^jl8&Iv%z=K41caL zq3eBmUg$O4bwZ2UHS7EI8}%o%GsY3&lUhem4^sHlNduXQ<)XvDi?y&{&dT%q$e^A` zpfQ7jQqRz7^OES!QJ*mdFBwOF*j&g)1|IWfZQt5XUNw_4=dlJ_i2&u~I2mT_lj6X! z`8uRt=~aEZ+ONGwAi1#PO?a9gg2KZsyiA>6X)Tpsuet5{@QHYQLZ;;VVmz|vw}M4`GMMP~ zEj>1WHgbZ&FpLC6`>;7(H&g`zxBD&EVnQ%3wF~#aE$KU~Bi;yks@A-$_$KnYq@im5 z4a=`EP2ooxPYu+mYnPK@Xp9n|6W44{fg`dRk>9SeK>Lqy|*Dq1SL~Yl0=>yQuOJb2GA-_qJgd_TIx9Mv0??0OEc2hUEh$Em)GW$!wuIcw+ysiCe(I} zL9P{YgN(^wP~`e4CdA!6?n-cQcQdY{NpMw_DC|yV-8%c2cpxDUx_A_d%;|l{UxBJN zAyo=;(R*JkzN$(ix5hDOVNk({^P%wzPSy|vm>#JQO9RyX7jd7T2>eNorj8FaGxP4` z?tE#JCniPe4Os{rmzMGh#88fq=G0rSx{nS2S_8j(y0+(02W8@Ob48}xM{=vj^ZS3$ z-D1F}kdgUeIW3;kz*d5XgF{Ne7Rtj=LK%JPcN9s2wVod?!$7)FEuK>0bhPtoGj-Y; zKC4kNYc*+21bxROa~mH+LaK z5;-`i9cfekCZvx4riNID_c*5i^$Xl!aM55=v^7%K!09~^pT^$^8tl6m|Ts=Y7W^?GDm&As!_TcuSic z|Lb|*%xCY2^eE$q!M40q`uzV~_4J^R+! ze?GZ)0I}GMjy``X*8kiIU2^TtSwTVi!b&-Ra)*%4*+(ZvN6qwUNh!#eFAYqtAEDr; zd;-QDJOKc$p`t*~W^gD{{Bj8* zC&}^waf--lK4BnJZmAZ59xkSWjisYa%>YH|e4xJpxg8j-+ z1P7Lql-n-zV?P!q>yX&`T{mo=Y$7pbXp$vt$SGMXNcj7pv&=baCR^2Zb`_D9o=sK~sLrFI!4TDL zz~U`Ny-VP$94VKr&e`qEVdI#3^=K+3kh@CADrP|B*Yp!*;8z!lne|Dy%Re z*S=vE&ty%l%n#`-6ZMI@IOLeU^JY|MbTKE)%(9a2U!9eu9#tBjEr4!#Uyz-gvl+81 zuco@2S+}%0Kej5U$TWWVi)l6D4#P%@tGDI;giFB;R#wWw3dabK^4hbOem;$yEUV{b z`9~kJt8^0zDX--fdzCjdTOg1SD;I!>x~^b{p8;cS*VgS+KeZhHx?ySkoiI+KWKKT9 z-RF}r)DcF5w*JSwo&s(T?zEsRtRoacXxe+ZTS|Ufh2_d z#KLIpX4JthZYor7#jfo-za1hxYRPZtoe5g`?Cun+o|3XNyA(vwMy>7SpoNxeqx@?B z_2j-w68`>JTkgpC#Pba10%z_-eXn^oiV-a@8(r4GEq{UnP=ZORDEq9%#c+I@it>O* zx4EHjeP;8uA;94b^u?kx3%hfRp)F)0Zoxe4(+EM)Ubi)z$JpH(_%sBL4G^eJq&oMfV;ael;FxhgZUd;s&mffOMfHhkxPHGpw5k z52T~9b`lc%LH(-RPaJAK$5H!w5#N?V6C!e{T8Ho;t|}{PZq6r^)s{&^9BEjGwGxSk zj`faTQoneM-k6x(c5Xe|rn^rN7fbFz6#LVFreYVQa?~RbdkNrlz~w%;QMZeH{_7I> zT~c@%^NSN46O{94_Z;Y_Jg>}|9sZi>{jtC5lm=0s`e|qOscN!QW@$eC&22bSYM8i- zliz}qR?foiB72*$3`dxhtBX>>GJcPmK99_SZYzNqeZ_ipYFSq&gZ2`-KujKuJm=*? z0W>DBeG{W7S9k)i+ft3Z`3wwd1A}uHqzy6N=(g!>d7@+=o;>oXf3ea^b0D#jYGU&` z&NMl__kbC^Y1`n=dL1eh%GM)xhsWlN5#w;{zS*8jFL&{^T|{PlP@wMZRf)xi^xytK ze@=F8Un+j4)glEqiYEhW!$8hkJ-Ju4xgvCo_(jzZao)5X?Z0NcPT7L)>Ud&?k z8cg7x)DJ4=cjc3*^coTD84s6i>hj-LHpuP z^dZb@NV4S9jN6#&`p8JZ&JRZMy-1gZ>)s#Mmdr+HB|lYsG8W6Und-guo*hpZghCZV zF!NU~`Z*ilz9t>&q5sS@_HbiT?q<9^%)yB{r#GM*2Mh#PsY6PRe^Mhfe6O?l2TJ+vRk8IM&u@*^wJiPu5>Kl8socjvf=vbBfPu>qPIoeB3 zJM4|AEV7y9d-ROhMSkqN1GE&Rl=g=MsTwW%PS$sadrJD}toCAxX*z6P)-vQQb=+Xj zjQM$p9sZnAk{c+$Qu5-UN78<;J1no(KA5!{sk@anrfZ#dYpC?ACK>)(Kpi~*=H#+v|MMtT+AZh0|lk*sn3VZ zvR1jWq%V``YVl=iiM{7x&5P?yi`9hR5Vd7zgex94_yNfk2hKzIWdr4lCEQhlcDj?y zVe>5z6@yIkU~Qtspf)~Oo>X;mE(r089F!aR2Fz!ZI*RE5A=2>8H@Gc_omoTHU}&XN zQM&pF#3Fe5rWnZaj>t2c-OHuj69C+AzvhictsmjyN)zow-N6Y;oU)lYUXo6baqN91 zeRg@A8RISIVl`Zf9LBZ$)w8QnA7+%}qf-GFA#ksyv{KDjlqQArsOqLUO>oPN6icNuW@ zxl)Je{NjlfhqPq3&parl*!aLC2uB5;9UWc~xihwjhn7i@H3!|4t~c~^!@zg7s`N{o z|H6wC7m%H`*7pQt7ba_V7a6&_xeKS&Z$7B3SyZ2g8tWdo#0fvGM}uOpE#}D~D$7q0 z!CE@6L+@WU0))YGCCQ&?)~&#+@jdj|#QAbf#`Ph)l++WnRH|xc4{{x+Y2CM*j8Eht z2LN}#&9msfkT<6jag%`~J<5rA>L)Ky1xtW$k_7k{sjB{bH!CETXn9ig-|sHiEJP4* z;1(yYHfCu?0@zFAgRklx&SW%;AJPuFUfgy^)N+aIfStb7e?hzAjBemYp{nK0#(1$2 z9YJ56yD(FXwh403Uu=Dt7x_q2O^>**`dnpS>?@H)Ti_)+3S5V+68i6sw6ay-mt5>F zk=pxcGb4N%!`y{;$`HdmWN=NPM6M*dG40YfGwmVoD70riYgoZ#%S5|jYMI3OwaFFm zep(sNqg7dvO4pCDnAr<%j~o?dX!CXB zpp}AbtR^W%rIc9v82j9}R`q-Z8VoVrt}J#DV~?L`aZ|T#ds92iRC#qnHc4;$fdmQt z(blaIStj#ZVbY028=FIw`+SZX@--$y0&QcOe*L+xn6?u{=q0HP4eZL-L9Evte1uy} zIWJShrU~*_3?r0<&@rRWAg_&%=ov`ZTR@Kc{Lvhrb#L1Xdamh15J__Iv_&{-PB|V(k5Cmu2CyvQIwaQJRG|>q&CyuoY1$i^R7}# zBCMh8RT zab5>_2<%K0`lAtr^p<=xWDknJP7H;n`s-nBw)v7en$4vn`Zm|=c;ZJ;B?nUI-pyiDBalyzh=zjC z^*#f@6|(Qm1awb}xvc73CbGkT?_)1t_TdOhxAU^=$T&9cig+AI*^Z>?vh8<{5Oo zI#{I+fHrQMiAEgU1UgV*4vynBMi#^Kwb;^+X^VaOVOc176_?x>*h5kNp- z#<7_uMkatHQ59xB%D27~tiUkFkhD>rtqRMlm}%mwOy2##oAM@enW-XebG4#ot~<~F zXB%2Y_jRL!h*DpJwdfv(vAnzkuLf5=A}2vovLpfxyN%D^pE_NYxM0{8b?vTMtD{6+e7DAsWGap?T;m*_8a`B$Ksh#2T{%Ffi{X)=_ z?{m%rJL{G~w4?`1go_*FMUDXp2iLdZ|2D_AG)7PsK<8s?IF(fc00{7m#_Stnr40MsO)@!CMtX6enNShw1@Q;LZ zXL~`nV2D|l=z0iZi8Yw6DYE7r4@03@F4(o)cn8PPH@G{*qmdZ&5_6*_Go3!e$dx3C zR+|k3@15vzCp4583s+uYM1RU-kW>!DG?NyCaY7%6k<8--UxoV_>FJRr*}$h z>axq6&sD62-OY9V1dvYMqmWE~^R#T!vpLmq(dtNhVushy0GJ%#L*QXS+;18<$)>xj zu?3w4Ltz`_@Pq1ybNIn)y4ofB=A##R>FzOwNr+(5d0bA0tIiZFB#?}kY@np&NBNai z0+oCPyXwA>gJc2!x-sP}Re4$rgtJiwiy7JRjB-)aB2rR3<|sT^l&*x~X|5;YZ_i)M zV4$(%8?l0#38rXYdD$#ASL8{-#e{ZBTxKktLK6sT9iVhU)iX4 zmr@_`R3k&v4o?~;{BE`xnJepvz%um_%Nr&B_Ov&C7m&^I98>` z{BQt=WAhiUdJN6g?Rxt#+4uO4rcq0y0KcVjjzvLnChu+JPtj&j@Gx^!F4ZKz{?HUd zdEszGls@L0p?Ynmg)Q{5`p6IV(x{7(PU0QiT_Yd97b~C@H#jxQX{y#ZYBKW&dRiAc zFL4>gIelNv1Qfj$j0?1ZYL%L)N?do3e%dfBSCx!5AZnzAytdF`MjtFUhW{#q zh}kKGGc6@)d<|blGnY_XvrOeDOueIU$*8sn(Fuft@O|RXGW>vjLrt%Hr>Q}6fhc-* zbsqyWP}(gk^}T6cR2W;z@2qkE~qaI#PP`9Li&AY{HRE#SIWrtzty27&O)ouLw8x zEl?4K+$MwZm8(nUHHQU3uR+TfcUdlPF6K-<=8L?4fhe391a2x>YP)77d#nu?xQrJs z`G4@m*|WZvDG_{+F|TNmnXa`sY>#tF{2)PNoMXjng1&NiamIA4SA}r1|7&Q6i{PcB z?XmOoRojS;pKTBR@J-d)-h;soBE1fLq@7+NgBZ^YFIuzt;Nfyqw?Az}vFh-(a1Nkd zWRP~MQNef+iwo!jRa{~+K7!m9eV$p_m#rR4=`A=pffI$Os>j>K8fi**cVB*8t&Cto zx?bMpqr=v|kmsrzop%Uv87@Z*LV1~kQUvViFS!i4328AK4M{?mx!^?}Uwv*p0yhLQ zSO){~cRegT1?n|OVq%F5NrsozA^)CBh=JOfUb`U#>M(!^tV>lB5F{T_wp zT=jo~*JQ}a1zSMvD==n6223ss7&4ou2nf)sU^~Hcp6u63Fepk$iF9EhZM5tCd>xU1 z{a^k;)$pjUu2rj!Q@rVLRP(f;qlt${<;m-oO_R>hdd&s}Vgkati zovgpK<@xQUGR5C#bngK1)82dSOdCjYUxv_Z^bB`TYf{A6WhgYa z4`!2;EnyNFIJmli9PozJdQj!kAhi!(9Gkk1jEryeBN=)gr*DxOX-F)2=j~91=YRO! zGDUvN2aw=kgYsYB2n!rH$OkHHGKVe2q`<%ul~AL--FZBbQnQEG6w9sjGa8K5kl&5~ z9MvU*??~kFi*#@*z3Ecv} zshoL=448Z^%b&7VHWM2Y7^Y}g!CP#+TYm{Ak%v?~b>}w*KP!Fa>x3+DOnI2zP^irBUUE}e+^wkX)JyyQE`x8%E_ zRJ|L0j1$O4Fys025t2Q8xL=ouLZ`fZCDBxlz9C&<4==L46cXPd(vSX_LptcFlhh zPvmf0E&K*TNw{mDceUgw`SGV=5pnznal_nMi?baCnF>m!bq9i&UqYFD%(lzwl!Gmo-|INZ&xA? zjN6me(>ADc=dmtFb1IO*((=L*6Ys(Z6<#<_wTN_8JAHuL&|F7Hg`D5=4+DTI+E*@| zcT%%7+{0PjlGiILYUyHD3)16@HXi-T6G%jM>*%5vVU9-6Ef^trlM!8+QW7B)X< zM>rq{(#e@wNheN$do3crw{@1hZpEEnL?lM-)z@duP-ZFhcof)}GqZ@}I`MO3N{p=b7~sw^mG$ zo+Ku~Jt4$^U4!3VwDnI@G&qV7HuT`IK4_1kd7yfwsD#s}i>Y#Qml42+JJ=}d-_|XG z)lnCp(^bZ~lP9GqS1=#EQK0F@2aUVb{OamKtudAAPW4)!8Y=Prz#v*n8NICfl`0Vm@6#v1!l(D8xbAv7x9el}f!THf zYh5?GbUdn90IQ2q0riHz@_-7U1#o@`*;Ztpes;9pY!0e8=bzjX_31KMBXb)8LIsoM zGr~mUx9<8L$i{;bG=g`pdHX}VD~IBc2!V@Tn5hgvRV5DvTdNcNR%9qo-RZF)tI6S1 zf6Xt*o)SxIj!j-Fd~pT6NPWDuLfcc)vAv0@dq?i69H<;R+C-U&4%qpJ=A+XKUH;9t zdHy~~+?*r2dz_R-(xV2|{2-URaCji9ehC1KQ=qdN6`XFg>DGjZRtdwDF>ZOq)DxlO zCq}?)lheMcA+09DA`a5!FZ<66PMMYPJUcK;eHUlW&$~ZuJ47wk=j~D?Pu~2e=Lj7& z5V2H_Dy>MMAA|;qmq1B-KG{6VH*1x%Rc;%fyz3z9pBbbpTQ(VQwg^I_xt8C46FLim zOv6Cx!wymp!FVFS_=lOT#4iBIA%zzvzziNK-c~HHeC>id-!Hfb(`HBC?3@V(qDbBE z<&=`qY_-!nu}4`+aFwsZz<;rbZU(egy+}W4I0ECT5MVW7DW7uWdwA9*i!`h7n}*)A z>%0jW$4%!@rKX*!BHNfMu6Qcxu%_p|bTbR|lz@05l-{4NILBzs0myhc7&BsI5IC$@ zGW)Hq>VVYygHLad%!wmG+=<%1iT%&ZAQrofLK<{+W|^syg9IFWsdDn5JdhP{#LEwx)w!6dsVT*DXLjm@v;JMh;D_gOI+=Dkq<&utSkr`QQ z3MAYu)o$-+s;dRqSwr3@&u{ad?ouM)#z*;;@$etgCde%`dCm{MSPY>-&^2pQp+h~- zA;qH#w{63Jga;ohUbgz``-v9 z?0l4^1A3V{h9K`#|L6GVzX`0%)qmcUpjbK=I=meFkZIpr%-Y6Py%S}joN-?BO*ki+ z80V(w`n)0YZ$>sXgtw4+a}numAT|l*$%`Y+UT6(p4y;Wf<=5?v%82tJ*OKh5AU`Pl zK(?+k$LBZj0D?%3evBiuLUQU5ky+;rYZIQ}cii~~)T&+N1zr{VdEI~>?nkL zogh)CC904>ebewDp|1q~O$^|-V;DtyPURjU1-oas27o;9zr$c*WsSHJyuhievb-E( zG`eZkap*5|$ZI-%605im0flZ6_2J$>yM(we&a-6IUZ37ezt>nHIUKNnC~W>of` zNQiaBg03(^`hy?UhIjt*-`GiY0*R-WmAj3@`zY^3j)e!+7s&tXX_*3KCRJdNSkOpc zl1=p%N};y>G;eaaZ_xYs$;pFQPy&_{eNHtb^L4s&$G1el&+_uSsFBq8nG@j73{G&n zb=iLhupvg1YiOm&oqE&%e9}h*32=-5U@`jhwK`hxh3e0uX=iSXoMdQ6xb(0ecmL}{ zDuORKy$zB4N19G|BO0u@ZE6GS|3lncM^&}8?Zbc@MM@9^q(eZuyG5kC8>CCRTT)W# z?(XiClJ4&Al+NGU=y{Ikyzle=_l<81#vW@g)?V|T_q=<~dCkAt{3jOxg(?PsZ_Z&U z692HQzr4#|bg%+&VEbZzCjXVl|7A_&KU=W?AKM#?IQchK|GH`bg5Md60-HZJ2>sRi zuef0e*sD{)fK&9(%FZ7YISAm7P&EE?G%-LSa-EnvITqq6$4ZGN2u6QTP9xC*oPV+* zO(*>yzIgJYUk)dJ@oe(8OK`X32M20QJQpxBX{4@t+@p-DOKIAWhkvYX7y4 zbmR+2a6_s{@*gyVyM~LhHI?dhx|KwT5F9Kcc2lM|>v8Cv{ORtf+@xHdT8r@4J|rY1pm2a#u-Rey4>|Kp zL632q9@}U+{WYj9?er5N<+AMrsKw}a5UnBkZS8m`Kx_xV_r~lQa0p}$f{IkVB zNcsO}ku|WJOI*CLG5qGYIH~0mS_N{C&)y zb>}}`0HC2hY9`dK?w3Dr|D~Rf_uRn1ZahvHVt*WW^N&_wzyV9@Nrv)c>guuhd~Y_I zsJ7Pc?vsWX%&#ZiQI|Hec(`9}ct1)!?<~OH>i*FBUuNGzb?IvJf#k_`Xn3!|+?!~z zdWp`^dbI_&U9Rez<)^=T^p4LXIX?9$LYW9GcECR0uh#5x$(*~79?THkQj6%+$9ngZ)x2I|YyD>EK6zb#Y`=zwoht`{X5-s|vN-osNi` zNUvX$)!P+ENhH3+sapOW@23)ifBhG59exC~&@OV1WOC(WCUPmK)e*oX(Uo6Z94${V z$!69j@S9!%Fui|qh4(E`$3a}r9-MjXvJ1Jz@~sbASG|+BGdqSO9h*8x=%e%?*wd?*AFyfU>Kz`3)$zUTsL?wR}J&w z2}lJA!=?KNnu^2u2@Aywhbn$2etZ!0b(Zb7prE2pcXM-%^}GW~6fgLwqJlgOps8b- zhstqAumGkGeFlKxR3TRAp;DlX!u?+ z_>IV~+aQ@HqO+5v_H5u;Mqb@a1rD&GWvQ-ZMF39Ce9trLcJ35{P~E!$f`^6wo(O`M z1NQQIQ=fx6G8LJ(MG{<~C;jWl4p4E+eFA@(Sr@LbghUwn{bi81&+EyVe8oJ@n>pDi zxWH%^0Wdxs!sFQc!yH*NyvJqi{iQj)DENiX@P5||UGR@ib`O6XCcB9h)#_&zTV^jXS-M-uV7Ur*SKIDWc0o+B^ZyNg4EBxk3)EVGl%=4O&tk&*k%f=@r-Pk~(S zHoJj=tdd09HIfA0Se4DKT zlI`q`k#5U02t61Xn!>;8f)#+ylHYlO_*dh}Z4eWITeAL71@^_E_1q#Wv z`sq0|e?4oc49Fi9*6<^=3Q*~I1-LNJ>$R9a5A69x!0*?!fEOh#`E`vUA3gIF{vv)) z8|;zLRvK30k-+k}T;f(Z1)s3Qs^UU4^n$d>Bd#~KVag+p<`IM9Ryu{OVG1w1BLRp9 zyaE5=chqLE4dd``X_LXuF)jY<-s;lsPkyU;O7JwoQAP{V7Y@$p#K{|{6>2r|`oFm}fCx~wx^&v30RHW@|GxVFX=~Hp1c0xWz_ZG@>lqBc z<>hZr@YfYMud7dckM&<42!(y`RKfC_mG~{t#+bVqIazWmgda1kZ5k96Mu*1Ub};FU zxW+vf>I} z#K`0b+3_8`hDF>q_Dtc$komQpF=;EK$<##+i&azL2qR@H<9cjL4gx`PiQeP9^1Ax+ z6=xv@wHL2rhD0~A#J^?au+E=Npdv=R>d7W$?#)|qorx34NNegG5f7buvyHbXTeQoj zkY$fWfOdM>4-*OlG#wVm5aaM5Uln7#G9tZP7% ztxQY~V)|StSUk$nm8P`NYI{y|Vr@j%2<+)9%6VmFy%NY(C|=2v+AwU-8Ts2t;AZ8U zn59+~$zkbKq9&;kbP&&GHZ{pi%IoS13I`2F;Lc|mYKCXZzKgQs5}WKy{7f^Un<%L_ zPG)9il2MwwphB#zjU4vPF5TO&_FnSr_VZvy1!r+>XjRgCLE;{d0*?8e&dI^^jHZCPit95h*ojZGSmR{z*qqja=sL7;Dt z)y%%g2_*k<*O6;>qS4CErXsyoakqRTo7Qx1>*PntS|^6PV_!Ho;#~ILN07B~a4!w^ z9Z&fK*VSb^unsiJF}F51r4km6hi66N^Gah59m3vo`QqC2fjeFsNoVS(#vZb!5-Py1%=* zHu28z7f8w@X8Z~ro$#a9NWw-;D)#nc-Pd=dl-|XtiX!$>VI@Ux`VNMh$aYO>PDCcp zToe^>mMbc5its^4yVpOsI{w^(bu_upQ|pvh)46m9t43&$=5)U?u*;4T>8{so7Mi*?w_FkqlgWvpH4Oc71a} zu6W>*K6s3Kp~&UEU@`DmPxGQw(%Vhab9s+D&6>Z;8cfu2`Q8oeM6m;#%EvedqBnlD zwwa48=dM$UQ{WffoUlQIV8S2$s9~hXMYvj(vC7=9AkD)bBsb%$dEnZm4WYPCf*Tn@ zMn3MFzB;UKZoJKwaOi7iQ=ETBe%3`a!#bfhOf{c9lY36JKf=5#=x9o1+3*B_H>(Vs zQ`Slb*#wJbXSoz*%;&k>wh)$&Q8yo)=1wX)UM$ z4AXOuTn6ztPWGUvgt<@M-%Q;HS*a>J!o5CCKxgh&y5rMuHcga;PdD93+BK2miMfmw zqQh&9>tw)BoX{AUK+g5m9K1w-P2nTIsT+@m;dXq4-tU-NsAI++Z6fS<>{UQfD393O zoZl~-IatEeG-~pBF5a>BbUP+(-G@4?QUxorr#;SVX6C+&1MSC&k!1Dd%eLeLmwuOhwk}TthW{_1V5_W$HlF zcl5z-H?dzkV_U(HajeM{`Hx7h7WgGAeYkCr!s<_ZD0B`cHIg&jJ;Put7Lx2r(WDNj z7UwTX=Uv%mdJ%q73@Ux@tAiu)ai-T-(tw$w$_ii3o6#x@Z328Jh{+K0Yb6PaJx_E6wqB91b-x^Fpds z|0bF(uXO?Q;%ihWN4cZg?ibA6@lu`$=jtY1m;@TG%JgwyDY8c8>@133b3r$XN$Lx* zYG6TCR-$9uBQ>}-~N!$Qw93D7eiSVXXIx7{NS@46RWVLzWx4-DCP^JNAO zX*>|=nf`+x+L#6b4u{QTB^?C-9r7v?T3@zg%vSWZL3iZ-C-Rp!h@(#GI-2V;wW*~F zA=o!k=wpg}57gc0Y>kVGgWcP2Y94pPA~AxeCgTLVe^roGUP|TCC?rgq8J;J5>0?2S zRt3w&1z-k~f-@9}l)7V%|I2P2ptC@6*`cz5q6M zp_5j>9#PFX#@Vk?*sWdEWjS9&q!|uNVvHT&lmEmTuTzty)(15@;Y+?$?6M!qmD#nI zuSQM7dQwU4+C8sw)LaR0D7W(GCb+zj!UY=Zz4k@8{ibHS{qgnOgGQ)0U!&IWz0d)p zP?osMG*V2JIXW^j1t2wLpx*uBTAA~SxW3MVVY2`|ae#Tsb+)ffCfZ4DvjZl!phk^{ zf-11yQ1&^-q;MsNOKM?&9^Ftjco7{Li`S!jugLZgN!3msNR#xV4nv+S>@X4+e@%&( zJe6T$pHd)IG$vykgTqrcU21AKUr?mzHxPx2{SbcFu38?7?xmsth^RJT%?2}aE8gE{?#xm%&e$$&EuWq_&i<^t11uwAz*2qZJ8$Z+S zpZwgS7zq2+`9!MTAvBrx{?$-B9gUEfO&F=)$k}>7)ArL5*hX2J=zC>*p=@ur^XQpO zm(xp9U(1_QnH4H^$8nB?uDvz5-0>Z-~m)HQ|*wq-5G2)GC zappk{g=^jRSrSf|_^x_x!5Da$xjqIDwlxKtT>&1mY~Mx0dK`#fYT0e&tZSJ__Zzb& z`eGl~5oyA{Aw>Yt+b14A4q>e(kI9Hl;l&!U%qTi$?Bz4%c1vKK6$ozZZA*l_!hC4< zfK98mxjbR3WJE7|Oz=T|9wrZjFG4EIe!)W`p-XWD$Ya9H5@k0G871kNs7CsJzZaSu zk$1Re&43Qwe__CgoU3|{=f|j+{JTbR%Y3Tk(F{_TI-rIc3$nyQv zi7^7gnW~=c+8Mn&wE@98W=X`lX`>za94Z7kVgbxHs znR_O}+9p(~u9EhxEs_hV!t;DpMq7?!bXT1;2Q>*;PBAwvva2xnP^c8E?^vU4u@*RA zC`!21KRi*#`+1{5euuNxX_v`k5)X987SRPfWBN`#MOd`nwuw=&YpAk!Yr{(2wKjpX zQKqNoXJ*=t_j~UyW5@XNHw4`LHzMefEOnXDmrVAZUr-aL-!fHRkLi+6+K4)~J4(1X zJZ9|;z|lrCSb3+97#vI0P#1dYsc;hcU+$eA2)hFg3HV>$U~CjZjSd=f*zNx$UJR$> zm(fv8-uO=YRU!2RFTVowyz8`xe?LF2w~IV5ez1^q{v*3GAe#9pKizRIQ)&w&+;aCh zHEdiGspWn1XmZ#o?!=(x$PdrwUq>6!;N7Y)m17%ko|m)X^`_sMf1QHS!5x`L59v04 z(O_=2aI4daQpP zc?0>YJ;1ICJnduQ%n6|F%F zMs?@z{OmUy@7AL7cQxNwuVa@)UEr<~kwcmW@*TEW0+*($?^U(yoWvxf6PeqBEX4(Q zuBG*@l&XDeNrBM5`zDL2vzSEeT3VHPft@;ys%?G{#;t6(Q2iJ6v2$ML^}HQIuUw~P zz0EgW%`nvq1J-3CN00OBE#ZI>;>s1)d`x~I2`pR(EyD^L9gc2PHAng{8b8~nH6`GT z)**i5fluBXUdq8NF4;-Y^Q*d>;=!}1x26sr=Of`f9bEQ(?m$p z>7Go$7?)Noh9xibOXshA=&Hf%9<*yBpcvRQ`jllC#Z$gm!(UhHX_scl`JezNra62^ zIBT4}>E3;(hF*dP#M9+6U962=^MMb^jXxo*^Vt{fO%EF{4h|}n=gi=zyg#alv3S44 z_Xdd}c=LP(UB0Cwh=726M|vtHM)TA(DA=;N{@iJD*7>l}#Q+p$9PW7Tb|DsMjza5HKDsO3zzZxfDJFHDvrHDbQ0t-`rZ+b}H0jd>;D% z#PgR642YS85tc|$qD0xllD+p5gJ`Y-#SsN~(iF@EP0SZh6DLpoy{NE&*DvrQ(s-EnKsUBIU=6z4g^L%9?>ZOyqL%3+~yzC&6J=Se%)nsgj0Ur(s z+p}e9iI$1+RNDClqNC$VhS$T7C zV169PvloSRM4BIa=c5jPlCr`2SB%Mhw!Jvh^Jd)xCc9VktYg0R%1Y1{yedG(O}Cf2+x$VxC+FLa*Agw-^s4w< z@b$H$tvM&ZBk{sr;j!K(2Zb>2iO{&be2x-?@iR22U!=nDH&hfGq ziG8WwbodZZz+co$56c;uj~IZ6(H?-}*q^?=^L;Y5z-1Uk?YI)i(#$$0rJ z_7U)~YE(1wrP@3>r@gSfi1Jw(x^Sm>iFLADt5ZyTuErv-L46f+3HiC{s4kyDaVuWI zwR$qPAdeC42_zhh%dr3s*SQj1p{gR!3MI3a`NCX=bK|u!^t|(-w$qX)na%x`<=QeN z(V0I4ot7MEj*+|f6pkwChE+gtV3I}eFts(@u)BfOX#T@h+gw@CprK4>rqI{7=>l>x z_ut;Viy();-9S?#wHDUKe$-MAVvtrsl{Z@FX9yTpkf+-*imo-ns0u94y(87J$wiz| zM51Y9hOvq>--ZLIig9Cfoa4SwCI|(zK>b}^_h~0)P<21y^b+a#>~<-23LVEn*DhXd+-uQgxyv>bj^B_8#QCP|@6Wnhv6VNlb} zC&;U-Ts(iHelK_sCBf1%!C+)cG8S@_*%)mhUR(F=%a760dj0lJ-wqlp_i2MhUlKOD z_RA^X6~2GBqKJHtic%>>!Z6-qOE2)u^_L8$6Kgdtz6n)$()R>VZcj@>i;cK<#;6)o zZ&Cap{5~m#Kcw!ou;?Z7(ezz;QO|iZzWESi51~%-PRphI6!)XKv5~gduDz`w3Y5rA zU#__cdCIu6h7`ix&$URKINFmqRVc=$q3SdH;hRXF;i4$QHhOH5wYnf#;JEBe#Wq$m zN@~VrMI*xvTNdnxhpn^>V*1t6?4!@CNQBtgH!m=6PHdNUQz=a;n@A;biJbL?=q+E0 zs-ik4bfW?Iut+5=p2=>;S>yTE`;AoR@%W&i*IcA}YWirN6>E`smWk?-{R(=PDB78ctiU;LL_&&lBJ@ zt3|$VeI0E`LR4^QCcmG(&ep#v=fv={y;+Pa`J)TJoXHk6HsM<6>uf3CBcW)tCPWvm zi1pE(#CBh2qpNuTMla4}23K-Fmq@gi5NoJ)1}CbW*EekfaZ@s=bqt-HFRDiF5T1 z?OCi2Ajm1CSQ=n{MA*8li zRzwo_kSdX-`7YQ^Pdh@8GOM9+$ucRBY0>P&(+^5H2AG>uXJ~fxg(1)w7DrXq9tS{w zK|c!@1#QlrH)fgd1kK)h5D0}qjA$6pzx9NLVZ{LU-+X^dO6r{@`C`mSL28IBXmGvV zGCMeur6{N6^4~$pf^2g>#<8xCl`F{rQ`!+80ul>ZOXqaI+F*!n zv;A9iG`s%2y1m|Tq~bxQBzNxkSc-&8k%Ii0luJWZcK*g27(Oi!)VmPjR_s=MN33T+7c?rPU!+EZx=bD6!*3U(8g@q{ z&reZ@NyhO;fLIY0^9I;r%8^8pnN3gn(dEsJRe3{oq=^+r2ak%V>DPG{vsXS&&=@l# zu)7&!evPS_mepsg6m{e7Zeh1$NygX5Jr@p|Oq(kbW~sGXzL*=}YYIr37c6xa(wF|eM!8`ug|zq3nH8-zTQ9cRb2wN$?^F54A!nY|$ojO4`>^Ktq2zq_XLx<=<@S3H+4%(+sq9q_JM zU)#^TkBl%*@r9TaeggacmtW8!gc#hWTW-?@f;JNJS5|!D<7?=Q*84QTH!9m)#pC!H9%r+2Nodjh74|wAeCuB7ZE}K1_hOr1 zZLq+;F7kvIS4=MxXZ!Z=I?I2+E~(QpGjFL$xBP={;3 z!1VzDY7%OVo6xcTZLznmotaN#fAk6s2eb#DvxoCNm*Z{u0`rFC`t|q(Kj)hdkdP)Q z{vip1{%eAGt(ck$lWlE zpAz&3=djKjgz8lSbbrb`N!#vVo0KkzZWiO7=`7nab*i*!G`+U^mP5Pn{zurvJ+)Kw z$3Rm=7PJpy3f)Ryi!3EJq$g)ZROs>|16@?BHObO^mc!-BP_<1rCzV7a{Nwve8^m7$ z&L9GD>j|VFaf##`cxDCtJ{bB5Xu#gZ@!GVjjnCbFk0#^Z03kcioW5`ijc7=Z!&-W= zY9qQ!U~EPcQF?rgn7$>%WHOD9f}lo8s;sO*dG8C$_)(go>+a`Km&msPo#~j(pG!wb z^HqmTk#w9BED;)LzS}V7yRYpB0+Q25f`ePL2F~6u8|1N(cJ*Z;5k38hFUj+9K5`xJ z3Xe=6&+eN))g7=ME7IkYLzalXq$fpEchYT1^zO^0K?$Ab$X8>dS;~ipG?SVB;mRT& zZ5H03ZXWch5*7LS)-@SZk3zGlJWP}t$zbyk6lhc`B#}dj^MvbEcX_j-$~e9(a3F}~!0|MCu6k>C zhAWTlLahGlRC--OzQ(wL*wXreuq7q|?dfVZG%0dd^nDYzBF#wSgWZkn>7pZxOx)4f zM6@<$R%Wb6XrJO*du1{rx9#&tPFsGS+m@l5P82k?K$(Lv}eC zGX_S7$1^3E=Vm9x)aQ-ao-Duj>Euk!)>Ri!wZBUanys3*AKu%pmR31oqi18;!K@BE zw$E7$r>7p>Pv4P(*|Q{Bu#&Ra1yE;1l7*CVDAm>FGa}c9dl|kqH1s*MGSW_o3uGRW zMQ5#f%gWP>0?0xVQ9FLezf8%107R$JW}U_m8sKPQJokOnZRSPwJ~&l& z?pFx%?TCO_Z`)l*6^09MG``{FXJz{oEgYLCdfF=pkrUqtz9gliM0*a;lfNJw?7Bk} zVv5wne1|&CKGNwIX-q?X&}=tpJ)*5Y`BTF2^IW@oXHRf!c7D)k!9{@h#c1Bzs*T11 z?ux;%so26|-hH`M6m5mI*eBWbx}Z@**E>k|5oscAsZU4o5(WW1k3DZJ==2_47 z2vHdo`%rdQ683k^`}GJlnqBeUY=~)Jj#I0oRt+UBz!l$h&ARU-lYwR8;JB-}xz-;|K_>`74bGkIkzK z_M7?TSu0MVs-?=d@GT^&@lUCSjAwY3IzJ3Z6}W|p8zq;7iq^QNe% z&JC{i;%~6d%Fa%4=w zZqK#**xamXs#ee^Z(iv<@OFMQpg`}14N9LB$U!>C#uL|;Tz7_c7bFLIB)9^n4!GO4 z7kH`Fi$(N#N1`33TYkS0h=C3u0GcY#p1D3mG#gDi1Tea>NFD_yu%-_wF| zEG$$7ztOH8${L~JIe!*mR)H)UHSE^L{WV@;KpG-K2w?ZZ)G&JXU*RzRtWvAH?EYxH zi)*u8zGBh&{L;~3v|rg^q+0Zl;CzKC+k(DD&_JC4ezba|U`Wn7E2uYSF^f9g9&JwK z=X)13Ssxwq;sG^2^S=6NT~3oiCRPQY_!!E>K*Pkr*wy2GRdIiLvvu7!`a|)k+HU!$ zP++M4sF|>3JA-l>Pv>W#_|}FHL4T*P1n~yy*@4BXj zmw;h;6*g&P)oE_4&HiB~hU;@Q-i0sBO{xTvsc})`z}hmwLlAvtcrLfS{ZDS5T`c>& zC}9cKL{|MurohIca3cY9akeWJbFWG4w``*$|gt4iqzE;GD-$PAl4=ud3y$V8fTG}siN<9xUqa>LDvVHD0; z!Z&Uf9>?lWV^WF zp@cLHP%f1&VjOr5MJ=JFABM-}E*`KGQK;^9Iw)zf?fD{K-j(t#kVQqaFb*;GrZe6U zfF9j51f(KyiL*TgmHKu1@T#7{vr$FOeo_f4rzbL)a438mu<||ijQsg-y8(LUi_6Z;)0|Y-7AC|10GR7hOIu(-BBFpf zJVi5RIhkfLl7Vo&n1>1opr^T-ftgLkdQ2pNT=MZR2wd8Gi)jP=wg%HPykjst_!Z)z z=U;s)l1RzQijJb1eP{cD`(ldr?(QP+ElFqBxb%|CFO~T9iO37M&Sy0+Qjmb_?6~YzeSfflwjkY?)slWH6R{ZI1jVESL z(Qd&`^(oiwkz6K**`F*#Dv1H?JsMFP2)HXq)Z)8S9Na#{C^%`o;SNJ~_-rR5WY z4oJZE{D;WDFItd+{Au4`5t<#~2?j9|;Y%D`2VHhE&Z(k6hP!=FyOo&H4K!wvMK=Y? z%ny$79MG%3f81j9{PI%i!7r?-X+m?<2$J9O53)SzfM~`^?2`61VE;H_#Kez?-AYp( zwJc&z$enYlGLwI)s>hDXjcqDB=luLzwvl+5b_%-h@h`tO{usp4%;{Q<)0TXErROn0 ziJ>$G!i!`&?sy%=Kbip&1#mTx2;v!AC58I8u}CAW`<$?07$sPv)&HNBFNQ6i1sTWXKN+((4w`j*Zi!?j=M{15@N43`NdEGjdzM z*oYa%Des)YpAQ4+TX|;)qoIn$OMxp;C}UsI-TaD+4-QiqSFC)Dy@!~-k&cCf|9J)K zsPDB}-qt773j+qa-nYp}0mlfLj1O;BUa|#Wa@)3lS7T*SkX0C3HzoNMY2aWL{tbhm6xS>ovto1jKVG*HH;@hy5?k z&c0w9#i-vrdsd%YnEt-Xz(W(;(b=y@H;UP}qggvnRg!-D0}etQ=q= zV}MrMAyt*eO4Uxs=$cVF`F=LdsW#Q= zG0a@oZzTrwpQ;T{j)GhfVaN7`b+T06Y$Y|==ad$!Gf_1+_TW3O9d$*# zfnPR3U*x-A=}jSu04j>d20I*11xqO6wkO`piYf~2>q9){#a{#9ZJif5Okvy`mKeY_ zCvA7vb(p)`qh%n+vw3K}08g#h>}Hg|9U4{S0UhGgYQxyr$xHDa$IGdmr|$G+Cmay~ zQ^I*`I>-?UF7N7J+^Sc$zb17RKhWSKuWbaW&sSDhFS?EmRrGE}?c;R3=b1&f)%obn z=Q^{-1IvH*Cf&LwJ$D_#Z2FiqT&Hhe?Q6O3%EzcUj=8MW>zmo!np^%Jo)dBec$@C? zIEGzZkpLb6lJX2``5Uf%wh~{bn>hJVm07sZo|EJWE=r}UwPr*eS{%%s?C&epZ!eA* z1kWPIRAx?ysnXhOG>7M=FS>QFeYp-2skQx!ax?@pUnNTm8pOJpOgroRCu$AL(>FtTaA%KpYt)B(va#n!hra{^nrcy$I(-_c=xW4b3smV1A5z_?&3Hta zckm1ujAABK@~gD-kAmh!WXp@P$YK?Z5K8O%kb^uM8^vuhPPw!r+@^Onb zUI|3RheK(#q2YrVTx(P_*j`}s-_i>9@Jr}0JK(6tg9>Kvl}5uLdU3k>?)mIT$8ZyF zo2@62eudr7B}T5~meCoB-wspb_LuYb!4ZFivyq<{KMgp_yEEUus8UMu?Qp+XZ#4O) zJwp-@WHo&5`RRIoz<`ooEYvDrTAab|@aTzW{6$lNQOt$~uCk}4+>mtP$wveJ^Bpdl zAqwkad;hPT>7PFf<7+*IJa6YzJJrl9nm6n;+(n#@zKH2*o2cUm8Z>?er-4L(S+=(_ zjE&2#!69-sqn0^4W>V%F|y9J z)di!g(6*hVuFGr63SX&=w~u9%+ggUi>Ug*}96&z9>X}}xk#ac1gcC?r`O*3LiC+?- z-VeluFs|LUcUiKAKXjM%uu0xUR>1JPS32;XdL8k)cV#G?H`#M?-H~x!sip3s!orGW z@;}4#U3s1fmr4AeeDP>%Mio_c2~@;b>U8*RaI~xyK<0F+C@{mW&YR0S;ePd{((02v*1Fcuu!4_cEFM z#t1ZVV=Uck7*SihuzT(xGj zqN%mj%OKd4QNFSn_H4y)=iAb}4-C9N50MHnJ*VGFTJ#j<+EXlBHaRb%=%p4L71mAU z0GNIf8luvNZcf|3umFm=xSseoN}}W5ywFZ@^EpAp>u+wMi#EY>ORLDx%n#|_B_>TM z3X6YP0x7F3L#f8k=1#faO5jNS^iCxK+e&1^yz6$zt4(x|=hE9vxy^Z?uO2Ay!Slyc zxZr&DqPHmqX7bsI&K&%rw9;*aYj1!0AvB6^aAi9GAmD=7?V)kIq+?sPQXdgCYOeTH z?Ccl30hF!N$J)z5nzjQGyx&}`IwN*Fil2m}2lJ>?$bXx9n;csQyD&;mZU~I4m>iUU zJCAfR=KumYByC1wK_2 z-3yrFv$@jAJowQbuDKtPq93vWf$W* z!kqmMmG$UFLz0Ua#dgnDGs3I&6En?s7T6lPfVeXj9s$Wh9;#G2aQhPaA$4_wN1RUj zeWv=xIBLuU==0==^CU8Z)P{xGeuwJAS zZ3+vls=R;|8znxz86UB?@wKP&$|8of3Nq3=X%n!lD28g-Uv=yjB>(xjt0}gxdQf%^p~X>+F3H7>MjZ|MaPQTt*MIOq=}W^JkgF zOxu*pu{#8>f|WMkc0?BqIW(o&i~y=W}6*&h6)p z)0T$7moH*k0&0l;=meZoyYk8vK`}jaQB^m7Pq%?JaR{TVp!Qd}q$7}Dq8|*J zqEHc?ziXU#d1oop_ZO5harIY-wueSV$#-`N$0Mpgq~529G!LuMj1#6KDLEZR*p2tv zb)!yBC-@;^^Q#&sEQ~owp{uG^VPN7fL4jVbx)V1rhQ2D*9e2N&Ua__o@|Fto{i&s+ zu0KO=Y}8*4+kcWrV|d}o6lap?cw+w@hT=hO6r1&FYIHUyNLJH*7DIu9y)yU8gFM7a z=DU&Lvl!)@of~T=6e+8>&)tNygr6h!4LMHx`Sn-m#wqL6I~xlahPalMVqZ)ti5? zwq|56$&u_bw{B^=pBYGX&NfElhO{d~oHqE?Jg~y&1Q97h<3-X{m~lbtTqg z@%9$U3k9EDRNA+KRneh#_UB-1M0n7(^PW7bv`W1YK5zXJ% zm5+AZ;P6Ns4F@7myF~^P3H>@FWRj*4X0DqsJ9XACjFiv_d8%V{~JOg!8?thN(+Dg z3yL*d0U%Dn+$}@oda$UUE2*VqpGEqfk=Yz>2Ri{2+SzH2Dm)AmQ@Q0Eoa;F{dGrvg z6n|VMz5N)yD;j%gZIM!$K<Sf^eH@0c)jL0mSEJ*ILp$3-8 zoiPGSW4u2z!^DVd#hkBkQ(|#h_aK02^7VU`n|6&ge#Kg<_MzuWhg(6fzdDa;MMqMq zTTOUqhMGm<=K2FHzs-wv9JeS~(X|KeZ%G`&?c4)s3zrKTtum$ayHsW;?_-*{k-uZ8 z3poG8O^>S+|8N$|ynRu(_BpEaB}-LG<3ZweXCnD4!guBJ^f14&bD(iXeAsiw43O`<9i9OxKtr$*x>w%%mw~~Ns`mG^#9#TnTmj+GH zW%vY7(K-{|$uAkBeY2P2x*$w)fbdk>#fUO0(u0gs+_}q%v^gt8(iawN(*m{+owUrz zs}LJ-Xp<)r0@F$PFtpz+;}+^Bp14XD4PzN7bBOvo0hhJvjiG?VsY$b#?f8fdB?Xh_ z*J;wp=8DH%W)Tz}=XO_I0b}pmn}N^>hVol?QA^`cJ1Sh)M$P)gxuZrOk2IWSeUz_h zs_v~>(flWtVK9m&#Vi#AKn#10O}h`;A5 zAbtjHGjNY&-`Naw3|_Od(&3`Y9TV zeZL$s%HR-rNMmAnm7WkmqFYlFU-lOKB?=FTS%T@&3G?vEHtQflfEKCQCnfsw80EPX zc*?=pc=rjc#SF2BfHEF;FtBI<0=XhjXK@Q3X(us5Gb#)vkF zDkpFXJGr4r-=Ib(98}3j5>@z!E>s+=ONIL7^hTj9xPtjv$4W8(<*^fMm%)j#Edq** zVHj?f2M{DL9nhWHS@T9vDws?;+dgxePE-b!(9U4FINz{SG%UbONN{il3;BHXq zOMTPjB{i>Lj<#Q8zJOb5WDBS2{({D+pdu%_*MIA7j6uS85K}Arr|84SfPrF0zDL3B4lYqH2z+VxprT}Oe zzsK70A6Ch-@!MhU&p3d5cRJ}s>HA}dVk$2pT;!o2O4CSYgtX`_AZ+HAfeQO&vM@GU zz>{h7HkqFMdf(PzTtkJ*fOhp+!cI0|Xbfs9^bsg*{j$`ZP**Qks^QWMt&08m=5xSc z^s05?GuXj&?X#mzvR4A^Ys-trg@(TjHECqn!z>eAD+0~RKCjjhyP|UgUJNvQ!*qT_ z>G+O)kcCqt4oigMg)b}_wbOC(q{_f!-Po}h9W=az7Ka|J3+={Br>Rl-jNLSfC%oB* z=ve5<{X-1VE`CJ$iI^^dJs9yp{geXz0pY>qIeMV8`KIo)U7rOjCMA4q+@VN8;Mhqg zkye;>233a$i7P zhDXIwW-edC(P-zQ>x1|Aj?Q`6j2G1Bjr-XRKd>x0@A%q+WuZ4MR7*W{y8+C;eF6bqx>=O7{tvi9pP z;)8?7=<^onlvG@P?o-D`rslIoYn-ZkT$$vc7l?qN0D%J=n=`R6D`)wmoE}y~3O=j@0u(;?w+XYKI2Wv$W_WKusWU+ z)wCTvSA2}tk1WtXG_$hehqr~Rw+7KMw9Q|_cJ z!N9_d)m12PwJJ7qOiQ^kDMp1DaiSYrfVanVx3b zwp=0+H65B{XerZja9^ERV$7Zu#dEI{ry6@PW#CLmZ?#s0hqdc$u`sg$E@Q@$0+PIwAj!^UqGG@u8#l@}1 zP;fpfnM{h1elOoMp=kPaJ!fIh_f3Tvp#6|3Gu$??LWllrbQ5uA>uuMU1-8HzV}FJ? z0vEW;%eIhrl-UW3>+{(|alpnNZAx_?)duhek({uN#RT^;xQ$xwCJ76SNs2d)WFYI3 z4BL{ZPb{iybF{UD3_ew#00o_)f1*6r<)Fi|$n+hPmBawTOP>*SDP3bq~T5a8OAMc${A z6ZdS#8uj_k#w#y(K)arw-mH7+8@)FMPZU8VU|76*Dry+~{ad~2nw3e(SiOzgipkML z{+zsLyEjy(v}?t!|Jkg33scBoO>kE=ILPQq(z8YV>C<}4re@)pus|E%W@**=fhRxu zE?)WgbqTrw)|~S4wRzt@5Ts&ALg=NS9)O?O9U`+1nSi2O z=L_iAERsxeG<^ShJUWEL5LYHQTX2HvR(Ykx>#k)kdk6;Blv<>W*&6ZbQG{2 z*o%UzXrMpSFov#%O+QJ@n0=r-$}&o)?(<5{VhQNlV3Yb9jfY=6ERSHBAU}Ncs7&@_ zvOSm5sLm^RSFVxU#0AIRl}KzKBX>EYAogJc{d}k0L@GTQqUY`ed**{G3p_N>I`O&< zT}tNT>qluZ>Bs7s^tyw|wJOY^nfP>|&~nT|SFwM=eD-;Dj@-#rZCO zQgWPg>fm|sz&y>(Y)70Eh1+3E8>}5$tB`^j%^Q&(=8m4+NGd(4!^En!#h$ngbbYoS zpVG67z^lpZXUTC$c{lDKx|YqyPEc>#6`}zbWv1vi>cbwa=Tn^GOh4k#r#5ESH_cL| zrj@(hk(!;0f7pu3W>H2Bz=3TKjCjwB>5)(EUM*l_4oGbYfN&}1(b~&>(_OD|0Mg~c zYsQ~p>+Q;dYhUA%$|TG%)I`F>$#!|8mr~j7L6-=6^;L+Jg9Y#&d{1g0`kr)Z`L5Lf zag@%K^`v}yl&#lU8reVl>V&EVjnr*K*hVlr1AXNMYu7j3nQ*-T7m9Y)Pd6;vP$1kb zpT1L@OFq_p=6P8{{M`fzU6Jw~&0F3Z#%>!q0PDgcp4@zs0Tbc!vR7HUY%WF|H6P-m zkRD5p`mltybC2Q7QqxJJ_=gI#84_Xl^+lB}*?dMi!pM9XRLzTLArfr3wOw%IQd3#k zS=eq8T;0X^+*>GMZXgV!qKYXHb$zL(meV5uUP32K}MmWE9<^=Q+)X)x3 z<`TolGhvS#6NTLXBDkMDVMz&Atbuj$QF-q(s0+9|Z~}{k;lwzGp!Ez+`4p^bGsYdG zBFx1D7}~4LWWHK%y7(atn)t)PIdk)1b>d|jI=?S1`@#W66E?{&VMtP%vJtw;QM;r0 zq%biC`X71GxRuq6?DI^M*eX^qFl1f9XWzo?RXZf{axO2X3=XSwgLNJG$ac+5dR2Ny zpOAik7a!vBqzHd0{=@~%$S+IuO?sh+YIRinCyJ8No~NB>fU=})TO8{{ zRJ={BlEZcsIp8bYjPej$hK|+$WoY$FqLN$psE$+S0`A=940~I}UJ_Y6h1S#_uXTm| zouae2*$2AUd2%Jo_30%b{#L^2q9{wcvT8k(mw1x~Ou&@eB|R|;;}keJx^rtzTzXS+ zNMC=h6~=e#ZBBoR9H4C=iZq=5HmmGPf08L!B|k9pHJ%QCdj0|9#gbaM(OM z)wv_WN9bWi^;38hV{M}0b7W`dO{K#g`aOGlr2!Q6+m!gPSc!mhDI+RFZm!4`1)(dg zYr_gjFY`x9s-WEZPHvP~8CwVJOWd&r2VY+}jo?2%F|Pdd?EPJ9%7@k zHU5hW;TA!|B20QP-zdtffxu2H(@>WwjnFD%p%7i0agSdEE>uP4Ntc&L5d>XqAnL&7 zuF`_sQ03M*7MMLZGHUaQaX(OKmxZQLOFmN|l3#i5mL)EIT}SZBbB0m*-aw_^2y?gH zj;9Jt4FxkrHAT{2;tH^RfG9gpN~rbDRRpWKXIZ`-7x8My17n)E6cHfXp9!QZNBxbM z`$uQ&M&9P0!s)3d6a*a}YUv`M+C|x8ezs9$QwJyq>Ui8beydzUB`)`OoXHh43OLW= z_U)Yk(n8b0Gv);R_I?b~em;^41YeLczIt12HJ&Wrh1@STN|*G(QBV$8N_3S;13(LWVg-M#rg*Ic zwqoD$HzX<@zvp7FH3O54_WZ1A|JqBmY!%+*@ox@;kI{6+{UF0;ZfL8ma(z`7_%BL? zNn3{(gOv)_H=s`5uNt@x9V~_Qf6*tzdo|_$*gL|uJ@KV6$>C;A%&5DsjRTd}2pn+eVH4}?NFOadWRi+e26VwlLN}NI@Ex&x2 zsyXeW+wOJw6lo^iS?6Cm6ij{`lXSu%Oa#(Ik*1z+D+5Vua!M zRuom2W8wOHu7@!nR^Gg<%Zqxs3b{jL@L3P#IJz|(N@mz@;-^Pyf&{|tVvmcakL$8{ z#0B^3opuMXMh~z9x%T}>Fqj4XUw@7jVozN!`+s6f=t6k)< z&n1b397m9pi&Aqe=i>&_3?j2ln5W1TY*$sLwbFiof6XpwV83C0gc6;5xgu(CXGIV2 z)fI2K{`>_T)3-1xS;O-+MZuTZJzWoU?_xYYb$I!P^r)R+X%jCqe6^=8psX%g2^u8S zM1oZH9&pT;tlGQw?0+9fe5tj+w2CJo*m6BGszj|Pi3Oj2;1W-fz;hlFZ%wUh(IS%} znHUr9=sPA07mlmciooHCjXZELP(bRQcPI01*`qwH> z1iB;}KMB0iFuM6^X~rwC6{Rs=+)EeyDlwmy-1O>ZCM?XEhk(jeE^8Rbia|&}eiMmW znFyRj<`fmZjrsgxuKCCMYDS2NUw^E1{o77_Ctv1Wf)!2KqYJq=?z#JO7(fVt*b7#2 zc&u?t@s+6d8j}0N7$B%^9l<8|gDhAkv59*I4Qy^MCrOC7EHkrYEX!4Eyi`j>?TvbZ zr#2~TBzCUfo@*;FSuv?_qq3iF{u-|}MmServ2g-rSM4+|xA+B0U)`s^ zXGzuaf~3;IO+2PM(ZjUaNErM{?JBhMQrxTUv{+hY@MLX*;#fLhfR#Dc< ziG$gBIP&K{WP_WXC>T4*!O66nPQPaC5Q@=ivMeLs!kL?NQ!K=!A^O8{7ivrls3}u= z@Uad#P15~T%g@GC1 zd|YhQvfP@{#~0Yg9)4sEh8VUCz-9DFMO>87AWL~)enR6^x zu3_+l({6gatkP&n8@KEyD0yeS46^>h;d|yD`ZrRMXsGFHB_4+cVNdsH6?$(=dvIy| zV+E{w8{=Gsu4HZe(|>YyryC71vX6%*`&F`=>LY^R)d4S=d<8ep>>MTeE^lx~p-7zT zhuA9H$$=0J5cF1N+@}w`CM)(mc2wq*r1X>~MTM5FAuJ*n(%yLcuWwv{$_D9+ST}xL zKYK5)n(Ot^v=Cvzj>B4v*oisM?O;4SaJYe|KI)z|-lg_!uwLi^LzwRDuu$@WIE?ef z;42%IO#$btflv|P5HeDbX<8){B&3o_lTEh^(d(`+U?}>S(82zSOTR^g`t+IhPY$u| z0@kTd5tf$e%58KXGU_MsPB#~CghTuthj&%X>3fFrG{Wm5&ze0O{R|P9>V+NQbvoh55Awd|?(zhMAqR*U#IHK9FOok?F*d}){J8d-e{md%YQT-?w=5r=GB5^mGYN?E|*{MHu}b%PflX^-G! zM&`&=&gYiSMfNB}N}8>ABvS6!sp@h7!Y+3um}NdA;gLt_N(AHHO zU>V7^Wv*nuOD#FmRZalscPd2JRbho+-SKDdPP0Z4s=g%Sd?8x9GZ{QtfRq!u?ZJaV zc9(Jr0S;2HmLd(F1#CoYUMP!eO~oN4T0NM`fT{t4k%?((60MuoFT3b=^f@grrCS$| ztxvIeUr;plCB|B-+oaO)*a%uB1bYbcz0Q2pE?cUd(Q;@6V)wQ8uJT~h5H5}(GK6iH zi}zq^yYLNrA#CEJxug7Hmk5VBtbY9s8A61`s*4img80yh{=<}~7Uugy{ja*-Ew<4T7U#i)DNOf^Z=;-ED zz1p5H4B+41#d@_M`$N)E#zv9k zU?x6eV{*)dQ(~JzLlF`-)Cl>ps7}#HTwHu68zCKmdwj?2O|2w4H&(jj$QX(D`;)s6 zzIzwKzy3D0gHs&nbzTD%Rqj0U|NUN`Cck#|0_|NPDj zm{6mV#Q$g9GiotCaF+!lcK7|}9K87d_Q?Ul>k8gM{hx8qKv!RInzDYAD&d{^@xQ@> zPYT#@Fk^`R3qb#Iv$-U27anx`!GDL;Zy=fie)+F)e7|!}{`<{3`oLY&*mTigh4;UI zWHI&>1McCq`hpH99?%Q&9wF7@ci(wRu=U2$vd@Lk_@0h{tRFHuHa1BlB)5@S9=;DB z8lIy5((T?ZX89NRI)IaD>J~4+r1ks;PGP3X-_9&}L{LWA=xvW_C-+95XtW$DCzy_U zFlH#-?IlHTav?jV|AIihqCWBOd8$w}!L9}2C9HMRZW^ENuuxGyO9nmTF4c$o?-hU; zM2-K(plT2hajz`+#Rq`))|-@Eea|5)Hool{?{^k)6b+7to5Pv~wsV)Gw!{9|HOoiX z;_!Z9Q?EU-Vea{51g7CEEtt?HKX$C(E73l_dDCoNwb-fi@#y24)7h4zCTNAVtHZ^P z7HHuOG)TR_NyihYyKgf^s)3CN#OlMtT(rrA_A*PFtzVw4mPfkI@Jy+U6^tU?*R%q1 zk)C?lZQ}MYNSYDUh+Z+6jOJ;U8FYq>SgZ)@7T7%`O#g-nHGobtIWI&Yyiov^%|?oZ z`7y2egI^Os@Gy+~MQ)sOQ6DomeEq*DLWcyPc4`>kdEjcHU`kqUi2I>I$tFJhpX>lU zbjSTRh6Sv9{{Folz-Of=tsX!&1~AimY^cdj_oqv%{F%pYt+2)l%bRW%inQS`g&!>z z2`VJsd%cK%Im2MShEGpSM2+X+mUXE5_)%Kdm4+gi(<1+;lM#=mol#fUymYHZ zZtp&Jir~7s5d`GjnpXjZb=I_Q2Vw;)=bNporb$X!V^Z?{C%Db~0p<0@Zj;pcW{d>{xhzPX5m0LPaj z#@@mE^e0$8A;z)M8%_W*nkXpKTF?<(2(n-!JuvNi1}KNwI58quIwPT$vK!U;c`e)K zM;|V-Hb6ncFaI8zSO*Cp5p{3UF@R*=J3{F~9=s=#_lbBe>7p(1Bkea6V|elDr6mvQ zJAd|T{Xd_-tGKXuUvUBMAw>%#yoo1_ZbCJV{&|Ng*3!RUj|SPASOJd(3rJNqJa8Q{ z+LN-OsQzedB{%nXo`XE#^gHmUN$7b~GN>J$lp(-mSSK;b_!OoOK*4XsRFDFazMw-- z6M#T1dXDi%x{N~w?uq#>PR2pIyIS(Ju+uYc*y_^KmAe7~^8SN&792RlO5ztFFmmeM z_ztgtO#R42bFL*naJiK~)CPQMf(O_k$kPibHPciLZ%(_Fwy8z+O>|u;pvB}kcM!9; zS?4$C4WU3FZ`*4iWP@-&T79S!i6lGdfFJHJap!3WR&jw?!oZrVApTD6ev$|a)S0{= zX%qF=dqNfM`WS!z8YV1*qV>_Q3e1b0%!<0%t!sG7inQxz>&0CZa{VRWtScekQL4pFv%>rP}#`r%I zm{-#I03+dUV#T;#$aWL507}vW+uq-<>mStpB{*A+PrBfz_ejTDW=bvN)tzKp#T*nAWr+4!2BfBBL zgo7#+`t_}UV{J&n0grnKsQ7ktG|U)yk`4`nAI7KR@NATIC#`VDbmnXNw?ts8Liv); z2MRizvWYhgIQ@u7whDZnXEk8r=otD>LW|f8H`qxb2!y}@8(t~?h1Y{}PhOI}9HxVN z_OR^t(T48oaN#T^0370H7BWdJ(xBr!n?Tyt8etwh=Vho?>5G^d{24 z_4$PCM=~%s$FEe5ms7UD2%P<<=c-7kH%RilI27*^ul)qXRU2B17%Pst%eWxtr_MIK zi{dEQ_7O2WmwBS^0!6KUpbg~Ra8ijNJ&52$PjGVD`>+&JfG;~>-jSaV9*$?^z*g8J z4e|08WWw>}D<7An z@ILjKyF7d31qBS25DFV_Y3lM4E;%~qP})si;Fa+=slnLqT4rWScrevy0Hyrgjz?V= zgg(C{{)GtR{`14PJq8|~usk3VA#I@)#e}~P_c2O>$)JWh(z}1MCuToBWKRHNeoNIQ zzGFvEK~&Etp9D2vUjm7kI`cHda@ie1E{$6FHK!eskXBp>6~{dj{8YB@3NRZ zN#}7`q*dUi;{|^KRVde5*Ww~^P$dQ%NS>}d6SXA8l~U05`#i595c14bK!n2-QNj-S zXb7kQ=HIp@$~ftP@(L|(r1eC@T&Dq*-JeJDHO3Rp~m-mZtO`XebJ=O1-8&p zwqrPb9|4u_X@?LQZT}=>oqB|Z=d!FTLk#k0#^K3JsxI}fcnqlTaiKa9UcIE!3;xUq zqS9l`4)yb+Jw6&!0AenuJl4c;vDX+9eUL^tPiJJF1iPuaHU!VDzH=HKSCnrUONnU+ zkGEw3@2UZ(C+R1VI_cBG9bB{q1KMZNb%F#S!;4hM-v($lSgufY1<>>D6fvtubK3Gyj^ zWU5Q%i=q*nT(>$wL;Zo@3c!e%&OPK4$P3vPkooT^p}x$wb2oKuTxx9Ovjv)=#=b@@ ze?m%*!z?Kw(>Xhc(pM!!H8hG+p#p@vqPr?qOGRCU>g&Ej8Wwj0;?yVTc2{+;`tz-k zrvM4{9K#s1^P>#A;j3rQ2#ztM`t(|ke31fPvS9j}P-2aM{P<4F)Q#H?XpL7!@R*T? zha%3-J`I;ldW8Ag9#@<|MfGSw1=ANux>p(ZrBk((sS&aI!zWO4wV+w4^}?dx3q=v& zh^{PMn`8|*+|*%)+9I&XfA0wM6!pD5lpIfUE3Acy?(X_N75EeSf=HXG(DoTVecf@>)wCx!=oK_ftRa|s$F*8=DXqZ7BUihUh2!dRwS{Wgx`gW ztdtq=HtptE5PjRy$RtS(vt$rI5j-2T7nVdrsXM2o@FH_MjU#}8u*5$>3v+f@6PAf&zGgf4ZOl+}gCtY|p9(Qb?t|y)~a= zho&8#AtQ>CJ2=oza)Ad2f0`!2vC!r!h!=qCE(g9i^}_5qJ(H3*Gt6p_;5!?n*Qo4h zUO+yZbG_~J>#uu^abti>zUGrZLn;XO5rI-EDD7Wiyu_OkEPbC71RZ3!Lj>yf%wPd7 z;%^FrM@0OLtqv$EC>Y%MlN@P!E&3$&>E(74?Kx51&pWAgE&cMkot#wM#~(A!A}C#^ z3YNB?>!2i6fWKM!S*L*vA zS|R780&QVNBkLq7;s`hB#;6>YaAw6NuCMK9u8tCBcT~E4n|57rQ2ZQ7^w>8!MxGjq zl`$)&&1^$ch@3HgaB6re!6PCc4>o^XH8d-sum%D};(D}#W@q0`DqC6-9<_||$^*{8 zIE~4d{|;h(Rr}kXhV%ooeTz2fOK?!!D=2V*+ZQ0XMaz{w_IpEl>h z$nqfLI;{e23if;fmsX}eAK~Klt{)GRdQ^LZeU0;v#&wS;w?d2`BYKnkydErl9U89| z5v|O=Lh;GHtz6bGCudwE1lVhl>!pBTEv?9f^&_9GyC&z*9h7*A`7x9;OBLv)afk4I zFv1mD}Z1J^cvmTQG@rc%i&Ws+RK zEfaMV(hhSbl|tJuo`kMTNss@J;LhJH1Jv9ZVK&bc^r7vH{u06+*8VC3?$r=4@oig} zV)a^wQ)FC?zraUEy9E|BD6-oQGawUJf+O&R=n9Kkb1(2Aq!-{lHV$C7L2&(;pBIp% zI9{k>YkN)4hfNln?KKnEtx(D^ek8I~7Rz0neAA<3?wbBdi46xO{pMG}fmCY|oRNmW zD~vLa*=cgbAIYOzX}V~Ht!gg@c|S%AOEbP6f3YB876sH@&X^`owh#^O&`)y{Fy=oa zb6y*OI|5Q-TbQKds~T<~z@eJNfhB*bc!|s-4b&UMIWjh%6=b$cc*)}Yh2QtLGvQwlc;=Oh(9lZD!i0i|te|CxGFow@ zpg9e}T!td6Y1T-i=$BoK_d>*3!Qb(Pu%m)N71p`a;*;{nFJYtwJ&~%CX{*(4x8|5ZsxL+^Eu|i`qXM@cQwJu;tKj9VK~{>+;zEfB|72S zpSpmuybJRy(v{L20b|M*QEmM<+UFs3(wigzn+o&m45rcP%5nlZqx6qpG(z~awVzy9 zEn>>!eq3F>0@c+)1rb)&r++$Gx&h*BjX8kozMX-?Nl7=Y^XwTuNE1MCV?%P#C?ol8 z7*MV<29#ZO^&C6yq>X*Dm%y@*^G1^GRyDCfXYG!`?id81Tv&S-Z=aH$_KNJm!gDu~0sZy!si- zim`BhMFjFCP~fxOF}y93PWrip70DlAdrnDBgiRkCCC$ zyyg<=P@Go;l(R#>`VKw|z1JZ+WKj4N&f_`x_^t)t5K&l}FDPAUib+2U~KkTlAMRb%! z^Gx-8K1+ab9FHV6ZjE+nnlftoV1pYscRo_r78kR@hz+PNPJJQ^Qh(lx3-nfVPc@mU zsw4|yp^MBxpWq?>c7_&Go-Dlxw#DSqp?WW+%Kabbqgac0qJa>D*TPIO5^d>*{Cx?;{< z6F+!m$RTH#O^~kEeLFW>fs_zT7#1saydV@+2BO_y>szu#ahmdrYQf88i?5csl}w#q zP8$}ctkIv{J%L7B>^h23>OQOv3*@#c7_^2;Alfy{ii%OvNeULYV-mSrvapV0_bK?m z5GiY@OQ$__kR(JWW}6Y*Lks|S8ua9IfHybE*$B40-ce|jpLVh~R4A|+-*$0QC|{0M zv+KQYZ|@1{gYkN@uVW25Y;Q96u^F$_3i>Yn4O+fHzRbu*E(;vWDeyc2qOP~gn9lq2 z%p{oa=Lipz32u;6M=kmIu7RvqAHwUxSsnJ0qUZpQf$3O8eHtm|o2xu#t?UxVQ&>T~ zC_*xF|HeXW*_U_ybsV3nHl7o_RlZOWOlYV_4*!sD6_!JM9Ty%&|D8@-DJSP*P~NC8 zXR7|EY*f5_j;4NKYVbq1*XGwvjVB+^cj#EbU2D8PKa-`TGc}jjaDETKa$^Q3*O{IV^JlY!?6&< z!BQ8*UYBL*aJL_`B!hCzmmQgFYi#S|m#8guDuwQ992`@Kb{7Kb>fZTUx1|l@Tgp0k zxU%=H8;LuB&W&)BNV**NXn*Nd9hJ$j9+0r)`+~NkE^l;bcAj1P@F2d|x=lu=J86s@ zI^{9y7Y8A5&iZh6{Vd zJ2(LM=&r{P%tuXP$3_lhd-fG@S0R8*SRa>_RdP6ccuYgd72SxjmwN2HU(c?`B3l)8 z7w}cA+zKl|s$HthlRr2Af!=K{uZOYpGtHa&*efqKkx{7K2(vq+cD&lx_1*QVyjrai z4fgxbh!U?t?YGA_YAg;$YWmAOulft^O;?+KqFH;|_nO~>#}UKW$|`Kv;|i4dR$V=! z=W_p@M18G)Y^)Gj$y9`=E*HacH)OZ1#oyT zSmL-(U4eTEo*fDS_on-4E*BvDxa$^-3eN%N`59_mf&0~Mfjrd=uVX%7b(baTx{S+V zC2B?{QpiYGNsdriYO^xm#!zTJ_m+1)cYeHmPl51Hyhb8#80_Z!$XHk=S2xAPb|~Pa zZxS*xk@wE=aY^r1g8CZVuxVME4%|2m=+t8ih4D?I4C3&Owf($Ta3o=kprylT3yKxd zhdkHfTMKLB8{^7tC^x>$ld~ny>E)%5lv;i~61B4qSu;zba0})1NcS&a)ARatmQS-Y zc)O#RwDlbMBKzqx{N3XvK>93&UTfQCK<`h*!rm_pCKbYj`{P~~-HZDxQJ|Go)?~zgZ<<2dRZi?+^ekg^u>?I=&@K}`i6kwK>+%;fB7N| zug#(d{ks;x9YY=E^X;}u$`#4Zq3bUYpl~1_Z|dAUtWtoFJ4KFrEfndVq~1S+?^{-y z&+E0jr>Q%1{QD0Z!2gH*_rd_c%{^rQ`4{kkH?MpCe>Y`^1k?2U|KFzU;H_CUv~-{6 z{p#+;$-kx#lYNf(UoJmDLj&kb-_(hN`k%|XU!dnNrsV(aHwZ6JaBFQgcIbN|{?}{o z$45gVqu>TX>zkh>)qCv&mLjzC!yLd_k!>jy4 zicl;COa-Ejd7$1hFeWiP%veF^laFsdtvuyO1N<2aJ>V9|_X9K)0DYS(nec)Cv|;*g z@&;nwl>wFM-WsY%5ZsAR>?ay-5{lT`nylJt2h%fL5HrvJtOQ04Xix2hX~gw=wErLr z61HBy|G4=3ZUAW+$MApPMjW0PNmlQ&EUxZS8)-2~Zeg)q{D3h}JfMqpjaTaZyuDDt zw6ee&x_&p6Pti?P^v{=D7ehk#F9WTmG#=%w_FVJNx~>XN7U%XT^&{W&Nf-^#MP2k3 z=Kb{@Qs!xZ7Lz}B>BjH$1kfZg8)Tc9=yqW{?y8*sGODUdm68&N3~rmZbKm*bVD?fs z`&{i*vG=`F$#@Jj0GHq88V9#WBSsa2T1pJgsO>GkqkyB>CNw|7Uymhu_nNc~*FwKo zpXpL`Hp5-ZlS^@FXt}Qw1y^f2jJ7f8sfva4MtD7lSXf|!xI#gxiBWgpUp$+(i7`C~+aStCw|#gtl(4MpwuhHQ3AdhVhHF(`ElUy!x84hJ+9_>G)XMRA)dkWOi zREr&C`H;IsLO^L!&{@q2u$x>|TXF^l1x3A(XbcNPY?W%5#=XaxK04y4OBm*&iw>DHduLx-#X?X>OpTVq_KaJx!?9!$e!At;v zf;b%JtQtqmA;Iho`2Wv}0DFcxuU1=v!36NSfuUiSH_8m9oZMLIN=U zjD1mofk%W%I4$?wh3X0{7_q_Sd*D5vu2BJ#(0n1rdruqxMUe44X}$Ov?)&ijKqgdB zkW|_Yw1TZ|BpDf*t;wA2y+O>!hQP$k%<29vFF8R>?905ZiN=$Ah@nvf43TEVi**O0 zfe0u(R?Fz2A?9F8L;?=gk$`(m0SOZbwP9pvXc(oasL1ZFd~i6lCWvsqI9W{M0+E+< z+$;C3{|=xoJl=ECwx5SsuGW4_a=3@kcTK)WDWespqaF=)cF{IA-wZoKo6}|=3{^g2 zS3u49_17qCgmG62{z2LD;VaAxY|An8Ubf3o!ec|l6U$vH>+Z{+{pT36$js6_% z=UweGJ}gI#WO?XG$46WT9&7x1$@Ffl?|e--Im$75So!u^X^f)Vh#Fs=KaNWi%-%7e zvsx35r#);k== zCeNv#*D=Q0@JC*z*W%`Oh2Qe67A2C)?mET8BSEhAV=$|)_CGyrXb9p}S-zS~b&uXV zY$al!dSE6TV9@7_JT_5xgQC4mfkLq7_w9ql?)lW`>7C~Qi+^NsWOq?Ty4jkJP;5nJ9yYtEt|~y3#u@QkdNI^1(}Cp zKo+klcHj808>gZeaMq5F$4zri?}0J?$=h=#`P&7r-zal@xBco?fzNrr+G=u3pTlTl zb?3beh?CMNKS2jOHAHDfn>R@tWa z!K2$MH|_LuTqpZnxUFK_ud;>Mwbh((Y6kaAhmX zlLuQLE1!OReR_AaNlTgq?p==;-s<{_^Py9pecf|UzP%?C*XMdSj2#-vDuxbP4jKhB z$+Q=|-ba_FuC*sK^t2auJ*vSXqDBp{7E}U;i0=6Y-ZsFK{iOjGn>=*FX78$9_tMRD zuBK-PzL30Bubus>-742Qo|o*?;>&vTn`=v&ubX9@MYV-fuw_X^}}lTDxXX|%G-icTj8iZtg$Iw(TURJlL)dZWKA9i*<@J3)FDh< zH_+eCGQ=n)+pjv|nOOx-KU^4fv|rusCNkeZd2r) zzU{8_!3Q%GfW4;`80`|VT7R1vB}EiVx32y!)jZ?H2dc1$;{5p+HUg3 zoQ5)hl3Zt&-{S!Ij08*|9=PAM0|A8z=@vSS51|xv46sdfFo)bcqVa||sLyS+MA{ZF zGJ9=1F{{skS@(Lsz8>NmXjt+1>zqTdzU6NI>RrdjJ1qj?RS0%hD1`&vYJP*`7wQ0< z;<1;qovij)%bsFe%|hYh+;rWRB@(so zn?5hR7$7<=mzny@x<}mtVCsF1OwaodEWAA#_cj5!=nX8Ez6GJ?BaiXSq-Qy93(n^g zL$1|5stL)a$=v2UP5rQ}{D0ZDsJ>8r5T%Dhigc4Sp<5EgYM@HfQCty~oeNnw1RlgQKX=t5OZ_$el>m=O5&|~scSu{0sH4Z z2qq$Ovq;`_OQ9fUvs<--GMG_|IF(>m?fnhBFyf@@;{IK`6eJl z%ptVW$@H6v|9SS|+jmhwXkgDs(meX}lg{oVu%$Wrb$rK7*^D!geZ<;Qqn_Uy}#m95}OT z$N7g^b;;;lPVhrihX1GScmFcaTY&VgSr-xifiN9<&aCG4v1y)}=|4u?{eWi_sGHh0 zY7hGkzyx-!E@Q&ZYQ_g!#%XoX`7)wwid$=X7{Dyo6MQb` zoK;x-J6jIyPD2HNBlPnf4LTSP#6Mwzd5^!puO%yl157(aA`aD^>&4zuvvJEfu66j_ zuisEOLhxACW^aEnn^^6J4)@;%F)^{gx)vgS=Bq-eJ8fs2w-?hud4RsH0I(qf@hv|6 zXI%qWs&8wA-8IT2OG_D+oBehob6##TA8(F}>{L5sEjGCjxrysci3=h9g+LJ4j>=wV zL;FK2#3PTAE-t@XpAuPy>xXa)yz@9u`j-)@K+zS5vnnnuTys@tTEKPq9WT|rx`*h` z01#d&=)j<|Gsxuk@(7UC8OM9QUzFj`Na>1m8n1+(2%u1w^mAX&ToEmYz3uM=HWS)4 zfPWkQjF?Xig(%ec;~mHI!N3<=b3j=##N2h4zeDIREuHK3UhA0xk8tmkWU|X(qUUZd zr{NVRER$6x0Xa=&;@8dp2y@^OOD>1AUFzH zGez$40r)cB8=Zk3pD({P@!sRmvFDDvNQ{Di zq{bf+a5v%a$^e^B%&O5K{sa1deS_Bmb52>*Lcs1*Qjw497t>;8T9H3i}&tdNcro1p8&VOdU1WZ2KN6p z%Q~>~IGv?q{6p&hS|%T{IUvIPwhNZO|8KMW1)gQysc+n0I_a-vLO|)hLdC04Y4`c- z_W=a2;0l%>Z{_IeL2T2FK+5q{W@eU}+J@;18fK}5Ax1K)JhSnTvdRka#lCHH0wUUd zoJn+oX}4P#V=K$>_AU^5LL}*a+}j)7vq_H7)6V|f!3Vn?SVxnH*Un`^ZU{!o-^fXV zQAD-=;h^1>w5^WVTk0DUwiTw2WCx-w8`}-rp6iP)?VpX<7=4v#;t-XdFtPMhY-dL1 zfP#Sdksl$&dfN-o`mA+d;j;k4=}PU6FOMKPy(GNUb_b;fPfP7vLj1$lXD z3ofOyvGLCK)49lv@=W=3txaW|cnX7e5_8)4)Yr&7@}(jntoVnO1Byx{o3qYuZPZE1 z@y0}3`Ure!82p06M?R9SyL1c=xpX!A>fc~Oxg}qLh_?UHNLN_EW|ZYt=8xE26Z2Y> zsUKL}+f6E)Sp0xogB3fw>2wOmjxOtUGKM*MFbOArv4By*|8pUtKTJ;L%Ccys%%)l6 zTz92B4JvuI;=8_d_+Vr8TRrrMI_7e2t)fTcFf89MJH@FD zdV{X#>xBu_{Ca}VHB)!_MY>|YS{=GX*_a8|=U=V{92|ZNS$96HOJH>V7+7`0A*W4s zR{Q9H=bV#uYWfMlhbh-yw_^=}z$FZ^`R%7874(c64*QT?E&iJdi49<;;&Ba!MXb8> zum~Lr^mc$g=#Hn%Zx1>^JP!5iEtLl22W_Rd>9pb+YBl|JDFp> zE^xl$&uDl>?I6*t`-A;!eFNM9j}r(e2l_`e)F`HyVNBVrkF5P8763PcnyHz=@mHDd z9~4An5=7Sum9SZ(kM%vEhWjpMYd;iRIgU#W4bJZ5A6yg;Ioh(&ZI>V3vP^JX&JjWn z=RSfK%A_orR>X}suhe|8Apu#GaFR@eHRDh_4ai%ZaBkDYjAuyJ)1x6C4PqNCR_|Rj zJcvV2#dvA{vMRmMbe3&(5^?@9N}9YqyMnHk+bCR&2krc(;^PhG3C7hq6up2(MMOf@2 zC3R?Uj0TCbqAQ6y!QhZ$SG+BaXsf1dq@0L6U^s5dri!)eWHw=EhQ zo40#@b6xx%mwt}UYX-!-5r!WXALKTm17L&zk;fN{1M^2rHAV2*^jcQ>jr!01bUTUI zo)Sc>)tkQB=ZsdGqzZLu%2y#k;ZxMlxz(C5JMao`oMRFp9>q`5%3N;V@fi7{Wy4?O zel9lcIwsg(tKpmE5+|w{IUgQ=;KE5LpL0>D*{D|0e?7E`ZFaj~x9mI=;McL_a+8(0 z(edr5$Xe1UIxzIj>2317>iXd~gC<&;VW`P3zRqs^+_6J-%O)rmiPpOj(Pm$QgQC2K z`@<0}^4V2D`fO6%8FG*26`sK1ZXEmY%~D)f-S6t}778N`_Q!sENA{Z=ARu{R6q_8E z3|TwStXxT2XsU)n7-v|Os^gTJChE3rW+YW~)I1~$wv@hx^r{1bq|~m$?omktNbOPm z%JJ%vQt)-j_#6opn+39D0$vu<>#*=|G$&Oxsvv#V_Rl!uYEZ)$H=`{CE;5`#+RO0& zkG;1Hi}DTr#T7vj6c9x^R1jFYSz0j&>5^9I?r!i40t!e=cddZL(xs?$cPt^jq|^dS z{GXN7-#OQHUi@F3>%7BtMT{v zKxSel2Na?XAn3D%l3ybu+|zNAOj6Br9y-}J%DpCE?KfUFxnWU5k1iS9E?wP4E?a<= zt7i=) zyd~k*18ij(zrHh)xzbEs6`LCHP=sWc;CvzWMurKZx-dHb!CferAGhMyb6t z%~l}bCv>OPORO<)X;oEKDd{RVam-h({VA*bpBHVy>+F${fW2jPMosG3S#|iR5;bDM z0IXOGs~}P5l3!3#q_8}g#jJC%orjnr5QDtg5$nK_z-S$&6izeOK*oRwRk8f}N$%VV zJHJeV)<`SH;2cECu|Jl)_vV1XN?jq9(R~efFC(b#^RBA{cOP?Oq2MDXJQj;HV zvNfti6fMiMouhP~+U-nslwS0wXqNygT(8Rd`h<(k-GesLFZl>?cu9~SjReFeX4W) zM@OTTt#yOweMxgg-XSv4a{Cz)sG7S@?zH=XeKoQLXu>novY_L~=2}$YchFq@oxPCK z?U|K?Sn1ZwbFPfP#o@l_NR+0zV&I0Ht~uCx+TgA;BP|(U8L(&8A2DE`{Wxw87d(eA zYN^)8;xsMIlqDplh-0#mf!5Pv0Mq%RXx8grEbgj;_V!0Z$Yy5YFkk10{PKN2 z@cMyN6fqn|pe(SPWSYpY{&U}}=duRm-_4ia zWqP@2@@m*gtFJI|x+PF1vR-Si^t*vR7;d=Z*^VPP6iiOXiQ}#hrUMh1! z+DAxorS~2oDSt~c{Sc3waprbk%gBn#%pI7%qPL%mkkF9G8PNxh*PQn2*+mBG3zz`7 z$K>!i_FO=w)NGl&&_pJ7sb9-Tyel-3VvfoD?$9d@w^Gb3f$mu*KIV2=C;)4oV*D2$ z>wIZqEim8i&XjylTwk68^zooMM}8J}{xfBUZ0uqxi@myg_+cSb(AfT_l6>K!G^FfY zuJgim;;q=&W#3x_8Y;M;UPM0Nz9?p+OIH;~Y~mu)Sp+6WxvEN?uS-LI?%u`?NgX>- zR<7Ml`+U%_;k&VnWgOH);oM$eJiJm?!~8$+1*%f#X!GnirvStRe0)#(mkdLlAd9-v zM)8aqDlB>tZSq<@#^tlOF#P4?5~-%HJ#2Yjzn&(O!6s%1_QFEnmQTvS%*Zldbp`i>!v8e7^c5P&Y zj4-2#mPfnlvZFSlzlD9`Ee9E{1Si>!#p}-e_OR|b17e(9NBHYc0*=vB_kw({95I>h z&6^D!E)&}ORprGg)ApHbKvZEYGaI!9%k>)B-g6rkmGW1mgAwYkUnn1w} zWh%Ze^=u?3VbfcPh>|8mSF=paSF`d(do1)(!d}&5cMWX|(rD$Y49z4n2b7SRAP@5} z@0Q3$JC?|%$ec*anaK)ok-C*h@4Y*BQMURET6xEzUx0HtH;WOw#&U5yoo1)Hb|QfA zUQ}(c_|_F~l*Np{lgtbAt`BxlE+6ZI8#%;cNkbx<_N3C6cGkUbr~hhLP1ht8QQ1w5 z652CRwH;sGYP*S??VI-XQF}e-UB2E%U{G=M1*C6wb~j6?cMue{jmwd57mM>0iaqTn zKUHuZgl)zFg-Mt8gpbEI43ohtvIHmF%y4m&OK^V@M=n2ch4`KhRxWPBqHROmyE+ey zH=&;sA+s^Y4PEQIHfc*6xEji!GQK^yx~3kqXROmx-Ed&sHYUc3+NOW!>gPkGq*+CYC)UL8;pLWty6%`IxV^BlJZ%LR_4M z;uqEn2={MdRe9I-A?rZ~O7-qQMP^O9k-2tmhZ#B`<`|OpJd1+>Ljur3aEQ3BoCwa7 z1x-(>&3apB(alJL6n!863pNqt8eyLEZ;PPt+wJYv&tyV?aL)R!=BhEx7pFk0qQ}GL zGq$rKw`O}_kpSRS3*rj5C<5@CBItbq&%WGt<nGrE_V=a0omG_&h!Lly(=yHv?vHbEG|0!3r^&|h*~hk zYXjM1FL+-@Bpog$8SwD~f9z9?Btf%8#_W&r!=iUKx_2P~w6sCHv?0-92H;2O*|IDL zB^`DgG@#}R-_!!*%$iR+!t}MJ>C#Hnx^rr9rI7vsCi>dobffxhrqJ%v+H@`LWNTdm z!J`_}y|qL&DktcY7F44*$7C);`6)thM~q%*!L&ibpzmiD4T7Fk1>8&^+*aDl-yLq~ zWo4m0?h_PVoT3_Zjh=5L#}Aa#`{24NxE<8s0z@3IUtyi^=rZAiw(B{sK zG|lr5Qc{RhlkZA$s}a0gSiaHV^0b+07ja|Vm_QqzT?S%dO(dG|%t!Fg!id%0TJ zWwAazfJ53VKPh^mfZIRl{F^)#KbNvrxpmSDka)ypr{5I$1-!zK5@O`20Y|mIuM$yW zMwmy-YlbbVmx-YW$KI+FvhnO4O6KFWZ>k9$N(X&XU6V@L9@__X1%cI&ib(wXiBp^H-G%Gea>)~G0d@rK|sFuwO7Pqc1wFTd7;JBhX zSeQF>Az;(+kw_hjGSL*BsZol2jlxcAMz>e~fbOJiQ@+YpfxLBt!OhksX%W8#nclHX zW%*H?Bli?U6$p}oWjZ)}!Pv`8OzoNjE-eXa12v`b{epIp56R8UP(UvW0D8Q~RoOpE zarrjj3t5e?K0Zm+6^SSZu)+Ke9bX5{^$qNmEIpWjh#a~jN4S}UcFCGZ3WAr}l(Lm~ zl=HP-^CqaVij2+}NV8BFJF&j*>rO15JA#t#S*m2Inz*hd8AnAg9=4D<9608TZ+59i z)?@fl8USIAZukv{m5MQh>Cp5rqC}yxX)md#_uJ)?Oboct5RNp>S$0pP4#n<#ALDfb zm+KhUey}4T%zj&voIIKD>$I?^gA#Q|o_AT*7UnuAn;(9UIZQzu`tjnbk0}`kjP<;4 zSZwBQ3|g%zEap;KQY#q5$5dg6NlmF-$p;?G#@#&3b7)SXwnyRkHoaZ-qmW~?&A?X8 za%_hNGQnO^B5Zwk{AEnq-Mi{=#ivOtVapSW$K`F5MMi&-w0P)C`h}k3G=KZHLgcw$ zqo1%+3xRX-U6&kN$ixKf)nb#MS&i@Nwe;<{*{?kOw)+kJU{+XO_*9=I|099khdk$vc0q%?VyiC@e!eQ~FsoaAO7{ZQ*Z|8nU;0DS#=S+!|0Re zIruiRNMtN?1j-+*Z^y$;;m#_THxd((kYyi2B&B;5BpxOWh!Zy0dyXH_pHbzAj-YYe zNj=S~t6V4Zfd5BP>5NGGK@ofnJ~6W4&_a)!yJb?wk)hYZ5p^TvZ5dr{`(n;dAU!{> zq}e4yi!LN!9;)}$g0|x00i{b5x5(m>$yf4$CLqCT5BM<9#3+z;Ae-?eSJ#87vs4Bx zIN-SNnKAL29kD)fCLYif>Aro^@I$h#LVhR@3dQ~wT(#*; zYzyCw6xt+!k`6R4X&)Jk|(?FTYzU`4}}$=OYasfW1~Zh-488 z{m)@+0<`GhEG5+Wv6MgE!o#SG!aWOZR=qgMBW#<-+o`2959wK3v-JCNI6MvrTQ~OU zR;xvjQ@GAc)73nmk3{RHu9KY<;k>@w0TOgC$CQ~*Xy~1{QBIMT85G`=sqZ&7{mClo zdbG-neT5FoBFj&$qifl!3Lp?}vAY68Y?QEjVoh_Q@ZoHwD}Wtt_rVtc*8qZOxqj;m zu4tE{tf7hhz9;+GO?0dChu5%#p$AW&2N2?ru~^LE2u(dsMC-{(boV1;;-7V6LZPq) zRHiS`dACriY*`DiseLklY*}nF&TCg91K5K{+6{S>eiUaBZYf%iZ$|SdzwfPRdxq~Q z=p-c_(S+ES>c!IXDCtsx-N(@CmWxJ`Iv>uYpw%6kUkMUc!g2j&=N}}qhEPl#39k6{ zkl3n=OQ=-kCZc@%C~dkm@Bqly^4iSPfhSm4B#nPV$w`cZt-?Wwh(r30<&NBe!-nY*Al~~^6L0Eyc9szsGg-l4aZ~aHk0h?ZgNQb z?LIE4or42wAmK=sEgZL1Q*{k4w98I7e4f0t&AkN9tn3dPJGRc4??ia#;wZXl<;Ke0 zLvAd`65&a1P))~i2R?aLUJZQ;a*v~RDKWR6)(`6P9s$+GXJIJWAch8hfBBvlYWSHk zs`&?YT0x3NuPByIXebREzlwZd0F>az6+Me9n|850V#~f<-7m`(NT32eas>tW1h-Kb zCuRN7-1|=xWhHqP6cO5LGjn<46{;H}jJY?rfCTR>5kt9!7d3)8

    =z0X5_etW7_ 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 literal 0 HcmV?d00001 diff --git a/samples/blockchain/images/visualizer_block.png b/samples/blockchain/images/visualizer_block.png new file mode 100644 index 0000000000000000000000000000000000000000..ff5182401342a4c11e2ad5f585aed9f70a07b23e GIT binary patch literal 50801 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 R5)r^31sP@O3Q3cY{{zDOz|{Z% literal 0 HcmV?d00001 diff --git a/samples/blockchain/images/visualizers.png b/samples/blockchain/images/visualizers.png new file mode 100644 index 0000000000000000000000000000000000000000..b50821fac10279e4b6001ebbde6344f24a3a473c GIT binary patch literal 750824 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

    !$6U{lycx?)iNDQn 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

    #iRe!xU#>ibHZ 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%0D$9)O#qSDlN4r;9_~ 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%0AwP&zOfaTt63OD8Ig#W@r)c7s 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 z zER3M4Qmj-c+)-K@NhVNx2Zm)J8o)83TTR^=ce)?)Vc1{Af{ao8oqgc%Nxs%qiNzn^ z>utkg4q_AMMol(CwXKKRY6gfI3r2EYL|@fC9Tvf18GHu64Y)cyk$i%8bR(if>y5A9 zl4SXVrrU&0BlAZCC5auy0(wN8B&4=Qyj9D^B|Yz^QZ55%80X8?mfc`AL9Uk<+GpdH z5}oJ!l4`6%Q;_FyZpTbzy(U~+61o?0oh+=vK_t$pNtlM=+?9-i z`>D93n#mhm)UyuF5zU!OgP0Q8?Tyb_#M#7j)p#q##(9<5`&Oo1j0?r%qg;)z;23$P z*4&-76#Dveysg952ba zSSnpLUziR^7!GYN2Iy}IBGR2EWvsIBL9@43iLZ!=qwnKN@8$C%&YPYq&z&A~A>Nbo zZ7UMsUOG`E9LA@OBH&Mu{k(N8NfRi|9IOkQ-s;c`6t3A7_<=Z{fJ&F+pugJlowno1 zAl3nCENNro6-shHiwZ^_nI;!Yjc!J?egoitjLgkF4fT2E^bsLZ@9qs_f`Kl<-B>lR2~M8{`$?6r z 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 Date: Sun, 4 Feb 2024 20:18:29 +0100 Subject: [PATCH 34/46] cleanup --- samples/blockchain/ws/main.go | 16 ---- samples/blockchain/ws/ws/wss.go | 137 -------------------------------- 2 files changed, 153 deletions(-) delete mode 100644 samples/blockchain/ws/main.go delete mode 100644 samples/blockchain/ws/ws/wss.go diff --git a/samples/blockchain/ws/main.go b/samples/blockchain/ws/main.go deleted file mode 100644 index 70b26afbf..000000000 --- a/samples/blockchain/ws/main.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -import "github.com/filecoin-project/mir/samples/blockchain/ws/ws" - -func main() { - ws, send, recv := ws.NewWsServer(8080) - go ws.StartServers() - - for { - msg := <-recv - println("Received message...") - println(msg.Payload) - send <- msg - } - -} diff --git a/samples/blockchain/ws/ws/wss.go b/samples/blockchain/ws/ws/wss.go deleted file mode 100644 index 135b6cbde..000000000 --- a/samples/blockchain/ws/ws/wss.go +++ /dev/null @@ -1,137 +0,0 @@ -package ws - -import ( - "fmt" - "log" - "net/http" - "sync" - - "github.com/google/uuid" - "github.com/gorilla/websocket" -) - -type WsMessage struct { - MessageType int - Payload []byte -} - -type connection struct { - id string - server *wsServer - ws *websocket.Conn - sendChan chan WsMessage - recvChan chan WsMessage -} - -type wsServer struct { - port int - connections map[string]connection - sendChan chan WsMessage - recvChan chan WsMessage - connectionsLock sync.Mutex -} - -var upgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, -} - -func (wss *wsServer) handleHome(w http.ResponseWriter, r *http.Request) { - if r.URL.Path != "/" { - http.Error(w, "Not found", http.StatusNotFound) - return - } - if r.Method != "GET" { - http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) - return - } - w.Write([]byte("Hello world!")) -} - -func (wsc *connection) wsConnection() { - log.Println("wsConnection") - // close connection - defer wsc.ws.Close() - // close up channels and remove connection - defer func() { - wsc.server.connectionsLock.Lock() - delete(wsc.server.connections, wsc.id) - close(wsc.sendChan) - wsc.server.connectionsLock.Unlock() - }() - - // sending msgs - go func() { - for { - message := <-wsc.sendChan - err := wsc.ws.WriteMessage(message.MessageType, message.Payload) - if err != nil { - log.Println(err) - } - } - }() - - // receiving msgs - for { - messageType, p, err := wsc.ws.ReadMessage() - if err != nil { - log.Println(err) - return - } - wsc.recvChan <- WsMessage{messageType, p} - } -} - -func (wss *wsServer) handleWs(w http.ResponseWriter, r *http.Request) { - // cors * - upgrader.CheckOrigin = func(r *http.Request) bool { return true } - ws, err := upgrader.Upgrade(w, r, nil) - if err != nil { - log.Printf("Upgrade failed: %v", err) - return - } - wss.connectionsLock.Lock() - id := "" - for { - id = uuid.New().String() - if _, ok := wss.connections[id]; !ok { - break - } - } - conn := connection{id, wss, ws, make(chan WsMessage), wss.recvChan} - wss.connections[id] = conn - wss.connectionsLock.Unlock() - go conn.wsConnection() -} - -func (wss *wsServer) setupHttpRoutes() { - http.HandleFunc("/", wss.handleHome) - http.HandleFunc("/ws", wss.handleWs) -} - -func (wss *wsServer) sendHandler() { - for { - message := <-wss.sendChan - for _, conn := range wss.connections { - conn.sendChan <- message - } - } -} - -func (wss *wsServer) StartServers() { - log.Println("Starting servers...") - wss.setupHttpRoutes() - go wss.sendHandler() - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", wss.port), nil)) -} - -func NewWsServer(port int) (*wsServer, chan WsMessage, chan WsMessage) { - wss := &wsServer{ - port: port, - connections: make(map[string]connection), - sendChan: make(chan WsMessage), - recvChan: make(chan WsMessage), - } - - return wss, wss.sendChan, wss.recvChan -} From 11b8b39ee01c48b00523c2e56edccc086c5d402d Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sun, 4 Feb 2024 20:18:41 +0100 Subject: [PATCH 35/46] more docs --- samples/blockchain/README.md | 267 +++++++++++++++++++++-------------- 1 file changed, 158 insertions(+), 109 deletions(-) diff --git a/samples/blockchain/README.md b/samples/blockchain/README.md index 71fb30949..17952c22e 100644 --- a/samples/blockchain/README.md +++ b/samples/blockchain/README.md @@ -2,11 +2,14 @@ Longest-Chain Consensus is a modular implementation of a longest-chain consensus protocol modeling proof-of-work. +The goal of this system is to provide a simple modular blockchain run on a fixed set of nodes. + It provides the core elements with only the actual business logic to be implemented by an application module. -## Goal +## Overview -The goal of this system is to provide a simple modular blockchain run on a fixed set of nodes. +We will first outline the different parts that make up the system. +How these elements interact with each other will be described in [Operation](#operation) and a more detailed description of every elements can be found in [Modules](#modules). Each node has a set of core modules running the blockchain and an application module that runs the business logic and provides certain functionality to the core modules. @@ -18,9 +21,42 @@ The blocks making up the blockchain contain the following: - **timestamp:** Timestamp of when the block was mined. - **miner id:** Identifier of the node that mined the block. -## Architecture +Every node keeps track of all nodes that it knows of. +At the very beginning, this is solely the genesis block. +After a couple of blocks have been mined, all nodes together form a tree of blocks. +Whichever leaf of this tree is the deepest is called the head and the chain of blocks from the genesis block to this leave is the canonical chain. +The payloads of all the blocks in the canonical chain together define the current state stored in the blockchain. + +```mermaid +flowchart LR; + +subgraph Longest Chain Consensus +BCM[Blockchain Management \n Module] +Miner[Miner \n Module] +Synchronizer[Synchronizer \n Module] +Transport[gRPC Transport \n Module] +Broadcast[Broadcast \n Module] + +end + +Application[Application \n Module] + + +Broadcast <--> Transport -The system consists of the following core modules: +Miner <--> Application +Miner <--> Broadcast + +BCM <--> Miner +BCM <--> Application +BCM <--> Synchronizer + +Synchronizer <--> Transport +``` + +(Note that the event mangler module and the timer module were omitted from the diagram above for simplicity.) + +The nodes consists of the following core modules: - **Blockchain Management Module (BCM):** It forms the core of the system and is responsible for managing the blockchain. @@ -35,7 +71,7 @@ The system consists of the following core modules: Broadcasts newly mined blocks to all other nodes. It can also simulate network delay and dropped messages. -and the following supporting modules: +and the following supporting modules (provided by mir): - **gRPC Transport Module:** Used for communication between nodes. @@ -49,41 +85,134 @@ and the following supporting modules: Lastly, a user-implemented **Application Module** handles all business logic. In particular, it needs to compute the state of the blockchain, verify transactions and provide transactions to the miner. -The following drawing depicts the relationship between the different modules at a high level. +Next to the modules, it also includes an interceptor that intercepts all communication between different modules and allows for visualization/debugging tools to consume this communication via a websocket connection. +An example of how to use the information provided by the interception can be found [here](https://github.com/komplexon3/longest-chain-project). -**Note:** The event mangler module and the timer module were omitted for simplicity. +(TODO - fix link!) -```mermaid -flowchart LR; +## Operation -subgraph Longest Chain Consensus -BCM[Blockchain Management \n Module] -Miner[Miner \n Module] -Synchronizer[Synchronizer \n Module] -Transport[gRPC Transport \n Module] -Broadcast[Broadcast \n Module] +We will now walk though how the different modules interact with each other. +At the start, the application module must initialize the BCM by sending it an _InitBlockchain_ event which contains the initial state. +The BCM creates a genesis block with an empty payload and stores it together with the initial state. +It then instruct the miner module to start mining via a _NewHead_ event. +In order to start mining, the miner module request a payload (_PayloadRequest_) from the application module. +The miner now starts to mine and the initialization sequence of the node is completed. + +Now, the node will remain inactive (other than mining a block) until a new block has been mined. +This block can either be mined by this node's miner or by another node's miner. +In the first case, the miner will send the new block to its BCM and brodcast the block to all other nodes via the broadcast module (_NewBlock_ event). + +The BCM will then check whether or not it can connect this block to its tree of blocks. +If it cannot connect the block, we call it an orphan block. +Such cases can occur if the block's parent was mined by another node and the broadcast message of for this block has not (yet) reached this node. +The BCM will try to resolve these problems with the help of the synchronizer, which would coordinate with other nodes to get the missing blocks. +This procedure will be described below. + +The BCM now potentially has multiple blocks to add; the new block and possibly a chain of additional blocks from the synchronizer. +Since the blocks might not be trusted since they could have been mined from another node, we must verify it. +This happens in two ways: + +1. The BCM sends all the blocks together with some additional information to the application module (_VerifyChainRequest_). + The application then verifies that the payloads are valid. + This logic is application specific. +2. The BCM verifies that the nodes link together correctly. + +The BCM can now add all new blocks to the block tree. +If the canonical chain changes, i.e. there is a new head, it instructs the miner to start mining on the new head (_NewHead_ event). +Also, it informas the application about the new head (_HeadChange_ event). +This event contains some additional information about how the canonical chain changes. +For example, if an different branch of the tree is now the canonical chain, it also includes which blocks are no longer part of the canonical chain. +This allows for the application to resubmit these payloads if desired. +In any case, the application will compute the state corresponding to the new head and register it in the BCM (_RegisterCheckpoint_). -end +**Note**: In the sequence diagrams, the boxes with a dotted outline have a different meaning depending on the label in the top left corner: -Application[Application \n Module] +- loop: The sequence in the box repeats indefinitely or until the condition in the brackets holds. +- alt: The two boxes making up this box describe two alternative sequences. +- opt: The sequence in the box is optional. + It is performed if the condition in the boxes holds. +- par: The two boxes making up this box describe two sequences that are performed in parallel. +```mermaid +sequenceDiagram + Application ->> BCM: InitBlockchain -Broadcast <--> Transport + BCM ->> Miner: NewHead + Miner ->> Application: PayloadRequest + Application ->> Miner: PayloadResponse + Note over Miner: Start mining -Miner <--> Application -Miner <--> Broadcast + loop + alt receiving new block from another node + Broadcast ->> BCM: NewBlock + else this node's miner mined a new block + Miner ->> Broadcast: NewBlock + Miner ->> BCM: NewBlock + end -BCM <--> Miner -BCM <--> Application -BCM <--> Synchronizer + Note over BCM: Check that the block can be connected to the block tree + opt if it cannot the block to the blocktree + BCM ->> Synchronizer: SyncRequest + Note over Synchronizer: Get missing blocks from other nodes, details omitted + Synchronizer ->> BCM: NewChain + end + BCM ->> Application: VerifyChainRequest + Note over Application: Verify that payloads are valid + Application ->> BCM: VerifyChainRespose + Note over BCM: Add blocks of chain to block tree, verify that they link together -Synchronizer <--> Transport + opt if head changes + par + BCM ->> Miner: NewHead + Note over Miner: Abort current mining operation, prepare to mine on new head + Miner ->> Application: PayloadRequest + Application ->> Miner: PayloadResponse + Note over Miner: Start mining on new head + and + BCM ->> Application: HeadChange + Note over Application: Compute state for new head + Application ->> BCM: RegisterCheckpoint + end + end + end ``` -Further, it also includes an interceptor that intercepts all communication between different modules and allows for visualization/debugging tools to consume this communication via a websocket connection. -An example of how to use the information provided by the interception can be found [here](https://github.com/komplexon3/longest-chain-project). +The functionality of the synchronizer was already outlined above. +This part will go into more detail on how the synchronizer resolves orphan blocks. +Every _SyncRequest_ from the BCM contains the id of the orphan block and a collection of id of block that the BCM has in its tree. +With this information, the synchronizer asks one node after another to give it a chain which connects the orphan block to one of the known blocks (_ChainRequest_). +The other node's synchronizers then query their BCM via a _GetChainRequest_ for such a segment and forward the answer back to the initiator's synchronizer. +The responses can be unseuccessful. +It that case, the synchronizer asks the next node. +When a successful response is received, the synchronizer instructs the BCM to add the new chain to the block tree (_NewChain_ event). -(TODO - fix link!) +```mermaid +sequenceDiagram + +box Initiator Node +participant BCM (I) +participant Synchronizer (I) +end + +box Other Node(s) +participant Synchronizer +participant BCM +end + +Note over BCM (I): Cannot connect block +BCM (I) ->> Synchronizer (I): SyncRequest + +loop: until successful, one node at a time +Synchronizer (I) ->> Synchronizer: ChainRequest +Synchronizer ->> BCM: GetChainRequest +BCM ->> Synchronizer: GetChainResponse (successful/unsuccessful) +Synchronizer ->> Synchronizer (I): ChainResponse (successful/unsuccessful) +end +Synchronizer (I) ->> BCM (I): NewChain +``` + +## Modules ### Blockchain Management Module (BCM) @@ -108,8 +237,8 @@ The BCM must perform the following tasks: 3. Register checkpoints when receiving a RegisterCheckpoint event from the application module. 4. It must provide the synchronizer with chains when requested. This is to resolve orphan blocks in other nodes. -5. When the head changes, it sends a HeadChange event to the application module. This event contains all information necessary for the application to compute the state at the new head - as well as information about which payloads are now part of the canonical (i.e., longest) and which ones are no longer part of the canonical chain. +5. When the head changes, it sends a HeadChange event to the application module. + This event contains all information necessary for the application to compute the state at the new head as well as information about which payloads are now part of the canonical (i.e., longest) and which ones are no longer part of the canonical chain. ### Miner Module @@ -197,86 +326,6 @@ The interceptor proto defines two such events: - TreeUpdate: This event is sent by the blockchain manager (BCM) when the blockchain is updated. It contains all blocks in the blockchain and the id of the new head. - StateUpdate: This event is sent by the application when it computes the state for the newest head of the blockchain. -## Operation - -**Note**: In the sequence diagrams, the boxes with a dotted outline have a different meaning depending on the label in the top left corner: - -- loop: The sequence in the box repeats indefinitely or until the condition in the brackets holds. -- alt: The two boxes making up this box describe two alternative sequences. -- opt: The sequence in the box is optional. - It is performed if the condition in the boxes holds. -- par: The two boxes making up this box describe two sequences that are performed in parallel. - -```mermaid -sequenceDiagram - Application ->> BCM: InitBlockchain - - BCM ->> Miner: NewHead - Miner ->> Application: PayloadRequest - Application ->> Miner: PayloadResponse - Note over Miner: Start mining - - loop - alt receiving new block from another node - Broadcast ->> BCM: NewBlock - else this node's miner mined a new block - Miner ->> Broadcast: NewBlock - Miner ->> BCM: NewBlock - end - - Note over BCM: Check that the block can be connected to the block tree - opt if it cannot the block to the blocktree - BCM ->> Synchronizer: SyncRequest - Note over Synchronizer: Get missing blocks from other nodes, details omitted - Synchronizer ->> BCM: NewChain - end - BCM ->> Application: VerifyChainRequest - Note over Application: Verify that payloads are valid - Application ->> BCM: VerifyChainRespose - Note over BCM: Add blocks of chain to block tree, verify that they link together - - - opt if head changes - par - BCM ->> Miner: NewHead - Note over Miner: Abort current mining operation, prepare to mine on new head - Miner ->> Application: PayloadRequest - Application ->> Miner: PayloadResponse - Note over Miner: Start mining on new head - and - BCM ->> Application: HeadChange - Note over Application: Compute state for new head - Application ->> BCM: RegisterCheckpoint - end - end - end -``` - -```mermaid -sequenceDiagram - -box Initiator Node -participant BCM (I) -participant Synchronizer (I) -end - -box Other Node(s) -participant Synchronizer -participant BCM -end - -Note over BCM (I): Cannot connect block -BCM (I) ->> Synchronizer (I): SyncRequest - -loop: until successful, one node at a time -Synchronizer (I) ->> Synchronizer: ChainRequest -Synchronizer ->> BCM: GetChainRequest -BCM ->> Synchronizer: GetChainResponse (successful/unsuccessful) -Synchronizer ->> Synchronizer (I): ChainResponse (successful/unsuccessful) -end -Synchronizer (I) ->> BCM (I): NewChain -``` - ### Glossary - **Blocktree**: From dfdbe0f17b8f8d402afaeeaabba204c5612b3372 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sun, 4 Feb 2024 22:20:34 +0100 Subject: [PATCH 36/46] restructure --- {samples => pkg}/blockchain/README.md | 0 .../blockchain => pkg/blockchain/bcm}/bcm.go | 6 +- .../blockchain/broadcast}/broadcast.go | 4 +- .../blockchain/images/visualizer.png | Bin .../blockchain/images/visualizer_block.png | Bin .../blockchain/images/visualizers.png | Bin .../blockchain/miner}/miner.go | 4 +- .../blockchain/synchronizer}/synchronizer.go | 6 +- pkg/blockchain/system.go | 93 ++++++++++++++++++ {samples => pkg}/blockchain/utils/format.go | 0 {samples => pkg}/blockchain/utils/hash.go | 0 {samples => pkg}/blockchain/utils/slices.go | 0 .../blockchain/wsInterceptor/wsInterceptor.go | 2 +- .../wsInterceptor/wsServer}/wsServer.go | 0 .../application/application.go | 6 +- .../application/payloads/payloads.go | 0 .../{blockchain => blockchain-chat}/main.go | 90 ++++------------- .../{blockchain => blockchain-chat}/run.sh | 0 .../{blockchain => blockchain-chat}/test.sh | 0 19 files changed, 123 insertions(+), 88 deletions(-) rename {samples => pkg}/blockchain/README.md (100%) rename {samples/blockchain => pkg/blockchain/bcm}/bcm.go (99%) rename {samples/blockchain => pkg/blockchain/broadcast}/broadcast.go (96%) rename {samples => pkg}/blockchain/images/visualizer.png (100%) rename {samples => pkg}/blockchain/images/visualizer_block.png (100%) rename {samples => pkg}/blockchain/images/visualizers.png (100%) rename {samples/blockchain => pkg/blockchain/miner}/miner.go (98%) rename {samples/blockchain => pkg/blockchain/synchronizer}/synchronizer.go (99%) create mode 100644 pkg/blockchain/system.go rename {samples => pkg}/blockchain/utils/format.go (100%) rename {samples => pkg}/blockchain/utils/hash.go (100%) rename {samples => pkg}/blockchain/utils/slices.go (100%) rename samples/blockchain/wsinterceptor/wsinterceptor.go => pkg/blockchain/wsInterceptor/wsInterceptor.go (95%) rename {samples/blockchain/wsinterceptor/wsServer.go => pkg/blockchain/wsInterceptor/wsServer}/wsServer.go (100%) rename samples/{blockchain => blockchain-chat}/application/application.go (97%) rename samples/{blockchain => blockchain-chat}/application/payloads/payloads.go (100%) rename samples/{blockchain => blockchain-chat}/main.go (55%) rename samples/{blockchain => blockchain-chat}/run.sh (100%) rename samples/{blockchain => blockchain-chat}/test.sh (100%) diff --git a/samples/blockchain/README.md b/pkg/blockchain/README.md similarity index 100% rename from samples/blockchain/README.md rename to pkg/blockchain/README.md diff --git a/samples/blockchain/bcm.go b/pkg/blockchain/bcm/bcm.go similarity index 99% rename from samples/blockchain/bcm.go rename to pkg/blockchain/bcm/bcm.go index cbc27611a..170b2c4c3 100644 --- a/samples/blockchain/bcm.go +++ b/pkg/blockchain/bcm/bcm.go @@ -1,12 +1,11 @@ -// blockchain manager - -package main +package bcm import ( "errors" "slices" "time" + "github.com/filecoin-project/mir/pkg/blockchain/utils" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" @@ -19,7 +18,6 @@ import ( synchronizerpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/synchronizerpb/dsl" blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" t "github.com/filecoin-project/mir/pkg/types" - "github.com/filecoin-project/mir/samples/blockchain/utils" "golang.org/x/exp/maps" "google.golang.org/protobuf/types/known/timestamppb" ) diff --git a/samples/blockchain/broadcast.go b/pkg/blockchain/broadcast/broadcast.go similarity index 96% rename from samples/blockchain/broadcast.go rename to pkg/blockchain/broadcast/broadcast.go index 8d8e8d51c..c3ae00710 100644 --- a/samples/blockchain/broadcast.go +++ b/pkg/blockchain/broadcast/broadcast.go @@ -1,8 +1,9 @@ // handles all commmunication between nodes -package main +package broadcast import ( + "github.com/filecoin-project/mir/pkg/blockchain/utils" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" @@ -12,7 +13,6 @@ import ( blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" t "github.com/filecoin-project/mir/pkg/types" - "github.com/filecoin-project/mir/samples/blockchain/utils" ) /** diff --git a/samples/blockchain/images/visualizer.png b/pkg/blockchain/images/visualizer.png similarity index 100% rename from samples/blockchain/images/visualizer.png rename to pkg/blockchain/images/visualizer.png diff --git a/samples/blockchain/images/visualizer_block.png b/pkg/blockchain/images/visualizer_block.png similarity index 100% rename from samples/blockchain/images/visualizer_block.png rename to pkg/blockchain/images/visualizer_block.png diff --git a/samples/blockchain/images/visualizers.png b/pkg/blockchain/images/visualizers.png similarity index 100% rename from samples/blockchain/images/visualizers.png rename to pkg/blockchain/images/visualizers.png diff --git a/samples/blockchain/miner.go b/pkg/blockchain/miner/miner.go similarity index 98% rename from samples/blockchain/miner.go rename to pkg/blockchain/miner/miner.go index de19c890c..83fc30df1 100644 --- a/samples/blockchain/miner.go +++ b/pkg/blockchain/miner/miner.go @@ -1,10 +1,11 @@ -package main +package miner import ( "context" "math/rand" "time" + "github.com/filecoin-project/mir/pkg/blockchain/utils" "github.com/filecoin-project/mir/pkg/events" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" @@ -17,7 +18,6 @@ import ( blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" "github.com/filecoin-project/mir/pkg/pb/eventpb" t "github.com/filecoin-project/mir/pkg/types" - "github.com/filecoin-project/mir/samples/blockchain/utils" "github.com/go-errors/errors" "github.com/mitchellh/hashstructure" "google.golang.org/protobuf/types/known/timestamppb" diff --git a/samples/blockchain/synchronizer.go b/pkg/blockchain/synchronizer/synchronizer.go similarity index 99% rename from samples/blockchain/synchronizer.go rename to pkg/blockchain/synchronizer/synchronizer.go index 93d2a4e5c..7ebdae067 100644 --- a/samples/blockchain/synchronizer.go +++ b/pkg/blockchain/synchronizer/synchronizer.go @@ -1,12 +1,11 @@ -// blockchain manager - -package main +package synchronizer import ( "errors" "fmt" "math/rand" + "github.com/filecoin-project/mir/pkg/blockchain/utils" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" @@ -16,7 +15,6 @@ import ( blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" transportpbdsl "github.com/filecoin-project/mir/pkg/pb/transportpb/dsl" t "github.com/filecoin-project/mir/pkg/types" - "github.com/filecoin-project/mir/samples/blockchain/utils" ) /** diff --git a/pkg/blockchain/system.go b/pkg/blockchain/system.go new file mode 100644 index 000000000..779556e27 --- /dev/null +++ b/pkg/blockchain/system.go @@ -0,0 +1,93 @@ +package blockchain + +import ( + "math" + "strconv" + "time" + + "github.com/filecoin-project/mir/pkg/blockchain/bcm" + "github.com/filecoin-project/mir/pkg/blockchain/broadcast" + "github.com/filecoin-project/mir/pkg/blockchain/miner" + "github.com/filecoin-project/mir/pkg/blockchain/synchronizer" + "github.com/filecoin-project/mir/pkg/eventmangler" + "github.com/filecoin-project/mir/pkg/logging" + "github.com/filecoin-project/mir/pkg/modules" + "github.com/filecoin-project/mir/pkg/net/grpc" + trantorpbtypes "github.com/filecoin-project/mir/pkg/pb/trantorpb/types" + "github.com/filecoin-project/mir/pkg/timer" + t "github.com/filecoin-project/mir/pkg/types" +) + +type System struct { + modules modules.Modules +} + +func New( + ownId t.NodeID, + application modules.Module, + disableMangle bool, + dropRate float64, + minDelay float64, + maxDelay float64, + numberOfNodes int, + logger logging.Logger, + transportLogger logging.Logger, +) *System { + // determine "other" nodes for this node + nodes := make(map[t.NodeID]*trantorpbtypes.NodeIdentity, numberOfNodes) + allNodeIds := make([]t.NodeID, numberOfNodes) + otherNodes := make([]t.NodeID, numberOfNodes-1) + otherNodesIndex := 0 + for i := 0; i < numberOfNodes; i++ { + nodeIdStr := strconv.Itoa(i) + nodeId := t.NodeID(nodeIdStr) + allNodeIds[i] = nodeId + if nodeId != ownId { + otherNodes[otherNodesIndex] = nodeId + otherNodesIndex++ + } + nodes[nodeId] = &trantorpbtypes.NodeIdentity{Id: nodeId, Addr: "/ip4/127.0.0.1/tcp/1000" + nodeIdStr, Key: nil, Weight: "1"} + } + membership := &trantorpbtypes.Membership{Nodes: nodes} + + // Instantiate network transport module and establish connections. + transport, err := grpc.NewTransport(ownId, membership.Nodes[ownId].Addr, transportLogger) + if err != nil { + panic(err) + } + if err := transport.Start(); err != nil { + panic(err) + } + transport.Connect(membership) + + modules := modules.Modules{ + "transport": transport, + "bcm": bcm.NewBCM(logging.Decorate(logger, "BCM:\t")), + "miner": miner.NewMiner(ownId, 0.2, logging.Decorate(logger, "Miner:\t")), + "broadcast": broadcast.NewBroadcast(otherNodes, !disableMangle, logging.Decorate(logger, "Comm:\t")), + "synchronizer": synchronizer.NewSynchronizer(ownId, otherNodes, logging.Decorate(logger, "Sync:\t")), + "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor + "application": application, + } + + if !disableMangle { + minDelayDuration := time.Duration(int64(math.Round(minDelay * float64(time.Second)))) + maxDelayDuration := time.Duration(int64(math.Round(maxDelay * float64(time.Second)))) + manglerModule, err := eventmangler.NewModule( + eventmangler.ModuleConfig{Self: "mangler", Dest: "transport", Timer: "timer"}, + &eventmangler.ModuleParams{MinDelay: minDelayDuration, MaxDelay: maxDelayDuration, DropRate: float32(dropRate)}, + ) + if err != nil { + panic(err) + } + + modules["timer"] = timer.New() + modules["mangler"] = manglerModule + } + + return &System{modules: modules} +} + +func (s *System) Modules() modules.Modules { + return s.modules +} diff --git a/samples/blockchain/utils/format.go b/pkg/blockchain/utils/format.go similarity index 100% rename from samples/blockchain/utils/format.go rename to pkg/blockchain/utils/format.go diff --git a/samples/blockchain/utils/hash.go b/pkg/blockchain/utils/hash.go similarity index 100% rename from samples/blockchain/utils/hash.go rename to pkg/blockchain/utils/hash.go diff --git a/samples/blockchain/utils/slices.go b/pkg/blockchain/utils/slices.go similarity index 100% rename from samples/blockchain/utils/slices.go rename to pkg/blockchain/utils/slices.go diff --git a/samples/blockchain/wsinterceptor/wsinterceptor.go b/pkg/blockchain/wsInterceptor/wsInterceptor.go similarity index 95% rename from samples/blockchain/wsinterceptor/wsinterceptor.go rename to pkg/blockchain/wsInterceptor/wsInterceptor.go index c543c2328..28f717915 100644 --- a/samples/blockchain/wsinterceptor/wsinterceptor.go +++ b/pkg/blockchain/wsInterceptor/wsInterceptor.go @@ -1,10 +1,10 @@ package wsInterceptor import ( + "github.com/filecoin-project/mir/pkg/blockchain/wsInterceptor/wsServer" "github.com/filecoin-project/mir/pkg/events" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/pb/eventpb" - wsServer "github.com/filecoin-project/mir/samples/blockchain/wsinterceptor/wsServer.go" "google.golang.org/protobuf/proto" ) diff --git a/samples/blockchain/wsinterceptor/wsServer.go/wsServer.go b/pkg/blockchain/wsInterceptor/wsServer/wsServer.go similarity index 100% rename from samples/blockchain/wsinterceptor/wsServer.go/wsServer.go rename to pkg/blockchain/wsInterceptor/wsServer/wsServer.go diff --git a/samples/blockchain/application/application.go b/samples/blockchain-chat/application/application.go similarity index 97% rename from samples/blockchain/application/application.go rename to samples/blockchain-chat/application/application.go index 9038017ca..914ed9764 100644 --- a/samples/blockchain/application/application.go +++ b/samples/blockchain-chat/application/application.go @@ -6,6 +6,7 @@ import ( "fmt" "time" + "github.com/filecoin-project/mir/pkg/blockchain/utils" "github.com/filecoin-project/mir/pkg/dsl" "github.com/filecoin-project/mir/pkg/logging" "github.com/filecoin-project/mir/pkg/modules" @@ -16,8 +17,7 @@ import ( statepbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" t "github.com/filecoin-project/mir/pkg/types" - "github.com/filecoin-project/mir/samples/blockchain/application/payloads" - "github.com/filecoin-project/mir/samples/blockchain/utils" + "github.com/filecoin-project/mir/samples/blockchain-chat/application/payloads" "google.golang.org/protobuf/types/known/timestamppb" ) @@ -197,7 +197,7 @@ func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { return nil } -func NewApplication(logger logging.Logger, nodeID t.NodeID) modules.PassiveModule { +func NewApplication(nodeID t.NodeID, logger logging.Logger) modules.Module { m := dsl.NewModule("application") am := &ApplicationModule{ diff --git a/samples/blockchain/application/payloads/payloads.go b/samples/blockchain-chat/application/payloads/payloads.go similarity index 100% rename from samples/blockchain/application/payloads/payloads.go rename to samples/blockchain-chat/application/payloads/payloads.go diff --git a/samples/blockchain/main.go b/samples/blockchain-chat/main.go similarity index 55% rename from samples/blockchain/main.go rename to samples/blockchain-chat/main.go index 2cb369f73..c9083295e 100644 --- a/samples/blockchain/main.go +++ b/samples/blockchain-chat/main.go @@ -5,24 +5,18 @@ import ( "context" "flag" "fmt" - "math" "os" "strconv" - "time" "github.com/filecoin-project/mir" - "github.com/filecoin-project/mir/pkg/eventmangler" + "github.com/filecoin-project/mir/pkg/blockchain" + "github.com/filecoin-project/mir/pkg/blockchain/wsInterceptor" "github.com/filecoin-project/mir/pkg/events" "github.com/filecoin-project/mir/pkg/logging" - "github.com/filecoin-project/mir/pkg/modules" - "github.com/filecoin-project/mir/pkg/net/grpc" applicationpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/events" "github.com/filecoin-project/mir/pkg/pb/eventpb" - trantorpbtypes "github.com/filecoin-project/mir/pkg/pb/trantorpb/types" - "github.com/filecoin-project/mir/pkg/timer" t "github.com/filecoin-project/mir/pkg/types" - application "github.com/filecoin-project/mir/samples/blockchain/application" - "github.com/filecoin-project/mir/samples/blockchain/wsInterceptor" + "github.com/filecoin-project/mir/samples/blockchain-chat/application" ) // Flags @@ -87,65 +81,24 @@ func main() { // Setting up node fmt.Println("Starting longest chain consensus protocol...") - logger := logging.ConsoleDebugLogger - - // determine "other" nodes for this node - nodes := make(map[t.NodeID]*trantorpbtypes.NodeIdentity, *numberOfNodes) - allNodeIds := make([]t.NodeID, *numberOfNodes) - otherNodes := make([]t.NodeID, *numberOfNodes-1) - otherNodesIndex := 0 - for i := 0; i < *numberOfNodes; i++ { - nodeIdStr := strconv.Itoa(i) - nodeId := t.NodeID(nodeIdStr) - allNodeIds[i] = nodeId - if nodeId != ownNodeID { - otherNodes[otherNodesIndex] = nodeId - otherNodesIndex++ - } - nodes[nodeId] = &trantorpbtypes.NodeIdentity{Id: nodeId, Addr: "/ip4/127.0.0.1/tcp/1000" + nodeIdStr, Key: nil, Weight: "1"} - } - membership := &trantorpbtypes.Membership{Nodes: nodes} + logger := logging.ConsoleInfoLogger - // Instantiate network transport module and establish connections. - transport, err := grpc.NewTransport(ownNodeID, membership.Nodes[ownNodeID].Addr, logging.ConsoleInfoLogger) - if err != nil { - panic(err) - } - if err := transport.Start(); err != nil { - panic(err) - } - transport.Connect(membership) - - modules := map[t.ModuleID]modules.Module{ - "transport": transport, - "bcm": NewBCM(logging.Decorate(logger, "BCM:\t")), - "miner": NewMiner(ownNodeID, 0.2, logging.Decorate(logger, "Miner:\t")), - "broadcast": NewBroadcast(otherNodes, !*disableMangle, logging.Decorate(logger, "Comm:\t")), - "application": application.NewApplication(logging.Decorate(logger, "App:\t"), ownNodeID), - "synchronizer": NewSynchronizer(ownNodeID, otherNodes, logging.Decorate(logger, "Sync:\t")), - "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor - } - - if !*disableMangle { - minDelayDuration := time.Duration(int64(math.Round(*minDelay * float64(time.Second)))) - maxDelayDuration := time.Duration(int64(math.Round(*maxDelay * float64(time.Second)))) - manglerModule, err := eventmangler.NewModule( - eventmangler.ModuleConfig{Self: "mangler", Dest: "transport", Timer: "timer"}, - &eventmangler.ModuleParams{MinDelay: minDelayDuration, MaxDelay: maxDelayDuration, DropRate: float32(*dropRate)}, - ) - if err != nil { - panic(err) - } - - modules["timer"] = timer.New() - modules["mangler"] = manglerModule - } + system := blockchain.New( + ownNodeID, + application.NewApplication(ownNodeID, logging.Decorate(logger, "Application:\t")), + *disableMangle, + *dropRate, + *minDelay, + *maxDelay, + *numberOfNodes, + logger, + logging.Decorate(logging.ConsoleInfoLogger, "Transport:\t")) // Instantiate Mir node. node, err := mir.NewNode( ownNodeID, mir.DefaultNodeConfig(), - modules, + system.Modules(), wsInterceptor.NewWsInterceptor( func(e *eventpb.Event) bool { switch e.Type.(type) { @@ -170,14 +123,9 @@ func main() { }() fmt.Println("Mir node running.") - // block until nodeError receives an error - // fmt.Printf("timer started\n") - - // fmt.Printf("Mir node stopped: %v\n", <-nodeError) - - // ================================================================================ - // Read chat messages from stdin and submit them as transactions. - // ================================================================================ + // ========================================================== + // Read chat messages from stdin and submit them as payloads. + // ========================================================== scanner := bufio.NewScanner(os.Stdin) @@ -186,7 +134,6 @@ func main() { // Read chat message from stdin. for scanner.Scan() { - fmt.Println("Gimme more") // Submit the chat message as transaction payload to the mempool module. if err := node.InjectEvents(ctx, events.ListOf( applicationpbevents.MessageInput("application", scanner.Text()).Pb(), @@ -202,6 +149,5 @@ func main() { // Stop the node. node.Stop() - transport.Stop() fmt.Printf("Mir node stopped") } diff --git a/samples/blockchain/run.sh b/samples/blockchain-chat/run.sh similarity index 100% rename from samples/blockchain/run.sh rename to samples/blockchain-chat/run.sh diff --git a/samples/blockchain/test.sh b/samples/blockchain-chat/test.sh similarity index 100% rename from samples/blockchain/test.sh rename to samples/blockchain-chat/test.sh From 10c60c289ed8b72bdeef22583da7ef25313e1edd Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sun, 4 Feb 2024 22:38:24 +0100 Subject: [PATCH 37/46] cleanup, add exp mining factor option --- pkg/blockchain/system.go | 21 ++-- pkg/pb/blockchainpb/blockchainpb.pb.go | 148 +++++++------------------ pkg/pb/blockchainpb/types/types.mir.go | 49 +------- samples/blockchain-chat/main.go | 16 ++- 4 files changed, 64 insertions(+), 170 deletions(-) diff --git a/pkg/blockchain/system.go b/pkg/blockchain/system.go index 779556e27..0a7a408b7 100644 --- a/pkg/blockchain/system.go +++ b/pkg/blockchain/system.go @@ -23,15 +23,16 @@ type System struct { } func New( - ownId t.NodeID, - application modules.Module, - disableMangle bool, - dropRate float64, - minDelay float64, - maxDelay float64, - numberOfNodes int, - logger logging.Logger, - transportLogger logging.Logger, + ownId t.NodeID, // id of this node + application modules.Module, // application module to use - see samples/blockchain-chat/application for example + disableMangle bool, // whether to disable mangling of messages + dropRate float64, // the rate at which to drop messages + minDelay float64, // minimum delay by which to delay messages [seconds] + maxDelay float64, // maximum delay by which to delay messages [seconds] + exponentialMiningFactor float64, // factor for exponential distribution for random mining duration + numberOfNodes int, // number of nodes in the network + logger logging.Logger, // logger to be used by the system + transportLogger logging.Logger, // logger to be used by the transport exlusively ) *System { // determine "other" nodes for this node nodes := make(map[t.NodeID]*trantorpbtypes.NodeIdentity, numberOfNodes) @@ -63,7 +64,7 @@ func New( modules := modules.Modules{ "transport": transport, "bcm": bcm.NewBCM(logging.Decorate(logger, "BCM:\t")), - "miner": miner.NewMiner(ownId, 0.2, logging.Decorate(logger, "Miner:\t")), + "miner": miner.NewMiner(ownId, exponentialMiningFactor, logging.Decorate(logger, "Miner:\t")), "broadcast": broadcast.NewBroadcast(otherNodes, !disableMangle, logging.Decorate(logger, "Comm:\t")), "synchronizer": synchronizer.NewSynchronizer(ownId, otherNodes, logging.Decorate(logger, "Sync:\t")), "devnull": modules.NullPassive{}, // for messages that are actually destined for the interceptor diff --git a/pkg/pb/blockchainpb/blockchainpb.pb.go b/pkg/pb/blockchainpb/blockchainpb.pb.go index 78f1fd46a..a5d143b60 100644 --- a/pkg/pb/blockchainpb/blockchainpb.pb.go +++ b/pkg/pb/blockchainpb/blockchainpb.pb.go @@ -23,61 +23,6 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) -type Blocktree struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Blocks []*Block `protobuf:"bytes,1,rep,name=blocks,proto3" json:"blocks,omitempty"` - Leaves []uint64 `protobuf:"varint,2,rep,packed,name=leaves,proto3" json:"leaves,omitempty"` -} - -func (x *Blocktree) Reset() { - *x = Blocktree{} - if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[0] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) - } -} - -func (x *Blocktree) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*Blocktree) ProtoMessage() {} - -func (x *Blocktree) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[0] - 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 Blocktree.ProtoReflect.Descriptor instead. -func (*Blocktree) Descriptor() ([]byte, []int) { - return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{0} -} - -func (x *Blocktree) GetBlocks() []*Block { - if x != nil { - return x.Blocks - } - return nil -} - -func (x *Blocktree) GetLeaves() []uint64 { - if x != nil { - return x.Leaves - } - return nil -} - type Block struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -93,7 +38,7 @@ type Block struct { func (x *Block) Reset() { *x = Block{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[1] + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[0] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -106,7 +51,7 @@ func (x *Block) String() string { func (*Block) ProtoMessage() {} func (x *Block) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_blockchainpb_proto_msgTypes[1] + mi := &file_blockchainpb_blockchainpb_proto_msgTypes[0] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -119,7 +64,7 @@ func (x *Block) ProtoReflect() protoreflect.Message { // Deprecated: Use Block.ProtoReflect.Descriptor instead. func (*Block) Descriptor() ([]byte, []int) { - return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{1} + return file_blockchainpb_blockchainpb_proto_rawDescGZIP(), []int{0} } func (x *Block) GetBlockId() uint64 { @@ -169,33 +114,28 @@ var file_blockchainpb_blockchainpb_proto_rawDesc = []byte{ 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x56, 0x0a, 0x09, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x74, - 0x72, 0x65, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, - 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, - 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x04, - 0x52, 0x06, 0x6c, 0x65, 0x61, 0x76, 0x65, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x8d, - 0x02, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, - 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, - 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, - 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x38, 0x0a, - 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x4f, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x65, 0x72, - 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x34, 0x82, 0xa6, 0x1d, 0x30, 0x67, - 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, - 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, - 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x52, - 0x07, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x35, - 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, - 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, - 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x02, 0x0a, 0x05, 0x42, 0x6c, 0x6f, 0x63, 0x6b, + 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2a, 0x0a, 0x11, 0x70, + 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x2c, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, + 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x52, 0x07, 0x70, 0x61, + 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, + 0x4f, 0x0a, 0x08, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x34, 0x82, 0xa6, 0x1d, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, + 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, + 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, + 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x52, 0x07, 0x6d, 0x69, 0x6e, 0x65, 0x72, 0x49, 0x64, + 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x35, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, + 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -210,22 +150,20 @@ func file_blockchainpb_blockchainpb_proto_rawDescGZIP() []byte { return file_blockchainpb_blockchainpb_proto_rawDescData } -var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_blockchainpb_blockchainpb_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_blockchainpb_blockchainpb_proto_goTypes = []interface{}{ - (*Blocktree)(nil), // 0: blockchainpb.Blocktree - (*Block)(nil), // 1: blockchainpb.Block - (*payloadpb.Payload)(nil), // 2: payloadpb.Payload - (*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp + (*Block)(nil), // 0: blockchainpb.Block + (*payloadpb.Payload)(nil), // 1: payloadpb.Payload + (*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp } var file_blockchainpb_blockchainpb_proto_depIdxs = []int32{ - 1, // 0: blockchainpb.Blocktree.blocks:type_name -> blockchainpb.Block - 2, // 1: blockchainpb.Block.payload:type_name -> payloadpb.Payload - 3, // 2: blockchainpb.Block.timestamp:type_name -> google.protobuf.Timestamp - 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 + 1, // 0: blockchainpb.Block.payload:type_name -> payloadpb.Payload + 2, // 1: blockchainpb.Block.timestamp:type_name -> google.protobuf.Timestamp + 2, // [2:2] is the sub-list for method output_type + 2, // [2:2] is the sub-list for method input_type + 2, // [2:2] is the sub-list for extension type_name + 2, // [2:2] is the sub-list for extension extendee + 0, // [0:2] is the sub-list for field type_name } func init() { file_blockchainpb_blockchainpb_proto_init() } @@ -235,18 +173,6 @@ func file_blockchainpb_blockchainpb_proto_init() { } if !protoimpl.UnsafeEnabled { file_blockchainpb_blockchainpb_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*Blocktree); i { - case 0: - return &v.state - case 1: - return &v.sizeCache - case 2: - return &v.unknownFields - default: - return nil - } - } - file_blockchainpb_blockchainpb_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Block); i { case 0: return &v.state @@ -265,7 +191,7 @@ func file_blockchainpb_blockchainpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_blockchainpb_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 1, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/types/types.mir.go b/pkg/pb/blockchainpb/types/types.mir.go index f223d920f..30f1979a7 100644 --- a/pkg/pb/blockchainpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/types/types.mir.go @@ -4,56 +4,19 @@ package blockchainpbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - types "github.com/filecoin-project/mir/codegen/model/types" blockchainpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb" - types1 "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" - types2 "github.com/filecoin-project/mir/pkg/types" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + types1 "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" timestamppb "google.golang.org/protobuf/types/known/timestamppb" ) -type Blocktree struct { - Blocks []*Block - Leaves []uint64 -} - -func BlocktreeFromPb(pb *blockchainpb.Blocktree) *Blocktree { - if pb == nil { - return nil - } - return &Blocktree{ - Blocks: types.ConvertSlice(pb.Blocks, func(t *blockchainpb.Block) *Block { - return BlockFromPb(t) - }), - Leaves: pb.Leaves, - } -} - -func (m *Blocktree) Pb() *blockchainpb.Blocktree { - if m == nil { - return nil - } - pbMessage := &blockchainpb.Blocktree{} - { - pbMessage.Blocks = types.ConvertSlice(m.Blocks, func(t *Block) *blockchainpb.Block { - return (t).Pb() - }) - pbMessage.Leaves = m.Leaves - } - - return pbMessage -} - -func (*Blocktree) MirReflect() mirreflect.Type { - return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*blockchainpb.Blocktree]()} -} - type Block struct { BlockId uint64 PreviousBlockId uint64 - Payload *types1.Payload + Payload *types.Payload Timestamp *timestamppb.Timestamp - MinerId types2.NodeID + MinerId types1.NodeID } func BlockFromPb(pb *blockchainpb.Block) *Block { @@ -63,9 +26,9 @@ func BlockFromPb(pb *blockchainpb.Block) *Block { return &Block{ BlockId: pb.BlockId, PreviousBlockId: pb.PreviousBlockId, - Payload: types1.PayloadFromPb(pb.Payload), + Payload: types.PayloadFromPb(pb.Payload), Timestamp: pb.Timestamp, - MinerId: (types2.NodeID)(pb.MinerId), + MinerId: (types1.NodeID)(pb.MinerId), } } diff --git a/samples/blockchain-chat/main.go b/samples/blockchain-chat/main.go index c9083295e..46261e0f4 100644 --- a/samples/blockchain-chat/main.go +++ b/samples/blockchain-chat/main.go @@ -20,12 +20,15 @@ import ( ) // Flags -var disableMangle = flag.Bool("disableMangle", false, "Disable mangling of messages") -var dropRate = flag.Float64("dropRate", 0.05, "The rate at which to drop messages") -var minDelay = flag.Float64("minDelay", 0.001, "The minimum delay to add to messages [seconds]") -var maxDelay = flag.Float64("maxDelay", 1, "The minimum delay to add to messages [seconds]") -var numberOfNodes = flag.Int("numberOfNodes", -1, "The number of nodes in the network [1, inf] REQUIRED") -var nodeID = flag.Int("nodeID", -1, "The ID of the node [0, numberOfNodes-1] REQUIRED") +var ( + disableMangle = flag.Bool("disableMangle", false, "Disable mangling of messages") + dropRate = flag.Float64("dropRate", 0.05, "The rate at which to drop messages") + minDelay = flag.Float64("minDelay", 0.001, "The minimum delay by which to delay messages between nodes [seconds]") + maxDelay = flag.Float64("maxDelay", 1, "The maximum delay by which to delay messages between nodes [seconds]") + exponentialMiningFactor = flag.Float64("expMiningFactor", 0.2, "Factor for exponential distribution for random mining duration") + numberOfNodes = flag.Int("numberOfNodes", -1, "The number of nodes in the network [1, inf] REQUIRED") + nodeID = flag.Int("nodeID", -1, "The ID of the node [0, numberOfNodes-1] REQUIRED") +) func main() { // Parse command line flags @@ -90,6 +93,7 @@ func main() { *dropRate, *minDelay, *maxDelay, + *exponentialMiningFactor, *numberOfNodes, logger, logging.Decorate(logging.ConsoleInfoLogger, "Transport:\t")) From f7d1b5bba59156e6ec066c1e00720a62ac48fa29 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sun, 4 Feb 2024 23:47:58 +0100 Subject: [PATCH 38/46] documentation + cleanup --- pkg/blockchain/README.md | 112 ++++++++++++------ .../application/application.go | 4 +- samples/blockchain-chat/run.sh | 10 +- samples/blockchain-chat/test.sh | 10 +- 4 files changed, 85 insertions(+), 51 deletions(-) diff --git a/pkg/blockchain/README.md b/pkg/blockchain/README.md index 17952c22e..d2ef3e320 100644 --- a/pkg/blockchain/README.md +++ b/pkg/blockchain/README.md @@ -1,6 +1,6 @@ -# Longest-Chain Consensus +# Blockchain System -Longest-Chain Consensus is a modular implementation of a longest-chain consensus protocol modeling proof-of-work. +This "Blockchain System" is a modular implementation of a longest-chain consensus protocol modeling proof-of-work. The goal of this system is to provide a simple modular blockchain run on a fixed set of nodes. @@ -9,7 +9,7 @@ It provides the core elements with only the actual business logic to be implemen ## Overview We will first outline the different parts that make up the system. -How these elements interact with each other will be described in [Operation](#operation) and a more detailed description of every elements can be found in [Modules](#modules). +How these elements interact with each other will be described in [Operation](#operation) and a more detailed description of every element can be found in [Modules](#modules). Each node has a set of core modules running the blockchain and an application module that runs the business logic and provides certain functionality to the core modules. @@ -56,7 +56,7 @@ Synchronizer <--> Transport (Note that the event mangler module and the timer module were omitted from the diagram above for simplicity.) -The nodes consists of the following core modules: +The nodes consist of the following core modules: - **Blockchain Management Module (BCM):** It forms the core of the system and is responsible for managing the blockchain. @@ -92,21 +92,21 @@ An example of how to use the information provided by the interception can be fou ## Operation -We will now walk though how the different modules interact with each other. +We will now walk through how the different modules interact with each other. At the start, the application module must initialize the BCM by sending it an _InitBlockchain_ event which contains the initial state. The BCM creates a genesis block with an empty payload and stores it together with the initial state. -It then instruct the miner module to start mining via a _NewHead_ event. -In order to start mining, the miner module request a payload (_PayloadRequest_) from the application module. +It then instructs the miner module to start mining via a _NewHead_ event. +In order to start mining, the miner module requests a payload (_PayloadRequest_) from the application module. The miner now starts to mine and the initialization sequence of the node is completed. Now, the node will remain inactive (other than mining a block) until a new block has been mined. This block can either be mined by this node's miner or by another node's miner. -In the first case, the miner will send the new block to its BCM and brodcast the block to all other nodes via the broadcast module (_NewBlock_ event). +In the first case, the miner will send the new block to its BCM and broadcast the block to all other nodes via the broadcast module (_NewBlock_ event). The BCM will then check whether or not it can connect this block to its tree of blocks. If it cannot connect the block, we call it an orphan block. -Such cases can occur if the block's parent was mined by another node and the broadcast message of for this block has not (yet) reached this node. -The BCM will try to resolve these problems with the help of the synchronizer, which would coordinate with other nodes to get the missing blocks. +Such cases can occur if the block's parent was mined by another node and the broadcast message for this block has not (yet) reached this node. +The BCM will try to resolve these problems with the help of the synchronizer, which will coordinate with other nodes to get the missing blocks. This procedure will be described below. The BCM now potentially has multiple blocks to add; the new block and possibly a chain of additional blocks from the synchronizer. @@ -115,14 +115,14 @@ This happens in two ways: 1. The BCM sends all the blocks together with some additional information to the application module (_VerifyChainRequest_). The application then verifies that the payloads are valid. - This logic is application specific. + This logic is application-specific. 2. The BCM verifies that the nodes link together correctly. The BCM can now add all new blocks to the block tree. If the canonical chain changes, i.e. there is a new head, it instructs the miner to start mining on the new head (_NewHead_ event). -Also, it informas the application about the new head (_HeadChange_ event). +Also, it informs the application about the new head (_HeadChange_ event). This event contains some additional information about how the canonical chain changes. -For example, if an different branch of the tree is now the canonical chain, it also includes which blocks are no longer part of the canonical chain. +For example, if a different branch of the tree is now the canonical chain, it also includes which blocks are no longer part of the canonical chain. This allows for the application to resubmit these payloads if desired. In any case, the application will compute the state corresponding to the new head and register it in the BCM (_RegisterCheckpoint_). @@ -180,11 +180,11 @@ sequenceDiagram The functionality of the synchronizer was already outlined above. This part will go into more detail on how the synchronizer resolves orphan blocks. -Every _SyncRequest_ from the BCM contains the id of the orphan block and a collection of id of block that the BCM has in its tree. -With this information, the synchronizer asks one node after another to give it a chain which connects the orphan block to one of the known blocks (_ChainRequest_). +Every _SyncRequest_ from the BCM contains the id of the orphan block and a collection of id of the block that the BCM has in its tree. +With this information, the synchronizer asks one node after another to give it a chain that connects the orphan block to one of the known blocks (_ChainRequest_). The other node's synchronizers then query their BCM via a _GetChainRequest_ for such a segment and forward the answer back to the initiator's synchronizer. -The responses can be unseuccessful. -It that case, the synchronizer asks the next node. +The responses can be unsuccessful. +In that case, the synchronizer asks the next node. When a successful response is received, the synchronizer instructs the BCM to add the new chain to the block tree (_NewChain_ event). ```mermaid @@ -308,7 +308,7 @@ The application module must perform the following tasks: 4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at an application level and respond with a VerifyBlocksResponse event. Whether or not the blocks link together correctly is verified by the BCM. -An example of such an application is the (lcc-chat-app)[link] that is in the samples directory. +An example of such an application is the [Blockchain Chat App](../../samples/blockchain-chat/) that is in the samples directory. As the name implies, it implements a simple chat application. [Note - different end] @@ -326,45 +326,67 @@ The interceptor proto defines two such events: - TreeUpdate: This event is sent by the blockchain manager (BCM) when the blockchain is updated. It contains all blocks in the blockchain and the id of the new head. - StateUpdate: This event is sent by the application when it computes the state for the newest head of the blockchain. -### Glossary +## How to use -- **Blocktree**: - Tree of all blocks with each block logically connected to its predecessor. -- **Head**: - Last block of the canonical (longest) chain. +To use the blockchain system, you must first define your application's state and payloads in `statepb.proto` and `payloadpb.proto` files. -## How to use +Next, you must implement an application module that performs the following actions: + +1. Initialize the blockchain by sending it an initial state in an InitBlockchain event to the BCM. +2. When it receives a PayloadRequest event, it must provide a payload for the next block. This payload can be empty. +3. When it receives a HeadChange event, it must compute the state at the new head of the blockchain. + This state is then registered with the BCM by sending it a RegisterCheckpoint event. + A checkpoint is a block stored by the BCM that has a state stored with it. +4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at an application level and respond with a VerifyBlocksResponse event. + Whether or not the blocks link together correctly is verified by the BCM. -In order to use the longest-chain consensus system, ... [add after restucture]. +See the example chat app as a reference. ## Example: Chat App -An example of how to use the longest-chain consensus is the Chat App [...here....]. +An example of how to use the longest-chain consensus is the [Blockchain Chat App](../../samples/blockchain-chat/). Users can enter input through standard input line by line and the system replicates all messages in the same order across all nodes. It enforces that all messages sent from the same sender appear in the history in a monotonically increasing order of submission time. -[Add note: node id ~=~ sender id] +### Specification of the Chat App + +The following lists the key specifications of the chat app. -#### Payload +- **Payload:** + The payloads consist of a message, the sender id (id of the node from which the message was sent) and a submission timestamp. -In this application, the payloads consist of a message, the sender id (id of the sender node) and a submission timestamp. +- **State:** + The state consists of the message history and a collection of "last sent" timestamps, one for each sender/node, where the timestamp corresponds to the submission time of the last message of this sender. -#### State +- **Applying Blocks:** + When applying a block to a state, the message in the payload is appended to the message history and the "last sent" timestamp corresponding to the sender is updated. -The state consists of the message history and a collection of "last sent" timestamps, one for each sender/node, where the timestamp corresponds to the submission time of the last message of this sender. +- **Verifying Blocks:** + To verify a block, the application verifies that the submission timestamp -#### Applying Blocks +- **Providing Payloads:** + Each node's application keeps track of all messages that were submitted to it. + Additionally, if a fork happens and the branch changes, it -When applying a block to a state, the message in the payload is appended to the message history and the "last sent" timestamp corresponding to the sender is updated. +### Running the chat app -#### Verifying Blocks +To run the chat app, you can run the following in multiple terminals, once for each node + +``` +go run . -numberOfNodes -nodeID +``` -To verify a block, the application verifies that the submission timestamp +Further, you can add the following options to modify the characteristics of the system(s). +If they are not set, default values will be used -#### Providing Payloads +- **-disableMangle:** + Disables all mangling of the messages between the nodes. +- **-dropRate:** The rate at which to drop messages between nodes. (Ignored if _disableMangle_ is set.) +- **-minDelay:** The minimum delay by which to delay messages between nodes. (Ignored if _disableMangle_ is set.) +- **-maxDelay:** The maximum delay by which to delay messages between nodes. (Ignored if _disableMangle_ is set.) +- **-expMiningFactor:** Factor for exponential distribution for random mining duration. -Each node's application keeps track of all messages that were submitted to it. -Additionally, if a fork happens and the branch changes, it +If `tmux` is installed, you can simply run `./run.sh` to start a network of 4 nodes with reasonable options set. ### Visualization @@ -381,7 +403,19 @@ To more easily compare the trees, each block's background color is dependent on ![visualizer](./images/visualizers.png) -Every single blocks shows the following information: +Every single block displays the following information: + +- **Block ID** +- **Miner ID:** The id of the node that mined the block. +- **Sent Timestamp:** + The time at which the message was sent. + This information is part of the payload. + Not to be confused with the time at which the block was mined. +- **Payload Message**: + The message that is part of the payload. +- **Sender ID:** + The id of the node from which the message was sent. + This information is part of the payload. ![visualizer block](./images/visualizer_block.png) diff --git a/samples/blockchain-chat/application/application.go b/samples/blockchain-chat/application/application.go index 914ed9764..7863261a1 100644 --- a/samples/blockchain-chat/application/application.go +++ b/samples/blockchain-chat/application/application.go @@ -36,8 +36,8 @@ import ( * 3. When it receives a HeadChange event, it must compute the state at the new head of the blockchain. * This state is then registered with the BCM by sending it a RegisterCheckpoint event. * A checkpoint is a block stored by the BCM that has a state stored with it. - * 4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at a application level and respond with a VerifyBlocksResponse event. - * Whether or not not the blocks link together correctly is verified by the BCM. + * 4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at an application level and respond with a VerifyBlocksResponse event. + * Whether or not the blocks link together correctly is verified by the BCM. * * This application module implements a simple chat application. * It takes new messages from the user (MessageInput event) and combines them with a sender id and "sent" timestamp as payloads. diff --git a/samples/blockchain-chat/run.sh b/samples/blockchain-chat/run.sh index 6feb9f20b..c84b5762b 100755 --- a/samples/blockchain-chat/run.sh +++ b/samples/blockchain-chat/run.sh @@ -1,6 +1,6 @@ #!/bin/bash -CONFIG="-dropRate 0.05 -minDelay 0.02 -maxDelay 1" +CONFIG="-numberOfNodes 4 -dropRate 0.05 -minDelay 0.01 -maxDelay 1" NODE_0_LOG="./node_0.log" NODE_1_LOG="./node_1.log" @@ -16,10 +16,10 @@ tmux new-session -d -s demo \; \ split-window -t demo:0.0 -h \; \ split-window -t "demo:0.2" -h \; \ \ - send-keys -t "demo:0.0" "go run . -numberOfNodes 4 -nodeID 0 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ - send-keys -t "demo:0.1" "go run . -numberOfNodes 4 -nodeID 1 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ - send-keys -t "demo:0.2" "go run . -numberOfNodes 4 -nodeID 2 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ - send-keys -t "demo:0.3" "go run . -numberOfNodes 4 -nodeID 3 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.0" "go run . -nodeID 0 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.1" "go run . -nodeID 1 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.2" "go run . -nodeID 2 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.3" "go run . -nodeID 3 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ attach-session -t "demo:0.0" #!/usr/bin/env bash set -e diff --git a/samples/blockchain-chat/test.sh b/samples/blockchain-chat/test.sh index d567f6558..9cb4337c4 100755 --- a/samples/blockchain-chat/test.sh +++ b/samples/blockchain-chat/test.sh @@ -1,6 +1,6 @@ #!/bin/bash -CONFIG="-dropRate 0.05 -minDelay 0.01 -maxDelay 1" +CONFIG="-numberOfNodes 4 -dropRate 0.05 -minDelay 0.01 -maxDelay 1" NODE_0_LOG="./node_0.log" NODE_1_LOG="./node_1.log" @@ -16,10 +16,10 @@ tmux new-session -d -s demo \; \ split-window -t demo:0.0 -h \; \ split-window -t "demo:0.2" -h \; \ \ - send-keys -t "demo:0.0" "go run . -numberOfNodes 4 -nodeID 0 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ - send-keys -t "demo:0.1" "go run . -numberOfNodes 4 -nodeID 1 $CONFIG 2>&1 | tee \"$NODE_1_LOG\"" Enter \; \ - send-keys -t "demo:0.2" "go run . -numberOfNodes 4 -nodeID 2 $CONFIG 2>&1 | tee \"$NODE_2_LOG\"" Enter \; \ - send-keys -t "demo:0.3" "go run . -numberOfNodes 4 -nodeID 3 $CONFIG 2>&1 | tee \"$NODE_3_LOG\"" Enter \; + send-keys -t "demo:0.0" "go run . -nodeID 0 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.1" "go run . -nodeID 1 $CONFIG 2>&1 | tee \"$NODE_1_LOG\"" Enter \; \ + send-keys -t "demo:0.2" "go run . -nodeID 2 $CONFIG 2>&1 | tee \"$NODE_2_LOG\"" Enter \; \ + send-keys -t "demo:0.3" "go run . -nodeID 3 $CONFIG 2>&1 | tee \"$NODE_3_LOG\"" Enter \; sleep 5 # wait for it to start up for i in {1..15}; do sleep 1; From ab9468fd9262a0b0a64f13e7cb1c92be38a30d9d Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Mon, 5 Feb 2024 10:20:12 +0100 Subject: [PATCH 39/46] add link to visualizer docs --- pkg/blockchain/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/pkg/blockchain/README.md b/pkg/blockchain/README.md index d2ef3e320..e9b18bbf9 100644 --- a/pkg/blockchain/README.md +++ b/pkg/blockchain/README.md @@ -6,6 +6,10 @@ The goal of this system is to provide a simple modular blockchain run on a fixed It provides the core elements with only the actual business logic to be implemented by an application module. +> #### Important +> +> This system is only intended for **demonstration purposes**. + ## Overview We will first outline the different parts that make up the system. @@ -86,7 +90,7 @@ Lastly, a user-implemented **Application Module** handles all business logic. In particular, it needs to compute the state of the blockchain, verify transactions and provide transactions to the miner. Next to the modules, it also includes an interceptor that intercepts all communication between different modules and allows for visualization/debugging tools to consume this communication via a websocket connection. -An example of how to use the information provided by the interception can be found [here](https://github.com/komplexon3/longest-chain-project). +An example of how to use the information provided by the interception can be found [here](https://github.com/komplexon3/longest-chain-project/tree/main/chain-visualizer). (TODO - fix link!) @@ -99,7 +103,7 @@ It then instructs the miner module to start mining via a _NewHead_ event. In order to start mining, the miner module requests a payload (_PayloadRequest_) from the application module. The miner now starts to mine and the initialization sequence of the node is completed. -Now, the node will remain inactive (other than mining a block) until a new block has been mined. +After this, the node will remain inactive (other than mining a block) until a new block has been mined. This block can either be mined by this node's miner or by another node's miner. In the first case, the miner will send the new block to its BCM and broadcast the block to all other nodes via the broadcast module (_NewBlock_ event). From ca58c7a2ee7f2d062f4a026c4cd2dbc0df3aac27 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Mon, 5 Feb 2024 10:45:07 +0100 Subject: [PATCH 40/46] add getchaintohead for state comp at any time --- pkg/blockchain/README.md | 10 + pkg/blockchain/bcm/bcm.go | 15 + pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go | 351 ++++++++++++++---- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go | 2 + pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go | 8 + pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go | 12 + .../blockchainpb/bcmpb/events/events.mir.go | 31 ++ .../bcmpb/oneof_interfaces.mir.go | 8 + pkg/pb/blockchainpb/bcmpb/types/types.mir.go | 119 ++++++ protos/blockchainpb/bcmpb/bcmpb.proto | 15 + 10 files changed, 490 insertions(+), 81 deletions(-) diff --git a/pkg/blockchain/README.md b/pkg/blockchain/README.md index e9b18bbf9..220f1c042 100644 --- a/pkg/blockchain/README.md +++ b/pkg/blockchain/README.md @@ -216,6 +216,16 @@ end Synchronizer (I) ->> BCM (I): NewChain ``` +Also, at any point in time, the application can get the current state at the head of the blockchain by sending a _GetChainToHeadRequest_ to the BCM. +The response to this will include a chain of blocks from a checkpoint to the current head and the state associated with the checkpoint. +Using this information, the application can compute the current state. + +```mermaid +sequenceDiagram +Application ->> BCM: GetChainToHeadRequest +BCM ->> Application: GetChainToHeadResponse +``` + ## Modules ### Blockchain Management Module (BCM) diff --git a/pkg/blockchain/bcm/bcm.go b/pkg/blockchain/bcm/bcm.go index 170b2c4c3..b689a04e4 100644 --- a/pkg/blockchain/bcm/bcm.go +++ b/pkg/blockchain/bcm/bcm.go @@ -490,6 +490,14 @@ func (bcm *bcmModule) handleGetChainRequest(requestID string, sourceModule t.Mod } +func (bcm *bcmModule) handleGetChainToHeadRequest(sourceModule t.ModuleID) error { + bcm.logger.Log(logging.LevelInfo, "Received get chain to head request", "sourceModule", sourceModule) + chain, checkpontState := bcm.getChainFromCheckpointToBlock(bcm.head) + bcmpbdsl.GetChainToHeadResponse(*bcm.m, sourceModule, chain, checkpontState) + return nil + +} + func (bcm *bcmModule) handleInitBlockchain(initialState *statepbtypes.State) error { // initialize blockchain @@ -574,6 +582,13 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { return bcm.handleGetChainRequest(requestID, sourceModule, endBlockId, sourceBlockIds) }) + bcmpbdsl.UponGetChainToHeadRequest(m, func(sourceModule t.ModuleID) error { + if err := bcm.checkInitialization(); err != nil { + return err + } + return bcm.handleGetChainToHeadRequest(sourceModule) + }) + bcmpbdsl.UponRegisterCheckpoint(m, bcm.handleRegisterCheckpoint) bcmpbdsl.UponInitBlockchain(m, bcm.handleInitBlockchain) diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go index af4e36179..4d01e24c6 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go @@ -35,6 +35,8 @@ type Event struct { // *Event_GetChainRequest // *Event_GetChainResponse // *Event_RegisterCheckpoint + // *Event_GetChainToHeadRequest + // *Event_GetChainToHeadResponse // *Event_InitBlockchain Type isEvent_Type `protobuf_oneof:"type"` } @@ -113,6 +115,20 @@ func (x *Event) GetRegisterCheckpoint() *RegisterCheckpoint { return nil } +func (x *Event) GetGetChainToHeadRequest() *GetChainToHeadRequest { + if x, ok := x.GetType().(*Event_GetChainToHeadRequest); ok { + return x.GetChainToHeadRequest + } + return nil +} + +func (x *Event) GetGetChainToHeadResponse() *GetChainToHeadResponse { + if x, ok := x.GetType().(*Event_GetChainToHeadResponse); ok { + return x.GetChainToHeadResponse + } + return nil +} + func (x *Event) GetInitBlockchain() *InitBlockchain { if x, ok := x.GetType().(*Event_InitBlockchain); ok { return x.InitBlockchain @@ -144,6 +160,14 @@ type Event_RegisterCheckpoint struct { RegisterCheckpoint *RegisterCheckpoint `protobuf:"bytes,7,opt,name=register_checkpoint,json=registerCheckpoint,proto3,oneof"` } +type Event_GetChainToHeadRequest struct { + GetChainToHeadRequest *GetChainToHeadRequest `protobuf:"bytes,8,opt,name=get_chain_to_head_request,json=getChainToHeadRequest,proto3,oneof"` +} + +type Event_GetChainToHeadResponse struct { + GetChainToHeadResponse *GetChainToHeadResponse `protobuf:"bytes,9,opt,name=get_chain_to_head_response,json=getChainToHeadResponse,proto3,oneof"` +} + type Event_InitBlockchain struct { InitBlockchain *InitBlockchain `protobuf:"bytes,100,opt,name=init_blockchain,json=initBlockchain,proto3,oneof"` } @@ -158,6 +182,10 @@ func (*Event_GetChainResponse) isEvent_Type() {} func (*Event_RegisterCheckpoint) isEvent_Type() {} +func (*Event_GetChainToHeadRequest) isEvent_Type() {} + +func (*Event_GetChainToHeadResponse) isEvent_Type() {} + func (*Event_InitBlockchain) isEvent_Type() {} type NewBlock struct { @@ -389,6 +417,108 @@ func (x *GetChainResponse) GetChain() []*blockchainpb.Block { return nil } +type GetChainToHeadRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + SourceModule string `protobuf:"bytes,1,opt,name=source_module,json=sourceModule,proto3" json:"source_module,omitempty"` +} + +func (x *GetChainToHeadRequest) Reset() { + *x = GetChainToHeadRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetChainToHeadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetChainToHeadRequest) ProtoMessage() {} + +func (x *GetChainToHeadRequest) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] + 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 GetChainToHeadRequest.ProtoReflect.Descriptor instead. +func (*GetChainToHeadRequest) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{5} +} + +func (x *GetChainToHeadRequest) GetSourceModule() string { + if x != nil { + return x.SourceModule + } + return "" +} + +type GetChainToHeadResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Chain []*blockchainpb.Block `protobuf:"bytes,1,rep,name=chain,proto3" json:"chain,omitempty"` // sorted from oldest to youngest + CheckpointState *statepb.State `protobuf:"bytes,2,opt,name=checkpointState,proto3" json:"checkpointState,omitempty"` +} + +func (x *GetChainToHeadResponse) Reset() { + *x = GetChainToHeadResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *GetChainToHeadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetChainToHeadResponse) ProtoMessage() {} + +func (x *GetChainToHeadResponse) ProtoReflect() protoreflect.Message { + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] + 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 GetChainToHeadResponse.ProtoReflect.Descriptor instead. +func (*GetChainToHeadResponse) Descriptor() ([]byte, []int) { + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{6} +} + +func (x *GetChainToHeadResponse) GetChain() []*blockchainpb.Block { + if x != nil { + return x.Chain + } + return nil +} + +func (x *GetChainToHeadResponse) GetCheckpointState() *statepb.State { + if x != nil { + return x.CheckpointState + } + return nil +} + type RegisterCheckpoint struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -401,7 +531,7 @@ type RegisterCheckpoint struct { func (x *RegisterCheckpoint) Reset() { *x = RegisterCheckpoint{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -414,7 +544,7 @@ func (x *RegisterCheckpoint) String() string { func (*RegisterCheckpoint) ProtoMessage() {} func (x *RegisterCheckpoint) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -427,7 +557,7 @@ func (x *RegisterCheckpoint) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterCheckpoint.ProtoReflect.Descriptor instead. func (*RegisterCheckpoint) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{5} + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{7} } func (x *RegisterCheckpoint) GetBlockId() uint64 { @@ -455,7 +585,7 @@ type InitBlockchain struct { func (x *InitBlockchain) Reset() { *x = InitBlockchain{} if protoimpl.UnsafeEnabled { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -468,7 +598,7 @@ func (x *InitBlockchain) String() string { func (*InitBlockchain) ProtoMessage() {} func (x *InitBlockchain) ProtoReflect() protoreflect.Message { - mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6] + mi := &file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -481,7 +611,7 @@ func (x *InitBlockchain) ProtoReflect() protoreflect.Message { // Deprecated: Use InitBlockchain.ProtoReflect.Descriptor instead. func (*InitBlockchain) Descriptor() ([]byte, []int) { - return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{6} + return file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP(), []int{8} } func (x *InitBlockchain) GetInitialState() *statepb.State { @@ -502,7 +632,7 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, - 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x9a, 0x03, 0x0a, 0x05, 0x45, + 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd1, 0x04, 0x0a, 0x05, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x12, 0x2e, 0x0a, 0x09, 0x6e, 0x65, 0x77, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0f, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x48, 0x00, 0x52, 0x08, 0x6e, 0x65, 0x77, 0x42, @@ -523,57 +653,84 @@ var file_blockchainpb_bcmpb_bcmpb_proto_rawDesc = []byte{ 0x32, 0x19, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x48, 0x00, 0x52, 0x12, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x12, 0x40, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, - 0x68, 0x61, 0x69, 0x6e, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x63, 0x6d, - 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, - 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, - 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, - 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, - 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, - 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3d, 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x74, 0x12, 0x58, 0x0a, 0x19, 0x67, 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x74, + 0x6f, 0x5f, 0x68, 0x65, 0x61, 0x64, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, + 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x48, 0x00, 0x52, 0x15, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x6f, + 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x5b, 0x0a, 0x1a, 0x67, + 0x65, 0x74, 0x5f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x5f, 0x74, 0x6f, 0x5f, 0x68, 0x65, 0x61, 0x64, + 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1d, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, + 0x54, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x48, 0x00, + 0x52, 0x16, 0x67, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x6f, 0x48, 0x65, 0x61, 0x64, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x40, 0x0a, 0x0f, 0x69, 0x6e, 0x69, 0x74, + 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x64, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x15, 0x2e, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x2e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x69, 0x6e, 0x69, 0x74, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x90, 0xa6, 0x1d, 0x01, + 0x42, 0x0c, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x22, 0x3b, + 0x0a, 0x08, 0x4e, 0x65, 0x77, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x12, 0x29, 0x0a, 0x05, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, + 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x3d, 0x0a, 0x08, 0x4e, + 0x65, 0x77, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x2b, 0x0a, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, + 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, + 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, + 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, + 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, + 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x6e, + 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, + 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, + 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x6c, + 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x73, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, + 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, + 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, + 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7a, 0x0a, 0x15, 0x47, 0x65, + 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, 0x54, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x6d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, 0xa6, 0x1d, 0x32, + 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, + 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, + 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x83, 0x01, 0x0a, 0x16, 0x47, 0x65, 0x74, 0x43, 0x68, + 0x61, 0x69, 0x6e, 0x54, 0x6f, 0x48, 0x65, 0x61, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2e, - 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x06, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x73, 0x3a, 0x04, 0x98, - 0xa6, 0x1d, 0x01, 0x22, 0xdf, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, 0x6e, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x5b, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x36, 0x82, - 0xa6, 0x1d, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, - 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, - 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x49, 0x44, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x4d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x12, 0x20, 0x0a, 0x0c, 0x65, 0x6e, 0x64, 0x5f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, - 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x0a, 0x65, 0x6e, 0x64, 0x42, 0x6c, - 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, - 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x04, 0x52, - 0x0e, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x73, 0x3a, - 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x7c, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x43, 0x68, 0x61, 0x69, - 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x75, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x12, 0x29, 0x0a, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x18, 0x03, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x13, 0x2e, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, - 0x2e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x3a, 0x04, 0x98, - 0xa6, 0x1d, 0x01, 0x22, 0x5b, 0x0a, 0x12, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, - 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, - 0x63, 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, - 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, - 0x22, 0x4b, 0x0a, 0x0e, 0x49, 0x6e, 0x69, 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x0d, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, - 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, - 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0c, 0x69, 0x6e, 0x69, 0x74, 0x69, - 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, - 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, - 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, - 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, - 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x63, 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x33, + 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x52, 0x05, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x38, 0x0a, 0x0f, + 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x0f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x53, 0x74, 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x5b, 0x0a, 0x12, + 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x6f, 0x69, + 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x5f, 0x69, 0x64, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x49, 0x64, 0x12, 0x24, 0x0a, + 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, + 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x52, 0x05, 0x73, 0x74, + 0x61, 0x74, 0x65, 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x22, 0x4b, 0x0a, 0x0e, 0x49, 0x6e, 0x69, + 0x74, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x12, 0x33, 0x0a, 0x0d, 0x69, + 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, + 0x74, 0x65, 0x52, 0x0c, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x6c, 0x53, 0x74, 0x61, 0x74, 0x65, + 0x3a, 0x04, 0x98, 0xa6, 0x1d, 0x01, 0x42, 0x3b, 0x5a, 0x39, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, + 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, + 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x62, 0x63, + 0x6d, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -588,35 +745,41 @@ func file_blockchainpb_bcmpb_bcmpb_proto_rawDescGZIP() []byte { return file_blockchainpb_bcmpb_bcmpb_proto_rawDescData } -var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 7) +var file_blockchainpb_bcmpb_bcmpb_proto_msgTypes = make([]protoimpl.MessageInfo, 9) var file_blockchainpb_bcmpb_bcmpb_proto_goTypes = []interface{}{ - (*Event)(nil), // 0: bcmpb.Event - (*NewBlock)(nil), // 1: bcmpb.NewBlock - (*NewChain)(nil), // 2: bcmpb.NewChain - (*GetChainRequest)(nil), // 3: bcmpb.GetChainRequest - (*GetChainResponse)(nil), // 4: bcmpb.GetChainResponse - (*RegisterCheckpoint)(nil), // 5: bcmpb.RegisterCheckpoint - (*InitBlockchain)(nil), // 6: bcmpb.InitBlockchain - (*blockchainpb.Block)(nil), // 7: blockchainpb.Block - (*statepb.State)(nil), // 8: statepb.State + (*Event)(nil), // 0: bcmpb.Event + (*NewBlock)(nil), // 1: bcmpb.NewBlock + (*NewChain)(nil), // 2: bcmpb.NewChain + (*GetChainRequest)(nil), // 3: bcmpb.GetChainRequest + (*GetChainResponse)(nil), // 4: bcmpb.GetChainResponse + (*GetChainToHeadRequest)(nil), // 5: bcmpb.GetChainToHeadRequest + (*GetChainToHeadResponse)(nil), // 6: bcmpb.GetChainToHeadResponse + (*RegisterCheckpoint)(nil), // 7: bcmpb.RegisterCheckpoint + (*InitBlockchain)(nil), // 8: bcmpb.InitBlockchain + (*blockchainpb.Block)(nil), // 9: blockchainpb.Block + (*statepb.State)(nil), // 10: statepb.State } var file_blockchainpb_bcmpb_bcmpb_proto_depIdxs = []int32{ 1, // 0: bcmpb.Event.new_block:type_name -> bcmpb.NewBlock 2, // 1: bcmpb.Event.new_chain:type_name -> bcmpb.NewChain 3, // 2: bcmpb.Event.get_chain_request:type_name -> bcmpb.GetChainRequest 4, // 3: bcmpb.Event.get_chain_response:type_name -> bcmpb.GetChainResponse - 5, // 4: bcmpb.Event.register_checkpoint:type_name -> bcmpb.RegisterCheckpoint - 6, // 5: bcmpb.Event.init_blockchain:type_name -> bcmpb.InitBlockchain - 7, // 6: bcmpb.NewBlock.block:type_name -> blockchainpb.Block - 7, // 7: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block - 7, // 8: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block - 8, // 9: bcmpb.RegisterCheckpoint.state:type_name -> statepb.State - 8, // 10: bcmpb.InitBlockchain.initial_state:type_name -> statepb.State - 11, // [11:11] is the sub-list for method output_type - 11, // [11:11] is the sub-list for method input_type - 11, // [11:11] is the sub-list for extension type_name - 11, // [11:11] is the sub-list for extension extendee - 0, // [0:11] is the sub-list for field type_name + 7, // 4: bcmpb.Event.register_checkpoint:type_name -> bcmpb.RegisterCheckpoint + 5, // 5: bcmpb.Event.get_chain_to_head_request:type_name -> bcmpb.GetChainToHeadRequest + 6, // 6: bcmpb.Event.get_chain_to_head_response:type_name -> bcmpb.GetChainToHeadResponse + 8, // 7: bcmpb.Event.init_blockchain:type_name -> bcmpb.InitBlockchain + 9, // 8: bcmpb.NewBlock.block:type_name -> blockchainpb.Block + 9, // 9: bcmpb.NewChain.blocks:type_name -> blockchainpb.Block + 9, // 10: bcmpb.GetChainResponse.chain:type_name -> blockchainpb.Block + 9, // 11: bcmpb.GetChainToHeadResponse.chain:type_name -> blockchainpb.Block + 10, // 12: bcmpb.GetChainToHeadResponse.checkpointState:type_name -> statepb.State + 10, // 13: bcmpb.RegisterCheckpoint.state:type_name -> statepb.State + 10, // 14: bcmpb.InitBlockchain.initial_state:type_name -> statepb.State + 15, // [15:15] is the sub-list for method output_type + 15, // [15:15] is the sub-list for method input_type + 15, // [15:15] is the sub-list for extension type_name + 15, // [15:15] is the sub-list for extension extendee + 0, // [0:15] is the sub-list for field type_name } func init() { file_blockchainpb_bcmpb_bcmpb_proto_init() } @@ -686,7 +849,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { } } file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { - switch v := v.(*RegisterCheckpoint); i { + switch v := v.(*GetChainToHeadRequest); i { case 0: return &v.state case 1: @@ -698,6 +861,30 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { } } file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*GetChainToHeadResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegisterCheckpoint); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_blockchainpb_bcmpb_bcmpb_proto_msgTypes[8].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*InitBlockchain); i { case 0: return &v.state @@ -716,6 +903,8 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { (*Event_GetChainRequest)(nil), (*Event_GetChainResponse)(nil), (*Event_RegisterCheckpoint)(nil), + (*Event_GetChainToHeadRequest)(nil), + (*Event_GetChainToHeadResponse)(nil), (*Event_InitBlockchain)(nil), } type x struct{} @@ -724,7 +913,7 @@ func file_blockchainpb_bcmpb_bcmpb_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_blockchainpb_bcmpb_bcmpb_proto_rawDesc, NumEnums: 0, - NumMessages: 7, + NumMessages: 9, NumExtensions: 0, NumServices: 0, }, diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go index fa04aa9f4..664eb50ce 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.mir.go @@ -13,6 +13,8 @@ func (*Event) ReflectTypeOptions() []reflect.Type { reflect.TypeOf((*Event_GetChainRequest)(nil)), reflect.TypeOf((*Event_GetChainResponse)(nil)), reflect.TypeOf((*Event_RegisterCheckpoint)(nil)), + reflect.TypeOf((*Event_GetChainToHeadRequest)(nil)), + reflect.TypeOf((*Event_GetChainToHeadResponse)(nil)), reflect.TypeOf((*Event_InitBlockchain)(nil)), } } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go index 21c82a57c..0366cd2ca 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/emit.mir.go @@ -32,6 +32,14 @@ func RegisterCheckpoint(m dsl.Module, destModule types.ModuleID, blockId uint64, dsl.EmitMirEvent(m, events.RegisterCheckpoint(destModule, blockId, state)) } +func GetChainToHeadRequest(m dsl.Module, destModule types.ModuleID, sourceModule types.ModuleID) { + dsl.EmitMirEvent(m, events.GetChainToHeadRequest(destModule, sourceModule)) +} + +func GetChainToHeadResponse(m dsl.Module, destModule types.ModuleID, chain []*types1.Block, checkpointState *types2.State) { + dsl.EmitMirEvent(m, events.GetChainToHeadResponse(destModule, chain, checkpointState)) +} + func InitBlockchain(m dsl.Module, destModule types.ModuleID, initialState *types2.State) { dsl.EmitMirEvent(m, events.InitBlockchain(destModule, initialState)) } diff --git a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go index 611e97369..5e65c6a02 100644 --- a/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/dsl/upon.mir.go @@ -54,6 +54,18 @@ func UponRegisterCheckpoint(m dsl.Module, handler func(blockId uint64, state *ty }) } +func UponGetChainToHeadRequest(m dsl.Module, handler func(sourceModule types3.ModuleID) error) { + UponEvent[*types.Event_GetChainToHeadRequest](m, func(ev *types.GetChainToHeadRequest) error { + return handler(ev.SourceModule) + }) +} + +func UponGetChainToHeadResponse(m dsl.Module, handler func(chain []*types2.Block, checkpointState *types4.State) error) { + UponEvent[*types.Event_GetChainToHeadResponse](m, func(ev *types.GetChainToHeadResponse) error { + return handler(ev.Chain, ev.CheckpointState) + }) +} + func UponInitBlockchain(m dsl.Module, handler func(initialState *types4.State) error) { UponEvent[*types.Event_InitBlockchain](m, func(ev *types.InitBlockchain) error { return handler(ev.InitialState) diff --git a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go index b07018fde..3dc5a3bd2 100644 --- a/pkg/pb/blockchainpb/bcmpb/events/events.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/events/events.mir.go @@ -91,6 +91,37 @@ func RegisterCheckpoint(destModule types.ModuleID, blockId uint64, state *types4 } } +func GetChainToHeadRequest(destModule types.ModuleID, sourceModule types.ModuleID) *types2.Event { + return &types2.Event{ + DestModule: destModule, + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_GetChainToHeadRequest{ + GetChainToHeadRequest: &types3.GetChainToHeadRequest{ + SourceModule: sourceModule, + }, + }, + }, + }, + } +} + +func GetChainToHeadResponse(destModule types.ModuleID, chain []*types1.Block, checkpointState *types4.State) *types2.Event { + return &types2.Event{ + DestModule: destModule, + Type: &types2.Event_Bcm{ + Bcm: &types3.Event{ + Type: &types3.Event_GetChainToHeadResponse{ + GetChainToHeadResponse: &types3.GetChainToHeadResponse{ + Chain: chain, + CheckpointState: checkpointState, + }, + }, + }, + }, + } +} + func InitBlockchain(destModule types.ModuleID, initialState *types4.State) *types2.Event { return &types2.Event{ DestModule: destModule, diff --git a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go index 28edad5e7..63456ea49 100644 --- a/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/oneof_interfaces.mir.go @@ -29,6 +29,14 @@ func (w *Event_RegisterCheckpoint) Unwrap() *RegisterCheckpoint { return w.RegisterCheckpoint } +func (w *Event_GetChainToHeadRequest) Unwrap() *GetChainToHeadRequest { + return w.GetChainToHeadRequest +} + +func (w *Event_GetChainToHeadResponse) Unwrap() *GetChainToHeadResponse { + return w.GetChainToHeadResponse +} + func (w *Event_InitBlockchain) Unwrap() *InitBlockchain { return w.InitBlockchain } diff --git a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go index 22e6c6770..2aab85903 100644 --- a/pkg/pb/blockchainpb/bcmpb/types/types.mir.go +++ b/pkg/pb/blockchainpb/bcmpb/types/types.mir.go @@ -43,6 +43,10 @@ func Event_TypeFromPb(pb bcmpb.Event_Type) Event_Type { return &Event_GetChainResponse{GetChainResponse: GetChainResponseFromPb(pb.GetChainResponse)} case *bcmpb.Event_RegisterCheckpoint: return &Event_RegisterCheckpoint{RegisterCheckpoint: RegisterCheckpointFromPb(pb.RegisterCheckpoint)} + case *bcmpb.Event_GetChainToHeadRequest: + return &Event_GetChainToHeadRequest{GetChainToHeadRequest: GetChainToHeadRequestFromPb(pb.GetChainToHeadRequest)} + case *bcmpb.Event_GetChainToHeadResponse: + return &Event_GetChainToHeadResponse{GetChainToHeadResponse: GetChainToHeadResponseFromPb(pb.GetChainToHeadResponse)} case *bcmpb.Event_InitBlockchain: return &Event_InitBlockchain{InitBlockchain: InitBlockchainFromPb(pb.InitBlockchain)} } @@ -169,6 +173,54 @@ func (*Event_RegisterCheckpoint) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_RegisterCheckpoint]()} } +type Event_GetChainToHeadRequest struct { + GetChainToHeadRequest *GetChainToHeadRequest +} + +func (*Event_GetChainToHeadRequest) isEvent_Type() {} + +func (w *Event_GetChainToHeadRequest) Unwrap() *GetChainToHeadRequest { + return w.GetChainToHeadRequest +} + +func (w *Event_GetChainToHeadRequest) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.GetChainToHeadRequest == nil { + return &bcmpb.Event_GetChainToHeadRequest{} + } + return &bcmpb.Event_GetChainToHeadRequest{GetChainToHeadRequest: (w.GetChainToHeadRequest).Pb()} +} + +func (*Event_GetChainToHeadRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetChainToHeadRequest]()} +} + +type Event_GetChainToHeadResponse struct { + GetChainToHeadResponse *GetChainToHeadResponse +} + +func (*Event_GetChainToHeadResponse) isEvent_Type() {} + +func (w *Event_GetChainToHeadResponse) Unwrap() *GetChainToHeadResponse { + return w.GetChainToHeadResponse +} + +func (w *Event_GetChainToHeadResponse) Pb() bcmpb.Event_Type { + if w == nil { + return nil + } + if w.GetChainToHeadResponse == nil { + return &bcmpb.Event_GetChainToHeadResponse{} + } + return &bcmpb.Event_GetChainToHeadResponse{GetChainToHeadResponse: (w.GetChainToHeadResponse).Pb()} +} + +func (*Event_GetChainToHeadResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.Event_GetChainToHeadResponse]()} +} + type Event_InitBlockchain struct { InitBlockchain *InitBlockchain } @@ -361,6 +413,73 @@ func (*GetChainResponse) MirReflect() mirreflect.Type { return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetChainResponse]()} } +type GetChainToHeadRequest struct { + SourceModule types2.ModuleID +} + +func GetChainToHeadRequestFromPb(pb *bcmpb.GetChainToHeadRequest) *GetChainToHeadRequest { + if pb == nil { + return nil + } + return &GetChainToHeadRequest{ + SourceModule: (types2.ModuleID)(pb.SourceModule), + } +} + +func (m *GetChainToHeadRequest) Pb() *bcmpb.GetChainToHeadRequest { + if m == nil { + return nil + } + pbMessage := &bcmpb.GetChainToHeadRequest{} + { + pbMessage.SourceModule = (string)(m.SourceModule) + } + + return pbMessage +} + +func (*GetChainToHeadRequest) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetChainToHeadRequest]()} +} + +type GetChainToHeadResponse struct { + Chain []*types.Block + CheckpointState *types3.State +} + +func GetChainToHeadResponseFromPb(pb *bcmpb.GetChainToHeadResponse) *GetChainToHeadResponse { + if pb == nil { + return nil + } + return &GetChainToHeadResponse{ + Chain: types1.ConvertSlice(pb.Chain, func(t *blockchainpb.Block) *types.Block { + return types.BlockFromPb(t) + }), + CheckpointState: types3.StateFromPb(pb.CheckpointState), + } +} + +func (m *GetChainToHeadResponse) Pb() *bcmpb.GetChainToHeadResponse { + if m == nil { + return nil + } + pbMessage := &bcmpb.GetChainToHeadResponse{} + { + pbMessage.Chain = types1.ConvertSlice(m.Chain, func(t *types.Block) *blockchainpb.Block { + return (t).Pb() + }) + if m.CheckpointState != nil { + pbMessage.CheckpointState = (m.CheckpointState).Pb() + } + } + + return pbMessage +} + +func (*GetChainToHeadResponse) MirReflect() mirreflect.Type { + return mirreflect.TypeImpl{PbType_: reflectutil.TypeOf[*bcmpb.GetChainToHeadResponse]()} +} + type RegisterCheckpoint struct { BlockId uint64 State *types3.State diff --git a/protos/blockchainpb/bcmpb/bcmpb.proto b/protos/blockchainpb/bcmpb/bcmpb.proto index 8f23dc126..e7ec2f67f 100644 --- a/protos/blockchainpb/bcmpb/bcmpb.proto +++ b/protos/blockchainpb/bcmpb/bcmpb.proto @@ -21,6 +21,8 @@ message Event { GetChainRequest get_chain_request = 5; GetChainResponse get_chain_response = 6; RegisterCheckpoint register_checkpoint = 7; + GetChainToHeadRequest get_chain_to_head_request = 8; + GetChainToHeadResponse get_chain_to_head_response = 9; InitBlockchain init_blockchain = 100; } @@ -57,6 +59,19 @@ message GetChainResponse { repeated blockchainpb.Block chain = 3; // sorted from oldest to youngest } +message GetChainToHeadRequest { + option (mir.event) = true; + string source_module = 1 [(mir.type) = "github.com/filecoin-project/mir/pkg/types.ModuleID"]; + +} + +message GetChainToHeadResponse { + option (mir.event) = true; + + repeated blockchainpb.Block chain = 1; // sorted from oldest to youngest + statepb.State checkpointState = 2; +} + message RegisterCheckpoint { option (mir.event) = true; From 815277fe53bc8727d050c861347eccefd05f0e60 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Mon, 5 Feb 2024 13:53:34 +0100 Subject: [PATCH 41/46] docs (almost wrapped up) --- pkg/blockchain/README.md | 170 ++++++++++-------- pkg/blockchain/bcm/bcm.go | 3 + pkg/blockchain/broadcast/broadcast.go | 2 + pkg/blockchain/synchronizer/synchronizer.go | 8 +- pkg/blockchain/wsInterceptor/wsInterceptor.go | 31 ++-- .../application/application.go | 46 ++--- 6 files changed, 145 insertions(+), 115 deletions(-) diff --git a/pkg/blockchain/README.md b/pkg/blockchain/README.md index 220f1c042..0ff7abc2d 100644 --- a/pkg/blockchain/README.md +++ b/pkg/blockchain/README.md @@ -58,15 +58,13 @@ BCM <--> Synchronizer Synchronizer <--> Transport ``` -(Note that the event mangler module and the timer module were omitted from the diagram above for simplicity.) - The nodes consist of the following core modules: - **Blockchain Management Module (BCM):** It forms the core of the system and is responsible for managing the blockchain. - **Miner Module:** - Mines new blocks simulating proof-of-work. + Mines new blocks by simulating proof-of-work. - **Synchronizer Module:** Resolves issues when new blocks are to be added to the blockchain but their parent blocks are unknown to this node's BCM. @@ -92,7 +90,7 @@ In particular, it needs to compute the state of the blockchain, verify transacti Next to the modules, it also includes an interceptor that intercepts all communication between different modules and allows for visualization/debugging tools to consume this communication via a websocket connection. An example of how to use the information provided by the interception can be found [here](https://github.com/komplexon3/longest-chain-project/tree/main/chain-visualizer). -(TODO - fix link!) +(Note that the event mangler module and the timer module were omitted from the diagram above for simplicity.) ## Operation @@ -104,17 +102,18 @@ In order to start mining, the miner module requests a payload (_PayloadRequest_) The miner now starts to mine and the initialization sequence of the node is completed. After this, the node will remain inactive (other than mining a block) until a new block has been mined. -This block can either be mined by this node's miner or by another node's miner. -In the first case, the miner will send the new block to its BCM and broadcast the block to all other nodes via the broadcast module (_NewBlock_ event). +This block was either mined by this node's miner or by another node's miner. +In the first case, the miner would send the new block to its BCM and broadcast the block to all other nodes via the broadcast module (_NewBlock_ event). +In the second case, the broadcast module would have received the block from the other node's broadcast module and sent it to the BCM as a _NewBlock_ event. The BCM will then check whether or not it can connect this block to its tree of blocks. If it cannot connect the block, we call it an orphan block. Such cases can occur if the block's parent was mined by another node and the broadcast message for this block has not (yet) reached this node. The BCM will try to resolve these problems with the help of the synchronizer, which will coordinate with other nodes to get the missing blocks. -This procedure will be described below. +This procedure will be described in more detail below. -The BCM now potentially has multiple blocks to add; the new block and possibly a chain of additional blocks from the synchronizer. -Since the blocks might not be trusted since they could have been mined from another node, we must verify it. +The BCM now potentially has multiple blocks to add, which are the new block and possibly a chain of additional blocks from the synchronizer. +The blocks are not trusted by the BCM as they might have been mined by another node and must, therefore, be verified. This happens in two ways: 1. The BCM sends all the blocks together with some additional information to the application module (_VerifyChainRequest_). @@ -123,7 +122,7 @@ This happens in two ways: 2. The BCM verifies that the nodes link together correctly. The BCM can now add all new blocks to the block tree. -If the canonical chain changes, i.e. there is a new head, it instructs the miner to start mining on the new head (_NewHead_ event). +If the canonical chain changes, i.e., there is a new head, it instructs the miner to start mining on the new head (_NewHead_ event). Also, it informs the application about the new head (_HeadChange_ event). This event contains some additional information about how the canonical chain changes. For example, if a different branch of the tree is now the canonical chain, it also includes which blocks are no longer part of the canonical chain. @@ -186,7 +185,7 @@ The functionality of the synchronizer was already outlined above. This part will go into more detail on how the synchronizer resolves orphan blocks. Every _SyncRequest_ from the BCM contains the id of the orphan block and a collection of id of the block that the BCM has in its tree. With this information, the synchronizer asks one node after another to give it a chain that connects the orphan block to one of the known blocks (_ChainRequest_). -The other node's synchronizers then query their BCM via a _GetChainRequest_ for such a segment and forward the answer back to the initiator's synchronizer. +The other nodes' synchronizers then query their BCM via a _GetChainRequest_ for such a segment and forward the answer back to the initiator's synchronizer. The responses can be unsuccessful. In that case, the synchronizer asks the next node. When a successful response is received, the synchronizer instructs the BCM to add the new chain to the block tree (_NewChain_ event). @@ -216,30 +215,34 @@ end Synchronizer (I) ->> BCM (I): NewChain ``` -Also, at any point in time, the application can get the current state at the head of the blockchain by sending a _GetChainToHeadRequest_ to the BCM. +At any point in time, the application can get the current state at the head of the blockchain by sending a _GetChainToHeadRequest_ to the BCM. The response to this will include a chain of blocks from a checkpoint to the current head and the state associated with the checkpoint. Using this information, the application can compute the current state. ```mermaid sequenceDiagram Application ->> BCM: GetChainToHeadRequest +Note over BCM: Compute shortest chain from any checkpoint to the head BCM ->> Application: GetChainToHeadResponse +Note over Application: Compute state from checkpoint state and chain to head ``` ## Modules +After outlining the modules that make up a node and describing how they interact with each other, the following will describe the modules' functionality in a bit more detail. + ### Blockchain Management Module (BCM) The blockchain manager module is responsible for managing the blockchain. It keeps track of all blocks and links them together to form a tree. -In particular, it keeps track of the head of the blockchain, all leaves, and so-called checkpoints. +In particular, it keeps track of the genesis block, the head of the blockchain, all leaves, and so-called checkpoints. A checkpoint is a block stored by the BCM that has a state stored with it. Technically, checkpoints are not necessary as the state can be computed from the blocks. However, it is convenient to not have to recompute the state from the genesis block every time it is needed. The BCM must perform the following tasks: -1. Initialize the blockchain by receiving an InitBlockchain event from the application module which contains the initial state that is associated with the genesis block. +1. Initialize the blockchain by receiving an _InitBlockchain_ event from the application module which contains the initial state that is associated with the genesis block. 2. Add new blocks to the blockchain. If a block is added that has a parent that is not in the blockchain, the BCM requests the missing block from the synchronizer. Blocks that are missing their parent are called orphans. All blocks added to the blockchain are verified in two steps: @@ -247,38 +250,42 @@ The BCM must perform the following tasks: - It has the application module verify that the payloads are valid given the chain that the block is part of. - The BCM must verify that the blocks link together correctly. - Additionally, it sends a TreeUpdate event to the interceptor module. This is solely for debugging/visualization purposes and is not necessary for the operation of the blockchain. + Additionally, it emits a _TreeUpdate_ event. This is solely for debugging/visualization purposes and is not necessary for the operation of the blockchain. -3. Register checkpoints when receiving a RegisterCheckpoint event from the application module. -4. It must provide the synchronizer with chains when requested. This is to resolve orphan blocks in other nodes. -5. When the head changes, it sends a HeadChange event to the application module. +3. Register checkpoints when receiving a _RegisterCheckpoint_ event from the application module. +4. Provide the synchronizer with chains when requested. This is to resolve orphan blocks in other nodes. +5. When the head changes, it sends a _HeadChange_ event to the application module. This event contains all information necessary for the application to compute the state at the new head as well as information about which payloads are now part of the canonical (i.e., longest) and which ones are no longer part of the canonical chain. + Also, it instructs the miner to start mining on the new head (_NewHead_ event). +6. Provide a chain of blocks from a checkpoint to the current head and the state associated with the checkpoint when receiving a _GetChainToHeadRequest_. + This is used by the application to query the current state. ### Miner Module The miner module is responsible for mining new blocks. It simulates the process of mining a block by waiting for a random amount of time and then broadcasting the mined block. This random amount of time is sampled from an exponential distribution with a mean of `expMinuteFactor` minutes. -The mining is orchestrated by a separate goroutine (mineWorkerManager) so that the miner module can continue to receive and process events. +The mining is orchestrated by a separate goroutine (`mineWorkerManager`) such that the miner module can continue to receive and process events. -The operation of the miner module at a high level is as follows: +The operation of the miner module at a is as follows: -1. When it is notified of a new head (NewHead event), it prepares to mine the next block by sending a PayloadRequest event to the application module. +1. When it is notified of a new head (_NewHead_ event), it prepares to mine the next block by sending a PayloadRequest event to the application module. If it is already mining a block, it aborts the ongoing mining operation. 2. When it receives the PayloadResponse containing a payload for the next block, it starts mining a new block with the received payload. -3. When it mines a new block, it broadcasts it to all other modules by sending a NewBlock message to the broadcast module. - It also shares the block with the blockchain manager module (BCM) by sending a NewBlock event to it. +3. When it mines a new block, it broadcasts it to all other modules by sending a _NewBlock_ message to the broadcast module. + It also shares the block with the blockchain manager module (BCM) by sending a _NewBlock_ event to it. ### Broadcast Module The broadcast module is responsible for broadcasting new blocks to all other nodes. It either does this directly via the transport module or the mangler (parameter mangle). If the mangler is used, messages might be dropped and delayed. +How many messages should be dropped can be configured by the parameter `dropRate` and the delay can be configured by the parameters `minDelay` and `maxDelay`. ### Synchronizer Module The synchronizer module assists the blockchain manager (BCM) in resolving cases when BCM receives an orphan block. -That is a block that cannot be linked to the blockchain because the blockchain does not contain the block that the orphan block is linked to. +An orphan block is a block that cannot be linked to the blockchain because the blockchain does not contain the block that the orphan block is linked to. To do this, the synchronizer module communicates with other nodes to get the missing blocks. Terminology: @@ -290,104 +297,114 @@ The synchronizer module performs the following tasks: For internal sync requests: -1. When it receives a SyncRequest event, it must register the request and send a ChainRequest message to another node. - It asks one node after another. -2. When it receives a successful ChainResponse message, it sends the blockchain manager (BCM) the chain fixing the missing bit with a Chain event. +1. When it receives a _SyncRequest_ event, it must register the request and send a _ChainRequest_ message to one of the another nodes. +2. When it receives a successful _ChainResponse_ message, it sends the blockchain manager (BCM) the chain fixing the missing bit with a _NewChain_ event. It then deletes the request. -3. When it receives a failed ChainResponse message, it sends a ChainRequest message to the next node. +3. When it receives an unsuccessful _ChainResponse_ message, it sends a _ChainRequest_ message to the next node. If there are no more nodes to ask, it deletes the request. For external sync requests: -1. When it receives a ChainRequest message, it must register the request and send a GetChainRequest event to the BCM. -2. The BCM will respond with a GetChainResponse event. The synchronizer then responds to the node that sent the ChainRequest message with a ChainResponse message. +1. When it receives a _ChainRequest_ message, it must register the request and send a _GetChainRequest_ event to the BCM. +2. The BCM will respond with a _GetChainResponse_ event. + The synchronizer then responds to the node that sent the _ChainRequest_ message with a _ChainResponse_ message. -**IMPORTANT:** This module assumes that all other nodes respond to requests and that no messages are lost. +**IMPORTANT:** +This module assumes that all other nodes respond to requests. +For this reason, the messages sent from the synchronizer do not go through the mangler. ### Application Module The application module is responsible for performing the actual application logic and interacting with users. It does not hold any persistent state but instead relies on the blockchain manager module (BCM) to store the state. -However, the application needs to compute the state. -Also, the application module is responsible for providing payloads for new blocks. +However, the application is responsible for computing the state given a chain of blocks and a state associated with the first block in the chain. +Also, the application module manages payloads and must provide payloads for new blocks to the miner. The application module must perform the following tasks: -1. Initialize the blockchain by sending it to the initial state in an InitBlockchain event to the BCM. -2. When it receives a PayloadRequest event, it must provide a payload for the next block. +1. Initialize the blockchain by sending the initial state to the BCM in an _InitBlockchain_ event. +2. When it receives a _PayloadRequest_ event, it must provide a payload for the next block. Even if no payloads are available, a payload must be provided, however, this payload can be empty. -3. When it receives a HeadChange event, it must compute the state at the new head of the blockchain. - This state is then registered with the BCM by sending it a RegisterCheckpoint event. +3. When it receives a _HeadChange_ event, it must compute the state at the new head of the blockchain. + This state is then registered with the BCM by sending it a _RegisterCheckpoint_ event. A checkpoint is a block stored by the BCM that has a state stored with it. -4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at an application level and respond with a VerifyBlocksResponse event. +4. When it receives a _VerifyBlocksRequest_ event, it must verify that the given chain is valid at an application level and respond with a _VerifyBlocksResponse_ event. Whether or not the blocks link together correctly is verified by the BCM. An example of such an application is the [Blockchain Chat App](../../samples/blockchain-chat/) that is in the samples directory. As the name implies, it implements a simple chat application. -[Note - different end] - ### Websocket Interceptor The websocket interceptor intercepts all events and sends them to a websocket server. Any connected client can then receive these events by subscribing to the websocket server. -The interceptor proto defines events that are specifically intended for the interceptor and not used by the actual blockchain. -Since these events don't have a destination module, they are sent to the "null" module (a module that simply ignores all incoming events). -However, all events are intercepted and sent to the websocket server. The interceptor proto is simply for "extra" events. +The interceptor proto file defines events that are specifically intended for the interceptor and not used by the actual blockchain. +Since these events technically don't have a destination module, they are sent to the "null" module (a module that simply ignores all incoming events). +However, all events are intercepted and sent to the websocket server. +The interceptor proto is simply for "extra" events. -The interceptor proto defines two such events: +The interceptor proto defines two such dedicated events: -- TreeUpdate: This event is sent by the blockchain manager (BCM) when the blockchain is updated. It contains all blocks in the blockchain and the id of the new head. -- StateUpdate: This event is sent by the application when it computes the state for the newest head of the blockchain. +- _TreeUpdate_: This event is sent by the blockchain manager (BCM) when the blockchain is updated. + It contains all blocks in the blockchain and the id of the new head. +- _StateUpdate_: This event is sent by the application when it computes the state for the newest head of the blockchain. -## How to use +**Important note:** +This is a very simple implementation and it fails to properly handle connections which causes a crash when subscribers disconnect. + +## How to use the system To use the blockchain system, you must first define your application's state and payloads in `statepb.proto` and `payloadpb.proto` files. Next, you must implement an application module that performs the following actions: -1. Initialize the blockchain by sending it an initial state in an InitBlockchain event to the BCM. -2. When it receives a PayloadRequest event, it must provide a payload for the next block. This payload can be empty. -3. When it receives a HeadChange event, it must compute the state at the new head of the blockchain. - This state is then registered with the BCM by sending it a RegisterCheckpoint event. - A checkpoint is a block stored by the BCM that has a state stored with it. -4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at an application level and respond with a VerifyBlocksResponse event. - Whether or not the blocks link together correctly is verified by the BCM. +1. Initialize the blockchain by sending an initial state as an _InitBlockchain_ event to the BCM. +2. When it receives a _PayloadRequest_ event, it must provide a payload for the next block. + Even if no payloads are available, a payload **must** be provided. + However, this payload can be empty. +3. When it receives a _HeadChange_ event, it must compute the state at the new head of the blockchain. + This state must then be registered with the BCM by sending it a _RegisterCheckpoint_ event. + Additionally, the information provided in the _HeadChange_ event might be useful for the payload management. +4. When it receives a _VerifyBlocksRequest_ event, it must verify that the given chain is valid at an application level and respond with a _VerifyBlocksResponse_ event. -See the example chat app as a reference. +See the example blockchain chat app as a reference. -## Example: Chat App +You can now set up all the modules using `system.New(...)` where you can configure the system and provide it with your application module. +For the node initialization, you can get all modules of the system by calling `system.Modules()`. -An example of how to use the longest-chain consensus is the [Blockchain Chat App](../../samples/blockchain-chat/). +## Example: Blockchain Chat App + +An example of how to use the longest-chain consensus system is the [Blockchain Chat App](../../samples/blockchain-chat/). Users can enter input through standard input line by line and the system replicates all messages in the same order across all nodes. It enforces that all messages sent from the same sender appear in the history in a monotonically increasing order of submission time. ### Specification of the Chat App -The following lists the key specifications of the chat app. +The following lists the key specifications of the chat app: - **Payload:** - The payloads consist of a message, the sender id (id of the node from which the message was sent) and a submission timestamp. + The payloads consist of a message, the sender id (id of the node from which the message was sent) and a submission ("sent") timestamp. - **State:** - The state consists of the message history and a collection of "last sent" timestamps, one for each sender/node, where the timestamp corresponds to the submission time of the last message of this sender. + The state consists of the message history and a collection of "last sent" timestamps, one for each sender, where the timestamp corresponds to the submission time of the last message of this sender. -- **Applying Blocks:** +- **Applying blocks to compute state:** When applying a block to a state, the message in the payload is appended to the message history and the "last sent" timestamp corresponding to the sender is updated. -- **Verifying Blocks:** - To verify a block, the application verifies that the submission timestamp +- **Verifying blocks:** + To verify a block, the application verifies that the "sent" timestamp of the payload is not before the "last sent" timestamp of the sender stored in the state associated with the block's parent block. -- **Providing Payloads:** +- **Providing payloads:** Each node's application keeps track of all messages that were submitted to it. - Additionally, if a fork happens and the branch changes, it + Additionally, if a fork happens and the branch changes, it re-adds payloads that were previously part of the old canonical chain to its payload pool and removes the ones that are now part of the canonical chain. + If no payloads are available, it simply provides an empty payload. ### Running the chat app To run the chat app, you can run the following in multiple terminals, once for each node ``` -go run . -numberOfNodes -nodeID +go run . -numberOfNodes -nodeID ``` Further, you can add the following options to modify the characteristics of the system(s). @@ -400,27 +417,29 @@ If they are not set, default values will be used - **-maxDelay:** The maximum delay by which to delay messages between nodes. (Ignored if _disableMangle_ is set.) - **-expMiningFactor:** Factor for exponential distribution for random mining duration. -If `tmux` is installed, you can simply run `./run.sh` to start a network of 4 nodes with reasonable options set. +If `tmux` is installed, you can run `./run.sh` to start a network of 4 nodes with reasonable options set. ### Visualization For this chat application, there exists an accompanying browser-based visualization tool that utilizes the aforementioned websocket interceptor. -The interface provides insight into the state of the different nodes. -In particular, it visualizes the block tree stored in every node, what node is the current head per node and the current message history (i.e., state corresponding to the current head). +The visualizer provides insight into the state of the different nodes. +In particular, it displays the block tree stored in every node, the current and the current chat/message history (i.e., state corresponding to the current head). -For each node, the visualizer displays the current state of each node. -In the box at the top, you see the state corresponding to the current head. +This information is shown once for each node. +In the box at the top, you see the chat/message history. Right below it, you can see the id of the current head (truncated for readability) and the message part of the head's payload. -The biggest part of the window is filled with the block tree that is stored by the node. +The biggest part of the window is filled with the block tree that is stored by the node's BCM. It shows how the blocks are connected and the current head is marked with a red border. -To more easily compare the trees, each block's background color is dependent on its id. +To more easily compare the trees, each block's background color is derived from its id. ![visualizer](./images/visualizers.png) Every single block displays the following information: -- **Block ID** +- **Block ID:** + The id of the block (truncated for readability). - **Miner ID:** The id of the node that mined the block. + The background color is unique per id. - **Sent Timestamp:** The time at which the message was sent. This information is part of the payload. @@ -429,7 +448,8 @@ Every single block displays the following information: The message that is part of the payload. - **Sender ID:** The id of the node from which the message was sent. - This information is part of the payload. + Again, the background is unique per id. + Note that this information is part of the payload. ![visualizer block](./images/visualizer_block.png) diff --git a/pkg/blockchain/bcm/bcm.go b/pkg/blockchain/bcm/bcm.go index b689a04e4..56e3a29e2 100644 --- a/pkg/blockchain/bcm/bcm.go +++ b/pkg/blockchain/bcm/bcm.go @@ -41,10 +41,13 @@ import ( * - It has the application module verify that the payloads are valid given the chain that the block is part of. * - The BCM must verify that the blocks link together correctly. * Additionally, it sends a TreeUpdate event to the interceptor module. This is solely for debugging/visualization purposes and not necessary for the operation of the blockchain. + * Also, it instructs the miner to start mining on the new head (NewHead event). * 3. Register checkpoints when receiving a RegisterCheckpoint event from the application module. * 4. It must provide the synchronizer with chains when requested. This is to resolve orphan blocks in other nodes. * 5. When the head changes, it sends a HeadChange event to the application module. This event contains all information necessary for the application to compute the state at the new head * as well as information about which payloads are now part of the canonical (i.e., longest) and which ones are no longer part of the canonical chain. + * 6. Provide a chain of blocks from a checkpoint to the current head and the state associated with the checkpoint when receiving a GetChainToHeadRequest. + * This is used by the application to query the current state. */ var ( diff --git a/pkg/blockchain/broadcast/broadcast.go b/pkg/blockchain/broadcast/broadcast.go index c3ae00710..5fd1da8a0 100644 --- a/pkg/blockchain/broadcast/broadcast.go +++ b/pkg/blockchain/broadcast/broadcast.go @@ -22,6 +22,8 @@ import ( * The broadcast module is responsible for broadcasting new blocks to all other nodes. * It either does this directly via the transport module or via the mangler (parameter mangle). * If the mangler is used, messages might will be dropped and delayed. +* How many messages should be dropped can be configured by the parameter `dropRate` +* and the delay can be configured by the parameters `minDelay` and `maxDelay`. */ func NewBroadcast(otherNodes []t.NodeID, mangle bool, logger logging.Logger) modules.PassiveModule { diff --git a/pkg/blockchain/synchronizer/synchronizer.go b/pkg/blockchain/synchronizer/synchronizer.go index 7ebdae067..25509350b 100644 --- a/pkg/blockchain/synchronizer/synchronizer.go +++ b/pkg/blockchain/synchronizer/synchronizer.go @@ -22,7 +22,7 @@ import ( * =================== * * The synchronizer module assists the blockchain manager (BCM) in resolving cases whe BCM receives an orphan block. - * That is, a block that cannot be linked to the blockchain because the blockchain does not contain the block that the orphan block is linked to. + * An orphan block is a block that cannot be linked to the blockchain because the blockchain does not contain the block that the orphan block is linked to. * To do this, the synchronizer module communicates with other nodes to get the missing blocks. * * Terminology: @@ -33,16 +33,16 @@ import ( * For internal sync requests: * 1. When it receives a SyncRequest event, it must register the request and send a ChainRequest message to another node. * It asks one node after another. - * 2. When it receives a successful ChainResponse message, it sends the blockchain manager (BCM) the chain fixing the missing bit with a Chain event. + * 2. When it receives a successful ChainResponse message, it sends the blockchain manager (BCM) the chain fixing the missing bit with a NewChain event. * It then deletes the request. - * 3. When it receives a failed ChainResponse message, it sends a ChainRequest message to the next node. + * 3. When it receives a unsuccessful ChainResponse message, it sends a ChainRequest message to the next node. * If there are no more nodes to ask, it deletes the request. * * For external sync requests: * 1. When it receives a ChainRequest message, it must register the request and send a GetChainRequest event to the BCM. * 2. The BCM will respond with a GetChainResponse event. The synchronizer then responds to the node that sent the ChainRequest message with a ChainResponse message. * - * IMPORTANT: This module assumes that all other nodes resppond to requests and that no messages are lost. + * IMPORTANT: This module assumes that all other nodes resppond to requests. For this reason, the messages send by the synchronizer do not go through the mangler. */ var ( diff --git a/pkg/blockchain/wsInterceptor/wsInterceptor.go b/pkg/blockchain/wsInterceptor/wsInterceptor.go index 28f717915..31d718b22 100644 --- a/pkg/blockchain/wsInterceptor/wsInterceptor.go +++ b/pkg/blockchain/wsInterceptor/wsInterceptor.go @@ -9,20 +9,23 @@ import ( ) /** - * Websocket interceptor - * ===================== - * - * The websocket interceptor intercepts all events and sends them to a websocket server. - * Any connected client can then receive these events by subscribing to the websocket server. - * - * The interceptor proto defines events which are specificly intended for the interceptor and not used by the actual blockchain. - * Since these events don't have a destination module, they are sent to the "devnull" module. - * However, all events are intercepted and sent to the websocket server. The interceptor proto is simply for "extra" events. - * - * The interceptor proto defines two such events: - * - TreeUpdate: This event is sent by the blockchain manager (BCM) when the blockchain is updated. It contains all blocks in the blockchain and the id of the new head. - * - StateUpdate: This event is sent by the application when it computes the state for the newest head of the blockchain. - */ +* Websocket interceptor +* ===================== +* +* The websocket interceptor intercepts all events and sends them to a websocket server. +* Any connected client can then receive these events by subscribing to the websocket server. +* +* The interceptor proto file defines events which are specificly intended for the interceptor and not used by the actual blockchain. +* Since these events technically don't have a destination module, they are sent to the "null" module. +* However, all events are intercepted and sent to the websocket server. +The interceptor proto is simply for "extra" events. +* +* The interceptor proto defines two such events: +* - TreeUpdate: This event is sent by the blockchain manager (BCM) when the blockchain is updated. It contains all blocks in the blockchain and the id of the new head. +* - StateUpdate: This event is sent by the application when it computes the state for the newest head of the blockchain. +* +* Important note: This is a very simple implementation and it fails to properly handle connections which causes a crash when subscribers disconnect. +*/ type eventFilterFn func(*eventpb.Event) bool diff --git a/samples/blockchain-chat/application/application.go b/samples/blockchain-chat/application/application.go index 7863261a1..c3e30933b 100644 --- a/samples/blockchain-chat/application/application.go +++ b/samples/blockchain-chat/application/application.go @@ -22,28 +22,30 @@ import ( ) /** - * Application module - * ================== - * - * The application module is reponsible for performing the actual application logic and to interact with users or other applications. - * It does not hold any state, but instead relies on the blockchain manager module (BCM) to store the state. - * However, the application needs to compute the state. - * Also, the application module is responsible for providing payloads for new blocks. - * - * The application module must perform the following tasks: - * 1. Initialize the blockchain by sending it the initial state in an InitBlockchain event to the BCM. - * 2. When it receives a PayloadRequest event, it must provide a payload for the next block. This payload can be empty. - * 3. When it receives a HeadChange event, it must compute the state at the new head of the blockchain. - * This state is then registered with the BCM by sending it a RegisterCheckpoint event. - * A checkpoint is a block stored by the BCM that has a state stored with it. - * 4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at an application level and respond with a VerifyBlocksResponse event. - * Whether or not the blocks link together correctly is verified by the BCM. - * - * This application module implements a simple chat application. - * It takes new messages from the user (MessageInput event) and combines them with a sender id and "sent" timestamp as payloads. - * These payloads are stored in the payload manager (see applicaion/payloads/payloads.go). - * The state is the list of all messages that have been sent and timestamps for when each sender last sent a message. - * At the application level, a chain is valid if the timestamps are monotonically increasing for each sender. +* Application module +* ================== +* +* The application module is reponsible for performing the actual application logic and to interact with users or other applications. +* It does not hold any state, but instead relies on the blockchain manager module (BCM) to store the state. +* However, the application is responsible for computing the state given a chain of blocks and a state associated with the first block in the chain. +* Also, the application module manages payloads and must provide payloads for new blocks to the miner. +* +* The application module must perform the following tasks: +* 1. Initialize the blockchain by sending the initial state to the BCM in an InitBlockchain event. +* 2. When it receives a PayloadRequest event, it must provide a payload for the next block. +* Even if no payloads are available, a payload **must** be provided. However, this payload can be empty. +* 3. When it receives a HeadChange event, it must compute the state at the new head of the blockchain. +* This state is then registered with the BCM by sending it a RegisterCheckpoint event. +* (A checkpoint is a block stored by the BCM that has a state stored with it.) +* Additionally, the information provided in the _HeadChange_ event might be useful for the payload management. +* 4. When it receives a VerifyBlocksRequest event, it must verify that the given chain is valid at an application level and respond with a VerifyBlocksResponse event. +* Whether or not the blocks link together correctly is verified by the BCM. +* +* This application module implements a simple chat application. +* It takes new messages from the user (MessageInput event) and combines them with a sender id and "sent" timestamp as payloads. +* These payloads are stored in the payload manager (see applicaion/payloads/payloads.go). +* The state is the list of all messages that have been sent and timestamps for when each sender last sent a message. +* At the application level, a chain is valid if the timestamps are monotonically increasing for each sender. */ type ApplicationModule struct { From f17a3027fffd66bd0048509c3b3a65f1ef3cf343 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Mon, 5 Feb 2024 20:45:26 +0100 Subject: [PATCH 42/46] docs wrap up... --- pkg/blockchain/README.md | 37 +++++++++---------- pkg/blockchain/bcm/bcm.go | 4 +- pkg/blockchain/synchronizer/synchronizer.go | 2 +- samples/blockchain-chat/README.md | 3 ++ .../application/application.go | 2 +- 5 files changed, 25 insertions(+), 23 deletions(-) create mode 100644 samples/blockchain-chat/README.md diff --git a/pkg/blockchain/README.md b/pkg/blockchain/README.md index 0ff7abc2d..d038240f6 100644 --- a/pkg/blockchain/README.md +++ b/pkg/blockchain/README.md @@ -28,7 +28,7 @@ The blocks making up the blockchain contain the following: Every node keeps track of all nodes that it knows of. At the very beginning, this is solely the genesis block. After a couple of blocks have been mined, all nodes together form a tree of blocks. -Whichever leaf of this tree is the deepest is called the head and the chain of blocks from the genesis block to this leave is the canonical chain. +Whichever leaf of this tree is the deepest is called the head, and the chain of blocks from the genesis block to this leaf is the canonical chain. The payloads of all the blocks in the canonical chain together define the current state stored in the blockchain. ```mermaid @@ -67,7 +67,7 @@ The nodes consist of the following core modules: Mines new blocks by simulating proof-of-work. - **Synchronizer Module:** - Resolves issues when new blocks are to be added to the blockchain but their parent blocks are unknown to this node's BCM. + Resolves issues when new blocks are to be added to the blockchain, but their parent blocks are unknown to this node's BCM. - **Broadcast Module:** Broadcasts newly mined blocks to all other nodes. @@ -85,7 +85,7 @@ and the following supporting modules (provided by mir): Used by the Miner to simulate proof-of-work. Lastly, a user-implemented **Application Module** handles all business logic. -In particular, it needs to compute the state of the blockchain, verify transactions and provide transactions to the miner. +In particular, it needs to compute the state of the blockchain, verify payloads, and provide payloads to the miner. Next to the modules, it also includes an interceptor that intercepts all communication between different modules and allows for visualization/debugging tools to consume this communication via a websocket connection. An example of how to use the information provided by the interception can be found [here](https://github.com/komplexon3/longest-chain-project/tree/main/chain-visualizer). @@ -95,11 +95,11 @@ An example of how to use the information provided by the interception can be fou ## Operation We will now walk through how the different modules interact with each other. -At the start, the application module must initialize the BCM by sending it an _InitBlockchain_ event which contains the initial state. +At the start, the application module must initialize the BCM by sending it an _InitBlockchain_ event, which contains the initial state. The BCM creates a genesis block with an empty payload and stores it together with the initial state. It then instructs the miner module to start mining via a _NewHead_ event. -In order to start mining, the miner module requests a payload (_PayloadRequest_) from the application module. -The miner now starts to mine and the initialization sequence of the node is completed. +To start mining, the miner module requests a payload (_PayloadRequest_) from the application module. +The miner now starts to mine, and the initialization sequence of the node is completed. After this, the node will remain inactive (other than mining a block) until a new block has been mined. This block was either mined by this node's miner or by another node's miner. @@ -229,7 +229,7 @@ Note over Application: Compute state from checkpoint state and chain to head ## Modules -After outlining the modules that make up a node and describing how they interact with each other, the following will describe the modules' functionality in a bit more detail. +After outlining the modules that make up a node and describing how they interact, the following will describe the modules' functionality in a bit more detail. ### Blockchain Management Module (BCM) @@ -238,12 +238,12 @@ It keeps track of all blocks and links them together to form a tree. In particular, it keeps track of the genesis block, the head of the blockchain, all leaves, and so-called checkpoints. A checkpoint is a block stored by the BCM that has a state stored with it. Technically, checkpoints are not necessary as the state can be computed from the blocks. -However, it is convenient to not have to recompute the state from the genesis block every time it is needed. +However, it is convenient not to have to recompute the state from the genesis block every time it is needed. The BCM must perform the following tasks: -1. Initialize the blockchain by receiving an _InitBlockchain_ event from the application module which contains the initial state that is associated with the genesis block. -2. Add new blocks to the blockchain. If a block is added that has a parent that is not in the blockchain, the BCM requests the missing block from the synchronizer. +1. Initialize the blockchain by receiving an _InitBlockchain_ event from the application module, which contains the initial state that is associated with the genesis block. +2. Add new blocks to the blockchain. If a block with a parent that is not in the blockchain is added, the BCM requests the missing block from the synchronizer. Blocks that are missing their parent are called orphans. All blocks added to the blockchain are verified in two steps: @@ -267,11 +267,11 @@ It simulates the process of mining a block by waiting for a random amount of tim This random amount of time is sampled from an exponential distribution with a mean of `expMinuteFactor` minutes. The mining is orchestrated by a separate goroutine (`mineWorkerManager`) such that the miner module can continue to receive and process events. -The operation of the miner module at a is as follows: +The operation of the miner module is as follows: -1. When it is notified of a new head (_NewHead_ event), it prepares to mine the next block by sending a PayloadRequest event to the application module. +1. When it is notified of a new head (_NewHead_ event), it prepares to mine the next block by sending a _PayloadRequest_ event to the application module. If it is already mining a block, it aborts the ongoing mining operation. -2. When it receives the PayloadResponse containing a payload for the next block, it starts mining a new block with the received payload. +2. When it receives the _PayloadResponse_ containing a payload for the next block, it starts mining a new block with the received payload. 3. When it mines a new block, it broadcasts it to all other modules by sending a _NewBlock_ message to the broadcast module. It also shares the block with the blockchain manager module (BCM) by sending a _NewBlock_ event to it. @@ -306,8 +306,7 @@ For internal sync requests: For external sync requests: 1. When it receives a _ChainRequest_ message, it must register the request and send a _GetChainRequest_ event to the BCM. -2. The BCM will respond with a _GetChainResponse_ event. - The synchronizer then responds to the node that sent the _ChainRequest_ message with a _ChainResponse_ message. +2. When the BCM responds with a _GetChainResponse_ event, the synchronizer responds to the node that sent the _ChainRequest_ message with a _ChainResponse_ message. **IMPORTANT:** This module assumes that all other nodes respond to requests. @@ -324,7 +323,7 @@ The application module must perform the following tasks: 1. Initialize the blockchain by sending the initial state to the BCM in an _InitBlockchain_ event. 2. When it receives a _PayloadRequest_ event, it must provide a payload for the next block. - Even if no payloads are available, a payload must be provided, however, this payload can be empty. + Even if no payloads are available, a payload must be provided; however, this payload can be empty. 3. When it receives a _HeadChange_ event, it must compute the state at the new head of the blockchain. This state is then registered with the BCM by sending it a _RegisterCheckpoint_ event. A checkpoint is a block stored by the BCM that has a state stored with it. @@ -343,7 +342,7 @@ Since these events technically don't have a destination module, they are sent to However, all events are intercepted and sent to the websocket server. The interceptor proto is simply for "extra" events. -The interceptor proto defines two such dedicated events: +For this implementation, there are two such dedicated events: - _TreeUpdate_: This event is sent by the blockchain manager (BCM) when the blockchain is updated. It contains all blocks in the blockchain and the id of the new head. @@ -364,7 +363,7 @@ Next, you must implement an application module that performs the following actio However, this payload can be empty. 3. When it receives a _HeadChange_ event, it must compute the state at the new head of the blockchain. This state must then be registered with the BCM by sending it a _RegisterCheckpoint_ event. - Additionally, the information provided in the _HeadChange_ event might be useful for the payload management. + Additionally, the information provided in the _HeadChange_ event might be useful for payload management. 4. When it receives a _VerifyBlocksRequest_ event, it must verify that the given chain is valid at an application level and respond with a _VerifyBlocksResponse_ event. See the example blockchain chat app as a reference. @@ -430,7 +429,7 @@ In the box at the top, you see the chat/message history. Right below it, you can see the id of the current head (truncated for readability) and the message part of the head's payload. The biggest part of the window is filled with the block tree that is stored by the node's BCM. It shows how the blocks are connected and the current head is marked with a red border. -To more easily compare the trees, each block's background color is derived from its id. +To compare the trees more easily, each block's background color is derived from its id. ![visualizer](./images/visualizers.png) diff --git a/pkg/blockchain/bcm/bcm.go b/pkg/blockchain/bcm/bcm.go index 56e3a29e2..074b46dd6 100644 --- a/pkg/blockchain/bcm/bcm.go +++ b/pkg/blockchain/bcm/bcm.go @@ -31,11 +31,11 @@ import ( * In particular, it keeps track of the head of the blockchain, all leaves and so-called checkpoints. * A checkpoint is a block stored by the BCM that has a state stored with it. * Technically, checkpoints are not necessary as the state can be computed from the blocks. - * However, it is convenient to not have to recompute the state from the genesis block every time it is needed. + * However, it is convenient not to have to recompute the state from the genesis block every time it is needed. * * The BCM must perform the following tasks: * 1. Initialize the blockchain by receiving an InitBlockchain event from the application module which contains the initial state that is associated with the genesis block. - * 2. Add new blocks to the blockchain. If a block is added that has a parent that is not in the blockchain, the BCM requests the missing block from the synchronizer. + * 2. Add new blocks to the blockchain. If a block with a parent that that is not in the blockchain is added, the BCM requests the missing block from the synchronizer. * Blocks that are missing their parent are called orphans. * All blocks added to the blockchain are verified in two steps: * - It has the application module verify that the payloads are valid given the chain that the block is part of. diff --git a/pkg/blockchain/synchronizer/synchronizer.go b/pkg/blockchain/synchronizer/synchronizer.go index 25509350b..fe2abbcf5 100644 --- a/pkg/blockchain/synchronizer/synchronizer.go +++ b/pkg/blockchain/synchronizer/synchronizer.go @@ -40,7 +40,7 @@ import ( * * For external sync requests: * 1. When it receives a ChainRequest message, it must register the request and send a GetChainRequest event to the BCM. - * 2. The BCM will respond with a GetChainResponse event. The synchronizer then responds to the node that sent the ChainRequest message with a ChainResponse message. + * 2. WHen the BCM responds with a GetChainResponse event, the synchronizer responds to the node that sent the ChainRequest message with a ChainResponse message. * * IMPORTANT: This module assumes that all other nodes resppond to requests. For this reason, the messages send by the synchronizer do not go through the mangler. */ diff --git a/samples/blockchain-chat/README.md b/samples/blockchain-chat/README.md new file mode 100644 index 000000000..dd5eff844 --- /dev/null +++ b/samples/blockchain-chat/README.md @@ -0,0 +1,3 @@ +# Blockchain Chat App + +See [Blockchain](../../pkg/blockchain/README.md) from documentation. diff --git a/samples/blockchain-chat/application/application.go b/samples/blockchain-chat/application/application.go index c3e30933b..fb7daf4cb 100644 --- a/samples/blockchain-chat/application/application.go +++ b/samples/blockchain-chat/application/application.go @@ -33,7 +33,7 @@ import ( * The application module must perform the following tasks: * 1. Initialize the blockchain by sending the initial state to the BCM in an InitBlockchain event. * 2. When it receives a PayloadRequest event, it must provide a payload for the next block. -* Even if no payloads are available, a payload **must** be provided. However, this payload can be empty. +* Even if no payloads are available, a payload **must** be provided; however, this payload can be empty. * 3. When it receives a HeadChange event, it must compute the state at the new head of the blockchain. * This state is then registered with the BCM by sending it a RegisterCheckpoint event. * (A checkpoint is a block stored by the BCM that has a state stored with it.) From 4d8d65b121d99cb887af0d6b883c62d6a9f1c410 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Mon, 5 Feb 2024 21:26:16 +0100 Subject: [PATCH 43/46] docs final? --- pkg/blockchain/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/blockchain/README.md b/pkg/blockchain/README.md index d038240f6..d182c9204 100644 --- a/pkg/blockchain/README.md +++ b/pkg/blockchain/README.md @@ -400,6 +400,8 @@ The following lists the key specifications of the chat app: ### Running the chat app +> All commands listed here are intended to be run in [this directory](../../samples/blockchain-chat/). + To run the chat app, you can run the following in multiple terminals, once for each node ``` @@ -420,6 +422,8 @@ If `tmux` is installed, you can run `./run.sh` to start a network of 4 nodes wit ### Visualization +> The visualization tool can be found [here](https://github.com/komplexon3/longest-chain-project/tree/main/chain-visualizer). + For this chat application, there exists an accompanying browser-based visualization tool that utilizes the aforementioned websocket interceptor. The visualizer provides insight into the state of the different nodes. In particular, it displays the block tree stored in every node, the current and the current chat/message history (i.e., state corresponding to the current head). @@ -451,5 +455,3 @@ Every single block displays the following information: Note that this information is part of the payload. ![visualizer block](./images/visualizer_block.png) - -## Conclusion From 9355527e536a371950f739eff73dd71f9e71bfbe Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Mon, 5 Feb 2024 21:39:34 +0100 Subject: [PATCH 44/46] cleanup (downgrading log levels) --- pkg/blockchain/bcm/bcm.go | 30 ++++++++++++------- pkg/blockchain/miner/miner.go | 2 +- pkg/blockchain/synchronizer/synchronizer.go | 2 +- .../wsInterceptor/wsServer/wsServer.go | 10 ++++--- .../application/application.go | 4 +-- 5 files changed, 30 insertions(+), 18 deletions(-) diff --git a/pkg/blockchain/bcm/bcm.go b/pkg/blockchain/bcm/bcm.go index 074b46dd6..bc5da2cff 100644 --- a/pkg/blockchain/bcm/bcm.go +++ b/pkg/blockchain/bcm/bcm.go @@ -130,7 +130,7 @@ func (bcm *bcmModule) blockTreeTraversal(traversalFunc func(currBlock *bcmBlock) } func (bcm *bcmModule) handleNewHead(newHead, oldHead *bcmBlock) { - bcm.logger.Log(logging.LevelInfo, "Head changed", "head", utils.FormatBlockId(newHead.block.BlockId)) + bcm.logger.Log(logging.LevelInfo, "Head changed", "old head", utils.FormatBlockId(oldHead.block.BlockId), "new head", utils.FormatBlockId(newHead.block.BlockId)) // TODO: short circuit if newHead is oldHead's parent // compute delta where removeChain is the chain leading to the old head and addChain is the chain leading to the new head @@ -245,7 +245,7 @@ func (bcm *bcmModule) computeDelta(removeHead *bcmBlock, addHead *bcmBlock) ([]* // TODO: might need to add some "cleanup" to handle very old leaves in case this grows too much func (bcm *bcmModule) addBlock(block *blockchainpbtypes.Block) error { - bcm.logger.Log(logging.LevelInfo, "Adding block...", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(block.PreviousBlockId)) + bcm.logger.Log(logging.LevelInfo, "Adding new block", "blockId", utils.FormatBlockId(block.BlockId), "parentId", utils.FormatBlockId(block.PreviousBlockId)) // check if block is already in the leaves, reject if so if _, ok := bcm.leaves[block.BlockId]; ok { @@ -300,7 +300,7 @@ func (bcm *bcmModule) addBlock(block *blockchainpbtypes.Block) error { return false, nil }); err != nil { - bcm.logger.Log(logging.LevelInfo, "Couldn't find parent", "blockId", utils.FormatBlockId(block.BlockId), "error", err) + bcm.logger.Log(logging.LevelWarn, "Couldn't find parent", "blockId", utils.FormatBlockId(block.BlockId), "error", err) return err } @@ -422,7 +422,7 @@ func (bcm *bcmModule) handleNewChain(blocks []*blockchainpbtypes.Block) error { } func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepbtypes.State) error { - bcm.logger.Log(logging.LevelInfo, "Received register checkpoint", "blockId", utils.FormatBlockId(block_id)) + bcm.logger.Log(logging.LevelDebug, "Received register checkpoint", "blockId", utils.FormatBlockId(block_id)) if err := bcm.checkInitialization(); err != nil { return err } @@ -447,7 +447,7 @@ func (bcm *bcmModule) handleRegisterCheckpoint(block_id uint64, state *statepbty } func (bcm *bcmModule) handleGetChainRequest(requestID string, sourceModule t.ModuleID, endBlockId uint64, sourceBlockIds []uint64) error { - bcm.logger.Log(logging.LevelInfo, "Received get chain request", "requestId", requestID, "sourceModule", sourceModule, "endBlockId", utils.FormatBlockId(endBlockId), "sourceBlockIds", utils.FormatBlockIdSlice(sourceBlockIds)) + bcm.logger.Log(logging.LevelDebug, "Received get chain request", "requestId", requestID, "sourceModule", sourceModule, "endBlockId", utils.FormatBlockId(endBlockId), "sourceBlockIds", utils.FormatBlockIdSlice(sourceBlockIds)) chain := make([]*blockchainpbtypes.Block, 0) // for easier lookup... @@ -494,7 +494,7 @@ func (bcm *bcmModule) handleGetChainRequest(requestID string, sourceModule t.Mod } func (bcm *bcmModule) handleGetChainToHeadRequest(sourceModule t.ModuleID) error { - bcm.logger.Log(logging.LevelInfo, "Received get chain to head request", "sourceModule", sourceModule) + bcm.logger.Log(logging.LevelDebug, "Received get chain to head request", "sourceModule", sourceModule) chain, checkpontState := bcm.getChainFromCheckpointToBlock(bcm.head) bcmpbdsl.GetChainToHeadResponse(*bcm.m, sourceModule, chain, checkpontState) return nil @@ -571,7 +571,7 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { }) bcmpbdsl.UponNewChain(m, func(blocks []*blockchainpbtypes.Block) error { - bcm.logger.Log(logging.LevelInfo, "Received chain from synchronizer") + bcm.logger.Log(logging.LevelDebug, "Received chain from synchronizer") if err := bcm.checkInitialization(); err != nil { return err } @@ -592,11 +592,21 @@ func NewBCM(logger logging.Logger) modules.PassiveModule { return bcm.handleGetChainToHeadRequest(sourceModule) }) - bcmpbdsl.UponRegisterCheckpoint(m, bcm.handleRegisterCheckpoint) + bcmpbdsl.UponRegisterCheckpoint(m, func(block_id uint64, state *statepbtypes.State) error { + if err := bcm.checkInitialization(); err != nil { + return err + } + return bcm.handleRegisterCheckpoint(block_id, state) + }) - bcmpbdsl.UponInitBlockchain(m, bcm.handleInitBlockchain) + applicationpbdsl.UponVerifyBlocksResponse(m, func(blocks []*blockchainpbtypes.Block) error { + if err := bcm.checkInitialization(); err != nil { + return err + } + return bcm.handleVerifyBlocksResponse(blocks) + }) - applicationpbdsl.UponVerifyBlocksResponse(m, bcm.handleVerifyBlocksResponse) + bcmpbdsl.UponInitBlockchain(m, bcm.handleInitBlockchain) return m } diff --git a/pkg/blockchain/miner/miner.go b/pkg/blockchain/miner/miner.go index 83fc30df1..5819c6ed4 100644 --- a/pkg/blockchain/miner/miner.go +++ b/pkg/blockchain/miner/miner.go @@ -115,7 +115,7 @@ func (m *minerModule) mineWorkerManager() { select { case <-ctx.Done(): // Mining aborted. Do nothing. - m.logger.Log(logging.LevelDebug, "Mining aborted", "headId", utils.FormatBlockId(blockRequest.HeadId)) + m.logger.Log(logging.LevelInfo, "Mining aborted", "headId", utils.FormatBlockId(blockRequest.HeadId)) return case <-time.After(delay): // Mining completed. Create block and broadcast. diff --git a/pkg/blockchain/synchronizer/synchronizer.go b/pkg/blockchain/synchronizer/synchronizer.go index fe2abbcf5..bd73604a6 100644 --- a/pkg/blockchain/synchronizer/synchronizer.go +++ b/pkg/blockchain/synchronizer/synchronizer.go @@ -226,7 +226,7 @@ func (sm *synchronizerModule) handleGetChainResponse(requestID string, found boo return ErrUnkownGetBlockRequest } - sm.externaLogger.Log(logging.LevelInfo, "Responsing to block request", "requestId", requestID, "found", found) + sm.externaLogger.Log(logging.LevelDebug, "Responsing to block request", "requestId", requestID, "found", found) // respond to sync request transportpbdsl.SendMessage(*sm.m, "transport", synchronizerpbmsgs.ChainResponse("synchronizer", requestID, found, chain), []t.NodeID{request.from}) diff --git a/pkg/blockchain/wsInterceptor/wsServer/wsServer.go b/pkg/blockchain/wsInterceptor/wsServer/wsServer.go index 7d4c3aefd..9991ba7ec 100644 --- a/pkg/blockchain/wsInterceptor/wsServer/wsServer.go +++ b/pkg/blockchain/wsInterceptor/wsServer/wsServer.go @@ -77,7 +77,7 @@ func (wsc *connection) wsConnection() { message := <-wsc.sendChan err := wsc.ws.WriteMessage(message.MessageType, message.Payload) if err != nil { - log.Println(err) + wsc.server.logger.Log(logging.LevelError, "Error sending WS message: ", err) } } }() @@ -94,7 +94,7 @@ func (wss *WsServer) handleWs(w http.ResponseWriter, r *http.Request) { upgrader.CheckOrigin = func(r *http.Request) bool { return true } ws, err := upgrader.Upgrade(w, r, nil) if err != nil { - log.Printf("Upgrade failed: %v", err) + wss.logger.Log(logging.LevelError, "Upgrade failed: ", err) return } wss.connectionsLock.Lock() @@ -126,10 +126,12 @@ func (wss *WsServer) sendHandler() { } func (wss *WsServer) StartServers() { - log.Println("Starting servers...") + wss.logger.Log(logging.LevelInfo, "Starting servers...") wss.setupHttpRoutes() go wss.sendHandler() - log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", wss.port), nil)) + if err := http.ListenAndServe(fmt.Sprintf(":%d", wss.port), nil); err != nil { + wss.logger.Log(logging.LevelError, "ListenAndServe: ", err) + } } func NewWsServer(port int, logger logging.Logger) *WsServer { diff --git a/samples/blockchain-chat/application/application.go b/samples/blockchain-chat/application/application.go index fb7daf4cb..c0d9edce5 100644 --- a/samples/blockchain-chat/application/application.go +++ b/samples/blockchain-chat/application/application.go @@ -105,7 +105,7 @@ func applyBlockToState(state *statepbtypes.State, block *blockchainpbtypes.Block // Handler called by BCM when the head changes. // It handles cases where the head changes because the canonical chain was extended as well as cases where the canonical chain changes because of a fork. func (am *ApplicationModule) handleHeadChange(removedChain, addedChain, checkpointToForkRootChain []*blockchainpbtypes.Block, checkpointState *statepbtypes.State) error { - am.logger.Log(logging.LevelInfo, "Processing fork update", "poolSize", am.pm.PoolSize()) + am.logger.Log(logging.LevelDebug, "Processing fork update", "poolSize", am.pm.PoolSize()) // add "remove chain" transactions to pool for _, block := range removedChain { @@ -128,7 +128,7 @@ func (am *ApplicationModule) handleHeadChange(removedChain, addedChain, checkpoi state = applyBlockToState(state, block) } - am.logger.Log(logging.LevelInfo, "Pool after fork", "poolSize", am.pm.PoolSize()) + am.logger.Log(logging.LevelDebug, "Pool after fork", "poolSize", am.pm.PoolSize()) // register checkpoint blockId := addedChain[len(addedChain)-1].BlockId From 3561eb411166f71076ce1d229d93d6450f886009 Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Mon, 5 Feb 2024 22:02:09 +0100 Subject: [PATCH 45/46] fix visualizer crash on disconnect --- .../wsInterceptor/wsServer/wsServer.go | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/pkg/blockchain/wsInterceptor/wsServer/wsServer.go b/pkg/blockchain/wsInterceptor/wsServer/wsServer.go index 9991ba7ec..245727836 100644 --- a/pkg/blockchain/wsInterceptor/wsServer/wsServer.go +++ b/pkg/blockchain/wsInterceptor/wsServer/wsServer.go @@ -1,8 +1,8 @@ package wsServer import ( + "context" "fmt" - "log" "net/http" "sync" @@ -60,7 +60,8 @@ func (wss *WsServer) handleHome(w http.ResponseWriter, r *http.Request) { } func (wsc *connection) wsConnection() { - log.Println("wsConnection") + wsc.server.logger.Log(logging.LevelInfo, "New WS connection", "total connections: ", len(wsc.server.connections)) + ctx, cancel := context.WithCancel(context.Background()) // new context for new mining // close connection defer wsc.ws.Close() // close up channels and remove connection @@ -70,21 +71,31 @@ func (wsc *connection) wsConnection() { close(wsc.sendChan) wsc.server.connectionsLock.Unlock() }() + defer cancel() // cancel context for event sending loop // sending msgs go func() { for { - message := <-wsc.sendChan - err := wsc.ws.WriteMessage(message.MessageType, message.Payload) - if err != nil { - wsc.server.logger.Log(logging.LevelError, "Error sending WS message: ", err) + select { + case <-ctx.Done(): + return + case message := <-wsc.sendChan: + err := wsc.ws.WriteMessage(message.MessageType, message.Payload) + if err != nil { + wsc.server.logger.Log(logging.LevelError, "Error sending WS message: ", err) + } } } }() // "receiving" msgs for { - wsc.ws.ReadMessage() + msgType, _, err := wsc.ws.ReadMessage() + // catching all erros but actually only interested in "going away" errors + if msgType == websocket.CloseMessage || err != nil { + wsc.server.logger.Log(logging.LevelWarn, "WS connection closed") + return + } wsc.server.logger.Log(logging.LevelWarn, "Received WS message - ignored") } } @@ -119,9 +130,11 @@ func (wss *WsServer) setupHttpRoutes() { func (wss *WsServer) sendHandler() { for { message := <-wss.SendChan + wss.connectionsLock.Lock() for _, conn := range wss.connections { conn.sendChan <- message } + wss.connectionsLock.Unlock() } } From 4df625857b602d70453283c7bb311ef858cb99fd Mon Sep 17 00:00:00 2001 From: Marc Widmer Date: Sun, 18 Feb 2024 19:40:57 +0100 Subject: [PATCH 46/46] mvp chat 'mobile' app --- go.mod | 1 + go.sum | 2 + pkg/net/grpc/grpctransport.pb.go | 2 +- pkg/net/grpc/grpctransport_grpc.pb.go | 2 +- pkg/pb/apppb/apppb.pb.go | 2 +- pkg/pb/availabilitypb/availabilitypb.pb.go | 2 +- .../availabilitypb/batchdbpb/batchdbpb.pb.go | 2 +- pkg/pb/availabilitypb/mscpb/mscpb.pb.go | 2 +- pkg/pb/batchfetcherpb/batchfetcherpb.pb.go | 2 +- pkg/pb/bcbpb/bcbpb.pb.go | 2 +- .../applicationpb/applicationpb.pb.go | 2 +- pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go | 2 +- pkg/pb/blockchainpb/blockchainpb.pb.go | 2 +- .../broadcastpb/broadcastpb.pb.go | 2 +- .../interceptorpb/interceptorpb.pb.go | 2 +- pkg/pb/blockchainpb/minerpb/minerpb.pb.go | 2 +- pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go | 2 +- pkg/pb/blockchainpb/statepb/statepb.pb.go | 86 ++++--- .../blockchainpb/statepb/types/types.mir.go | 24 +- .../synchronizerpb/synchronizerpb.pb.go | 2 +- pkg/pb/checkpointpb/checkpointpb.pb.go | 2 +- .../chkpvalidatorpb/chkpvalidatorpb.pb.go | 2 +- pkg/pb/contextstorepb/contextstorepb.pb.go | 2 +- pkg/pb/cryptopb/cryptopb.pb.go | 2 +- pkg/pb/dslpb/dslpb.pb.go | 2 +- pkg/pb/eventpb/eventpb.pb.go | 2 +- pkg/pb/factorypb/factorypb.pb.go | 2 +- pkg/pb/hasherpb/hasherpb.pb.go | 2 +- pkg/pb/isspb/isspb.pb.go | 2 +- pkg/pb/mempoolpb/mempoolpb.pb.go | 2 +- pkg/pb/messagepb/messagepb.pb.go | 2 +- pkg/pb/mir/codegen_extensions.pb.go | 2 +- pkg/pb/net/codegen_extensions.pb.go | 2 +- pkg/pb/ordererpb/ordererpb.pb.go | 2 +- .../pprepvalidatorpb/pprepvalidatorpb.pb.go | 2 +- pkg/pb/pbftpb/pbftpb.pb.go | 2 +- pkg/pb/pingpongpb/pingpongpb.pb.go | 2 +- pkg/pb/recordingpb/recordingpb.pb.go | 2 +- pkg/pb/testerpb/testerpb.pb.go | 2 +- pkg/pb/threshcryptopb/threshcryptopb.pb.go | 2 +- pkg/pb/transportpb/transportpb.pb.go | 2 +- pkg/pb/trantorpb/trantorpb.pb.go | 2 +- .../transactionreceiver.pb.go | 2 +- .../transactionreceiver_grpc.pb.go | 2 +- protos/blockchainpb/statepb/statepb.proto | 3 +- samples/blockchain-chat-mobile/README.md | 3 + .../application/application.go | 237 ++++++++++++++++++ .../application/payloads/payloads.go | 90 +++++++ samples/blockchain-chat-mobile/main.go | 170 +++++++++++++ samples/blockchain-chat-mobile/run.sh | 25 ++ .../blockchain-chat-mobile/server/server.go | 68 +++++ samples/blockchain-chat-mobile/test.sh | 34 +++ 52 files changed, 733 insertions(+), 90 deletions(-) create mode 100644 samples/blockchain-chat-mobile/README.md create mode 100644 samples/blockchain-chat-mobile/application/application.go create mode 100644 samples/blockchain-chat-mobile/application/payloads/payloads.go create mode 100644 samples/blockchain-chat-mobile/main.go create mode 100755 samples/blockchain-chat-mobile/run.sh create mode 100644 samples/blockchain-chat-mobile/server/server.go create mode 100755 samples/blockchain-chat-mobile/test.sh diff --git a/go.mod b/go.mod index 5e44796f4..9f67e281e 100644 --- a/go.mod +++ b/go.mod @@ -120,6 +120,7 @@ require ( github.com/quic-go/quic-go v0.33.0 // indirect github.com/quic-go/webtransport-go v0.5.3 // indirect github.com/raulk/go-watchdog v1.3.0 // indirect + github.com/rs/cors v1.10.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.5.0 // indirect diff --git a/go.sum b/go.sum index ee178f61b..b5ba08aa5 100644 --- a/go.sum +++ b/go.sum @@ -346,6 +346,8 @@ github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtB github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= github.com/reactivex/rxgo/v2 v2.5.0 h1:FhPgHwX9vKdNQB2gq9EPt+EKk9QrrzoeztGbEEnZam4= github.com/reactivex/rxgo/v2 v2.5.0/go.mod h1:bs4fVZxcb5ZckLIOeIeVH942yunJLWDABWGbrHAW+qU= +github.com/rs/cors v1.10.1 h1:L0uuZVXIKlI1SShY2nhFfo44TYvDPQ1w4oFkUJNfhyo= +github.com/rs/cors v1.10.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= diff --git a/pkg/net/grpc/grpctransport.pb.go b/pkg/net/grpc/grpctransport.pb.go index 03f807471..7b367b225 100644 --- a/pkg/net/grpc/grpctransport.pb.go +++ b/pkg/net/grpc/grpctransport.pb.go @@ -6,7 +6,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: net/grpc/grpctransport.proto package grpc diff --git a/pkg/net/grpc/grpctransport_grpc.pb.go b/pkg/net/grpc/grpctransport_grpc.pb.go index cdb7b2e2c..33258bf7e 100644 --- a/pkg/net/grpc/grpctransport_grpc.pb.go +++ b/pkg/net/grpc/grpctransport_grpc.pb.go @@ -6,7 +6,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v4.24.4 +// - protoc v4.25.2 // source: net/grpc/grpctransport.proto package grpc diff --git a/pkg/pb/apppb/apppb.pb.go b/pkg/pb/apppb/apppb.pb.go index 49f04ce5d..2de466a54 100644 --- a/pkg/pb/apppb/apppb.pb.go +++ b/pkg/pb/apppb/apppb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: apppb/apppb.proto package apppb diff --git a/pkg/pb/availabilitypb/availabilitypb.pb.go b/pkg/pb/availabilitypb/availabilitypb.pb.go index f1c92a5b3..ae27f9f6c 100644 --- a/pkg/pb/availabilitypb/availabilitypb.pb.go +++ b/pkg/pb/availabilitypb/availabilitypb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: availabilitypb/availabilitypb.proto package availabilitypb diff --git a/pkg/pb/availabilitypb/batchdbpb/batchdbpb.pb.go b/pkg/pb/availabilitypb/batchdbpb/batchdbpb.pb.go index 37e92832f..d52e69ab4 100644 --- a/pkg/pb/availabilitypb/batchdbpb/batchdbpb.pb.go +++ b/pkg/pb/availabilitypb/batchdbpb/batchdbpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: availabilitypb/batchdbpb/batchdbpb.proto package batchdbpb diff --git a/pkg/pb/availabilitypb/mscpb/mscpb.pb.go b/pkg/pb/availabilitypb/mscpb/mscpb.pb.go index 869a6a424..4da32c509 100644 --- a/pkg/pb/availabilitypb/mscpb/mscpb.pb.go +++ b/pkg/pb/availabilitypb/mscpb/mscpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: availabilitypb/mscpb/mscpb.proto package mscpb diff --git a/pkg/pb/batchfetcherpb/batchfetcherpb.pb.go b/pkg/pb/batchfetcherpb/batchfetcherpb.pb.go index 3f8ea3436..62ac595e4 100644 --- a/pkg/pb/batchfetcherpb/batchfetcherpb.pb.go +++ b/pkg/pb/batchfetcherpb/batchfetcherpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: batchfetcherpb/batchfetcherpb.proto package batchfetcherpb diff --git a/pkg/pb/bcbpb/bcbpb.pb.go b/pkg/pb/bcbpb/bcbpb.pb.go index b72376997..81a71f5e5 100644 --- a/pkg/pb/bcbpb/bcbpb.pb.go +++ b/pkg/pb/bcbpb/bcbpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: bcbpb/bcbpb.proto package bcbpb diff --git a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go index c4a0695ef..3bdeecdf3 100644 --- a/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go +++ b/pkg/pb/blockchainpb/applicationpb/applicationpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: blockchainpb/applicationpb/applicationpb.proto package applicationpb diff --git a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go index 4d01e24c6..91ff2a44e 100644 --- a/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go +++ b/pkg/pb/blockchainpb/bcmpb/bcmpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: blockchainpb/bcmpb/bcmpb.proto package bcmpb diff --git a/pkg/pb/blockchainpb/blockchainpb.pb.go b/pkg/pb/blockchainpb/blockchainpb.pb.go index a5d143b60..c1a8467ac 100644 --- a/pkg/pb/blockchainpb/blockchainpb.pb.go +++ b/pkg/pb/blockchainpb/blockchainpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: blockchainpb/blockchainpb.proto package blockchainpb diff --git a/pkg/pb/blockchainpb/broadcastpb/broadcastpb.pb.go b/pkg/pb/blockchainpb/broadcastpb/broadcastpb.pb.go index 8ca803459..68c3110e8 100644 --- a/pkg/pb/blockchainpb/broadcastpb/broadcastpb.pb.go +++ b/pkg/pb/blockchainpb/broadcastpb/broadcastpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: blockchainpb/broadcastpb/broadcastpb.proto package broadcastpb diff --git a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go index 4fa5c54ce..695dc4a90 100644 --- a/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go +++ b/pkg/pb/blockchainpb/interceptorpb/interceptorpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: blockchainpb/interceptorpb/interceptorpb.proto package interceptorpb diff --git a/pkg/pb/blockchainpb/minerpb/minerpb.pb.go b/pkg/pb/blockchainpb/minerpb/minerpb.pb.go index 9d49f69bd..188169f97 100644 --- a/pkg/pb/blockchainpb/minerpb/minerpb.pb.go +++ b/pkg/pb/blockchainpb/minerpb/minerpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: blockchainpb/minerpb/minerpb.proto package minerpb diff --git a/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go b/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go index b850787d3..66951a67f 100644 --- a/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go +++ b/pkg/pb/blockchainpb/payloadpb/payloadpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: blockchainpb/payloadpb/payloadpb.proto package payloadpb diff --git a/pkg/pb/blockchainpb/statepb/statepb.pb.go b/pkg/pb/blockchainpb/statepb/statepb.pb.go index deba7c845..0f7da5336 100644 --- a/pkg/pb/blockchainpb/statepb/statepb.pb.go +++ b/pkg/pb/blockchainpb/statepb/statepb.pb.go @@ -1,12 +1,13 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: blockchainpb/statepb/statepb.proto package statepb import ( + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" _ "github.com/filecoin-project/mir/pkg/pb/mir" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" @@ -28,7 +29,7 @@ type State struct { unknownFields protoimpl.UnknownFields // application specific state - MessageHistory []string `protobuf:"bytes,1,rep,name=message_history,json=messageHistory,proto3" json:"message_history,omitempty"` + History []*payloadpb.Payload `protobuf:"bytes,1,rep,name=history,proto3" json:"history,omitempty"` LastSentTimestamps []*LastSentTimestamp `protobuf:"bytes,2,rep,name=last_sent_timestamps,json=lastSentTimestamps,proto3" json:"last_sent_timestamps,omitempty"` } @@ -64,9 +65,9 @@ func (*State) Descriptor() ([]byte, []int) { return file_blockchainpb_statepb_statepb_proto_rawDescGZIP(), []int{0} } -func (x *State) GetMessageHistory() []string { +func (x *State) GetHistory() []*payloadpb.Payload { if x != nil { - return x.MessageHistory + return x.History } return nil } @@ -138,34 +139,37 @@ var File_blockchainpb_statepb_statepb_proto protoreflect.FileDescriptor var file_blockchainpb_statepb_statepb_proto_rawDesc = []byte{ 0x0a, 0x22, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x1a, 0x1c, 0x6d, - 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, - 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, - 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x84, 0x01, 0x0a, - 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x27, 0x0a, 0x0f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, - 0x65, 0x5f, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, - 0x0e, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x48, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, - 0x4c, 0x0a, 0x14, 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, - 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x53, - 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x3a, 0x04, 0x80, - 0xa6, 0x1d, 0x01, 0x22, 0xa2, 0x01, 0x0a, 0x11, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x4d, 0x0a, 0x07, 0x6e, 0x6f, 0x64, - 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x34, 0x82, 0xa6, 0x1d, 0x30, - 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, - 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, - 0x70, 0x6b, 0x67, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, - 0x52, 0x06, 0x6e, 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, - 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, - 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, - 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, - 0x6d, 0x70, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, - 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, - 0x70, 0x62, 0x2f, 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, - 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x73, 0x74, 0x61, 0x74, 0x65, 0x70, 0x62, 0x1a, 0x26, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2f, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x6d, 0x69, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x67, + 0x65, 0x6e, 0x5f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x89, 0x01, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x2c, + 0x0a, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, + 0x12, 0x2e, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x70, 0x62, 0x2e, 0x50, 0x61, 0x79, 0x6c, + 0x6f, 0x61, 0x64, 0x52, 0x07, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0x79, 0x12, 0x4c, 0x0a, 0x14, + 0x6c, 0x61, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, + 0x61, 0x6d, 0x70, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x70, 0x62, 0x2e, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x12, 0x6c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x73, 0x3a, 0x04, 0x80, 0xa6, 0x1d, 0x01, + 0x22, 0xa2, 0x01, 0x0a, 0x11, 0x4c, 0x61, 0x73, 0x74, 0x53, 0x65, 0x6e, 0x74, 0x54, 0x69, 0x6d, + 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x12, 0x4d, 0x0a, 0x07, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x69, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x34, 0x82, 0xa6, 0x1d, 0x30, 0x67, 0x69, 0x74, + 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, + 0x2d, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, + 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x4e, 0x6f, 0x64, 0x65, 0x49, 0x44, 0x52, 0x06, 0x6e, + 0x6f, 0x64, 0x65, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, + 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, + 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x3a, + 0x04, 0x80, 0xa6, 0x1d, 0x01, 0x42, 0x3d, 0x5a, 0x3b, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x66, 0x69, 0x6c, 0x65, 0x63, 0x6f, 0x69, 0x6e, 0x2d, 0x70, 0x72, 0x6f, + 0x6a, 0x65, 0x63, 0x74, 0x2f, 0x6d, 0x69, 0x72, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x70, 0x62, 0x2f, + 0x62, 0x6c, 0x6f, 0x63, 0x6b, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x70, 0x62, 0x2f, 0x73, 0x74, 0x61, + 0x74, 0x65, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -184,16 +188,18 @@ var file_blockchainpb_statepb_statepb_proto_msgTypes = make([]protoimpl.MessageI var file_blockchainpb_statepb_statepb_proto_goTypes = []interface{}{ (*State)(nil), // 0: statepb.State (*LastSentTimestamp)(nil), // 1: statepb.LastSentTimestamp - (*timestamppb.Timestamp)(nil), // 2: google.protobuf.Timestamp + (*payloadpb.Payload)(nil), // 2: payloadpb.Payload + (*timestamppb.Timestamp)(nil), // 3: google.protobuf.Timestamp } var file_blockchainpb_statepb_statepb_proto_depIdxs = []int32{ - 1, // 0: statepb.State.last_sent_timestamps:type_name -> statepb.LastSentTimestamp - 2, // 1: statepb.LastSentTimestamp.timestamp:type_name -> google.protobuf.Timestamp - 2, // [2:2] is the sub-list for method output_type - 2, // [2:2] is the sub-list for method input_type - 2, // [2:2] is the sub-list for extension type_name - 2, // [2:2] is the sub-list for extension extendee - 0, // [0:2] is the sub-list for field type_name + 2, // 0: statepb.State.history:type_name -> payloadpb.Payload + 1, // 1: statepb.State.last_sent_timestamps:type_name -> statepb.LastSentTimestamp + 3, // 2: statepb.LastSentTimestamp.timestamp:type_name -> google.protobuf.Timestamp + 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 } func init() { file_blockchainpb_statepb_statepb_proto_init() } diff --git a/pkg/pb/blockchainpb/statepb/types/types.mir.go b/pkg/pb/blockchainpb/statepb/types/types.mir.go index 308fb8ad0..7531157a5 100644 --- a/pkg/pb/blockchainpb/statepb/types/types.mir.go +++ b/pkg/pb/blockchainpb/statepb/types/types.mir.go @@ -4,15 +4,17 @@ package statepbtypes import ( mirreflect "github.com/filecoin-project/mir/codegen/mirreflect" - types "github.com/filecoin-project/mir/codegen/model/types" + types1 "github.com/filecoin-project/mir/codegen/model/types" + payloadpb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb" + types "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" statepb "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb" - types1 "github.com/filecoin-project/mir/pkg/types" + types2 "github.com/filecoin-project/mir/pkg/types" reflectutil "github.com/filecoin-project/mir/pkg/util/reflectutil" timestamppb "google.golang.org/protobuf/types/known/timestamppb" ) type State struct { - MessageHistory []string + History []*types.Payload LastSentTimestamps []*LastSentTimestamp } @@ -21,8 +23,10 @@ func StateFromPb(pb *statepb.State) *State { return nil } return &State{ - MessageHistory: pb.MessageHistory, - LastSentTimestamps: types.ConvertSlice(pb.LastSentTimestamps, func(t *statepb.LastSentTimestamp) *LastSentTimestamp { + History: types1.ConvertSlice(pb.History, func(t *payloadpb.Payload) *types.Payload { + return types.PayloadFromPb(t) + }), + LastSentTimestamps: types1.ConvertSlice(pb.LastSentTimestamps, func(t *statepb.LastSentTimestamp) *LastSentTimestamp { return LastSentTimestampFromPb(t) }), } @@ -34,8 +38,10 @@ func (m *State) Pb() *statepb.State { } pbMessage := &statepb.State{} { - pbMessage.MessageHistory = m.MessageHistory - pbMessage.LastSentTimestamps = types.ConvertSlice(m.LastSentTimestamps, func(t *LastSentTimestamp) *statepb.LastSentTimestamp { + pbMessage.History = types1.ConvertSlice(m.History, func(t *types.Payload) *payloadpb.Payload { + return (t).Pb() + }) + pbMessage.LastSentTimestamps = types1.ConvertSlice(m.LastSentTimestamps, func(t *LastSentTimestamp) *statepb.LastSentTimestamp { return (t).Pb() }) } @@ -48,7 +54,7 @@ func (*State) MirReflect() mirreflect.Type { } type LastSentTimestamp struct { - NodeId types1.NodeID + NodeId types2.NodeID Timestamp *timestamppb.Timestamp } @@ -57,7 +63,7 @@ func LastSentTimestampFromPb(pb *statepb.LastSentTimestamp) *LastSentTimestamp { return nil } return &LastSentTimestamp{ - NodeId: (types1.NodeID)(pb.NodeId), + NodeId: (types2.NodeID)(pb.NodeId), Timestamp: pb.Timestamp, } } diff --git a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go index 6e7d53a86..65f1f32aa 100644 --- a/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go +++ b/pkg/pb/blockchainpb/synchronizerpb/synchronizerpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: blockchainpb/synchronizerpb/synchronizerpb.proto package synchronizerpb diff --git a/pkg/pb/checkpointpb/checkpointpb.pb.go b/pkg/pb/checkpointpb/checkpointpb.pb.go index 5fb060f45..de983d0e5 100644 --- a/pkg/pb/checkpointpb/checkpointpb.pb.go +++ b/pkg/pb/checkpointpb/checkpointpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: checkpointpb/checkpointpb.proto package checkpointpb diff --git a/pkg/pb/checkpointpb/chkpvalidatorpb/chkpvalidatorpb.pb.go b/pkg/pb/checkpointpb/chkpvalidatorpb/chkpvalidatorpb.pb.go index a19da6d2f..f6e90107c 100644 --- a/pkg/pb/checkpointpb/chkpvalidatorpb/chkpvalidatorpb.pb.go +++ b/pkg/pb/checkpointpb/chkpvalidatorpb/chkpvalidatorpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: checkpointpb/chkpvalidatorpb/chkpvalidatorpb.proto package chkpvalidatorpb diff --git a/pkg/pb/contextstorepb/contextstorepb.pb.go b/pkg/pb/contextstorepb/contextstorepb.pb.go index ab3d71c45..9ae54524e 100644 --- a/pkg/pb/contextstorepb/contextstorepb.pb.go +++ b/pkg/pb/contextstorepb/contextstorepb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: contextstorepb/contextstorepb.proto package contextstorepb diff --git a/pkg/pb/cryptopb/cryptopb.pb.go b/pkg/pb/cryptopb/cryptopb.pb.go index e6c852095..cd2d3a002 100644 --- a/pkg/pb/cryptopb/cryptopb.pb.go +++ b/pkg/pb/cryptopb/cryptopb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: cryptopb/cryptopb.proto package cryptopb diff --git a/pkg/pb/dslpb/dslpb.pb.go b/pkg/pb/dslpb/dslpb.pb.go index c1f7e8f3e..e12dcfc65 100644 --- a/pkg/pb/dslpb/dslpb.pb.go +++ b/pkg/pb/dslpb/dslpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: dslpb/dslpb.proto package dslpb diff --git a/pkg/pb/eventpb/eventpb.pb.go b/pkg/pb/eventpb/eventpb.pb.go index a5c654c2d..0ecc7935e 100644 --- a/pkg/pb/eventpb/eventpb.pb.go +++ b/pkg/pb/eventpb/eventpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: eventpb/eventpb.proto package eventpb diff --git a/pkg/pb/factorypb/factorypb.pb.go b/pkg/pb/factorypb/factorypb.pb.go index 62849de7f..fbf3c61fe 100644 --- a/pkg/pb/factorypb/factorypb.pb.go +++ b/pkg/pb/factorypb/factorypb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: factorypb/factorypb.proto package factorypb diff --git a/pkg/pb/hasherpb/hasherpb.pb.go b/pkg/pb/hasherpb/hasherpb.pb.go index 6c7ca4459..1265733ad 100644 --- a/pkg/pb/hasherpb/hasherpb.pb.go +++ b/pkg/pb/hasherpb/hasherpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: hasherpb/hasherpb.proto package hasherpb diff --git a/pkg/pb/isspb/isspb.pb.go b/pkg/pb/isspb/isspb.pb.go index a96af63ec..f9979d9f5 100644 --- a/pkg/pb/isspb/isspb.pb.go +++ b/pkg/pb/isspb/isspb.pb.go @@ -6,7 +6,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: isspb/isspb.proto package isspb diff --git a/pkg/pb/mempoolpb/mempoolpb.pb.go b/pkg/pb/mempoolpb/mempoolpb.pb.go index 6bb80ee39..699d6c4f7 100644 --- a/pkg/pb/mempoolpb/mempoolpb.pb.go +++ b/pkg/pb/mempoolpb/mempoolpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: mempoolpb/mempoolpb.proto package mempoolpb diff --git a/pkg/pb/messagepb/messagepb.pb.go b/pkg/pb/messagepb/messagepb.pb.go index b4fefafc4..e43cd0d2d 100644 --- a/pkg/pb/messagepb/messagepb.pb.go +++ b/pkg/pb/messagepb/messagepb.pb.go @@ -6,7 +6,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: messagepb/messagepb.proto package messagepb diff --git a/pkg/pb/mir/codegen_extensions.pb.go b/pkg/pb/mir/codegen_extensions.pb.go index b956c9892..bf200d1bf 100644 --- a/pkg/pb/mir/codegen_extensions.pb.go +++ b/pkg/pb/mir/codegen_extensions.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: mir/codegen_extensions.proto package mir diff --git a/pkg/pb/net/codegen_extensions.pb.go b/pkg/pb/net/codegen_extensions.pb.go index e94241aeb..962fb62fc 100644 --- a/pkg/pb/net/codegen_extensions.pb.go +++ b/pkg/pb/net/codegen_extensions.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: net/codegen_extensions.proto package net diff --git a/pkg/pb/ordererpb/ordererpb.pb.go b/pkg/pb/ordererpb/ordererpb.pb.go index 0f779a5db..019a1b3a1 100644 --- a/pkg/pb/ordererpb/ordererpb.pb.go +++ b/pkg/pb/ordererpb/ordererpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: ordererpb/ordererpb.proto package ordererpb diff --git a/pkg/pb/ordererpb/pprepvalidatorpb/pprepvalidatorpb.pb.go b/pkg/pb/ordererpb/pprepvalidatorpb/pprepvalidatorpb.pb.go index 970334dfa..98ca79826 100644 --- a/pkg/pb/ordererpb/pprepvalidatorpb/pprepvalidatorpb.pb.go +++ b/pkg/pb/ordererpb/pprepvalidatorpb/pprepvalidatorpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: ordererpb/pprepvalidatorpb/pprepvalidatorpb.proto package pprepvalidatorpb diff --git a/pkg/pb/pbftpb/pbftpb.pb.go b/pkg/pb/pbftpb/pbftpb.pb.go index 099be2f90..5596be21b 100644 --- a/pkg/pb/pbftpb/pbftpb.pb.go +++ b/pkg/pb/pbftpb/pbftpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: pbftpb/pbftpb.proto package pbftpb diff --git a/pkg/pb/pingpongpb/pingpongpb.pb.go b/pkg/pb/pingpongpb/pingpongpb.pb.go index 46647c4f9..fb7de322a 100644 --- a/pkg/pb/pingpongpb/pingpongpb.pb.go +++ b/pkg/pb/pingpongpb/pingpongpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: pingpongpb/pingpongpb.proto package pingpongpb diff --git a/pkg/pb/recordingpb/recordingpb.pb.go b/pkg/pb/recordingpb/recordingpb.pb.go index 2c7685b39..044bf710d 100644 --- a/pkg/pb/recordingpb/recordingpb.pb.go +++ b/pkg/pb/recordingpb/recordingpb.pb.go @@ -6,7 +6,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: recordingpb/recordingpb.proto package recordingpb diff --git a/pkg/pb/testerpb/testerpb.pb.go b/pkg/pb/testerpb/testerpb.pb.go index 33d137a9b..329766e11 100644 --- a/pkg/pb/testerpb/testerpb.pb.go +++ b/pkg/pb/testerpb/testerpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: testerpb/testerpb.proto package testerpb diff --git a/pkg/pb/threshcryptopb/threshcryptopb.pb.go b/pkg/pb/threshcryptopb/threshcryptopb.pb.go index a890a1d9f..914e1e242 100644 --- a/pkg/pb/threshcryptopb/threshcryptopb.pb.go +++ b/pkg/pb/threshcryptopb/threshcryptopb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: threshcryptopb/threshcryptopb.proto package threshcryptopb diff --git a/pkg/pb/transportpb/transportpb.pb.go b/pkg/pb/transportpb/transportpb.pb.go index 17ac6ee68..417771918 100644 --- a/pkg/pb/transportpb/transportpb.pb.go +++ b/pkg/pb/transportpb/transportpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: transportpb/transportpb.proto package transportpb diff --git a/pkg/pb/trantorpb/trantorpb.pb.go b/pkg/pb/trantorpb/trantorpb.pb.go index d0c353c46..21b20e504 100644 --- a/pkg/pb/trantorpb/trantorpb.pb.go +++ b/pkg/pb/trantorpb/trantorpb.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: trantorpb/trantorpb.proto package trantorpb diff --git a/pkg/transactionreceiver/transactionreceiver.pb.go b/pkg/transactionreceiver/transactionreceiver.pb.go index 2045bd2fc..04dd3d8ec 100644 --- a/pkg/transactionreceiver/transactionreceiver.pb.go +++ b/pkg/transactionreceiver/transactionreceiver.pb.go @@ -6,7 +6,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.31.0 -// protoc v4.24.4 +// protoc v4.25.2 // source: transactionreceiver/transactionreceiver.proto package transactionreceiver diff --git a/pkg/transactionreceiver/transactionreceiver_grpc.pb.go b/pkg/transactionreceiver/transactionreceiver_grpc.pb.go index 94d0f6e6d..5e06ef7a6 100644 --- a/pkg/transactionreceiver/transactionreceiver_grpc.pb.go +++ b/pkg/transactionreceiver/transactionreceiver_grpc.pb.go @@ -6,7 +6,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v4.24.4 +// - protoc v4.25.2 // source: transactionreceiver/transactionreceiver.proto package transactionreceiver diff --git a/protos/blockchainpb/statepb/statepb.proto b/protos/blockchainpb/statepb/statepb.proto index 187661352..bde7fb3f8 100644 --- a/protos/blockchainpb/statepb/statepb.proto +++ b/protos/blockchainpb/statepb/statepb.proto @@ -4,6 +4,7 @@ package statepb; option go_package = "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb"; +import "blockchainpb/payloadpb/payloadpb.proto"; import "mir/codegen_extensions.proto"; import "google/protobuf/timestamp.proto"; @@ -11,7 +12,7 @@ message State { option (mir.struct) = true; // application specific state - repeated string message_history = 1; + repeated payloadpb.Payload history = 1; repeated LastSentTimestamp last_sent_timestamps = 2; } diff --git a/samples/blockchain-chat-mobile/README.md b/samples/blockchain-chat-mobile/README.md new file mode 100644 index 000000000..dd5eff844 --- /dev/null +++ b/samples/blockchain-chat-mobile/README.md @@ -0,0 +1,3 @@ +# Blockchain Chat App + +See [Blockchain](../../pkg/blockchain/README.md) from documentation. diff --git a/samples/blockchain-chat-mobile/application/application.go b/samples/blockchain-chat-mobile/application/application.go new file mode 100644 index 000000000..aac7454e9 --- /dev/null +++ b/samples/blockchain-chat-mobile/application/application.go @@ -0,0 +1,237 @@ +package application + +// app module + +import ( + "fmt" + "time" + + "github.com/filecoin-project/mir/pkg/blockchain/utils" + "github.com/filecoin-project/mir/pkg/dsl" + "github.com/filecoin-project/mir/pkg/logging" + "github.com/filecoin-project/mir/pkg/modules" + applicationpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/dsl" + bcmpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/bcmpb/dsl" + interceptorpbdsl "github.com/filecoin-project/mir/pkg/pb/blockchainpb/interceptorpb/dsl" + payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + statepbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/statepb/types" + blockchainpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/types" + t "github.com/filecoin-project/mir/pkg/types" + "github.com/filecoin-project/mir/samples/blockchain-chat-mobile/application/payloads" + "github.com/filecoin-project/mir/samples/blockchain-chat-mobile/server" + "google.golang.org/protobuf/types/known/timestamppb" +) + +/** +* Application module - Mobile Chat Demo +* ================== +* +* Based on /samples/blockchain-chat/application/application.go +* + */ + +type ApplicationModule struct { + m *dsl.Module + pm *payloads.PayloadManager // store for payloads + nodeID t.NodeID // to set sender in payload + logger logging.Logger + mobileServer *server.Server +} + +// application-application events + +/** +* Helper functions + */ + +// helper function that applies a block's payload to the given state, thereby computing the new state +func applyBlockToState(state *statepbtypes.State, block *blockchainpbtypes.Block) *statepbtypes.State { + sender := block.Payload.Sender + timeStamps := state.LastSentTimestamps + history := state.History + + for i, lt := range timeStamps { + if lt.NodeId == sender { + ltTimestamp := lt.Timestamp.AsTime() + if ltTimestamp.After(block.Payload.Timestamp.AsTime()) { + panic("invalid ordering - there is a block that should never have been accepted") + } + + // remove old timestamp, if it exists + timeStamps[i] = timeStamps[len(state.LastSentTimestamps)-1] + timeStamps = timeStamps[:len(state.LastSentTimestamps)-1] + } + } + + timeStamps = append(timeStamps, &statepbtypes.LastSentTimestamp{ + NodeId: sender, + Timestamp: block.Payload.Timestamp, + }) + + // empty block, just skip + if block.Payload.Message != "" { + history = append(history, block.Payload) + } + + return &statepbtypes.State{ + History: history, + LastSentTimestamps: timeStamps, + } +} + +/** +* Mir event handlers + */ + +// TODO: rename - fork is confusing + +// Handler called by BCM when the head changes. +// It handles cases where the head changes because the canonical chain was extended as well as cases where the canonical chain changes because of a fork. +func (am *ApplicationModule) handleHeadChange(removedChain, addedChain, checkpointToForkRootChain []*blockchainpbtypes.Block, checkpointState *statepbtypes.State) error { + am.logger.Log(logging.LevelDebug, "Processing fork update", "poolSize", am.pm.PoolSize()) + + // add "remove chain" transactions to pool + for _, block := range removedChain { + am.pm.AddPayload(block.Payload) + } + + // remove "add chain" transactions from pool + for _, block := range addedChain { + am.pm.RemovePayload(block.Payload) + } + + state := checkpointState + // compute state at fork roo + // skip first as checkpoint state already 'included' its payload + for _, block := range checkpointToForkRootChain[1:] { + state = applyBlockToState(state, block) + } + // compute state at new head + for _, block := range addedChain { + state = applyBlockToState(state, block) + } + + am.logger.Log(logging.LevelDebug, "Pool after fork", "poolSize", am.pm.PoolSize()) + + // register checkpoint + blockId := addedChain[len(addedChain)-1].BlockId + bcmpbdsl.RegisterCheckpoint(*am.m, "bcm", blockId, state) + interceptorpbdsl.StateUpdate(*am.m, "devnull", state) + + // update state in mobile server + am.mobileServer.SetHistory(state.History) + + // print state + fmt.Printf("=== STATE ===\n") + for _, pl := range state.History { + fmt.Println(pl.Message) + } + fmt.Printf("=============\nEnter new message: \n") + + return nil +} + +// Handler called by BCM when a chain needs to be verified. +// The checks are purely at the application level. +// In this implementation, it simply checks that the timestamps are monotonically increasing for each sender. +func (am *ApplicationModule) handleVerifyBlocksRequest(checkpointState *statepbtypes.State, chainCheckpointToStart, chainToVerify []*blockchainpbtypes.Block) error { + am.logger.Log(logging.LevelDebug, "Processing verify blocks request", "checkpoint", utils.FormatBlockId(chainCheckpointToStart[0].BlockId), "first block to verify", utils.FormatBlockId(chainToVerify[0].BlockId), "last block to verify", utils.FormatBlockId(chainToVerify[len(chainToVerify)-1].BlockId)) + + timestampMap := make(map[t.NodeID]time.Time) + for _, lt := range checkpointState.LastSentTimestamps { + timestampMap[lt.NodeId] = lt.Timestamp.AsTime() + } + + chain := append(chainCheckpointToStart, chainToVerify...) // chainCheckpointToStart wouldn't need to be verified, but we need it to get the timestamps + + for _, block := range chain { + blockTs := block.Payload.Timestamp.AsTime() + // verify block + if ts, ok := timestampMap[block.Payload.Sender]; ok { + if ts.After(blockTs) { + // block in chain invalid, don't respond + return nil + } + } + + // update timestamp + timestampMap[block.Payload.Sender] = blockTs + } + + // if no blocks are invalid, responds with chain + applicationpbdsl.VerifyBlocksResponse(*am.m, "bcm", chainToVerify) + + return nil +} + +// Handler called by the miner when it needs a payload for the next block. +func (am *ApplicationModule) handlePayloadRequest(head_id uint64) error { + am.logger.Log(logging.LevelDebug, "Processing payload request", "headId", utils.FormatBlockId(head_id)) + + payload := am.pm.GetPayload() + + // if no payload is available, create empty payload + // this is important to ensure that all nodes are always mining new blocks + if payload == nil { + payload = &payloadpbtypes.Payload{ + Message: "", + Timestamp: timestamppb.Now(), + Sender: am.nodeID, + } + } + + applicationpbdsl.PayloadResponse(*am.m, "miner", head_id, payload) + + return nil +} + +func (am *ApplicationModule) addNewMessage(message string) { + am.pm.AddPayload(&payloadpbtypes.Payload{ + Message: message, + Timestamp: timestamppb.Now(), + Sender: am.nodeID, + }) +} + +func NewApplication(nodeID t.NodeID, logger logging.Logger) modules.Module { + + m := dsl.NewModule("application") + am := &ApplicationModule{ + m: &m, + logger: logger, + nodeID: nodeID, + pm: payloads.New(), + mobileServer: nil, + } + + am.mobileServer = server.NewServer(":818"+string(nodeID), am.addNewMessage) // janky port def but should be ok as we will never have >10 + + fmt.Println("===========================================") + go am.mobileServer.Run() + fmt.Println("===========================================") + + dsl.UponInit(m, func() error { + // init blockchain + bcmpbdsl.InitBlockchain(*am.m, "bcm", &statepbtypes.State{ + History: []*payloadpbtypes.Payload{}, + LastSentTimestamps: []*statepbtypes.LastSentTimestamp{}, + }) + + return nil + }) + + applicationpbdsl.UponPayloadRequest(m, am.handlePayloadRequest) + applicationpbdsl.UponHeadChange(m, am.handleHeadChange) + applicationpbdsl.UponVerifyBlocksRequest(m, am.handleVerifyBlocksRequest) + + applicationpbdsl.UponMessageInput(m, func(text string) error { + am.pm.AddPayload(&payloadpbtypes.Payload{ + Message: text, + Timestamp: timestamppb.Now(), + Sender: am.nodeID, + }) + return nil + }) + + return m +} diff --git a/samples/blockchain-chat-mobile/application/payloads/payloads.go b/samples/blockchain-chat-mobile/application/payloads/payloads.go new file mode 100644 index 000000000..51f0d6066 --- /dev/null +++ b/samples/blockchain-chat-mobile/application/payloads/payloads.go @@ -0,0 +1,90 @@ +package payloads + +import ( + "slices" + + payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + "github.com/mitchellh/hashstructure" +) + +/** + * Payload manager + * =============== + * + * The payload manager is part of the application and responsible for managing payloads. + * It keeps track of all incoming payloads and provides the application with the oldest payload. + * If no payloads are available, it returns nil. + * The application can then use this payload to create a new block. + * Payloads can be added and removed from the payload manager. + * Equivalence of payloads is determined by their hash. + */ + +type storedPayload struct { + hash uint64 + payload *payloadpbtypes.Payload +} + +type PayloadManager struct { + payloads []storedPayload // sorted by timestamp +} + +func (tm *PayloadManager) PoolSize() int { + return len(tm.payloads) +} + +func New() *PayloadManager { + return &PayloadManager{ + payloads: []storedPayload{}, + } +} + +func (tm *PayloadManager) GetPayload() *payloadpbtypes.Payload { + // return oldest payload, where timestamp is a field in the payload + if len(tm.payloads) == 0 { + return nil + } + // sort by timestamp + // TODO: keep it sorted + slices.SortFunc(tm.payloads, func(i, j storedPayload) int { + return i.payload.Timestamp.AsTime().Compare(j.payload.Timestamp.AsTime()) + }) + return tm.payloads[0].payload +} + +func (tm *PayloadManager) AddPayload(payload *payloadpbtypes.Payload) error { + hash, err := hashstructure.Hash(payload, nil) + if err != nil { + return err + } + + if slices.ContainsFunc(tm.payloads, func(t storedPayload) bool { + return t.hash == hash + }) { + // already exists, ignore + return nil + } + + storedPaylod := storedPayload{ + hash: hash, + payload: payload, + } + + tm.payloads = append(tm.payloads, storedPaylod) + + return nil +} + +func (tm *PayloadManager) RemovePayload(payload *payloadpbtypes.Payload) error { + hash, err := hashstructure.Hash(payload, nil) + if err != nil { + return err + } + + // goes through all storedPayloads and removes the one with the matching hash + // NOTE: consider adding (hash) index to improve performance - not important rn + tm.payloads = slices.DeleteFunc(tm.payloads, func(t storedPayload) bool { + return t.hash == hash + }) + + return nil +} diff --git a/samples/blockchain-chat-mobile/main.go b/samples/blockchain-chat-mobile/main.go new file mode 100644 index 000000000..de3e85fac --- /dev/null +++ b/samples/blockchain-chat-mobile/main.go @@ -0,0 +1,170 @@ +package main + +import ( + "bufio" + "context" + "flag" + "fmt" + "os" + "strconv" + + "github.com/filecoin-project/mir" + "github.com/filecoin-project/mir/pkg/blockchain" + "github.com/filecoin-project/mir/pkg/blockchain/wsInterceptor" + "github.com/filecoin-project/mir/pkg/events" + "github.com/filecoin-project/mir/pkg/logging" + applicationpbevents "github.com/filecoin-project/mir/pkg/pb/blockchainpb/applicationpb/events" + "github.com/filecoin-project/mir/pkg/pb/eventpb" + t "github.com/filecoin-project/mir/pkg/types" + "github.com/filecoin-project/mir/samples/blockchain-chat-mobile/application" +) + +// Flags +var ( + disableMangle = flag.Bool("disableMangle", false, "Disable mangling of messages") + dropRate = flag.Float64("dropRate", 0.05, "The rate at which to drop messages") + minDelay = flag.Float64("minDelay", 0.001, "The minimum delay by which to delay messages between nodes [seconds]") + maxDelay = flag.Float64("maxDelay", 1, "The maximum delay by which to delay messages between nodes [seconds]") + exponentialMiningFactor = flag.Float64("expMiningFactor", 0.2, "Factor for exponential distribution for random mining duration") + numberOfNodes = flag.Int("numberOfNodes", -1, "The number of nodes in the network [1, inf] REQUIRED") + nodeID = flag.Int("nodeID", -1, "The ID of the node [0, numberOfNodes-1] REQUIRED") +) + +var idToUserMap = map[int]string{ + 0: "Alice", + 1: "Bob", + 2: "Charlie", + 3: "Donna", + 4: "Eve", +} + +func main() { + // Parse command line flags + flag.Parse() + + requiredFlags := []string{"numberOfNodes", "nodeID"} + flagsSet := make(map[string]bool) + missedRequiredFlags := false + + // Check which flags are set + flag.Visit(func(f *flag.Flag) { + flagsSet[f.Name] = true + }) + + // Check for missed required flags + for _, f := range requiredFlags { + if !flagsSet[f] { + fmt.Printf("Flag %s is required\n", f) + missedRequiredFlags = true + } + } + + // Exit if missing any required flag + if missedRequiredFlags { + os.Exit(2) + } + + // Check for valid flag values + if *numberOfNodes < 1 { + fmt.Printf("Number of nodes must be greater than 0.\n") + os.Exit(2) + } else if *nodeID < 0 || *nodeID >= *numberOfNodes { + fmt.Printf("Node ID must be between 0 and numberOfNodes-1.\n") + os.Exit(2) + } else if *minDelay < 0 { + fmt.Printf("Minimum delay must be greater than or equal to 0.\n") + os.Exit(2) + } else if *maxDelay < 0 { + fmt.Printf("Maximum delay must be greater than or equal to 0.\n") + os.Exit(2) + } else if *minDelay > *maxDelay { + fmt.Printf("Minimum delay must be less than or equal to maximum delay.\n") + os.Exit(2) + } else if *dropRate < 0 || *dropRate > 1 { + fmt.Printf("Drop rate must be between 0 and 1.\n") + os.Exit(2) + } else if *disableMangle && (flagsSet["dropRate"] || flagsSet["minDelay"] || flagsSet["maxDelay"]) { + fmt.Printf("WARNING: Settings for drop rate, minimum delay, and maximum delay are ignored when mangling is disabled.\n") + } + + ownNodeID := t.NodeID(strconv.Itoa(*nodeID)) + + // Setting up node + fmt.Println("Starting longest chain consensus protocol...") + + logger := logging.ConsoleInfoLogger + + system := blockchain.New( + ownNodeID, + application.NewApplication(ownNodeID, logging.Decorate(logger, "Application:\t")), + *disableMangle, + *dropRate, + *minDelay, + *maxDelay, + *exponentialMiningFactor, + *numberOfNodes, + logger, + logging.Decorate(logging.ConsoleInfoLogger, "Transport:\t")) + + // Instantiate Mir node. + node, err := mir.NewNode( + ownNodeID, + mir.DefaultNodeConfig(), + system.Modules(), + wsInterceptor.NewWsInterceptor( + func(e *eventpb.Event) bool { + switch e.Type.(type) { + case *eventpb.Event_Bcinterceptor: + return true + default: + return false + } + }, + 8080+*nodeID, + logging.Decorate(logger, "WSInter:\t"), + )) + if err != nil { + panic(err) + } + + fmt.Printf("Starting node %d: %s\n", *nodeID, idToUserMap[*nodeID]) + fmt.Printf("Interceptor exposed on port: %d\n", 8080+*nodeID) + fmt.Printf("Client server exposed on port: %d\n", 8180+*nodeID) + fmt.Printf("Access client under: http://k3-mbp:8280/?user=%s\n", idToUserMap[*nodeID]) + + ctx := context.Background() + + nodeError := make(chan error) + go func() { + nodeError <- node.Run(ctx) + }() + fmt.Println("Mir node running.") + + // ========================================================== + // Read chat messages from stdin and submit them as payloads. + // ========================================================== + + scanner := bufio.NewScanner(os.Stdin) + + // Prompt for chat message input. + fmt.Println("Type in your messages and press 'Enter' to send.") + + // Read chat message from stdin. + for scanner.Scan() { + // Submit the chat message as transaction payload to the mempool module. + if err := node.InjectEvents(ctx, events.ListOf( + applicationpbevents.MessageInput("application", scanner.Text()).Pb(), + )); err != nil { + // Print error if occurred. + fmt.Println(err) + } + + } + if err := scanner.Err(); err != nil { + fmt.Printf("Error reading input: %v\n", err) + } + + // Stop the node. + node.Stop() + fmt.Printf("Mir node stopped") +} diff --git a/samples/blockchain-chat-mobile/run.sh b/samples/blockchain-chat-mobile/run.sh new file mode 100755 index 000000000..ca89ab346 --- /dev/null +++ b/samples/blockchain-chat-mobile/run.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +CONFIG="-numberOfNodes 4 -expMiningFactor 0.15 -dropRate 0.01 -minDelay 0.001 -maxDelay 0.5" + +NODE_0_LOG="./node_0.log" +NODE_1_LOG="./node_1.log" +NODE_2_LOG="./node_2.log" +NODE_3_LOG="./node_3.log" + +rm -rf ./node_*.log + +tmux kill-session -t demo +tmux new-session -d -s demo \; \ + \ + split-window -t demo:0 -v \; \ + split-window -t demo:0.0 -h \; \ + split-window -t "demo:0.2" -h \; \ + \ + send-keys -t "demo:0.0" "go run . -nodeID 0 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.1" "go run . -nodeID 1 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.2" "go run . -nodeID 2 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.3" "go run . -nodeID 3 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + attach-session -t "demo:0.0" +#!/usr/bin/env bash +set -e diff --git a/samples/blockchain-chat-mobile/server/server.go b/samples/blockchain-chat-mobile/server/server.go new file mode 100644 index 000000000..a6768136a --- /dev/null +++ b/samples/blockchain-chat-mobile/server/server.go @@ -0,0 +1,68 @@ +package server + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + + payloadpbtypes "github.com/filecoin-project/mir/pkg/pb/blockchainpb/payloadpb/types" + "github.com/rs/cors" +) + +type Server struct { + history []*payloadpbtypes.Payload + port string + newMessageCallback func(message string) +} + +type postMsgPayload struct { + Message string `json:"message"` +} + +type getHistoryPayload struct { + Messages []*payloadpbtypes.Payload `json:"messages"` +} + +func (s *Server) handlePostMsg(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method Not Allowed - Only POST", http.StatusMethodNotAllowed) + } + + var pl postMsgPayload + if err := json.NewDecoder(r.Body).Decode(&pl); err != nil { + http.Error(w, "Expected {\"message\": \"some string\"}", http.StatusBadRequest) + } + + // pass msg to application + s.newMessageCallback(pl.Message) + + io.WriteString(w, "Submitted") +} + +func (s *Server) handleGetHistory(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + http.Error(w, "Method Not Allowed - Only GET", http.StatusMethodNotAllowed) + } + + json.NewEncoder(w).Encode(getHistoryPayload{s.history}) +} + +func (s *Server) SetHistory(history []*payloadpbtypes.Payload) { + s.history = history +} + +func (s *Server) Run() error { + http.HandleFunc("/sendMessage", s.handlePostMsg) + http.HandleFunc("/history", s.handleGetHistory) + fmt.Printf("===== Server running on port %s\n", s.port) + return http.ListenAndServe(s.port, cors.Default().Handler(http.DefaultServeMux)) +} + +func NewServer(port string, newMessageCallback func(message string)) *Server { + return &Server{ + history: []*payloadpbtypes.Payload{}, + port: port, + newMessageCallback: newMessageCallback, + } +} diff --git a/samples/blockchain-chat-mobile/test.sh b/samples/blockchain-chat-mobile/test.sh new file mode 100755 index 000000000..9cb4337c4 --- /dev/null +++ b/samples/blockchain-chat-mobile/test.sh @@ -0,0 +1,34 @@ +#!/bin/bash + +CONFIG="-numberOfNodes 4 -dropRate 0.05 -minDelay 0.01 -maxDelay 1" + +NODE_0_LOG="./node_0.log" +NODE_1_LOG="./node_1.log" +NODE_2_LOG="./node_2.log" +NODE_3_LOG="./node_3.log" + +rm -rf ./node_*.log + +tmux kill-session -t demo +tmux new-session -d -s demo \; \ + \ + split-window -t demo:0 -v \; \ + split-window -t demo:0.0 -h \; \ + split-window -t "demo:0.2" -h \; \ + \ + send-keys -t "demo:0.0" "go run . -nodeID 0 $CONFIG 2>&1 | tee \"$NODE_0_LOG\"" Enter \; \ + send-keys -t "demo:0.1" "go run . -nodeID 1 $CONFIG 2>&1 | tee \"$NODE_1_LOG\"" Enter \; \ + send-keys -t "demo:0.2" "go run . -nodeID 2 $CONFIG 2>&1 | tee \"$NODE_2_LOG\"" Enter \; \ + send-keys -t "demo:0.3" "go run . -nodeID 3 $CONFIG 2>&1 | tee \"$NODE_3_LOG\"" Enter \; + +sleep 5 # wait for it to start up +for i in {1..15}; do sleep 1; + tmux send-keys -t "demo:0.0" "0-$i" Enter \;; + tmux send-keys -t "demo:0.1" "1-$i" Enter \;; + tmux send-keys -t "demo:0.2" "2-$i" Enter \;; + tmux send-keys -t "demo:0.3" "3-$i" Enter \;; +done + +tmux attach-session -t "demo:0.0" +#!/usr/bin/env bash +set -e \ No newline at end of file

    6x4{3` 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�VP*MPnb8wDla=QH7DfTNnmRA5zl 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