From 03ff847058843b040201db96609067f3bd85df6b Mon Sep 17 00:00:00 2001 From: Ahmed Al-Salih Date: Thu, 23 Nov 2023 12:32:29 -0500 Subject: [PATCH 01/38] integrate the BDLS consensus algorithim in Fabric main branch Signed-off-by: Ahmed Al-Salih --- go.mod | 9 +- go.sum | 8 +- orderer/common/server/main.go | 6 +- orderer/consensus/bdls/agent-tcp/ecdh.go | 12 + orderer/consensus/bdls/agent-tcp/ecdh_test.go | 23 + orderer/consensus/bdls/agent-tcp/errors.go | 15 + orderer/consensus/bdls/agent-tcp/gossip.pb.go | 1130 +++++++++++ orderer/consensus/bdls/agent-tcp/gossip.proto | 36 + orderer/consensus/bdls/agent-tcp/tcp_peer.go | 657 +++++++ .../consensus/bdls/agent-tcp/tcp_peer_test.go | 220 +++ orderer/consensus/bdls/assembler.go | 195 ++ orderer/consensus/bdls/blockcreator.go | 46 + orderer/consensus/bdls/chain.go | 1075 +++++++++++ orderer/consensus/bdls/consenter.go | 285 +++ orderer/consensus/bdls/egress.go | 86 + orderer/consensus/bdls/ingress.go | 69 + orderer/consensus/bdls/metrics.go | 72 + orderer/consensus/bdls/multi-clients-TPS.go | 101 + orderer/consensus/bdls/signature.go | 95 + orderer/consensus/bdls/signer.go | 82 + orderer/consensus/bdls/util.go | 500 +++++ orderer/consensus/bdls/verifier.go | 447 +++++ .../github.com/BDLS-bft/bdls/.gitattributes | 2 + vendor/github.com/BDLS-bft/bdls/.gitignore | 3 + .../BDLS-bft/bdls/CODE_OF_CONDUCT.md | 8 + .../github.com/BDLS-bft/bdls/CONTRIBUTING.md | 32 + vendor/github.com/BDLS-bft/bdls/LICENSE | 202 ++ .../github.com/BDLS-bft/bdls/MAINTAINERS.md | 12 + vendor/github.com/BDLS-bft/bdls/README.md | 114 ++ vendor/github.com/BDLS-bft/bdls/SECURITY.md | 9 + vendor/github.com/BDLS-bft/bdls/config.go | 72 + vendor/github.com/BDLS-bft/bdls/consensus.go | 1677 +++++++++++++++++ .../BDLS-bft/bdls/crypto/blake2b/blake2b.go | 319 ++++ .../bdls/crypto/blake2b/blake2bAVX2_amd64.go | 37 + .../bdls/crypto/blake2b/blake2bAVX2_amd64.s | 717 +++++++ .../bdls/crypto/blake2b/blake2b_amd64.go | 24 + .../bdls/crypto/blake2b/blake2b_amd64.s | 253 +++ .../bdls/crypto/blake2b/blake2b_f_fuzz.go | 57 + .../bdls/crypto/blake2b/blake2b_generic.go | 180 ++ .../bdls/crypto/blake2b/blake2b_ref.go | 11 + .../BDLS-bft/bdls/crypto/blake2b/blake2x.go | 177 ++ .../BDLS-bft/bdls/crypto/blake2b/register.go | 32 + .../BDLS-bft/bdls/crypto/btcec/README.md | 68 + .../BDLS-bft/bdls/crypto/btcec/btcec.go | 976 ++++++++++ .../BDLS-bft/bdls/crypto/btcec/ciphering.go | 216 +++ .../BDLS-bft/bdls/crypto/btcec/doc.go | 21 + .../BDLS-bft/bdls/crypto/btcec/field.go | 1352 +++++++++++++ .../bdls/crypto/btcec/gensecp256k1.go | 203 ++ .../BDLS-bft/bdls/crypto/btcec/precompute.go | 67 + .../BDLS-bft/bdls/crypto/btcec/privkey.go | 73 + .../BDLS-bft/bdls/crypto/btcec/pubkey.go | 194 ++ .../BDLS-bft/bdls/crypto/btcec/secp256k1.go | 10 + .../BDLS-bft/bdls/crypto/btcec/signature.go | 540 ++++++ vendor/github.com/BDLS-bft/bdls/doc.go | 12 + vendor/github.com/BDLS-bft/bdls/errors.go | 82 + vendor/github.com/BDLS-bft/bdls/ipc_peer.go | 145 ++ vendor/github.com/BDLS-bft/bdls/message.go | 193 ++ vendor/github.com/BDLS-bft/bdls/message.pb.go | 1055 +++++++++++ vendor/github.com/BDLS-bft/bdls/message.proto | 54 + vendor/github.com/BDLS-bft/bdls/peer.go | 17 + vendor/github.com/BDLS-bft/bdls/protogen.sh | 3 + .../BDLS-bft/bdls/timer/timedsched.go | 147 ++ .../github.com/mattn/go-runewidth/.travis.yml | 14 +- .../go-runewidth/{README.mkd => README.md} | 2 +- .../github.com/mattn/go-runewidth/go.test.sh | 12 + .../mattn/go-runewidth/runewidth.go | 738 +------- .../mattn/go-runewidth/runewidth_posix.go | 5 +- .../mattn/go-runewidth/runewidth_table.go | 437 +++++ vendor/modules.txt | 10 +- 69 files changed, 15011 insertions(+), 742 deletions(-) create mode 100644 orderer/consensus/bdls/agent-tcp/ecdh.go create mode 100644 orderer/consensus/bdls/agent-tcp/ecdh_test.go create mode 100644 orderer/consensus/bdls/agent-tcp/errors.go create mode 100644 orderer/consensus/bdls/agent-tcp/gossip.pb.go create mode 100644 orderer/consensus/bdls/agent-tcp/gossip.proto create mode 100644 orderer/consensus/bdls/agent-tcp/tcp_peer.go create mode 100644 orderer/consensus/bdls/agent-tcp/tcp_peer_test.go create mode 100644 orderer/consensus/bdls/assembler.go create mode 100644 orderer/consensus/bdls/blockcreator.go create mode 100644 orderer/consensus/bdls/chain.go create mode 100644 orderer/consensus/bdls/consenter.go create mode 100644 orderer/consensus/bdls/egress.go create mode 100644 orderer/consensus/bdls/ingress.go create mode 100644 orderer/consensus/bdls/metrics.go create mode 100644 orderer/consensus/bdls/multi-clients-TPS.go create mode 100644 orderer/consensus/bdls/signature.go create mode 100644 orderer/consensus/bdls/signer.go create mode 100644 orderer/consensus/bdls/util.go create mode 100644 orderer/consensus/bdls/verifier.go create mode 100644 vendor/github.com/BDLS-bft/bdls/.gitattributes create mode 100644 vendor/github.com/BDLS-bft/bdls/.gitignore create mode 100644 vendor/github.com/BDLS-bft/bdls/CODE_OF_CONDUCT.md create mode 100644 vendor/github.com/BDLS-bft/bdls/CONTRIBUTING.md create mode 100644 vendor/github.com/BDLS-bft/bdls/LICENSE create mode 100644 vendor/github.com/BDLS-bft/bdls/MAINTAINERS.md create mode 100644 vendor/github.com/BDLS-bft/bdls/README.md create mode 100644 vendor/github.com/BDLS-bft/bdls/SECURITY.md create mode 100644 vendor/github.com/BDLS-bft/bdls/config.go create mode 100644 vendor/github.com/BDLS-bft/bdls/consensus.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/blake2b/blake2b.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/blake2b/blake2bAVX2_amd64.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/blake2b/blake2bAVX2_amd64.s create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/blake2b/blake2b_amd64.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/blake2b/blake2b_amd64.s create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/blake2b/blake2b_f_fuzz.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/blake2b/blake2b_generic.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/blake2b/blake2b_ref.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/blake2b/blake2x.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/blake2b/register.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/btcec/README.md create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/btcec/btcec.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/btcec/ciphering.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/btcec/doc.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/btcec/field.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/btcec/gensecp256k1.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/btcec/precompute.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/btcec/privkey.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/btcec/pubkey.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/btcec/secp256k1.go create mode 100644 vendor/github.com/BDLS-bft/bdls/crypto/btcec/signature.go create mode 100644 vendor/github.com/BDLS-bft/bdls/doc.go create mode 100644 vendor/github.com/BDLS-bft/bdls/errors.go create mode 100644 vendor/github.com/BDLS-bft/bdls/ipc_peer.go create mode 100644 vendor/github.com/BDLS-bft/bdls/message.go create mode 100644 vendor/github.com/BDLS-bft/bdls/message.pb.go create mode 100644 vendor/github.com/BDLS-bft/bdls/message.proto create mode 100644 vendor/github.com/BDLS-bft/bdls/peer.go create mode 100644 vendor/github.com/BDLS-bft/bdls/protogen.sh create mode 100644 vendor/github.com/BDLS-bft/bdls/timer/timedsched.go rename vendor/github.com/mattn/go-runewidth/{README.mkd => README.md} (82%) create mode 100644 vendor/github.com/mattn/go-runewidth/go.test.sh create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_table.go diff --git a/go.mod b/go.mod index e5aeb08c10b..28b9c0bdda3 100644 --- a/go.mod +++ b/go.mod @@ -46,7 +46,11 @@ require ( gopkg.in/yaml.v2 v2.4.0 ) -require google.golang.org/protobuf v1.30.0 +require ( + github.com/BDLS-bft/bdls v0.0.0-20230426164822-28f164ce5034 + github.com/gogo/protobuf v1.3.2 + google.golang.org/protobuf v1.30.0 +) require ( github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect @@ -66,7 +70,6 @@ require ( github.com/felixge/httpsnoop v1.0.1 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect - github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hyperledger/fabric-amcl v0.0.0-20210603140002-2670f91851c8 // indirect @@ -74,7 +77,7 @@ require ( github.com/klauspost/compress v1.13.6 // indirect github.com/kr/text v0.2.0 // indirect github.com/magiconair/properties v1.8.1 // indirect - github.com/mattn/go-runewidth v0.0.4 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/moby/patternmatcher v0.6.0 // indirect diff --git a/go.sum b/go.sum index 4402010e4de..7ccb739fe94 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,7 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +code.cloudfoundry.org/bytefmt v0.0.0-20211005130812-5bb3c17173e5 h1:tM5+dn2C9xZw1RzgI6WTQW1rGqdUimKB3RFbyu4h6Hc= code.cloudfoundry.org/clock v1.0.0 h1:kFXWQM4bxYvdBw2X8BbBeXwQNgfoWv1vqAk2ZZyBN2o= code.cloudfoundry.org/clock v1.0.0/go.mod h1:QD9Lzhd/ux6eNQVUDVRJX/RKTigpewimNYBi7ivZKY8= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -38,6 +39,8 @@ github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9 github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/BDLS-bft/bdls v0.0.0-20230426164822-28f164ce5034 h1:HhyRyqlWnacpLMM18NJQEkFo1On4GUZyOcTPwpj2H7c= +github.com/BDLS-bft/bdls v0.0.0-20230426164822-28f164ce5034/go.mod h1:hesZ3fp+xEnW6dqvjJrbKru9K0v7y/kQloGL7iD4vR4= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/IBM/idemix v0.0.0-20220112103229-701e7610d405 h1:7cKDQL0CWDXO9acHJCCc7SUYpMcJ9H9NGVjLvwG66Nc= @@ -89,6 +92,7 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bits-and-blooms/bitset v1.2.1 h1:M+/hrU9xlMp7t4TyTDQW97d3tRPVuKFC6zBEK16QnXY= github.com/bits-and-blooms/bitset v1.2.1/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/btcsuite/btcd v0.22.0-beta h1:LTDpDKUM5EeOFBPM8IXpinEcmZ6FWfNZbE3lfrfdnWo= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -376,8 +380,9 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= @@ -427,6 +432,7 @@ github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtb github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= diff --git a/orderer/common/server/main.go b/orderer/common/server/main.go index 80cf7770442..293fc289487 100644 --- a/orderer/common/server/main.go +++ b/orderer/common/server/main.go @@ -42,8 +42,8 @@ import ( "github.com/hyperledger/fabric/orderer/common/metadata" "github.com/hyperledger/fabric/orderer/common/multichannel" "github.com/hyperledger/fabric/orderer/consensus" + "github.com/hyperledger/fabric/orderer/consensus/bdls" "github.com/hyperledger/fabric/orderer/consensus/etcdraft" - "github.com/hyperledger/fabric/orderer/consensus/smartbft" "github.com/hyperledger/fabric/protoutil" "go.uber.org/zap/zapcore" "google.golang.org/grpc" @@ -623,7 +623,9 @@ func initializeMultichannelRegistrar( // the orderer can start without channels at all and have an initialized cluster type consenter etcdraftConsenter, clusterMetrics := etcdraft.New(clusterDialer, conf, srvConf, srv, registrar, metricsProvider, bccsp) consenters["etcdraft"] = etcdraftConsenter - consenters["BFT"] = smartbft.New(dpmr.Registry(), signer, clusterDialer, conf, srvConf, srv, registrar, metricsProvider, clusterMetrics, bccsp) + //consenters["BFT"] = smartbft.New(dpmr.Registry(), signer, clusterDialer, conf, srvConf, srv, registrar, metricsProvider, clusterMetrics, bccsp) + + consenters["BFT"] = bdls.New(dpmr.Registry(), signer, clusterDialer, conf, srvConf, srv, registrar, metricsProvider, clusterMetrics, bccsp) registrar.Initialize(consenters) return registrar diff --git a/orderer/consensus/bdls/agent-tcp/ecdh.go b/orderer/consensus/bdls/agent-tcp/ecdh.go new file mode 100644 index 00000000000..d0b9744d889 --- /dev/null +++ b/orderer/consensus/bdls/agent-tcp/ecdh.go @@ -0,0 +1,12 @@ + +package agent + +import ( + "crypto/ecdsa" + "math/big" +) + +func ECDH(publicKey *ecdsa.PublicKey, key *ecdsa.PrivateKey) *big.Int { + secret, _ := key.Curve.ScalarMult(publicKey.X, publicKey.Y, key.D.Bytes()) + return secret +} diff --git a/orderer/consensus/bdls/agent-tcp/ecdh_test.go b/orderer/consensus/bdls/agent-tcp/ecdh_test.go new file mode 100644 index 00000000000..733fecbea7a --- /dev/null +++ b/orderer/consensus/bdls/agent-tcp/ecdh_test.go @@ -0,0 +1,23 @@ +package agent + +import ( + "crypto/ecdsa" + "crypto/rand" + "testing" + + "github.com/BDLS-bft/bdls" + + "github.com/stretchr/testify/assert" +) + +func TestECDH(t *testing.T) { + key1, err := ecdsa.GenerateKey(bdls.S256Curve, rand.Reader) + assert.Nil(t, err) + key2, err := ecdsa.GenerateKey(bdls.S256Curve, rand.Reader) + assert.Nil(t, err) + + s1 := ECDH(&key1.PublicKey, key2) + s2 := ECDH(&key2.PublicKey, key1) + + assert.Equal(t, s1, s2) +} diff --git a/orderer/consensus/bdls/agent-tcp/errors.go b/orderer/consensus/bdls/agent-tcp/errors.go new file mode 100644 index 00000000000..f8286ca4bec --- /dev/null +++ b/orderer/consensus/bdls/agent-tcp/errors.go @@ -0,0 +1,15 @@ + + +package agent + +import "errors" + +var ( + ErrLocalKeyAuthInit = errors.New("incorrect state for local KeyAuthInitmessage") + ErrKeyNotOnCurve = errors.New("the public key is not on curve") + ErrPeerKeyAuthInit = errors.New("incorrect state for peer KeyAuthInit message") + ErrPeerKeyAuthChallenge = errors.New("incorrect state for peer KeyAuthChallenge message") + ErrPeerKeyAuthChallengeResponse = errors.New("incorrect state for peer KeyAuthChallengeResponse message") + ErrPeerAuthenticatedFailed = errors.New("public key authentication failed for peer") + ErrMessageLengthExceed = errors.New("message size exceeded maximum") +) diff --git a/orderer/consensus/bdls/agent-tcp/gossip.pb.go b/orderer/consensus/bdls/agent-tcp/gossip.pb.go new file mode 100644 index 00000000000..8470a867ba9 --- /dev/null +++ b/orderer/consensus/bdls/agent-tcp/gossip.pb.go @@ -0,0 +1,1130 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: gossip.proto + +package agent + +import ( + fmt "fmt" + proto "github.com/gogo/protobuf/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// CommandType defines supported commands +type CommandType int32 + +const ( + CommandType_NOP CommandType = 0 + CommandType_KEY_AUTH_INIT CommandType = 1 + CommandType_KEY_AUTH_CHALLENGE CommandType = 2 + CommandType_KEY_AUTH_CHALLENGE_REPLY CommandType = 3 + CommandType_CONSENSUS CommandType = 4 +) + +var CommandType_name = map[int32]string{ + 0: "NOP", + 1: "KEY_AUTH_INIT", + 2: "KEY_AUTH_CHALLENGE", + 3: "KEY_AUTH_CHALLENGE_REPLY", + 4: "CONSENSUS", +} + +var CommandType_value = map[string]int32{ + "NOP": 0, + "KEY_AUTH_INIT": 1, + "KEY_AUTH_CHALLENGE": 2, + "KEY_AUTH_CHALLENGE_REPLY": 3, + "CONSENSUS": 4, +} + +func (x CommandType) String() string { + return proto.EnumName(CommandType_name, int32(x)) +} + +func (CommandType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_878fa4887b90140c, []int{0} +} + +// Gossip defines a stream based protocol +type Gossip struct { + Command CommandType `protobuf:"varint,1,opt,name=Command,proto3,enum=agent.CommandType" json:"Command,omitempty"` + Message []byte `protobuf:"bytes,2,opt,name=Message,proto3" json:"Message,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Gossip) Reset() { *m = Gossip{} } +func (m *Gossip) String() string { return proto.CompactTextString(m) } +func (*Gossip) ProtoMessage() {} +func (*Gossip) Descriptor() ([]byte, []int) { + return fileDescriptor_878fa4887b90140c, []int{0} +} +func (m *Gossip) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Gossip) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Gossip.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Gossip) XXX_Merge(src proto.Message) { + xxx_messageInfo_Gossip.Merge(m, src) +} +func (m *Gossip) XXX_Size() int { + return m.Size() +} +func (m *Gossip) XXX_DiscardUnknown() { + xxx_messageInfo_Gossip.DiscardUnknown(m) +} + +var xxx_messageInfo_Gossip proto.InternalMessageInfo + +func (m *Gossip) GetCommand() CommandType { + if m != nil { + return m.Command + } + return CommandType_NOP +} + +func (m *Gossip) GetMessage() []byte { + if m != nil { + return m.Message + } + return nil +} + +type KeyAuthInit struct { + // client public key + X []byte `protobuf:"bytes,1,opt,name=X,proto3" json:"X,omitempty"` + Y []byte `protobuf:"bytes,2,opt,name=Y,proto3" json:"Y,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *KeyAuthInit) Reset() { *m = KeyAuthInit{} } +func (m *KeyAuthInit) String() string { return proto.CompactTextString(m) } +func (*KeyAuthInit) ProtoMessage() {} +func (*KeyAuthInit) Descriptor() ([]byte, []int) { + return fileDescriptor_878fa4887b90140c, []int{1} +} +func (m *KeyAuthInit) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KeyAuthInit) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_KeyAuthInit.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *KeyAuthInit) XXX_Merge(src proto.Message) { + xxx_messageInfo_KeyAuthInit.Merge(m, src) +} +func (m *KeyAuthInit) XXX_Size() int { + return m.Size() +} +func (m *KeyAuthInit) XXX_DiscardUnknown() { + xxx_messageInfo_KeyAuthInit.DiscardUnknown(m) +} + +var xxx_messageInfo_KeyAuthInit proto.InternalMessageInfo + +func (m *KeyAuthInit) GetX() []byte { + if m != nil { + return m.X + } + return nil +} + +func (m *KeyAuthInit) GetY() []byte { + if m != nil { + return m.Y + } + return nil +} + +type KeyAuthChallenge struct { + // server ephermal publickey for client authentication + X []byte `protobuf:"bytes,1,opt,name=X,proto3" json:"X,omitempty"` + Y []byte `protobuf:"bytes,2,opt,name=Y,proto3" json:"Y,omitempty"` + // the challenge message, the peer can create the correct HMAC with this message + Challenge []byte `protobuf:"bytes,3,opt,name=Challenge,proto3" json:"Challenge,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *KeyAuthChallenge) Reset() { *m = KeyAuthChallenge{} } +func (m *KeyAuthChallenge) String() string { return proto.CompactTextString(m) } +func (*KeyAuthChallenge) ProtoMessage() {} +func (*KeyAuthChallenge) Descriptor() ([]byte, []int) { + return fileDescriptor_878fa4887b90140c, []int{2} +} +func (m *KeyAuthChallenge) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KeyAuthChallenge) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_KeyAuthChallenge.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *KeyAuthChallenge) XXX_Merge(src proto.Message) { + xxx_messageInfo_KeyAuthChallenge.Merge(m, src) +} +func (m *KeyAuthChallenge) XXX_Size() int { + return m.Size() +} +func (m *KeyAuthChallenge) XXX_DiscardUnknown() { + xxx_messageInfo_KeyAuthChallenge.DiscardUnknown(m) +} + +var xxx_messageInfo_KeyAuthChallenge proto.InternalMessageInfo + +func (m *KeyAuthChallenge) GetX() []byte { + if m != nil { + return m.X + } + return nil +} + +func (m *KeyAuthChallenge) GetY() []byte { + if m != nil { + return m.Y + } + return nil +} + +func (m *KeyAuthChallenge) GetChallenge() []byte { + if m != nil { + return m.Challenge + } + return nil +} + +type KeyAuthChallengeReply struct { + HMAC []byte `protobuf:"bytes,1,opt,name=HMAC,proto3" json:"HMAC,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *KeyAuthChallengeReply) Reset() { *m = KeyAuthChallengeReply{} } +func (m *KeyAuthChallengeReply) String() string { return proto.CompactTextString(m) } +func (*KeyAuthChallengeReply) ProtoMessage() {} +func (*KeyAuthChallengeReply) Descriptor() ([]byte, []int) { + return fileDescriptor_878fa4887b90140c, []int{3} +} +func (m *KeyAuthChallengeReply) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *KeyAuthChallengeReply) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_KeyAuthChallengeReply.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *KeyAuthChallengeReply) XXX_Merge(src proto.Message) { + xxx_messageInfo_KeyAuthChallengeReply.Merge(m, src) +} +func (m *KeyAuthChallengeReply) XXX_Size() int { + return m.Size() +} +func (m *KeyAuthChallengeReply) XXX_DiscardUnknown() { + xxx_messageInfo_KeyAuthChallengeReply.DiscardUnknown(m) +} + +var xxx_messageInfo_KeyAuthChallengeReply proto.InternalMessageInfo + +func (m *KeyAuthChallengeReply) GetHMAC() []byte { + if m != nil { + return m.HMAC + } + return nil +} + +func init() { + proto.RegisterEnum("agent.CommandType", CommandType_name, CommandType_value) + proto.RegisterType((*Gossip)(nil), "agent.Gossip") + proto.RegisterType((*KeyAuthInit)(nil), "agent.KeyAuthInit") + proto.RegisterType((*KeyAuthChallenge)(nil), "agent.KeyAuthChallenge") + proto.RegisterType((*KeyAuthChallengeReply)(nil), "agent.KeyAuthChallengeReply") +} + +func init() { proto.RegisterFile("gossip.proto", fileDescriptor_878fa4887b90140c) } + +var fileDescriptor_878fa4887b90140c = []byte{ + // 285 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x91, 0xcd, 0x6a, 0x83, 0x50, + 0x10, 0x85, 0x7b, 0x63, 0x1a, 0xc9, 0xc4, 0x94, 0xdb, 0x81, 0x16, 0x17, 0x41, 0x82, 0xab, 0xf4, + 0x07, 0x17, 0xed, 0x13, 0x58, 0x91, 0x28, 0x31, 0x46, 0xd4, 0x40, 0x5c, 0x89, 0xa5, 0x17, 0x13, + 0x30, 0x2a, 0xd5, 0x2e, 0x7c, 0xc3, 0x2e, 0xfb, 0x08, 0xc5, 0x27, 0x29, 0xbd, 0x98, 0xb4, 0xb4, + 0x90, 0xdd, 0x9c, 0x8f, 0x6f, 0x0e, 0x0c, 0x03, 0x52, 0x5a, 0x54, 0xd5, 0xae, 0xd4, 0xca, 0xd7, + 0xa2, 0x2e, 0xf0, 0x3c, 0x49, 0x59, 0x5e, 0xab, 0x1e, 0x0c, 0xe6, 0x1c, 0xe3, 0x3d, 0x88, 0x46, + 0xb1, 0xdf, 0x27, 0xf9, 0x8b, 0x4c, 0xa6, 0x64, 0x76, 0xf1, 0x80, 0x1a, 0x57, 0xb4, 0x8e, 0x86, + 0x4d, 0xc9, 0xfc, 0x83, 0x82, 0x32, 0x88, 0x4b, 0x56, 0x55, 0x49, 0xca, 0xe4, 0xde, 0x94, 0xcc, + 0x24, 0xff, 0x10, 0xd5, 0x1b, 0x18, 0x2d, 0x58, 0xa3, 0xbf, 0xd5, 0x5b, 0x3b, 0xdf, 0xd5, 0x28, + 0x01, 0xd9, 0xf0, 0x42, 0xc9, 0x27, 0x9b, 0xef, 0x14, 0x75, 0x0b, 0x24, 0x52, 0x1d, 0xa0, 0x9d, + 0x6a, 0x6c, 0x93, 0x2c, 0x63, 0x79, 0xca, 0x4e, 0xf9, 0x38, 0x81, 0xe1, 0x51, 0x94, 0x05, 0x4e, + 0x7f, 0x80, 0x7a, 0x07, 0x57, 0x7f, 0xdb, 0x7c, 0x56, 0x66, 0x0d, 0x22, 0xf4, 0xad, 0xa5, 0x6e, + 0x74, 0xad, 0x7c, 0xbe, 0xcd, 0x61, 0xf4, 0xeb, 0x2e, 0x14, 0x41, 0x70, 0x57, 0x1e, 0x3d, 0xc3, + 0x4b, 0x18, 0x2f, 0xcc, 0x28, 0xd6, 0xd7, 0xa1, 0x15, 0xdb, 0xae, 0x1d, 0x52, 0x82, 0xd7, 0x80, + 0x47, 0x64, 0x58, 0xba, 0xe3, 0x98, 0xee, 0xdc, 0xa4, 0x3d, 0x9c, 0x80, 0xfc, 0x9f, 0xc7, 0xbe, + 0xe9, 0x39, 0x11, 0x15, 0x70, 0x0c, 0x43, 0x63, 0xe5, 0x06, 0xa6, 0x1b, 0xac, 0x03, 0xda, 0x7f, + 0x92, 0xde, 0x5b, 0x85, 0x7c, 0xb4, 0x0a, 0xf9, 0x6c, 0x15, 0xf2, 0x3c, 0xe0, 0x3f, 0x78, 0xfc, + 0x0a, 0x00, 0x00, 0xff, 0xff, 0x96, 0x66, 0x6e, 0x1f, 0x93, 0x01, 0x00, 0x00, +} + +func (m *Gossip) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Gossip) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Gossip) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintGossip(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x12 + } + if m.Command != 0 { + i = encodeVarintGossip(dAtA, i, uint64(m.Command)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *KeyAuthInit) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KeyAuthInit) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KeyAuthInit) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Y) > 0 { + i -= len(m.Y) + copy(dAtA[i:], m.Y) + i = encodeVarintGossip(dAtA, i, uint64(len(m.Y))) + i-- + dAtA[i] = 0x12 + } + if len(m.X) > 0 { + i -= len(m.X) + copy(dAtA[i:], m.X) + i = encodeVarintGossip(dAtA, i, uint64(len(m.X))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *KeyAuthChallenge) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KeyAuthChallenge) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KeyAuthChallenge) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.Challenge) > 0 { + i -= len(m.Challenge) + copy(dAtA[i:], m.Challenge) + i = encodeVarintGossip(dAtA, i, uint64(len(m.Challenge))) + i-- + dAtA[i] = 0x1a + } + if len(m.Y) > 0 { + i -= len(m.Y) + copy(dAtA[i:], m.Y) + i = encodeVarintGossip(dAtA, i, uint64(len(m.Y))) + i-- + dAtA[i] = 0x12 + } + if len(m.X) > 0 { + i -= len(m.X) + copy(dAtA[i:], m.X) + i = encodeVarintGossip(dAtA, i, uint64(len(m.X))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *KeyAuthChallengeReply) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *KeyAuthChallengeReply) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *KeyAuthChallengeReply) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.HMAC) > 0 { + i -= len(m.HMAC) + copy(dAtA[i:], m.HMAC) + i = encodeVarintGossip(dAtA, i, uint64(len(m.HMAC))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func encodeVarintGossip(dAtA []byte, offset int, v uint64) int { + offset -= sovGossip(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *Gossip) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Command != 0 { + n += 1 + sovGossip(uint64(m.Command)) + } + l = len(m.Message) + if l > 0 { + n += 1 + l + sovGossip(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *KeyAuthInit) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.X) + if l > 0 { + n += 1 + l + sovGossip(uint64(l)) + } + l = len(m.Y) + if l > 0 { + n += 1 + l + sovGossip(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *KeyAuthChallenge) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.X) + if l > 0 { + n += 1 + l + sovGossip(uint64(l)) + } + l = len(m.Y) + if l > 0 { + n += 1 + l + sovGossip(uint64(l)) + } + l = len(m.Challenge) + if l > 0 { + n += 1 + l + sovGossip(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *KeyAuthChallengeReply) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.HMAC) + if l > 0 { + n += 1 + l + sovGossip(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovGossip(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGossip(x uint64) (n int) { + return sovGossip(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *Gossip) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Gossip: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Gossip: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Command", wireType) + } + m.Command = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Command |= CommandType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGossip + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGossip + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) + if m.Message == nil { + m.Message = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGossip(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGossip + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGossip + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KeyAuthInit) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KeyAuthInit: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KeyAuthInit: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGossip + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGossip + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.X = append(m.X[:0], dAtA[iNdEx:postIndex]...) + if m.X == nil { + m.X = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Y", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGossip + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGossip + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Y = append(m.Y[:0], dAtA[iNdEx:postIndex]...) + if m.Y == nil { + m.Y = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGossip(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGossip + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGossip + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KeyAuthChallenge) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KeyAuthChallenge: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KeyAuthChallenge: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGossip + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGossip + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.X = append(m.X[:0], dAtA[iNdEx:postIndex]...) + if m.X == nil { + m.X = []byte{} + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Y", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGossip + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGossip + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Y = append(m.Y[:0], dAtA[iNdEx:postIndex]...) + if m.Y == nil { + m.Y = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Challenge", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGossip + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGossip + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Challenge = append(m.Challenge[:0], dAtA[iNdEx:postIndex]...) + if m.Challenge == nil { + m.Challenge = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGossip(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGossip + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGossip + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *KeyAuthChallengeReply) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: KeyAuthChallengeReply: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: KeyAuthChallengeReply: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field HMAC", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGossip + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthGossip + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthGossip + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.HMAC = append(m.HMAC[:0], dAtA[iNdEx:postIndex]...) + if m.HMAC == nil { + m.HMAC = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGossip(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthGossip + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthGossip + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGossip(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGossip + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGossip + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGossip + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGossip + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGossip + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGossip + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGossip = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGossip = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGossip = fmt.Errorf("proto: unexpected end of group") +) diff --git a/orderer/consensus/bdls/agent-tcp/gossip.proto b/orderer/consensus/bdls/agent-tcp/gossip.proto new file mode 100644 index 00000000000..9b424d0ea3d --- /dev/null +++ b/orderer/consensus/bdls/agent-tcp/gossip.proto @@ -0,0 +1,36 @@ + +syntax = "proto3"; +package agent; + +// CommandType defines supported commands +enum CommandType { + NOP = 0; + KEY_AUTH_INIT=1; + KEY_AUTH_CHALLENGE=2; + KEY_AUTH_CHALLENGE_REPLY= 3; + CONSENSUS=4; +} + +// Gossip defines a stream based protocol +message Gossip{ + CommandType Command = 1; + bytes Message=2; +} + +message KeyAuthInit { + // client public key + bytes X = 1; + bytes Y = 2; +} + +message KeyAuthChallenge { + // server ephermal publickey for client authentication + bytes X=1; + bytes Y=2; + // the challenge message, the peer can create the correct HMAC with this message + bytes Challenge=3; +} + +message KeyAuthChallengeReply{ + bytes HMAC=1; +} diff --git a/orderer/consensus/bdls/agent-tcp/tcp_peer.go b/orderer/consensus/bdls/agent-tcp/tcp_peer.go new file mode 100644 index 00000000000..9aad5d5aaa8 --- /dev/null +++ b/orderer/consensus/bdls/agent-tcp/tcp_peer.go @@ -0,0 +1,657 @@ +package agent + +import ( + "crypto/ecdsa" + "crypto/rand" + "crypto/subtle" + "encoding/binary" + fmt "fmt" + io "io" + "log" + "math/big" + "net" + "sync" + "time" + "unsafe" + + "github.com/BDLS-bft/bdls" + "github.com/BDLS-bft/bdls/crypto/blake2b" + + proto "github.com/gogo/protobuf/proto" +) + +const ( + // Frame format: + // |MessageLength(4bytes)| Message(MessageLength) ... | + MessageLength = 4 + + // Message max length(32MB) + MaxMessageLength = 32 * 1024 * 1024 + + // timeout for a unresponsive connection + defaultReadTimeout = 60 * time.Second + defaultWriteTimeout = 60 * time.Second + + // challengeSize + challengeSize = 1024 +) + +// authenticationState is the authentication status for both peer +type authenticationState byte + +// peer initated public-key authentication status +const ( + // peerNotAuthenticated: the peer has just connected + peerNotAuthenticated authenticationState = iota + // peerSentAuthkey: the peer begined it's public key authentication, + // and we've sent out our challenge. + peerAuthkeyReceived + // peerAuthenticated: the peer has been authenticated to it's public key + peerAuthenticated + // peer failed to accept our challenge + peerAuthenticatedFailed +) + +// local initated public key authentication status +const ( + localNotAuthenticated authenticationState = iota + // localSentAuthKey: we have sent auth key command to the peer + localAuthKeySent + // localChallengeAccepted: we have received challenge from peer and responded + localChallengeAccepted +) + +// A TCPAgent binds consensus core to a TCPAgent object, which may have multiple TCPPeer +type TCPAgent struct { + consensus *bdls.Consensus // the consensus core + privateKey *ecdsa.PrivateKey // a private key to sign messages + peers []*TCPPeer // connected peers + consensusMessages [][]byte // all consensus message awaiting to be processed + chConsensusMessages chan struct{} // notification of new consensus message + + die chan struct{} // tcp agent closing + dieOnce sync.Once + sync.Mutex // fields lock +} + +// NewTCPAgent initiate a TCPAgent which talks consensus protocol with peers +func NewTCPAgent(consensus *bdls.Consensus, privateKey *ecdsa.PrivateKey) *TCPAgent { + agent := new(TCPAgent) + agent.consensus = consensus + agent.privateKey = privateKey + agent.die = make(chan struct{}) + agent.chConsensusMessages = make(chan struct{}, 1) + go agent.inputConsensusMessage() + return agent +} + +// AddPeer adds a peer to this agent +func (agent *TCPAgent) AddPeer(p *TCPPeer) bool { + agent.Lock() + defer agent.Unlock() + + select { + case <-agent.die: + return false + default: + agent.peers = append(agent.peers, p) + return agent.consensus.Join(p) + } +} + +// RemovePeer removes a TCPPeer from this agent +func (agent *TCPAgent) RemovePeer(p *TCPPeer) bool { + agent.Lock() + defer agent.Unlock() + + peerAddress := p.RemoteAddr().String() + for k := range agent.peers { + if agent.peers[k].RemoteAddr().String() == peerAddress { + copy(agent.peers[k:], agent.peers[k+1:]) + agent.peers = agent.peers[:len(agent.peers)-1] + return agent.consensus.Leave(p.RemoteAddr()) + } + } + return false +} + +// Close stops all activities on this agent +func (agent *TCPAgent) Close() { + agent.Lock() + defer agent.Unlock() + + agent.dieOnce.Do(func() { + close(agent.die) + // close all peers + for k := range agent.peers { + agent.peers[k].Close() + } + }) +} + +// Update is the consensus updater +func (agent *TCPAgent) Update() { + agent.Lock() + defer agent.Unlock() + + select { + case <-agent.die: + default: + // call consensus update + agent.consensus.Update(time.Now()) + //timer.SystemTimedSched.Put(agent.Update, time.Now().Add(20*time.Millisecond)) + } +} + +// Propose a state, awaiting to be finalized at next height. +func (agent *TCPAgent) Propose(s bdls.State) { + agent.Lock() + defer agent.Unlock() + agent.consensus.Propose(s) +} + +// GetLatestState returns latest state +func (agent *TCPAgent) GetLatestState() (height uint64, round uint64, data bdls.State) { + agent.Lock() + defer agent.Unlock() + return agent.consensus.CurrentState() +} + +// handleConsensusMessage will be called if TCPPeer received a consensus message +func (agent *TCPAgent) handleConsensusMessage(bts []byte) { + agent.Lock() + defer agent.Unlock() + agent.consensusMessages = append(agent.consensusMessages, bts) + agent.notifyConsensus() +} + +func (agent *TCPAgent) notifyConsensus() { + select { + case agent.chConsensusMessages <- struct{}{}: + default: + } +} + +// consensus message receiver +func (agent *TCPAgent) inputConsensusMessage() { + for { + select { + case <-agent.chConsensusMessages: + agent.Lock() + msgs := agent.consensusMessages + agent.consensusMessages = nil + + for _, msg := range msgs { + agent.consensus.ReceiveMessage(msg, time.Now()) + } + agent.Unlock() + case <-agent.die: + return + } + } +} + +// fake address for Pipe +type fakeAddress string + +func (fakeAddress) Network() string { return "pipe" } +func (f fakeAddress) String() string { return string(f) } + +// TCPPeer represents a peer(endpoint) related to a tcp connection +type TCPPeer struct { + agent *TCPAgent // the agent it belongs to + conn net.Conn // the connection to this peer + peerAuthStatus authenticationState // peer authentication status + // the announced public key of the peer, only becomes valid if peerAuthStatus == peerAuthenticated + peerPublicKey *ecdsa.PublicKey + + // local authentication status + localAuthState authenticationState + + // the HMAC of the challenge text if peer has requested key authentication + hmac []byte + + // message queues and their notifications + consensusMessages [][]byte // all pending outgoing consensus messages to this peer + chConsensusMessage chan struct{} // notification on new consensus data + + // agent messages + agentMessages [][]byte // all pending outgoing agent messages to this peer. + chAgentMessage chan struct{} // notification on new agent exchange messages + + // peer closing signal + die chan struct{} + dieOnce sync.Once + + // mutex for all fields + sync.Mutex +} + +// NewTCPPeer creates a TCPPeer with protocol over this connection +func NewTCPPeer(conn net.Conn, agent *TCPAgent) *TCPPeer { + p := new(TCPPeer) + p.chConsensusMessage = make(chan struct{}, 1) + p.chAgentMessage = make(chan struct{}, 1) + p.conn = conn + p.agent = agent + p.die = make(chan struct{}) + // we start readLoop & sendLoop for each connection + go p.readLoop() + go p.sendLoop() + return p +} + +// RemoteAddr implements PeerInterface, GetPublicKey returns peer's +// public key, returns nil if peer's has not authenticated it's public-key +func (p *TCPPeer) GetPublicKey() *ecdsa.PublicKey { + p.Lock() + defer p.Unlock() + if p.peerAuthStatus == peerAuthenticated { + //log.Println("get public key:", p.peerPublicKey) + return p.peerPublicKey + } + return nil +} + +// RemoteAddr implements PeerInterface, returns peer's address as connection identity +func (p *TCPPeer) RemoteAddr() net.Addr { + if p.conn.RemoteAddr().Network() == "pipe" { + return fakeAddress(fmt.Sprint(unsafe.Pointer(p))) + } + return p.conn.RemoteAddr() +} + +// Send implements PeerInterface, to send message to this peer +func (p *TCPPeer) Send(out []byte) error { + p.Lock() + defer p.Unlock() + p.consensusMessages = append(p.consensusMessages, out) + p.notifyConsensusMessage() + return nil +} + +// notifyConsensusMessage notifies goroutines there're messages pending to send +func (p *TCPPeer) notifyConsensusMessage() { + select { + case p.chConsensusMessage <- struct{}{}: + default: + } +} + +// notifyAgentMessage, notifies goroutines there're agent messages pending to send +func (p *TCPPeer) notifyAgentMessage() { + select { + case p.chAgentMessage <- struct{}{}: + default: + } +} + +// Close terminates connection to this peer +func (p *TCPPeer) Close() { + p.dieOnce.Do(func() { + p.conn.Close() + close(p.die) + }) + go p.agent.RemovePeer(p) +} + +// InitiatePublicKeyAuthentication will initate a procedure to convince +// the other peer to trust my ownership of public key +func (p *TCPPeer) InitiatePublicKeyAuthentication() error { + p.Lock() + defer p.Unlock() + if p.localAuthState == localNotAuthenticated { + auth := KeyAuthInit{} + auth.X = p.agent.privateKey.PublicKey.X.Bytes() + auth.Y = p.agent.privateKey.PublicKey.Y.Bytes() + + // proto marshal + bts, err := proto.Marshal(&auth) + if err != nil { + log.Println(err) + panic(err) + } + + g := Gossip{Command: CommandType_KEY_AUTH_INIT, Message: bts} + // proto marshal + out, err := proto.Marshal(&g) + if err != nil { + log.Println(err) + panic(err) + } + + // enqueue + p.agentMessages = append(p.agentMessages, out) + p.notifyAgentMessage() + p.localAuthState = localAuthKeySent + return nil + } else { + return ErrPeerKeyAuthInit + } +} + +// handleGossip will process all messages from this peer based on it's message types +func (p *TCPPeer) handleGossip(msg *Gossip) error { + switch msg.Command { + case CommandType_NOP: // NOP can be used for connection keepalive + case CommandType_KEY_AUTH_INIT: + // this peer initated it's publickey authentication + var m KeyAuthInit + err := proto.Unmarshal(msg.Message, &m) + if err != nil { + log.Println(err) + return err + } + + err = p.handleKeyAuthInit(&m) + if err != nil { + log.Println(err) + return err + } + case CommandType_KEY_AUTH_CHALLENGE: + // received a challenge from this peer + var m KeyAuthChallenge + err := proto.Unmarshal(msg.Message, &m) + if err != nil { + log.Println(err) + return err + } + + err = p.handleKeyAuthChallenge(&m) + if err != nil { + log.Println(err) + return err + } + + case CommandType_KEY_AUTH_CHALLENGE_REPLY: + // this peer sends back a challenge reply to authenticate it's publickey + var m KeyAuthChallengeReply + err := proto.Unmarshal(msg.Message, &m) + if err != nil { + log.Println(err) + return err + } + + err = p.handleKeyAuthChallengeReply(&m) + if err != nil { + log.Println(err) + return err + } + + case CommandType_CONSENSUS: + // received a consensus message from this peer + p.agent.handleConsensusMessage(msg.Message) + default: + panic(msg) + } + return nil +} + +// peer initiated key authentication +func (p *TCPPeer) handleKeyAuthInit(authKey *KeyAuthInit) error { + p.Lock() + defer p.Unlock() + // only when in init status, authentication process cannot rollback + // to prevent from malicious re-authentication DoS + if p.peerAuthStatus == peerNotAuthenticated { + peerPublicKey := &ecdsa.PublicKey{Curve: bdls.S256Curve, X: big.NewInt(0).SetBytes(authKey.X), Y: big.NewInt(0).SetBytes(authKey.Y)} + + // on curve test + if !bdls.S256Curve.IsOnCurve(peerPublicKey.X, peerPublicKey.Y) { + p.peerAuthStatus = peerAuthenticatedFailed + return ErrKeyNotOnCurve + } + // temporarily stored announced key + p.peerPublicKey = peerPublicKey + + // create ephermal key for authentication + ephemeral, err := ecdsa.GenerateKey(bdls.S256Curve, rand.Reader) + if err != nil { + log.Println(err) + panic(err) + } + // derive secret + secret := ECDH(p.peerPublicKey, ephemeral) + + // generate challenge texts + var challenge KeyAuthChallenge + challenge.X = ephemeral.PublicKey.X.Bytes() + challenge.Y = ephemeral.PublicKey.Y.Bytes() + challenge.Challenge = make([]byte, challengeSize) + _, err = io.ReadFull(rand.Reader, challenge.Challenge) + if err != nil { + log.Println(err) + panic(err) + } + + // calculates & store HMAC for this random message + hmac, err := blake2b.New256(secret.Bytes()) + if err != nil { + log.Println(err) + panic(err) + } + hmac.Write(challenge.Challenge) + p.hmac = hmac.Sum(nil) + + // proto marshal + bts, err := proto.Marshal(&challenge) + if err != nil { + log.Println(err) + panic(err) + } + + g := Gossip{Command: CommandType_KEY_AUTH_CHALLENGE, Message: bts} + // proto marshal + out, err := proto.Marshal(&g) + if err != nil { + log.Println(err) + panic(err) + } + + // enqueue + p.agentMessages = append(p.agentMessages, out) + p.notifyAgentMessage() + + // state shift + p.peerAuthStatus = peerAuthkeyReceived + return nil + } else { + return ErrPeerKeyAuthInit + } +} + +// handle key authentication challenge +func (p *TCPPeer) handleKeyAuthChallenge(challenge *KeyAuthChallenge) error { + p.Lock() + defer p.Unlock() + if p.localAuthState == localAuthKeySent { + // use ECDH to recover shared-key + pubkey := &ecdsa.PublicKey{Curve: bdls.S256Curve, X: big.NewInt(0).SetBytes(challenge.X), Y: big.NewInt(0).SetBytes(challenge.Y)} + // derive secret with my private key + secret := ECDH(pubkey, p.agent.privateKey) + + // calculates HMAC for the challenge with the key above + var response KeyAuthChallengeReply + hmac, err := blake2b.New256(secret.Bytes()) + if err != nil { + log.Println(err) + panic(err) + } + hmac.Write(challenge.Challenge) + response.HMAC = hmac.Sum(nil) + + // proto marshal + bts, err := proto.Marshal(&response) + if err != nil { + log.Println(err) + panic(err) + } + + g := Gossip{Command: CommandType_KEY_AUTH_CHALLENGE_REPLY, Message: bts} + // proto marshal + out, err := proto.Marshal(&g) + if err != nil { + log.Println(err) + panic(err) + } + + // enqueue + p.agentMessages = append(p.agentMessages, out) + p.notifyAgentMessage() + + // state shift + p.localAuthState = localChallengeAccepted + return nil + } else { + return ErrPeerKeyAuthChallenge + } +} + +// handle key authentication challenge reply +func (p *TCPPeer) handleKeyAuthChallengeReply(response *KeyAuthChallengeReply) error { + p.Lock() + defer p.Unlock() + if p.peerAuthStatus == peerAuthkeyReceived { + if subtle.ConstantTimeCompare(p.hmac, response.HMAC) == 1 { + p.hmac = nil + p.peerAuthStatus = peerAuthenticated + return nil + } else { + p.peerAuthStatus = peerAuthenticatedFailed + return ErrPeerAuthenticatedFailed + } + } else { + return ErrPeerKeyAuthInit + } +} + +// readLoop keeps reading messages from peer +func (p *TCPPeer) readLoop() { + defer p.Close() + msgLength := make([]byte, MessageLength) + + for { + select { + case <-p.die: + return + default: + // read message size + p.conn.SetReadDeadline(time.Now().Add(defaultReadTimeout)) + _, err := io.ReadFull(p.conn, msgLength) + if err != nil { + log.Println(err) + return + } + + // check length + length := binary.LittleEndian.Uint32(msgLength) + if length > MaxMessageLength { + log.Println(err) + return + } + + if length == 0 { + log.Println("zero length") + return + } + + // read message bytes + p.conn.SetReadDeadline(time.Now().Add(defaultReadTimeout)) + bts := make([]byte, length) + _, err = io.ReadFull(p.conn, bts) + if err != nil { + log.Println(err) + return + } + + // unmarshal bytes to message + var gossip Gossip + err = proto.Unmarshal(bts, &gossip) + if err != nil { + log.Println(err) + return + } + + err = p.handleGossip(&gossip) + if err != nil { + log.Println(err) + return + } + } + } +} + +// sendLoop keeps sending consensus message to this peer +func (p *TCPPeer) sendLoop() { + defer p.Close() + + var pending [][]byte + var msg Gossip + msg.Command = CommandType_CONSENSUS + msgLength := make([]byte, MessageLength) + + for { + select { + case <-p.chConsensusMessage: + p.Lock() + pending = p.consensusMessages + p.consensusMessages = nil + p.Unlock() + + for _, bts := range pending { + // we need to encapsulate consensus messages + msg.Message = bts + out, err := proto.Marshal(&msg) + if err != nil { + log.Println(err) + panic(err) + } + + if len(out) > MaxMessageLength { + panic("maximum message size exceeded") + } + + binary.LittleEndian.PutUint32(msgLength, uint32(len(out))) + p.conn.SetWriteDeadline(time.Now().Add(defaultWriteTimeout)) + // write length + _, err = p.conn.Write(msgLength) + if err != nil { + log.Println(err) + return + } + + // write message + _, err = p.conn.Write(out) + if err != nil { + log.Println(err) + return + } + } + case <-p.chAgentMessage: + p.Lock() + pending = p.agentMessages + p.agentMessages = nil + p.Unlock() + + for _, bts := range pending { + binary.LittleEndian.PutUint32(msgLength, uint32(len(bts))) + // write length + _, err := p.conn.Write(msgLength) + if err != nil { + log.Println(err) + return + } + + // write message + _, err = p.conn.Write(bts) + if err != nil { + log.Println(err) + return + } + } + + case <-p.die: + return + } + } +} diff --git a/orderer/consensus/bdls/agent-tcp/tcp_peer_test.go b/orderer/consensus/bdls/agent-tcp/tcp_peer_test.go new file mode 100644 index 00000000000..9f4186b2ce9 --- /dev/null +++ b/orderer/consensus/bdls/agent-tcp/tcp_peer_test.go @@ -0,0 +1,220 @@ +package agent + +import ( + "bytes" + "crypto/ecdsa" + "crypto/rand" + "encoding/binary" + "encoding/hex" + io "io" + "log" + "net" + "net/http" + _ "net/http/pprof" + "sync" + "testing" + "time" + + + "github.com/BDLS-bft/bdls" + "github.com/BDLS-bft/bdls/crypto/blake2b" + + "github.com/davecgh/go-spew/spew" + "github.com/stretchr/testify/assert" +) + +// init will listen for 6060 while debugging +func init() { + go func() { + log.Println(http.ListenAndServe("0.0.0.0:6060", nil)) + }() +} + +type testParam struct { + numPeers int + numParticipants int + stopHeight int + expectedLatency time.Duration +} + +func TestTCPPeer(t *testing.T) { + var params = []testParam{ + { + numPeers: 20, + numParticipants: 20, + stopHeight: 5, + expectedLatency: 100 * time.Millisecond, + }, + { + numPeers: 20, + numParticipants: 20, + stopHeight: 5, + expectedLatency: 200 * time.Millisecond, + }, + { + numPeers: 20, + numParticipants: 20, + stopHeight: 5, + expectedLatency: 300 * time.Millisecond, + }, + { + numPeers: 20, + numParticipants: 20, + stopHeight: 5, + expectedLatency: 500 * time.Millisecond, + }, + { + numPeers: 20, + numParticipants: 20, + stopHeight: 5, + expectedLatency: 1000 * time.Millisecond, + }, + } + for i := 0; i < len(params); i++ { + t.Logf("-=-=- TESTING CASE: [%v/%v] -=-=-", i+1, len(params)) + testConsensus(t, ¶ms[i]) + } +} + +func testConsensus(t *testing.T, param *testParam) { + t.Logf("PARAMETERS: %+v", spew.Sprintf("%+v", param)) + var participants []*ecdsa.PrivateKey + var coords []bdls.Identity + for i := 0; i < param.numParticipants; i++ { + privateKey, err := ecdsa.GenerateKey(bdls.S256Curve, rand.Reader) + if err != nil { + t.Fatal(err) + } + + participants = append(participants, privateKey) + coords = append(coords, bdls.DefaultPubKeyToIdentity(&privateKey.PublicKey)) + } + + // consensus for one height + consensusOneHeight := func(currentHeight uint64) { + // randomize participants, fisher yates shuffle + n := uint32(len(participants)) + for i := n - 1; i > 0; i-- { + var j uint32 + binary.Read(rand.Reader, binary.LittleEndian, &j) + j = j % (i + 1) + participants[i], participants[j] = participants[j], participants[i] + } + + // created a locked consensus object + var all []*bdls.Consensus + + // same epoch + epoch := time.Now() + // create numPeer peers + for i := 0; i < param.numPeers; i++ { + // initiate config + config := new(bdls.Config) + config.Epoch = epoch + config.CurrentHeight = currentHeight + config.PrivateKey = participants[i] // randomized participants + config.Participants = coords // keep all pubkeys + + // should replace with real function + config.StateCompare = func(a bdls.State, b bdls.State) int { return bytes.Compare(a, b) } + config.StateValidate = func(a bdls.State) bool { return true } + + // consensus + consensus, err := bdls.NewConsensus(config) + assert.Nil(t, err) + consensus.SetLatency(param.expectedLatency) + all = append(all, consensus) + } + + // establish full connected mesh with tcp_peer + numConns := 0 + agents := make([]*TCPAgent, len(all)) + for i := 0; i < len(all); i++ { + agents[i] = NewTCPAgent(all[i], participants[i]) + } + + for i := 0; i < len(all); i++ { + for j := 0; j < len(all); j++ { + if i != j { + c1, c2 := net.Pipe() // in memory duplex pipe to connection i & j + p1 := NewTCPPeer(c1, agents[i]) + p2 := NewTCPPeer(c2, agents[j]) + ok := agents[i].AddPeer(p1) + assert.True(t, ok) + ok = agents[j].AddPeer(p2) + assert.True(t, ok) + numConns += 2 + + // auth public key + p1.InitiatePublicKeyAuthentication() + p2.InitiatePublicKeyAuthentication() + } + } + } + + <-time.After(2 * time.Second) + + // make sure authentication completed + for i := 0; i < len(all); i++ { + for _, peer := range agents[i].peers { + peer.Lock() + assert.Equal(t, peer.localAuthState, localChallengeAccepted) + assert.Equal(t, peer.peerAuthStatus, peerAuthenticated) + peer.Unlock() + } + } + + // after all connections have established, start updater, + // this must be done after connection establishement + // to prevent from missing messages + for i := 0; i < len(all); i++ { + agents[i].Update() + } + + var wg sync.WaitGroup + wg.Add(param.numPeers) + + // selected random peers + for k := range agents { + go func(i int) { + agent := agents[i] + defer wg.Done() + + data := make([]byte, 1024) + io.ReadFull(rand.Reader, data) + agent.Propose(data) + + for { + newHeight, newRound, newState := agent.GetLatestState() + if newHeight > currentHeight { + now := time.Now() + // only one peer print the decide + if i == 0 { + h := blake2b.Sum256(newState) + t.Logf("%v at height:%v round:%v hash:%v", now.Format("15:04:05"), newHeight, newRound, hex.EncodeToString(h[:])) + } + + return + } + + // wait + <-time.After(20 * time.Millisecond) + } + }(k) + } + + // wait for all peers exit + wg.Wait() + // close all peers when waitgroup exit + for k := range agents { + agents[k].Close() + } + } + + // loop to stopHeight + for i := 0; i < param.stopHeight; i++ { + consensusOneHeight(uint64(i)) + } + + t.Logf("consensus stopped at height:%v for %v peers %v participants", param.stopHeight, param.numPeers, param.numParticipants) +} diff --git a/orderer/consensus/bdls/assembler.go b/orderer/consensus/bdls/assembler.go new file mode 100644 index 00000000000..ea5d6bac274 --- /dev/null +++ b/orderer/consensus/bdls/assembler.go @@ -0,0 +1,195 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import ( + "encoding/asn1" + "sync/atomic" + + "github.com/SmartBFT-Go/consensus/pkg/types" + cb "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric/common/flogging" + "github.com/hyperledger/fabric/orderer/common/cluster" + "github.com/hyperledger/fabric/protoutil" + "github.com/pkg/errors" +) + +//go:generate mockery -dir . -name Ledger -case underscore -output mocks + +// Ledger returns the height and a block with the given number +type Ledger interface { + // Height returns the number of blocks in the ledger this channel is associated with. + Height() uint64 + + // Block returns a block with the given number, + // or nil if such a block doesn't exist. + Block(number uint64) *cb.Block +} + +// Assembler is the proposal assembler +type Assembler struct { + RuntimeConfig *atomic.Value + Logger *flogging.FabricLogger + VerificationSeq func() uint64 +} + +// AssembleProposal assembles a proposal from the metadata and the request +func (a *Assembler) AssembleProposal(metadata []byte, requests [][]byte) (nextProp types.Proposal) { + rtc := a.RuntimeConfig.Load().(RuntimeConfig) + + lastConfigBlockNum := rtc.LastConfigBlock.Header.Number + lastBlock := rtc.LastBlock + + if len(requests) == 0 { + a.Logger.Panicf("Programming error, no requests in proposal") + } + batchedRequests := singleConfigTxOrSeveralNonConfigTx(requests, a.Logger) + + block := protoutil.NewBlock(lastBlock.Header.Number+1, protoutil.BlockHeaderHash(lastBlock.Header)) + block.Data = &cb.BlockData{Data: batchedRequests} + //block.Header.DataHash = protoutil.BlockDataHash(block.Data) + block.Header.DataHash = protoutil.ComputeBlockDataHash(block.Data) + + if protoutil.IsConfigBlock(block) { + lastConfigBlockNum = block.Header.Number + } + + block.Metadata.Metadata[cb.BlockMetadataIndex_LAST_CONFIG] = protoutil.MarshalOrPanic(&cb.Metadata{ + Value: protoutil.MarshalOrPanic(&cb.LastConfig{Index: lastConfigBlockNum}), + }) + block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES] = protoutil.MarshalOrPanic(&cb.Metadata{ + Value: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{ + ConsenterMetadata: metadata, + LastConfig: &cb.LastConfig{ + Index: lastConfigBlockNum, + }, + }), + }) + + tuple := &ByteBufferTuple{ + A: protoutil.MarshalOrPanic(block.Data), + B: protoutil.MarshalOrPanic(block.Metadata), + } + + prop := types.Proposal{ + Header: protoutil.BlockHeaderBytes(block.Header), + Payload: tuple.ToBytes(), + Metadata: metadata, + VerificationSequence: int64(a.VerificationSeq()), + } + + return prop +} + +func singleConfigTxOrSeveralNonConfigTx(requests [][]byte, logger Logger) [][]byte { + // Scan until a config transaction is found + var batchedRequests [][]byte + var i int + for i < len(requests) { + currentRequest := requests[i] + envelope, err := protoutil.UnmarshalEnvelope(currentRequest) + if err != nil { + logger.Panicf("Programming error, received bad envelope but should have validated it: %v", err) + continue + } + + // If we saw a config transaction, we cannot add any more transactions to the batch. + if protoutil.IsConfigTransaction(envelope) { + break + } + + // Else, it's not a config transaction, so add it to the batch. + batchedRequests = append(batchedRequests, currentRequest) + i++ + } + + // If we don't have any transaction in the batch, it is safe to assume we only + // saw a single transaction which is a config transaction. + if len(batchedRequests) == 0 { + batchedRequests = [][]byte{requests[0]} + } + + // At this point, batchedRequests contains either a single config transaction, or a few non config transactions. + return batchedRequests +} + +// LastConfigBlockFromLedgerOrPanic returns the last config block from the ledger +func LastConfigBlockFromLedgerOrPanic(ledger Ledger, logger Logger) *cb.Block { + block, err := lastConfigBlockFromLedger(ledger) + if err != nil { + logger.Panicf("Failed retrieving last config block: %v", err) + } + return block +} + +func lastConfigBlockFromLedger(ledger Ledger) (*cb.Block, error) { + lastBlockSeq := ledger.Height() - 1 + lastBlock := ledger.Block(lastBlockSeq) + if lastBlock == nil { + return nil, errors.Errorf("unable to retrieve block [%d]", lastBlockSeq) + } + lastConfigBlock, err := cluster.LastConfigBlock(lastBlock, ledger) + if err != nil { + return nil, err + } + return lastConfigBlock, nil +} + +func PreviousConfigBlockFromLedgerOrPanic(ledger Ledger, logger Logger) *cb.Block { + block, err := previousConfigBlockFromLedger(ledger) + if err != nil { + logger.Panicf("Failed retrieving previous config block: %v", err) + } + return block +} + +func previousConfigBlockFromLedger(ledger Ledger) (*cb.Block, error) { + previousBlockSeq := ledger.Height() - 2 + if ledger.Height() == 1 { + previousBlockSeq = 0 + } + previousBlock := ledger.Block(previousBlockSeq) + if previousBlock == nil { + return nil, errors.Errorf("unable to retrieve block [%d]", previousBlockSeq) + } + previousConfigBlock, err := cluster.LastConfigBlock(previousBlock, ledger) + if err != nil { + return nil, err + } + return previousConfigBlock, nil +} + +// LastBlockFromLedgerOrPanic returns the last block from the ledger +func LastBlockFromLedgerOrPanic(ledger Ledger, logger Logger) *cb.Block { + lastBlockSeq := ledger.Height() - 1 + lastBlock := ledger.Block(lastBlockSeq) + if lastBlock == nil { + logger.Panicf("Failed retrieving last block") + } + return lastBlock +} + +// ByteBufferTuple is the byte slice tuple +type ByteBufferTuple struct { + A []byte + B []byte +} + +// ToBytes marshals the buffer tuple to bytes +func (bbt *ByteBufferTuple) ToBytes() []byte { + bytes, err := asn1.Marshal(*bbt) + if err != nil { + panic(err) + } + return bytes +} + +// FromBytes unmarshals bytes to a buffer tuple +func (bbt *ByteBufferTuple) FromBytes(bytes []byte) error { + _, err := asn1.Unmarshal(bytes, bbt) + return err +} diff --git a/orderer/consensus/bdls/blockcreator.go b/orderer/consensus/bdls/blockcreator.go new file mode 100644 index 00000000000..484d8a2e1f9 --- /dev/null +++ b/orderer/consensus/bdls/blockcreator.go @@ -0,0 +1,46 @@ +/* +Copyright Ahmed Al Salih All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import ( + "github.com/golang/protobuf/proto" + cb "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric/common/flogging" + "github.com/hyperledger/fabric/protoutil" +) + +// blockCreator holds number and hash of latest block +// so that next block will be created based on it. +type blockCreator struct { + hash []byte + number uint64 + + logger *flogging.FabricLogger +} + +func (bc *blockCreator) createNextBlock(envs []*cb.Envelope) *cb.Block { + data := &cb.BlockData{ + Data: make([][]byte, len(envs)), + } + + var err error + for i, env := range envs { + data.Data[i], err = proto.Marshal(env) + if err != nil { + bc.logger.Panicf("Could not marshal envelope: %s", err) + } + } + + bc.number++ + + block := protoutil.NewBlock(bc.number, bc.hash) + block.Header.DataHash = protoutil.ComputeBlockDataHash(data) + block.Data = data + + bc.hash = protoutil.BlockHeaderHash(block.Header) + return block +} diff --git a/orderer/consensus/bdls/chain.go b/orderer/consensus/bdls/chain.go new file mode 100644 index 00000000000..c074e3ac418 --- /dev/null +++ b/orderer/consensus/bdls/chain.go @@ -0,0 +1,1075 @@ +/* +Copyright Ahmed Al Salih. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import ( + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/x509" + "encoding/pem" + "fmt" + "math/big" + "net" + "sync" + "sync/atomic" + "time" + + "code.cloudfoundry.org/clock" + "github.com/BDLS-bft/bdls" + "github.com/hyperledger/fabric-protos-go/common" + + //cb "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric-protos-go/orderer" + "github.com/hyperledger/fabric/orderer/common/msgprocessor" + + types2 "github.com/hyperledger/fabric/orderer/common/types" + + //"google.golang.org/protobuf/proto" + "github.com/golang/protobuf/proto" + //"github.com/hyperledger/fabric-protos-go/msp" + //"github.com/hyperledger/fabric-protos-go/orderer/etcdraft" + "github.com/hyperledger/fabric/bccsp" + "github.com/hyperledger/fabric/common/flogging" + "github.com/hyperledger/fabric/common/policies" + "github.com/hyperledger/fabric/orderer/common/cluster" + "github.com/hyperledger/fabric/orderer/consensus" + "github.com/hyperledger/fabric/protoutil" + "github.com/pkg/errors" + "go.uber.org/zap" + + "github.com/BDLS-bft/bdls/crypto/btcec" + agent "github.com/hyperledger/fabric/orderer/consensus/bdls/agent-tcp" +) + +// ConfigValidator interface +type ConfigValidator interface { + ValidateConfig(env *common.Envelope) error +} + +type BlockPuller interface { + PullBlock(seq uint64) *common.Block + HeightsByEndpoints() (map[string]uint64, error) + Close() +} + +// secp256k1 elliptic curve +var S256Curve elliptic.Curve = btcec.S256() + +const ( + baseLatency = 500 * time.Millisecond + maxBaseLatency = 10 * time.Second + proposalCollectionTimeout = 3 * time.Second + updatePeriod = 20 * time.Millisecond + resendPeriod = 10 * time.Second +) + +type signerSerializer interface { + // Sign a message and return the signature over the digest, or error on failure + Sign(message []byte) ([]byte, error) + + // Serialize converts an identity to bytes + Serialize() ([]byte, error) +} + +type submit struct { + req *orderer.SubmitRequest + //leader chan uint64 +} + +type apply struct { + //height uint64 + //round uint64 + state bdls.State +} + +// Chain represents a BDLS chain. +type Chain struct { + bdlsId uint64 + Channel string + + //agent *agent + + //BDLS + consensus *bdls.Consensus + config *bdls.Config + consensusMessages [][]byte // all consensus message awaiting to be processed + sync.Mutex // fields lock + chConsensusMessages chan struct{} // notification of new consensus message + + submitC chan *submit + applyC chan apply + haltC chan struct{} // Signals to goroutines that the chain is halting + doneC chan struct{} // Closes when the chain halts + startC chan struct{} // Closes when the node is started + + errorCLock sync.RWMutex + errorC chan struct{} // returned by Errored() + haltCallback func() + + Logger *flogging.FabricLogger + support consensus.ConsenterSupport + verifier *Verifier + opts Options + + lastBlock *common.Block + //TBD + RuntimeConfig *atomic.Value + + //Config types.Configuration + BlockPuller BlockPuller + Comm cluster.Communicator + SignerSerializer signerSerializer + PolicyManager policies.Manager + + WALDir string + + clusterService *cluster.ClusterService + + assembler *Assembler + Metrics *Metrics + bccsp bccsp.BCCSP + + bdlsChainLock sync.RWMutex + + unreachableLock sync.RWMutex + unreachable map[uint64]struct{} + + statusReportMutex sync.Mutex + consensusRelation types2.ConsensusRelation + status types2.Status + + configInflight bool // this is true when there is config block or ConfChange in flight + blockInflight int // number of in flight blocks + transportLayer *agent.TCPAgent + + latency time.Duration + die chan struct{} + dieOnce sync.Once + msgCount int64 + bytesCount int64 + minLatency time.Duration + maxLatency time.Duration + totalLatency time.Duration + + clock clock.Clock // Tests can inject a fake clock +} + +type Options struct { + //BlockMetadata *etcdraft.BlockMetadata + Clock clock.Clock + // BlockMetadata and Consenters should only be modified while under lock + // of bdlsChainLock + //Consenters map[uint64]*etcdraft.Consenter + Consenters []*common.Consenter + + portAddress string + + MaxInflightBlocks int +} + +// Order accepts a message which has been processed at a given configSeq. +func (c *Chain) Order(env *common.Envelope, configSeq uint64) error { + + seq := c.support.Sequence() + if configSeq < seq { + c.Logger.Warnf("Normal message was validated against %d, although current config seq has advanced (%d)", configSeq, seq) + if _, err := c.support.ProcessNormalMsg(env); err != nil { + return errors.Errorf("bad normal message: %s", err) + } + } + + return c.submit(env, configSeq) +} + +// Configure accepts a message which reconfigures the channel +func (c *Chain) Configure(env *common.Envelope, configSeq uint64) error { + seq := c.support.Sequence() + if configSeq < seq { + c.Logger.Warnf("Normal message was validated against %d, although current config seq has advanced (%d)", configSeq, seq) + if configEnv, _, err := c.support.ProcessConfigMsg(env); err != nil { + return errors.Errorf("bad normal message: %s", err) + } else { + return c.submit(configEnv, configSeq) + } + } + return c.submit(env, configSeq) +} + +func (c *Chain) submit(env *common.Envelope, configSeq uint64) error { + + /*if err := c.isRunning(); err != nil { + c.Metrics.ProposalFailures.Add(1) + return err + }*/ + req := &orderer.SubmitRequest{LastValidationSeq: configSeq, Payload: env, Channel: c.Channel} + + select { + case c.submitC <- &submit{req}: + return nil + case <-c.doneC: + c.Metrics.ProposalFailures.Add(1) + return errors.Errorf("chain is stopped") + } + +} + +// WaitReady blocks waiting for consenter to be ready for accepting new messages. +func (c *Chain) WaitReady() error { + if err := c.isRunning(); err != nil { + return err + } + + select { + case c.submitC <- nil: + case <-c.doneC: + return errors.Errorf("chain is stopped") + } + return nil +} + +// Errored returns a channel which will close when an error has occurred. +func (c *Chain) Errored() <-chan struct{} { + //TODO + return nil +} + +// NewChain creates new chain +func NewChain( + //cv ConfigValidator, + selfID uint64, + //config types.Configuration, + walDir string, + blockPuller BlockPuller, + comm cluster.Communicator, + signerSerializer signerSerializer, + policyManager policies.Manager, + support consensus.ConsenterSupport, + metrics *Metrics, + bccsp bccsp.BCCSP, + opts Options, + +) (*Chain, error) { + /*requestInspector := &RequestInspector{ + ValidateIdentityStructure: func(_ *msp.SerializedIdentity) error { + return nil + }, + }*/ + + logger := flogging.MustGetLogger("orderer.consensus.bdls.chain").With(zap.String("channel", support.ChannelID())) + //oldb := support.Block(support.Height() - 1) + b := LastBlockFromLedgerOrPanic(support, logger) + + if b == nil { + return nil, errors.Errorf("failed to get last block") + } + + c := &Chain{ + //RuntimeConfig: &atomic.Value{}, + Channel: support.ChannelID(), + //Config: config, + lastBlock: b, + WALDir: walDir, + Comm: comm, + support: support, + + SignerSerializer: signerSerializer, + PolicyManager: policyManager, + BlockPuller: blockPuller, + Logger: logger, + opts: opts, + bdlsId: selfID, + + applyC: make(chan apply), + submitC: make(chan *submit), + haltC: make(chan struct{}), + doneC: make(chan struct{}), + startC: make(chan struct{}), + errorC: make(chan struct{}), + + clock: opts.Clock, + consensusRelation: types2.ConsensusRelationConsenter, + status: types2.StatusActive, + + Metrics: &Metrics{ + ClusterSize: metrics.ClusterSize.With("channel", support.ChannelID()), + CommittedBlockNumber: metrics.CommittedBlockNumber.With("channel", support.ChannelID()), + IsLeader: metrics.IsLeader.With("channel", support.ChannelID()), + LeaderID: metrics.LeaderID.With("channel", support.ChannelID()), + }, + bccsp: bccsp, + + chConsensusMessages: make(chan struct{}, 1), + } + /* + lastBlock := LastBlockFromLedgerOrPanic(support, c.Logger) + lastConfigBlock := LastConfigBlockFromLedgerOrPanic(support, c.Logger) + + rtc := RuntimeConfig{ + logger: logger, + id: selfID, + }*/ + /* rtc, err := rtc.BlockCommitted(lastConfigBlock, bccsp) + if err != nil { + return nil, errors.Wrap(err, "failed constructing RuntimeConfig") + } + rtc, err = rtc.BlockCommitted(lastBlock, bccsp) + if err != nil { + return nil, errors.Wrap(err, "failed constructing RuntimeConfig") + } + + c.RuntimeConfig.Store(rtc) + */ + //c.verifier = buildVerifier(cv, c.RuntimeConfig, support, requestInspector, policyManager) + //c.consensus = bftSmartConsensusBuild(c, requestInspector) + + // Setup communication with list of remotes notes for the new channel + + //if err != nil { + // return nil, errors.WithStack(err) + //} + + /*privateKey, err := ecdsa.GenerateKey(S256Curve, rand.Reader) + if err != nil { + c.Logger.Warnf("error generating privateKey value:", err) + }*/ + + // setup consensus config at the given height + config := &bdls.Config{ + Epoch: time.Now(), + CurrentHeight: c.lastBlock.Header.Number, //support.Height() - 1, //0, + StateCompare: func(a bdls.State, b bdls.State) int { return bytes.Compare(a, b) }, + StateValidate: func(bdls.State) bool { return true }, + } + /*config := new(bdls.Config) + config.Epoch = time.Now() + config.CurrentHeight = 0 // c.support.Height() + config.StateCompare = func(a bdls.State, b bdls.State) int { return bytes.Compare(a, b) } + config.StateValidate = func(bdls.State) bool { return true } + */ + Keys := make([]string, 0) + Keys = append(Keys, + "68082493172628484253808951113461196766221768923883438540199548009461479956986", + "44652770827640294682875208048383575561358062645764968117337703282091165609211", + "80512969964988849039583604411558290822829809041684390237207179810031917243659", + "55978351916851767744151875911101025920456547576858680756045508192261620541580") + for k := range Keys { //c.opts.Consenters { + //for k := range c.opts.Consenters { + i := new(big.Int) + _, err := fmt.Sscan(Keys[k], i) + if err != nil { + c.Logger.Warnf("error scanning value:", err) + } + priv := new(ecdsa.PrivateKey) + priv.PublicKey.Curve = bdls.S256Curve + priv.D = i + priv.PublicKey.X, priv.PublicKey.Y = bdls.S256Curve.ScalarBaseMult(priv.D.Bytes()) + // myself + logger.Info("XXXXXXX ", c.bdlsId, k, " XXXXXXXX ") + if int(c.bdlsId) == k+1 { + logger.Info("----- T r u e -----") + config.PrivateKey = priv + } + + // set validator sequence + config.Participants = append(config.Participants, bdls.DefaultPubKeyToIdentity(&priv.PublicKey)) + } + + c.config = config + + /*if err := c.startConsensus(config); err != nil { + logger.Error(err) + }*/ + + // create the consensus object + /*consensus, err := bdls.NewConsensus(config) + if err != nil { + logger.Warnf(" ^^^^^^^ Cannot init BDLS instincas: %v", err) + return nil, errors.WithStack(err) + + }*/ + + //c.consensus = consensus + + //c.opts.Consenters + nodes, err := c.remotePeers() + + if err != nil { + return nil, errors.WithStack(err) + } + c.Comm.Configure(c.support.ChannelID(), nodes) + + /* if err := c.consensus.ValidateConfiguration(rtc.Nodes); err != nil { + return nil, errors.Wrap(err, "failed to verify SmartBFT-Go configuration") + } + */ + logger.Infof("BDLS is now servicing chain %s", support.ChannelID()) + + return c, nil +} + +// Halt frees the resources which were allocated for this Chain. +func (c *Chain) Halt() { + + //TODO +} + +// Get the remote peers from the []*cb.Consenter +func (c *Chain) remotePeers() ([]cluster.RemoteNode, error) { + c.bdlsChainLock.RLock() + defer c.bdlsChainLock.RUnlock() + + var nodes []cluster.RemoteNode + for id, consenter := range c.opts.Consenters { + // No need to know yourself + if uint64(id) == c.bdlsId { + //c.opts.portAddress = fmt.Sprint(consenter.Port) + continue + } + serverCertAsDER, err := pemToDER(consenter.ServerTlsCert, uint64(id), "server", c.Logger) + if err != nil { + return nil, errors.WithStack(err) + } + clientCertAsDER, err := pemToDER(consenter.ClientTlsCert, uint64(id), "client", c.Logger) + if err != nil { + return nil, errors.WithStack(err) + } + nodes = append(nodes, cluster.RemoteNode{ + NodeAddress: cluster.NodeAddress{ + ID: uint64(id), + Endpoint: fmt.Sprintf("%s:%d", consenter.Host, consenter.Port), + }, + NodeCerts: cluster.NodeCerts{ + ServerTLSCert: serverCertAsDER, + ClientTLSCert: clientCertAsDER, + }, + }) + //c.Logger.Infof("BDLS Node ID from the remotePeers(): %s ------------", nodes[0].ID) + } + + return nodes, nil +} + +// HandleMessage handles the message from the sender +func (c *Chain) HandleMessage(sender uint64, m *bdls.Message /**smartbftprotos.Message*/) { + c.Logger.Debugf("Message from %d", sender) + date, err := proto.Marshal(m) + if err != nil { + c.Logger.Info(err) + } + c.consensus.ReceiveMessage(date, time.Now()) +} + +// HandleRequest handles the request from the sender +func (c *Chain) HandleRequest(sender uint64, req []byte) { + c.Logger.Debugf("HandleRequest from %d", sender) + if _, err := c.verifier.VerifyRequest(req); err != nil { + c.Logger.Warnf("Got bad request from %d: %v", sender, err) + return + } + c.consensus.SubmitRequest(req, time.Now()) +} + +func pemToDER(pemBytes []byte, id uint64, certType string, logger *flogging.FabricLogger) ([]byte, error) { + bl, _ := pem.Decode(pemBytes) + if bl == nil { + logger.Errorf("Rejecting PEM block of %s TLS cert for node %d, offending PEM is: %s", certType, id, string(pemBytes)) + return nil, errors.Errorf("invalid PEM block") + } + return bl.Bytes, nil +} + +// publicKeyFromCertificate returns the public key of the given ASN1 DER certificate. +func publicKeyFromCertificate(der []byte) ([]byte, error) { + cert, err := x509.ParseCertificate(der) + if err != nil { + return nil, err + } + return x509.MarshalPKIXPublicKey(cert.PublicKey) +} + +// Orders the envelope in the `msg` content. SubmitRequest. +// Returns +// +// -- batches [][]*common.Envelope; the batches cut, +// -- pending bool; if there are envelopes pending to be ordered, +// -- err error; the error encountered, if any. +// +// It takes care of config messages as well as the revalidation of messages if the config sequence has advanced. +func (c *Chain) ordered(msg *orderer.SubmitRequest) (batches [][]*common.Envelope, pending bool, err error) { + seq := c.support.Sequence() + + isconfig, err := c.isConfig(msg.Payload) + if err != nil { + return nil, false, errors.Errorf("bad message: %s", err) + } + + if isconfig { + // ConfigMsg + if msg.LastValidationSeq < seq { + c.Logger.Warnf("Config message was validated against %d, although current config seq has advanced (%d)", msg.LastValidationSeq, seq) + msg.Payload, _, err = c.support.ProcessConfigMsg(msg.Payload) + if err != nil { + //c.Metrics.ProposalFailures.Add(1) + return nil, true, errors.Errorf("bad config message: %s", err) + } + } + + batch := c.support.BlockCutter().Cut() + batches = [][]*common.Envelope{} + if len(batch) != 0 { + batches = append(batches, batch) + } + batches = append(batches, []*common.Envelope{msg.Payload}) + return batches, false, nil + } + // it is a normal message + if msg.LastValidationSeq < seq { + c.Logger.Warnf("Normal message was validated against %d, although current config seq has advanced (%d)", msg.LastValidationSeq, seq) + if _, err := c.support.ProcessNormalMsg(msg.Payload); err != nil { + //c.Metrics.ProposalFailures.Add(1) + return nil, true, errors.Errorf("bad normal message: %s", err) + } + } + batches, pending = c.support.BlockCutter().Ordered(msg.Payload) + return batches, pending, nil +} + +func (c *Chain) propose(ch chan<- *common.Block, bc *blockCreator, batches ...[]*common.Envelope) { + for _, batch := range batches { + b := bc.createNextBlock(batch) + c.Logger.Infof("Created block [%d], there are %d blocks in flight", b.Header.Number, c.blockInflight) + + select { + case ch <- b: + default: + c.Logger.Panic("Programming error: limit of in-flight blocks does not properly take effect or block is proposed by follower") + } + + // if it is config block, then we should wait for the commit of the block + if protoutil.IsConfigBlock(b) { + c.configInflight = true + } + + c.blockInflight++ + } +} + +func (c *Chain) writeBlock(block *common.Block, index uint64) { + if block.Header.Number > c.lastBlock.Header.Number+1 { + c.Logger.Panicf("Got block [%d], expect block [%d]", block.Header.Number, c.lastBlock.Header.Number+1) + } else if block.Header.Number < c.lastBlock.Header.Number+1 { + c.Logger.Infof("Got block [%d], expect block [%d], this node was forced to catch up", block.Header.Number, c.lastBlock.Header.Number+1) + return + } + + if c.blockInflight > 0 { + c.blockInflight-- // only reduce on leader + } + c.lastBlock = block + + c.Logger.Infof("Writing block [%d] (Raft index: %d) to ledger", block.Header.Number, index) + + if protoutil.IsConfigBlock(block) { + c.configInflight = false + //c.writeConfigBlock(block, index) + c.support.WriteConfigBlock(block, nil) + return + } + + c.support.WriteBlock(block, nil) +} + +func (c *Chain) configureComm() error { + // Reset unreachable map when communication is reconfigured + c.unreachableLock.Lock() + c.unreachable = make(map[uint64]struct{}) + c.unreachableLock.Unlock() + + nodes, err := c.remotePeers() + if err != nil { + return err + } + + //c.configurator.Configure(c.channelID, nodes) + c.Comm.Configure(c.support.ChannelID(), nodes) + return nil +} + +func (c *Chain) isConfig(env *common.Envelope) (bool, error) { + h, err := protoutil.ChannelHeader(env) + if err != nil { + c.Logger.Errorf("failed to extract channel header from envelope") + return false, err + } + + return h.Type == int32(common.HeaderType_CONFIG), nil +} + +func (c *Chain) isRunning() error { + select { + case <-c.startC: + default: + return errors.Errorf("chain is not started") + } + + select { + case <-c.doneC: + return errors.Errorf("chain is stopped") + default: + } + + return nil +} + +// Start should allocate whatever resources are needed for staying up to date with the chain. +// Typically, this involves creating a thread which reads from the ordering source, passes those +// messages to a block cutter, and writes the resulting blocks to the ledger. +func (c *Chain) Start() { + c.Logger.Infof("Starting BDLS node") + + close(c.startC) + close(c.errorC) + + go c.startConsensus(c.config) + go c.run() + +} + +// consensus for one round with full procedure +func (c *Chain) startConsensus(config *bdls.Config) error { + + // var propC chan<- *common.Block + + // create consensus + consensus, err := bdls.NewConsensus(config) + if err != nil { + c.Logger.Error("cannot create BDLS NewConsensus", err) + } + consensus.SetLatency(200 * time.Millisecond) + // load endpoints + peers := []string{"localhost:4680", "localhost:4681", "localhost:4682", "localhost:4683"} + + // start listener + tcpaddr, err := net.ResolveTCPAddr("tcp", fmt.Sprint(":", 4679+int(c.bdlsId))) + if err != nil { + c.Logger.Error("cannot create ResolveTCPAddr", err) + } + + l, err := net.ListenTCP("tcp", tcpaddr) + if err != nil { + c.Logger.Error("cannot create ListenTCP", err) + } + defer l.Close() + c.Logger.Info("listening on:", fmt.Sprint(":", 4679+int(c.bdlsId))) + + // initiate tcp agent + transportLayer := agent.NewTCPAgent(consensus, config.PrivateKey) + if err != nil { + c.Logger.Error("cannot create NewTCPAgent", err) + } + + // start updater + //transportLayer.Update() + + // passive connection from peers + go func() { + for { + conn, err := l.Accept() + if err != nil { + return + } + c.Logger.Info("peer connected from:", conn.RemoteAddr()) + // peer endpoint created + p := agent.NewTCPPeer(conn, transportLayer) + transportLayer.AddPeer(p) + // prove my identity to this peer + p.InitiatePublicKeyAuthentication() + } + }() + + // active connections to peers + for k := range peers { + go func(raddr string) { + for { + conn, err := net.Dial("tcp", raddr) + if err == nil { + c.Logger.Info("connected to peer:", conn.RemoteAddr()) + // peer endpoint created + p := agent.NewTCPPeer(conn, transportLayer) + transportLayer.AddPeer(p) + // prove my identity to this peer + p.InitiatePublicKeyAuthentication() + return + } + <-time.After(time.Second) + } + }(peers[k]) + } + + //go c.TestMultiClients() + + c.transportLayer = transportLayer + //updateTick := time.NewTicker(updatePeriod) + + for { + //<-updateTick.C + //c.transportLayer.Update() + // Check for confirmed new block + height /*round*/, _, state := c.transportLayer.GetLatestState() + if height > c.lastBlock.Header.Number { + c.applyC <- apply{state} + } + } + //defer c.testOrder() + /* + // c.Order(env, 0) + var bc *blockCreator + NEXTHEIGHT: + for { + + env := &common.Envelope{ + Payload: marshalOrPanic(&common.Payload{ + Header: &common.Header{ChannelHeader: marshalOrPanic(&common.ChannelHeader{Type: int32(common.HeaderType_MESSAGE), ChannelId: c.Channel})}, + Data: []byte("TEST_MESSAGE-UNCC-2023-09-12---"), + }), + } + //req := &orderer.SubmitRequest{LastValidationSeq: 0, Payload: env, Channel: c.Channel} + // batches, pending, err := c.ordered(req) + // if err != nil { + // c.Logger.Errorf("Failed to order message: %s", err) + // continue + // } + batches, pending := c.support.BlockCutter().Ordered(env) + + if !pending && len(batches) == 0 { + c.Logger.Info("batches, pending ", batches, pending) + //continue + } + + batch := c.support.BlockCutter().Cut() + if len(batch) == 0 { + c.Logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug") + continue + } + + bc = &blockCreator{ + hash: protoutil.BlockHeaderHash(c.lastBlock.Header), + number: c.lastBlock.Header.Number, + logger: c.Logger, + } + //batch = c.support.BlockCutter().Cut() + //for _, batch := range batches { + //block := c.support.CreateNextBlock(batch) + + block := bc.createNextBlock(batch) + + data, err := proto.Marshal(block) + if err != nil { + c.Logger.Info("*** cannot Marshal Block ", err) + } + //transportLayer.Propose(data) + c.transportLayer.Propose(data) + //} //batches for-loop end + for { + newHeight, _, newState := transportLayer.GetLatestState() //newRound + if newHeight > c.lastBlock.Header.Number { + //h := blake2b.Sum256(data) + + newBlock, err := protoutil.UnmarshalBlock(newState) + if err != nil { + return errors.Errorf("failed to unmarshal bdls State to block: %s", err) + } + + c.Logger.Infof("Unmarshal bdls State to \r\n block: %v \r\n Header.Number: %v ", newBlock.Data, newBlock.Header.Number) + + c.Logger.Info("lastBlock number before write decide block: ", c.lastBlock.Header.Number, c.lastBlock.Header.PreviousHash) + + // c.Logger.Infof(" at height:%v round:%v hash:%v", newHeight, newRound, hex.EncodeToString(h[:])) + + c.lastBlock = newBlock + // TODO use the bdls info for the new block + + // Using the newState type of bdls.State. it represented the proposed blocked that achived consensus + //c.writeBlock(newBlock, 0) + c.support.WriteBlock(newBlock, nil) + + continue NEXTHEIGHT + } + // wait + <-time.After(20 * time.Millisecond) + } + //} + }*/ + //return nil +} + +func (c *Chain) apply( /*height uint64, round uint64,*/ state bdls.State) { + + newBlock, err := protoutil.UnmarshalBlock(state) + if err != nil { + c.Logger.Errorf("failed to unmarshal bdls State to block: %s", err) + } + c.Logger.Infof("Unmarshal bdls State to \r\n block data: %v ", newBlock.Data) + c.Logger.Infof("lastBlock number before write decide block Number : %v ", c.lastBlock.Header.Number) + c.writeBlock(newBlock, 0) + c.Metrics.CommittedBlockNumber.Set(float64(newBlock.Header.Number)) +} + +func marshalOrPanic(pb proto.Message) []byte { + data, err := proto.Marshal(pb) + if err != nil { + panic(err) + } + return data +} + +func (c *Chain) run() { + // BDLS consensus updater ticker + updateTick := time.NewTicker(updatePeriod) + + ticking := false + timer := c.clock.NewTimer(time.Second) + // we need a stopped timer rather than nil, + // because we will be select waiting on timer.C() + if !timer.Stop() { + <-timer.C() + } + + // if timer is already started, this is a no-op + startTimer := func() { + if !ticking { + ticking = true + timer.Reset(c.support.SharedConfig().BatchTimeout()) + } + } + + stopTimer := func() { + if !timer.Stop() && ticking { + // we only need to drain the channel if the timer expired (not explicitly stopped) + <-timer.C() + } + ticking = false + } + + //TODO replace the: + defer updateTick.Stop() + + submitC := c.submitC + //var propC chan<- *common.Block + ch := make(chan *common.Block, c.opts.MaxInflightBlocks) + c.blockInflight = 0 + + var bc *blockCreator + + c.Logger.Infof("Start accepting requests at block [%d]", c.lastBlock.Header.Number) + + // Leader should call Propose in go routine, because this method may be blocked + // if node is leaderless (this can happen when leader steps down in a heavily + // loaded network). We need to make sure applyC can still be consumed properly. + go func(ch chan *common.Block) { + for { + // select { + /*case*/ + b := <-ch + data := protoutil.MarshalOrPanic(b) + c.transportLayer.Propose(data) + c.Logger.Debugf("Proposed block [%d] to BDLS consensus", b.Header.Number) + + /*case <-ctx.Done(): + c.Logger.Debugf("Quit proposing blocks, discarded %d blocks in the queue", len(ch)) + return + }*/ + + c.Logger.Debugf("INSIDE GOROUTINE Proposed block [%d] to bdls consensus", b.Header.Number) + } + }(ch) + /*go func() { + for { + <-updateTick.C + c.transportLayer.Update() + // Check for confirmed new block + height , _, state := c.transportLayer.GetLatestState() + if height > c.lastBlock.Header.Number { + c.applyC <- apply{state} + } + } + }()*/ + for { + select { + case <-updateTick.C: + + // Calling BDLS consensus Update() function + c.transportLayer.Update() + + // check if new block confirmed + /*height, round, state := c.transportLayer.GetLatestState() + if height > c.lastBlock.Header.Number { + go c.apply(height, round, state) + }*/ + + // EOL the consensus updater ticker + case <-c.chConsensusMessages: + c.Lock() + msgs := c.consensusMessages + c.consensusMessages = nil + + for _, msg := range msgs { + c.consensus.ReceiveMessage(msg, time.Now()) + } + c.Unlock() + case s := <-submitC: + if s == nil { + // polled by `WaitReady` + continue + } + //batches, pending := c.support.BlockCutter().Ordered(s.req.Payload) + + batches, pending, err := c.ordered(s.req) + if err != nil { + c.Logger.Errorf("Failed to order message: %s", err) + continue + } + + if !pending && len(batches) == 0 { + continue + } + + if pending { + startTimer() // no-op if timer is already started + } else { + stopTimer() + } + + bc = &blockCreator{ + hash: protoutil.BlockHeaderHash(c.lastBlock.Header), + number: c.lastBlock.Header.Number, + logger: c.Logger, + } + + c.propose(ch, bc, batches...) + /* + batch := c.support.BlockCutter().Cut() + + if len(batch) == 0 { + c.Logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug") + continue + } + + + block := bc.createNextBlock(batch) + data, err := proto.Marshal(block) + if err != nil { + c.Logger.Info("*** cannot Marshal Block ", err) + } + c.transportLayer.Propose(data) + */ + + if c.configInflight { + c.Logger.Info("Received config transaction, pause accepting transaction till it is committed") + submitC = nil + } else if c.blockInflight >= c.opts.MaxInflightBlocks { + c.Logger.Debugf("Number of in-flight blocks (%d) reaches limit (%d), pause accepting transaction", + c.blockInflight, c.opts.MaxInflightBlocks) + submitC = nil + } + case app := <-c.applyC: + c.apply(app.state) + if c.configInflight { + c.Logger.Info("Config block or ConfChange in flight, pause accepting transaction") + submitC = nil + } else if c.blockInflight < c.opts.MaxInflightBlocks { + submitC = c.submitC + } + /*//if app.state != nil { + newBlock, err := protoutil.UnmarshalBlock(app.state) + if err != nil { + c.Logger.Errorf("failed to unmarshal bdls State to block: %s", err) + } + + c.Logger.Infof("Unmarshal bdls State to \r\n block data: %v ", newBlock.Data) + + c.Logger.Infof("lastBlock number before write decide block Number : %v ", c.lastBlock.Header.Number) + + //c.Logger.Infof(" at height:%v round:%v hash:%v", newHeight, newRound, hex.EncodeToString(h[:])) + + c.lastBlock = newBlock + + // Using the newState type of bdls.State. + // represented the proposed blocked that achived consensus + //TODO use the c.WriteBlock + + //c.writeBlock(newBlock, 0) + c.support.WriteBlock(newBlock, nil) + + c.Metrics.CommittedBlockNumber.Set(float64(newBlock.Header.Number)) + // }*/ + case <-timer.C(): + ticking = false + + batch := c.support.BlockCutter().Cut() + if len(batch) == 0 { + c.Logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug") + continue + } + + c.Logger.Debugf("Batch timer expired, creating block") + c.propose(ch, bc, batch) // we are certain this is normal block, no need to block + + // required tick for BDLS + /*case <-updateTick.C: + c.transportLayer.Update()*/ + case <-c.die: + return + + case <-c.doneC: + stopTimer() + //cancelProp() + updateTick.Stop() + + select { + case <-c.errorC: // avoid closing closed channel + default: + close(c.errorC) + } + + c.Logger.Infof("Stop serving requests") + //c.periodicChecker.Stop() + return + + } + } + +} + +// StatusReport returns the ConsensusRelation & Status +func (c *Chain) StatusReport() (types2.ConsensusRelation, types2.Status) { + c.statusReportMutex.Lock() + defer c.statusReportMutex.Unlock() + + return c.consensusRelation, c.status +} + +type chainACL struct { + policyManager policies.Manager + Logger *flogging.FabricLogger +} + +// Evaluate evaluates signed data +func (c *chainACL) Evaluate(signatureSet []*protoutil.SignedData) error { + policy, ok := c.policyManager.GetPolicy(policies.ChannelWriters) + if !ok { + return fmt.Errorf("could not find policy %s", policies.ChannelWriters) + } + + err := policy.EvaluateSignedData(signatureSet) + if err != nil { + c.Logger.Debugf("SigFilter evaluation failed: %s, policyName: %s", err.Error(), policies.ChannelWriters) + return errors.Wrap(errors.WithStack(msgprocessor.ErrPermissionDenied), err.Error()) + } + return nil +} diff --git a/orderer/consensus/bdls/consenter.go b/orderer/consensus/bdls/consenter.go new file mode 100644 index 00000000000..ce9919e28d0 --- /dev/null +++ b/orderer/consensus/bdls/consenter.go @@ -0,0 +1,285 @@ +/* +Copyright @Ahmed Al Salih. @BDLS @UNCC All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import ( + "bytes" + "path" + "reflect" + + "code.cloudfoundry.org/clock" + "github.com/hyperledger/fabric-protos-go/common" + + //cb "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric-protos-go/msp" + ab "github.com/hyperledger/fabric-protos-go/orderer" + + //"github.com/hyperledger/fabric-protos-go/orderer/smartbft" + "github.com/pkg/errors" + + //cb "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric/bccsp" + "github.com/hyperledger/fabric/common/channelconfig" + "github.com/hyperledger/fabric/common/crypto" + "github.com/hyperledger/fabric/common/flogging" + "github.com/hyperledger/fabric/common/metrics" + "github.com/hyperledger/fabric/common/policies" + "github.com/hyperledger/fabric/internal/pkg/comm" + "github.com/hyperledger/fabric/orderer/common/cluster" + "github.com/hyperledger/fabric/orderer/common/localconfig" + "github.com/hyperledger/fabric/orderer/common/multichannel" + "github.com/hyperledger/fabric/orderer/consensus" + "github.com/hyperledger/fabric/protoutil" + + //"google.golang.org/protobuf/proto" + "github.com/golang/protobuf/proto" +) + +// Config contains bdls configurations +type Config struct { +} + +// ChainGetter obtains instances of ChainSupport for the given channel +type ChainGetter interface { + // GetChain obtains the ChainSupport for the given channel. + // Returns nil, false when the ChainSupport for the given channel + // isn't found. + GetChain(chainID string) *multichannel.ChainSupport +} + +// PolicyManagerRetriever is the policy manager retriever function +type PolicyManagerRetriever func(channel string) policies.Manager + +// Consenter implements bdls consenter +type Consenter struct { + CreateChain func(chainName string) + GetPolicyManager PolicyManagerRetriever + Logger *flogging.FabricLogger + Identity []byte + Comm *cluster.AuthCommMgr + Chains ChainGetter + SignerSerializer SignerSerializer + Registrar *multichannel.Registrar + WALBaseDir string + ClusterDialer *cluster.PredicateDialer + Conf *localconfig.TopLevel + Metrics *Metrics + BCCSP bccsp.BCCSP + ClusterService *cluster.ClusterService +} + +// HandleChain returns a new Chain instance or an error upon failure +func (c *Consenter) HandleChain(support consensus.ConsenterSupport, metadata *common.Metadata) (consensus.Chain, error) { + //configOptions := &smartbft.Options{} + consenters := support.SharedConfig().Consenters() + /*if err := proto.Unmarshal(support.SharedConfig().ConsensusMetadata(), configOptions); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal consensus metadata") + }*/ + + selfID, err := c.detectSelfID(consenters) + if err != nil { + return nil, errors.Wrap(err, "without a system channel, a follower should have been created") + } + c.Logger.Infof("Local consenter id is %d", selfID) + + puller, err := newBlockPuller(support, c.ClusterDialer, c.Conf.General.Cluster, c.BCCSP) + if err != nil { + c.Logger.Panicf("Failed initializing block puller") + } + + //config, err := configFromMetadataOptions((uint64)(selfID), configOptions) + if err != nil { + return nil, errors.Wrap(err, "failed parsing smartbft configuration") + } + //c.Logger.Debugf("SmartBFT-Go config: %+v", config) + /* + configValidator := &ConfigBlockValidator{ + ValidatingChannel: support.ChannelID(), + Filters: c.Registrar, + ConfigUpdateProposer: c.Registrar, + Logger: c.Logger, + } + */ + opts := Options{ + Consenters: consenters, + MaxInflightBlocks: 5, + Clock: clock.NewClock(), + } + + chain, err := NewChain( + //configValidator, + (uint64)(selfID), + //config, + + path.Join(c.WALBaseDir, support.ChannelID()), + puller, + c.Comm, + c.SignerSerializer, + c.GetPolicyManager(support.ChannelID()), + support, + c.Metrics, + c.BCCSP, + opts, + ) + if err != nil { + return nil, errors.Wrap(err, "failed creating a new Chain") + } + chain.opts = opts + + // refresh cluster service with updated consenters + c.ClusterService.ConfigureNodeCerts(chain.Channel, consenters) + chain.clusterService = c.ClusterService + + return chain, nil +} + +func New( + pmr PolicyManagerRetriever, + signerSerializer SignerSerializer, + clusterDialer *cluster.PredicateDialer, + conf *localconfig.TopLevel, + srvConf comm.ServerConfig, // TODO why is this not used? + srv *comm.GRPCServer, + r *multichannel.Registrar, + metricsProvider metrics.Provider, + clusterMetrics *cluster.Metrics, + BCCSP bccsp.BCCSP, +) *Consenter { + logger := flogging.MustGetLogger("orderer.consensus.bdls") + + //var walConfig WALConfig + + logger.Infof("Starting NEW bdls.....U-N-C-C****/////.......") + consenter := &Consenter{ + Registrar: r, + GetPolicyManager: pmr, + Conf: conf, + ClusterDialer: clusterDialer, + Logger: logger, + Chains: r, + SignerSerializer: signerSerializer, + //WALBaseDir: walConfig.WALDir, + Metrics: NewMetrics(metricsProvider), + CreateChain: r.CreateChain, + BCCSP: BCCSP, + } + + identity, _ := signerSerializer.Serialize() + sID := &msp.SerializedIdentity{} + if err := proto.Unmarshal(identity, sID); err != nil { + logger.Panicf("failed unmarshaling identity: %s", err) + } + + consenter.Identity = sID.IdBytes + + consenter.Comm = &cluster.AuthCommMgr{ + Logger: flogging.MustGetLogger("orderer.common.cluster"), + Metrics: clusterMetrics, + SendBufferSize: conf.General.Cluster.SendBufferSize, + Chan2Members: make(cluster.MembersByChannel), + Connections: cluster.NewConnectionMgr(clusterDialer.Config), + Signer: signerSerializer, + NodeIdentity: sID.IdBytes, + } + + consenter.ClusterService = &cluster.ClusterService{ + StreamCountReporter: &cluster.StreamCountReporter{ + Metrics: clusterMetrics, + }, + Logger: flogging.MustGetLogger("orderer.common.cluster"), + StepLogger: flogging.MustGetLogger("orderer.common.cluster.step"), + MinimumExpirationWarningInterval: cluster.MinimumExpirationWarningInterval, + CertExpWarningThreshold: conf.General.Cluster.CertExpirationWarningThreshold, + MembershipByChannel: make(map[string]*cluster.ChannelMembersConfig), + NodeIdentity: sID.IdBytes, + RequestHandler: &Ingress{ + Logger: logger, + ChainSelector: consenter, + }, + } + + ab.RegisterClusterNodeServiceServer(srv.Server(), consenter.ClusterService) + + return consenter +} + +// ReceiverGetter obtains instances of MessageReceiver given a channel ID +// type ReceiverGetter interface must implement this interface function in consenter +// ReceiverByChain returns the MessageReceiver for the given channelID or nil if not found. +func (c *Consenter) ReceiverByChain(channelID string) MessageReceiver { + cs := c.Chains.GetChain(channelID) + if cs == nil { + return nil + } + if cs.Chain == nil { + c.Logger.Panicf("Programming error - Chain %s is nil although it exists in the mapping", channelID) + } + if bdlsChain, isBDLS := cs.Chain.(*Chain); isBDLS { + return bdlsChain // error if not implement the MessageReceiver interface functions + // in chain.go (HandleMessage & HandleRequest) + } + c.Logger.Warningf("Chain %s is of type %v and not bdls.Chain", channelID, reflect.TypeOf(cs.Chain)) + return nil +} + +func (c *Consenter) IsChannelMember(joinBlock *common.Block) (bool, error) { + if joinBlock == nil { + return false, errors.New("nil block") + } + envelopeConfig, err := protoutil.ExtractEnvelope(joinBlock, 0) + if err != nil { + return false, err + } + bundle, err := channelconfig.NewBundleFromEnvelope(envelopeConfig, c.BCCSP) + if err != nil { + return false, err + } + oc, exists := bundle.OrdererConfig() + if !exists { + return false, errors.New("no orderer config in bundle") + } + member := false + for _, consenter := range oc.Consenters() { + santizedCert, err := crypto.SanitizeX509Cert(consenter.Identity) + if err != nil { + return false, err + } + if bytes.Equal(c.Identity, santizedCert) { + member = true + break + } + } + + return member, nil +} + +// TargetChannel extracts the channel from the given proto.Message. +// Returns an empty string on failure. +func (c *Consenter) TargetChannel(message proto.Message) string { + switch req := message.(type) { + case *ab.ConsensusRequest: + return req.Channel + case *ab.SubmitRequest: + return req.Channel + default: + return "" + } +} + +func (c *Consenter) detectSelfID(consenters []*common.Consenter) (uint32, error) { + for _, cst := range consenters { + santizedCert, err := crypto.SanitizeX509Cert(cst.Identity) + if err != nil { + return 0, err + } + if bytes.Equal(c.Comm.NodeIdentity, santizedCert) { + return cst.Id, nil + } + } + c.Logger.Warning("Could not find the node in channel consenters set") + return 0, cluster.ErrNotInChannel +} diff --git a/orderer/consensus/bdls/egress.go b/orderer/consensus/bdls/egress.go new file mode 100644 index 00000000000..141a5468449 --- /dev/null +++ b/orderer/consensus/bdls/egress.go @@ -0,0 +1,86 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import ( + "sync/atomic" + + //protos "github.com/SmartBFT-Go/consensus/smartbftprotos" + "github.com/BDLS-bft/bdls" + "github.com/golang/protobuf/proto" + cb "github.com/hyperledger/fabric-protos-go/common" + ab "github.com/hyperledger/fabric-protos-go/orderer" + "github.com/hyperledger/fabric/protoutil" +) + +//go:generate mockery -dir . -name RPC -case underscore -output mocks + +// RPC sends a consensus and submits a request +type RPC interface { + SendConsensus(dest uint64, msg *ab.ConsensusRequest) error + // SendSubmit(dest uint64, request *ab.SubmitRequest) error + SendSubmit(destination uint64, request *ab.SubmitRequest, report func(error)) error +} + +// Logger specifies the logger +type Logger interface { + Warnf(template string, args ...interface{}) + Panicf(template string, args ...interface{}) +} + +// Egress implementation +type Egress struct { + Channel string + RPC RPC + Logger Logger + RuntimeConfig *atomic.Value +} + +// Nodes returns nodes from the runtime config +func (e *Egress) Nodes() []uint64 { + nodes := e.RuntimeConfig.Load().(RuntimeConfig).Nodes + var res []uint64 + for _, n := range nodes { + res = append(res, (uint64)(n)) + } + return res +} + +// SendConsensus sends the BFT message to the cluster +func (e *Egress) SendConsensus(targetID uint64, m *bdls.Message) { + err := e.RPC.SendConsensus(targetID, bftMsgToClusterMsg(m, e.Channel)) + if err != nil { + e.Logger.Warnf("Failed sending to %d: %v", targetID, err) + } +} + +// SendTransaction sends the transaction to the cluster +func (e *Egress) SendTransaction(targetID uint64, request []byte) { + env := &cb.Envelope{} + err := proto.Unmarshal(request, env) + if err != nil { + e.Logger.Panicf("Failed unmarshaling request %v to envelope: %v", request, err) + } + msg := &ab.SubmitRequest{ + Channel: e.Channel, + Payload: env, + } + + report := func(err error) { + if err != nil { + e.Logger.Warnf("Failed sending transaction to %d: %v", targetID, err) + } + } + e.RPC.SendSubmit(targetID, msg, report) +} + +func bftMsgToClusterMsg(message *bdls.Message, channel string) *ab.ConsensusRequest { + return &ab.ConsensusRequest{ + Payload: protoutil.MarshalOrPanic(message), + Channel: channel, + } +} diff --git a/orderer/consensus/bdls/ingress.go b/orderer/consensus/bdls/ingress.go new file mode 100644 index 00000000000..d649b2cd8af --- /dev/null +++ b/orderer/consensus/bdls/ingress.go @@ -0,0 +1,69 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import ( + //protos "github.com/SmartBFT-Go/consensus/smartbftprotos" + protos "github.com/BDLS-bft/bdls" + "github.com/golang/protobuf/proto" + ab "github.com/hyperledger/fabric-protos-go/orderer" + "github.com/hyperledger/fabric/protoutil" + "github.com/pkg/errors" +) + +//go:generate mockery -dir . -name MessageReceiver -case underscore -output mocks + +// MessageReceiver receives messages +type MessageReceiver interface { + HandleMessage(sender uint64, m *protos.Message) + HandleRequest(sender uint64, req []byte) +} + +//go:generate mockery -dir . -name ReceiverGetter -case underscore -output mocks + +// ReceiverGetter obtains instances of MessageReceiver given a channel ID +type ReceiverGetter interface { + // ReceiverByChain returns the MessageReceiver if it exists, or nil if it doesn't + ReceiverByChain(channelID string) MessageReceiver +} + +type WarningLogger interface { + Warningf(template string, args ...interface{}) +} + +// Ingress dispatches Submit and Step requests to the designated per chain instances +type Ingress struct { + Logger WarningLogger + ChainSelector ReceiverGetter +} + +// OnConsensus notifies the Ingress for a reception of a StepRequest from a given sender on a given channel +func (in *Ingress) OnConsensus(channel string, sender uint64, request *ab.ConsensusRequest) error { + receiver := in.ChainSelector.ReceiverByChain(channel) + if receiver == nil { + in.Logger.Warningf("An attempt to send a consensus request to a non existing channel (%s) was made by %d", channel, sender) + return errors.Errorf("channel %s doesn't exist", channel) + } + msg := &protos.Message{} + if err := proto.Unmarshal(request.Payload, msg); err != nil { + in.Logger.Warningf("Malformed message: %v", err) + return errors.Wrap(err, "malformed message") + } + receiver.HandleMessage(sender, msg) + return nil +} + +// OnSubmit notifies the Ingress for a reception of a SubmitRequest from a given sender on a given channel +func (in *Ingress) OnSubmit(channel string, sender uint64, request *ab.SubmitRequest) error { + receiver := in.ChainSelector.ReceiverByChain(channel) + if receiver == nil { + in.Logger.Warningf("An attempt to submit a transaction to a non existing channel (%s) was made by %d", channel, sender) + return errors.Errorf("channel %s doesn't exist", channel) + } + receiver.HandleRequest(sender, protoutil.MarshalOrPanic(request.Payload)) + return nil +} diff --git a/orderer/consensus/bdls/metrics.go b/orderer/consensus/bdls/metrics.go new file mode 100644 index 00000000000..1c4e79006ce --- /dev/null +++ b/orderer/consensus/bdls/metrics.go @@ -0,0 +1,72 @@ +/* +Copyright LFX-CLP-2023 All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import "github.com/hyperledger/fabric/common/metrics" + +var ( + clusterSizeOpts = metrics.GaugeOpts{ + Namespace: "consensus", + Subsystem: "BFT", + Name: "cluster_size", + Help: "Number of nodes in this channel.", + LabelNames: []string{"channel"}, + StatsdFormat: "%{#fqname}.%{channel}", + } + proposalFailuresOpts = metrics.CounterOpts{ + Namespace: "consensus", + Subsystem: "etcdraft", + Name: "proposal_failures", + Help: "The number of proposal failures.", + LabelNames: []string{"channel"}, + StatsdFormat: "%{#fqname}.%{channel}", + } + committedBlockNumberOpts = metrics.GaugeOpts{ + Namespace: "consensus", + Subsystem: "BFT", + Name: "committed_block_number", + Help: "The number of the latest committed block.", + LabelNames: []string{"channel"}, + StatsdFormat: "%{#fqname}.%{channel}", + } + isLeaderOpts = metrics.GaugeOpts{ + Namespace: "consensus", + Subsystem: "BFT", + Name: "is_leader", + Help: "The leadership status of the current node according to the latest committed block: 1 if it is the leader else 0.", + LabelNames: []string{"channel"}, + StatsdFormat: "%{#fqname}.%{channel}", + } + leaderIDOpts = metrics.GaugeOpts{ + Namespace: "consensus", + Subsystem: "BFT", + Name: "leader_id", + Help: "The id of the current leader according to the latest committed block.", + LabelNames: []string{"channel"}, + StatsdFormat: "%{#fqname}.%{channel}", + } +) + +// Metrics defines the metrics for the cluster. +type Metrics struct { + ClusterSize metrics.Gauge + CommittedBlockNumber metrics.Gauge + IsLeader metrics.Gauge + LeaderID metrics.Gauge + ProposalFailures metrics.Counter +} + +// NewMetrics creates the Metrics +func NewMetrics(p metrics.Provider) *Metrics { + return &Metrics{ + ClusterSize: p.NewGauge(clusterSizeOpts), + CommittedBlockNumber: p.NewGauge(committedBlockNumberOpts), + ProposalFailures: p.NewCounter(proposalFailuresOpts), + IsLeader: p.NewGauge(isLeaderOpts), + LeaderID: p.NewGauge(leaderIDOpts), + } +} diff --git a/orderer/consensus/bdls/multi-clients-TPS.go b/orderer/consensus/bdls/multi-clients-TPS.go new file mode 100644 index 00000000000..9157719083b --- /dev/null +++ b/orderer/consensus/bdls/multi-clients-TPS.go @@ -0,0 +1,101 @@ +/* +Copyright Ahmed Al Salih. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import ( + "fmt" + "sync" + "time" + + "github.com/hyperledger/fabric-protos-go/common" +) + +// TestMultiClients function runs multiple clients concurrently +// Submits different envelopes to measure the TPS. +func (c *Chain) TestMultiClients() { + time.Sleep(10 * time.Second) + start := time.Now() + c.Logger.Debugf("TEST TPS start:", start) + //multichannel.SetTPSStart() + wg := new(sync.WaitGroup) + wg.Add(4) + go c.TestOrderClient1(wg) + go c.TestOrderClient2(wg) + go c.TestOrderClient3(wg) + go c.TestOrderClient4(wg) + wg.Wait() + + /*end := time.Now() + + total := end.Sub(start) + tps := float64(10000) / (float64(total) * math.Pow(10, -9)) + c.TPS = tps + c.Logger.Infof("**TEST** The total time of execution is: %v with TPS: %f **TEST**", total, tps)*/ +} + +func (c *Chain) TestOrderClient1(wg *sync.WaitGroup) { + //time.Sleep(1000 * time.Millisecond) + c.Logger.Infof("For client %v", 1) + for i := 0; i < 2500; i++ { + env := &common.Envelope{ + Payload: marshalOrPanic(&common.Payload{ + Header: &common.Header{ChannelHeader: marshalOrPanic(&common.ChannelHeader{Type: int32(common.HeaderType_MESSAGE), ChannelId: c.Channel})}, + Data: []byte(fmt.Sprintf("TEST_MESSAGE-UNCC-Client-1-%v", i)), + }), + } + c.Order(env, 0) + } + wg.Done() +} + +// This test will run after 20 second for network healthchck after TCP IO error being generated +func (c *Chain) TestOrderClient2(wg *sync.WaitGroup) { + //time.Sleep(1000 * time.Millisecond) + c.Logger.Infof("For client %v", 2) + for i := 0; i < 2500; i++ { + env := &common.Envelope{ + Payload: marshalOrPanic(&common.Payload{ + Header: &common.Header{ChannelHeader: marshalOrPanic(&common.ChannelHeader{Type: int32(common.HeaderType_MESSAGE), ChannelId: c.Channel})}, + Data: []byte(fmt.Sprintf("TEST_MESSAGE-UNCC-Client-2-%v", i)), + }), + } + c.Order(env, 0) + } + wg.Done() +} + +// This test will run after 20 second for network healthchck after TCP IO error being generated +func (c *Chain) TestOrderClient3(wg *sync.WaitGroup) { + //time.Sleep(1000 * time.Millisecond) + c.Logger.Infof("For client %v", 3) + for i := 0; i < 2500; i++ { + env := &common.Envelope{ + Payload: marshalOrPanic(&common.Payload{ + Header: &common.Header{ChannelHeader: marshalOrPanic(&common.ChannelHeader{Type: int32(common.HeaderType_MESSAGE), ChannelId: c.Channel})}, + Data: []byte(fmt.Sprintf("TEST_MESSAGE-UNCC-Client-3-%v", i)), + }), + } + c.Order(env, 0) + } + wg.Done() +} + +// This test will run after 20 second for network healthchck after TCP IO error being generated +func (c *Chain) TestOrderClient4(wg *sync.WaitGroup) { + //time.Sleep(1000 * time.Millisecond) + c.Logger.Infof("For client %v", 3) + for i := 0; i < 2500; i++ { + env := &common.Envelope{ + Payload: marshalOrPanic(&common.Payload{ + Header: &common.Header{ChannelHeader: marshalOrPanic(&common.ChannelHeader{Type: int32(common.HeaderType_MESSAGE), ChannelId: c.Channel})}, + Data: []byte(fmt.Sprintf("TEST_MESSAGE-UNCC-Client-4-%v", i)), + }), + } + c.Order(env, 0) + } + wg.Done() +} diff --git a/orderer/consensus/bdls/signature.go b/orderer/consensus/bdls/signature.go new file mode 100644 index 00000000000..3b05c1d9fe3 --- /dev/null +++ b/orderer/consensus/bdls/signature.go @@ -0,0 +1,95 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import ( + "encoding/asn1" + "math/big" + + "github.com/SmartBFT-Go/consensus/pkg/types" + "github.com/golang/protobuf/proto" + cb "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric/common/util" + "github.com/pkg/errors" +) + +// Signature implementation +type Signature struct { + IdentifierHeader []byte + BlockHeader []byte + OrdererBlockMetadata []byte +} + +// Unmarshal the signature +func (sig *Signature) Unmarshal(bytes []byte) error { + _, err := asn1.Unmarshal(bytes, sig) + return err +} + +// Marshal the signature +func (sig *Signature) Marshal() []byte { + bytes, err := asn1.Marshal(*sig) + if err != nil { + panic(err) + } + return bytes +} + +// AsBytes returns the message to sign +func (sig Signature) AsBytes() []byte { + msg2Sign := util.ConcatenateBytes(sig.OrdererBlockMetadata, sig.IdentifierHeader, sig.BlockHeader) + return msg2Sign +} + +// ProposalToBlock marshals the proposal the the block +func ProposalToBlock(proposal types.Proposal) (*cb.Block, error) { + // initialize block with empty fields + block := &cb.Block{ + Data: &cb.BlockData{}, + Metadata: &cb.BlockMetadata{}, + } + + if len(proposal.Header) == 0 { + return nil, errors.New("proposal header cannot be nil") + } + + hdr := &asn1Header{} + + if _, err := asn1.Unmarshal(proposal.Header, hdr); err != nil { + return nil, errors.Wrap(err, "bad header") + } + + block.Header = &cb.BlockHeader{ + Number: hdr.Number.Uint64(), + PreviousHash: hdr.PreviousHash, + DataHash: hdr.DataHash, + } + + if len(proposal.Payload) == 0 { + return nil, errors.New("proposal payload cannot be nil") + } + + tuple := &ByteBufferTuple{} + if err := tuple.FromBytes(proposal.Payload); err != nil { + return nil, errors.Wrap(err, "bad payload and metadata tuple") + } + + if err := proto.Unmarshal(tuple.A, block.Data); err != nil { + return nil, errors.Wrap(err, "bad payload") + } + + if err := proto.Unmarshal(tuple.B, block.Metadata); err != nil { + return nil, errors.Wrap(err, "bad metadata") + } + return block, nil +} + +type asn1Header struct { + Number *big.Int + PreviousHash []byte + DataHash []byte +} diff --git a/orderer/consensus/bdls/signer.go b/orderer/consensus/bdls/signer.go new file mode 100644 index 00000000000..62272913e93 --- /dev/null +++ b/orderer/consensus/bdls/signer.go @@ -0,0 +1,82 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import ( + "github.com/SmartBFT-Go/consensus/pkg/types" + cb "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric/common/crypto" + "github.com/hyperledger/fabric/internal/pkg/identity" + "github.com/hyperledger/fabric/protoutil" +) + +//go:generate mockery -dir . -name SignerSerializer -case underscore -output ./mocks/ + +// SignerSerializer signs messages and serializes identities +type SignerSerializer interface { + identity.SignerSerializer +} + +// Signer implementation +type Signer struct { + ID uint64 + SignerSerializer SignerSerializer + Logger Logger + LastConfigBlockNum func(*cb.Block) uint64 +} + +// Sign signs the message +func (s *Signer) Sign(msg []byte) []byte { + signature, err := s.SignerSerializer.Sign(msg) + if err != nil { + s.Logger.Panicf("Failed signing message: %v", err) + } + return signature +} + +// SignProposal signs the proposal +func (s *Signer) SignProposal(proposal types.Proposal, _ []byte) *types.Signature { + block, err := ProposalToBlock(proposal) + if err != nil { + s.Logger.Panicf("Tried to sign bad proposal: %v", err) + } + + nonce := randomNonceOrPanic() + + sig := Signature{ + BlockHeader: protoutil.BlockHeaderBytes(block.Header), + IdentifierHeader: protoutil.MarshalOrPanic(s.newIdentifierHeaderOrPanic(nonce)), + OrdererBlockMetadata: protoutil.MarshalOrPanic(&cb.OrdererBlockMetadata{ + LastConfig: &cb.LastConfig{Index: uint64(s.LastConfigBlockNum(block))}, + ConsenterMetadata: proposal.Metadata, + }), + } + + signature := protoutil.SignOrPanic(s.SignerSerializer, sig.AsBytes()) + + return &types.Signature{ + ID: s.ID, + Value: signature, + Msg: sig.Marshal(), + } +} + +// NewSignatureHeader creates a SignatureHeader with the correct signing identity and a valid nonce +func (s *Signer) newIdentifierHeaderOrPanic(nonce []byte) *cb.IdentifierHeader { + return &cb.IdentifierHeader{ + Identifier: uint32(s.ID), + Nonce: nonce, + } +} + +func randomNonceOrPanic() []byte { + nonce, err := crypto.GetRandomNonce() + if err != nil { + panic(err) + } + return nonce +} diff --git a/orderer/consensus/bdls/util.go b/orderer/consensus/bdls/util.go new file mode 100644 index 00000000000..6e6966ed859 --- /dev/null +++ b/orderer/consensus/bdls/util.go @@ -0,0 +1,500 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import ( + "bytes" + "crypto/sha256" + "crypto/x509" + "encoding/hex" + "encoding/pem" + "fmt" + "sort" + + "github.com/SmartBFT-Go/consensus/pkg/types" + "github.com/SmartBFT-Go/consensus/smartbftprotos" + "github.com/golang/protobuf/proto" + cb "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric-protos-go/msp" + "github.com/hyperledger/fabric-protos-go/orderer/smartbft" + "github.com/hyperledger/fabric/bccsp" + "github.com/hyperledger/fabric/common/channelconfig" + "github.com/hyperledger/fabric/common/crypto" + "github.com/hyperledger/fabric/common/flogging" + "github.com/hyperledger/fabric/orderer/common/cluster" + "github.com/hyperledger/fabric/orderer/common/localconfig" + "github.com/hyperledger/fabric/orderer/consensus" + "github.com/hyperledger/fabric/orderer/consensus/etcdraft" + "github.com/hyperledger/fabric/protoutil" + "github.com/pkg/errors" +) + +// RuntimeConfig defines the configuration of the consensus +// that is related to runtime. +type RuntimeConfig struct { + BFTConfig types.Configuration + isConfig bool + logger *flogging.FabricLogger + id uint64 + LastCommittedBlockHash string + RemoteNodes []cluster.RemoteNode + ID2Identities NodeIdentitiesByID + LastBlock *cb.Block + LastConfigBlock *cb.Block + Nodes []uint64 + consenters []*cb.Consenter +} + +/* +// BlockCommitted updates the config from the block +func (rtc RuntimeConfig) BlockCommitted(block *cb.Block, bccsp bccsp.BCCSP) (RuntimeConfig, error) { + if _, err := cluster.ConfigFromBlock(block); err == nil { + return rtc.configBlockCommitted(block, bccsp) + } + return RuntimeConfig{ + consenters: rtc.consenters, + BFTConfig: rtc.BFTConfig, + id: rtc.id, + logger: rtc.logger, + LastCommittedBlockHash: hex.EncodeToString(protoutil.BlockHeaderHash(block.Header)), + Nodes: rtc.Nodes, + ID2Identities: rtc.ID2Identities, + RemoteNodes: rtc.RemoteNodes, + LastBlock: block, + LastConfigBlock: rtc.LastConfigBlock, + }, nil +}*/ +/* +func (rtc RuntimeConfig) configBlockCommitted(block *cb.Block, bccsp bccsp.BCCSP) (RuntimeConfig, error) { + nodeConf, err := RemoteNodesFromConfigBlock(block, rtc.logger, bccsp) + if err != nil { + return rtc, errors.Wrap(err, "remote nodes cannot be computed, rejecting config block") + } + + bftConfig, err := configBlockToBFTConfig(rtc.id, block, bccsp) + if err != nil { + return RuntimeConfig{}, err + } + + return RuntimeConfig{ + consenters: nodeConf.consenters, + BFTConfig: bftConfig, + isConfig: true, + id: rtc.id, + logger: rtc.logger, + LastCommittedBlockHash: hex.EncodeToString(protoutil.BlockHeaderHash(block.Header)), + Nodes: nodeConf.nodeIDs, + ID2Identities: nodeConf.id2Identities, + RemoteNodes: nodeConf.remoteNodes, + LastBlock: block, + LastConfigBlock: block, + }, nil +}*/ + +/* +func configBlockToBFTConfig(selfID uint64, block *cb.Block, bccsp bccsp.BCCSP) (types.Configuration, error) { + if block == nil || block.Data == nil || len(block.Data.Data) == 0 { + return types.Configuration{}, errors.New("empty block") + } + + env, err := protoutil.UnmarshalEnvelope(block.Data.Data[0]) + if err != nil { + return types.Configuration{}, err + } + bundle, err := channelconfig.NewBundleFromEnvelope(env, bccsp) + if err != nil { + return types.Configuration{}, err + } + + oc, ok := bundle.OrdererConfig() + if !ok { + return types.Configuration{}, errors.New("no orderer config") + } + + consensusConfigOptions := &smartbft.Options{} + if err := proto.Unmarshal(oc.ConsensusMetadata(), consensusConfigOptions); err != nil { + return types.Configuration{}, err + } + + return configFromMetadataOptions(selfID, consensusConfigOptions) +}*/ + +//go:generate counterfeiter -o mocks/mock_blockpuller.go . BlockPuller + +// newBlockPuller creates a new block puller +func newBlockPuller( + support consensus.ConsenterSupport, + baseDialer *cluster.PredicateDialer, + clusterConfig localconfig.Cluster, + bccsp bccsp.BCCSP) (BlockPuller, error) { + verifyBlockSequence := func(blocks []*cb.Block, _ string) error { + vb := cluster.BlockVerifierBuilder(bccsp) + return cluster.VerifyBlocksBFT(blocks, support.SignatureVerifier(), vb) + } + + stdDialer := &cluster.StandardDialer{ + Config: baseDialer.Config.Clone(), + } + stdDialer.Config.AsyncConnect = false + stdDialer.Config.SecOpts.VerifyCertificate = nil + + // Extract the TLS CA certs and endpoints from the configuration, + endpoints, err := etcdraft.EndpointconfigFromSupport(support, bccsp) + if err != nil { + return nil, err + } + + der, _ := pem.Decode(stdDialer.Config.SecOpts.Certificate) + if der == nil { + return nil, errors.Errorf("client certificate isn't in PEM format: %v", + string(stdDialer.Config.SecOpts.Certificate)) + } + + bp := &cluster.BlockPuller{ + VerifyBlockSequence: verifyBlockSequence, + Logger: flogging.MustGetLogger("orderer.common.cluster.puller"), + RetryTimeout: clusterConfig.ReplicationRetryTimeout, + MaxTotalBufferBytes: clusterConfig.ReplicationBufferSize, + FetchTimeout: clusterConfig.ReplicationPullTimeout, + Endpoints: endpoints, + Signer: support, + TLSCert: der.Bytes, + Channel: support.ChannelID(), + Dialer: stdDialer, + } + + return bp, nil +} + +func getViewMetadataFromBlock(block *cb.Block) (*smartbftprotos.ViewMetadata, error) { + if block.Header.Number == 0 { + // Genesis block has no prior metadata so we just return an un-initialized metadata + return new(smartbftprotos.ViewMetadata), nil + } + + signatureMetadata := protoutil.GetMetadataFromBlockOrPanic(block, cb.BlockMetadataIndex_SIGNATURES) + ordererMD := &cb.OrdererBlockMetadata{} + if err := proto.Unmarshal(signatureMetadata.Value, ordererMD); err != nil { + return nil, errors.Wrap(err, "failed unmarshaling OrdererBlockMetadata") + } + + var viewMetadata smartbftprotos.ViewMetadata + if err := proto.Unmarshal(ordererMD.ConsenterMetadata, &viewMetadata); err != nil { + return nil, err + } + + return &viewMetadata, nil +} + +/* + func configFromMetadataOptions(selfID uint64, options *smartbft.Options) (types.Configuration, error) { + var err error + + config := types.DefaultConfig + config.SelfID = (uint64)(selfID) + + if options == nil { + return config, errors.New("config metadata options field is nil") + } + + config.RequestBatchMaxCount = options.RequestBatchMaxCount + config.RequestBatchMaxBytes = options.RequestBatchMaxBytes + if config.RequestBatchMaxInterval, err = time.ParseDuration(options.RequestBatchMaxInterval); err != nil { + return config, errors.Wrap(err, "bad config metadata option RequestBatchMaxInterval") + } + config.IncomingMessageBufferSize = options.IncomingMessageBufferSize + config.RequestPoolSize = options.RequestPoolSize + if config.RequestForwardTimeout, err = time.ParseDuration(options.RequestForwardTimeout); err != nil { + return config, errors.Wrap(err, "bad config metadata option RequestForwardTimeout") + } + if config.RequestComplainTimeout, err = time.ParseDuration(options.RequestComplainTimeout); err != nil { + return config, errors.Wrap(err, "bad config metadata option RequestComplainTimeout") + } + if config.RequestAutoRemoveTimeout, err = time.ParseDuration(options.RequestAutoRemoveTimeout); err != nil { + return config, errors.Wrap(err, "bad config metadata option RequestAutoRemoveTimeout") + } + if config.ViewChangeResendInterval, err = time.ParseDuration(options.ViewChangeResendInterval); err != nil { + return config, errors.Wrap(err, "bad config metadata option ViewChangeResendInterval") + } + if config.ViewChangeTimeout, err = time.ParseDuration(options.ViewChangeTimeout); err != nil { + return config, errors.Wrap(err, "bad config metadata option ViewChangeTimeout") + } + if config.LeaderHeartbeatTimeout, err = time.ParseDuration(options.LeaderHeartbeatTimeout); err != nil { + return config, errors.Wrap(err, "bad config metadata option LeaderHeartbeatTimeout") + } + config.LeaderHeartbeatCount = options.LeaderHeartbeatCount + if config.CollectTimeout, err = time.ParseDuration(options.CollectTimeout); err != nil { + return config, errors.Wrap(err, "bad config metadata option CollectTimeout") + } + config.SyncOnStart = options.SyncOnStart + config.SpeedUpViewChange = options.SpeedUpViewChange + + config.LeaderRotation = false + config.DecisionsPerLeader = 0 + + if err = config.Validate(); err != nil { + return config, errors.Wrap(err, "config validation failed") + } + + config.RequestMaxBytes = 500 * 1024 + return config, nil + } +*/ +type request struct { + sigHdr *cb.SignatureHeader + envelope *cb.Envelope + chHdr *cb.ChannelHeader +} + +// RequestInspector inspects incomming requests and validates serialized identity +type RequestInspector struct { + ValidateIdentityStructure func(identity *msp.SerializedIdentity) error +} + +func (ri *RequestInspector) requestIDFromSigHeader(sigHdr *cb.SignatureHeader) (types.RequestInfo, error) { + sID := &msp.SerializedIdentity{} + if err := proto.Unmarshal(sigHdr.Creator, sID); err != nil { + return types.RequestInfo{}, errors.Wrap(err, "identity isn't an MSP Identity") + } + + if err := ri.ValidateIdentityStructure(sID); err != nil { + return types.RequestInfo{}, err + } + + var preimage []byte + preimage = append(preimage, sigHdr.Nonce...) + preimage = append(preimage, sigHdr.Creator...) + txID := sha256.Sum256(preimage) + clientID := sha256.Sum256(sigHdr.Creator) + return types.RequestInfo{ + ID: hex.EncodeToString(txID[:]), + ClientID: hex.EncodeToString(clientID[:]), + }, nil +} + +// RequestID unwraps the request info from the raw request +func (ri *RequestInspector) RequestID(rawReq []byte) types.RequestInfo { + req, err := ri.unwrapReq(rawReq) + if err != nil { + return types.RequestInfo{} + } + reqInfo, err := ri.requestIDFromSigHeader(req.sigHdr) + if err != nil { + return types.RequestInfo{} + } + return reqInfo +} + +func (ri *RequestInspector) unwrapReq(req []byte) (*request, error) { + envelope, err := protoutil.UnmarshalEnvelope(req) + if err != nil { + return nil, err + } + payload := &cb.Payload{} + if err := proto.Unmarshal(envelope.Payload, payload); err != nil { + return nil, errors.Wrap(err, "failed unmarshaling payload") + } + + if payload.Header == nil { + return nil, errors.Errorf("no header in payload") + } + + sigHdr := &cb.SignatureHeader{} + if err := proto.Unmarshal(payload.Header.SignatureHeader, sigHdr); err != nil { + return nil, err + } + + if len(payload.Header.ChannelHeader) == 0 { + return nil, errors.New("no channel header in payload") + } + + chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader) + if err != nil { + return nil, errors.WithMessage(err, "error unmarshaling channel header") + } + + return &request{ + chHdr: chdr, + sigHdr: sigHdr, + envelope: envelope, + }, nil +} + +// RemoteNodesFromConfigBlock unmarshals the node config from the block metadata +func RemoteNodesFromConfigBlock(block *cb.Block, logger *flogging.FabricLogger, bccsp bccsp.BCCSP) (*nodeConfig, error) { + env := &cb.Envelope{} + if err := proto.Unmarshal(block.Data.Data[0], env); err != nil { + return nil, errors.Wrap(err, "failed unmarshaling envelope of config block") + } + bundle, err := channelconfig.NewBundleFromEnvelope(env, bccsp) + if err != nil { + return nil, errors.Wrap(err, "failed getting a new bundle from envelope of config block") + } + + channelMSPs, err := bundle.MSPManager().GetMSPs() + if err != nil { + return nil, errors.Wrap(err, "failed obtaining MSPs from MSPManager") + } + + oc, ok := bundle.OrdererConfig() + if !ok { + return nil, errors.New("no orderer config in config block") + } + + configOptions := &smartbft.Options{} + if err := proto.Unmarshal(oc.ConsensusMetadata(), configOptions); err != nil { + return nil, errors.Wrap(err, "failed to unmarshal consensus metadata") + } + + var nodeIDs []uint64 + var remoteNodes []cluster.RemoteNode + id2Identies := map[uint64][]byte{} + for _, consenter := range oc.Consenters() { + sanitizedID, err := crypto.SanitizeIdentity(protoutil.MarshalOrPanic(&msp.SerializedIdentity{ + IdBytes: consenter.Identity, + Mspid: consenter.MspId, + })) + if err != nil { + logger.Panicf("Failed to sanitize identity: %v [%s]", err, string(consenter.Identity)) + } + id2Identies[(uint64)(consenter.Id)] = sanitizedID + logger.Infof("%s %d ---> %s", bundle.ConfigtxValidator().ChannelID(), consenter.Id, string(consenter.Identity)) + + nodeIDs = append(nodeIDs, (uint64)(consenter.Id)) + + serverCertAsDER, err := pemToDER(consenter.ServerTlsCert, (uint64)(consenter.Id), "server", logger) + if err != nil { + return nil, errors.WithStack(err) + } + clientCertAsDER, err := pemToDER(consenter.ClientTlsCert, (uint64)(consenter.Id), "client", logger) + if err != nil { + return nil, errors.WithStack(err) + } + + // Validate certificate structure + for _, cert := range [][]byte{serverCertAsDER, clientCertAsDER} { + if _, err := x509.ParseCertificate(cert); err != nil { + pemBytes := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert}) + logger.Errorf("Invalid certificate: %s", string(pemBytes)) + return nil, err + } + } + + nodeMSP, exists := channelMSPs[consenter.MspId] + if !exists { + return nil, errors.Errorf("no MSP found for MSP with ID of %s", consenter.MspId) + } + + var rootCAs [][]byte + rootCAs = append(rootCAs, nodeMSP.GetTLSRootCerts()...) + rootCAs = append(rootCAs, nodeMSP.GetTLSIntermediateCerts()...) + + sanitizedCert, err := crypto.SanitizeX509Cert(consenter.Identity) + if err != nil { + return nil, err + } + + remoteNodes = append(remoteNodes, cluster.RemoteNode{ + NodeAddress: cluster.NodeAddress{ + ID: (uint64)(consenter.Id), + Endpoint: fmt.Sprintf("%s:%d", consenter.Host, consenter.Port), + }, + + NodeCerts: cluster.NodeCerts{ + ClientTLSCert: clientCertAsDER, + ServerTLSCert: serverCertAsDER, + ServerRootCA: rootCAs, + Identity: sanitizedCert, + }, + }) + } + + sort.Slice(nodeIDs, func(i, j int) bool { + return nodeIDs[i] < nodeIDs[j] + }) + + return &nodeConfig{ + consenters: oc.Consenters(), + remoteNodes: remoteNodes, + id2Identities: id2Identies, + nodeIDs: nodeIDs, + }, nil +} + +type nodeConfig struct { + id2Identities NodeIdentitiesByID + remoteNodes []cluster.RemoteNode + nodeIDs []uint64 + consenters []*cb.Consenter +} + +// ConsenterCertificate denotes a TLS certificate of a consenter +type ConsenterCertificate struct { + ConsenterCertificate []byte + CryptoProvider bccsp.BCCSP +} + +// IsConsenterOfChannel returns whether the caller is a consenter of a channel +// by inspecting the given configuration block. +// It returns nil if true, else returns an error. +func (conCert ConsenterCertificate) IsConsenterOfChannel(configBlock *cb.Block) error { + if configBlock == nil { + return errors.New("nil block") + } + envelopeConfig, err := protoutil.ExtractEnvelope(configBlock, 0) + if err != nil { + return err + } + bundle, err := channelconfig.NewBundleFromEnvelope(envelopeConfig, conCert.CryptoProvider) + if err != nil { + return err + } + oc, exists := bundle.OrdererConfig() + if !exists { + return errors.New("no orderer config in bundle") + } + if oc.ConsensusType() != "BFT" { + return errors.New("not a SmartBFT config block") + } + + for _, consenter := range oc.Consenters() { + if bytes.Equal(conCert.ConsenterCertificate, consenter.ServerTlsCert) || bytes.Equal(conCert.ConsenterCertificate, consenter.ClientTlsCert) { + return nil + } + } + return cluster.ErrNotInChannel +} + +type worker struct { + work [][]byte + f func([]byte) + workerNum int + id int +} + +func (w *worker) doWork() { + // sanity check + if w.workerNum == 0 { + panic("worker number is not defined") + } + + if w.f == nil { + panic("worker function is not defined") + } + + if len(w.work) == 0 { + panic("work is not defined") + } + + for i, datum := range w.work { + if i%w.workerNum != w.id { + continue + } + + w.f(datum) + } +} diff --git a/orderer/consensus/bdls/verifier.go b/orderer/consensus/bdls/verifier.go new file mode 100644 index 00000000000..fe3d046eb80 --- /dev/null +++ b/orderer/consensus/bdls/verifier.go @@ -0,0 +1,447 @@ +/* +Copyright IBM Corp. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package bdls + +import ( + "bytes" + "encoding/base64" + "encoding/hex" + "fmt" + "sync" + "sync/atomic" + + "github.com/SmartBFT-Go/consensus/pkg/types" + "github.com/SmartBFT-Go/consensus/smartbftprotos" + "github.com/golang/protobuf/proto" + cb "github.com/hyperledger/fabric-protos-go/common" + "github.com/hyperledger/fabric-protos-go/msp" + "github.com/hyperledger/fabric/common/flogging" + "github.com/hyperledger/fabric/common/policies" + "github.com/hyperledger/fabric/common/util" + "github.com/hyperledger/fabric/protoutil" + "github.com/pkg/errors" + "go.uber.org/zap/zapcore" +) + +//go:generate mockery -dir . -name Sequencer -case underscore -output mocks + +// Sequencer returns sequences +type Sequencer interface { + Sequence() uint64 +} + +//go:generate mockery -dir . -name ConsenterVerifier -case underscore -output mocks + +// ConsenterVerifier is used to determine whether a signature from one of the consenters is valid +type ConsenterVerifier interface { + // Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy + Evaluate(signatureSet []*protoutil.SignedData) error +} + +//go:generate mockery -dir . -name AccessController -case underscore -output mocks + +// AccessController is used to determine if a signature of a certain client is valid +type AccessController interface { + // Evaluate takes a set of SignedData and evaluates whether this set of signatures satisfies the policy + Evaluate(signatureSet []*protoutil.SignedData) error +} + +type requestVerifier func(req []byte, isolated bool) (types.RequestInfo, error) + +// NodeIdentitiesByID stores Identities by id +type NodeIdentitiesByID map[uint64][]byte + +// IdentityToID looks up the Identity in NodeIdentitiesByID and returns id and flag true if found +func (nibd NodeIdentitiesByID) IdentityToID(identity []byte) (uint64, bool) { + sID := &msp.SerializedIdentity{} + if err := proto.Unmarshal(identity, sID); err != nil { + return 0, false + } + for id, currIdentity := range nibd { + currentID := &msp.SerializedIdentity{} + if err := proto.Unmarshal(currIdentity, currentID); err != nil { + return 0, false + } + if proto.Equal(currentID, sID) { + return id, true + } + } + return 0, false +} + +// Verifier verifies proposals and signatures +type Verifier struct { + RuntimeConfig *atomic.Value + ReqInspector *RequestInspector + ConsenterVerifier ConsenterVerifier + AccessController AccessController + VerificationSequencer Sequencer + Ledger Ledger + Logger *flogging.FabricLogger + ConfigValidator ConfigValidator +} + +// AuxiliaryData unmarshals and returns auxiliary data from signature +func (v *Verifier) AuxiliaryData(msg []byte) []byte { + sig := &Signature{} + if err := sig.Unmarshal(msg); err != nil { + v.Logger.Warnf("Failed unmarshalling signature message %s: %v", hex.EncodeToString(msg), err) + } + return nil +} + +// VerifyProposal verifies proposal and returns []RequestInfo +func (v *Verifier) VerifyProposal(proposal types.Proposal) ([]types.RequestInfo, error) { + block, err := ProposalToBlock(proposal) + if err != nil { + return nil, err + } + + rtc := v.RuntimeConfig.Load().(RuntimeConfig) + if err := verifyHashChain(block, rtc.LastCommittedBlockHash); err != nil { + return nil, err + } + + requests, err := v.verifyBlockDataAndMetadata(block, proposal.Metadata) + if err != nil { + return nil, err + } + + verificationSeq := v.VerificationSequence() + if verificationSeq != uint64(proposal.VerificationSequence) { + return nil, errors.Errorf("expected verification sequence %d, but proposal has %d", verificationSeq, proposal.VerificationSequence) + } + + return requests, nil +} + +// RequestsFromProposal converts proposal to []RequestInfo +func (v *Verifier) RequestsFromProposal(proposal types.Proposal) []types.RequestInfo { + block, err := ProposalToBlock(proposal) + if err != nil { + return []types.RequestInfo{} + } + + if block.Data == nil { + return []types.RequestInfo{} + } + + var res []types.RequestInfo + for _, txn := range block.Data.Data { + req := v.ReqInspector.RequestID(txn) + res = append(res, req) + } + + return res +} + +// VerifySignature verifies signature +func (v *Verifier) VerifySignature(signature types.Signature) error { + id2Identity := v.RuntimeConfig.Load().(RuntimeConfig).ID2Identities + identity, exists := id2Identity[signature.ID] + if !exists { + return errors.Errorf("node with id of %d doesn't exist", signature.ID) + } + + return v.ConsenterVerifier.Evaluate([]*protoutil.SignedData{ + {Identity: identity, Data: signature.Msg, Signature: signature.Value}, + }) +} + +// VerifyRequest verifies raw request +func (v *Verifier) VerifyRequest(rawRequest []byte) (types.RequestInfo, error) { + return v.verifyRequest(rawRequest, false) +} + +func (v *Verifier) verifyRequest(rawRequest []byte, noConfigAllowed bool) (types.RequestInfo, error) { + req, err := v.ReqInspector.unwrapReq(rawRequest) + if err != nil { + return types.RequestInfo{}, err + } + + err = v.AccessController.Evaluate([]*protoutil.SignedData{ + {Identity: req.sigHdr.Creator, Data: req.envelope.Payload, Signature: req.envelope.Signature}, + }) + + if err != nil { + return types.RequestInfo{}, errors.Wrap(err, "access denied") + } + + if noConfigAllowed && req.chHdr.Type != int32(cb.HeaderType_ENDORSER_TRANSACTION) { + return types.RequestInfo{}, errors.Errorf("only endorser transactions can be sent with other transactions") + } + + switch req.chHdr.Type { + case int32(cb.HeaderType_CONFIG): + case int32(cb.HeaderType_ORDERER_TRANSACTION): + return types.RequestInfo{}, fmt.Errorf("orderer transactions are not supported in v3") + case int32(cb.HeaderType_ENDORSER_TRANSACTION): + default: + return types.RequestInfo{}, errors.Errorf("transaction of type %s is not allowed to be included in blocks", cb.HeaderType_name[req.chHdr.Type]) + } + + if req.chHdr.Type == int32(cb.HeaderType_CONFIG) { + err := v.ConfigValidator.ValidateConfig(req.envelope) + if err != nil { + v.Logger.Errorf("Error verifying config update: %v", err) + return types.RequestInfo{}, err + } + } + + return v.ReqInspector.requestIDFromSigHeader(req.sigHdr) +} + +// VerifyConsenterSig verifies consenter signature +func (v *Verifier) VerifyConsenterSig(signature types.Signature, prop types.Proposal) ([]byte, error) { + id2Identity := v.RuntimeConfig.Load().(RuntimeConfig).ID2Identities + + identity, exists := id2Identity[signature.ID] + if !exists { + return nil, errors.Errorf("node with id of %d doesn't exist", signature.ID) + } + + sig := &Signature{} + if err := sig.Unmarshal(signature.Msg); err != nil { + v.Logger.Errorf("Failed unmarshaling signature from %d: %v", signature.ID, err) + v.Logger.Errorf("Offending signature Msg: %s", base64.StdEncoding.EncodeToString(signature.Msg)) + v.Logger.Errorf("Offending signature Value: %s", base64.StdEncoding.EncodeToString(signature.Value)) + return nil, errors.Wrap(err, "malformed signature format") + } + + if err := v.verifySignatureIsBoundToProposal(sig, signature.ID, prop); err != nil { + return nil, err + } + + expectedMsgToBeSigned := util.ConcatenateBytes(sig.OrdererBlockMetadata, sig.IdentifierHeader, sig.BlockHeader, nil) + signedData := &protoutil.SignedData{ + Signature: signature.Value, + Data: expectedMsgToBeSigned, + Identity: identity, + } + + return nil, v.ConsenterVerifier.Evaluate([]*protoutil.SignedData{signedData}) +} + +// VerificationSequence returns verification sequence +func (v *Verifier) VerificationSequence() uint64 { + return v.VerificationSequencer.Sequence() +} + +func verifyHashChain(block *cb.Block, prevHeaderHash string) error { + thisHdrHashOfPrevHdr := hex.EncodeToString(block.Header.PreviousHash) + if prevHeaderHash != thisHdrHashOfPrevHdr { + return errors.Errorf("previous header hash is %s but expected %s", thisHdrHashOfPrevHdr, prevHeaderHash) + } + + dataHash := hex.EncodeToString(block.Header.DataHash) + actualHashOfData := hex.EncodeToString(protoutil.ComputeBlockDataHash(block.Data)) + if dataHash != actualHashOfData { + return errors.Errorf("data hash is %s but expected %s", dataHash, actualHashOfData) + } + return nil +} + +func (v *Verifier) verifyBlockDataAndMetadata(block *cb.Block, metadata []byte) ([]types.RequestInfo, error) { + if block.Data == nil || len(block.Data.Data) == 0 { + return nil, errors.New("empty block data") + } + + if block.Metadata == nil || len(block.Metadata.Metadata) < len(cb.BlockMetadataIndex_name) { + return nil, errors.New("block metadata is either missing or contains too few entries") + } + + signatureMetadata, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_SIGNATURES) + if err != nil { + return nil, err + } + ordererMetadataFromSignature := &cb.OrdererBlockMetadata{} + if err := proto.Unmarshal(signatureMetadata.Value, ordererMetadataFromSignature); err != nil { + return nil, errors.Wrap(err, "failed unmarshaling OrdererBlockMetadata") + } + + // Ensure the view metadata in the block signature and in the proposal are the same + + metadataInBlock := &smartbftprotos.ViewMetadata{} + if err := proto.Unmarshal(ordererMetadataFromSignature.ConsenterMetadata, metadataInBlock); err != nil { + return nil, errors.Wrap(err, "failed unmarshaling smartbft metadata from block") + } + + metadataFromProposal := &smartbftprotos.ViewMetadata{} + if err := proto.Unmarshal(metadata, metadataFromProposal); err != nil { + return nil, errors.Wrap(err, "failed unmarshaling smartbft metadata from proposal") + } + + if !proto.Equal(metadataInBlock, metadataFromProposal) { + return nil, errors.Errorf("expected metadata in block to be [view_id:%d latest_sequence:%d] but got [view_id:%d latest_sequence:%d]", + metadataFromProposal.GetViewId(), metadataFromProposal.GetLatestSequence(), + metadataInBlock.GetViewId(), metadataInBlock.GetLatestSequence(), + ) + } + + rtc := v.RuntimeConfig.Load().(RuntimeConfig) + lastConfig := rtc.LastConfigBlock.Header.Number + + if protoutil.IsConfigBlock(block) { + lastConfig = block.Header.Number + } + + // Verify last config + if ordererMetadataFromSignature.LastConfig == nil { + return nil, errors.Errorf("last config is nil") + } + + if ordererMetadataFromSignature.LastConfig.Index != lastConfig { + return nil, errors.Errorf("last config in block orderer metadata points to %d but our persisted last config is %d", ordererMetadataFromSignature.LastConfig.Index, lastConfig) + } + + rawLastConfig, err := protoutil.GetMetadataFromBlock(block, cb.BlockMetadataIndex_LAST_CONFIG) + if err != nil { + return nil, err + } + lastConf := &cb.LastConfig{} + if err := proto.Unmarshal(rawLastConfig.Value, lastConf); err != nil { + return nil, err + } + if lastConf.Index != lastConfig { + return nil, errors.Errorf("last config in block metadata points to %d but our persisted last config is %d", ordererMetadataFromSignature.LastConfig.Index, lastConfig) + } + + return validateTransactions(block.Data.Data, v.verifyRequest) +} + +func validateTransactions(blockData [][]byte, verifyReq requestVerifier) ([]types.RequestInfo, error) { + var validationFinished sync.WaitGroup + validationFinished.Add(len(blockData)) + + type txnValidation struct { + indexInBlock int + extractedInfo types.RequestInfo + validationErr error + } + + noConfigAllowed := len(blockData) > 1 + + validations := make(chan txnValidation, len(blockData)) + for i, payload := range blockData { + go func(indexInBlock int, payload []byte) { + defer validationFinished.Done() + reqInfo, err := verifyReq(payload, noConfigAllowed) + validations <- txnValidation{ + indexInBlock: indexInBlock, + extractedInfo: reqInfo, + validationErr: err, + } + }(i, payload) + } + + validationFinished.Wait() + close(validations) + + indexToRequestInfo := make(map[int]types.RequestInfo) + for validationResult := range validations { + indexToRequestInfo[validationResult.indexInBlock] = validationResult.extractedInfo + if validationResult.validationErr != nil { + return nil, validationResult.validationErr + } + } + + var res []types.RequestInfo + for indexInBlock := range blockData { + res = append(res, indexToRequestInfo[indexInBlock]) + } + + return res, nil +} + +func (v *Verifier) verifySignatureIsBoundToProposal(sig *Signature, identityID uint64, prop types.Proposal) error { + // We verify the following fields: + // ConsenterMetadata []byte + // SignatureHeader []byte + // BlockHeader []byte + // OrdererBlockMetadata []byte + + // Ensure block header is equal + if !bytes.Equal(prop.Header, sig.BlockHeader) { + v.Logger.Errorf("Expected block header %s but got %s", base64.StdEncoding.EncodeToString(prop.Header), + base64.StdEncoding.EncodeToString(sig.BlockHeader)) + return errors.Errorf("mismatched block header") + } + + // Ensure signature header matches the identity + sigHdr := &cb.IdentifierHeader{} + if err := proto.Unmarshal(sig.IdentifierHeader, sigHdr); err != nil { + return errors.Wrap(err, "malformed signature header") + } + if identityID != uint64(sigHdr.Identifier) { + v.Logger.Warnf("Expected identity %d but got %d", identityID, + sigHdr.Identifier) + return errors.Errorf("identity in signature header does not match expected identity") + } + + // Ensure orderer block metadata's consenter MD matches the proposal + ordererMD := &cb.OrdererBlockMetadata{} + if err := proto.Unmarshal(sig.OrdererBlockMetadata, ordererMD); err != nil { + return errors.Wrap(err, "malformed orderer metadata in signature") + } + + if !bytes.Equal(ordererMD.ConsenterMetadata, prop.Metadata) { + v.Logger.Warnf("Expected consenter metadata %s but got %s in proposal", + base64.StdEncoding.EncodeToString(ordererMD.ConsenterMetadata), base64.StdEncoding.EncodeToString(prop.Metadata)) + return errors.Errorf("consenter metadata in OrdererBlockMetadata doesn't match proposal") + } + + block, err := ProposalToBlock(prop) + if err != nil { + v.Logger.Warnf("got malformed proposal: %v", err) + return err + } + + // Ensure Metadata slice is of the right size + if len(block.Metadata.Metadata) != len(cb.BlockMetadataIndex_name) { + return errors.Errorf("block metadata is of size %d but should be of size %d", + len(block.Metadata.Metadata), len(cb.BlockMetadataIndex_name)) + } + + signatureMetadata := &cb.Metadata{} + if err := proto.Unmarshal(block.Metadata.Metadata[cb.BlockMetadataIndex_SIGNATURES], signatureMetadata); err != nil { + return errors.Wrap(err, "malformed signature metadata") + } + + ordererMDFromBlock := &cb.OrdererBlockMetadata{} + if err := proto.Unmarshal(signatureMetadata.Value, ordererMDFromBlock); err != nil { + return errors.Wrap(err, "malformed orderer metadata in block") + } + + // Ensure the block's OrdererBlockMetadata matches the signature. + if !proto.Equal(ordererMDFromBlock, ordererMD) { + return errors.Errorf("signature's OrdererBlockMetadata and OrdererBlockMetadata extracted from block do not match") + } + + return nil +} + +type consenterVerifier struct { + logger *flogging.FabricLogger + channel string + policyManager policies.Manager +} + +// Evaluate evaluates signed data and returns no error if signature is valid and satisfies the policy +func (cv *consenterVerifier) Evaluate(signatureSet []*protoutil.SignedData) error { + policy, ok := cv.policyManager.GetPolicy(policies.ChannelOrdererWriters) + if !ok { + cv.logger.Errorf("[%s] Error: could not find policy %s in policy manager %v", cv.channel, policies.ChannelOrdererWriters, cv.policyManager) + return errors.Errorf("could not find policy %s", policies.ChannelOrdererWriters) + } + + if cv.logger.IsEnabledFor(zapcore.DebugLevel) { + cv.logger.Debugf("== Evaluating %T Policy %s ==", policy, policies.ChannelOrdererWriters) + defer cv.logger.Debugf("== Done Evaluating %T Policy %s", policy, policies.ChannelOrdererWriters) + } + + return policy.EvaluateSignedData(signatureSet) +} diff --git a/vendor/github.com/BDLS-bft/bdls/.gitattributes b/vendor/github.com/BDLS-bft/bdls/.gitattributes new file mode 100644 index 00000000000..dfe0770424b --- /dev/null +++ b/vendor/github.com/BDLS-bft/bdls/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/vendor/github.com/BDLS-bft/bdls/.gitignore b/vendor/github.com/BDLS-bft/bdls/.gitignore new file mode 100644 index 00000000000..2f44eb9dc75 --- /dev/null +++ b/vendor/github.com/BDLS-bft/bdls/.gitignore @@ -0,0 +1,3 @@ + +bdls.test +cpu.out diff --git a/vendor/github.com/BDLS-bft/bdls/CODE_OF_CONDUCT.md b/vendor/github.com/BDLS-bft/bdls/CODE_OF_CONDUCT.md new file mode 100644 index 00000000000..339bf98f497 --- /dev/null +++ b/vendor/github.com/BDLS-bft/bdls/CODE_OF_CONDUCT.md @@ -0,0 +1,8 @@ +Code of Conduct Guidelines +========================== + +Please review the Hyperledger [Code of +Conduct](https://wiki.hyperledger.org/community/hyperledger-project-code-of-conduct) +before participating. It is important that we keep things civil. + +Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. \ No newline at end of file diff --git a/vendor/github.com/BDLS-bft/bdls/CONTRIBUTING.md b/vendor/github.com/BDLS-bft/bdls/CONTRIBUTING.md new file mode 100644 index 00000000000..e876b427df6 --- /dev/null +++ b/vendor/github.com/BDLS-bft/bdls/CONTRIBUTING.md @@ -0,0 +1,32 @@ +# CONTRIBUTING + +* The integrating efforts to run BDLS as consensus protocol using the Fabric fork: +`https://github.com/BDLS-bft/fabric` + +* You can submit a PR, Issue or contact me directly for any inquiry +```xml +Email: a.alsalih2@gmail.com, +Discord: ahmedalsalih#6158 +``` + +* You can join the Mentorship Project 2023 : +https://wiki.hyperledger.org/display/INTERN/Integrate+new+BFT+protocol+%28BDLS+consensus%29+with+Fabric + + +* Bi-weekly lab meeting. +``` +Next meeting: Thursday, Mar-30, 2023 +9:30am to 10:00am Eastern Time +Repeats: +Every 2 weeks on Thursday +Location: +https://zoom.us/my/hyperledger.community.3?pwd=UE90WHhEaHRqOGEyMkV3cldKa2d2dz09 +``` + +The lab runs bi-weekly open community calls, and you are welcome to join to learn more about what the lab is, how to use it and how to get involved and help out. +You can join the calls every other Thursday at 9:30 AM Eastern at: + + +You can also talk with the developers of the lab in the **`#bdls`** channel on the Hyperledger Discord server at: + +https://chat.hyperledger.org/ diff --git a/vendor/github.com/BDLS-bft/bdls/LICENSE b/vendor/github.com/BDLS-bft/bdls/LICENSE new file mode 100644 index 00000000000..da6debcd2c7 --- /dev/null +++ b/vendor/github.com/BDLS-bft/bdls/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/vendor/github.com/BDLS-bft/bdls/MAINTAINERS.md b/vendor/github.com/BDLS-bft/bdls/MAINTAINERS.md new file mode 100644 index 00000000000..fb269b9db67 --- /dev/null +++ b/vendor/github.com/BDLS-bft/bdls/MAINTAINERS.md @@ -0,0 +1,12 @@ +## Maintainers + +### Active Maintainers +| name | Github | Discord | +|-------------------|-----------|----------------| +| Yongge Wang | [@yonggewang](https://github.com/yonggewang) | | +| Ahmed Al Salih | [@ahmed82](https://github.com/ahmed82) | | + +### Emeritus Maintainers +| name | Github | Discord | +|-------------------|-----------|----------------| +| Li Fu | [@xtaci](https://github.com/xtaci) | | diff --git a/vendor/github.com/BDLS-bft/bdls/README.md b/vendor/github.com/BDLS-bft/bdls/README.md new file mode 100644 index 00000000000..18f56d40840 --- /dev/null +++ b/vendor/github.com/BDLS-bft/bdls/README.md @@ -0,0 +1,114 @@ +# BDLS +Efficient BFT in partial synchronous networks + +[![GoDoc][1]][2] [![License][3]][4] [![Build Status][5]][6] [![Go Report Card][7]][8] [![Coverage Statusd][9]][10] [![Sourcegraph][11]][12] + +[1]: https://godoc.org/github.com/ahmed82/bdls?status.svg +[2]: https://godoc.org/github.com/ahmed82/bdls +[3]: https://img.shields.io/badge/License-Apache_2.0-blue.svg +[4]: LICENSE +[5]: https://travis-ci.org/BDLS-bft/bdls.svg?branch=master +[6]: https://travis-ci.org/BDLS-bft/bdls +[7]: https://goreportcard.com/badge/github.com/BDLS-bft/bdls?bdls +[8]: https://goreportcard.com/report/github.com/BDLS-bft/bdls +[9]: https://codecov.io/gh/BDLS-bft/bdls/branch/master/graph/badge.svg +[10]: https://codecov.io/gh/BDLS-bft/bdls +[11]: https://sourcegraph.com/github.com/BDLS-bft/bdls/-/badge.svg +[12]: https://sourcegraph.com/github.com/BDLS-bft/bdls?badge + +# BDLS Consensus +* ## [Contributing info](CONTRIBUTING.md) + +## Introduction + +BDLS is an innovative BFT consensus algorithm that features safety and liveness by +presenting a mathematically proven secure BFT protocol that is resilient in open networks such as +the Internet. BDLS overcomes many problems, such as the deadlock problem caused by unreliable +p2p/broadcast channels. These problems are all very relevant to existing realistic open +network scenarios, and are the focus of extensive work in improving Internet security, but it +is an area largely ignored by most in mainstream BFT protocol design. +(Paper: https://eprint.iacr.org/2019/1460.pdf or https://dl.acm.org/doi/abs/10.1145/3538227 or https://doi.org/10.1145/3538227 or https://www.doi.org/10.1007/978-3-030-91859-0_2 ) + +For this library, to make the runtime behavior of consensus algorithm predictable as function: +y = f(x, t), where 'x' is the message it received, and 't' is the time while being called, + then'y' is the deterministic status of consensus after 'x' and 't' applied to 'f', +it has been designed in a deterministic scheme, without parallel computing, networking, and +the correctness of program implementation can be proven with proper test cases. + +## Features + +1. Pure algorithm implementation in deterministic and predictable behavior, easily to be integrated into existing projects, refer to [DFA](https://en.wikipedia.org/wiki/Deterministic_finite_automaton) for more. +2. Well-tested on various platforms with complicated cases. +3. Auto back-off under heavy payload, guaranteed finalization(worst case gurantee). +4. Easy integratation into Blockchain & non-Blockchain consensus, like [WAL replication](https://en.wikipedia.org/wiki/Replication_(computing)#Database_replication) in database. +5. Builtin network emulation for various network latency with comprehensive statistics. + + + +## Documentation + +For complete documentation, see the associated [Godoc](https://pkg.go.dev/github.com/BDLS-bft/bdls). + + +## Install BDLS on Ubuntu Server 20.04 + +``` +sudo apt-get update +sudo apt-get -y upgrade +sudo apt-get install autoconf automake libtool curl make g++ unzip +cd /tmp +wget https://go.dev/dl/go1.17.5.linux-amd64.tar.gz +sudo tar -xvf go1.17.5.linux-amd64.tar.gz +sudo mv go /usr/local +cd +echo 'export GOROOT=/usr/local/go' >> .profile +echo 'export GOPATH=$HOME/go' >> .profile +echo 'export PATH=$GOPATH/bin:$GOROOT/bin:$PATH' >> .profile +source ~/.profile +go version +go env +git clone https://github.com/hyperledger-labs/bdls.git +cd bdls/ +git checkout master +cd cmd/emucon/ +go build . +./emucon help genkeys +./emucon genkeys --count 4 + +[open four terminals to run four participants. if you log to remote Linux, +you may use tmux commands. In tmux, you can switch termian using "ctrl+b d" +and use "tmux attach -t 0" to enter the terminal. Use "tmux list-session" +to check the current active terminals] + + +./emucon run --id 0 --listen ":4680" +./emucon run --id 1 --listen ":4681" +./emucon run --id 2 --listen ":4682" +./emucon run --id 3 --listen ":4683" + +cd ../.. +go test -v -cpuprofile=cpu.out -memprofile=mem.out -timeout 2h +``` +## Regenerate go.mod and go.sum +``` +rm go.* +go mod init github.com/hyperledger-labs/bdls +go mod tidy +go mod vendor +``` + +See benchmark ourput at: [AMD-NORMAL.TXT](benchmarks/AMD-NORMAL.TXT) and [PI4-OVERLOAD.TXT](benchmarks/PI4-OVERLOAD.TXT) + +## Specification + +1. Consensus messages are specified in [message.proto](message.proto), users of this library can encapsulate this message in a carrier message, like gossip in TCP. +2. Consensus algorithm is **NOT** thread-safe, it **MUST** be protected by some synchronization mechanism, like `sync.Mutex` or `chan` + `goroutine`. + +## Usage + +1. A testing IPC peer -- [ipc_peer.go](ipc_peer.go) +2. A testing TCP node -- [TCP based Consensus Emualtor](cmd/emucon) + +## Status + +On-going diff --git a/vendor/github.com/BDLS-bft/bdls/SECURITY.md b/vendor/github.com/BDLS-bft/bdls/SECURITY.md new file mode 100644 index 00000000000..dfe20518193 --- /dev/null +++ b/vendor/github.com/BDLS-bft/bdls/SECURITY.md @@ -0,0 +1,9 @@ +# Hyperledger Security Policy + +## Reporting a Security Bug + +If you think you have discovered a security issue in any of the Hyperledger projects, we'd love to hear from you. We will take all security bugs seriously and if confirmed upon investigation we will patch it within a reasonable amount of time and release a public security bulletin discussing the impact and credit the discoverer. + +The easiest is to email a description of the flaw and any related information (e.g. reproduction steps, version) to [security at hyperledger dot org](mailto:security@hyperledger.org). + +The process by which the Hyperledger Security Team handles security bugs is documented further in our [Defect Response page](https://wiki.hyperledger.org/display/SEC/Defect+Response) on our [wiki](https://wiki.hyperledger.org). \ No newline at end of file diff --git a/vendor/github.com/BDLS-bft/bdls/config.go b/vendor/github.com/BDLS-bft/bdls/config.go new file mode 100644 index 00000000000..14167a4b0ba --- /dev/null +++ b/vendor/github.com/BDLS-bft/bdls/config.go @@ -0,0 +1,72 @@ + +package bdls + +import ( + "crypto/ecdsa" + "time" +) + +const ( + // ConfigMinimumParticipants is the minimum number of participant allow in consensus protocol + ConfigMinimumParticipants = 4 +) + +// Config is to config the parameters of BDLS consensus protocol +type Config struct { + // the starting time point for consensus + Epoch time.Time + // CurrentHeight + CurrentHeight uint64 + // PrivateKey + PrivateKey *ecdsa.PrivateKey + // Consensus Group + Participants []Identity + // EnableCommitUnicast sets to true to enable message to be delivered via unicast + // if not(by default), message will be broadcasted + EnableCommitUnicast bool + + // StateCompare is a function from user to compare states, + // The result will be 0 if a==b, -1 if a < b, and +1 if a > b. + // Usually this will lead to block header comparsion in blockchain, or replication log in database, + // users should check fields in block header to make comparison. + StateCompare func(a State, b State) int + + // StateValidate is a function from user to validate the integrity of + // state data. + StateValidate func(State) bool + + // MessageValidator is an external validator to be called when a message inputs into ReceiveMessage + MessageValidator func(c *Consensus, m *Message, signed *SignedProto) bool + + // MessageOutCallback will be called if not nil before a message send out + MessageOutCallback func(m *Message, signed *SignedProto) + + // Identity derviation from ecdsa.PublicKey + // (optional). Default to DefaultPubKeyToIdentity + PubKeyToIdentity func(pubkey *ecdsa.PublicKey) (ret Identity) +} + +// VerifyConfig verifies the integrity of this config when creating new consensus object +func VerifyConfig(c *Config) error { + if c.Epoch.IsZero() { + return ErrConfigEpoch + } + + if c.StateCompare == nil { + return ErrConfigStateCompare + } + + if c.StateValidate == nil { + return ErrConfigStateValidate + } + + if c.PrivateKey == nil { + return ErrConfigPrivateKey + } + + if len(c.Participants) < ConfigMinimumParticipants { + return ErrConfigParticipants + } + + return nil +} diff --git a/vendor/github.com/BDLS-bft/bdls/consensus.go b/vendor/github.com/BDLS-bft/bdls/consensus.go new file mode 100644 index 00000000000..ee6ba09e310 --- /dev/null +++ b/vendor/github.com/BDLS-bft/bdls/consensus.go @@ -0,0 +1,1677 @@ +package bdls + +import ( + "bytes" + "container/list" + "crypto/ecdsa" + "crypto/elliptic" + "net" + "sort" + "time" + + //"fmt" + "github.com/BDLS-bft/bdls/crypto/blake2b" + + + proto "github.com/gogo/protobuf/proto" +) + +const ( + // the current BDLS protocol version, + // version will be sent along with messages for protocol upgrading. + ProtocolVersion = 1 + // DefaultConsensusLatency is the default propagation latency setting for + // consensus protocol, user can adjust consensus object's latency setting + // via Consensus.SetLatency() + DefaultConsensusLatency = 300 * time.Millisecond + + // MaxConsensusLatency is the ceiling of latencies + MaxConsensusLatency = 10 * time.Second +) + +type ( + // State is the data to participant in consensus. This could be candidate + // blocks in blockchain systems + State []byte + // StateHash = H(State) + StateHash [blake2b.Size256]byte +) + +// defaultHash is the system default hash function +func defaultHash(s State) StateHash { return blake2b.Sum256(s) } + +type ( + // consensusStage defines the status of consensus automata + consensusStage byte +) + +// status definitions for consensus state machine +const ( + // stages are strictly ordered, do not change! + stageRoundChanging consensusStage = iota + stageLock + stageCommit + stageLockRelease +) + +type messageTuple struct { + StateHash StateHash // computed while adding + Message *Message // the decoded message + Signed *SignedProto // the encoded message with signature +} + +// a sorter for messageTuple slice +type tupleSorter struct { + tuples []messageTuple + by func(t1, t2 *messageTuple) bool +} + +// Len implements sort.Interface +func (s *tupleSorter) Len() int { return len(s.tuples) } + +// Swap implements sort.Interface +func (s *tupleSorter) Swap(i, j int) { s.tuples[i], s.tuples[j] = s.tuples[j], s.tuples[i] } + +// Less implements sort.Interface +func (s *tupleSorter) Less(i, j int) bool { return s.by(&s.tuples[i], &s.tuples[j]) } + +// consensusRound maintains exchanging messages in a round. +type consensusRound struct { + c *Consensus // the consensus object belongs to + Stage consensusStage // indicates current status in consensus automata + RoundNumber uint64 // round number + LockedState State // leader's locked state + LockedStateHash StateHash // hash of the leaders's locked state + RoundChangeSent bool // mark if the message of this round has sent + CommitSent bool // mark if this round has sent commit message once + + // NOTE: we MUST keep the original message, to re-marshal the message may + // result in different BITS LAYOUT, and different hash of course. + roundChanges []messageTuple // stores message tuples of this round + commits []messageTuple // stores message tuples of this round + + // track current max proposed state in , we don't have to compute this for + // a non-leader participant, or if there're no more than 2t+1 messages for leader. + MaxProposedState State + MaxProposedCount int +} + +// newConsensusRound creates a new round, and sets the round number +func newConsensusRound(round uint64, c *Consensus) *consensusRound { + r := new(consensusRound) + r.RoundNumber = round + r.c = c + return r +} + +// AddRoundChange adds a message to this round, and +// checks to accept only one message from one participant, +// to prevent multiple proposals attack. +func (r *consensusRound) AddRoundChange(sp *SignedProto, m *Message) bool { + for k := range r.roundChanges { + if r.roundChanges[k].Signed.X == sp.X && r.roundChanges[k].Signed.Y == sp.Y { + return false + } + } + + r.roundChanges = append(r.roundChanges, messageTuple{StateHash: r.c.stateHash(m.State), Message: m, Signed: sp}) + return true +} + +// FindRoundChange will try to find a from a given participant, +// and returns index, -1 if not found +func (r *consensusRound) FindRoundChange(X PubKeyAxis, Y PubKeyAxis) int { + for k := range r.roundChanges { + if r.roundChanges[k].Signed.X == X && r.roundChanges[k].Signed.Y == Y { + return k + } + } + return -1 +} + +// RemoveRoundChange removes the given message at idx +func (r *consensusRound) RemoveRoundChange(idx int) { + // swap to the end and shrink slice + n := len(r.roundChanges) - 1 + r.roundChanges[idx], r.roundChanges[n] = r.roundChanges[n], r.roundChanges[idx] + r.roundChanges[n] = messageTuple{} // set to nil to avoid memory leak + r.roundChanges = r.roundChanges[:n] +} + +// NumRoundChanges returns count of messages. +func (r *consensusRound) NumRoundChanges() int { return len(r.roundChanges) } + +// SignedRoundChanges converts and returns []*SignedProto(as slice) +func (r *consensusRound) SignedRoundChanges() []*SignedProto { + proof := make([]*SignedProto, 0, len(r.roundChanges)) + for k := range r.roundChanges { + proof = append(proof, r.roundChanges[k].Signed) + } + return proof +} + +// RoundChangeStates returns all non-nil state in exchanging round change message as slice +func (r *consensusRound) RoundChangeStates() []State { + states := make([]State, 0, len(r.roundChanges)) + for k := range r.roundChanges { + if r.roundChanges[k].Message.State != nil { + states = append(states, r.roundChanges[k].Message.State) + } + } + return states +} + +// AddCommit adds decoded messages along with its original signed message unchanged, +// also, messages will be de-duplicated to prevent multiple proposals attack. +func (r *consensusRound) AddCommit(sp *SignedProto, m *Message) bool { + for k := range r.commits { + if r.commits[k].Signed.X == sp.X && r.commits[k].Signed.Y == sp.Y { + return false + } + } + r.commits = append(r.commits, messageTuple{StateHash: r.c.stateHash(m.State), Message: m, Signed: sp}) + return true +} + +// NumCommitted counts messages which points to what the leader has locked. +func (r *consensusRound) NumCommitted() int { + var count int + for k := range r.commits { + if r.commits[k].StateHash == r.LockedStateHash { + count++ + } + } + return count +} + +// SignedCommits converts and returns []*SignedProto +func (r *consensusRound) SignedCommits() []*SignedProto { + proof := make([]*SignedProto, 0, len(r.commits)) + for k := range r.commits { + proof = append(proof, r.commits[k].Signed) + } + return proof +} + +// GetMaxProposed finds the most agreed-on non-nil state, if these is any. +func (r *consensusRound) GetMaxProposed() (s State, count int) { + if len(r.roundChanges) == 0 { + return nil, 0 + } + + // sort by hash, to group identical hashes together + // O(n*logn) + sorter := tupleSorter{ + tuples: r.roundChanges, + // sort by it's hash lexicographically + by: func(t1, t2 *messageTuple) bool { + return bytes.Compare(t1.StateHash[:], t2.StateHash[:]) < 0 + }, + } + sort.Sort(&sorter) + + // find the maximum occurred hash + // O(n) + maxCount := 1 + maxState := r.roundChanges[0] + curCount := 1 + + n := len(r.roundChanges) + for i := 1; i < n; i++ { + if r.roundChanges[i].StateHash == r.roundChanges[i-1].StateHash { + curCount++ + } else { + if curCount > maxCount { + maxCount = curCount + maxState = r.roundChanges[i-1] + } + curCount = 1 + } + } + + // if the last hash is the maximum occurred + if curCount > maxCount { + maxCount = curCount + maxState = r.roundChanges[n-1] + } + + return maxState.Message.State, maxCount +} + +// Consensus implements a deterministic BDLS consensus protocol. +// +// It has no internal clocking or IO, and no parallel processing. +// The runtime behavior is predictable and deterministic. +// Users should write their own timing and IO function to feed in +// messages and ticks to trigger timeouts. +type Consensus struct { + latestState State // latest confirmed state of current height + latestHeight uint64 // latest confirmed height + latestRound uint64 // latest confirmed round + latestProof *SignedProto // latest message to prove the state + + unconfirmed []State // data awaiting to be confirmed at next height + + rounds list.List // all rounds at next height(consensus round in progress) + currentRound *consensusRound // current round which has collected >=2t+1 + + // timeouts in different stage + rcTimeout time.Time // roundchange status timeout: Delta_0 + lockTimeout time.Time // lock status timeout: Delta_1 + commitTimeout time.Time // commit status timeout: Delta_2 + lockReleaseTimeout time.Time // lock-release status timeout: Delta_3 + + // locked states, along with its signatures and hashes in tuple + locks []messageTuple + + // the StateCompare function from config + stateCompare func(State, State) int + // the StateValidate function from config + stateValidate func(State) bool + // message in callback + messageValidator func(c *Consensus, m *Message, sp *SignedProto) bool + // message out callback + messageOutCallback func(m *Message, sp *SignedProto) + // public key to identity function + pubKeyToIdentity func(pubkey *ecdsa.PublicKey) Identity + + // the StateHash function to identify a state + stateHash func(State) StateHash + + // private key + privateKey *ecdsa.PrivateKey + // my publickey coodinate + identity Identity + // curve retrieved from private key + curve elliptic.Curve + + // transmission delay + latency time.Duration + + // all connected peers + peers []PeerInterface + + // participants is the consensus group, current leader is r % quorum + participants []Identity + + // count num of individual identities + numIdentities int //[YONGGE WANG' comments:] make sure this is synchronized with []Identity + + // set to true to enable message unicast + enableCommitUnicast bool + + // NOTE: fixed leader for testing purpose + fixedLeader *Identity + + // broadcasting messages being sent to myself + loopback [][]byte + + // the last message which caused round change + lastRoundChangeProof []*SignedProto +} + +// NewConsensus creates a BDLS consensus object to participant in consensus procedure, +// the consensus object returned is data in memory without goroutines or other +// non-deterministic objects, and errors will be returned if there is problem, with +// the given config. +func NewConsensus(config *Config) (*Consensus, error) { + err := VerifyConfig(config) + if err != nil { + return nil, err + } + + c := new(Consensus) + c.init(config) + return c, nil +} + +// init consensus with config +func (c *Consensus) init(config *Config) { + // setting current state & height + c.latestHeight = config.CurrentHeight + c.participants = config.Participants + c.stateCompare = config.StateCompare + c.stateValidate = config.StateValidate + c.messageValidator = config.MessageValidator + c.messageOutCallback = config.MessageOutCallback + c.privateKey = config.PrivateKey + c.pubKeyToIdentity = config.PubKeyToIdentity + c.enableCommitUnicast = config.EnableCommitUnicast + + // if config has not set hash function, use the default + if c.stateHash == nil { + c.stateHash = defaultHash + } + // if config has not set public key to identity function, use the default + if c.pubKeyToIdentity == nil { + c.pubKeyToIdentity = DefaultPubKeyToIdentity + } + c.identity = c.pubKeyToIdentity(&c.privateKey.PublicKey) + c.curve = c.privateKey.Curve + + // initial default parameters settings + c.latency = DefaultConsensusLatency + + // and initiated the first proposal + c.switchRound(0) + c.currentRound.Stage = stageRoundChanging + c.broadcastRoundChange() + // set rcTimeout to lockTimeout + c.rcTimeout = config.Epoch.Add(c.roundchangeDuration(0)) + + // count number of individual identites + ids := make(map[Identity]bool) + for _, id := range c.participants { + ids[id] = true + } + c.numIdentities = len(ids) +} + +// calculates roundchangeDuration +func (c *Consensus) roundchangeDuration(round uint64) time.Duration { + d := 2 * c.latency * (1 << round) + if d > MaxConsensusLatency { + d = MaxConsensusLatency + } + return d +} + +// calculates collectDuration +func (c *Consensus) collectDuration(round uint64) time.Duration { + d := 2 * c.latency * (1 << round) + if d > MaxConsensusLatency { + d = MaxConsensusLatency + } + return d +} + +// calculates lockDuration +func (c *Consensus) lockDuration(round uint64) time.Duration { + d := 4 * c.latency * (1 << round) + if d > MaxConsensusLatency { + d = MaxConsensusLatency + } + return d +} + +// calculates commitDuration +func (c *Consensus) commitDuration(round uint64) time.Duration { + d := 2 * c.latency * (1 << round) + if d > MaxConsensusLatency { + d = MaxConsensusLatency + } + return d +} + +// calculates lockReleaseDuration +func (c *Consensus) lockReleaseDuration(round uint64) time.Duration { + d := 2 * c.latency * (1 << round) + if d > MaxConsensusLatency { + d = MaxConsensusLatency + } + return d +} + +// maximalLocked finds the maximum locked data in this round, +// with regard to StateCompare function in config. +func (c *Consensus) maximalLocked() State { + if len(c.locks) > 0 { + maxState := c.locks[0].Message.State + for i := 1; i < len(c.locks); i++ { + if c.stateCompare(maxState, c.locks[i].Message.State) < 0 { + maxState = c.locks[i].Message.State + } + } + return maxState + } + return nil +} + +// maximalUnconfirmed finds the maximal unconfirmed data with, +// regard to the StateCompare function in config. +func (c *Consensus) maximalUnconfirmed() State { + if len(c.unconfirmed) > 0 { + maxState := c.unconfirmed[0] + for i := 1; i < len(c.unconfirmed); i++ { + if c.stateCompare(maxState, c.unconfirmed[i]) < 0 { + maxState = c.unconfirmed[i] + } + } + return maxState + } + return nil +} + +// verifyMessage verifies message signature against it's & , +// and also checks if the signer is a valid participant. +// returns it's decoded 'Message' object if signature has proved authentic. +// returns nil and error if message has not been correctly signed or from an unknown participant. +func (c *Consensus) verifyMessage(signed *SignedProto) (*Message, error) { + if signed == nil { + return nil, ErrMessageIsEmpty + } + + // check signer's identity, all participants have proven + // public key + knownParticipants := false + coord := c.pubKeyToIdentity(signed.PublicKey(c.curve)) + for k := range c.participants { + if coord == c.participants[k] { + knownParticipants = true + } + } + + if !knownParticipants { + return nil, ErrMessageUnknownParticipant + } + + /* + // public key validation + p := defaultCurve.Params().P + x := new(big.Int).SetBytes(signed.X[:]) + y := new(big.Int).SetBytes(signed.Y[:]) + if x.Cmp(p) >= 0 || y.Cmp(p) >= 0 { + return nil, ErrMessageSignature + } + if !defaultCurve.IsOnCurve(x, y) { + return nil, ErrMessageSignature + } + */ + + // as public key is proven , we don't have to verify the public key + if !signed.Verify(c.curve) { + return nil, ErrMessageSignature + } + + // decode message + m := new(Message) + err := proto.Unmarshal(signed.Message, m) + if err != nil { + return nil, err + } + return m, nil +} + +// verify message +func (c *Consensus) verifyRoundChangeMessage(m *Message) error { + // check message height + if m.Height != c.latestHeight+1 { + return ErrRoundChangeHeightMismatch + } + + // check round in protocol + if m.Round < c.currentRound.RoundNumber { + return ErrRoundChangeRoundLower + } + + // state data validation for non-null + if m.State != nil { + if !c.stateValidate(m.State) { + return ErrRoundChangeStateValidation + } + } + + return nil +} + +// verifyLockMessage verifies proofs from messages, +// a lock message must contain at least 2t+1 individual +// messages on B' +func (c *Consensus) verifyLockMessage(m *Message, signed *SignedProto) error { + // check message height + if m.Height != c.latestHeight+1 { + return ErrLockHeightMismatch + } + + // check round in protocol + if m.Round < c.currentRound.RoundNumber { + return ErrLockRoundLower + } + + // a message from leader MUST include data along with the message + if m.State == nil { + return ErrLockEmptyState + } + + // state data validation + if !c.stateValidate(m.State) { + return ErrLockStateValidation + } + + // make sure this message has been signed by the leader + leaderKey := c.roundLeader(m.Round) + if c.pubKeyToIdentity(signed.PublicKey(c.curve)) != leaderKey { + return ErrLockNotSignedByLeader + } + + // validate proofs enclosed in the message one by one + rcs := make(map[Identity]State) + for _, proof := range m.Proof { + // first we need to verify the signature,and identity of this proof + mProof, err := c.verifyMessage(proof) + if err != nil { + if err == ErrMessageUnknownParticipant { + return ErrLockProofUnknownParticipant + } + return err + } + + // then we need to check the message type + if mProof.Type != MessageType_RoundChange { + return ErrLockProofTypeMismatch + } + + // and we also need to check the height & round field, + // all messages must be in the same round as the lock message + if mProof.Height != m.Height { + return ErrLockProofHeightMismatch + } + + if mProof.Round != m.Round { + return ErrLockProofRoundMismatch + } + + // state data validation in proofs + if mProof.State != nil { + if !c.stateValidate(mProof.State) { + return ErrLockProofStateValidation + } + } + + // use map to guarantee we will only accept at most 1 message from one + // individual participant + rcs[c.pubKeyToIdentity(proof.PublicKey(c.curve))] = mProof.State + } + + // count individual proofs to B', which has already guaranteed to be the maximal one. + var numValidateProofs int + mHash := c.stateHash(m.State) + for _, v := range rcs { + if c.stateHash(v) == mHash { // B' + numValidateProofs++ + } + } + + // check if valid proofs count is less that 2*t+1 + if numValidateProofs < 2*c.t()+1 { + return ErrLockProofInsufficient + } + return nil +} + +// verifyLockReleaseMessage will verify LockRelease field in a messages, +// returns the embedded message if valid +func (c *Consensus) verifyLockReleaseMessage(signed *SignedProto) (*Message, error) { + // not in lock release status, omit this message + if c.currentRound.Stage != stageLockRelease { + return nil, ErrLockReleaseStatus + } + + // verify and decode the embedded lock message + lockmsg, err := c.verifyMessage(signed) + if err != nil { + return nil, err + } + + // recursively verify proofs in lock message + err = c.verifyLockMessage(lockmsg, signed) + if err != nil { + return nil, err + } + return lockmsg, nil +} + +// verifySelectMessage verifies proofs from message MUST contain at least 2t+1 individual messages, but +// proofs from + if m.State != nil { + if !c.stateValidate(m.State) { + return ErrSelectStateValidation + } + } + + // make sure this message has been signed by the leader + leaderKey := c.roundLeader(m.Round) + if c.pubKeyToIdentity(signed.PublicKey(c.curve)) != leaderKey { + return ErrSelectNotSignedByLeader + } + + rcs := make(map[Identity]State) + for _, proof := range m.Proof { + mProof, err := c.verifyMessage(proof) + if err != nil { + if err == ErrMessageUnknownParticipant { + return ErrSelectProofUnknownParticipant + } + return err + } + + if mProof.Type != MessageType_RoundChange { + return ErrSelectProofTypeMismatch + } + + if mProof.Height != m.Height { + return ErrSelectProofHeightMismatch + } + + if mProof.Round != m.Round { + return ErrSelectProofRoundMismatch + } + + // state data validation in proofs + if mProof.State != nil { + if !c.stateValidate(mProof.State) { + return ErrSelectProofStateValidation + } + } + + // we also need to check the B'' selected by leader is the maximal one, + // if data has been proposed. + if mProof.State != nil && m.State != nil { + if c.stateCompare(m.State, mProof.State) < 0 { + return ErrSelectProofNotTheMaximal + } + } + + // we also stores B'' == NULL for counting + rcs[c.pubKeyToIdentity(proof.PublicKey(c.curve))] = mProof.State + } + + // check we have at least 2*t+1 proof + if len(rcs) < 2*c.t()+1 { + return ErrSelectProofInsufficient + } + + // count maximum proofs with B' != NULL with identical data hash, + // to prevent leader cheating on select. + dataProposals := make(map[StateHash]int) + for _, data := range rcs { + if data != nil { + dataProposals[c.stateHash(data)]++ + } + } + + // if m.State == NULL, but there are non-NULL proofs, + // the leader may be cheating + if m.State == nil && len(dataProposals) > 0 { + return ErrSelectStateMismatch + } + + // find the highest proposed B'(not NULL) + var maxProposed int + for _, count := range dataProposals { + if count > maxProposed { + maxProposed = count + } + } + + // if these are more than 2*t+1 valid proofs to B', + // this also suggests that the leader may cheat. + if maxProposed >= 2*c.t()+1 { + return ErrSelectProofExceeded + } + + return nil +} + +// verifyCommitMessage will check if this message is acceptable to consensus +func (c *Consensus) verifyCommitMessage(m *Message) error { + // the leader has to be in COMMIT status to process this message + if c.currentRound.Stage != stageCommit { + return ErrCommitStatus + } + + // a message from participants MUST includes data along with the message + if m.State == nil { + return ErrCommitEmptyState + } + + // state data validation + if !c.stateValidate(m.State) { + return ErrCommitStateValidation + } + + // check height + if m.Height != c.latestHeight+1 { + return ErrCommitHeightMismatch + } + + // only accept commits to current round + if c.currentRound.RoundNumber != m.Round { + return ErrCommitRoundMismatch + } + + // check state match + if c.stateHash(m.State) != c.currentRound.LockedStateHash { + return ErrCommitStateMismatch + } + + return nil +} + +// ValidateDecideMessage validates a message for non-participants, +// the consensus core must be correctly initialized to validate. +// the targetState is to compare the target state enclosed in decide message +func (c *Consensus) ValidateDecideMessage(bts []byte, targetState []byte) error { + signed, err := DecodeSignedMessage(bts) + if err != nil { + return err + } + + return c.validateDecideMessage(signed, targetState) +} + +// DecodeSignedMessage decodes a binary representation of signed consensus message. +func DecodeSignedMessage(bts []byte) (*SignedProto, error) { + signed := new(SignedProto) + err := proto.Unmarshal(bts, signed) + if err != nil { + return nil, err + } + return signed, nil +} + +// DecodeMessage decodes a binary representation of consensus message. +func DecodeMessage(bts []byte) (*Message, error) { + msg := new(Message) + err := proto.Unmarshal(bts, msg) + if err != nil { + return nil, err + } + return msg, nil +} + +// validateDecideMessage validates a decoded message for non-participants, +// the consensus core must be correctly initialized to validate. +func (c *Consensus) validateDecideMessage(signed *SignedProto, targetState []byte) error { + // check message version + if signed.Version != ProtocolVersion { + return ErrMessageVersion + } + + // check message signature & qualifications + m, err := c.verifyMessage(signed) + if err != nil { + return err + } + + // compare state + if !bytes.Equal(m.State, targetState) { + return ErrMismatchedTargetState + } + + // verify decide message + if m.Type == MessageType_Decide { + err := c.verifyDecideMessage(m, signed) + if err != nil { + return err + } + return nil + } + return ErrMessageUnknownMessageType +} + +// verifyDecideMessage verifies proofs from message, which MUST +// contain at least 2t+1 individual messages to B'. +func (c *Consensus) verifyDecideMessage(m *Message, signed *SignedProto) error { + // a message from leader MUST include data along with the message + if m.State == nil { + return ErrDecideEmptyState + } + + // state data validation + if !c.stateValidate(m.State) { + return ErrDecideStateValidation + } + + // check height + if m.Height <= c.latestHeight { + return ErrDecideHeightLower + } + + // make sure this message has been signed by the leader + leaderKey := c.roundLeader(m.Round) + if c.pubKeyToIdentity(signed.PublicKey(c.curve)) != leaderKey { + return ErrDecideNotSignedByLeader + } + + commits := make(map[Identity]State) + for _, proof := range m.Proof { + mProof, err := c.verifyMessage(proof) + if err != nil { + if err == ErrMessageUnknownParticipant { + return ErrDecideProofUnknownParticipant + } + return err + } + + if mProof.Type != MessageType_Commit { + return ErrDecideProofTypeMismatch + } + + if mProof.Height != m.Height { + return ErrDecideProofHeightMismatch + } + + if mProof.Round != m.Round { + return ErrDecideProofRoundMismatch + } + + if !c.stateValidate(mProof.State) { + return ErrDecideProofStateValidation + } + + // state data validation in proofs + if mProof.State != nil { + if !c.stateValidate(mProof.State) { + return ErrSelectProofStateValidation + } + } + + commits[c.pubKeyToIdentity(proof.PublicKey(c.curve))] = mProof.State + } + + // count proofs to m.State + var numValidateProofs int + mHash := c.stateHash(m.State) + for _, v := range commits { + if c.stateHash(v) == mHash { + numValidateProofs++ + } + } + + // check to see if the message has at least 2*t+1 valid proofs, + // if not, the leader may cheat. + if numValidateProofs < 2*c.t()+1 { + return ErrDecideProofInsufficient + } + return nil +} + +// broadcastRoundChange will broadcast messages on +// current round, taking the maximal B' from unconfirmed data. +func (c *Consensus) broadcastRoundChange() { + // if has sent in this round, + // then just ignore. But if we are in roundchanging state, + // we should send repeatedly, for boostrap process. + if c.currentRound.RoundChangeSent && c.currentRound.Stage != stageRoundChanging { + return + } + + // first we need to check if there is any locked data, + // locked data must be sent if there is any. + data := c.maximalLocked() + if data == nil { + // if there's none locked data, we pick the maximum unconfirmed data to propose + data = c.maximalUnconfirmed() + // if still null, return + if data == nil { + return + } + } + + var m Message + m.Type = MessageType_RoundChange + m.Height = c.latestHeight + 1 + m.Round = c.currentRound.RoundNumber + m.State = data + c.broadcast(&m) + c.currentRound.RoundChangeSent = true + //log.Println("broadcast:") +} + +// broadcastLock will broadcast messages on current round, +// the currentRound should have a chosen data in this round. +func (c *Consensus) broadcastLock() { + var m Message + m.Type = MessageType_Lock + m.Height = c.latestHeight + 1 + m.Round = c.currentRound.RoundNumber + m.State = c.currentRound.LockedState + m.Proof = c.currentRound.SignedRoundChanges() + c.broadcast(&m) + //log.Println("broadcast:") +} + +// broadcastLockRelease will broadcast messages, +func (c *Consensus) broadcastLockRelease(signed *SignedProto) { + var m Message + m.Type = MessageType_LockRelease + m.Height = c.latestHeight + 1 + m.Round = c.currentRound.RoundNumber + m.LockRelease = signed + c.broadcast(&m) + //log.Println("broadcast:") +} + +// broadcastSelect will broadcast a ", m.State) +} + +// broadcastDecide will broadcast a message by the leader, +// from current round with proofs. +func (c *Consensus) broadcastDecide() *SignedProto { + var m Message + m.Type = MessageType_Decide + m.Height = c.latestHeight + 1 + m.Round = c.currentRound.RoundNumber + m.State = c.currentRound.LockedState + m.Proof = c.currentRound.SignedCommits() + return c.broadcast(&m) + //log.Println("broadcast:") +} + +// broadcastResync will broadcast a message by the leader, +// from current round with proofs. +func (c *Consensus) broadcastResync() { + if c.lastRoundChangeProof == nil { + return + } + + var m Message + m.Type = MessageType_Resync + // we only care about messages in resync + m.Proof = c.lastRoundChangeProof + c.broadcast(&m) + //log.Println("broadcast:") +} + +// sendCommit will send a message by participants to the leader +// from received message. +func (c *Consensus) sendCommit(msgLock *Message) { + if c.currentRound.CommitSent { + return + } + + var m Message + m.Type = MessageType_Commit + m.Height = msgLock.Height // h + m.Round = msgLock.Round // r + m.State = msgLock.State // B'j + if c.enableCommitUnicast { + c.sendTo(&m, c.roundLeader(m.Round)) + } else { + c.broadcast(&m) + } + c.currentRound.CommitSent = true + //log.Println("send:") +} + +// broadcast signs the message with private key before broadcasting to all peers. +func (c *Consensus) broadcast(m *Message) *SignedProto { + // sign + sp := new(SignedProto) + sp.Version = ProtocolVersion + sp.Sign(m, c.privateKey) + + // message callback + if c.messageOutCallback != nil { + c.messageOutCallback(m, sp) + } + // protobuf marshalling + out, err := proto.Marshal(sp) + if err != nil { + panic(err) + } + + // send to peers one by one + for _, peer := range c.peers { + _ = peer.Send(out) + } + + // we also need to send this message to myself + c.loopback = append(c.loopback, out) + return sp +} + +// sendTo signs the message with private key before transmitting to the peer. +func (c *Consensus) sendTo(m *Message, leader Identity) { + // sign + sp := new(SignedProto) + sp.Version = ProtocolVersion + sp.Sign(m, c.privateKey) + + // message callback + if c.messageOutCallback != nil { + c.messageOutCallback(m, sp) + } + + // protobuf marshalling + out, err := proto.Marshal(sp) + if err != nil { + panic(err) + } + + // we need to send this message to myself (via loopback) if i'm the leader + if leader == c.identity { + c.loopback = append(c.loopback, out) + return + } + + // otherwise, find and transmit to the leader + for _, peer := range c.peers { + if pk := peer.GetPublicKey(); pk != nil { + coord := c.pubKeyToIdentity(pk) + if coord == leader { + // we do not return here to avoid missing re-connected peer. + peer.Send(out) + } + } + } +} + +// propagate broadcasts signed message UNCHANGED to peers. +func (c *Consensus) propagate(bts []byte) { + // send to peers one by one + for _, peer := range c.peers { + _ = peer.Send(bts) + } +} + +// getRound returns the consensus round with given idx, create one if not exists +// if purgeLower has set, all lower rounds will be cleared +func (c *Consensus) getRound(idx uint64, purgeLower bool) *consensusRound { + var next *list.Element + for elem := c.rounds.Front(); elem != nil; elem = next { + next = elem.Next() + r := elem.Value.(*consensusRound) + + if r.RoundNumber < idx { // lower round + // if remove flag has set, remove this round safely, + // usually used by switchRound + if purgeLower { + c.rounds.Remove(elem) + } + continue + } else if idx < r.RoundNumber { // higher round + // insert a new round entry before this round + // to make sure the list is ordered + newr := newConsensusRound(idx, c) + c.rounds.InsertBefore(newr, elem) + return newr + } else if r.RoundNumber == idx { // found entry + return r + } + } + + // looped to the end, we create and push back + newr := newConsensusRound(idx, c) + c.rounds.PushBack(newr) + return newr +} + +// lockRelease updates locks while entering lock-release status +// and will broadcast its max B' if there is any. +func (c *Consensus) lockRelease() { + // only keep the locked B' with the max round number + // while switching to lock-release status + if len(c.locks) > 0 { + max := c.locks[0] + for i := 1; i < len(c.locks); i++ { + if max.Message.Round < c.locks[i].Message.Round { + max = c.locks[i] + } + } + c.locks = []messageTuple{max} + c.broadcastLockRelease(max.Signed) + } +} + +// switchRound sets currentRound to the given idx, and creates new a consensusRound +// if it's not been initialized. +// and all lower rounds will be cleared while switching. +func (c *Consensus) switchRound(round uint64) { c.currentRound = c.getRound(round, true) } + +// roundLeader returns leader's identity for a given round +func (c *Consensus) roundLeader(round uint64) Identity { + // NOTE: fixed leader is for testing + if c.fixedLeader != nil { + return *c.fixedLeader + } + return c.participants[int(round)%len(c.participants)] +} + +// heightSync changes current height to the given height with state +// resets all fields to this new height. +func (c *Consensus) heightSync(height uint64, round uint64, s State, now time.Time) { + c.latestHeight = height // set height + c.latestRound = round // set round + c.latestState = s // set state + + c.currentRound = nil // clean current round pointer + c.lastRoundChangeProof = nil // clean round change proof + c.rounds.Init() // clean all round + c.locks = nil // clean locks + c.unconfirmed = nil // clean all unconfirmed states from previous heights + c.switchRound(0) // start new round at new height + c.currentRound.Stage = stageRoundChanging +} + +// t calculates (n-1)/3 +func (c *Consensus) t() int { return (c.numIdentities - 1) / 3 } + +// Propose adds a new state to unconfirmed queue to particpate in +// consensus at next height. +func (c *Consensus) Propose(s State) { + if s == nil { + return + } + + sHash := c.stateHash(s) + for k := range c.unconfirmed { + if c.stateHash(c.unconfirmed[k]) == sHash { + return + } + } + c.unconfirmed = append(c.unconfirmed, s) +} + +// ReceiveMessage processes incoming consensus messages, and returns error +// if message cannot be processed for some reason. +func (c *Consensus) ReceiveMessage(bts []byte, now time.Time) (err error) { + // messages broadcasted to myself may be queued recursively, and + // we only process these messages in defer to avoid side effects + // while processing. + defer func() { + for len(c.loopback) > 0 { + bts := c.loopback[0] + c.loopback = c.loopback[1:] + // NOTE: message directed to myself ignores error. + _ = c.receiveMessage(bts, now) + } + }() + + return c.receiveMessage(bts, now) +} + +func (c *Consensus) receiveMessage(bts []byte, now time.Time) error { + // unmarshal signed message + signed := new(SignedProto) + err := proto.Unmarshal(bts, signed) + if err != nil { + return err + } + + // check message version + if signed.Version != ProtocolVersion { + return ErrMessageVersion + } + + // check message signature & qualifications + m, err := c.verifyMessage(signed) + if err != nil { + return err + } + + // callback for incoming message + if c.messageValidator != nil { + if !c.messageValidator(c, m, signed) { + return ErrMessageValidator + } + } + + // message switch + switch m.Type { + case MessageType_Nop: + // nop does nothing + return nil + case MessageType_RoundChange: + err := c.verifyRoundChangeMessage(m) + if err != nil { + return err + } + + // for message, we need to find in each round + // to check if this sender has already sent + // we only keep the message from the max round. + // NOTE: we don't touch current round to prevent removing + // valid proofs. + // NOTE: the total messages are bounded to max 2*participants + // at any time, so the loop has O(n) time complexity + var next *list.Element + for elem := c.rounds.Front(); elem != nil; elem = next { + next = elem.Next() + cr := elem.Value.(*consensusRound) + if idx := cr.FindRoundChange(signed.X, signed.Y); idx != -1 { // located! + if m.Round == c.currentRound.RoundNumber { // don't remove now! + continue + } else if cr.RoundNumber > m.Round { + // existing message is higher than incoming message, + // just ignore. + return nil + } else if cr.RoundNumber < m.Round { + // existing message is lower than incoming message, + // remove the existing message from this round. + cr.RemoveRoundChange(idx) + // if no message remained in this round, release + // the round resources too, to prevent OOM attack + if cr.NumRoundChanges() == 0 { + c.rounds.Remove(elem) + } + } + } + } + + // locate to round m.Round. + // NOTE: getRound must not be called before previous checks done + // in order to prevent OOM attack by creating round objects. + round := c.getRound(m.Round, false) + // as we cleared all lower rounds message, we handle the message + // at round m.Round. if this message is not duplicated in m.Round, + // round records message along with its signed message + // to provide proofs in the future. + if round.AddRoundChange(signed, m) { + // During any time of the protocol, if a the Pacemaker of Pj (including Pi) + // receives at least 2t + 1 round-change message (including round-change + // message from himself) for round r (which is larger than its current round + // status), it enters lock status of round r + // + // NOTE: m.Round lower than currentRound.RoundNumber has been tested by + // verifyRoundChangeMessage + // NOTE: lock stage can only be entered once for a single round, malicious + // participant can keep on broadcasting increasing to everyone, + // and old messages will be removed from previous rounds in such + // case, so rounds may possibly satisify 2*t+1 more than once. + // + // Example: P sends r+1 to remove from r, and sends to r again to trigger 2t+1 once + // more to reset timeout. + if round.NumRoundChanges() == 2*c.t()+1 && round.Stage < stageLock { + // switch to this round + c.switchRound(m.Round) + // record this round change proof for resyncing + c.lastRoundChangeProof = c.currentRound.SignedRoundChanges() + + // If Pj has not broadcasted the round-change message yet, + // it broadcasts now. + c.broadcastRoundChange() + + // leader of this round MUST wait on collectDuration, + // to decide to broadcast or message + err := c.verifySelectMessage(m, signed) + if err != nil { + return err + } + + // round will be increased monotonically + if m.Round > c.currentRound.RoundNumber { + c.switchRound(m.Round) + c.lastRoundChangeProof = []*SignedProto{signed} // record this proof for resyncing + } + + // for rounds r' >= r, we must check c.stage to stageLockRelease + // only once to prevent resetting lockReleaseTimeout or shifting c.cstage + if c.currentRound.Stage < stageLockRelease { + c.currentRound.Stage = stageLockRelease + c.lockReleaseTimeout = now.Add(c.commitDuration(m.Round)) + c.lockRelease() + // add to Blockj + c.Propose(m.State) + } + + case MessageType_Lock: + // verify message + err := c.verifyLockMessage(m, signed) + if err != nil { + return err + } + + // round will be increased monotonically + if m.Round > c.currentRound.RoundNumber { + c.switchRound(m.Round) + c.lastRoundChangeProof = []*SignedProto{signed} // record this proof for resyncing + } + + // for rounds r' >= r, we must check to enter commit status + // only once to prevent resetting commitTimeout or shifting c.cstage + if c.currentRound.Stage < stageCommit { + c.currentRound.Stage = stageCommit + c.commitTimeout = now.Add(c.commitDuration(m.Round)) + + mHash := c.stateHash(m.State) + // release any potential lock on B' in this round + // in-place deletion + o := 0 + for i := 0; i < len(c.locks); i++ { + if c.locks[i].StateHash != mHash { + c.locks[o] = c.locks[i] + o++ // o is the new length of c.locks + } + } + c.locks = c.locks[:o] + // append the new element + c.locks = append(c.locks, messageTuple{StateHash: mHash, Message: m, Signed: signed}) + } + + // for any incoming message with r=r', sendCommit will send + // once. + c.sendCommit(m) + + case MessageType_LockRelease: + // verifies the LockRelease field in message. + lockmsg, err := c.verifyLockReleaseMessage(m.LockRelease) + if err != nil { + return err + } + + // length of locks is 0, append and return. + if len(c.locks) == 0 { + c.locks = append(c.locks, messageTuple{StateHash: c.stateHash(lockmsg.State), Message: lockmsg, Signed: m.LockRelease}) + return nil + } + + // remove any locks if lockmsg.r > r' and keep lockmsg.r, + o := 0 + for i := 0; i < len(c.locks); i++ { + if !(lockmsg.Round > c.locks[i].Message.Round) { + // if the round of this lock is not larger than what we + // have kept, ignore and continue. + c.locks[o] = c.locks[i] + o++ + } + } + + // some locks have been removed if o is smaller than original locks length, + // then we keep this lock. + if o < len(c.locks) { + c.locks = c.locks[:o] + c.locks = append(c.locks, messageTuple{StateHash: c.stateHash(lockmsg.State), Message: lockmsg, Signed: m.LockRelease}) + } + + case MessageType_Commit: + // leader process commits message from all participants, + // check to see if I'm the leader of this round to process this message. + leaderKey := c.roundLeader(m.Round) + if leaderKey == c.identity { + // verify commit message. + // NOTE: leader only accept commits for current height & round. + err := c.verifyCommitMessage(m) + if err != nil { + return err + } + + // verifyCommitMessage can guarantee that the message is to currentRound, + // so we're safe to process in current round. + if c.currentRound.AddCommit(signed, m) { + // NOTE: we proceed the following only when AddCommit returns true. + // NumCommitted will only return commits with locked B' + // and ignore non-B' commits. + if c.currentRound.NumCommitted() >= 2*c.t()+1 { + /* + log.Println("======= LEADER'S DECIDE=====") + log.Println("Height:", c.currentHeight+1) + log.Println("Round:", c.currentRound.RoundNumber) + log.Println("State:", State(c.currentRound.LockedState).hash()) + */ + + // broadcast decide will return what it has sent + c.latestProof = c.broadcastDecide() + c.heightSync(c.latestHeight+1, c.currentRound.RoundNumber, c.currentRound.LockedState, now) + // leader should wait for 1 more latency + c.rcTimeout = now.Add(c.roundchangeDuration(0) + c.latency) + // broadcast at new height + c.broadcastRoundChange() + } + } + } + + case MessageType_Decide: + err := c.verifyDecideMessage(m, signed) + if err != nil { + return err + } + + // record this proof for chaining + c.latestProof = signed + + // propagate this message to my neighbour. + // NOTE: verifyDecideMessage() can stop broadcast storm. + c.propagate(bts) + // passive confirmation from the leader. + c.heightSync(m.Height, m.Round, m.State, now) + // non-leader starts waiting for rcTimeout + c.rcTimeout = now.Add(c.roundchangeDuration(0)) + // we sync our height and broadcast new . + c.broadcastRoundChange() + + case MessageType_Resync: + // push the proofs in loopback device + for k := range m.Proof { + // protobuf marshalling + out, err := proto.Marshal(m.Proof[k]) + if err != nil { + panic(err) + } + c.loopback = append(c.loopback, out) + } + + default: + return ErrMessageUnknownMessageType + } + return nil +} + +// Update will process timing event for the state machine, callers +// from outside MUST call this function periodically(like 20ms). +func (c *Consensus) Update(now time.Time) error { + // as in ReceiveMessage, we also need to handle broadcasting messages + // directed to myself. + defer func() { + for len(c.loopback) > 0 { + bts := c.loopback[0] + c.loopback = c.loopback[1:] + _ = c.receiveMessage(bts, now) + } + }() + + // stage switch + switch c.currentRound.Stage { + case stageRoundChanging: + if c.rcTimeout.IsZero() { + panic("roundchanging stage entered, but lockTimeout not set") + } + + if now.After(c.rcTimeout) { + c.broadcastRoundChange() + c.broadcastResync() // we also need to broadcast the round change event message if there is any + c.rcTimeout = now.Add(c.roundchangeDuration(c.currentRound.RoundNumber)) + } + case stageLock: + if c.lockTimeout.IsZero() { + panic("lock stage entered, but lockTimeout not set") + } + // leader's collection, we perform periodically check for or message to participants. + // enqueue all received non-NULL data + states := c.currentRound.RoundChangeStates() + for k := range states { + c.Propose(states[k]) + } + + // broadcast this related + ErrSelectStateValidation = errors.New("the state data validation failed message has another height than expected") + ErrSelectRoundLower = errors.New("the message is not signed by leader") + ErrSelectStateMismatch = errors.New("the message has unknown participant") + ErrSelectProofTypeMismatch = errors.New("the proofs in message has mismatched height") + ErrSelectProofRoundMismatch = errors.New("the proofs in message has invalid state data") + ErrSelectProofNotTheMaximal = errors.New("the proposed state is not the maximal one in the message has insufficient overall proofs") + ErrSelectProofExceeded = errors.New("the message + MessageType_Select MessageType = 3 + // MessageCommit = message + MessageType_Commit MessageType = 4 + // MessageLockRelease = message + MessageType_LockRelease MessageType = 5 + // MessageDecide = message + MessageType_Decide MessageType = 6 + // MessageResync= message + MessageType_Resync MessageType = 7 +) + +var MessageType_name = map[int32]string{ + 0: "Nop", + 1: "RoundChange", + 2: "Lock", + 3: "Select", + 4: "Commit", + 5: "LockRelease", + 6: "Decide", + 7: "Resync", +} + +var MessageType_value = map[string]int32{ + "Nop": 0, + "RoundChange": 1, + "Lock": 2, + "Select": 3, + "Commit": 4, + "LockRelease": 5, + "Decide": 6, + "Resync": 7, +} + +func (x MessageType) String() string { + return proto.EnumName(MessageType_name, int32(x)) +} + +func (MessageType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_33c57e4bae7b9afd, []int{0} +} + +// SignedProto defines a message with signature and it's publickey +type SignedProto struct { + Version uint32 `protobuf:"varint,1,opt,name=version,proto3" json:"version,omitempty"` + // the Message encoded raw protobuf in bytes + Message []byte `protobuf:"bytes,2,opt,name=Message,proto3" json:"Message,omitempty"` + // signer's public key + X PubKeyAxis `protobuf:"bytes,3,opt,name=x,proto3,customtype=PubKeyAxis" json:"x"` + Y PubKeyAxis `protobuf:"bytes,4,opt,name=y,proto3,customtype=PubKeyAxis" json:"y"` + // signature r,s for prefix+messages+version+x+y above + R []byte `protobuf:"bytes,5,opt,name=r,proto3" json:"r,omitempty"` + S []byte `protobuf:"bytes,6,opt,name=s,proto3" json:"s,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *SignedProto) Reset() { *m = SignedProto{} } +func (m *SignedProto) String() string { return proto.CompactTextString(m) } +func (*SignedProto) ProtoMessage() {} +func (*SignedProto) Descriptor() ([]byte, []int) { + return fileDescriptor_33c57e4bae7b9afd, []int{0} +} +func (m *SignedProto) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *SignedProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_SignedProto.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *SignedProto) XXX_Merge(src proto.Message) { + xxx_messageInfo_SignedProto.Merge(m, src) +} +func (m *SignedProto) XXX_Size() int { + return m.Size() +} +func (m *SignedProto) XXX_DiscardUnknown() { + xxx_messageInfo_SignedProto.DiscardUnknown(m) +} + +var xxx_messageInfo_SignedProto proto.InternalMessageInfo + +func (m *SignedProto) GetVersion() uint32 { + if m != nil { + return m.Version + } + return 0 +} + +func (m *SignedProto) GetMessage() []byte { + if m != nil { + return m.Message + } + return nil +} + +func (m *SignedProto) GetR() []byte { + if m != nil { + return m.R + } + return nil +} + +func (m *SignedProto) GetS() []byte { + if m != nil { + return m.S + } + return nil +} + +// Message defines a consensus message +type Message struct { + // Type of this message + Type MessageType `protobuf:"varint,1,opt,name=Type,proto3,enum=bdls.MessageType" json:"Type,omitempty"` + // Height in consensus + Height uint64 `protobuf:"varint,2,opt,name=Height,proto3" json:"Height,omitempty"` + // Round in consensus + Round uint64 `protobuf:"varint,3,opt,name=Round,proto3" json:"Round,omitempty"` + // Proposed state (optional) + State []byte `protobuf:"bytes,4,opt,name=State,proto3" json:"State,omitempty"` + // Proofs related + Proof []*SignedProto `protobuf:"bytes,5,rep,name=Proof,proto3" json:"Proof,omitempty"` + // for lock-release, it's an embeded message + LockRelease *SignedProto `protobuf:"bytes,6,opt,name=LockRelease,proto3" json:"LockRelease,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *Message) Reset() { *m = Message{} } +func (m *Message) String() string { return proto.CompactTextString(m) } +func (*Message) ProtoMessage() {} +func (*Message) Descriptor() ([]byte, []int) { + return fileDescriptor_33c57e4bae7b9afd, []int{1} +} +func (m *Message) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Message) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Message.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Message) XXX_Merge(src proto.Message) { + xxx_messageInfo_Message.Merge(m, src) +} +func (m *Message) XXX_Size() int { + return m.Size() +} +func (m *Message) XXX_DiscardUnknown() { + xxx_messageInfo_Message.DiscardUnknown(m) +} + +var xxx_messageInfo_Message proto.InternalMessageInfo + +func (m *Message) GetType() MessageType { + if m != nil { + return m.Type + } + return MessageType_Nop +} + +func (m *Message) GetHeight() uint64 { + if m != nil { + return m.Height + } + return 0 +} + +func (m *Message) GetRound() uint64 { + if m != nil { + return m.Round + } + return 0 +} + +func (m *Message) GetState() []byte { + if m != nil { + return m.State + } + return nil +} + +func (m *Message) GetProof() []*SignedProto { + if m != nil { + return m.Proof + } + return nil +} + +func (m *Message) GetLockRelease() *SignedProto { + if m != nil { + return m.LockRelease + } + return nil +} + +func init() { + proto.RegisterEnum("bdls.MessageType", MessageType_name, MessageType_value) + proto.RegisterType((*SignedProto)(nil), "bdls.SignedProto") + proto.RegisterType((*Message)(nil), "bdls.Message") +} + +func init() { proto.RegisterFile("message.proto", fileDescriptor_33c57e4bae7b9afd) } + +var fileDescriptor_33c57e4bae7b9afd = []byte{ + // 381 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xcd, 0xaa, 0xda, 0x40, + 0x14, 0xc7, 0x1d, 0xf3, 0x61, 0x39, 0xd1, 0x76, 0x3a, 0x94, 0x32, 0x74, 0xa1, 0x41, 0x28, 0x95, + 0x42, 0x23, 0xd4, 0x27, 0xa8, 0x76, 0x51, 0xe8, 0x07, 0x32, 0xf6, 0x05, 0xf2, 0x71, 0x8c, 0xa1, + 0x26, 0x23, 0x99, 0xa4, 0x98, 0xc7, 0xe9, 0xdb, 0xb8, 0x2c, 0x5d, 0x76, 0x21, 0x17, 0x9f, 0xe4, + 0x32, 0x13, 0xbd, 0x64, 0x71, 0xef, 0xee, 0xfc, 0xce, 0xff, 0x7f, 0xce, 0xf9, 0x27, 0x0c, 0x8c, + 0x72, 0x54, 0x2a, 0x4c, 0x31, 0x38, 0x94, 0xb2, 0x92, 0xcc, 0x8e, 0x92, 0xbd, 0x7a, 0xf3, 0x21, + 0xcd, 0xaa, 0x5d, 0x1d, 0x05, 0xb1, 0xcc, 0xe7, 0xa9, 0x4c, 0xe5, 0xdc, 0x88, 0x51, 0xbd, 0x35, + 0x64, 0xc0, 0x54, 0xed, 0xd0, 0xf4, 0x0f, 0x01, 0x6f, 0x93, 0xa5, 0x05, 0x26, 0x6b, 0xb3, 0x84, + 0xc3, 0xe0, 0x37, 0x96, 0x2a, 0x93, 0x05, 0x27, 0x3e, 0x99, 0x8d, 0xc4, 0x0d, 0xb5, 0xf2, 0xbd, + 0xbd, 0xc7, 0xfb, 0x3e, 0x99, 0x0d, 0xc5, 0x0d, 0x99, 0x0f, 0xe4, 0xc8, 0x2d, 0xdd, 0x5b, 0xb2, + 0xd3, 0x79, 0xd2, 0xfb, 0x7f, 0x9e, 0xc0, 0xba, 0x8e, 0xbe, 0x62, 0xf3, 0xe9, 0x98, 0x29, 0x41, + 0x8e, 0xda, 0xd1, 0x70, 0xfb, 0x69, 0x47, 0xc3, 0x86, 0x40, 0x4a, 0xee, 0x98, 0xbd, 0xa4, 0xd4, + 0xa4, 0xb8, 0xdb, 0x92, 0x9a, 0xfe, 0x23, 0x0f, 0xa7, 0xd9, 0x5b, 0xb0, 0x7f, 0x36, 0x07, 0x34, + 0xe1, 0x9e, 0x7f, 0x7c, 0x19, 0xe8, 0x6f, 0x0e, 0xae, 0xa2, 0x16, 0x84, 0x91, 0xd9, 0x6b, 0x70, + 0xbf, 0x60, 0x96, 0xee, 0x2a, 0x93, 0xd5, 0x16, 0x57, 0x62, 0xaf, 0xc0, 0x11, 0xb2, 0x2e, 0x12, + 0x13, 0xd7, 0x16, 0x2d, 0xe8, 0xee, 0xa6, 0x0a, 0x2b, 0x6c, 0x23, 0x8a, 0x16, 0xd8, 0x3b, 0x70, + 0xd6, 0xa5, 0x94, 0x5b, 0xee, 0xf8, 0xd6, 0xcc, 0xbb, 0xdd, 0xea, 0xfc, 0x2c, 0xd1, 0xea, 0x6c, + 0x01, 0xde, 0x37, 0x19, 0xff, 0x12, 0xb8, 0xc7, 0x50, 0xa1, 0xc9, 0xfd, 0xa8, 0xbd, 0xeb, 0x7a, + 0x5f, 0x82, 0xd7, 0x89, 0xcd, 0x06, 0x60, 0xfd, 0x90, 0x07, 0xda, 0x63, 0x2f, 0xc0, 0x33, 0xa1, + 0x56, 0xbb, 0xb0, 0x48, 0x91, 0x12, 0xf6, 0x0c, 0x6c, 0x3d, 0x47, 0xfb, 0x0c, 0xc0, 0xdd, 0xe0, + 0x1e, 0xe3, 0x8a, 0x5a, 0xba, 0x5e, 0xc9, 0x3c, 0xcf, 0x2a, 0x6a, 0xeb, 0x91, 0xce, 0x66, 0xea, + 0x68, 0xf1, 0x33, 0xc6, 0x59, 0x82, 0xd4, 0xd5, 0xb5, 0x40, 0xd5, 0x14, 0x31, 0x1d, 0x2c, 0x87, + 0xa7, 0xcb, 0x98, 0xfc, 0xbd, 0x8c, 0xc9, 0xdd, 0x65, 0x4c, 0x22, 0xd7, 0xbc, 0x80, 0xc5, 0x7d, + 0x00, 0x00, 0x00, 0xff, 0xff, 0x43, 0xc3, 0x64, 0x6c, 0x47, 0x02, 0x00, 0x00, +} + +func (m *SignedProto) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *SignedProto) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *SignedProto) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if len(m.S) > 0 { + i -= len(m.S) + copy(dAtA[i:], m.S) + i = encodeVarintMessage(dAtA, i, uint64(len(m.S))) + i-- + dAtA[i] = 0x32 + } + if len(m.R) > 0 { + i -= len(m.R) + copy(dAtA[i:], m.R) + i = encodeVarintMessage(dAtA, i, uint64(len(m.R))) + i-- + dAtA[i] = 0x2a + } + { + size := m.Y.Size() + i -= size + if _, err := m.Y.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintMessage(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + { + size := m.X.Size() + i -= size + if _, err := m.X.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintMessage(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if len(m.Message) > 0 { + i -= len(m.Message) + copy(dAtA[i:], m.Message) + i = encodeVarintMessage(dAtA, i, uint64(len(m.Message))) + i-- + dAtA[i] = 0x12 + } + if m.Version != 0 { + i = encodeVarintMessage(dAtA, i, uint64(m.Version)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *Message) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Message) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Message) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.XXX_unrecognized != nil { + i -= len(m.XXX_unrecognized) + copy(dAtA[i:], m.XXX_unrecognized) + } + if m.LockRelease != nil { + { + size, err := m.LockRelease.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMessage(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } + if len(m.Proof) > 0 { + for iNdEx := len(m.Proof) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Proof[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintMessage(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } + if len(m.State) > 0 { + i -= len(m.State) + copy(dAtA[i:], m.State) + i = encodeVarintMessage(dAtA, i, uint64(len(m.State))) + i-- + dAtA[i] = 0x22 + } + if m.Round != 0 { + i = encodeVarintMessage(dAtA, i, uint64(m.Round)) + i-- + dAtA[i] = 0x18 + } + if m.Height != 0 { + i = encodeVarintMessage(dAtA, i, uint64(m.Height)) + i-- + dAtA[i] = 0x10 + } + if m.Type != 0 { + i = encodeVarintMessage(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintMessage(dAtA []byte, offset int, v uint64) int { + offset -= sovMessage(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *SignedProto) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Version != 0 { + n += 1 + sovMessage(uint64(m.Version)) + } + l = len(m.Message) + if l > 0 { + n += 1 + l + sovMessage(uint64(l)) + } + l = m.X.Size() + n += 1 + l + sovMessage(uint64(l)) + l = m.Y.Size() + n += 1 + l + sovMessage(uint64(l)) + l = len(m.R) + if l > 0 { + n += 1 + l + sovMessage(uint64(l)) + } + l = len(m.S) + if l > 0 { + n += 1 + l + sovMessage(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func (m *Message) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Type != 0 { + n += 1 + sovMessage(uint64(m.Type)) + } + if m.Height != 0 { + n += 1 + sovMessage(uint64(m.Height)) + } + if m.Round != 0 { + n += 1 + sovMessage(uint64(m.Round)) + } + l = len(m.State) + if l > 0 { + n += 1 + l + sovMessage(uint64(l)) + } + if len(m.Proof) > 0 { + for _, e := range m.Proof { + l = e.Size() + n += 1 + l + sovMessage(uint64(l)) + } + } + if m.LockRelease != nil { + l = m.LockRelease.Size() + n += 1 + l + sovMessage(uint64(l)) + } + if m.XXX_unrecognized != nil { + n += len(m.XXX_unrecognized) + } + return n +} + +func sovMessage(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozMessage(x uint64) (n int) { + return sovMessage(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *SignedProto) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: SignedProto: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: SignedProto: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Version", wireType) + } + m.Version = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Version |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMessage + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMessage + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Message = append(m.Message[:0], dAtA[iNdEx:postIndex]...) + if m.Message == nil { + m.Message = []byte{} + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field X", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMessage + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMessage + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.X.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Y", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMessage + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMessage + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Y.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field R", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMessage + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMessage + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.R = append(m.R[:0], dAtA[iNdEx:postIndex]...) + if m.R == nil { + m.R = []byte{} + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field S", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMessage + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMessage + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.S = append(m.S[:0], dAtA[iNdEx:postIndex]...) + if m.S == nil { + m.S = []byte{} + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMessage(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMessage + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthMessage + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Message) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Message: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Message: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Type |= MessageType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Height", wireType) + } + m.Height = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Height |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Round", wireType) + } + m.Round = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Round |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field State", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthMessage + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthMessage + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.State = append(m.State[:0], dAtA[iNdEx:postIndex]...) + if m.State == nil { + m.State = []byte{} + } + iNdEx = postIndex + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Proof", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMessage + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMessage + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Proof = append(m.Proof, &SignedProto{}) + if err := m.Proof[len(m.Proof)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field LockRelease", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowMessage + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthMessage + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthMessage + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.LockRelease == nil { + m.LockRelease = &SignedProto{} + } + if err := m.LockRelease.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipMessage(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthMessage + } + if (iNdEx + skippy) < 0 { + return ErrInvalidLengthMessage + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...) + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipMessage(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMessage + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMessage + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowMessage + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthMessage + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupMessage + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthMessage + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthMessage = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowMessage = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupMessage = fmt.Errorf("proto: unexpected end of group") +) diff --git a/vendor/github.com/BDLS-bft/bdls/message.proto b/vendor/github.com/BDLS-bft/bdls/message.proto new file mode 100644 index 00000000000..fa65dcd248e --- /dev/null +++ b/vendor/github.com/BDLS-bft/bdls/message.proto @@ -0,0 +1,54 @@ + +syntax = "proto3"; +package bdls; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +// SignedProto defines a message with signature and it's publickey +message SignedProto { + uint32 version=1; + // the Message encoded raw protobuf in bytes + bytes Message=2; + // signer's public key + bytes x = 3 [(gogoproto.customtype) = "PubKeyAxis", (gogoproto.nullable) = false]; + bytes y = 4 [(gogoproto.customtype) = "PubKeyAxis", (gogoproto.nullable) = false]; + // signature r,s for prefix+messages+version+x+y above + bytes r = 5; + bytes s = 6; +} + +// MessageType defines supported message types +enum MessageType{ + // No operation, for default message type, and keepalive connection + Nop = 0; + // MessageRoundChange = message + RoundChange = 1; + // MessageLock = message + Lock = 2; + // MessageSelect =