diff --git a/abci/cmd/abci-cli/abci-cli.go b/abci/cmd/abci-cli/abci-cli.go index d00b82995..c34d493d2 100644 --- a/abci/cmd/abci-cli/abci-cli.go +++ b/abci/cmd/abci-cli/abci-cli.go @@ -2,9 +2,11 @@ package main import ( "bufio" + "bytes" "encoding/hex" "errors" "fmt" + "github.com/line/ostracon/config" "io" "os" "strings" @@ -22,6 +24,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 +52,9 @@ var ( // kvstore flagPersist string + + // staking power for make validator_tx + flagStakingPower int64 ) var RootCmd = &cobra.Command{ @@ -143,6 +149,10 @@ func addKVStoreFlags() { 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() { RootCmd.AddCommand(batchCmd) RootCmd.AddCommand(consoleCmd) @@ -162,6 +172,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 +283,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 +707,55 @@ func cmdKVStore(cmd *cobra.Command, args []string) error { select {} } +func cmdPersistKVStoreMakeValSetChangeTx(cmd *cobra.Command, args []string) error { + c := config.DefaultConfig() + c.SetRoot(os.Getenv("HOME") + "/" + 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.Println(fmt.Sprintf("DeliverTxSync: data=%s, tx=%s", 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.Println(fmt.Sprintf("QuerySync: data=%s", 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.Println(fmt.Sprintf("original:publicKey:%s", publicKey)) + validatorUpdate := types.ValidatorUpdate{} + validatorUpdate.Unmarshal(res.Value) + types.ReadMessage(bytes.NewReader(res.Value), &validatorUpdate) + fmt.Println(fmt.Sprintf("saved :publicKey:%s", validatorUpdate.PubKey)) + } + return nil +} + //-------------------------------------------------------------------------------- func printResponse(cmd *cobra.Command, args []string, rsp response) { @@ -702,7 +773,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..d0d4af1f6 100644 --- a/abci/example/kvstore/helpers.go +++ b/abci/example/kvstore/helpers.go @@ -1,13 +1,35 @@ package kvstore import ( + "fmt" "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" + "io/ioutil" ) -// 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\n", keyFilePath) + } + keyJSONBytes, err := ioutil.ReadFile(keyFilePath) + if err != nil { + return nil, fmt.Errorf("private validator file %s does not read\n", keyFilePath) + } + pvKey := privval.FilePVKey{} + err = tmjson.Unmarshal(keyJSONBytes, &pvKey) + if err != nil { + return nil, fmt.Errorf("Error reading PrivValidator key from %v: %v\n", 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/persistent_kvstore.go b/abci/example/kvstore/persistent_kvstore.go index bc269bf37..35c3d1d73 100644 --- a/abci/example/kvstore/persistent_kvstore.go +++ b/abci/example/kvstore/persistent_kvstore.go @@ -113,6 +113,15 @@ func (app *PersistentKVStoreApplication) Query(reqQuery types.RequestQuery) (res resQuery.Key = reqQuery.Data resQuery.Value = value + validatorUpdate := types.ValidatorUpdate{} + validatorUpdate.Unmarshal(resQuery.Value) + types.ReadMessage(bytes.NewReader(resQuery.Value), &validatorUpdate) + pubKey, err := cryptoenc.PubKeyFromProto(&validatorUpdate.PubKey) + if err != nil { + panic(err) + } + 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 +215,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 +233,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 crypto.PubKey.Bytes +// See MakeValSetChangeTx func (app *PersistentKVStoreApplication) execValidatorTx(tx []byte) types.ResponseDeliverTx { tx = tx[len(ValidatorSetChangePrefix):] @@ -237,20 +252,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 +281,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 +289,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 +299,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..e4a1b5d77 --- /dev/null +++ b/abci/example/kvstore/tool/make_val_set_change_tx.go @@ -0,0 +1,44 @@ +package main + +import ( + "flag" + "fmt" + "github.com/line/ostracon/abci/example/kvstore" + "github.com/line/ostracon/config" + "github.com/line/ostracon/crypto/encoding" + "net/url" + "os" +) + +func main() { + c := config.DefaultConfig() + c.SetRoot(os.Getenv("HOME") + "/" + 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..a4ac8baad --- /dev/null +++ b/abci/example/kvstore/tool/make_val_set_change_tx.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +IFS_BACK=$IFS +IFS=$'\r\n' + +# 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