From 6994d020fcebdef4ebdcaa8bcc112800ad856543 Mon Sep 17 00:00:00 2001 From: TAKAMI Torao Date: Fri, 3 Jul 2020 23:38:48 +0900 Subject: [PATCH] introduced a composite-key that delegates processing to each key-function --- .circleci/config.yml | 4 +- .github/workflows/tests.yml | 8 +- CHANGELOG_PENDING.md | 3 +- Makefile | 35 ++- abci/example/kvstore/helpers.go | 7 +- abci/example/kvstore/persistent_kvstore.go | 12 +- abci/tests/server/client.go | 8 +- abci/types/pubkey.go | 8 +- cmd/contract_tests/main.go | 4 +- consensus/common_test.go | 5 +- crypto/bls/bls.go | 25 +- crypto/composite/composite.go | 94 +++++++ crypto/composite/composite_test.go | 172 +++++++++++++ crypto/crypto.go | 7 + crypto/ed25519/ed25519.go | 28 ++ crypto/encoding/amino/amino.go | 17 ++ crypto/encoding/amino/encode_test.go | 12 +- crypto/encoding/codec.go | 48 ++++ crypto/encoding/codec_test.go | 31 +++ crypto/multisig/threshold_pubkey.go | 7 + crypto/secp256k1/secp256k1.go | 10 + crypto/sr25519/privkey.go | 5 + crypto/sr25519/pubkey.go | 5 + crypto/vrf/vrf.go | 16 +- crypto/vrf/vrf_coniks.go | 6 +- crypto/vrf/vrf_coniks_test.go | 21 +- crypto/vrf/vrf_libsodium.go | 7 +- crypto/vrf/vrf_r2ishiguro.go | 11 +- crypto/vrf/vrf_test.go | 25 +- go.mod | 1 + go.sum | 6 + lite/helpers.go | 4 +- lite2/client_test.go | 5 +- lite2/helpers_test.go | 13 +- networks/local/localnode/Dockerfile | 8 +- networks/local/localnode/wrapper.sh | 3 + p2p/conn/secret_connection_test.go | 9 +- p2p/key.go | 10 +- privval/file.go | 16 +- privval/file_test.go | 14 +- privval/messages.go | 3 +- privval/retry_signer_client.go | 5 +- privval/signer_client.go | 3 +- privval/signer_client_test.go | 12 +- proto/crypto/keys/types.pb.go | 285 ++++++++++++++++++++- proto/crypto/keys/types.proto | 11 + state/execution.go | 4 +- state/state.go | 4 +- state/state_test.go | 4 +- state/tx_filter_test.go | 2 +- state/validation.go | 8 +- types/block.go | 7 +- types/block_test.go | 15 +- types/evidence.go | 2 +- types/params.go | 4 +- types/priv_validator.go | 18 +- types/proposal.go | 2 +- types/protobuf.go | 31 ++- types/protobuf_test.go | 14 +- types/signable.go | 3 +- types/vote.go | 4 +- types/voter_set_test.go | 7 +- 62 files changed, 984 insertions(+), 194 deletions(-) create mode 100644 crypto/composite/composite.go create mode 100644 crypto/composite/composite_test.go create mode 100644 crypto/encoding/codec_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index aaf339f98..497bb1856 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -155,7 +155,7 @@ jobs: name: run localnet and exit on failure command: | set -x - docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang:1.14.1-alpine /bin/sh -c "apk add --update git make gcc libc-dev build-base && make build" + make build-linux make localnet-start & ./scripts/localnet-blocks-test.sh 40 5 10 localhost @@ -370,7 +370,7 @@ jobs: ./scripts/get_nodejs.sh # build the binaries with a proper version of Go # Build Tendermint - docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang:1.14.1-alpine /bin/sh -c "apk add --update git make gcc libc-dev build-base && make build" + make build-linux # Build contract-tests docker run --rm -v "$PWD":/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint ubuntu:20.10 ./scripts/prepare_dredd_test.sh # This docker image works with go 1.7, we can install here the hook handler that contract-tests is going to use diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 75ca8b4a0..ffd6d1950 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,7 +29,7 @@ jobs: - uses: actions/cache@v1 with: path: ~/go/bin - key: ${{ runner.os }}-go-tm-binary + key: ${{ runner.os }}-go-tm-binary-${{ hashFiles('**/*.go', '**/go.sum', 'Makefile') }} test_abci_apps: runs-on: ubuntu-latest @@ -43,7 +43,7 @@ jobs: - uses: actions/cache@v1 with: path: ~/go/bin - key: ${{ runner.os }}-go-tm-binary + key: ${{ runner.os }}-go-tm-binary-${{ hashFiles('**/*.go', '**/go.sum', 'Makefile') }} - name: test_abci_apps run: abci/tests/test_app/test.sh shell: bash @@ -60,7 +60,7 @@ jobs: - uses: actions/cache@v1 with: path: ~/go/bin - key: ${{ runner.os }}-go-tm-binary + key: ${{ runner.os }}-go-tm-binary-${{ hashFiles('**/*.go', '**/go.sum', 'Makefile') }} - run: abci/tests/test_cli/test.sh shell: bash @@ -76,7 +76,7 @@ jobs: - uses: actions/cache@v1 with: path: ~/go/bin - key: ${{ runner.os }}-go-tm-binary + key: ${{ runner.os }}-go-tm-binary-${{ hashFiles('**/*.go', '**/go.sum', 'Makefile') }} - name: test_apps run: test/app/test.sh shell: bash diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md index 83014a498..561d83ff5 100644 --- a/CHANGELOG_PENDING.md +++ b/CHANGELOG_PENDING.md @@ -11,10 +11,11 @@ - Apps - P2P Protocol - + - Go API - Blockchain Protocol +- [consensus] [\#101](https://github.com/line/tendermint/pull/101) Introduce composite key to delegate features to each function key ### FEATURES: diff --git a/Makefile b/Makefile index c6eb94c09..d412c6b83 100644 --- a/Makefile +++ b/Makefile @@ -226,9 +226,42 @@ build-docker: ### Local testnet using docker ### ############################################################################### +DOCKER_HOME = /go/src/github.com/tendermint/tendermint +DOCKER_CMD = docker run --rm \ + -v `pwd`:$(DOCKER_HOME) \ + -w $(DOCKER_HOME) +DOCKER_IMG = golang:1.14.6-alpine3.12 +APK_CMD = apk add --update --no-cache git make gcc libc-dev build-base curl jq file gmp-dev clang +APK_CMD += && cd crypto/bls/internal/bls-eth-go-binary +APK_CMD += && make CXX=clang++ +APK_CMD += && cd $(DOCKER_HOME) +APK_CMD += && go mod edit -replace github.com/herumi/bls-eth-go-binary=./crypto/bls/internal/bls-eth-go-binary +APK_CMD += && make build +APK_CMD += && go mod edit -dropreplace github.com/herumi/bls-eth-go-binary + +# Login docker-container for confirmation building linux binary +build-shell: + $(DOCKER_CMD) -it --entrypoint '' ${DOCKER_IMG} /bin/sh +.PHONY: build-shell + # Build linux binary on other platforms + build-linux: - docker run --rm -v `pwd`:/go/src/github.com/tendermint/tendermint -w /go/src/github.com/tendermint/tendermint golang:1.14.1-alpine /bin/sh -c "apk add --update git make gcc libc-dev build-base && make build" + # Download, build and add the BSL local library to modules + if [ ! -d $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary ]; then \ + mkdir -p $(SRCPATH)/crypto/bls/internal && \ + git clone https://github.com/herumi/mcl $(SRCPATH)/crypto/bls/internal/mcl && \ + git clone https://github.com/herumi/bls $(SRCPATH)/crypto/bls/internal/bls && \ + git clone https://github.com/herumi/bls-eth-go-binary $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary; \ + fi + + # Build Linux binary + $(DOCKER_CMD) ${DOCKER_IMG} /bin/sh -c "$(APK_CMD)" + + # Remove the BLS local library from modules + rm -rf $(SRCPATH)/crypto/bls/internal/mcl + rm -rf $(SRCPATH)/crypto/bls/internal/bls + rm -rf $(SRCPATH)/crypto/bls/internal/bls-eth-go-binary .PHONY: build-linux build-docker-localnode: diff --git a/abci/example/kvstore/helpers.go b/abci/example/kvstore/helpers.go index d1334b312..81be6c97f 100644 --- a/abci/example/kvstore/helpers.go +++ b/abci/example/kvstore/helpers.go @@ -2,15 +2,18 @@ package kvstore import ( "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/composite" tmrand "github.com/tendermint/tendermint/libs/rand" + tmtypes "github.com/tendermint/tendermint/types" ) // RandVal creates one random validator, with a key derived // from the input value func RandVal(i int) types.ValidatorUpdate { - pubkey := tmrand.Bytes(32) + pk := composite.GenPrivKey().PubKey() + pubkey := tmtypes.TM2PB.PubKey(pk) power := tmrand.Uint16() + 1 - v := types.Ed25519ValidatorUpdate(pubkey, int64(power)) + v := types.NewValidatorUpdate(tmtypes.ABCIPubKeyTypeComposite, pubkey.Data, int64(power)) return v } diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index fffc617be..d9dae5e99 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -11,7 +11,6 @@ import ( "github.com/tendermint/tendermint/abci/example/code" "github.com/tendermint/tendermint/abci/types" - "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/libs/log" tmtypes "github.com/tendermint/tendermint/types" ) @@ -205,15 +204,20 @@ func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.Respon } // update - return app.updateValidator(types.Ed25519ValidatorUpdate(pubkey, power)) + // TODO The type of public key to be restored should be kept its original type. + return app.updateValidator(types.NewValidatorUpdate(tmtypes.ABCIPubKeyTypeComposite, pubkey, power)) } // add, update, or remove a validator func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate) types.ResponseDeliverTx { key := []byte("val:" + string(v.PubKey.Data)) - pubkey := ed25519.PubKeyEd25519{} - copy(pubkey[:], v.PubKey.Data) + pubkey, err := tmtypes.PB2TM.PubKey(v.PubKey) + if err != nil { + return types.ResponseDeliverTx{ + Code: code.CodeTypeEncodingError, + Log: fmt.Sprintf("Error encoding validator: %v", err)} + } if v.Power == 0 { // remove validator diff --git a/abci/tests/server/client.go b/abci/tests/server/client.go index 36989f6ac..acb16e44d 100644 --- a/abci/tests/server/client.go +++ b/abci/tests/server/client.go @@ -5,6 +5,9 @@ import ( "errors" "fmt" + "github.com/tendermint/tendermint/crypto/composite" + tmtypes "github.com/tendermint/tendermint/types" + abcicli "github.com/tendermint/tendermint/abci/client" "github.com/tendermint/tendermint/abci/types" tmrand "github.com/tendermint/tendermint/libs/rand" @@ -14,9 +17,10 @@ func InitChain(client abcicli.Client) error { total := 10 vals := make([]types.ValidatorUpdate, total) for i := 0; i < total; i++ { - pubkey := tmrand.Bytes(33) + pk := composite.GenPrivKey().PubKey() + pubkey := tmtypes.TM2PB.PubKey(pk).Data power := tmrand.Int() - vals[i] = types.Ed25519ValidatorUpdate(pubkey, int64(power)) + vals[i] = types.NewValidatorUpdate("composite", pubkey, int64(power)) } _, err := client.InitChainSync(types.RequestInitChain{ Validators: vals, diff --git a/abci/types/pubkey.go b/abci/types/pubkey.go index 46cd8c5e8..483ce0267 100644 --- a/abci/types/pubkey.go +++ b/abci/types/pubkey.go @@ -4,13 +4,17 @@ const ( PubKeyEd25519 = "ed25519" ) -func Ed25519ValidatorUpdate(pubkey []byte, power int64) ValidatorUpdate { +func NewValidatorUpdate(keyType string, pubkey []byte, power int64) ValidatorUpdate { return ValidatorUpdate{ // Address: PubKey: PubKey{ - Type: PubKeyEd25519, + Type: keyType, Data: pubkey, }, Power: power, } } + +func Ed25519ValidatorUpdate(pubkey []byte, power int64) ValidatorUpdate { + return NewValidatorUpdate(PubKeyEd25519, pubkey, power) +} diff --git a/cmd/contract_tests/main.go b/cmd/contract_tests/main.go index d3a17b18c..41cf0f7f2 100644 --- a/cmd/contract_tests/main.go +++ b/cmd/contract_tests/main.go @@ -5,10 +5,10 @@ import ( "fmt" "strings" + "github.com/tendermint/tendermint/cmd/contract_tests/unmarshaler" + "github.com/snikch/goodman/hooks" "github.com/snikch/goodman/transaction" - - "github.com/tendermint/tendermint/cmd/contract_tests/unmarshaler" ) func main() { diff --git a/consensus/common_test.go b/consensus/common_test.go index 499d03bd4..05e488b56 100644 --- a/consensus/common_test.go +++ b/consensus/common_test.go @@ -19,6 +19,7 @@ import ( "github.com/go-kit/kit/log/term" "github.com/pkg/errors" "github.com/stretchr/testify/require" + dbm "github.com/tendermint/tm-db" abcicli "github.com/tendermint/tendermint/abci/client" @@ -27,7 +28,6 @@ import ( abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" cstypes "github.com/tendermint/tendermint/consensus/types" - "github.com/tendermint/tendermint/crypto/vrf" tmbytes "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" tmos "github.com/tendermint/tendermint/libs/os" @@ -495,7 +495,8 @@ func forceProposer(cs *State, vals []*validatorStub, index []int, height []int64 if j+1 < len(height) && height[j+1] > height[j] { message := types.MakeRoundHash(currentHash, height[j]-1, round[j]) proof, _ := curVal.PrivValidator.GenerateVRFProof(message) - currentHash, _ = vrf.ProofToHash(proof) + pubKey, _ := curVal.PrivValidator.GetPubKey() + currentHash, _ = pubKey.VRFVerify(proof, message) } } if allMatch { diff --git a/crypto/bls/bls.go b/crypto/bls/bls.go index bcd51f3b7..4744ef947 100644 --- a/crypto/bls/bls.go +++ b/crypto/bls/bls.go @@ -51,7 +51,11 @@ func GenPrivKey() PrivKeyBLS12 { sigKey := bls.SecretKey{} sigKey.SetByCSPRNG() sigKeyBinary := PrivKeyBLS12{} - copy(sigKeyBinary[:], sigKey.Serialize()) + binary := sigKey.Serialize() + if len(binary) != PrivKeyBLS12Size { + panic(fmt.Sprintf("unexpected BLS private key size: %d != %d", len(binary), PrivKeyBLS12Size)) + } + copy(sigKeyBinary[:], binary) return sigKeyBinary } @@ -62,6 +66,9 @@ func (privKey PrivKeyBLS12) Bytes() []byte { // Sign produces a signature on the provided message. func (privKey PrivKeyBLS12) Sign(msg []byte) ([]byte, error) { + if msg == nil { + panic(fmt.Sprintf("Nil specified as the message")) + } blsKey := bls.SecretKey{} err := blsKey.Deserialize(privKey[:]) if err != nil { @@ -71,6 +78,11 @@ func (privKey PrivKeyBLS12) Sign(msg []byte) ([]byte, error) { return sign.Serialize(), nil } +// VRFProve is not supported in BLS12. +func (privKey PrivKeyBLS12) VRFProve(seed []byte) (crypto.Proof, error) { + return nil, fmt.Errorf("VRF prove is not supported by the BLS12") +} + // PubKey gets the corresponding public key from the private key. func (privKey PrivKeyBLS12) PubKey() crypto.PubKey { blsKey := bls.SecretKey{} @@ -80,7 +92,11 @@ func (privKey PrivKeyBLS12) PubKey() crypto.PubKey { } pubKey := blsKey.GetPublicKey() pubKeyBinary := PubKeyBLS12{} - copy(pubKeyBinary[:], pubKey.Serialize()) + binary := pubKey.Serialize() + if len(binary) != PubKeyBLS12Size { + panic(fmt.Sprintf("unexpected BLS public key size: %d != %d", len(binary), PubKeyBLS12Size)) + } + copy(pubKeyBinary[:], binary) return pubKeyBinary } @@ -130,6 +146,11 @@ func (pubKey PubKeyBLS12) VerifyBytes(msg []byte, sig []byte) bool { return blsSign.VerifyByte(&blsPubKey, msg) } +// VRFVerify is not supported in BLS12. +func (pubKey PubKeyBLS12) VRFVerify(proof crypto.Proof, seed []byte) (crypto.Output, error) { + return nil, fmt.Errorf("VRF verify is not supported by the BLS12") +} + func (pubKey PubKeyBLS12) String() string { return fmt.Sprintf("PubKeyBLS12{%X}", pubKey[:]) } diff --git a/crypto/composite/composite.go b/crypto/composite/composite.go new file mode 100644 index 000000000..fbc792cbf --- /dev/null +++ b/crypto/composite/composite.go @@ -0,0 +1,94 @@ +package composite + +import ( + "bytes" + + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/ed25519" + "github.com/tendermint/tendermint/crypto/tmhash" + + "github.com/tendermint/tendermint/crypto" +) + +// PubKeyComposite and PrivKeyComposite are intended to allow public key algorithms to be selected for each function. + +const ( + PubKeyCompositeAminoName = "tendermint/PubKeyComposite" + PrivKeyCompositeAminoName = "tendermint/PrivKeyComposite" +) + +type PubKeyComposite struct { + SignKey crypto.PubKey `json:"sign"` + VrfKey crypto.PubKey `json:"vrf"` +} + +func (pk *PubKeyComposite) Identity() crypto.PubKey { + return pk.VrfKey +} + +func (pk PubKeyComposite) Address() crypto.Address { + return crypto.Address(tmhash.SumTruncated(pk.Bytes())) +} + +func (pk PubKeyComposite) Bytes() []byte { + msg := bytes.NewBuffer(pk.SignKey.Bytes()) + msg.Write(pk.VrfKey.Bytes()) + return msg.Bytes() +} + +func (pk PubKeyComposite) VerifyBytes(msg []byte, sig []byte) bool { + return pk.SignKey.VerifyBytes(msg, sig) +} + +// VRFVerify verifies that the given VRF Proof was generated from the seed by the owner of this public key. +func (pk PubKeyComposite) VRFVerify(proof crypto.Proof, seed []byte) (crypto.Output, error) { + return pk.VrfKey.VRFVerify(proof, seed) +} + +func (pk PubKeyComposite) Equals(key crypto.PubKey) bool { + other, ok := key.(PubKeyComposite) + return ok && pk.SignKey.Equals(other.SignKey) && pk.VrfKey.Equals(other.VrfKey) +} + +type PrivKeyComposite struct { + SignKey crypto.PrivKey `json:"sign"` + VrfKey crypto.PrivKey `json:"vrf"` +} + +func GenPrivKey() *PrivKeyComposite { + return NewPrivKeyComposite(bls.GenPrivKey(), ed25519.GenPrivKey()) +} + +func NewPrivKeyComposite(sign crypto.PrivKey, vrf crypto.PrivKey) *PrivKeyComposite { + return &PrivKeyComposite{SignKey: sign, VrfKey: vrf} +} + +func (sk PrivKeyComposite) Identity() crypto.PrivKey { + return sk.VrfKey +} + +func (sk PrivKeyComposite) Bytes() []byte { + return sk.Identity().Bytes() +} + +func (sk PrivKeyComposite) Sign(msg []byte) ([]byte, error) { + return sk.SignKey.Sign(msg) +} + +// VRFProve generates a VRF Proof for given seed to generate a verifiable random. +func (sk PrivKeyComposite) VRFProve(seed []byte) (crypto.Proof, error) { + return sk.VrfKey.VRFProve(seed) +} + +func (sk PrivKeyComposite) PubKey() crypto.PubKey { + return PubKeyComposite{sk.SignKey.PubKey(), sk.VrfKey.PubKey()} +} + +func (sk PrivKeyComposite) Equals(key crypto.PrivKey) bool { + switch other := key.(type) { + case *PrivKeyComposite: + return sk.SignKey.Equals(other.SignKey) && sk.VrfKey.Equals(other.VrfKey) + default: + return false + } +} diff --git a/crypto/composite/composite_test.go b/crypto/composite/composite_test.go new file mode 100644 index 000000000..c85327b03 --- /dev/null +++ b/crypto/composite/composite_test.go @@ -0,0 +1,172 @@ +package composite_test + +import ( + "bytes" + "testing" + + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/composite" + "github.com/tendermint/tendermint/crypto/ed25519" +) + +func TestPrivKeyComposite_Bytes(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk := composite.NewPrivKeyComposite(sign, vrf) + sk.Bytes() +} + +func TestPrivKeyComposite_Equals(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk1 := composite.NewPrivKeyComposite(sign, vrf) + if !sk1.Equals(sk1) { + t.Errorf("%v", sk1.SignKey.Equals(sk1.SignKey)) + t.Errorf("%v", sk1.VrfKey.Equals(sk1.VrfKey)) + t.Errorf("Identical key is evaluated as different: %v != %v", sk1, sk1) + } + sk2 := composite.NewPrivKeyComposite(sign, vrf) + if !sk1.Equals(sk2) || !sk2.Equals(sk1) { + t.Errorf("The same keys are evaluated as different") + } + sk3 := composite.NewPrivKeyComposite(ed25519.GenPrivKey(), bls.GenPrivKey()) + if sk1.Equals(sk3) || sk3.Equals(sk1) { + t.Errorf("The different keys are evaluated as the same") + } + if sk1.Equals(sign) || sk1.Equals(vrf) { + t.Errorf("The different kind of keys are evaluated as the same") + } +} + +func TestPrivKeyComposite_Identity(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk := composite.NewPrivKeyComposite(sign, vrf) + if sk.Identity() != vrf { + t.Errorf("The identity key is not a vrf key") + } +} + +func TestPrivKeyComposite_PubKey(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk := composite.NewPrivKeyComposite(sign, vrf) + sk.PubKey() +} + +func TestPrivKeyComposite_Sign(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk := composite.NewPrivKeyComposite(sign, vrf) + msg := []byte("hello, world") + s1, err := sk.Sign(msg) + if err != nil { + t.Errorf("Fail to generate signature.") + } + s2, _ := sign.Sign(msg) + if !bytes.Equal(s1, s2) { + t.Errorf("The signature is not generated by sign key") + } +} + +func TestPrivKeyComposite_VRFProve(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk := composite.NewPrivKeyComposite(sign, vrf) + msg := []byte("hello, world") + p, err := sk.VRFProve(msg) + if err != nil { + t.Errorf("Fail to generate proof.") + } + _, err = sk.PubKey().VRFVerify(p, msg) + if err != nil { + t.Errorf("Fail to verify the vrf proof.") + } +} + +func TestPubKeyComposite_Address(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk := composite.NewPrivKeyComposite(sign, vrf) + pk := sk.PubKey().(composite.PubKeyComposite) + // check the byte-size is the same + if len(pk.Address().Bytes()) != len(vrf.PubKey().Address().Bytes()) { + t.Errorf("The address length is not compatible") + } +} + +func TestPubKeyComposite_Bytes(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk := composite.NewPrivKeyComposite(sign, vrf) + pk := sk.PubKey().(composite.PubKeyComposite) + pk.Bytes() +} + +func TestPubKeyComposite_Equals(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk := composite.NewPrivKeyComposite(sign, vrf) + pk1 := sk.PubKey().(composite.PubKeyComposite) + if !pk1.Equals(pk1) { + t.Errorf("Identical key is evaluated as different") + } + pk2 := sk.PubKey().(composite.PubKeyComposite) + if !pk1.Equals(pk2) || !pk2.Equals(pk1) { + t.Errorf("The same keys are evaluated as different") + } + sk3 := composite.NewPrivKeyComposite(ed25519.GenPrivKey(), bls.GenPrivKey()) + pk3 := sk3.PubKey().(composite.PubKeyComposite) + if pk1.Equals(pk3) || pk3.Equals(pk1) { + t.Errorf("The different keys are evaluated as the same") + } + if pk1.Equals(sign.PubKey()) || pk1.Equals(vrf.PubKey()) { + t.Errorf("The different kind of keys are evaluated as the same") + } +} + +func TestPubKeyComposite_Identity(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk := composite.NewPrivKeyComposite(sign, vrf) + pk := sk.PubKey().(composite.PubKeyComposite) + pk.Identity() +} + +func TestPubKeyComposite_VerifyBytes(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk := composite.NewPrivKeyComposite(sign, vrf) + pk := sk.PubKey().(composite.PubKeyComposite) + msg := []byte("hello, world") + s, err := sk.Sign(msg) + if err != nil { + t.Errorf("Fail to generate signature.") + } + if !pk.VerifyBytes(msg, s) { + t.Errorf("Fail to verify signature.") + } + if pk.VerifyBytes([]byte("brown fox"), s) { + t.Errorf("Signature validation for the different messages is successful.") + } +} + +func TestPubKeyComposite_VRFVerify(t *testing.T) { + sign := bls.GenPrivKey() + vrf := ed25519.GenPrivKey() + sk := composite.NewPrivKeyComposite(sign, vrf) + pk := sk.PubKey().(composite.PubKeyComposite) + msg := []byte("hello, world") + proof, err := sk.VRFProve(msg) + if err != nil { + t.Errorf("Fail to generage vrf proof.") + } + output1, err := pk.VRFVerify(proof, msg) + if err != nil { + t.Errorf("Fail to verify vrf proof.") + } + output2, _ := vrf.PubKey().VRFVerify(proof, msg) + if !bytes.Equal(output1, output2) { + t.Errorf("Output is different from the VRF key.") + } +} diff --git a/crypto/crypto.go b/crypto/crypto.go index 045a35e86..c7ae210f4 100644 --- a/crypto/crypto.go +++ b/crypto/crypto.go @@ -19,16 +19,23 @@ func AddressHash(bz []byte) Address { return Address(tmhash.SumTruncated(bz)) } +// Proof represents the VRF Proof. +// It should be defined separately from Ed25519 VRF Proof to avoid circular import. +type Proof []byte +type Output []byte + type PubKey interface { Address() Address Bytes() []byte VerifyBytes(msg []byte, sig []byte) bool + VRFVerify(proof Proof, seed []byte) (Output, error) Equals(PubKey) bool } type PrivKey interface { Bytes() []byte Sign(msg []byte) ([]byte, error) + VRFProve(seed []byte) (Proof, error) PubKey() PubKey Equals(PrivKey) bool } diff --git a/crypto/ed25519/ed25519.go b/crypto/ed25519/ed25519.go index 1ce6f7b11..2d1ac6914 100644 --- a/crypto/ed25519/ed25519.go +++ b/crypto/ed25519/ed25519.go @@ -6,6 +6,8 @@ import ( "fmt" "io" + "github.com/tendermint/tendermint/crypto/vrf" + amino "github.com/tendermint/go-amino" "golang.org/x/crypto/ed25519" @@ -57,6 +59,16 @@ func (privKey PrivKeyEd25519) Sign(msg []byte) ([]byte, error) { return signatureBytes, nil } +// VRFProve generates a VRF Proof for given seed to generate a verifiable random. +func (privKey PrivKeyEd25519) VRFProve(seed []byte) (crypto.Proof, error) { + pubKey := privKey.PubKey().(PubKeyEd25519) + proof, err := vrf.Prove(privKey[:], pubKey[:], seed) + if err != nil { + return nil, err + } + return crypto.Proof(proof[:]), nil +} + // PubKey gets the corresponding public key from the private key. func (privKey PrivKeyEd25519) PubKey() crypto.PubKey { privKeyBytes := [64]byte(privKey) @@ -156,6 +168,22 @@ func (pubKey PubKeyEd25519) VerifyBytes(msg []byte, sig []byte) bool { return ed25519.Verify(pubKey[:], msg, sig) } +// VRFVerify verifies that the given VRF Proof was generated from the seed by the owner of this public key. +func (pubKey PubKeyEd25519) VRFVerify(proof crypto.Proof, seed []byte) (crypto.Output, error) { + valid, err := vrf.Verify(pubKey[:], vrf.Proof(proof), seed) + if err != nil { + return nil, fmt.Errorf("the specified proof is not a valid ed25519 proof: %v", proof) + } + if !valid { + return nil, fmt.Errorf("the specified Proof is not generated with this pair-key: %v", proof) + } + output, err := vrf.ProofToHash(vrf.Proof(proof)) + if err != nil { + return nil, err + } + return crypto.Output(output), nil +} + func (pubKey PubKeyEd25519) String() string { return fmt.Sprintf("PubKeyEd25519{%X}", pubKey[:]) } diff --git a/crypto/encoding/amino/amino.go b/crypto/encoding/amino/amino.go index f7a2dde77..028edc08b 100644 --- a/crypto/encoding/amino/amino.go +++ b/crypto/encoding/amino/amino.go @@ -6,6 +6,8 @@ import ( amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/composite" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/multisig" "github.com/tendermint/tendermint/crypto/secp256k1" @@ -31,10 +33,12 @@ func init() { // TODO: Have amino provide a way to go from concrete struct to route directly. // Its currently a private API + nameTable[reflect.TypeOf(bls.PubKeyBLS12{})] = bls.PubKeyAminoName nameTable[reflect.TypeOf(ed25519.PubKeyEd25519{})] = ed25519.PubKeyAminoName nameTable[reflect.TypeOf(sr25519.PubKeySr25519{})] = sr25519.PubKeyAminoName nameTable[reflect.TypeOf(secp256k1.PubKeySecp256k1{})] = secp256k1.PubKeyAminoName nameTable[reflect.TypeOf(multisig.PubKeyMultisigThreshold{})] = multisig.PubKeyMultisigThresholdAminoRoute + nameTable[reflect.TypeOf(composite.PubKeyComposite{})] = composite.PubKeyCompositeAminoName } // PubkeyAminoName returns the amino route of a pubkey @@ -49,6 +53,8 @@ func PubkeyAminoName(cdc *amino.Codec, key crypto.PubKey) (string, bool) { func RegisterAmino(cdc *amino.Codec) { // These are all written here instead of cdc.RegisterInterface((*crypto.PubKey)(nil), nil) + cdc.RegisterConcrete(bls.PubKeyBLS12{}, + bls.PubKeyAminoName, nil) cdc.RegisterConcrete(ed25519.PubKeyEd25519{}, ed25519.PubKeyAminoName, nil) cdc.RegisterConcrete(sr25519.PubKeySr25519{}, @@ -57,14 +63,20 @@ func RegisterAmino(cdc *amino.Codec) { secp256k1.PubKeyAminoName, nil) cdc.RegisterConcrete(multisig.PubKeyMultisigThreshold{}, multisig.PubKeyMultisigThresholdAminoRoute, nil) + cdc.RegisterConcrete(composite.PubKeyComposite{}, + composite.PubKeyCompositeAminoName, nil) cdc.RegisterInterface((*crypto.PrivKey)(nil), nil) + cdc.RegisterConcrete(bls.PrivKeyBLS12{}, + bls.PrivKeyAminoName, nil) cdc.RegisterConcrete(ed25519.PrivKeyEd25519{}, ed25519.PrivKeyAminoName, nil) cdc.RegisterConcrete(sr25519.PrivKeySr25519{}, sr25519.PrivKeyAminoName, nil) cdc.RegisterConcrete(secp256k1.PrivKeySecp256k1{}, secp256k1.PrivKeyAminoName, nil) + cdc.RegisterConcrete(composite.PrivKeyComposite{}, + composite.PrivKeyCompositeAminoName, nil) } // RegisterKeyType registers an external key type to allow decoding it from bytes @@ -84,3 +96,8 @@ func PubKeyFromBytes(pubKeyBytes []byte) (pubKey crypto.PubKey, err error) { err = cdc.UnmarshalBinaryBare(pubKeyBytes, &pubKey) return } + +// PubKeyToBytes marshals the public key to bytes +func PubKeyToBytes(pubKey crypto.PubKey) ([]byte, error) { + return cdc.MarshalBinaryBare(pubKey) +} diff --git a/crypto/encoding/amino/encode_test.go b/crypto/encoding/amino/encode_test.go index edc54292f..e3f38d152 100644 --- a/crypto/encoding/amino/encode_test.go +++ b/crypto/encoding/amino/encode_test.go @@ -58,13 +58,17 @@ func ExamplePrintRegisteredTypes() { cdc.PrintTypes(os.Stdout) // Output: | Type | Name | Prefix | Length | Notes | //| ---- | ---- | ------ | ----- | ------ | + //| PubKeyBLS12 | tendermint/PubKeyBLS12 | 0xD68FFBC1 | 0x30 | | //| PubKeyEd25519 | tendermint/PubKeyEd25519 | 0x1624DE64 | 0x20 | | //| PubKeySr25519 | tendermint/PubKeySr25519 | 0x0DFB1005 | 0x20 | | //| PubKeySecp256k1 | tendermint/PubKeySecp256k1 | 0xEB5AE987 | 0x21 | | //| PubKeyMultisigThreshold | tendermint/PubKeyMultisigThreshold | 0x22C1F7E2 | variable | | + //| PubKeyComposite | tendermint/PubKeyComposite | 0x01886E34 | variable | | + //| PrivKeyBLS12 | tendermint/PrivKeyBLS12 | 0xEAECF03F | 0x20 | | //| PrivKeyEd25519 | tendermint/PrivKeyEd25519 | 0xA3288910 | 0x40 | | //| PrivKeySr25519 | tendermint/PrivKeySr25519 | 0x2F82D78B | 0x20 | | //| PrivKeySecp256k1 | tendermint/PrivKeySecp256k1 | 0xE1B0F79B | 0x20 | | + //| PrivKeyComposite | tendermint/PrivKeyComposite | 0x9F3EE8F0 | variable | | } func TestKeyEncodings(t *testing.T) { @@ -172,7 +176,10 @@ func (privkey testPriv) PubKey() crypto.PubKey { return testPub{} } func (privkey testPriv) Bytes() []byte { return testCdc.MustMarshalBinaryBare(privkey) } -func (privkey testPriv) Sign(msg []byte) ([]byte, error) { return []byte{}, nil } +func (privkey testPriv) Sign(msg []byte) ([]byte, error) { return []byte{}, nil } +func (privkey testPriv) VRFProve(seed []byte) (crypto.Proof, error) { + return nil, nil +} func (privkey testPriv) Equals(other crypto.PrivKey) bool { return true } type testPub []byte @@ -181,6 +188,9 @@ func (key testPub) Address() crypto.Address { return crypto.Address{} } func (key testPub) Bytes() []byte { return testCdc.MustMarshalBinaryBare(key) } +func (key testPub) VRFVerify(proof crypto.Proof, seed []byte) (crypto.Output, error) { + return nil, nil +} func (key testPub) VerifyBytes(msg []byte, sig []byte) bool { return true } func (key testPub) Equals(other crypto.PubKey) bool { return true } diff --git a/crypto/encoding/codec.go b/crypto/encoding/codec.go index 261d0ed07..991753660 100644 --- a/crypto/encoding/codec.go +++ b/crypto/encoding/codec.go @@ -5,6 +5,8 @@ import ( "fmt" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/composite" "github.com/tendermint/tendermint/crypto/ed25519" pc "github.com/tendermint/tendermint/proto/crypto/keys" ) @@ -16,6 +18,29 @@ func PubKeyToProto(k crypto.PubKey) (pc.PublicKey, error) { } var kp pc.PublicKey switch k := k.(type) { + case composite.PubKeyComposite: + sign, err := PubKeyToProto(k.SignKey) + if err != nil { + return kp, err + } + vrf, err := PubKeyToProto(k.VrfKey) + if err != nil { + return kp, err + } + kp = pc.PublicKey{ + Sum: &pc.PublicKey_Composite{ + Composite: &pc.CompositePublicKey{ + SignKey: &sign, + VrfKey: &vrf, + }, + }, + } + case bls.PubKeyBLS12: + kp = pc.PublicKey{ + Sum: &pc.PublicKey_Bls{ + Bls: k[:], + }, + } case ed25519.PubKeyEd25519: kp = pc.PublicKey{ Sum: &pc.PublicKey_Ed25519{ @@ -34,6 +59,29 @@ func PubKeyFromProto(k *pc.PublicKey) (crypto.PubKey, error) { return nil, errors.New("nil PublicKey") } switch k := k.Sum.(type) { + case *pc.PublicKey_Composite: + var pk composite.PubKeyComposite + sign, err := PubKeyFromProto(k.Composite.SignKey) + if err != nil { + return pk, err + } + vrf, err := PubKeyFromProto(k.Composite.VrfKey) + if err != nil { + return pk, err + } + pk = composite.PubKeyComposite{ + SignKey: sign, + VrfKey: vrf, + } + return pk, nil + case *pc.PublicKey_Bls: + if len(k.Bls) != bls.PubKeyBLS12Size { + return nil, fmt.Errorf("invalid size for PubKeyBLS. Got %d, expected %d", + len(k.Bls), bls.PubKeyBLS12Size) + } + var pk bls.PubKeyBLS12 + copy(pk[:], k.Bls) + return pk, nil case *pc.PublicKey_Ed25519: if len(k.Ed25519) != ed25519.PubKeyEd25519Size { return nil, fmt.Errorf("invalid size for PubKeyEd25519. Got %d, expected %d", diff --git a/crypto/encoding/codec_test.go b/crypto/encoding/codec_test.go new file mode 100644 index 000000000..6b5499c1a --- /dev/null +++ b/crypto/encoding/codec_test.go @@ -0,0 +1,31 @@ +package encoding + +import ( + "testing" + + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/composite" + "github.com/tendermint/tendermint/crypto/ed25519" +) + +func TestPubKeyFromToProto(t *testing.T) { + vrf := ed25519.GenPrivKey() + signer := bls.GenPrivKey() + sk := composite.NewPrivKeyComposite(signer, vrf) + pk := sk.PubKey() + pbPubKey, err := PubKeyToProto(pk) + if err != nil { + t.Fatalf("The public key could not be converted to a ProtocolBuffers format: %s; %+v", err, pk) + } + pk2, err := PubKeyFromProto(&pbPubKey) + if err != nil { + t.Fatalf("The public key could not be retrieved from a ProtocolBuffers format: %s; %+v", err, pbPubKey) + } + cpk, ok := pk2.(composite.PubKeyComposite) + if !ok { + t.Fatalf("The retrieved public key was not composite key: %+v", pk2) + } + if !cpk.Equals(pk) { + t.Fatalf("The retrieved composite public key was not match: %+v != %+v", cpk, pk) + } +} diff --git a/crypto/multisig/threshold_pubkey.go b/crypto/multisig/threshold_pubkey.go index 36e2dc2dd..8c6380fc7 100644 --- a/crypto/multisig/threshold_pubkey.go +++ b/crypto/multisig/threshold_pubkey.go @@ -1,6 +1,8 @@ package multisig import ( + "fmt" + "github.com/tendermint/tendermint/crypto" ) @@ -67,6 +69,11 @@ func (pk PubKeyMultisigThreshold) VerifyBytes(msg []byte, marshalledSig []byte) return true } +// VRFVerify is not supported in MultisigThreshold. +func (pk PubKeyMultisigThreshold) VRFVerify(proof crypto.Proof, seed []byte) (crypto.Output, error) { + return nil, fmt.Errorf("VRF verify is not supported by the MultisigThreshold") +} + // Bytes returns the amino encoded version of the PubKeyMultisigThreshold func (pk PubKeyMultisigThreshold) Bytes() []byte { return cdc.MustMarshalBinaryBare(pk) diff --git a/crypto/secp256k1/secp256k1.go b/crypto/secp256k1/secp256k1.go index 5338d10a5..b92e4bfce 100644 --- a/crypto/secp256k1/secp256k1.go +++ b/crypto/secp256k1/secp256k1.go @@ -46,6 +46,11 @@ func (privKey PrivKeySecp256k1) Bytes() []byte { return cdc.MustMarshalBinaryBare(privKey) } +// VRFProve is not supported in Secp256k1. +func (privKey PrivKeySecp256k1) VRFProve(seed []byte) (crypto.Proof, error) { + return nil, fmt.Errorf("VRF prove is not supported by the secp256k1") +} + // PubKey performs the point-scalar multiplication from the privKey on the // generator point to get the pubkey. func (privKey PrivKeySecp256k1) PubKey() crypto.PubKey { @@ -158,6 +163,11 @@ func (pubKey PubKeySecp256k1) Bytes() []byte { return bz } +// VRFVerify is not supported in Secp256k1. +func (pubKey PubKeySecp256k1) VRFVerify(proof crypto.Proof, seed []byte) (crypto.Output, error) { + return nil, fmt.Errorf("VRF verify is not supported by the secp256k1") +} + func (pubKey PubKeySecp256k1) String() string { return fmt.Sprintf("PubKeySecp256k1{%X}", pubKey[:]) } diff --git a/crypto/sr25519/privkey.go b/crypto/sr25519/privkey.go index 17d33ebf2..a2a401782 100644 --- a/crypto/sr25519/privkey.go +++ b/crypto/sr25519/privkey.go @@ -40,6 +40,11 @@ func (privKey PrivKeySr25519) Sign(msg []byte) ([]byte, error) { return sigBytes[:], nil } +// VRFProve is not supported in Sr25519. +func (privKey PrivKeySr25519) VRFProve(seed []byte) (crypto.Proof, error) { + return nil, fmt.Errorf("VRF prove is not supported by the sr25519") +} + // PubKey gets the corresponding public key from the private key. func (privKey PrivKeySr25519) PubKey() crypto.PubKey { miniSecretKey, err := schnorrkel.NewMiniSecretKeyFromRaw(privKey) diff --git a/crypto/sr25519/pubkey.go b/crypto/sr25519/pubkey.go index a678806f2..07c6af722 100644 --- a/crypto/sr25519/pubkey.go +++ b/crypto/sr25519/pubkey.go @@ -57,6 +57,11 @@ func (pubKey PubKeySr25519) VerifyBytes(msg []byte, sig []byte) bool { return publicKey.Verify(signature, signingContext) } +// VRFVerify is not supported in Sr25519. +func (pubKey PubKeySr25519) VRFVerify(proof crypto.Proof, seed []byte) (crypto.Output, error) { + return nil, fmt.Errorf("VRF verify is not supported by the sr25519") +} + func (pubKey PubKeySr25519) String() string { return fmt.Sprintf("PubKeySr25519{%X}", pubKey[:]) } diff --git a/crypto/vrf/vrf.go b/crypto/vrf/vrf.go index 57ed5fb67..28518d1e9 100644 --- a/crypto/vrf/vrf.go +++ b/crypto/vrf/vrf.go @@ -2,8 +2,6 @@ package vrf import ( "math/big" - - "github.com/tendermint/tendermint/crypto/ed25519" ) // defaultVrf is assigned to vrfEd25519r2ishiguro by init() of vrf_r2ishguro.go @@ -15,9 +13,13 @@ var defaultVrf vrfEd25519 type Proof []byte type Output []byte +func (proof Proof) ToHash() ([]byte, error) { + return ProofToHash(proof) +} + type vrfEd25519 interface { - Prove(privateKey ed25519.PrivKeyEd25519, message []byte) (Proof, error) - Verify(publicKey ed25519.PubKeyEd25519, proof Proof, message []byte) (bool, error) + Prove(privateKey []byte, publicKey []byte, message []byte) (Proof, error) + Verify(publicKey []byte, proof Proof, message []byte) (bool, error) ProofToHash(proof Proof) (Output, error) } @@ -27,11 +29,11 @@ func (op Output) ToInt() *big.Int { return &i } -func Prove(privateKey ed25519.PrivKeyEd25519, message []byte) (Proof, error) { - return defaultVrf.Prove(privateKey, message) +func Prove(privateKey []byte, publicKey []byte, message []byte) (Proof, error) { + return defaultVrf.Prove(privateKey, publicKey, message) } -func Verify(publicKey ed25519.PubKeyEd25519, proof Proof, message []byte) (bool, error) { +func Verify(publicKey []byte, proof Proof, message []byte) (bool, error) { return defaultVrf.Verify(publicKey, proof, message) } diff --git a/crypto/vrf/vrf_coniks.go b/crypto/vrf/vrf_coniks.go index af121087a..bea4c0683 100644 --- a/crypto/vrf/vrf_coniks.go +++ b/crypto/vrf/vrf_coniks.go @@ -5,8 +5,6 @@ import ( "errors" coniksimpl "github.com/coniks-sys/coniks-go/crypto/vrf" - - "github.com/tendermint/tendermint/crypto/ed25519" ) //nolint @@ -25,7 +23,7 @@ func newVrfEd25519ConiksForVerifier(output Output, proof Proof) *vrfEd25519Conik return &vrfEd25519Coniks{output, proof} } -func (base *vrfEd25519Coniks) Prove(privateKey ed25519.PrivKeyEd25519, message []byte) (Proof, error) { +func (base *vrfEd25519Coniks) Prove(privateKey []byte, _ []byte, message []byte) (Proof, error) { if len(privateKey) != coniksimpl.PrivateKeySize { return nil, errors.New("private key size is invalid") } @@ -37,7 +35,7 @@ func (base *vrfEd25519Coniks) Prove(privateKey ed25519.PrivKeyEd25519, message [ return proof, nil } -func (base *vrfEd25519Coniks) Verify(publicKey ed25519.PubKeyEd25519, proof Proof, message []byte) (bool, error) { +func (base *vrfEd25519Coniks) Verify(publicKey []byte, proof Proof, message []byte) (bool, error) { if base.generatedHash == nil { return false, errors.New("vrf hash was not given") } diff --git a/crypto/vrf/vrf_coniks_test.go b/crypto/vrf/vrf_coniks_test.go index 1b60690ec..cc225dea3 100644 --- a/crypto/vrf/vrf_coniks_test.go +++ b/crypto/vrf/vrf_coniks_test.go @@ -4,23 +4,23 @@ import ( "bytes" "testing" + "crypto/ed25519" + coniksimpl "github.com/coniks-sys/coniks-go/crypto/vrf" "github.com/stretchr/testify/require" - - "github.com/tendermint/tendermint/crypto/ed25519" ) func TestProveAndVerifyConiks(t *testing.T) { secret := [SEEDBYTES]byte{} - privateKey := ed25519.GenPrivKeyFromSecret(secret[:]) - publicKey, _ := privateKey.PubKey().(ed25519.PubKeyEd25519) + privateKey := ed25519.NewKeyFromSeed(secret[:]) + publicKey := privateKey.Public().(ed25519.PublicKey) t.Logf("private key: [%s]", enc(privateKey[:])) t.Logf("public key: [%s]", enc(publicKey[:])) vrfImpl := newVrfEd25519Coniks() message := []byte("hello, world") - proof, err1 := vrfImpl.Prove(privateKey, message) + proof, err1 := vrfImpl.Prove(privateKey[:], publicKey[:], message) if err1 != nil { t.Fatalf("failed to prove: %s", err1) } @@ -32,7 +32,7 @@ func TestProveAndVerifyConiks(t *testing.T) { } t.Logf("hash for \"%s\": %s", message, hash1.ToInt()) - verified, err3 := vrfImpl.Verify(publicKey, proof, message) + verified, err3 := vrfImpl.Verify(publicKey[:], proof, message) if err3 != nil { t.Errorf("failed to verify: %s", err3) } @@ -42,8 +42,8 @@ func TestProveAndVerifyConiks(t *testing.T) { func TestKeyPairCompatibilityConiks(t *testing.T) { secret := [SEEDBYTES]byte{} - privateKey := ed25519.GenPrivKeyFromSecret(secret[:]) - publicKey, _ := privateKey.PubKey().(ed25519.PubKeyEd25519) + privateKey := ed25519.NewKeyFromSeed(secret[:]) + publicKey := privateKey.Public().(ed25519.PublicKey) privateKey2 := coniksimpl.PrivateKey(make([]byte, 64)) copy(privateKey2, privateKey[:]) @@ -55,9 +55,8 @@ func TestKeyPairCompatibilityConiks(t *testing.T) { privateKey2, _ = coniksimpl.GenerateKey(nil) publicKey2, _ = privateKey2.Public() - privateKey = ed25519.PrivKeyEd25519{} - copy(privateKey[:], privateKey2) - publicKey = privateKey.PubKey().(ed25519.PubKeyEd25519) + copy(privateKey[:], privateKey2[:]) + publicKey = privateKey.Public().(ed25519.PublicKey) if !bytes.Equal(publicKey[:], publicKey2) { t.Error("public key is not matched(tm key -> coniks key") } diff --git a/crypto/vrf/vrf_libsodium.go b/crypto/vrf/vrf_libsodium.go index f4fafb47b..8a7a7c57a 100644 --- a/crypto/vrf/vrf_libsodium.go +++ b/crypto/vrf/vrf_libsodium.go @@ -8,7 +8,6 @@ import ( "bytes" "unsafe" - "github.com/tendermint/tendermint/crypto/ed25519" libsodium "github.com/tendermint/tendermint/crypto/vrf/internal/vrf" ) @@ -23,8 +22,8 @@ func init() { defaultVrf = newVrfEd25519ImplLibsodium() } -func (base vrfImplLibsodium) Prove(privateKey ed25519.PrivKeyEd25519, message []byte) (Proof, error) { - privKey := (*[libsodium.SECRETKEYBYTES]byte)(unsafe.Pointer(&privateKey)) +func (base vrfImplLibsodium) Prove(privateKey []byte, message []byte) (Proof, error) { + privKey := (*[libsodium.SECRETKEYBYTES]byte)(unsafe.Pointer(&(*privateKey))) pf, err := libsodium.Prove(privKey, message) if err != nil { return nil, err @@ -32,7 +31,7 @@ func (base vrfImplLibsodium) Prove(privateKey ed25519.PrivKeyEd25519, message [] return newProof(pf), nil } -func (base vrfImplLibsodium) Verify(publicKey ed25519.PubKeyEd25519, proof Proof, message []byte) (bool, error) { +func (base vrfImplLibsodium) Verify(publicKey []byte, proof Proof, message []byte) (bool, error) { pubKey := (*[libsodium.PUBLICKEYBYTES]byte)(unsafe.Pointer(&publicKey)) op, err := libsodium.Verify(pubKey, toArray(proof), message) if err != nil { diff --git a/crypto/vrf/vrf_r2ishiguro.go b/crypto/vrf/vrf_r2ishiguro.go index 69eb762a3..4e9eb47a3 100644 --- a/crypto/vrf/vrf_r2ishiguro.go +++ b/crypto/vrf/vrf_r2ishiguro.go @@ -4,8 +4,6 @@ package vrf import ( r2ishiguro "github.com/r2ishiguro/vrf/go/vrf_ed25519" - - "github.com/tendermint/tendermint/crypto/ed25519" ) type vrfEd25519r2ishiguro struct { @@ -19,13 +17,12 @@ func newVrfEd25519r2ishiguro() vrfEd25519r2ishiguro { return vrfEd25519r2ishiguro{} } -func (base vrfEd25519r2ishiguro) Prove(privateKey ed25519.PrivKeyEd25519, message []byte) (Proof, error) { - pubKey := privateKey.PubKey().(ed25519.PubKeyEd25519) - return r2ishiguro.ECVRF_prove(pubKey[:], privateKey[:], message) +func (base vrfEd25519r2ishiguro) Prove(privateKey []byte, publicKey []byte, message []byte) (Proof, error) { + return r2ishiguro.ECVRF_prove(publicKey, privateKey, message) } -func (base vrfEd25519r2ishiguro) Verify(publicKey ed25519.PubKeyEd25519, proof Proof, message []byte) (bool, error) { - return r2ishiguro.ECVRF_verify(publicKey[:], proof, message) +func (base vrfEd25519r2ishiguro) Verify(publicKey []byte, proof Proof, message []byte) (bool, error) { + return r2ishiguro.ECVRF_verify(publicKey, proof, message) } func (base vrfEd25519r2ishiguro) ProofToHash(proof Proof) (Output, error) { diff --git a/crypto/vrf/vrf_test.go b/crypto/vrf/vrf_test.go index 2907829b4..31fe526f3 100644 --- a/crypto/vrf/vrf_test.go +++ b/crypto/vrf/vrf_test.go @@ -1,17 +1,16 @@ package vrf import ( + "crypto/ed25519" "encoding/hex" "fmt" "testing" "github.com/stretchr/testify/require" - - "github.com/tendermint/tendermint/crypto/ed25519" ) const ( - SEEDBYTES = 64 + SEEDBYTES = ed25519.SeedSize ) var ( @@ -35,10 +34,11 @@ func enc(s []byte) string { func TestProofToHash(t *testing.T) { secret := [SEEDBYTES]byte{} - privateKey := ed25519.GenPrivKeyFromSecret(secret[:]) + privateKey := ed25519.NewKeyFromSeed(secret[:]) + publicKey := privateKey.Public().(ed25519.PublicKey) message := []byte("hello, world") - proof, err1 := Prove(privateKey, message) + proof, err1 := Prove(privateKey[:], publicKey[:], message) if err1 != nil { t.Fatalf("failed to prove: %s", err1) } @@ -61,14 +61,14 @@ func TestProofToHash(t *testing.T) { func TestProveAndVerify(t *testing.T) { secret := [SEEDBYTES]byte{} - privateKey := ed25519.GenPrivKeyFromSecret(secret[:]) - publicKey, _ := privateKey.PubKey().(ed25519.PubKeyEd25519) + privateKey := ed25519.NewKeyFromSeed(secret[:]) + publicKey := privateKey.Public().(ed25519.PublicKey) t.Logf("private key: [%s]", enc(privateKey[:])) t.Logf("public key: [%s]", enc(publicKey[:])) message := []byte("hello, world") - proof, err1 := Prove(privateKey, message) + proof, err1 := Prove(privateKey[:], publicKey[:], message) if err1 != nil { t.Fatalf("failed to prove: %s", err1) } @@ -80,7 +80,7 @@ func TestProveAndVerify(t *testing.T) { } t.Logf("hash for \"%s\": %s", message, hash1.ToInt()) - verified, err3 := Verify(publicKey, proof, message) + verified, err3 := Verify(publicKey[:], proof, message) if err3 != nil { t.Errorf("failed to verify: %s", err3) } else if !verified { @@ -90,12 +90,13 @@ func TestProveAndVerify(t *testing.T) { func TestAvalancheEffect(t *testing.T) { secret := [SEEDBYTES]byte{} - privateKey := ed25519.GenPrivKeyFromSecret(secret[:]) + privateKey := ed25519.NewKeyFromSeed(secret[:]) + publicKey := privateKey.Public().(ed25519.PublicKey) for _, messageString := range Message { message := []byte(messageString) - proof, err := Prove(privateKey, message) + proof, err := Prove(privateKey[:], publicKey[:], message) require.NoError(t, err) hash, err := ProofToHash(proof) require.NoError(t, err) @@ -108,7 +109,7 @@ func TestAvalancheEffect(t *testing.T) { old := message[i/8] message[i/8] ^= byte(uint(1) << (uint(i) % uint(8))) // modify 1 bit - proof2, err := Prove(privateKey, message) + proof2, err := Prove(privateKey[:], publicKey[:], message) require.NoError(t, err) hash2, err := ProofToHash(proof2) require.NoError(t, err) diff --git a/go.mod b/go.mod index d37089a2d..7a8d083bb 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/golang/protobuf v1.4.0 github.com/gorilla/websocket v1.4.2 github.com/gtank/merlin v0.1.1 + github.com/herumi/bls v0.0.0-20200721070902-5e2af1489a06 github.com/herumi/bls-eth-go-binary v0.0.0-20200522010937-01d282b5380b github.com/libp2p/go-buffer-pool v0.0.2 github.com/magiconair/properties v1.8.1 diff --git a/go.sum b/go.sum index 816bd3d47..dced540f8 100644 --- a/go.sum +++ b/go.sum @@ -215,6 +215,8 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= +github.com/herumi/bls v0.0.0-20200721070902-5e2af1489a06 h1:4w5HyoC9FiD1UcO9j6MBkBIeBaLTe8JJOYmSiIFlo8c= +github.com/herumi/bls v0.0.0-20200721070902-5e2af1489a06/go.mod h1:i4wRNUUFF1nNmYFHM9UDl13MGoxEQkMVCLAd82qZz4s= github.com/herumi/bls-eth-go-binary v0.0.0-20200522010937-01d282b5380b h1:mu+F5uA3Y68oB6KXZqWlASKMetbNufhQx2stMI+sD+Y= github.com/herumi/bls-eth-go-binary v0.0.0-20200522010937-01d282b5380b/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= @@ -393,6 +395,8 @@ github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa h1:YJfZp12Z3AFhSBeX github.com/snikch/goodman v0.0.0-20171125024755-10e37e294daa/go.mod h1:oJyF+mSPHbB5mVY2iO9KV3pTt/QbIkGaO8gQ2WrDbP4= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/soroushjp/go_wrapper_c_err v0.0.0-20141212071639-01fa9e68a598 h1:CgZF9rgW+GegumEuVIvpE3FKdpXrgrvCWfuubXkmTn0= +github.com/soroushjp/go_wrapper_c_err v0.0.0-20141212071639-01fa9e68a598/go.mod h1:ytCTj+a7lhvFLwY3dwljqRnDtOiE+Y/+zFT0vDOvoPA= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= @@ -435,6 +439,8 @@ github.com/tendermint/tm-db v0.5.1 h1:H9HDq8UEA7Eeg13kdYckkgwwkQLBnJGgX4PgLJRhie github.com/tendermint/tm-db v0.5.1/go.mod h1:g92zWjHpCYlEvQXvy9M168Su8V1IBEeawpXVVBaK4f4= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/toxeus/go-secp256k1 v0.0.0-20180117210953-77e56d25bcd6 h1:AIC/fedOD4UZqxoxa31sGs2U98JU9M/MppHzYUKE4IM= +github.com/toxeus/go-secp256k1 v0.0.0-20180117210953-77e56d25bcd6/go.mod h1:N4r0cLx8bd/xd7H6JbrLHJ2RGWv2Mhe/RdSRWiwOHz8= github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= diff --git a/lite/helpers.go b/lite/helpers.go index baff0dc21..f56071ed0 100644 --- a/lite/helpers.go +++ b/lite/helpers.go @@ -4,8 +4,6 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/secp256k1" - "github.com/tendermint/tendermint/crypto/vrf" - tmbytes "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" @@ -125,7 +123,7 @@ func genHeader(chainID string, height int64, txs types.Txs, secret := [64]byte{} privateKey := ed25519.GenPrivKeyFromSecret(secret[:]) message := []byte("hello, world") - proof, _ := vrf.Prove(privateKey, message) + proof, _ := privateKey.VRFProve(message) return &types.Header{ ChainID: chainID, diff --git a/lite2/client_test.go b/lite2/client_test.go index 5a278bc5a..cb5adb895 100644 --- a/lite2/client_test.go +++ b/lite2/client_test.go @@ -10,7 +10,6 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/vrf" tmbytes "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/rand" @@ -211,8 +210,8 @@ func genSignedHeaderWithInvalidProof(pkz privKeys, chainID string, height int64, secret := [64]byte{} privateKey := ed25519.GenPrivKeyFromSecret(secret[:]) message := rand.Bytes(10) - proof, _ := vrf.Prove(privateKey, message) - proofHash, _ := vrf.ProofToHash(proof) + proof, _ := privateKey.VRFProve(message) + proofHash, _ := privateKey.PubKey().VRFVerify(proof, message) invalidProofHash := make([]byte, len(proofHash)) copy(invalidProofHash, proofHash) invalidProofHash[0] ^= 0x01 // force invalid proof hash diff --git a/lite2/helpers_test.go b/lite2/helpers_test.go index 29c433f99..291b417a4 100644 --- a/lite2/helpers_test.go +++ b/lite2/helpers_test.go @@ -6,7 +6,6 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" "github.com/tendermint/tendermint/crypto/tmhash" - "github.com/tendermint/tendermint/crypto/vrf" tmbytes "github.com/tendermint/tendermint/libs/bytes" "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/types" @@ -190,8 +189,8 @@ func (pkz privKeys) GenSignedHeader(chainID string, height int64, bTime time.Tim secret := [64]byte{} privateKey := ed25519.GenPrivKeyFromSecret(secret[:]) message := rand.Bytes(10) - proof, _ := vrf.Prove(privateKey, message) - proofHash, _ := vrf.ProofToHash(proof) + proof, _ := privateKey.VRFProve(message) + proofHash, _ := privateKey.PubKey().VRFVerify(proof, message) voterSet := types.SelectVoter(valset, proofHash, voterParams) header := genHeader(chainID, height, bTime, txs, voterSet, valset, nextValset, appHash, consHash, resHash, @@ -209,8 +208,8 @@ func (pkz privKeys) GenSignedHeaderByRate(chainID string, height int64, bTime ti secret := [64]byte{} privateKey := ed25519.GenPrivKeyFromSecret(secret[:]) message := rand.Bytes(10) - proof, _ := vrf.Prove(privateKey, message) - proofHash, _ := vrf.ProofToHash(proof) + proof, _ := privateKey.VRFProve(message) + proofHash, _ := privateKey.PubKey().VRFVerify(proof, message) voterSet := types.SelectVoter(valset, proofHash, voterParams) header := genHeader(chainID, height, bTime, txs, voterSet, valset, nextValset, appHash, consHash, resHash, @@ -229,8 +228,8 @@ func (pkz privKeys) GenSignedHeaderLastBlockID(chainID string, height int64, bTi secret := [64]byte{} privateKey := ed25519.GenPrivKeyFromSecret(secret[:]) message := rand.Bytes(10) - proof, _ := vrf.Prove(privateKey, message) - proofHash, _ := vrf.ProofToHash(proof) + proof, _ := privateKey.VRFProve(message) + proofHash, _ := privateKey.PubKey().VRFVerify(proof, message) voterSet := types.SelectVoter(valset, proofHash, voterParams) header := genHeader(chainID, height, bTime, txs, voterSet, valset, nextValset, appHash, consHash, resHash, diff --git a/networks/local/localnode/Dockerfile b/networks/local/localnode/Dockerfile index 4aee97557..f47557fca 100644 --- a/networks/local/localnode/Dockerfile +++ b/networks/local/localnode/Dockerfile @@ -1,11 +1,11 @@ -FROM alpine:3.11 +FROM golang:1.14-alpine MAINTAINER Greg Szabo RUN apk update && \ apk upgrade && \ - apk --no-cache add curl jq file gcc libc-dev build-base - -VOLUME [ /tendermint ] + apk add --update --no-cache git make gcc libc-dev build-base curl jq file \ + gmp-dev clang +VOLUME [ "/tendermint" ] WORKDIR /tendermint EXPOSE 26656 26657 ENTRYPOINT ["/usr/bin/wrapper.sh"] diff --git a/networks/local/localnode/wrapper.sh b/networks/local/localnode/wrapper.sh index fe8031e66..ca2dac253 100755 --- a/networks/local/localnode/wrapper.sh +++ b/networks/local/localnode/wrapper.sh @@ -13,6 +13,9 @@ LOG=${LOG:-tendermint.log} if ! [ -f "${BINARY}" ]; then echo "The binary $(basename "${BINARY}") cannot be found. Please add the binary to the shared folder. Please use the BINARY environment variable if the name of the binary is not 'tendermint' E.g.: -e BINARY=tendermint_my_test_version" exit 1 +elif ! [ -x "${BINARY}" ]; then + echo "The binary $(basename "${BINARY}") is not executable." + exit 1 fi BINARY_CHECK="$(file "$BINARY" | grep 'ELF 64-bit LSB executable, x86-64')" if [ -z "${BINARY_CHECK}" ]; then diff --git a/p2p/conn/secret_connection_test.go b/p2p/conn/secret_connection_test.go index 9044d73be..279d2b81a 100644 --- a/p2p/conn/secret_connection_test.go +++ b/p2p/conn/secret_connection_test.go @@ -326,10 +326,11 @@ type privKeyWithNilPubKey struct { orig crypto.PrivKey } -func (pk privKeyWithNilPubKey) Bytes() []byte { return pk.orig.Bytes() } -func (pk privKeyWithNilPubKey) Sign(msg []byte) ([]byte, error) { return pk.orig.Sign(msg) } -func (pk privKeyWithNilPubKey) PubKey() crypto.PubKey { return nil } -func (pk privKeyWithNilPubKey) Equals(pk2 crypto.PrivKey) bool { return pk.orig.Equals(pk2) } +func (pk privKeyWithNilPubKey) Bytes() []byte { return pk.orig.Bytes() } +func (pk privKeyWithNilPubKey) Sign(msg []byte) ([]byte, error) { return pk.orig.Sign(msg) } +func (pk privKeyWithNilPubKey) VRFProve(msg []byte) (crypto.Proof, error) { return nil, nil } +func (pk privKeyWithNilPubKey) PubKey() crypto.PubKey { return nil } +func (pk privKeyWithNilPubKey) Equals(pk2 crypto.PrivKey) bool { return pk.orig.Equals(pk2) } func TestNilPubkey(t *testing.T) { var fooConn, barConn = makeKVStoreConnPair() diff --git a/p2p/key.go b/p2p/key.go index 4af5a5416..f4c9413ab 100644 --- a/p2p/key.go +++ b/p2p/key.go @@ -7,7 +7,6 @@ import ( "io/ioutil" "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/bls" "github.com/tendermint/tendermint/crypto/ed25519" tmos "github.com/tendermint/tendermint/libs/os" ) @@ -26,8 +25,7 @@ const IDByteLength = crypto.AddressSize // NodeKey is the persistent peer key. // It contains the nodes private key for authentication. type NodeKey struct { - PrivKey crypto.PrivKey `json:"priv_key"` // our priv key - SigKey bls.PrivKeyBLS12 `json:"sig_key"` // for signature aggregation + PrivKey crypto.PrivKey `json:"priv_key"` // our priv key } // ID returns the peer's canonical ID - the hash of its public key. @@ -40,10 +38,6 @@ func (nodeKey *NodeKey) PubKey() crypto.PubKey { return nodeKey.PrivKey.PubKey() } -func (nodeKey *NodeKey) SigPubKey() crypto.PubKey { - return nodeKey.SigKey.PubKey() -} - // PubKeyToID returns the ID corresponding to the given PubKey. // It's the hex-encoding of the pubKey.Address(). func PubKeyToID(pubKey crypto.PubKey) ID { @@ -78,10 +72,8 @@ func LoadNodeKey(filePath string) (*NodeKey, error) { func genNodeKey(filePath string) (*NodeKey, error) { privKey := ed25519.GenPrivKey() - sigKey := bls.GenPrivKey() nodeKey := &NodeKey{ PrivKey: privKey, - SigKey: sigKey, } jsonBytes, err := cdc.MarshalJSON(nodeKey) diff --git a/privval/file.go b/privval/file.go index c29a2066d..aa63b4a2d 100644 --- a/privval/file.go +++ b/privval/file.go @@ -7,9 +7,9 @@ import ( "io/ioutil" "time" - "github.com/tendermint/tendermint/crypto/vrf" - "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/composite" "github.com/tendermint/tendermint/crypto/ed25519" tmbytes "github.com/tendermint/tendermint/libs/bytes" tmos "github.com/tendermint/tendermint/libs/os" @@ -152,7 +152,9 @@ type FilePV struct { // GenFilePV generates a new validator with randomly generated private key // and sets the filePaths, but does not call Save(). func GenFilePV(keyFilePath, stateFilePath string) *FilePV { - privKey := ed25519.GenPrivKey() + signKey := bls.GenPrivKey() + vrfKey := ed25519.GenPrivKey() + privKey := composite.NewPrivKeyComposite(signKey, vrfKey) return &FilePV{ Key: FilePVKey{ @@ -262,12 +264,8 @@ func (pv *FilePV) SignProposal(chainID string, proposal *types.Proposal) error { } // GenerateVRFProof generates a proof for specified message. -func (pv *FilePV) GenerateVRFProof(message []byte) (vrf.Proof, error) { - privKey, ok := pv.Key.PrivKey.(ed25519.PrivKeyEd25519) - if !ok { - return nil, types.NewErrUnsupportedKey("ed25519") - } - return vrf.Prove(privKey, message) +func (pv *FilePV) GenerateVRFProof(message []byte) (crypto.Proof, error) { + return pv.Key.PrivKey.VRFProve(message) } // Save persists the FilePV to disk. diff --git a/privval/file_test.go b/privval/file_test.go index 71f273ddf..a4b3a3c8c 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -9,8 +9,6 @@ import ( "testing" "time" - "github.com/tendermint/tendermint/crypto/vrf" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -247,8 +245,6 @@ func TestSignProposal(t *testing.T) { } func TestGenerateVRFProof(t *testing.T) { - assert := assert.New(t) - tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") require.Nil(t, err) tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") @@ -256,20 +252,14 @@ func TestGenerateVRFProof(t *testing.T) { privVal := GenFilePV(tempKeyFile.Name(), tempStateFile.Name()) - privKeyEd25519, ok := privVal.Key.PrivKey.(ed25519.PrivKeyEd25519) - require.True(t, ok) - pubKeyEd25519, ok := privKeyEd25519.PubKey().(ed25519.PubKeyEd25519) - require.True(t, ok) success := [][]byte{{}, {0x00}, make([]byte, 100)} for _, msg := range success { proof, err := privVal.GenerateVRFProof(msg) require.Nil(t, err) t.Log(" Message : ", hex.EncodeToString(msg), " -> ", hex.EncodeToString(proof[:])) - _, err = vrf.ProofToHash(proof) - require.Nil(t, err) - expected, err := vrf.Verify(pubKeyEd25519, proof, msg) + pubKey, _ := privVal.GetPubKey() + _, err = pubKey.VRFVerify(proof, msg) require.Nil(t, err) - assert.True(expected) } } diff --git a/privval/messages.go b/privval/messages.go index a5f218a1f..b6b28f1ab 100644 --- a/privval/messages.go +++ b/privval/messages.go @@ -4,7 +4,6 @@ import ( amino "github.com/tendermint/go-amino" "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/vrf" "github.com/tendermint/tendermint/types" ) @@ -66,7 +65,7 @@ type VRFProofRequest struct { // VRFProofResponse is a PrivValidatorSocket message containing a Proof. type VRFProofResponse struct { - Proof vrf.Proof + Proof crypto.Proof Error *RemoteSignerError } diff --git a/privval/retry_signer_client.go b/privval/retry_signer_client.go index e798f4786..8347b14f5 100644 --- a/privval/retry_signer_client.go +++ b/privval/retry_signer_client.go @@ -5,7 +5,6 @@ import ( "time" "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/vrf" "github.com/tendermint/tendermint/types" ) @@ -83,9 +82,9 @@ func (sc *RetrySignerClient) SignProposal(chainID string, proposal *types.Propos return fmt.Errorf("exhausted all attempts to sign proposal: %w", err) } -func (sc *RetrySignerClient) GenerateVRFProof(message []byte) (vrf.Proof, error) { +func (sc *RetrySignerClient) GenerateVRFProof(message []byte) (crypto.Proof, error) { var err error - var proof vrf.Proof + var proof crypto.Proof for i := 0; i < sc.retries || sc.retries == 0; i++ { proof, err = sc.next.GenerateVRFProof(message) if err == nil { diff --git a/privval/signer_client.go b/privval/signer_client.go index 593cbeddf..1db6b4623 100644 --- a/privval/signer_client.go +++ b/privval/signer_client.go @@ -7,7 +7,6 @@ import ( "github.com/pkg/errors" "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/vrf" "github.com/tendermint/tendermint/types" ) @@ -134,7 +133,7 @@ func (sc *SignerClient) SignProposal(chainID string, proposal *types.Proposal) e } // GenerateVRFProof requests a remote signer to generate a VRF proof -func (sc *SignerClient) GenerateVRFProof(message []byte) (vrf.Proof, error) { +func (sc *SignerClient) GenerateVRFProof(message []byte) (crypto.Proof, error) { response, err := sc.endpoint.SendRequest(&VRFProofRequest{Message: message}) if err != nil { sc.endpoint.Logger.Error("SignerClient::GenerateVRFProof", "err", err) diff --git a/privval/signer_client_test.go b/privval/signer_client_test.go index 47207949b..1853192cc 100644 --- a/privval/signer_client_test.go +++ b/privval/signer_client_test.go @@ -9,7 +9,6 @@ import ( "github.com/stretchr/testify/require" "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/vrf" "github.com/tendermint/tendermint/libs/rand" "github.com/tendermint/tendermint/types" ) @@ -118,15 +117,10 @@ func TestSignerGenerateVRFProof(t *testing.T) { proof, err := tc.signerClient.GenerateVRFProof(message) require.Nil(t, err) - _, err = vrf.ProofToHash(proof) + pubKey, _ := tc.signerClient.GetPubKey() + output, err := pubKey.VRFVerify(proof, message) require.Nil(t, err) - pubKey, err2 := tc.signerClient.GetPubKey() - require.NoError(t, err2) - pubKeyEd25519, ok := pubKey.(ed25519.PubKeyEd25519) - require.True(t, ok) - expected, err := vrf.Verify(pubKeyEd25519, proof, message) - require.Nil(t, err) - assert.True(t, expected) + require.NotNil(t, output) } } diff --git a/proto/crypto/keys/types.pb.go b/proto/crypto/keys/types.pb.go index 717e875c1..54f92a714 100644 --- a/proto/crypto/keys/types.pb.go +++ b/proto/crypto/keys/types.pb.go @@ -22,10 +22,59 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// CompositePublicKey allows to use different public keys for different features +type CompositePublicKey struct { + SignKey *PublicKey `protobuf:"bytes,1,opt,name=sign_key,json=signKey,proto3" json:"sign_key,omitempty"` + VrfKey *PublicKey `protobuf:"bytes,2,opt,name=vrf_key,json=vrfKey,proto3" json:"vrf_key,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CompositePublicKey) Reset() { *m = CompositePublicKey{} } +func (m *CompositePublicKey) String() string { return proto.CompactTextString(m) } +func (*CompositePublicKey) ProtoMessage() {} +func (*CompositePublicKey) Descriptor() ([]byte, []int) { + return fileDescriptor_943d79b57ec0188f, []int{0} +} +func (m *CompositePublicKey) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CompositePublicKey.Unmarshal(m, b) +} +func (m *CompositePublicKey) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CompositePublicKey.Marshal(b, m, deterministic) +} +func (m *CompositePublicKey) XXX_Merge(src proto.Message) { + xxx_messageInfo_CompositePublicKey.Merge(m, src) +} +func (m *CompositePublicKey) XXX_Size() int { + return xxx_messageInfo_CompositePublicKey.Size(m) +} +func (m *CompositePublicKey) XXX_DiscardUnknown() { + xxx_messageInfo_CompositePublicKey.DiscardUnknown(m) +} + +var xxx_messageInfo_CompositePublicKey proto.InternalMessageInfo + +func (m *CompositePublicKey) GetSignKey() *PublicKey { + if m != nil { + return m.SignKey + } + return nil +} + +func (m *CompositePublicKey) GetVrfKey() *PublicKey { + if m != nil { + return m.VrfKey + } + return nil +} + // PublicKey defines the keys available for use with Tendermint Validators type PublicKey struct { // Types that are valid to be assigned to Sum: // *PublicKey_Ed25519 + // *PublicKey_Bls + // *PublicKey_Composite Sum isPublicKey_Sum `protobuf_oneof:"sum"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` @@ -36,7 +85,7 @@ func (m *PublicKey) Reset() { *m = PublicKey{} } func (m *PublicKey) String() string { return proto.CompactTextString(m) } func (*PublicKey) ProtoMessage() {} func (*PublicKey) Descriptor() ([]byte, []int) { - return fileDescriptor_943d79b57ec0188f, []int{0} + return fileDescriptor_943d79b57ec0188f, []int{1} } func (m *PublicKey) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_PublicKey.Unmarshal(m, b) @@ -65,8 +114,16 @@ type isPublicKey_Sum interface { type PublicKey_Ed25519 struct { Ed25519 []byte `protobuf:"bytes,1,opt,name=ed25519,proto3,oneof" json:"ed25519,omitempty"` } +type PublicKey_Bls struct { + Bls []byte `protobuf:"bytes,2,opt,name=bls,proto3,oneof" json:"bls,omitempty"` +} +type PublicKey_Composite struct { + Composite *CompositePublicKey `protobuf:"bytes,3,opt,name=composite,proto3,oneof" json:"composite,omitempty"` +} -func (*PublicKey_Ed25519) isPublicKey_Sum() {} +func (*PublicKey_Ed25519) isPublicKey_Sum() {} +func (*PublicKey_Bls) isPublicKey_Sum() {} +func (*PublicKey_Composite) isPublicKey_Sum() {} func (m *PublicKey) GetSum() isPublicKey_Sum { if m != nil { @@ -82,10 +139,26 @@ func (m *PublicKey) GetEd25519() []byte { return nil } +func (m *PublicKey) GetBls() []byte { + if x, ok := m.GetSum().(*PublicKey_Bls); ok { + return x.Bls + } + return nil +} + +func (m *PublicKey) GetComposite() *CompositePublicKey { + if x, ok := m.GetSum().(*PublicKey_Composite); ok { + return x.Composite + } + return nil +} + // XXX_OneofWrappers is for the internal use of the proto package. func (*PublicKey) XXX_OneofWrappers() []interface{} { return []interface{}{ (*PublicKey_Ed25519)(nil), + (*PublicKey_Bls)(nil), + (*PublicKey_Composite)(nil), } } @@ -104,7 +177,7 @@ func (m *PrivateKey) Reset() { *m = PrivateKey{} } func (m *PrivateKey) String() string { return proto.CompactTextString(m) } func (*PrivateKey) ProtoMessage() {} func (*PrivateKey) Descriptor() ([]byte, []int) { - return fileDescriptor_943d79b57ec0188f, []int{1} + return fileDescriptor_943d79b57ec0188f, []int{2} } func (m *PrivateKey) XXX_Unmarshal(b []byte) error { return xxx_messageInfo_PrivateKey.Unmarshal(m, b) @@ -156,6 +229,7 @@ func (*PrivateKey) XXX_OneofWrappers() []interface{} { } func init() { + proto.RegisterType((*CompositePublicKey)(nil), "tendermint.proto.crypto.keys.CompositePublicKey") proto.RegisterType((*PublicKey)(nil), "tendermint.proto.crypto.keys.PublicKey") proto.RegisterType((*PrivateKey)(nil), "tendermint.proto.crypto.keys.PrivateKey") } @@ -163,21 +237,64 @@ func init() { func init() { proto.RegisterFile("proto/crypto/keys/types.proto", fileDescriptor_943d79b57ec0188f) } var fileDescriptor_943d79b57ec0188f = []byte{ - // 190 bytes of a gzipped FileDescriptorProto + // 293 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x2d, 0x28, 0xca, 0x2f, 0xc9, 0xd7, 0x4f, 0x2e, 0xaa, 0x2c, 0x28, 0xc9, 0xd7, 0xcf, 0x4e, 0xad, 0x2c, 0xd6, 0x2f, 0xa9, 0x2c, 0x48, 0x2d, 0xd6, 0x03, 0x8b, 0x0b, 0xc9, 0x94, 0xa4, 0xe6, 0xa5, 0xa4, 0x16, 0xe5, 0x66, 0xe6, 0x95, 0x40, 0x44, 0xf4, 0x20, 0x2a, 0xf5, 0x40, 0x2a, 0xa5, 0xd4, 0x4a, 0x32, 0x32, 0x8b, 0x52, 0xe2, 0x0b, 0x12, 0x8b, 0x4a, 0x2a, 0xf5, 0x21, 0x06, 0xa5, 0xe7, 0xa7, 0xe7, 0x23, 0x58, - 0x10, 0x3d, 0x4a, 0x16, 0x5c, 0x9c, 0x01, 0xa5, 0x49, 0x39, 0x99, 0xc9, 0xde, 0xa9, 0x95, 0x42, - 0x52, 0x5c, 0xec, 0xa9, 0x29, 0x46, 0xa6, 0xa6, 0x86, 0x96, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x3c, - 0x1e, 0x0c, 0x41, 0x30, 0x01, 0x2b, 0x8e, 0x17, 0x0b, 0xe4, 0x19, 0x5f, 0x2c, 0x94, 0x67, 0x74, - 0x62, 0xe5, 0x62, 0x2e, 0x2e, 0xcd, 0x55, 0xd2, 0xe7, 0xe2, 0x0a, 0x28, 0xca, 0x2c, 0x4b, 0x2c, - 0x49, 0x25, 0xa0, 0x15, 0xaa, 0xc1, 0xc9, 0x24, 0xca, 0x28, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, - 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0xe1, 0x7a, 0x64, 0x26, 0x86, 0x97, 0x93, 0xd8, 0xc0, 0x42, 0xc6, - 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x51, 0xcf, 0x02, 0x32, 0x0e, 0x01, 0x00, 0x00, + 0x10, 0x3d, 0x4a, 0x4b, 0x18, 0xb9, 0x84, 0x9c, 0xf3, 0x73, 0x0b, 0xf2, 0x8b, 0x33, 0x4b, 0x52, + 0x03, 0x4a, 0x93, 0x72, 0x32, 0x93, 0xbd, 0x53, 0x2b, 0x85, 0x9c, 0xb8, 0x38, 0x8a, 0x33, 0xd3, + 0xf3, 0xe2, 0xb3, 0x53, 0x2b, 0x25, 0x18, 0x15, 0x18, 0x35, 0xb8, 0x8d, 0xd4, 0xf5, 0xf0, 0xd9, + 0xa7, 0x07, 0xd7, 0x1a, 0xc4, 0x0e, 0xd2, 0x08, 0x32, 0xc3, 0x81, 0x8b, 0xbd, 0xac, 0x28, 0x0d, + 0x6c, 0x04, 0x13, 0x69, 0x46, 0xb0, 0x95, 0x15, 0xa5, 0x79, 0xa7, 0x56, 0x5a, 0x71, 0xbc, 0x58, + 0x20, 0xcf, 0xf8, 0x62, 0xa1, 0x3c, 0xa3, 0xd2, 0x3c, 0x46, 0x2e, 0x4e, 0x84, 0xeb, 0xa4, 0xb8, + 0xd8, 0x53, 0x53, 0x8c, 0x4c, 0x4d, 0x0d, 0x2d, 0xc1, 0x8e, 0xe3, 0xf1, 0x60, 0x08, 0x82, 0x09, + 0x08, 0x09, 0x71, 0x31, 0x27, 0xe5, 0x14, 0x83, 0x6d, 0x04, 0x89, 0x83, 0x38, 0x42, 0x01, 0x5c, + 0x9c, 0xc9, 0x30, 0x3f, 0x4a, 0x30, 0x83, 0xdd, 0x62, 0x80, 0xdf, 0x2d, 0x98, 0x41, 0xe2, 0xc1, + 0x10, 0x84, 0x30, 0x04, 0xe1, 0x32, 0x27, 0x56, 0x2e, 0xe6, 0xe2, 0xd2, 0x5c, 0x25, 0x7d, 0x2e, + 0xae, 0x80, 0xa2, 0xcc, 0xb2, 0xc4, 0x92, 0x54, 0x02, 0x0e, 0x84, 0x6a, 0x70, 0x32, 0x89, 0x32, + 0x4a, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x47, 0x38, 0x06, 0x99, 0x89, + 0x91, 0x00, 0x92, 0xd8, 0xc0, 0x42, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x9f, 0x5f, 0xb9, + 0x2c, 0x1c, 0x02, 0x00, 0x00, } +func (this *CompositePublicKey) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*CompositePublicKey) + if !ok { + that2, ok := that.(CompositePublicKey) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if c := this.SignKey.Compare(that1.SignKey); c != 0 { + return c + } + if c := this.VrfKey.Compare(that1.VrfKey); c != 0 { + return c + } + if c := bytes.Compare(this.XXX_unrecognized, that1.XXX_unrecognized); c != 0 { + return c + } + return 0 +} func (this *PublicKey) Compare(that interface{}) int { if that == nil { if this == nil { @@ -214,6 +331,10 @@ func (this *PublicKey) Compare(that interface{}) int { switch this.Sum.(type) { case *PublicKey_Ed25519: thisType = 0 + case *PublicKey_Bls: + thisType = 1 + case *PublicKey_Composite: + thisType = 2 default: panic(fmt.Sprintf("compare: unexpected type %T in oneof", this.Sum)) } @@ -221,6 +342,10 @@ func (this *PublicKey) Compare(that interface{}) int { switch that1.Sum.(type) { case *PublicKey_Ed25519: that1Type = 0 + case *PublicKey_Bls: + that1Type = 1 + case *PublicKey_Composite: + that1Type = 2 default: panic(fmt.Sprintf("compare: unexpected type %T in oneof", that1.Sum)) } @@ -269,6 +394,96 @@ func (this *PublicKey_Ed25519) Compare(that interface{}) int { } return 0 } +func (this *PublicKey_Bls) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*PublicKey_Bls) + if !ok { + that2, ok := that.(PublicKey_Bls) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if c := bytes.Compare(this.Bls, that1.Bls); c != 0 { + return c + } + return 0 +} +func (this *PublicKey_Composite) Compare(that interface{}) int { + if that == nil { + if this == nil { + return 0 + } + return 1 + } + + that1, ok := that.(*PublicKey_Composite) + if !ok { + that2, ok := that.(PublicKey_Composite) + if ok { + that1 = &that2 + } else { + return 1 + } + } + if that1 == nil { + if this == nil { + return 0 + } + return 1 + } else if this == nil { + return -1 + } + if c := this.Composite.Compare(that1.Composite); c != 0 { + return c + } + return 0 +} +func (this *CompositePublicKey) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*CompositePublicKey) + if !ok { + that2, ok := that.(CompositePublicKey) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.SignKey.Equal(that1.SignKey) { + return false + } + if !this.VrfKey.Equal(that1.VrfKey) { + return false + } + if !bytes.Equal(this.XXX_unrecognized, that1.XXX_unrecognized) { + return false + } + return true +} func (this *PublicKey) Equal(that interface{}) bool { if that == nil { return this == nil @@ -326,3 +541,51 @@ func (this *PublicKey_Ed25519) Equal(that interface{}) bool { } return true } +func (this *PublicKey_Bls) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*PublicKey_Bls) + if !ok { + that2, ok := that.(PublicKey_Bls) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !bytes.Equal(this.Bls, that1.Bls) { + return false + } + return true +} +func (this *PublicKey_Composite) Equal(that interface{}) bool { + if that == nil { + return this == nil + } + + that1, ok := that.(*PublicKey_Composite) + if !ok { + that2, ok := that.(PublicKey_Composite) + if ok { + that1 = &that2 + } else { + return false + } + } + if that1 == nil { + return this == nil + } else if this == nil { + return false + } + if !this.Composite.Equal(that1.Composite) { + return false + } + return true +} diff --git a/proto/crypto/keys/types.proto b/proto/crypto/keys/types.proto index be4abd609..ccdce7e54 100644 --- a/proto/crypto/keys/types.proto +++ b/proto/crypto/keys/types.proto @@ -5,6 +5,15 @@ option go_package = "github.com/tendermint/tendermint/proto/crypto/keys"; import "third_party/proto/gogoproto/gogo.proto"; +// CompositePublicKey allows to use different public keys for different features +message CompositePublicKey { + option (gogoproto.compare) = true; + option (gogoproto.equal) = true; + + PublicKey sign_key = 1; + PublicKey vrf_key = 2; +} + // PublicKey defines the keys available for use with Tendermint Validators message PublicKey { option (gogoproto.compare) = true; @@ -12,6 +21,8 @@ message PublicKey { oneof sum { bytes ed25519 = 1; + bytes bls = 2; + CompositePublicKey composite = 3; } } diff --git a/state/execution.go b/state/execution.go index 119638dd5..db845cdc1 100644 --- a/state/execution.go +++ b/state/execution.go @@ -4,6 +4,8 @@ import ( "fmt" "time" + "github.com/tendermint/tendermint/crypto" + dbm "github.com/tendermint/tm-db" abci "github.com/tendermint/tendermint/abci/types" @@ -95,7 +97,7 @@ func (blockExec *BlockExecutor) CreateProposalBlock( state State, commit *types.Commit, proposerAddr []byte, round int, - proof vrf.Proof, + proof crypto.Proof, ) (*types.Block, *types.PartSet) { maxBytes := state.ConsensusParams.Block.MaxBytes diff --git a/state/state.go b/state/state.go index bb924abb3..6341389a1 100644 --- a/state/state.go +++ b/state/state.go @@ -6,7 +6,7 @@ import ( "io/ioutil" "time" - "github.com/tendermint/tendermint/crypto/vrf" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/types" tmtime "github.com/tendermint/tendermint/types/time" "github.com/tendermint/tendermint/version" @@ -149,7 +149,7 @@ func (state State) MakeBlock( evidence []types.Evidence, proposerAddress []byte, round int, - proof vrf.Proof, + proof crypto.Proof, ) (*types.Block, *types.PartSet) { // Build base block with block data. diff --git a/state/state_test.go b/state/state_test.go index b50807415..023117a5b 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -17,7 +17,6 @@ import ( abci "github.com/tendermint/tendermint/abci/types" cfg "github.com/tendermint/tendermint/config" "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/vrf" "github.com/tendermint/tendermint/libs/kv" "github.com/tendermint/tendermint/libs/rand" tmrand "github.com/tendermint/tendermint/libs/rand" @@ -1136,7 +1135,8 @@ func TestState_MakeHashMessage(t *testing.T) { privVal := makePrivVal() proof, _ := privVal.GenerateVRFProof(message1) - output, _ := vrf.ProofToHash(proof) + pubKey, _ := privVal.GetPubKey() + output, _ := pubKey.VRFVerify(proof, message1) state.LastProofHash = output message3 := state.MakeHashMessage(0) require.False(t, bytes.Equal(message1, message3)) diff --git a/state/tx_filter_test.go b/state/tx_filter_test.go index feecbaddc..25a687ca8 100644 --- a/state/tx_filter_test.go +++ b/state/tx_filter_test.go @@ -16,7 +16,7 @@ import ( func TestTxFilter(t *testing.T) { genDoc := randomGenesisDoc() - genDoc.ConsensusParams.Block.MaxBytes = 3000 + genDoc.ConsensusParams.Block.MaxBytes = 3035 // Max size of Txs is much smaller than size of block, // since we need to account for commits and evidence. diff --git a/state/validation.go b/state/validation.go index 4b8f02357..b5c1fc301 100644 --- a/state/validation.go +++ b/state/validation.go @@ -8,8 +8,6 @@ import ( dbm "github.com/tendermint/tm-db" "github.com/tendermint/tendermint/crypto" - "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/vrf" "github.com/tendermint/tendermint/types" ) @@ -177,14 +175,12 @@ func validateBlock(evidencePool EvidencePool, stateDB dbm.DB, state State, round // validate vrf proof message := state.MakeHashMessage(block.Round) _, val := state.Validators.GetByAddress(block.ProposerAddress) - verified, err := vrf.Verify(val.PubKey.(ed25519.PubKeyEd25519), block.Proof.Bytes(), message) + proof := crypto.Proof(block.Proof) + _, err := val.PubKey.VRFVerify(proof, message) if err != nil { return types.NewErrInvalidProof(fmt.Sprintf( "verification failed: %s; proof: %v, prevProofHash: %v, height=%d, round=%d, addr: %v", err.Error(), block.Proof, state.LastProofHash, state.LastBlockHeight, block.Round, block.ProposerAddress)) - } else if !verified { - return types.NewErrInvalidProof(fmt.Sprintf("proof: %v, prevProofHash: %v, height=%d, round=%d, addr: %v", - block.Proof, state.LastProofHash, state.LastBlockHeight, block.Round, block.ProposerAddress)) } return nil diff --git a/types/block.go b/types/block.go index ad2e4c8bc..96274dd10 100644 --- a/types/block.go +++ b/types/block.go @@ -12,7 +12,6 @@ import ( "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/crypto/tmhash" - "github.com/tendermint/tendermint/crypto/vrf" "github.com/tendermint/tendermint/libs/bits" tmbytes "github.com/tendermint/tendermint/libs/bytes" tmmath "github.com/tendermint/tendermint/libs/math" @@ -322,7 +321,7 @@ func (h *Header) Populate( consensusHash, appHash, lastResultsHash []byte, proposerAddress Address, round int, - proof vrf.Proof, + proof crypto.Proof, ) { h.Version = version h.ChainID = chainID @@ -641,7 +640,7 @@ func (cs CommitSig) ValidateBasic() error { return errors.New("signature is missing") } if len(cs.Signature) > MaxSignatureSize { - return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize) + return fmt.Errorf("signature is too big %d (max: %d)", len(cs.Signature), MaxSignatureSize) } } @@ -688,6 +687,8 @@ type Commit struct { BlockID BlockID `json:"block_id"` Signatures []CommitSig `json:"signatures"` + AggregatedSignature []byte `json:"aggregated_signature"` + // Memoized in first call to corresponding method. // NOTE: can't memoize in constructor because constructor isn't used for // unmarshaling. diff --git a/types/block_test.go b/types/block_test.go index 5b8536df6..58d409bf7 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -128,9 +128,12 @@ func TestBlockMakePartSetWithEvidence(t *testing.T) { ev := NewMockEvidence(h, time.Now(), 0, voterSet.Voters[0].Address) evList := []Evidence{ev} - partSet := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList).MakePartSet(512) + block := MakeBlock(h, []Tx{Tx("Hello World")}, commit, evList) + bz, _ := cdc.MarshalBinaryLengthPrefixed(block) + blockSize := len(bz) + partSet := block.MakePartSet(512) assert.NotNil(t, partSet) - assert.Equal(t, 3, partSet.Total()) + assert.Equal(t, int(math.Ceil(float64(blockSize)/512.0)), partSet.Total()) } func TestBlockHashesTo(t *testing.T) { @@ -385,8 +388,8 @@ func TestBlockMaxDataBytes(t *testing.T) { 0: {-10, 1, 0, true, 0}, 1: {10, 1, 0, true, 0}, 2: {865, 1, 0, true, 0}, - 3: {900, 1, 0, false, 0}, - 4: {901, 1, 0, false, 1}, + 3: {932, 1, 0, false, 0}, + 4: {933, 1, 0, false, 1}, } for i, tc := range testCases { @@ -414,8 +417,8 @@ func TestBlockMaxDataBytesUnknownEvidence(t *testing.T) { 0: {-10, 1, true, 0}, 1: {10, 1, true, 0}, 2: {961, 1, true, 0}, - 3: {999, 1, false, 0}, - 4: {1001, 1, false, 1}, + 3: {1035, 1, false, 0}, + 4: {1036, 1, false, 1}, } for i, tc := range testCases { diff --git a/types/evidence.go b/types/evidence.go index 95bda987e..3bc3dce6d 100644 --- a/types/evidence.go +++ b/types/evidence.go @@ -18,7 +18,7 @@ import ( const ( // MaxEvidenceBytes is a maximum size of any evidence (including amino overhead). - MaxEvidenceBytes int64 = 484 + MaxEvidenceBytes int64 = 548 ) // ErrEvidenceInvalid wraps a piece of evidence and the error denoting how or why it is invalid. diff --git a/types/params.go b/types/params.go index 44d64e473..57792153a 100644 --- a/types/params.go +++ b/types/params.go @@ -98,9 +98,9 @@ func DefaultEvidenceParams() EvidenceParams { } // DefaultValidatorParams returns a default ValidatorParams, which allows -// only ed25519 pubkeys. +// only key type generated by the default init command. func DefaultValidatorParams() ValidatorParams { - return ValidatorParams{[]string{ABCIPubKeyTypeEd25519}} + return ValidatorParams{[]string{ABCIPubKeyTypeEd25519, ABCIPubKeyTypeComposite}} } func (params *ValidatorParams) IsValidPubkeyType(pubkeyType string) bool { diff --git a/types/priv_validator.go b/types/priv_validator.go index 71ac31cb8..abc6b9e74 100644 --- a/types/priv_validator.go +++ b/types/priv_validator.go @@ -5,9 +5,10 @@ import ( "errors" "fmt" + "github.com/tendermint/tendermint/crypto/composite" + "github.com/tendermint/tendermint/crypto" "github.com/tendermint/tendermint/crypto/ed25519" - "github.com/tendermint/tendermint/crypto/vrf" ) // PrivValidator defines the functionality of a local Tendermint validator @@ -18,7 +19,7 @@ type PrivValidator interface { SignVote(chainID string, vote *Vote) error SignProposal(chainID string, proposal *Proposal) error - GenerateVRFProof(message []byte) (vrf.Proof, error) + GenerateVRFProof(message []byte) (crypto.Proof, error) } //---------------------------------------- @@ -61,7 +62,7 @@ type MockPV struct { } func NewMockPV() MockPV { - return MockPV{ed25519.GenPrivKey(), false, false} + return MockPV{composite.GenPrivKey(), false, false} } // NewMockPVWithParams allows one to create a MockPV instance, but with finer @@ -73,6 +74,9 @@ func NewMockPVWithParams(privKey crypto.PrivKey, breakProposalSigning, breakVote // Implements PrivValidator. func (pv MockPV) GetPubKey() (crypto.PubKey, error) { + //signKey := bls.GenPrivKey() + //vrfKey := ed25519.GenPrivKey() + //return composite.NewPrivKeyComposite(signKey, vrfKey).PubKey(), nil return pv.PrivKey.PubKey(), nil } @@ -107,12 +111,8 @@ func (pv MockPV) SignProposal(chainID string, proposal *Proposal) error { } // Implements PrivValidator. -func (pv MockPV) GenerateVRFProof(message []byte) (vrf.Proof, error) { - privKey, ok := pv.PrivKey.(ed25519.PrivKeyEd25519) - if !ok { - return nil, NewErrUnsupportedKey("ed25519") - } - return vrf.Prove(privKey, message) +func (pv MockPV) GenerateVRFProof(message []byte) (crypto.Proof, error) { + return pv.PrivKey.VRFProve(message) } // String returns a string representation of the MockPV. diff --git a/types/proposal.go b/types/proposal.go index 065dfbbbd..255d0071f 100644 --- a/types/proposal.go +++ b/types/proposal.go @@ -72,7 +72,7 @@ func (p *Proposal) ValidateBasic() error { return errors.New("signature is missing") } if len(p.Signature) > MaxSignatureSize { - return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize) + return fmt.Errorf("signature is too big %d (max: %d)", len(p.Signature), MaxSignatureSize) } return nil } diff --git a/types/protobuf.go b/types/protobuf.go index bb59025ff..40a95bb11 100644 --- a/types/protobuf.go +++ b/types/protobuf.go @@ -7,7 +7,9 @@ import ( abci "github.com/tendermint/tendermint/abci/types" "github.com/tendermint/tendermint/crypto" + "github.com/tendermint/tendermint/crypto/composite" "github.com/tendermint/tendermint/crypto/ed25519" + cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" "github.com/tendermint/tendermint/crypto/secp256k1" "github.com/tendermint/tendermint/crypto/sr25519" ) @@ -21,6 +23,7 @@ const ( ) const ( + ABCIPubKeyTypeComposite = "composite" ABCIPubKeyTypeEd25519 = "ed25519" ABCIPubKeyTypeSr25519 = "sr25519" ABCIPubKeyTypeSecp256k1 = "secp256k1" @@ -29,6 +32,7 @@ const ( // TODO: Make non-global by allowing for registration of more pubkey types var ABCIPubKeyTypesToAminoNames = map[string]string{ + ABCIPubKeyTypeComposite: composite.PubKeyCompositeAminoName, ABCIPubKeyTypeEd25519: ed25519.PubKeyAminoName, ABCIPubKeyTypeSr25519: sr25519.PubKeyAminoName, ABCIPubKeyTypeSecp256k1: secp256k1.PubKeyAminoName, @@ -101,6 +105,15 @@ func (tm2pb) ValidatorUpdate(val *Validator) abci.ValidatorUpdate { // TODO: add cases when new pubkey types are added to crypto func (tm2pb) PubKey(pubKey crypto.PubKey) abci.PubKey { switch pk := pubKey.(type) { + case composite.PubKeyComposite: + b, err := cryptoamino.PubKeyToBytes(pk) + if err != nil { + panic(fmt.Sprintf("failed to serialize the composite public key: %+v, %s", pk, err)) + } + return abci.PubKey{ + Type: ABCIPubKeyTypeComposite, + Data: b, + } case ed25519.PubKeyEd25519: return abci.PubKey{ Type: ABCIPubKeyTypeEd25519, @@ -195,11 +208,25 @@ var PB2TM = pb2tm{} type pb2tm struct{} func (pb2tm) PubKey(pubKey abci.PubKey) (crypto.PubKey, error) { + pk, err := PB2TM.restorePubKey(pubKey) + if err != nil { + return nil, err + } + return pk, nil +} + +func (pb2tm) restorePubKey(pubKey abci.PubKey) (crypto.PubKey, error) { switch pubKey.Type { + case ABCIPubKeyTypeComposite: + pk, err := cryptoamino.PubKeyFromBytes(pubKey.Data) + if err != nil { + return nil, err + } + return pk, nil case ABCIPubKeyTypeEd25519: if len(pubKey.Data) != ed25519.PubKeyEd25519Size { - return nil, fmt.Errorf("invalid size for PubKeyEd25519. Got %d, expected %d", - len(pubKey.Data), ed25519.PubKeyEd25519Size) + return nil, fmt.Errorf("invalid size for PubKeyEd25519. Got %d, expected %d: %+v", + len(pubKey.Data), ed25519.PubKeyEd25519Size, pubKey) } var pk ed25519.PubKeyEd25519 copy(pk[:], pubKey.Data) diff --git a/types/protobuf_test.go b/types/protobuf_test.go index 469393aae..998b5d73d 100644 --- a/types/protobuf_test.go +++ b/types/protobuf_test.go @@ -4,6 +4,9 @@ import ( "testing" "time" + "github.com/tendermint/tendermint/crypto/bls" + "github.com/tendermint/tendermint/crypto/composite" + "github.com/golang/protobuf/proto" // nolint: staticcheck // still used by gogoproto "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -20,8 +23,10 @@ import ( func TestABCIPubKey(t *testing.T) { pkEd := ed25519.GenPrivKey().PubKey() pkSecp := secp256k1.GenPrivKey().PubKey() + pkComposite := composite.NewPrivKeyComposite(bls.GenPrivKey(), ed25519.GenPrivKey()).PubKey() testABCIPubKey(t, pkEd, ABCIPubKeyTypeEd25519) testABCIPubKey(t, pkSecp, ABCIPubKeyTypeSecp256k1) + testABCIPubKey(t, pkComposite, ABCIPubKeyTypeComposite) } func testABCIPubKey(t *testing.T, pk crypto.PubKey, typeStr string) { @@ -151,10 +156,11 @@ func TestABCIEvidence(t *testing.T) { type pubKeyEddie struct{} -func (pubKeyEddie) Address() Address { return []byte{} } -func (pubKeyEddie) Bytes() []byte { return []byte{} } -func (pubKeyEddie) VerifyBytes(msg []byte, sig []byte) bool { return false } -func (pubKeyEddie) Equals(crypto.PubKey) bool { return false } +func (pubKeyEddie) Address() Address { return []byte{} } +func (pubKeyEddie) Bytes() []byte { return []byte{} } +func (pubKeyEddie) VerifyBytes(msg []byte, sig []byte) bool { return false } +func (pubKeyEddie) VRFVerify(proof crypto.Proof, msg []byte) (crypto.Output, error) { return nil, nil } +func (pubKeyEddie) Equals(crypto.PubKey) bool { return false } func TestABCIValidatorFromPubKeyAndPower(t *testing.T) { pubkey := ed25519.GenPrivKey().PubKey() diff --git a/types/signable.go b/types/signable.go index 074654cc5..2a2876bc2 100644 --- a/types/signable.go +++ b/types/signable.go @@ -1,6 +1,7 @@ package types import ( + "github.com/tendermint/tendermint/crypto/bls" "github.com/tendermint/tendermint/crypto/ed25519" tmmath "github.com/tendermint/tendermint/libs/math" ) @@ -9,7 +10,7 @@ var ( // MaxSignatureSize is a maximum allowed signature size for the Proposal // and Vote. // XXX: secp256k1 does not have Size nor MaxSize defined. - MaxSignatureSize = tmmath.MaxInt(ed25519.SignatureSize, 64) + MaxSignatureSize = tmmath.MaxInt(tmmath.MaxInt(ed25519.SignatureSize, bls.SignatureSize), 64) ) // Signable is an interface for all signable things. diff --git a/types/vote.go b/types/vote.go index 533cf7d5f..0cd9b4151 100644 --- a/types/vote.go +++ b/types/vote.go @@ -13,7 +13,7 @@ import ( const ( // MaxVoteBytes is a maximum vote size (including amino overhead). - MaxVoteBytes int64 = 223 + MaxVoteBytes int64 = 255 nilVoteStr string = "nil-Vote" ) @@ -168,7 +168,7 @@ func (vote *Vote) ValidateBasic() error { return errors.New("signature is missing") } if len(vote.Signature) > MaxSignatureSize { - return fmt.Errorf("signature is too big (max: %d)", MaxSignatureSize) + return fmt.Errorf("signature is too big %d (max: %d)", len(vote.Signature), MaxSignatureSize) } return nil } diff --git a/types/voter_set_test.go b/types/voter_set_test.go index e7a4b8399..c90a4fb1a 100644 --- a/types/voter_set_test.go +++ b/types/voter_set_test.go @@ -9,7 +9,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/tendermint/tendermint/crypto/vrf" tmtime "github.com/tendermint/tendermint/types/time" ) @@ -194,7 +193,8 @@ func findLargestStakingPowerGap(t *testing.T, loopCount int, minMaxRate int, max proposer := valSet.SelectProposer(hash, int64(i), 0) message := MakeRoundHash(hash, int64(i), 0) proof, _ := privMap[proposer.Address.String()].GenerateVRFProof(message) - hash, _ = vrf.ProofToHash(proof) + pubKey, _ := privMap[proposer.Address.String()].GetPubKey() + hash, _ = pubKey.VRFVerify(proof, message) totalVoters += voterSet.Size() } largestGap := float64(0) @@ -320,7 +320,8 @@ func electVotersForLoop(t *testing.T, hash []byte, valSet *ValidatorSet, privMap proposer := valSet.SelectProposer(hash, int64(i), 0) message := MakeRoundHash(hash, int64(i), 0) proof, _ := privMap[proposer.Address.String()].GenerateVRFProof(message) - hash, _ = vrf.ProofToHash(proof) + pubKey, _ := privMap[proposer.Address.String()].GetPubKey() + hash, _ = pubKey.VRFVerify(proof, message) } t.Logf("[accuracy=%f] voters=%d, fault=%d, avg byzantines=%f", accuracyFromElectionPrecision(accuracy), totalVoters/loopCount, byzantineFault, float64(totalByzantines)/float64(loopCount))