diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go index d00b82995..54e5b4e2a 100644 --- a/abci/cmd/abci-cli/abci-cli.go +++ b/abci/cmd/abci-cli/abci-cli.go @@ -2,13 +2,17 @@ package main import ( "bufio" + "bytes" "encoding/hex" "errors" "fmt" "io" "os" + "path/filepath" "strings" + "github.com/line/ostracon/config" + "github.com/spf13/cobra" "github.com/line/ostracon/libs/log" @@ -22,6 +26,7 @@ import ( servertest "github.com/line/ostracon/abci/tests/server" "github.com/line/ostracon/abci/types" "github.com/line/ostracon/abci/version" + "github.com/line/ostracon/crypto/encoding" "github.com/line/ostracon/proto/ostracon/crypto" ) @@ -49,6 +54,9 @@ var ( // kvstore flagPersist string + + // staking power for make validator_tx + flagStakingPower int64 ) var RootCmd = &cobra.Command{ @@ -136,11 +144,18 @@ func addQueryFlags() { } func addCounterFlags() { - counterCmd.PersistentFlags().BoolVarP(&flagSerial, "serial", "", false, "enforce incrementing (serial) transactions") + counterCmd.PersistentFlags().BoolVarP(&flagSerial, "serial", "", false, + "enforce incrementing (serial) transactions") } func addKVStoreFlags() { - kvstoreCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", "directory to use for a database") + kvstoreCmd.PersistentFlags().StringVarP(&flagPersist, "persist", "", "", + "directory to use for a database") +} + +func addPersistKVStoreMakeValSetChangeTxFlags() { + kvstoreCmd.PersistentFlags().Int64VarP(&flagStakingPower, "staking_power", "p", int64(10), + "stakin power for ValSetChangeTx") } func addCommands() { @@ -162,6 +177,10 @@ func addCommands() { RootCmd.AddCommand(counterCmd) addKVStoreFlags() RootCmd.AddCommand(kvstoreCmd) + + // for examples of persist_kvstore + addPersistKVStoreMakeValSetChangeTxFlags() + RootCmd.AddCommand(persistKvstoreMakeValSetChangeTxCmd) } var batchCmd = &cobra.Command{ @@ -269,20 +288,28 @@ var queryCmd = &cobra.Command{ var counterCmd = &cobra.Command{ Use: "counter", - Short: "ABCI demo example", - Long: "ABCI demo example", + Short: "ABCI demo example - counter", + Long: "ABCI demo example - counter", Args: cobra.ExactArgs(0), RunE: cmdCounter, } var kvstoreCmd = &cobra.Command{ Use: "kvstore", - Short: "ABCI demo example", - Long: "ABCI demo example", + Short: "ABCI demo example - kvstore", + Long: "ABCI demo example - kvstore", Args: cobra.ExactArgs(0), RunE: cmdKVStore, } +var persistKvstoreMakeValSetChangeTxCmd = &cobra.Command{ + Use: "valset_change_tx", + Short: "persist_kvstore - make ValSetChangeTx", + Long: "persist_kvstore - make ValSetChangeTx", + Args: cobra.ExactArgs(0), + RunE: cmdPersistKVStoreMakeValSetChangeTx, +} + var testCmd = &cobra.Command{ Use: "test", Short: "run integration tests", @@ -685,6 +712,61 @@ func cmdKVStore(cmd *cobra.Command, args []string) error { select {} } +func cmdPersistKVStoreMakeValSetChangeTx(cmd *cobra.Command, args []string) error { + c := config.DefaultConfig() + userHome, err := os.UserHomeDir() + if err != nil { + panic(err) + } + c.SetRoot(filepath.Join(userHome, config.DefaultOstraconDir)) + keyFilePath := c.PrivValidatorKeyFile() + if !tmos.FileExists(keyFilePath) { + return fmt.Errorf("private validator file %s does not exist", keyFilePath) + } + keyFile, err := kvstore.LoadPrivValidatorKeyFile(keyFilePath) + if err != nil { + panic(err) + } + publicKey, err := encoding.PubKeyToProto(keyFile.PubKey) + if err != nil { + panic(err) + } + pubStr, tx := kvstore.MakeValSetChangeTxAndMore(publicKey, flagStakingPower) + { + fmt.Printf("DeliverTxSync: data=%s, tx=%s\n", pubStr, tx) + res, err := client.DeliverTxSync(types.RequestDeliverTx{Tx: []byte(tx)}) + if err != nil { + return err + } + printResponse(cmd, args, response{ + Code: res.Code, + Data: res.Data, + Info: res.Info, + Log: res.Log, + }) + } + { + fmt.Printf("QuerySync: data=%s\n", pubStr) + res, err := client.QuerySync(types.RequestQuery{Path: "/val", Data: []byte(pubStr)}) + if err != nil { + return err + } + printResponse(cmd, args, response{ + Code: res.Code, + Info: res.Info, + Log: res.Log, + }) + fmt.Printf("original:publicKey:%s\n", publicKey) + validatorUpdate := types.ValidatorUpdate{} + err = types.ReadMessage(bytes.NewReader(res.Value), &validatorUpdate) + if err != nil { + panic(err) + } + fmt.Printf("saved :publicKey:%s\n", validatorUpdate.PubKey) + } + return nil +} + //-------------------------------------------------------------------------------- func printResponse(cmd *cobra.Command, args []string, rsp response) { @@ -702,7 +784,7 @@ func printResponse(cmd *cobra.Command, args []string, rsp response) { } if len(rsp.Data) != 0 { - // Do no print this line when using the commit command + // Do not print this line when using the commit command // because the string comes out as gibberish if cmd.Use != "commit" { fmt.Printf("-> data: %s\n", rsp.Data) diff --git a/abci/example/kvstore/helpers.go b/abci/example/kvstore/helpers.go index 53f3789bc..2ee0ae40f 100644 --- a/abci/example/kvstore/helpers.go +++ b/abci/example/kvstore/helpers.go @@ -1,13 +1,33 @@ package kvstore import ( + "fmt" + "io/ioutil" + "github.com/line/ostracon/abci/types" "github.com/line/ostracon/crypto" "github.com/line/ostracon/crypto/composite" + tmjson "github.com/line/ostracon/libs/json" + tmos "github.com/line/ostracon/libs/os" tmrand "github.com/line/ostracon/libs/rand" + "github.com/line/ostracon/privval" ) -// Generates a default private key for use in an example or test. +// LoadPrivValidatorKeyFile Load private key for use in an example or test. +func LoadPrivValidatorKeyFile(keyFilePath string) (*privval.FilePVKey, error) { + if !tmos.FileExists(keyFilePath) { + return nil, fmt.Errorf("private validator file %s does not exist", keyFilePath) + } + keyJSONBytes, _ := ioutil.ReadFile(keyFilePath) + pvKey := privval.FilePVKey{} + err := tmjson.Unmarshal(keyJSONBytes, &pvKey) + if err != nil { + return nil, fmt.Errorf("error reading PrivValidator key from %v: %v", keyFilePath, err) + } + return &pvKey, nil +} + +// GenDefaultPrivKey Generates a default private key for use in an example or test. func GenDefaultPrivKey() crypto.PrivKey { return composite.GenPrivKey() } diff --git a/abci/example/kvstore/helpers_test.go b/abci/example/kvstore/helpers_test.go new file mode 100644 index 000000000..79aabb30e --- /dev/null +++ b/abci/example/kvstore/helpers_test.go @@ -0,0 +1,43 @@ +package kvstore + +import ( + "io/ioutil" + "testing" + + "github.com/line/ostracon/privval" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestLoadPrivValidatorKeyFile(t *testing.T) { + tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") + require.Nil(t, err) + tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") + require.Nil(t, err) + + { + // does not exist + _, err := LoadPrivValidatorKeyFile("DOES_NOT_EXIST") + require.NotNil(t, err) + require.Contains(t, err.Error(), "does not exist") + } + + { + // error reading since empty + _, err := LoadPrivValidatorKeyFile(tempKeyFile.Name()) + require.NotNil(t, err) + require.Contains(t, err.Error(), "error reading") + } + + expected, err := privval.GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), privval.PrivKeyTypeEd25519) + require.Nil(t, err) + + expected.Save() + + // success + actual, err := LoadPrivValidatorKeyFile(tempKeyFile.Name()) + require.Nil(t, err) + assert.Equal(t, expected.Key.Address, actual.Address) + assert.Equal(t, expected.Key.PrivKey, actual.PrivKey) + assert.Equal(t, expected.Key.PubKey, actual.PubKey) +} diff --git a/abci/example/kvstore/kvstore_test.go b/abci/example/kvstore/kvstore_test.go index 976bcc36f..af1a6b9bc 100644 --- a/abci/example/kvstore/kvstore_test.go +++ b/abci/example/kvstore/kvstore_test.go @@ -6,6 +6,8 @@ import ( "sort" "testing" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/line/ostracon/libs/log" @@ -182,6 +184,9 @@ func TestValUpdates(t *testing.T) { vals2 = kvstore.Validators() valsEqual(t, vals1, vals2) + for _, v := range vals2 { + existInPersistStore(t, kvstore, v) + } } func makeApplyBlock( @@ -210,6 +215,27 @@ func makeApplyBlock( } +func existInPersistStore(t *testing.T, kvstore types.Application, v types.ValidatorUpdate) { + // success + pubkeyStr, _ := MakeValSetChangeTxAndMore(v.PubKey, v.Power) + resQuery := kvstore.Query(types.RequestQuery{Path: "/val", Data: []byte(pubkeyStr)}) + assert.False(t, resQuery.IsErr(), resQuery) + assert.Regexp(t, "^key=.+, validatorUpdate.PubKey=.+, validatorUpdate.Power=.+$", resQuery.Log) + // failures + { + // default Query: does not exist + r := kvstore.Query(types.RequestQuery{Path: "/val_", Data: []byte(pubkeyStr)}) + assert.False(t, r.IsErr(), r) + assert.Contains(t, r.Log, "does not exist") + } + { + // Query: does not exist + r := kvstore.Query(types.RequestQuery{Path: "/val", Data: []byte{}}) + assert.False(t, r.IsErr(), r) + assert.Contains(t, r.Log, "cannot get") + } +} + // order doesn't matter func valsEqual(t *testing.T, vals1, vals2 []types.ValidatorUpdate) { if len(vals1) != len(vals2) { diff --git a/abci/example/kvstore/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index bc269bf37..fcbe676e5 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -113,6 +113,19 @@ func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) (res resQuery.Key = reqQuery.Data resQuery.Value = value + + if value == nil { + resQuery.Log = "cannot get" + return + } + validatorUpdate := types.ValidatorUpdate{} + err = types.ReadMessage(bytes.NewReader(resQuery.Value), &validatorUpdate) + if err != nil { + panic(err) + } + pubKey, _ := cryptoenc.PubKeyFromProto(&validatorUpdate.PubKey) + resQuery.Log = fmt.Sprintf("key=%s, validatorUpdate.PubKey=%v, validatorUpdate.Power=%d", + resQuery.Key, pubKey, validatorUpdate.Power) return default: return app.app.Query(reqQuery) @@ -206,12 +219,17 @@ func (app *PersistentKVStoreApplication) Validators() (validators []types.Valida } func MakeValSetChangeTx(pubkey pc.PublicKey, power int64) []byte { + _, tx := MakeValSetChangeTxAndMore(pubkey, power) + return []byte(tx) +} + +func MakeValSetChangeTxAndMore(pubkey pc.PublicKey, power int64) (string, string) { pkBytes, err := pubkey.Marshal() if err != nil { panic(err) } pubStr := base64.StdEncoding.EncodeToString(pkBytes) - return []byte(fmt.Sprintf("val:%s!%d", pubStr, power)) + return pubStr, fmt.Sprintf("val:%s!%d", pubStr, power) } func isValidatorTx(tx []byte) bool { @@ -219,7 +237,8 @@ func isValidatorTx(tx []byte) bool { } // format is "val:pubkey!power" -// pubkey is a base64-encoded 32-byte ed25519 key +// pubkey is a base64-encoded proto.ostracon.crypto.PublicKey bytes +// See MakeValSetChangeTx func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.ResponseDeliverTx { tx = tx[len(ValidatorSetChangePrefix):] @@ -237,20 +256,20 @@ func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.Respon if err != nil { return types.ResponseDeliverTx{ Code: code.CodeTypeEncodingError, - Log: fmt.Sprintf("Pubkey (%s) is invalid base64", pubkeyS)} + Log: fmt.Sprintf("pubkeyS (%s) is invalid base64", pubkeyS)} } var pkProto pc.PublicKey err = pkProto.Unmarshal(pkBytes) if err != nil { return types.ResponseDeliverTx{ Code: code.CodeTypeEncodingError, - Log: fmt.Sprintf("Pubkey (%s) is invalid binary", pubkeyS)} + Log: fmt.Sprintf("pkBytes (%x) is invalid binary", pkBytes)} } pubkey, err := cryptoenc.PubKeyFromProto(&pkProto) if err != nil { return types.ResponseDeliverTx{ Code: code.CodeTypeEncodingError, - Log: fmt.Sprintf("Pubkey (%s) is invalid binary", pubkeyS)} + Log: fmt.Sprintf("pkProto (%s) is invalid binary", pkProto)} } // decode the power @@ -266,6 +285,7 @@ func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.Respon } // add, update, or remove a validator +// See MakeValSetChangeTx func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate) types.ResponseDeliverTx { pubkey, err := cryptoenc.PubKeyFromProto(&v.PubKey) if err != nil { @@ -273,7 +293,8 @@ func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate Code: code.CodeTypeEncodingError, Log: fmt.Sprintf("Error encoding Public Key: %s", err)} } - key := []byte("val:" + string(pubkey.Bytes())) + pubStr, _ := MakeValSetChangeTxAndMore(v.PubKey, v.Power) + key := []byte("val:" + pubStr) if v.Power == 0 { // remove validator @@ -282,7 +303,6 @@ func (app *PersistentKVStoreApplication) updateValidator(v types.ValidatorUpdate panic(err) } if !hasKey { - pubStr := base64.StdEncoding.EncodeToString(pubkey.Bytes()) return types.ResponseDeliverTx{ Code: code.CodeTypeUnauthorized, Log: fmt.Sprintf("Cannot remove non-existent validator %s", pubStr)} diff --git a/abci/example/kvstore/tool/make_val_set_change_tx.go b/abci/example/kvstore/tool/make_val_set_change_tx.go new file mode 100644 index 000000000..8f9f554c6 --- /dev/null +++ b/abci/example/kvstore/tool/make_val_set_change_tx.go @@ -0,0 +1,50 @@ +package main + +import ( + "flag" + "fmt" + "net/url" + "os" + "path/filepath" + + "github.com/line/ostracon/abci/example/kvstore" + "github.com/line/ostracon/config" + "github.com/line/ostracon/crypto/encoding" +) + +func main() { + c := config.DefaultConfig() + userHome, err := os.UserHomeDir() + if err != nil { + panic(err) + } + c.SetRoot(filepath.Join(userHome, config.DefaultOstraconDir)) + keyFilePath := c.PrivValidatorKeyFile() + var flagKeyFilePath = flag.String("priv-key", keyFilePath, "priv val key file path") + var flagStakingPower = flag.Int64("staking", 10, "staking power for priv valedator") + flag.Parse() + keyFile, err := kvstore.LoadPrivValidatorKeyFile(*flagKeyFilePath) + if err != nil { + panic(err) + } + publicKey, err := encoding.PubKeyToProto(keyFile.PubKey) + if err != nil { + panic(err) + } + pubStr, tx := kvstore.MakeValSetChangeTxAndMore(publicKey, *flagStakingPower) + { + fmt.Println("\n# Send tx of ValSetChangeTx for persist_kvstore") + fmt.Println("# See: persist_kvstore.go#DeliveredTx") + broadcastTxCommit := fmt.Sprintf("curl -s 'localhost:26657/broadcast_tx_commit?tx=\"%s\"'", + url.QueryEscape(tx)) + fmt.Println(broadcastTxCommit) + } + { + fmt.Println("\n# Query tx of ValSetChangeTx for persist_kvstore") + fmt.Println("# See: persist_kvstore.go#Query") + query := fmt.Sprintf("curl -s 'localhost:26657/abci_query?path=\"%s\"&data=\"%s\"'", + url.QueryEscape("/val"), + url.QueryEscape(pubStr)) + fmt.Println(query) + } +} diff --git a/abci/example/kvstore/tool/make_val_set_change_tx.sh b/abci/example/kvstore/tool/make_val_set_change_tx.sh new file mode 100644 index 000000000..565e7697c --- /dev/null +++ b/abci/example/kvstore/tool/make_val_set_change_tx.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +IFS_BACK=$IFS +IFS=$'\r\n' + +cd `dirname $0` + +# updator validator with default parameter +commands=`go run make_val_set_change_tx.go --staking=10 --priv-key=${HOME}/.ostracon/config/priv_validator_key.json` +# remove validator tx +commands=`go run make_val_set_change_tx.go --staking=0` +# update validator tx +commands=`go run make_val_set_change_tx.go` +for command in ${commands[@]}; do + if [[ "$command" =~ \# ]]; then + echo $command + else + echo $command + eval $command + RET=$? + echo "" + if [ ${RET} -ne 0 ]; then + echo "ERROR: Result Code of calling RPC: ${RET}" + exit ${RET} + fi + fi +done + +IFS=$IFS_BACK diff --git a/cmd/ostracon/main.go b/cmd/ostracon/main.go index 17736aaf6..88bd4a6bd 100644 --- a/cmd/ostracon/main.go +++ b/cmd/ostracon/main.go @@ -44,7 +44,11 @@ func main() { rootCmd.AddCommand(cmd.NewInitCmd()) rootCmd.AddCommand(cmd.NewRunNodeCmd(nodeFunc)) - cmd := cli.PrepareBaseCmd(rootCmd, "OC", os.ExpandEnv(filepath.Join("$HOME", cfg.DefaultOstraconDir))) + userHome, err := os.UserHomeDir() + if err != nil { + panic(err) + } + cmd := cli.PrepareBaseCmd(rootCmd, "OC", filepath.Join(userHome, cfg.DefaultOstraconDir)) if err := cmd.Execute(); err != nil { panic(err) } diff --git a/crypto/bls/bls.go b/crypto/bls/bls.go index 680fbd0c3..48ba34924 100644 --- a/crypto/bls/bls.go +++ b/crypto/bls/bls.go @@ -217,7 +217,7 @@ func (pubKey PubKey) VRFVerify(proof crypto.Proof, seed []byte) (crypto.Output, } func (pubKey PubKey) String() string { - return fmt.Sprintf("PubKey{%X}", pubKey[:]) + return fmt.Sprintf("PubKeyBLS12{%X}", pubKey[:]) } func (pubKey PubKey) Equals(other crypto.PubKey) bool { diff --git a/crypto/composite/composite.go b/crypto/composite/composite.go index 60994ba0f..e32f2bae1 100644 --- a/crypto/composite/composite.go +++ b/crypto/composite/composite.go @@ -35,6 +35,17 @@ type PubKey struct { VrfKey crypto.PubKey `json:"vrf"` } +func PubKeyFromBytes(bz []byte) PubKey { + if len(bz) != bls.PubKeySize+ed25519.PubKeySize { + panic(fmt.Sprintf("Wrong PubKey bytes size: %d", len(bz))) + } + sign := bls.PubKey{} + copy(sign[:], bz[:bls.PubKeySize]) + vrf := ed25519.PubKey(make([]byte, ed25519.PubKeySize)) + copy(vrf, bz[bls.PubKeySize:]) + return PubKey{SignKey: sign, VrfKey: vrf} +} + func (pk *PubKey) Identity() crypto.PubKey { return pk.VrfKey } @@ -44,9 +55,9 @@ func (pk PubKey) Address() crypto.Address { } func (pk PubKey) Bytes() []byte { - msg := bytes.NewBuffer(pk.SignKey.Bytes()) - msg.Write(pk.VrfKey.Bytes()) - return msg.Bytes() + bz := bytes.NewBuffer(pk.SignKey.Bytes()) + bz.Write(pk.VrfKey.Bytes()) + return bz.Bytes() } func (pk PubKey) VerifySignature(msg []byte, sig []byte) bool { @@ -80,12 +91,27 @@ func NewPrivKeyComposite(sign crypto.PrivKey, vrf crypto.PrivKey) *PrivKey { return &PrivKey{SignKey: sign, VrfKey: vrf} } +// PrivKeyFromBytes depends on PrivKey.Bytes +// See PrivKey.Bytes +func PrivKeyFromBytes(bz []byte) *PrivKey { + if len(bz) != bls.PrivKeySize+ed25519.PrivateKeySize { + panic(fmt.Sprintf("Wrong PrivKey bytes size: %d", len(bz))) + } + sign := bls.PrivKey{} + copy(sign[:], bz[:bls.PrivKeySize]) + vrf := ed25519.PrivKey(make([]byte, ed25519.PrivateKeySize)) + copy(vrf, bz[bls.PrivKeySize:]) + return &PrivKey{SignKey: sign, VrfKey: vrf} +} + func (sk PrivKey) Identity() crypto.PrivKey { return sk.VrfKey } func (sk PrivKey) Bytes() []byte { - return sk.Identity().Bytes() + bz := bytes.NewBuffer(sk.SignKey.Bytes()) + bz.Write(sk.VrfKey.Bytes()) + return bz.Bytes() } func (sk PrivKey) Sign(msg []byte) ([]byte, error) { diff --git a/crypto/composite/composite_test.go b/crypto/composite/composite_test.go index 4ebd19fbd..3abcac202 100644 --- a/crypto/composite/composite_test.go +++ b/crypto/composite/composite_test.go @@ -7,6 +7,8 @@ import ( "fmt" "testing" + "github.com/stretchr/testify/assert" + tmjson "github.com/line/ostracon/libs/json" "github.com/line/ostracon/crypto/bls" @@ -14,11 +16,24 @@ import ( "github.com/line/ostracon/crypto/ed25519" ) +func TestGenPrivKey(t *testing.T) { + sk := composite.GenPrivKey() + sign := sk.SignKey + vrf := sk.VrfKey + compositeKey := composite.NewPrivKeyComposite(sign, vrf) + assert.Equal(t, sk, compositeKey) + bz := compositeKey.Bytes() + assert.Equal(t, sk, composite.PrivKeyFromBytes(bz)) +} + func TestPrivKeyComposite_Bytes(t *testing.T) { sign := bls.GenPrivKey() vrf := ed25519.GenPrivKey() sk := composite.NewPrivKeyComposite(sign, vrf) - sk.Bytes() + compositeKey := composite.PrivKeyFromBytes(sk.Bytes()) + assert.Equal(t, sign, compositeKey.SignKey) + assert.Equal(t, vrf, compositeKey.VrfKey) + assert.Equal(t, sk, compositeKey) } func TestPrivKeyComposite_Equals(t *testing.T) { @@ -56,7 +71,11 @@ func TestPrivKeyComposite_PubKey(t *testing.T) { sign := bls.GenPrivKey() vrf := ed25519.GenPrivKey() sk := composite.NewPrivKeyComposite(sign, vrf) - sk.PubKey() + pk := sk.PubKey() + compositeKey := composite.PubKeyFromBytes(pk.Bytes()) + assert.Equal(t, sign.PubKey(), compositeKey.SignKey) + assert.Equal(t, vrf.PubKey(), compositeKey.VrfKey) + assert.Equal(t, pk, compositeKey) } func TestPrivKeyComposite_Sign(t *testing.T) { @@ -105,7 +124,10 @@ func TestPubKeyComposite_Bytes(t *testing.T) { vrf := ed25519.GenPrivKey() sk := composite.NewPrivKeyComposite(sign, vrf) pk := sk.PubKey().(composite.PubKey) - pk.Bytes() + compositeKey := composite.PubKeyFromBytes(pk.Bytes()) + assert.Equal(t, sign.PubKey(), compositeKey.SignKey) + assert.Equal(t, vrf.PubKey(), compositeKey.VrfKey) + assert.Equal(t, pk, compositeKey) } func TestPubKeyComposite_Equals(t *testing.T) { @@ -135,7 +157,7 @@ func TestPubKeyComposite_Identity(t *testing.T) { vrf := ed25519.GenPrivKey() sk := composite.NewPrivKeyComposite(sign, vrf) pk := sk.PubKey().(composite.PubKey) - pk.Identity() + assert.Equal(t, vrf.PubKey(), pk.Identity()) } func TestPubKeyComposite_VerifyBytes(t *testing.T) { diff --git a/crypto/encoding/codec.go b/crypto/encoding/codec.go index a86b9dcce..991cac0cb 100644 --- a/crypto/encoding/codec.go +++ b/crypto/encoding/codec.go @@ -16,6 +16,8 @@ func init() { json.RegisterType((*pc.PublicKey)(nil), "ostracon.crypto.PublicKey") json.RegisterType((*pc.PublicKey_Ed25519)(nil), "ostracon.crypto.PublicKey_Ed25519") json.RegisterType((*pc.PublicKey_Secp256K1)(nil), "ostracon.crypto.PublicKey_Secp256K1") + json.RegisterType((*pc.PublicKey_Composite)(nil), "ostracon.crypto.PublicKey_Composite") + json.RegisterType((*pc.PublicKey_Bls12)(nil), "ostracon.crypto.PublicKey_Bls12") } // PubKeyToProto takes crypto.PubKey and transforms it to a protobuf Pubkey @@ -81,14 +83,6 @@ func PubKeyFromProto(k *pc.PublicKey) (crypto.PubKey, error) { VrfKey: vrf, } return pk, nil - case *pc.PublicKey_Ed25519: - if len(k.Ed25519) != ed25519.PubKeySize { - return nil, fmt.Errorf("invalid size for PubKeyEd25519. Got %d, expected %d", - len(k.Ed25519), ed25519.PubKeySize) - } - pk := make(ed25519.PubKey, ed25519.PubKeySize) - copy(pk, k.Ed25519) - return pk, nil case *pc.PublicKey_Bls12: if len(k.Bls12) != bls.PubKeySize { return nil, fmt.Errorf("invalid size for PubKeyBls12. Got %d, expected %d", @@ -97,6 +91,14 @@ func PubKeyFromProto(k *pc.PublicKey) (crypto.PubKey, error) { pk := bls.PubKey{} copy(pk[:], k.Bls12) return pk, nil + case *pc.PublicKey_Ed25519: + if len(k.Ed25519) != ed25519.PubKeySize { + return nil, fmt.Errorf("invalid size for PubKeyEd25519. Got %d, expected %d", + len(k.Ed25519), ed25519.PubKeySize) + } + pk := make(ed25519.PubKey, ed25519.PubKeySize) + copy(pk, k.Ed25519) + return pk, nil case *pc.PublicKey_Secp256K1: if len(k.Secp256K1) != secp256k1.PubKeySize { return nil, fmt.Errorf("invalid size for PubKeySecp256k1. Got %d, expected %d", diff --git a/privval/file_test.go b/privval/file_test.go index f7d39edfa..95bf2082f 100644 --- a/privval/file_test.go +++ b/privval/file_test.go @@ -1,7 +1,6 @@ package privval import ( - "encoding/base64" "encoding/hex" "fmt" "io/ioutil" @@ -10,6 +9,9 @@ import ( "testing" "time" + "github.com/line/ostracon/crypto" + tmjson "github.com/line/ostracon/libs/json" + "github.com/line/ostracon/crypto/composite" "github.com/stretchr/testify/assert" @@ -17,26 +19,52 @@ import ( "github.com/line/ostracon/crypto/ed25519" "github.com/line/ostracon/crypto/tmhash" - tmjson "github.com/line/ostracon/libs/json" tmrand "github.com/line/ostracon/libs/rand" tmproto "github.com/line/ostracon/proto/ostracon/types" "github.com/line/ostracon/types" tmtime "github.com/line/ostracon/types/time" ) +var testingKeyTypes = []struct { + keyType string + privKey crypto.PrivKey + pubKey crypto.PubKey +}{ + { + keyType: PrivKeyTypeEd25519, + privKey: ed25519.GenPrivKey(), + pubKey: ed25519.PubKey{}, + }, + { + keyType: PrivKeyTypeComposite, + privKey: *composite.GenPrivKey(), + pubKey: composite.PubKey{}, + }, +} + +var jsonKey = func(t *testing.T, addr crypto.Address, privKey crypto.PrivKey, pubKey crypto.PubKey) string { + privKeyBytes, err := tmjson.MarshalIndent(privKey, " ", " ") + assert.Nil(t, err) + pubKeyBytes, err := tmjson.MarshalIndent(pubKey, " ", " ") + assert.Nil(t, err) + return fmt.Sprintf(`{ + "address": "%s", + "pub_key": %s, + "priv_key": %s +}`, addr, pubKeyBytes, privKeyBytes) +} + func TestGenFilePV(t *testing.T) { tempKeyFile, err := ioutil.TempFile("", "priv_validator_key_") require.Nil(t, err) tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privValEd25519, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) - require.EqualValues(t, reflect.TypeOf(ed25519.PubKey{}), reflect.TypeOf(privValEd25519.Key.PubKey)) - - privValComposite, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeComposite) - require.Nil(t, err) - require.EqualValues(t, reflect.TypeOf(composite.PubKey{}), reflect.TypeOf(privValComposite.Key.PubKey)) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) + require.EqualValues(t, reflect.TypeOf(testing.pubKey), reflect.TypeOf(privVal.Key.PubKey)) + } privValUndefinedPrivKeyType, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), "") require.NotNil(t, err) @@ -51,17 +79,19 @@ func TestGenLoadValidator(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) - height := int64(100) - privVal.LastSignState.Height = height - privVal.Save() - addr := privVal.GetAddress() + height := int64(100) + privVal.LastSignState.Height = height + privVal.Save() + addr := privVal.GetAddress() - privVal = LoadFilePV(tempKeyFile.Name(), tempStateFile.Name()) - assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") - assert.Equal(height, privVal.LastSignState.Height, "expected privval.LastHeight to have been saved") + privVal = LoadFilePV(tempKeyFile.Name(), tempStateFile.Name()) + assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") + assert.Equal(height, privVal.LastSignState.Height, "expected privval.LastHeight to have been saved") + } } func TestResetValidator(t *testing.T) { @@ -70,29 +100,31 @@ func TestResetValidator(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) - emptyState := FilePVLastSignState{filePath: tempStateFile.Name()} + emptyState := FilePVLastSignState{filePath: tempStateFile.Name()} - // new priv val has empty state - assert.Equal(t, privVal.LastSignState, emptyState) + // new priv val has empty state + assert.Equal(t, privVal.LastSignState, emptyState) - // test vote - height, round := int64(10), int32(1) - voteType := tmproto.PrevoteType - randBytes := tmrand.Bytes(tmhash.Size) - blockID := types.BlockID{Hash: randBytes, PartSetHeader: types.PartSetHeader{}} - vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) - err = privVal.SignVote("mychainid", vote.ToProto()) - assert.NoError(t, err, "expected no error signing vote") + // test vote + height, round := int64(10), int32(1) + voteType := tmproto.PrevoteType + randBytes := tmrand.Bytes(tmhash.Size) + blockID := types.BlockID{Hash: randBytes, PartSetHeader: types.PartSetHeader{}} + vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) + err = privVal.SignVote("mychainid", vote.ToProto()) + assert.NoError(t, err, "expected no error signing vote") - // priv val after signing is not same as empty - assert.NotEqual(t, privVal.LastSignState, emptyState) + // priv val after signing is not same as empty + assert.NotEqual(t, privVal.LastSignState, emptyState) - // priv val after AcceptNewConnection is same as empty - privVal.Reset() - assert.Equal(t, privVal.LastSignState, emptyState) + // priv val after AcceptNewConnection is same as empty + privVal.Reset() + assert.Equal(t, privVal.LastSignState, emptyState) + } } func TestLoadOrGenValidator(t *testing.T) { @@ -103,23 +135,25 @@ func TestLoadOrGenValidator(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - tempKeyFilePath := tempKeyFile.Name() - if err := os.Remove(tempKeyFilePath); err != nil { - t.Error(err) - } - tempStateFilePath := tempStateFile.Name() - if err := os.Remove(tempStateFilePath); err != nil { - t.Error(err) - } - - privVal, err := LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath, PrivKeyTypeEd25519) - require.Nil(t, err) + for _, testing := range testingKeyTypes { + tempKeyFilePath := tempKeyFile.Name() + if err := os.Remove(tempKeyFilePath); err != nil { + t.Error(err) + } + tempStateFilePath := tempStateFile.Name() + if err := os.Remove(tempStateFilePath); err != nil { + t.Error(err) + } + + privVal, err := LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath, testing.keyType) + require.Nil(t, err) - addr := privVal.GetAddress() - privVal, err = LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath, PrivKeyTypeEd25519) - require.Nil(t, err) + addr := privVal.GetAddress() + privVal, err = LoadOrGenFilePV(tempKeyFilePath, tempStateFilePath, testing.keyType) + require.Nil(t, err) - assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same") + assert.Equal(addr, privVal.GetAddress(), "expected privval addr to be the same", "keyType", testing.keyType) + } } func TestUnmarshalValidatorState(t *testing.T) { @@ -150,40 +184,28 @@ func TestUnmarshalValidatorState(t *testing.T) { func TestUnmarshalValidatorKey(t *testing.T) { assert, require := assert.New(t), require.New(t) - // create some fixed values - privKey := ed25519.GenPrivKey() - pubKey := privKey.PubKey() - addr := pubKey.Address() - pubBytes := pubKey.Bytes() - privBytes := privKey.Bytes() - pubB64 := base64.StdEncoding.EncodeToString(pubBytes) - privB64 := base64.StdEncoding.EncodeToString(privBytes) - - serialized := fmt.Sprintf(`{ - "address": "%s", - "pub_key": { - "type": "ostracon/PubKeyEd25519", - "value": "%s" - }, - "priv_key": { - "type": "ostracon/PrivKeyEd25519", - "value": "%s" - } -}`, addr, pubB64, privB64) - - val := FilePVKey{} - err := tmjson.Unmarshal([]byte(serialized), &val) - require.Nil(err, "%+v", err) + for _, testing := range testingKeyTypes { + // create some fixed values + privKey := testing.privKey + pubKey := privKey.PubKey() + addr := pubKey.Address() - // make sure the values match - assert.EqualValues(addr, val.Address) - assert.EqualValues(pubKey, val.PubKey) - assert.EqualValues(privKey, val.PrivKey) + serialized := jsonKey(t, addr, privKey, pubKey) - // export it and make sure it is the same - out, err := tmjson.Marshal(val) - require.Nil(err, "%+v", err) - assert.JSONEq(serialized, string(out)) + val := FilePVKey{} + err := tmjson.Unmarshal([]byte(serialized), &val) + require.Nil(err, "%+v", err) + + // make sure the values match + assert.EqualValues(addr, val.Address) + assert.EqualValues(pubKey, val.PubKey) + assert.EqualValues(privKey, val.PrivKey) + + // export it and make sure it is the same + out, err := tmjson.Marshal(val) + require.Nil(err, "%+v", err) + assert.JSONEq(serialized, string(out)) + } } func TestSignVote(t *testing.T) { @@ -194,50 +216,52 @@ func TestSignVote(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) - randbytes := tmrand.Bytes(tmhash.Size) - randbytes2 := tmrand.Bytes(tmhash.Size) - - block1 := types.BlockID{Hash: randbytes, - PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} - block2 := types.BlockID{Hash: randbytes2, - PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}} - - height, round := int64(10), int32(1) - voteType := tmproto.PrevoteType - - // sign a vote for first time - vote := newVote(privVal.Key.Address, 0, height, round, voteType, block1) - v := vote.ToProto() - err = privVal.SignVote("mychainid", v) - assert.NoError(err, "expected no error signing vote") - - // try to sign the same vote again; should be fine - err = privVal.SignVote("mychainid", v) - assert.NoError(err, "expected no error on signing same vote") - - // now try some bad votes - cases := []*types.Vote{ - newVote(privVal.Key.Address, 0, height, round-1, voteType, block1), // round regression - newVote(privVal.Key.Address, 0, height-1, round, voteType, block1), // height regression - newVote(privVal.Key.Address, 0, height-2, round+4, voteType, block1), // height regression and different round - newVote(privVal.Key.Address, 0, height, round, voteType, block2), // different block - } + randbytes := tmrand.Bytes(tmhash.Size) + randbytes2 := tmrand.Bytes(tmhash.Size) - for _, c := range cases { - cpb := c.ToProto() - err = privVal.SignVote("mychainid", cpb) - assert.Error(err, "expected error on signing conflicting vote") - } + block1 := types.BlockID{Hash: randbytes, + PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} + block2 := types.BlockID{Hash: randbytes2, + PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}} - // try signing a vote with a different time stamp - sig := vote.Signature - vote.Timestamp = vote.Timestamp.Add(time.Duration(1000)) - err = privVal.SignVote("mychainid", v) - assert.NoError(err) - assert.Equal(sig, vote.Signature) + height, round := int64(10), int32(1) + voteType := tmproto.PrevoteType + + // sign a vote for first time + vote := newVote(privVal.Key.Address, 0, height, round, voteType, block1) + v := vote.ToProto() + err = privVal.SignVote("mychainid", v) + assert.NoError(err, "expected no error signing vote") + + // try to sign the same vote again; should be fine + err = privVal.SignVote("mychainid", v) + assert.NoError(err, "expected no error on signing same vote") + + // now try some bad votes + cases := []*types.Vote{ + newVote(privVal.Key.Address, 0, height, round-1, voteType, block1), // round regression + newVote(privVal.Key.Address, 0, height-1, round, voteType, block1), // height regression + newVote(privVal.Key.Address, 0, height-2, round+4, voteType, block1), // height regression and different round + newVote(privVal.Key.Address, 0, height, round, voteType, block2), // different block + } + + for _, c := range cases { + cpb := c.ToProto() + err = privVal.SignVote("mychainid", cpb) + assert.Error(err, "expected error on signing conflicting vote") + } + + // try signing a vote with a different time stamp + sig := vote.Signature + vote.Timestamp = vote.Timestamp.Add(time.Duration(1000)) + err = privVal.SignVote("mychainid", v) + assert.NoError(err) + assert.Equal(sig, vote.Signature) + } } func TestSignProposal(t *testing.T) { @@ -248,47 +272,49 @@ func TestSignProposal(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) - randbytes := tmrand.Bytes(tmhash.Size) - randbytes2 := tmrand.Bytes(tmhash.Size) - - block1 := types.BlockID{Hash: randbytes, - PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} - block2 := types.BlockID{Hash: randbytes2, - PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}} - height, round := int64(10), int32(1) - - // sign a proposal for first time - proposal := newProposal(height, round, block1) - pbp := proposal.ToProto() - err = privVal.SignProposal("mychainid", pbp) - assert.NoError(err, "expected no error signing proposal") - - // try to sign the same proposal again; should be fine - err = privVal.SignProposal("mychainid", pbp) - assert.NoError(err, "expected no error on signing same proposal") - - // now try some bad Proposals - cases := []*types.Proposal{ - newProposal(height, round-1, block1), // round regression - newProposal(height-1, round, block1), // height regression - newProposal(height-2, round+4, block1), // height regression and different round - newProposal(height, round, block2), // different block - } + randbytes := tmrand.Bytes(tmhash.Size) + randbytes2 := tmrand.Bytes(tmhash.Size) - for _, c := range cases { - err = privVal.SignProposal("mychainid", c.ToProto()) - assert.Error(err, "expected error on signing conflicting proposal") - } + block1 := types.BlockID{Hash: randbytes, + PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} + block2 := types.BlockID{Hash: randbytes2, + PartSetHeader: types.PartSetHeader{Total: 10, Hash: randbytes2}} + height, round := int64(10), int32(1) - // try signing a proposal with a different time stamp - sig := proposal.Signature - proposal.Timestamp = proposal.Timestamp.Add(time.Duration(1000)) - err = privVal.SignProposal("mychainid", pbp) - assert.NoError(err) - assert.Equal(sig, proposal.Signature) + // sign a proposal for first time + proposal := newProposal(height, round, block1) + pbp := proposal.ToProto() + err = privVal.SignProposal("mychainid", pbp) + assert.NoError(err, "expected no error signing proposal") + + // try to sign the same proposal again; should be fine + err = privVal.SignProposal("mychainid", pbp) + assert.NoError(err, "expected no error on signing same proposal") + + // now try some bad Proposals + cases := []*types.Proposal{ + newProposal(height, round-1, block1), // round regression + newProposal(height-1, round, block1), // height regression + newProposal(height-2, round+4, block1), // height regression and different round + newProposal(height, round, block2), // different block + } + + for _, c := range cases { + err = privVal.SignProposal("mychainid", c.ToProto()) + assert.Error(err, "expected error on signing conflicting proposal") + } + + // try signing a proposal with a different time stamp + sig := proposal.Signature + proposal.Timestamp = proposal.Timestamp.Add(time.Duration(1000)) + err = privVal.SignProposal("mychainid", pbp) + assert.NoError(err) + assert.Equal(sig, proposal.Signature) + } } func TestGenerateVRFProof(t *testing.T) { @@ -297,19 +323,21 @@ func TestGenerateVRFProof(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) - - 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[:])) - pubKey, err := privVal.GetPubKey() - require.NoError(t, err) - output, err := pubKey.VRFVerify(proof, msg) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) require.Nil(t, err) - require.NotNil(t, output) + + 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[:])) + pubKey, err := privVal.GetPubKey() + require.NoError(t, err) + output, err := pubKey.VRFVerify(proof, msg) + require.Nil(t, err) + require.NotNil(t, output) + } } } @@ -319,59 +347,61 @@ func TestDifferByTimestamp(t *testing.T) { tempStateFile, err := ioutil.TempFile("", "priv_validator_state_") require.Nil(t, err) - privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), PrivKeyTypeEd25519) - require.Nil(t, err) - randbytes := tmrand.Bytes(tmhash.Size) - block1 := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} - height, round := int64(10), int32(1) - chainID := "mychainid" - - // test proposal - { - proposal := newProposal(height, round, block1) - pb := proposal.ToProto() - err := privVal.SignProposal(chainID, pb) - assert.NoError(t, err, "expected no error signing proposal") - signBytes := types.ProposalSignBytes(chainID, pb) - - sig := proposal.Signature - timeStamp := proposal.Timestamp - - // manipulate the timestamp. should get changed back - pb.Timestamp = pb.Timestamp.Add(time.Millisecond) - var emptySig []byte - proposal.Signature = emptySig - err = privVal.SignProposal("mychainid", pb) - assert.NoError(t, err, "expected no error on signing same proposal") - - assert.Equal(t, timeStamp, pb.Timestamp) - assert.Equal(t, signBytes, types.ProposalSignBytes(chainID, pb)) - assert.Equal(t, sig, proposal.Signature) - } - - // test vote - { - voteType := tmproto.PrevoteType - blockID := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{}} - vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) - v := vote.ToProto() - err := privVal.SignVote("mychainid", v) - assert.NoError(t, err, "expected no error signing vote") - - signBytes := types.VoteSignBytes(chainID, v) - sig := v.Signature - timeStamp := vote.Timestamp - - // manipulate the timestamp. should get changed back - v.Timestamp = v.Timestamp.Add(time.Millisecond) - var emptySig []byte - v.Signature = emptySig - err = privVal.SignVote("mychainid", v) - assert.NoError(t, err, "expected no error on signing same vote") - - assert.Equal(t, timeStamp, v.Timestamp) - assert.Equal(t, signBytes, types.VoteSignBytes(chainID, v)) - assert.Equal(t, sig, v.Signature) + for _, testing := range testingKeyTypes { + privVal, err := GenFilePV(tempKeyFile.Name(), tempStateFile.Name(), testing.keyType) + require.Nil(t, err) + randbytes := tmrand.Bytes(tmhash.Size) + block1 := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{Total: 5, Hash: randbytes}} + height, round := int64(10), int32(1) + chainID := "mychainid" + + // test proposal + { + proposal := newProposal(height, round, block1) + pb := proposal.ToProto() + err := privVal.SignProposal(chainID, pb) + assert.NoError(t, err, "expected no error signing proposal") + signBytes := types.ProposalSignBytes(chainID, pb) + + sig := proposal.Signature + timeStamp := proposal.Timestamp + + // manipulate the timestamp. should get changed back + pb.Timestamp = pb.Timestamp.Add(time.Millisecond) + var emptySig []byte + proposal.Signature = emptySig + err = privVal.SignProposal("mychainid", pb) + assert.NoError(t, err, "expected no error on signing same proposal") + + assert.Equal(t, timeStamp, pb.Timestamp) + assert.Equal(t, signBytes, types.ProposalSignBytes(chainID, pb)) + assert.Equal(t, sig, proposal.Signature) + } + + // test vote + { + voteType := tmproto.PrevoteType + blockID := types.BlockID{Hash: randbytes, PartSetHeader: types.PartSetHeader{}} + vote := newVote(privVal.Key.Address, 0, height, round, voteType, blockID) + v := vote.ToProto() + err := privVal.SignVote("mychainid", v) + assert.NoError(t, err, "expected no error signing vote") + + signBytes := types.VoteSignBytes(chainID, v) + sig := v.Signature + timeStamp := vote.Timestamp + + // manipulate the timestamp. should get changed back + v.Timestamp = v.Timestamp.Add(time.Millisecond) + var emptySig []byte + v.Signature = emptySig + err = privVal.SignVote("mychainid", v) + assert.NoError(t, err, "expected no error on signing same vote") + + assert.Equal(t, timeStamp, v.Timestamp) + assert.Equal(t, signBytes, types.VoteSignBytes(chainID, v)) + assert.Equal(t, sig, v.Signature) + } } } diff --git a/rpc/client/evidence_test.go b/rpc/client/evidence_test.go index 3798a7d35..b980f8e32 100644 --- a/rpc/client/evidence_test.go +++ b/rpc/client/evidence_test.go @@ -6,11 +6,13 @@ import ( "testing" "time" + "github.com/line/ostracon/abci/example/kvstore" + "github.com/line/ostracon/crypto/ed25519" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" abci "github.com/line/ostracon/abci/types" - "github.com/line/ostracon/crypto/ed25519" cryptoenc "github.com/line/ostracon/crypto/encoding" "github.com/line/ostracon/crypto/tmhash" tmrand "github.com/line/ostracon/libs/rand" @@ -113,6 +115,11 @@ func makeEvidences( } func TestBroadcastEvidence_DuplicateVoteEvidence(t *testing.T) { + // https://github.com/tendermint/tendermint/pull/6678 + // previous versions of this test used a shared fixture with + // other tests, and in this version we give it a little time + // for the node to make progress before running the test + time.Sleep(100 * time.Millisecond) var ( config = rpctest.GetConfig() chainID = config.ChainID() @@ -139,7 +146,11 @@ func TestBroadcastEvidence_DuplicateVoteEvidence(t *testing.T) { ed25519pub := pv.Key.PubKey.(ed25519.PubKey) rawpub := ed25519pub.Bytes() - result2, err := c.ABCIQuery(context.Background(), "/val", rawpub) + publicKey, err := cryptoenc.PubKeyToProto(pv.Key.PubKey) + assert.NoError(t, err) + pubStr, _ := kvstore.MakeValSetChangeTxAndMore(publicKey, 10) + // See kvstore.PersistentKVStoreApplication#Query + result2, err := c.ABCIQuery(context.Background(), "/val", []byte(pubStr)) require.NoError(t, err) qres := result2.Response require.True(t, qres.IsOK()) diff --git a/test/maverick/main.go b/test/maverick/main.go index dca2f416d..a34da66ed 100644 --- a/test/maverick/main.go +++ b/test/maverick/main.go @@ -125,7 +125,11 @@ func main() { "e.g. --misbehaviors double-prevote,3\n"+ "You can also have multiple misbehaviors: e.g. double-prevote,3,no-vote,5") - cmd := cli.PrepareBaseCmd(rootCmd, "OC", os.ExpandEnv(filepath.Join("$HOME", cfg.DefaultOstraconDir))) + userHome, err := os.UserHomeDir() + if err != nil { + panic(err) + } + cmd := cli.PrepareBaseCmd(rootCmd, "OC", filepath.Join(userHome, cfg.DefaultOstraconDir)) if err := cmd.Execute(); err != nil { panic(err) } diff --git a/types/block.go b/types/block.go index ef1fa23c5..03442b8ac 100644 --- a/types/block.go +++ b/types/block.go @@ -888,7 +888,11 @@ func CommitToVoteSet(chainID string, commit *Commit, voters *VoterSet) *VoteSet panic(fmt.Sprintf("Cannot find voter %d in voterSet of size %d", vote.ValidatorIndex, voters.Size())) } msg := VoteSignBytes(chainID, vote.ToProto()) - blsPubKeys = append(blsPubKeys, voter.PubKey.(composite.PubKey).SignKey.(bls.PubKey)) + blsPubKey := GetSignatureKey(voter.PubKey) + if blsPubKey == nil { + panic(fmt.Errorf("signature %d has been omitted, even though it is not a BLS key", idx)) + } + blsPubKeys = append(blsPubKeys, *blsPubKey) msgs = append(msgs, msg) } }