From 07a894b9cef5ca70ebe64b0c494f2d2398ec75fc Mon Sep 17 00:00:00 2001 From: garian <55307968+Mdaiki0730@users.noreply.github.com> Date: Wed, 30 Nov 2022 12:18:53 +0900 Subject: [PATCH] Add validation of the ValidatorsHash, Round and Proof (#510) * Add ProofLength to vrf interface * Add validation of the ValidatorsHash, Round and Proof * Add test case of the ValidatorsHash, Round and Proof * Fix makeRandHeader to generate rand proof * Make libsodium compatible with M1 Mac * Fix to run test with correct Header.Proof * Resolve e2e test failure due to build tag diff * Change ProofLength() to const ProofSize * Separate the diff with tendermint down into the code * Change e2e test build from libsodium to r2ishiguro * Revert e2e test build and Change runner build from default to libsodium * Fix e2e test actions bug * Add build tag option to test proccess * Use ProofSize from coniks * Remove unnecessary upload --- .github/workflows/e2e.yml | 3 +++ crypto/vrf/README.md | 3 +++ crypto/vrf/internal/vrf/vrf.go | 2 ++ crypto/vrf/vrf_coniks.go | 2 ++ crypto/vrf/vrf_libsodium.go | 2 ++ crypto/vrf/vrf_r2ishiguro.go | 2 ++ evidence/pool_test.go | 13 +++++++------ evidence/verify_test.go | 3 +-- test/e2e/Makefile | 4 ++-- test/e2e/runner/test.go | 2 +- types/block.go | 17 ++++++++++++++--- types/block_test.go | 21 ++++++++++++++++++--- types/validation.go | 13 +++++++++++++ 13 files changed, 70 insertions(+), 17 deletions(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 4fc7b5bcf..47b323963 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -122,6 +122,9 @@ jobs: docker load -i ${{ needs.e2e-build.outputs.CACHE_FILE }} if: "env.GIT_DIFF != ''" + - name: Build libsodium + run: make libsodium + - name: Build e2e runner working-directory: test/e2e run: make runner diff --git a/crypto/vrf/README.md b/crypto/vrf/README.md index 6caaf6477..19f47c147 100644 --- a/crypto/vrf/README.md +++ b/crypto/vrf/README.md @@ -25,17 +25,20 @@ Use `func init()` with `build` option * `//go:build libsodium` * `// +build !libsodium,!coniks` * `func init() { defaultVrf = newVrfEd25519r2ishiguro() }` + * `const ProofSize = 81` * vrf_r2ishiguro.go * (coniks) * `//go:build coniks` * `// +build coniks` * `func init() { defaultVrf = newVrfEd25519coniks() }` + * `const ProofSize = 96` * vrf_coniks.go * vrf_coniks_test.go * (libsodium) * `//go:build libsodium` * `// +build libsodium` * `func init() { defaultVrf = newVrfEd25519libsodium() }` + * `const ProofSize = int(libsodium.PROOFBYTES)` * vrf_libsodium.go * vrf_libsodium_test.go diff --git a/crypto/vrf/internal/vrf/vrf.go b/crypto/vrf/internal/vrf/vrf.go index aebbde992..48c6694cd 100644 --- a/crypto/vrf/internal/vrf/vrf.go +++ b/crypto/vrf/internal/vrf/vrf.go @@ -8,6 +8,8 @@ package vrf #cgo CFLAGS: -Wall -std=c99 #cgo darwin,amd64 CFLAGS: -I./sodium/darwin_amd64/include/ #cgo darwin,amd64 LDFLAGS: -L./sodium/darwin_amd64/lib -lsodium +#cgo darwin,arm64 CFLAGS: -I./sodium/darwin_arm64/include/ +#cgo darwin,arm64 LDFLAGS: -L./sodium/darwin_arm64/lib -lsodium #cgo linux,amd64 CFLAGS: -I./sodium/linux_amd64/include #cgo linux,amd64 LDFLAGS: -L./sodium/linux_amd64/lib -lsodium #cgo linux,arm64 CFLAGS: -I./sodium/linux_arm64/include diff --git a/crypto/vrf/vrf_coniks.go b/crypto/vrf/vrf_coniks.go index bb6126b91..1d29a8015 100644 --- a/crypto/vrf/vrf_coniks.go +++ b/crypto/vrf/vrf_coniks.go @@ -19,6 +19,8 @@ func init() { defaultVrf = newVrfEd25519coniks() } +const ProofSize = coniks.ProofSize + func newVrfEd25519coniks() *vrfEd25519coniks { return &vrfEd25519coniks{nil, nil} } diff --git a/crypto/vrf/vrf_libsodium.go b/crypto/vrf/vrf_libsodium.go index 033ca496f..babab44f8 100644 --- a/crypto/vrf/vrf_libsodium.go +++ b/crypto/vrf/vrf_libsodium.go @@ -18,6 +18,8 @@ func init() { defaultVrf = newVrfEd25519libsodium() } +const ProofSize = int(libsodium.PROOFBYTES) + func newVrfEd25519libsodium() vrfEd25519libsodium { return vrfEd25519libsodium{} } diff --git a/crypto/vrf/vrf_r2ishiguro.go b/crypto/vrf/vrf_r2ishiguro.go index 07b9b2c61..b89337a17 100644 --- a/crypto/vrf/vrf_r2ishiguro.go +++ b/crypto/vrf/vrf_r2ishiguro.go @@ -19,6 +19,8 @@ func init() { } } +const ProofSize = 81 + func newVrfEd25519r2ishiguro() vrfEd25519r2ishiguro { return vrfEd25519r2ishiguro{} } diff --git a/evidence/pool_test.go b/evidence/pool_test.go index 8f696a640..38b0b8f86 100644 --- a/evidence/pool_test.go +++ b/evidence/pool_test.go @@ -6,6 +6,7 @@ import ( "testing" "time" + "github.com/line/ostracon/crypto" "github.com/line/ostracon/crypto/bls" "github.com/line/ostracon/crypto/composite" "github.com/line/ostracon/crypto/ed25519" @@ -325,12 +326,11 @@ func TestLightClientAttackEvidenceLifecycle(t *testing.T) { func TestRecoverPendingEvidence(t *testing.T) { height := int64(10) val := types.NewMockPV(types.PrivKeyComposite) // TODO 🏺 need to test by all key types - valAddress := val.PrivKey.PubKey().Address() evidenceDB := dbm.NewMemDB() stateStore := initializeValidatorState(val, height) state, err := stateStore.Load() require.NoError(t, err) - blockStore := initializeBlockStore(dbm.NewMemDB(), state, valAddress) + blockStore := initializeBlockStore(dbm.NewMemDB(), state, val.PrivKey) // create previous pool and populate it pool, err := evidence.NewPool(evidenceDB, stateStore, blockStore) require.NoError(t, err) @@ -424,13 +424,15 @@ func initializeValidatorState(privVal types.PrivValidator, height int64) sm.Stor // initializeBlockStore creates a block storage and populates it w/ a dummy // block at +height+. -func initializeBlockStore(db dbm.DB, state sm.State, valAddr []byte) *store.BlockStore { +func initializeBlockStore(db dbm.DB, state sm.State, valPrivkey crypto.PrivKey) *store.BlockStore { blockStore := store.NewBlockStore(db) + valAddr := valPrivkey.PubKey().Address() for i := int64(1); i <= state.LastBlockHeight; i++ { round := int32(0) lastCommit := makeCommit(i-1, valAddr) - proof := state.MakeHashMessage(round) + message := state.MakeHashMessage(round) + proof, _ := valPrivkey.VRFProve(message) block, _ := state.MakeBlock(i, []types.Tx{}, lastCommit, nil, state.Validators.SelectProposer(proof, i, round).Address, round, proof) block.Header.Time = defaultEvidenceTime.Add(time.Duration(i) * time.Minute) @@ -457,11 +459,10 @@ func makeCommit(height int64, valAddr []byte) *types.Commit { func defaultTestPool(height int64) (*evidence.Pool, types.MockPV) { val := types.NewMockPV(types.PrivKeyComposite) // TODO 🏺 need to test by all key types - valAddress := val.PrivKey.PubKey().Address() evidenceDB := dbm.NewMemDB() stateStore := initializeValidatorState(val, height) state, _ := stateStore.Load() - blockStore := initializeBlockStore(dbm.NewMemDB(), state, valAddress) + blockStore := initializeBlockStore(dbm.NewMemDB(), state, val.PrivKey) pool, err := evidence.NewPool(evidenceDB, stateStore, blockStore) if err != nil { panic("test evidence pool could not be created") diff --git a/evidence/verify_test.go b/evidence/verify_test.go index d8a3b5fbc..573b3b637 100644 --- a/evidence/verify_test.go +++ b/evidence/verify_test.go @@ -8,8 +8,6 @@ import ( "github.com/line/ostracon/light" - "github.com/coniks-sys/coniks-go/crypto/vrf" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,6 +15,7 @@ import ( "github.com/line/ostracon/crypto" "github.com/line/ostracon/crypto/tmhash" + "github.com/line/ostracon/crypto/vrf" "github.com/line/ostracon/evidence" "github.com/line/ostracon/evidence/mocks" "github.com/line/ostracon/libs/log" diff --git a/test/e2e/Makefile b/test/e2e/Makefile index dc9f2f30a..aafb91839 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -15,9 +15,9 @@ maverick: go build -o build/maverick -tags libsodium,badgerdb,boltdb,cleveldb,rocksdb ../maverick generator: - go build -o build/generator ./generator + go build -o build/generator -tags libsodium ./generator runner: - go build -o build/runner ./runner + go build -o build/runner -tags libsodium ./runner .PHONY: all node docker generator maverick runner diff --git a/test/e2e/runner/test.go b/test/e2e/runner/test.go index 0efd80d64..2b8de16af 100644 --- a/test/e2e/runner/test.go +++ b/test/e2e/runner/test.go @@ -15,5 +15,5 @@ func Test(testnet *e2e.Testnet) error { return err } - return execVerbose("go", "test", "-count", "1", "./tests/...") + return execVerbose("go", "test", "-count", "1", "-tags", "libsodium", "./tests/...") } diff --git a/types/block.go b/types/block.go index 58824b3c5..6eff57dd7 100644 --- a/types/block.go +++ b/types/block.go @@ -455,10 +455,9 @@ func (h Header) ValidateBasic() error { // Basic validation of hashes related to application data. // Will validate fully against state in state#ValidateBlock. - if err := ValidateHash(h.VotersHash); err != nil { - return fmt.Errorf("wrong VotersHash: %v", err) + if err := ValidateHash(h.ValidatorsHash); err != nil { + return fmt.Errorf("wrong ValidatorsHash: %v", err) } - // TODO When we add `Header.ValidatorsHash` in a future commit, we have to add a similar check here. if err := ValidateHash(h.NextValidatorsHash); err != nil { return fmt.Errorf("wrong NextValidatorsHash: %v", err) } @@ -470,6 +469,18 @@ func (h Header) ValidateBasic() error { return fmt.Errorf("wrong LastResultsHash: %v", err) } + if err := ValidateHash(h.VotersHash); err != nil { + return fmt.Errorf("wrong VotersHash: %v", err) + } + + if h.Round < 0 { + return errors.New("negative Round") + } + + if err := ValidateProof(h.Proof); err != nil { + return fmt.Errorf("wrong Proof: %v", err) + } + return nil } diff --git a/types/block_test.go b/types/block_test.go index 7493d5397..41e2305fe 100644 --- a/types/block_test.go +++ b/types/block_test.go @@ -518,7 +518,9 @@ func TestHeaderHash(t *testing.T) { EvidenceHash: tmhash.Sum([]byte("evidence_hash")), ProposerAddress: crypto.AddressHash([]byte("proposer_address")), Round: 1, - Proof: tmhash.Sum([]byte("proof")), + // The Proof defined here does not depend on the vrf ProofLength, + // but it is a fixed value for the purpose of calculating the Hash value. + Proof: tmhash.Sum([]byte("proof")), }, hexBytesFromString("0368E6F15B6B7BC9DC5B10F36F37D6F867E132A22333F083A11290324274E183")}, {"nil header yields nil", nil, nil}, {"nil VotersHash yields nil", &Header{ @@ -537,7 +539,9 @@ func TestHeaderHash(t *testing.T) { EvidenceHash: tmhash.Sum([]byte("evidence_hash")), ProposerAddress: crypto.AddressHash([]byte("proposer_address")), Round: 1, - Proof: tmhash.Sum([]byte("proof")), + // The Proof defined here does not depend on the vrf ProofLength, + // but it is a fixed value for the purpose of calculating the Hash value. + Proof: tmhash.Sum([]byte("proof")), }, nil}, } for _, tc := range testCases { @@ -638,6 +642,15 @@ func TestHeaderValidateBasic(t *testing.T) { {"Invalid Results Hash", func(header *Header) { header.LastResultsHash = []byte(strings.Repeat("h", invalidHashLength)) }, true}, + {"Negative Round", func(header *Header) { + header.Round = -1 + }, true}, + {"Invalid Proof", func(header *Header) { + header.Proof = make([]byte, vrf.ProofSize-1) + }, true}, + {"Invalid Validators Hash", func(header *Header) { + header.ValidatorsHash = []byte(strings.Repeat("h", invalidHashLength)) + }, true}, } for i, tc := range testCases { tc := tc @@ -660,7 +673,7 @@ func TestHeaderValidateBasic(t *testing.T) { EvidenceHash: tmhash.Sum([]byte("evidence_hash")), ProposerAddress: crypto.AddressHash([]byte("proposer_address")), Round: 1, - Proof: tmhash.Sum([]byte("proof")), + Proof: make([]byte, vrf.ProofSize), } tc.malleateHeader(header) err := header.ValidateBasic() @@ -1268,6 +1281,7 @@ func makeRandHeader() Header { height := tmrand.Int63() randBytes := tmrand.Bytes(tmhash.Size) randAddress := tmrand.Bytes(crypto.AddressSize) + randProof := tmrand.Bytes(vrf.ProofSize) h := Header{ Version: tmversion.Consensus{Block: version.BlockProtocol, App: 1}, ChainID: chainID, @@ -1285,6 +1299,7 @@ func makeRandHeader() Header { EvidenceHash: randBytes, ProposerAddress: randAddress, + Proof: randProof, } return h diff --git a/types/validation.go b/types/validation.go index bb086ce79..4eae08e37 100644 --- a/types/validation.go +++ b/types/validation.go @@ -5,6 +5,7 @@ import ( "time" "github.com/line/ostracon/crypto/tmhash" + "github.com/line/ostracon/crypto/vrf" tmtime "github.com/line/ostracon/types/time" ) @@ -38,3 +39,15 @@ func ValidateHash(h []byte) error { } return nil } + +// ValidateProof returns an error if the proof is not empty, but its +// size != vrf.ProofSize. +func ValidateProof(h []byte) error { + if len(h) > 0 && len(h) != vrf.ProofSize { + return fmt.Errorf("expected size to be %d bytes, got %d bytes", + vrf.ProofSize, + len(h), + ) + } + return nil +}