From e6a8a4df559c9474e753eab19b54a0354e60b059 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 7 Sep 2018 11:59:16 +0100 Subject: [PATCH 1/4] Implement command/REST endpoint for offline tx sign off #1953 * Add sign CLI command to sign transactions generated with the --generate-only flag. * Add /sign REST endpoint for Voyager support. Redirect password prompt to STDERR to avoid messing up cli commands output. As a rule of thumb, program's output should always go to STDOUT, whilst errors&diagnostics go to STDERR as per POSIX's philosophy and specs. --- Makefile | 2 +- PENDING.md | 7 ++- client/input.go | 2 +- client/lcd/lcd_test.go | 30 ++++++++++- client/utils/utils.go | 53 +++++++++++++++++++ cmd/gaia/cli_test/cli_test.go | 42 ++++++++++++++- cmd/gaia/cmd/gaiacli/main.go | 1 + docs/light/api.md | 69 ++++++++++++++++++++++++ docs/sdk/clients.md | 11 +++- x/auth/client/cli/sign.go | 91 ++++++++++++++++++++++++++++++++ x/auth/client/context/context.go | 60 +++++++++++++++------ x/auth/client/rest/query.go | 4 ++ x/auth/client/rest/sign.go | 60 +++++++++++++++++++++ 13 files changed, 409 insertions(+), 23 deletions(-) create mode 100644 x/auth/client/cli/sign.go create mode 100644 x/auth/client/rest/sign.go diff --git a/Makefile b/Makefile index 2f23fd54f..78fc864c9 100644 --- a/Makefile +++ b/Makefile @@ -179,7 +179,7 @@ test_cover: test_lint: gometalinter.v2 --config=tools/gometalinter.json ./... - !(gometalinter.v2 --disable-all --enable='errcheck' --vendor ./... | grep -v "client/") + !(gometalinter.v2 --disable-all --enable='errcheck' --vendor ./... | grep -v -e "client/" -e "fmt\.Fprintf") find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s dep status >> /dev/null !(grep -n branch Gopkg.toml) diff --git a/PENDING.md b/PENDING.md index d23a372a3..bfcbcd978 100644 --- a/PENDING.md +++ b/PENDING.md @@ -29,7 +29,7 @@ BREAKING CHANGES * A new bech32 prefix has been introduced for Tendermint signing keys and addresses, `cosmosconspub` and `cosmoscons` respectively. * [x/gov] \#2195 Made governance use BFT Time instead of Block Heights for deposit and voting periods. - + * SDK * [core] [\#1807](https://github.com/cosmos/cosmos-sdk/issues/1807) Switch from use of rational to decimal * [types] [\#1901](https://github.com/cosmos/cosmos-sdk/issues/1901) Validator interface's GetOwner() renamed to GetOperator() @@ -48,6 +48,7 @@ FEATURES * [lcd] Endpoints to query staking pool and params * [lcd] [\#2110](https://github.com/cosmos/cosmos-sdk/issues/2110) Add support for `simulate=true` requests query argument to endpoints that send txs to run simulations of transactions * [lcd] [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add support for `generate_only=true` query argument to generate offline unsigned transactions + * [lcd] [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) Add /sign endpoint to sign transactions generated with `generate_only=true`. * Gaia CLI (`gaiacli`) * [cli] Cmds to query staking pool and params @@ -57,7 +58,9 @@ FEATURES * [cli] [\#2047](https://github.com/cosmos/cosmos-sdk/issues/2047) Setting the --gas flag value to 0 triggers a simulation of the tx before the actual execution. The gas estimate obtained via the simulation will be used as gas limit in the actual execution. * [cli] [\#2047](https://github.com/cosmos/cosmos-sdk/issues/2047) The --gas-adjustment flag can be used to adjust the estimate obtained via the simulation triggered by --gas=0. * [cli] [\#2110](https://github.com/cosmos/cosmos-sdk/issues/2110) Add --dry-run flag to perform a simulation of a transaction without broadcasting it. The --gas flag is ignored as gas would be automatically estimated. - * [cli] [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add --generate-only flag to build an unsigned transaction and write it to STDOUT. + * [cli] [\#2204](https://github.com/cosmos/cosmos-sdk/issues/2204) Support generating and broadcasting messages with multiple signatures via command line: + * [\#966](https://github.com/cosmos/cosmos-sdk/issues/966) Add --generate-only flag to build an unsigned transaction and write it to STDOUT. + * [\#1953](https://github.com/cosmos/cosmos-sdk/issues/1953) New `sign` command to sign transactions generated with the --generate-only flag. * Gaia * [cli] #2170 added ability to show the node's address via `gaiad tendermint show-address` diff --git a/client/input.go b/client/input.go index e7d13f3bf..b10f65ce6 100644 --- a/client/input.go +++ b/client/input.go @@ -24,7 +24,7 @@ func BufferStdin() *bufio.Reader { // It enforces the password length func GetPassword(prompt string, buf *bufio.Reader) (pass string, err error) { if inputIsTty() { - pass, err = speakeasy.Ask(prompt) + pass, err = speakeasy.FAsk(os.Stderr, prompt) } else { pass, err = readLineFromBuf(buf) } diff --git a/client/lcd/lcd_test.go b/client/lcd/lcd_test.go index 656362bcd..8b4e31868 100644 --- a/client/lcd/lcd_test.go +++ b/client/lcd/lcd_test.go @@ -26,6 +26,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" "github.com/cosmos/cosmos-sdk/x/auth" + authrest "github.com/cosmos/cosmos-sdk/x/auth/client/rest" "github.com/cosmos/cosmos-sdk/x/gov" "github.com/cosmos/cosmos-sdk/x/slashing" "github.com/cosmos/cosmos-sdk/x/stake" @@ -313,12 +314,13 @@ func TestIBCTransfer(t *testing.T) { // TODO: query ibc egress packet state } -func TestCoinSendGenerateOnly(t *testing.T) { +func TestCoinSendGenerateAndSign(t *testing.T) { name, password := "test", "1234567890" addr, seed := CreateAddr(t, "test", password, GetKeyBase(t)) cleanup, _, port := InitializeTestLCD(t, 1, []sdk.AccAddress{addr}) defer cleanup() - // create TX + + // generate TX res, body, _ := doSendWithGas(t, port, seed, name, password, addr, 0, 0, "?generate_only=true") require.Equal(t, http.StatusOK, res.StatusCode, body) var msg auth.StdTx @@ -327,6 +329,30 @@ func TestCoinSendGenerateOnly(t *testing.T) { require.Equal(t, msg.Msgs[0].Type(), "bank") require.Equal(t, msg.Msgs[0].GetSigners(), []sdk.AccAddress{addr}) require.Equal(t, 0, len(msg.Signatures)) + + // sign tx + var signedMsg auth.StdTx + acc := getAccount(t, port, addr) + accnum := acc.GetAccountNumber() + sequence := acc.GetSequence() + + payload := authrest.SignBody{ + Tx: msg, + LocalAccountName: name, + Password: password, + ChainID: viper.GetString(client.FlagChainID), + AccountNumber: accnum, + Sequence: sequence, + } + json, err := cdc.MarshalJSON(payload) + require.Nil(t, err) + res, body = Request(t, port, "POST", "/sign", json) + require.Equal(t, http.StatusOK, res.StatusCode, body) + require.Nil(t, cdc.UnmarshalJSON([]byte(body), &signedMsg)) + require.Equal(t, len(msg.Msgs), len(signedMsg.Msgs)) + require.Equal(t, msg.Msgs[0].Type(), signedMsg.Msgs[0].Type()) + require.Equal(t, msg.Msgs[0].GetSigners(), signedMsg.Msgs[0].GetSigners()) + require.Equal(t, 1, len(signedMsg.Signatures)) } func TestTxs(t *testing.T) { diff --git a/client/utils/utils.go b/client/utils/utils.go index fa5bcf817..554406b4a 100644 --- a/client/utils/utils.go +++ b/client/utils/utils.go @@ -1,6 +1,7 @@ package utils import ( + "bytes" "fmt" "os" @@ -99,6 +100,49 @@ func PrintUnsignedStdTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs return } +// SignStdTx appends a signature to a StdTx and returns a copy of a it. If appendSig +// is false, it replaces the signatures already attached with the new signature. +func SignStdTx(txCtx authctx.TxContext, cliCtx context.CLIContext, name string, stdTx auth.StdTx, appendSig bool) (auth.StdTx, error) { + var signedStdTx auth.StdTx + + keybase, err := keys.GetKeyBase() + if err != nil { + return signedStdTx, err + } + info, err := keybase.Get(name) + if err != nil { + return signedStdTx, err + } + addr := info.GetPubKey().Address() + + // Check whether the address is a signer + if !isTxSigner(sdk.AccAddress(addr), stdTx.GetSigners()) { + fmt.Fprintf(os.Stderr, "WARNING: The generated transaction's intended signer does not match the given signer: '%v'", name) + } + + if txCtx.AccountNumber == 0 { + accNum, err := cliCtx.GetAccountNumber(addr) + if err != nil { + return signedStdTx, err + } + txCtx = txCtx.WithAccountNumber(accNum) + } + + if txCtx.Sequence == 0 { + accSeq, err := cliCtx.GetAccountSequence(addr) + if err != nil { + return signedStdTx, err + } + txCtx = txCtx.WithSequence(accSeq) + } + + passphrase, err := keys.GetPassphrase(name) + if err != nil { + return signedStdTx, err + } + return txCtx.SignStdTx(name, passphrase, stdTx, appendSig) +} + func adjustGasEstimate(estimate int64, adjustment float64) int64 { return int64(adjustment * float64(estimate)) } @@ -163,3 +207,12 @@ func buildUnsignedStdTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs } return auth.NewStdTx(stdSignMsg.Msgs, stdSignMsg.Fee, nil, stdSignMsg.Memo), nil } + +func isTxSigner(user sdk.AccAddress, signers []sdk.AccAddress) bool { + for _, s := range signers { + if bytes.Equal(user.Bytes(), s.Bytes()) { + return true + } + } + return false +} diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index 8b66ab4a1..e67ac3082 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -5,6 +5,7 @@ package clitest import ( "encoding/json" "fmt" + "io/ioutil" "os" "testing" @@ -332,7 +333,7 @@ func TestGaiaCLISubmitProposal(t *testing.T) { require.Equal(t, " 2 - Apples", proposalsQuery) } -func TestGaiaCLISendGenerateOnly(t *testing.T) { +func TestGaiaCLISendGenerateAndSign(t *testing.T) { chainID, servAddr, port := initializeFixtures(t) flags := fmt.Sprintf("--home=%s --node=%v --chain-id=%v", gaiacliHome, servAddr, chainID) @@ -343,6 +344,7 @@ func TestGaiaCLISendGenerateOnly(t *testing.T) { tests.WaitForTMStart(port) tests.WaitForNextNBlocksTM(2, port) + fooAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show foo --output=json --home=%s", gaiacliHome)) barAddr, _ := executeGetAddrPK(t, fmt.Sprintf("gaiacli keys show bar --output=json --home=%s", gaiacliHome)) // Test generate sendTx with default gas @@ -376,6 +378,35 @@ func TestGaiaCLISendGenerateOnly(t *testing.T) { require.Equal(t, msg.Fee.Gas, int64(100)) require.Equal(t, len(msg.Msgs), 1) require.Equal(t, 0, len(msg.GetSignatures())) + + // Write the output to disk + unsignedTxFile := writeToNewTempFile(t, stdout) + defer os.Remove(unsignedTxFile.Name()) + + // Test sign --print-sigs + success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( + "gaiacli sign %v --print-sigs %v", flags, unsignedTxFile.Name())) + require.True(t, success) + require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n", fooAddr.String()), stdout) + + // Test sign + success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( + "gaiacli sign %v --name=foo %v", flags, unsignedTxFile.Name()), app.DefaultKeyPass) + require.True(t, success) + msg = unmarshalStdTx(t, stdout) + require.Equal(t, len(msg.Msgs), 1) + require.Equal(t, 1, len(msg.GetSignatures())) + require.Equal(t, fooAddr.String(), msg.GetSigners()[0].String()) + + // Write the output to disk + signedTxFile := writeToNewTempFile(t, stdout) + defer os.Remove(signedTxFile.Name()) + + // Test sign --print-signatures + success, stdout, _ = executeWriteRetStdStreams(t, fmt.Sprintf( + "gaiacli sign %v --print-sigs %v", flags, signedTxFile.Name())) + require.True(t, success) + require.Equal(t, fmt.Sprintf("Signers:\n 0: %v\n\nSignatures:\n 0: %v\n", fooAddr.String(), fooAddr.String()), stdout) } //___________________________________________________________________________________ @@ -408,6 +439,15 @@ func unmarshalStdTx(t *testing.T, s string) (stdTx auth.StdTx) { return } +func writeToNewTempFile(t *testing.T, s string) *os.File { + fp, err := ioutil.TempFile(os.TempDir(), "cosmos_cli_test_") + require.Nil(t, err) + // defer os.Remove(signedTxFile.Name()) + _, err = fp.WriteString(s) + require.Nil(t, err) + return fp +} + //___________________________________________________________________________________ // executors diff --git a/cmd/gaia/cmd/gaiacli/main.go b/cmd/gaia/cmd/gaiacli/main.go index df0fd3c11..7a1af6221 100644 --- a/cmd/gaia/cmd/gaiacli/main.go +++ b/cmd/gaia/cmd/gaiacli/main.go @@ -127,6 +127,7 @@ func main() { rootCmd.AddCommand( client.GetCommands( authcmd.GetAccountCmd("acc", cdc, authcmd.GetAccountDecoder(cdc)), + authcmd.GetSignCommand(cdc, authcmd.GetAccountDecoder(cdc)), )...) rootCmd.AddCommand( client.PostCommands( diff --git a/docs/light/api.md b/docs/light/api.md index 7168cf9d7..293b023cd 100644 --- a/docs/light/api.md +++ b/docs/light/api.md @@ -229,6 +229,75 @@ Returns on success: } ``` +### POST /auth/accounts/sign + +- **URL**: `/auth/sign` +- **Functionality**: Sign a transaction without broadcasting it. +- Returns on success: + +```json +{ + "rest api": "1.0", + "code": 200, + "error": "", + "result": { + "type": "auth/StdTx", + "value": { + "msg": [ + { + "type": "cosmos-sdk/Send", + "value": { + "inputs": [ + { + "address": "cosmos1ql4ekxkujf3xllk8h5ldhhgh4ylpu7kwec6q3d", + "coins": [ + { + "denom": "steak", + "amount": "1" + } + ] + } + ], + "outputs": [ + { + "address": "cosmos1dhyqhg4px33ed3erqymls0hc7q2lxw9hhfwklj", + "coins": [ + { + "denom": "steak", + "amount": "1" + } + ] + } + ] + } + } + ], + "fee": { + "amount": [ + { + "denom": "", + "amount": "0" + } + ], + "gas": "2742" + }, + "signatures": [ + { + "pub_key": { + "type": "tendermint/PubKeySecp256k1", + "value": "A2A/f2IYnrPUMTMqhwN81oas9jurtfcsvxdeLlNw3gGy" + }, + "signature": "MEQCIGVn73y9QLwBa3vmsAD1bs3ygX75Wo+lAFSAUDs431ZPAiBWAf2amyqTCDXE9J87rL9QF9sd5JvVMt7goGSuamPJwg==", + "account_number": "1", + "sequence": "0" + } + ], + "memo": "" + } + } +} +``` + ## ICS20 - TokenAPI The TokenAPI exposes all functionality needed to query account balances and send transactions. diff --git a/docs/sdk/clients.md b/docs/sdk/clients.md index fdfbca7bd..0cc87cf21 100644 --- a/docs/sdk/clients.md +++ b/docs/sdk/clients.md @@ -147,7 +147,16 @@ gaiacli send \ --chain-id= \ --name= \ --to= \ - --generate-only + --generate-only > unsignedSendTx.json +``` + +You can now sign the transaction file generated through the `--generate-only` flag by providing your key to the following command: + +```bash +gaiacli sign \ + --chain-id= \ + --name= + unsignedSendTx.json > signedSendTx.json ``` ### Staking diff --git a/x/auth/client/cli/sign.go b/x/auth/client/cli/sign.go new file mode 100644 index 000000000..e5cd41235 --- /dev/null +++ b/x/auth/client/cli/sign.go @@ -0,0 +1,91 @@ +package cli + +import ( + "fmt" + "io/ioutil" + + "github.com/spf13/viper" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context" + "github.com/spf13/cobra" + amino "github.com/tendermint/go-amino" +) + +const ( + flagAppend = "append" + flagPrintSigs = "print-sigs" +) + +// GetSignCommand returns the sign command +func GetSignCommand(codec *amino.Codec, decoder auth.AccountDecoder) *cobra.Command { + cmd := &cobra.Command{ + Use: "sign ", + Short: "Sign transactions", + Long: `Sign transactions created with the --generate-only flag. +Read a transaction from , sign it, and print its JSON encoding.`, + RunE: makeSignCmd(codec, decoder), + Args: cobra.ExactArgs(1), + } + cmd.Flags().String(client.FlagName, "", "Name of private key with which to sign") + cmd.Flags().Bool(flagAppend, true, "Append the signature to the existing ones. If disabled, old signatures would be overwritten") + cmd.Flags().Bool(flagPrintSigs, false, "Print the addresses that must sign the transaction and those who have already signed it, then exit") + return cmd +} + +func makeSignCmd(cdc *amino.Codec, decoder auth.AccountDecoder) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) (err error) { + stdTx, err := readAndUnmarshalStdTx(cdc, args[0]) + if err != nil { + return + } + + if viper.GetBool(flagPrintSigs) { + printSignatures(stdTx) + return nil + } + + name := viper.GetString(client.FlagName) + cliCtx := context.NewCLIContext().WithCodec(cdc).WithAccountDecoder(decoder) + txCtx := authctx.NewTxContextFromCLI() + + newTx, err := utils.SignStdTx(txCtx, cliCtx, name, stdTx, viper.GetBool(flagAppend)) + if err != nil { + return err + } + json, err := cdc.MarshalJSON(newTx) + if err != nil { + return err + } + fmt.Printf("%s\n", json) + return + } +} + +func printSignatures(stdTx auth.StdTx) { + fmt.Println("Signers:") + for i, signer := range stdTx.GetSigners() { + fmt.Printf(" %v: %v\n", i, signer.String()) + } + fmt.Println("") + fmt.Println("Signatures:") + for i, sig := range stdTx.GetSignatures() { + fmt.Printf(" %v: %v\n", i, sdk.AccAddress(sig.Address()).String()) + } + return +} + +func readAndUnmarshalStdTx(cdc *amino.Codec, filename string) (stdTx auth.StdTx, err error) { + var bytes []byte + if bytes, err = ioutil.ReadFile(filename); err != nil { + return + } + if err = cdc.UnmarshalJSON(bytes, &stdTx); err != nil { + return + } + return +} diff --git a/x/auth/client/context/context.go b/x/auth/client/context/context.go index 5e55696b8..fe030449b 100644 --- a/x/auth/client/context/context.go +++ b/x/auth/client/context/context.go @@ -117,24 +117,11 @@ func (ctx TxContext) Build(msgs []sdk.Msg) (auth.StdSignMsg, error) { // Sign signs a transaction given a name, passphrase, and a single message to // signed. An error is returned if signing fails. func (ctx TxContext) Sign(name, passphrase string, msg auth.StdSignMsg) ([]byte, error) { - keybase, err := keys.GetKeyBase() + sig, err := MakeSignature(name, passphrase, msg) if err != nil { return nil, err } - - sig, pubkey, err := keybase.Sign(name, passphrase, msg.Bytes()) - if err != nil { - return nil, err - } - - sigs := []auth.StdSignature{{ - AccountNumber: msg.AccountNumber, - Sequence: msg.Sequence, - PubKey: pubkey, - Signature: sig, - }} - - return ctx.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo)) + return ctx.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, []auth.StdSignature{sig}, msg.Memo)) } // BuildAndSign builds a single message to be signed, and signs a transaction @@ -177,3 +164,46 @@ func (ctx TxContext) BuildWithPubKey(name string, msgs []sdk.Msg) ([]byte, error return ctx.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo)) } + +// SignStdTx appends a signature to a StdTx and returns a copy of a it. If append +// is false, it replaces the signatures already attached with the new signature. +func (ctx TxContext) SignStdTx(name, passphrase string, stdTx auth.StdTx, appendSig bool) (signedStdTx auth.StdTx, err error) { + stdSignature, err := MakeSignature(name, passphrase, auth.StdSignMsg{ + ChainID: ctx.ChainID, + AccountNumber: ctx.AccountNumber, + Sequence: ctx.Sequence, + Fee: stdTx.Fee, + Msgs: stdTx.GetMsgs(), + Memo: stdTx.GetMemo(), + }) + if err != nil { + return + } + + sigs := stdTx.GetSignatures() + if len(sigs) == 0 || !appendSig { + sigs = []auth.StdSignature{stdSignature} + } else { + sigs = append(sigs, stdSignature) + } + signedStdTx = auth.NewStdTx(stdTx.GetMsgs(), stdTx.Fee, sigs, stdTx.GetMemo()) + return +} + +// MakeSignature builds a StdSignature given key name, passphrase, and a StdSignMsg. +func MakeSignature(name, passphrase string, msg auth.StdSignMsg) (sig auth.StdSignature, err error) { + keybase, err := keys.GetKeyBase() + if err != nil { + return + } + sigBytes, pubkey, err := keybase.Sign(name, passphrase, msg.Bytes()) + if err != nil { + return + } + return auth.StdSignature{ + AccountNumber: msg.AccountNumber, + Sequence: msg.Sequence, + PubKey: pubkey, + Signature: sigBytes, + }, nil +} diff --git a/x/auth/client/rest/query.go b/x/auth/client/rest/query.go index 6ad50a14d..3a5ab756d 100644 --- a/x/auth/client/rest/query.go +++ b/x/auth/client/rest/query.go @@ -20,6 +20,10 @@ func RegisterRoutes(cliCtx context.CLIContext, r *mux.Router, cdc *wire.Codec, s "/accounts/{address}", QueryAccountRequestHandlerFn(storeName, cdc, authcmd.GetAccountDecoder(cdc), cliCtx), ).Methods("GET") + r.HandleFunc( + "/sign", + SignTxRequestHandlerFn(cdc, cliCtx), + ).Methods("POST") } // query accountREST Handler diff --git a/x/auth/client/rest/sign.go b/x/auth/client/rest/sign.go new file mode 100644 index 000000000..bbbf7f26c --- /dev/null +++ b/x/auth/client/rest/sign.go @@ -0,0 +1,60 @@ +package rest + +import ( + "io/ioutil" + "net/http" + + "github.com/cosmos/cosmos-sdk/client/context" + "github.com/cosmos/cosmos-sdk/client/utils" + "github.com/cosmos/cosmos-sdk/wire" + "github.com/cosmos/cosmos-sdk/x/auth" + authctx "github.com/cosmos/cosmos-sdk/x/auth/client/context" +) + +// SignBody defines the properties of a sign request's body. +type SignBody struct { + Tx auth.StdTx `json:"tx"` + LocalAccountName string `json:"name"` + Password string `json:"password"` + ChainID string `json:"chain_id"` + AccountNumber int64 `json:"account_number"` + Sequence int64 `json:"sequence"` +} + +// sign tx REST handler +func SignTxRequestHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.HandlerFunc { + + return func(w http.ResponseWriter, r *http.Request) { + var m SignBody + + body, err := ioutil.ReadAll(r.Body) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + err = cdc.UnmarshalJSON(body, &m) + if err != nil { + utils.WriteErrorResponse(w, http.StatusBadRequest, err.Error()) + return + } + + txCtx := authctx.TxContext{ + ChainID: m.ChainID, + AccountNumber: m.AccountNumber, + Sequence: m.Sequence, + } + + signedTx, err := txCtx.SignStdTx(m.LocalAccountName, m.Password, m.Tx, false) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + + output, err := wire.MarshalJSONIndent(cdc, signedTx) + if err != nil { + utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) + return + } + w.Write(output) + } +} From a450bf7ae4728b5003c6952d3fc7739922cbeba7 Mon Sep 17 00:00:00 2001 From: Juan Leni Date: Fri, 7 Sep 2018 15:33:24 +0200 Subject: [PATCH 2/4] Upgrading ledger-goclient --- .gitignore | 1 + Gopkg.lock | 21 +++++++++++---------- Gopkg.toml | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index eb10faca7..8498547cc 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ # Build vendor +.vendor-new build tools/bin/* examples/build/* diff --git a/Gopkg.lock b/Gopkg.lock index 224522341..f5221a4e5 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1,6 +1,14 @@ # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. +[[projects]] + branch = "master" + digest = "1:7736fc6da04620727f8f3aa2ced8d77be8e074a302820937aa5993848c769b27" + name = "github.com/ZondaX/hid-go" + packages = ["."] + pruneopts = "UT" + revision = "48b08affede2cea076a3cf13b2e3f72ed262b743" + [[projects]] digest = "1:09a7f74eb6bb3c0f14d8926610c87f569c5cff68e978d30e9a3540aeb626fdf0" name = "github.com/bartekn/go-bip39" @@ -24,14 +32,6 @@ revision = "4aabc24848ce5fd31929f7d1e4ea74d3709c14cd" version = "v0.1.0" -[[projects]] - branch = "master" - digest = "1:70f6b224a59b2fa453debffa85c77f71063d8754b90c8c4fbad5794e2c382b0f" - name = "github.com/brejski/hid" - packages = ["."] - pruneopts = "UT" - revision = "06112dcfcc50a7e0e4fd06e17f9791e788fdaafc" - [[projects]] branch = "master" digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8" @@ -498,11 +498,12 @@ version = "v0.9.0" [[projects]] - digest = "1:4dcb0dd65feecb068ce23a234d1a07c7868a1e39f52a6defcae0bb371d03abf6" + digest = "1:7886f86064faff6f8d08a3eb0e8c773648ff5a2e27730831e2bfbf07467f6666" name = "github.com/zondax/ledger-goclient" packages = ["."] pruneopts = "UT" - revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259" + revision = "58598458c11bc0ad1c1b8dac3dc3e11eaf270b79" + version = "v0.1.0" [[projects]] branch = "master" diff --git a/Gopkg.toml b/Gopkg.toml index f8194ccd0..1b440891d 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -65,7 +65,7 @@ [[constraint]] name = "github.com/zondax/ledger-goclient" - revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259" + version = "=v0.1.0" [prune] go-tests = true From f5a7f2524f328e2e2e4923293e5d8db1932cc0a2 Mon Sep 17 00:00:00 2001 From: Alessio Treglia Date: Fri, 7 Sep 2018 15:25:20 +0100 Subject: [PATCH 3/4] ACK and incorporate @cwgoes comments --- Makefile | 2 +- cmd/gaia/cli_test/cli_test.go | 1 - x/auth/client/rest/sign.go | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 78fc864c9..2f23fd54f 100644 --- a/Makefile +++ b/Makefile @@ -179,7 +179,7 @@ test_cover: test_lint: gometalinter.v2 --config=tools/gometalinter.json ./... - !(gometalinter.v2 --disable-all --enable='errcheck' --vendor ./... | grep -v -e "client/" -e "fmt\.Fprintf") + !(gometalinter.v2 --disable-all --enable='errcheck' --vendor ./... | grep -v "client/") find . -name '*.go' -type f -not -path "./vendor*" -not -path "*.git*" | xargs gofmt -d -s dep status >> /dev/null !(grep -n branch Gopkg.toml) diff --git a/cmd/gaia/cli_test/cli_test.go b/cmd/gaia/cli_test/cli_test.go index e67ac3082..d8d242e5b 100644 --- a/cmd/gaia/cli_test/cli_test.go +++ b/cmd/gaia/cli_test/cli_test.go @@ -442,7 +442,6 @@ func unmarshalStdTx(t *testing.T, s string) (stdTx auth.StdTx) { func writeToNewTempFile(t *testing.T, s string) *os.File { fp, err := ioutil.TempFile(os.TempDir(), "cosmos_cli_test_") require.Nil(t, err) - // defer os.Remove(signedTxFile.Name()) _, err = fp.WriteString(s) require.Nil(t, err) return fp diff --git a/x/auth/client/rest/sign.go b/x/auth/client/rest/sign.go index bbbf7f26c..7ec703884 100644 --- a/x/auth/client/rest/sign.go +++ b/x/auth/client/rest/sign.go @@ -19,6 +19,7 @@ type SignBody struct { ChainID string `json:"chain_id"` AccountNumber int64 `json:"account_number"` Sequence int64 `json:"sequence"` + AppendSig bool `json:"append_sig"` } // sign tx REST handler @@ -44,7 +45,7 @@ func SignTxRequestHandlerFn(cdc *wire.Codec, cliCtx context.CLIContext) http.Han Sequence: m.Sequence, } - signedTx, err := txCtx.SignStdTx(m.LocalAccountName, m.Password, m.Tx, false) + signedTx, err := txCtx.SignStdTx(m.LocalAccountName, m.Password, m.Tx, m.AppendSig) if err != nil { utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error()) return From 72e9664ce10d30f97fc4e6eab603981ff1ccfa0a Mon Sep 17 00:00:00 2001 From: Christopher Goes Date: Fri, 7 Sep 2018 17:00:57 +0200 Subject: [PATCH 4/4] Revert "Merge PR #2217: Governance BFT Time" This reverts commit 94b86f85c193c11bcc42c6c99619fad20103bb6b, reversing changes made to 2a4edcca48e0a81226b231508349f1d060b09cc5. --- PENDING.md | 3 +-- docs/spec/governance/state.md | 16 +++++------ x/gov/endblocker_test.go | 51 +++++++---------------------------- x/gov/genesis.go | 6 ++--- x/gov/handler.go | 8 +++--- x/gov/keeper.go | 19 ++++++------- x/gov/keeper_test.go | 9 +++---- x/gov/procedures.go | 8 +++--- x/gov/proposals.go | 27 +++++++++---------- 9 files changed, 55 insertions(+), 92 deletions(-) diff --git a/PENDING.md b/PENDING.md index d23a372a3..e5ea9884a 100644 --- a/PENDING.md +++ b/PENDING.md @@ -28,8 +28,7 @@ BREAKING CHANGES * [x/stake] [\#2040](https://github.com/cosmos/cosmos-sdk/issues/2040) Validator operator type has now changed to `sdk.ValAddress` * A new bech32 prefix has been introduced for Tendermint signing keys and addresses, `cosmosconspub` and `cosmoscons` respectively. - * [x/gov] \#2195 Made governance use BFT Time instead of Block Heights for deposit and voting periods. - + * SDK * [core] [\#1807](https://github.com/cosmos/cosmos-sdk/issues/1807) Switch from use of rational to decimal * [types] [\#1901](https://github.com/cosmos/cosmos-sdk/issues/1901) Validator interface's GetOwner() renamed to GetOperator() diff --git a/docs/spec/governance/state.md b/docs/spec/governance/state.md index 0d2927d7a..728ded371 100644 --- a/docs/spec/governance/state.md +++ b/docs/spec/governance/state.md @@ -13,13 +13,13 @@ has to be created and the previous one rendered inactive. ```go type DepositProcedure struct { MinDeposit sdk.Coins // Minimum deposit for a proposal to enter voting period. - MaxDepositPeriod time.Time // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months + MaxDepositPeriod int64 // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months } ``` ```go type VotingProcedure struct { - VotingPeriod time.Time // Length of the voting period. Initial value: 2 weeks + VotingPeriod int64 // Length of the voting period. Initial value: 2 weeks } ``` @@ -28,7 +28,7 @@ type TallyingProcedure struct { Threshold sdk.Dec // Minimum propotion of Yes votes for proposal to pass. Initial value: 0.5 Veto sdk.Dec // Minimum proportion of Veto votes to Total votes ratio for proposal to be vetoed. Initial value: 1/3 GovernancePenalty sdk.Dec // Penalty if validator does not vote - GracePeriod time.Time // If validator entered validator set in this period of blocks before vote ended, governance penalty does not apply + GracePeriod int64 // If validator entered validator set in this period of blocks before vote ended, governance penalty does not apply } ``` @@ -97,10 +97,10 @@ type Proposal struct { Type ProposalType // Type of proposal. Initial set {PlainTextProposal, SoftwareUpgradeProposal} TotalDeposit sdk.Coins // Current deposit on this proposal. Initial value is set at InitialDeposit Deposits []Deposit // List of deposits on the proposal - SubmitTime time.Time // Time of the block where TxGovSubmitProposal was included + SubmitBlock int64 // Height of the block where TxGovSubmitProposal was included Submitter sdk.Address // Address of the submitter - VotingStartTime time.Time // Time of the block where MinDeposit was reached. time.Time{} if MinDeposit is not reached + VotingStartBlock int64 // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached CurrentStatus ProposalStatus // Current status of the proposal YesVotes sdk.Dec @@ -137,7 +137,7 @@ For pseudocode purposes, here are the two function we will use to read or write * `ProposalProcessingQueue`: A queue `queue[proposalID]` containing all the `ProposalIDs` of proposals that reached `MinDeposit`. Each round, the oldest element of `ProposalProcessingQueue` is checked during `BeginBlock` to see if - `CurrentTime == VotingStartTime + activeProcedure.VotingPeriod`. If it is, + `CurrentBlock == VotingStartBlock + activeProcedure.VotingPeriod`. If it is, then the application tallies the votes, compute the votes of each validator and checks if every validator in the valdiator set have voted and, if not, applies `GovernancePenalty`. If the proposal is accepted, deposits are refunded. After that proposal is ejected from `ProposalProcessingQueue` and the next element of the queue is evaluated. @@ -159,7 +159,7 @@ And the pseudocode for the `ProposalProcessingQueue`: proposal = load(Governance, ) // proposal is a const key votingProcedure = load(GlobalParams, 'VotingProcedure') - if (CurrentTime == proposal.VotingStartTime + votingProcedure.VotingPeriod && proposal.CurrentStatus == ProposalStatusActive) + if (CurrentBlock == proposal.VotingStartBlock + votingProcedure.VotingPeriod && proposal.CurrentStatus == ProposalStatusActive) // End of voting period, tally @@ -194,7 +194,7 @@ And the pseudocode for the `ProposalProcessingQueue`: // Slash validators that did not vote, or update tally if they voted for each validator in validators - if (validator.bondTime < CurrentTime - tallyingProcedure.GracePeriod) + if (validator.bondHeight < CurrentBlock - tallyingProcedure.GracePeriod) // only slash if validator entered validator set before grace period if (!tmpValMap(validator).HasVoted) slash validator by tallyingProcedure.GovernancePenalty diff --git a/x/gov/endblocker_test.go b/x/gov/endblocker_test.go index 4cbd1d758..710ecb1db 100644 --- a/x/gov/endblocker_test.go +++ b/x/gov/endblocker_test.go @@ -2,7 +2,6 @@ package gov import ( "testing" - "time" "github.com/stretchr/testify/require" @@ -29,18 +28,12 @@ func TestTickExpiredDepositPeriod(t *testing.T) { require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) - newHeader := ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) - ctx = ctx.WithBlockHeader(newHeader) - + ctx = ctx.WithBlockHeight(10) EndBlocker(ctx, keeper) require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) - newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod) - ctx = ctx.WithBlockHeader(newHeader) - + ctx = ctx.WithBlockHeight(250) require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) require.True(t, shouldPopInactiveProposalQueue(ctx, keeper)) EndBlocker(ctx, keeper) @@ -66,10 +59,7 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) - newHeader := ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(2) * time.Second) - ctx = ctx.WithBlockHeader(newHeader) - + ctx = ctx.WithBlockHeight(10) EndBlocker(ctx, keeper) require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) @@ -78,20 +68,14 @@ func TestTickMultipleExpiredDepositPeriod(t *testing.T) { res = govHandler(ctx, newProposalMsg2) require.True(t, res.IsOK()) - newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(time.Duration(-1) * time.Second) - ctx = ctx.WithBlockHeader(newHeader) - + ctx = ctx.WithBlockHeight(205) require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) require.True(t, shouldPopInactiveProposalQueue(ctx, keeper)) EndBlocker(ctx, keeper) require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) - newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(5) * time.Second) - ctx = ctx.WithBlockHeader(newHeader) - + ctx = ctx.WithBlockHeight(215) require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) require.True(t, shouldPopInactiveProposalQueue(ctx, keeper)) EndBlocker(ctx, keeper) @@ -121,10 +105,7 @@ func TestTickPassedDepositPeriod(t *testing.T) { require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) - newHeader := ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) - ctx = ctx.WithBlockHeader(newHeader) - + ctx = ctx.WithBlockHeight(10) EndBlocker(ctx, keeper) require.NotNil(t, keeper.InactiveProposalQueuePeek(ctx)) require.False(t, shouldPopInactiveProposalQueue(ctx, keeper)) @@ -165,20 +146,14 @@ func TestTickPassedVotingPeriod(t *testing.T) { var proposalID int64 keeper.cdc.UnmarshalBinaryBare(res.Data, &proposalID) - newHeader := ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) - ctx = ctx.WithBlockHeader(newHeader) - + ctx = ctx.WithBlockHeight(10) newDepositMsg := NewMsgDeposit(addrs[1], proposalID, sdk.Coins{sdk.NewInt64Coin("steak", 5)}) res = govHandler(ctx, newDepositMsg) require.True(t, res.IsOK()) EndBlocker(ctx, keeper) - newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod) - ctx = ctx.WithBlockHeader(newHeader) - + ctx = ctx.WithBlockHeight(215) require.True(t, shouldPopActiveProposalQueue(ctx, keeper)) depositsIterator := keeper.GetDeposits(ctx, proposalID) require.True(t, depositsIterator.Valid()) @@ -222,10 +197,7 @@ func TestSlashing(t *testing.T) { var proposalID int64 keeper.cdc.UnmarshalBinaryBare(res.Data, &proposalID) - newHeader := ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(time.Duration(1) * time.Second) - ctx = ctx.WithBlockHeader(newHeader) - + ctx = ctx.WithBlockHeight(10) require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, proposalID).GetStatus()) newVoteMsg := NewMsgVote(addrs[0], proposalID, OptionYes) @@ -234,10 +206,7 @@ func TestSlashing(t *testing.T) { EndBlocker(ctx, keeper) - newHeader = ctx.BlockHeader() - newHeader.Time = ctx.BlockHeader().Time.Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod).Add(keeper.GetDepositProcedure(ctx).MaxDepositPeriod) - ctx = ctx.WithBlockHeader(newHeader) - + ctx = ctx.WithBlockHeight(215) require.Equal(t, StatusVotingPeriod, keeper.GetProposal(ctx, proposalID).GetStatus()) EndBlocker(ctx, keeper) diff --git a/x/gov/genesis.go b/x/gov/genesis.go index 58273c8e8..15f952c00 100644 --- a/x/gov/genesis.go +++ b/x/gov/genesis.go @@ -1,8 +1,6 @@ package gov import ( - "time" - sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -29,10 +27,10 @@ func DefaultGenesisState() GenesisState { StartingProposalID: 1, DepositProcedure: DepositProcedure{ MinDeposit: sdk.Coins{sdk.NewInt64Coin("steak", 10)}, - MaxDepositPeriod: time.Duration(172800) * time.Second, + MaxDepositPeriod: 200, }, VotingProcedure: VotingProcedure{ - VotingPeriod: time.Duration(172800) * time.Second, + VotingPeriod: 200, }, TallyingProcedure: TallyingProcedure{ Threshold: sdk.NewDecWithPrec(5, 1), diff --git a/x/gov/handler.go b/x/gov/handler.go index 59e47a14e..6424bb0a1 100644 --- a/x/gov/handler.go +++ b/x/gov/handler.go @@ -122,9 +122,9 @@ func EndBlocker(ctx sdk.Context, keeper Keeper) (resTags sdk.Tags) { for shouldPopActiveProposalQueue(ctx, keeper) { activeProposal := keeper.ActiveProposalQueuePop(ctx) - proposalStartTime := activeProposal.GetVotingStartTime() + proposalStartBlock := activeProposal.GetVotingStartBlock() votingPeriod := keeper.GetVotingProcedure(ctx).VotingPeriod - if ctx.BlockHeader().Time.Before(proposalStartTime.Add(votingPeriod)) { + if ctx.BlockHeight() < proposalStartBlock+votingPeriod { continue } @@ -172,7 +172,7 @@ func shouldPopInactiveProposalQueue(ctx sdk.Context, keeper Keeper) bool { return false } else if peekProposal.GetStatus() != StatusDepositPeriod { return true - } else if !ctx.BlockHeader().Time.Before(peekProposal.GetSubmitTime().Add(depositProcedure.MaxDepositPeriod)) { + } else if ctx.BlockHeight() >= peekProposal.GetSubmitBlock()+depositProcedure.MaxDepositPeriod { return true } return false @@ -184,7 +184,7 @@ func shouldPopActiveProposalQueue(ctx sdk.Context, keeper Keeper) bool { if peekProposal == nil { return false - } else if !ctx.BlockHeader().Time.Before(peekProposal.GetVotingStartTime().Add(votingProcedure.VotingPeriod)) { + } else if ctx.BlockHeight() >= peekProposal.GetVotingStartBlock()+votingProcedure.VotingPeriod { return true } return false diff --git a/x/gov/keeper.go b/x/gov/keeper.go index 3b9d8a7ff..576d2cc22 100644 --- a/x/gov/keeper.go +++ b/x/gov/keeper.go @@ -66,14 +66,15 @@ func (keeper Keeper) NewTextProposal(ctx sdk.Context, title string, description return nil } var proposal Proposal = &TextProposal{ - ProposalID: proposalID, - Title: title, - Description: description, - ProposalType: proposalType, - Status: StatusDepositPeriod, - TallyResult: EmptyTallyResult(), - TotalDeposit: sdk.Coins{}, - SubmitTime: ctx.BlockHeader().Time, + ProposalID: proposalID, + Title: title, + Description: description, + ProposalType: proposalType, + Status: StatusDepositPeriod, + TallyResult: EmptyTallyResult(), + TotalDeposit: sdk.Coins{}, + SubmitBlock: ctx.BlockHeight(), + VotingStartBlock: -1, // TODO: Make Time } keeper.SetProposal(ctx, proposal) keeper.InactiveProposalQueuePush(ctx, proposal) @@ -199,7 +200,7 @@ func (keeper Keeper) peekCurrentProposalID(ctx sdk.Context) (proposalID int64, e } func (keeper Keeper) activateVotingPeriod(ctx sdk.Context, proposal Proposal) { - proposal.SetVotingStartTime(ctx.BlockHeader().Time) + proposal.SetVotingStartBlock(ctx.BlockHeight()) proposal.SetStatus(StatusVotingPeriod) keeper.SetProposal(ctx, proposal) keeper.ActiveProposalQueuePush(ctx, proposal) diff --git a/x/gov/keeper_test.go b/x/gov/keeper_test.go index 91c41d7d7..a61292b93 100644 --- a/x/gov/keeper_test.go +++ b/x/gov/keeper_test.go @@ -2,7 +2,6 @@ package gov import ( "testing" - "time" "github.com/stretchr/testify/require" @@ -46,12 +45,12 @@ func TestActivateVotingPeriod(t *testing.T) { proposal := keeper.NewTextProposal(ctx, "Test", "description", ProposalTypeText) - require.True(t, proposal.GetVotingStartTime().Equal(time.Time{})) + require.Equal(t, int64(-1), proposal.GetVotingStartBlock()) require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) keeper.activateVotingPeriod(ctx, proposal) - require.True(t, proposal.GetVotingStartTime().Equal(ctx.BlockHeader().Time)) + require.Equal(t, proposal.GetVotingStartBlock(), ctx.BlockHeight()) require.Equal(t, proposal.GetProposalID(), keeper.ActiveProposalQueuePeek(ctx).GetProposalID()) } @@ -78,7 +77,7 @@ func TestDeposits(t *testing.T) { // Check no deposits at beginning deposit, found := keeper.GetDeposit(ctx, proposalID, addrs[1]) require.False(t, found) - require.True(t, keeper.GetProposal(ctx, proposalID).GetVotingStartTime().Equal(time.Time{})) + require.Equal(t, keeper.GetProposal(ctx, proposalID).GetVotingStartBlock(), int64(-1)) require.Nil(t, keeper.ActiveProposalQueuePeek(ctx)) // Check first deposit @@ -115,7 +114,7 @@ func TestDeposits(t *testing.T) { require.Equal(t, addr1Initial.Minus(fourSteak), keeper.ck.GetCoins(ctx, addrs[1])) // Check that proposal moved to voting period - require.True(t, keeper.GetProposal(ctx, proposalID).GetVotingStartTime().Equal(ctx.BlockHeader().Time)) + require.Equal(t, ctx.BlockHeight(), keeper.GetProposal(ctx, proposalID).GetVotingStartBlock()) require.NotNil(t, keeper.ActiveProposalQueuePeek(ctx)) require.Equal(t, proposalID, keeper.ActiveProposalQueuePeek(ctx).GetProposalID()) diff --git a/x/gov/procedures.go b/x/gov/procedures.go index e453add79..f74091c74 100644 --- a/x/gov/procedures.go +++ b/x/gov/procedures.go @@ -1,15 +1,13 @@ package gov import ( - "time" - sdk "github.com/cosmos/cosmos-sdk/types" ) // Procedure around Deposits for governance type DepositProcedure struct { - MinDeposit sdk.Coins `json:"min_deposit"` // Minimum deposit for a proposal to enter voting period. - MaxDepositPeriod time.Duration `json:"max_deposit_period"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months + MinDeposit sdk.Coins `json:"min_deposit"` // Minimum deposit for a proposal to enter voting period. + MaxDepositPeriod int64 `json:"max_deposit_period"` // Maximum period for Atom holders to deposit on a proposal. Initial value: 2 months } // Procedure around Tallying votes in governance @@ -21,5 +19,5 @@ type TallyingProcedure struct { // Procedure around Voting in governance type VotingProcedure struct { - VotingPeriod time.Duration `json:"voting_period"` // Length of the voting period. + VotingPeriod int64 `json:"voting_period"` // Length of the voting period. } diff --git a/x/gov/proposals.go b/x/gov/proposals.go index e68069957..b4c678367 100644 --- a/x/gov/proposals.go +++ b/x/gov/proposals.go @@ -3,7 +3,6 @@ package gov import ( "encoding/json" "fmt" - "time" "github.com/pkg/errors" @@ -31,14 +30,14 @@ type Proposal interface { GetTallyResult() TallyResult SetTallyResult(TallyResult) - GetSubmitTime() time.Time - SetSubmitTime(time.Time) + GetSubmitBlock() int64 + SetSubmitBlock(int64) GetTotalDeposit() sdk.Coins SetTotalDeposit(sdk.Coins) - GetVotingStartTime() time.Time - SetVotingStartTime(time.Time) + GetVotingStartBlock() int64 + SetVotingStartBlock(int64) } // checks if two proposals are equal @@ -49,9 +48,9 @@ func ProposalEqual(proposalA Proposal, proposalB Proposal) bool { proposalA.GetProposalType() == proposalB.GetProposalType() && proposalA.GetStatus() == proposalB.GetStatus() && proposalA.GetTallyResult().Equals(proposalB.GetTallyResult()) && - proposalA.GetSubmitTime().Equal(proposalB.GetSubmitTime()) && + proposalA.GetSubmitBlock() == proposalB.GetSubmitBlock() && proposalA.GetTotalDeposit().IsEqual(proposalB.GetTotalDeposit()) && - proposalA.GetVotingStartTime().Equal(proposalB.GetVotingStartTime()) { + proposalA.GetVotingStartBlock() == proposalB.GetVotingStartBlock() { return true } return false @@ -68,10 +67,10 @@ type TextProposal struct { Status ProposalStatus `json:"proposal_status"` // Status of the Proposal {Pending, Active, Passed, Rejected} TallyResult TallyResult `json:"tally_result"` // Result of Tallys - SubmitTime time.Time `json:"submit_block"` // Height of the block where TxGovSubmitProposal was included + SubmitBlock int64 `json:"submit_block"` // Height of the block where TxGovSubmitProposal was included TotalDeposit sdk.Coins `json:"total_deposit"` // Current deposit on this proposal. Initial value is set at InitialDeposit - VotingStartTime time.Time `json:"voting_start_block"` // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached + VotingStartBlock int64 `json:"voting_start_block"` // Height of the block where MinDeposit was reached. -1 if MinDeposit is not reached } // Implements Proposal Interface @@ -90,13 +89,13 @@ func (tp TextProposal) GetStatus() ProposalStatus { return tp.S func (tp *TextProposal) SetStatus(status ProposalStatus) { tp.Status = status } func (tp TextProposal) GetTallyResult() TallyResult { return tp.TallyResult } func (tp *TextProposal) SetTallyResult(tallyResult TallyResult) { tp.TallyResult = tallyResult } -func (tp TextProposal) GetSubmitTime() time.Time { return tp.SubmitTime } -func (tp *TextProposal) SetSubmitTime(submitTime time.Time) { tp.SubmitTime = submitTime } +func (tp TextProposal) GetSubmitBlock() int64 { return tp.SubmitBlock } +func (tp *TextProposal) SetSubmitBlock(submitBlock int64) { tp.SubmitBlock = submitBlock } func (tp TextProposal) GetTotalDeposit() sdk.Coins { return tp.TotalDeposit } func (tp *TextProposal) SetTotalDeposit(totalDeposit sdk.Coins) { tp.TotalDeposit = totalDeposit } -func (tp TextProposal) GetVotingStartTime() time.Time { return tp.VotingStartTime } -func (tp *TextProposal) SetVotingStartTime(votingStartTime time.Time) { - tp.VotingStartTime = votingStartTime +func (tp TextProposal) GetVotingStartBlock() int64 { return tp.VotingStartBlock } +func (tp *TextProposal) SetVotingStartBlock(votingStartBlock int64) { + tp.VotingStartBlock = votingStartBlock } //-----------------------------------------------------------