Merge branch 'develop' into cwgoes/update-tendermint-upstream
This commit is contained in:
commit
b144dc7aa2
|
@ -7,6 +7,7 @@
|
|||
|
||||
# Build
|
||||
vendor
|
||||
.vendor-new
|
||||
build
|
||||
tools/bin/*
|
||||
examples/build/*
|
||||
|
|
|
@ -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:7736fc6da04620727f8f3aa2ced8d77be8e074a302820937aa5993848c769b27"
|
||||
name = "github.com/brejski/hid"
|
||||
packages = ["."]
|
||||
pruneopts = "UT"
|
||||
revision = "48b08affede2cea076a3cf13b2e3f72ed262b743"
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
digest = "1:2c00f064ba355903866cbfbf3f7f4c0fe64af6638cc7d1b8bdcf3181bc67f1d8"
|
||||
|
@ -508,11 +508,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"
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
|
||||
[[constraint]]
|
||||
name = "github.com/zondax/ledger-goclient"
|
||||
revision = "4296ee5701e945f9b3a7dbe51f402e0b9be57259"
|
||||
version = "=v0.1.0"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
|
|
|
@ -28,6 +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] \#2219 Update to Tendermint 0.24.0
|
||||
|
@ -51,6 +52,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
|
||||
|
@ -60,7 +62,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`
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -93,9 +93,8 @@ func appStateFn(r *rand.Rand, keys []crypto.PrivKey, accs []sdk.AccAddress) json
|
|||
func testAndRunTxs(app *GaiaApp) []simulation.Operation {
|
||||
return []simulation.Operation{
|
||||
banksim.SimulateSingleInputMsgSend(app.accountMapper),
|
||||
govsim.SimulateMsgSubmitProposal(app.govKeeper, app.stakeKeeper),
|
||||
govsim.SimulateSubmittingVotingAndSlashingForProposal(app.govKeeper, app.stakeKeeper),
|
||||
govsim.SimulateMsgDeposit(app.govKeeper, app.stakeKeeper),
|
||||
govsim.SimulateMsgVote(app.govKeeper, app.stakeKeeper),
|
||||
stakesim.SimulateMsgCreateValidator(app.accountMapper, app.stakeKeeper),
|
||||
stakesim.SimulateMsgEditValidator(app.stakeKeeper),
|
||||
stakesim.SimulateMsgDelegate(app.accountMapper, app.stakeKeeper),
|
||||
|
|
|
@ -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,14 @@ 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)
|
||||
_, err = fp.WriteString(s)
|
||||
require.Nil(t, err)
|
||||
return fp
|
||||
}
|
||||
|
||||
//___________________________________________________________________________________
|
||||
// executors
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
- [ ] 3. Merge items in `PENDING.md` into the `CHANGELOG.md`. While doing this make sure that each entry contains links to issues/PRs for each item
|
||||
- [ ] 4. Summarize breaking API changes section under “Breaking Changes” section to the `CHANGELOG.md` to bring attention to any breaking API changes that affect RPC consumers.
|
||||
- [ ] 5. Tag the commit `{ .Release.Name }-rcN`
|
||||
- [ ] 6. Kick off 1 day of automated fuzz testing
|
||||
- [ ] 7. Release Lead assigns 2 people to perform [buddy testing script](/docs/RELEASE_TEST_SCRIPT.md) and update the relevant documentation
|
||||
- [ ] 8. If errors are found in either #6 or #7 go back to #2 (*NOTE*: be sure to increment the `rcN`)
|
||||
- [ ] 9. After #6 and #7 have successfully completed then merge the release PR and push the final release tag
|
||||
- [ ] 6. Open a branch & PR to merge the pending release back into `develop`. If any changes are made to the release branch, they must also be made to the pending develop merge, and both must pass tests.
|
||||
- [ ] 7. Kick off 1 day of automated fuzz testing
|
||||
- [ ] 8. Release Lead assigns 2 people to perform [buddy testing script](/docs/RELEASE_TEST_SCRIPT.md) and update the relevant documentation
|
||||
- [ ] 9. If errors are found in either #6 or #7 go back to #2 (*NOTE*: be sure to increment the `rcN`)
|
||||
- [ ] 10. After #6 and #7 have successfully completed then merge the release PR and push the final release tag
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -147,7 +147,16 @@ gaiacli send \
|
|||
--chain-id=<chain_id> \
|
||||
--name=<key_name> \
|
||||
--to=<destination_cosmosaccaddr> \
|
||||
--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=<chain_id> \
|
||||
--name=<key_name>
|
||||
unsignedSendTx.json > signedSendTx.json
|
||||
```
|
||||
|
||||
### Staking
|
||||
|
|
|
@ -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 <file>",
|
||||
Short: "Sign transactions",
|
||||
Long: `Sign transactions created with the --generate-only flag.
|
||||
Read a transaction from <file>, 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
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
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"`
|
||||
AppendSig bool `json:"append_sig"`
|
||||
}
|
||||
|
||||
// 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, m.AppendSig)
|
||||
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)
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ package simulation
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
|
@ -18,30 +19,89 @@ const (
|
|||
denom = "steak"
|
||||
)
|
||||
|
||||
// SimulateSubmittingVotingAndSlashingForProposal simulates creating a msg Submit Proposal
|
||||
// voting on the proposal, and subsequently slashing the proposal. It is implemented using
|
||||
// future operations.
|
||||
// TODO: Vote more intelligently, so we can actually do some checks regarding votes passing or failing
|
||||
// TODO: Actually check that validator slashings happened
|
||||
func SimulateSubmittingVotingAndSlashingForProposal(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
||||
handler := gov.NewHandler(k)
|
||||
// The states are:
|
||||
// column 1: All validators vote
|
||||
// column 2: 90% vote
|
||||
// column 3: 75% vote
|
||||
// column 4: 40% vote
|
||||
// column 5: 15% vote
|
||||
// column 6: noone votes
|
||||
// All columns sum to 100 for simplicity, values chosen by @valardragon semi-arbitrarily,
|
||||
// feel free to change.
|
||||
numVotesTransitionMatrix, _ := simulation.CreateTransitionMatrix([][]int{
|
||||
{20, 10, 0, 0, 0, 0},
|
||||
{55, 50, 20, 10, 0, 0},
|
||||
{25, 25, 30, 25, 30, 15},
|
||||
{0, 15, 30, 25, 30, 30},
|
||||
{0, 0, 20, 30, 30, 30},
|
||||
{0, 0, 0, 10, 10, 25},
|
||||
})
|
||||
statePercentageArray := []float64{1, .9, .75, .4, .15, 0}
|
||||
curNumVotesState := 1
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOps []simulation.FutureOperation, err sdk.Error) {
|
||||
// 1) submit proposal now
|
||||
sender := simulation.RandomKey(r, keys)
|
||||
msg := simulationCreateMsgSubmitProposal(tb, r, sender, log)
|
||||
action = simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event)
|
||||
proposalID := k.GetLastProposalID(ctx)
|
||||
// 2) Schedule operations for votes
|
||||
// 2.1) first pick a number of people to vote.
|
||||
curNumVotesState = numVotesTransitionMatrix.NextState(r, curNumVotesState)
|
||||
numVotes := int(math.Ceil(float64(len(keys)) * statePercentageArray[curNumVotesState]))
|
||||
// 2.2) select who votes and when
|
||||
whoVotes := r.Perm(len(keys))
|
||||
// didntVote := whoVotes[numVotes:]
|
||||
whoVotes = whoVotes[:numVotes]
|
||||
votingPeriod := k.GetVotingProcedure(ctx).VotingPeriod
|
||||
fops := make([]simulation.FutureOperation, numVotes+1)
|
||||
for i := 0; i < numVotes; i++ {
|
||||
whenVote := ctx.BlockHeight() + r.Int63n(votingPeriod)
|
||||
fops[i] = simulation.FutureOperation{int(whenVote), operationSimulateMsgVote(k, sk, keys[whoVotes[i]], proposalID)}
|
||||
}
|
||||
// 3) Make an operation to ensure slashes were done correctly. (Really should be a future invariant)
|
||||
// TODO: Find a way to check if a validator was slashed other than just checking their balance a block
|
||||
// before and after.
|
||||
|
||||
return action, fops, nil
|
||||
}
|
||||
}
|
||||
|
||||
// SimulateMsgSubmitProposal simulates a msg Submit Proposal
|
||||
// Note: Currently doesn't ensure that the proposal txt is in JSON form
|
||||
func SimulateMsgSubmitProposal(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
||||
handler := gov.NewHandler(k)
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOps []simulation.FutureOperation, err sdk.Error) {
|
||||
msg := simulationCreateMsgSubmitProposal(tb, r, keys, log)
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
// Update pool to keep invariants
|
||||
pool := sk.GetPool(ctx)
|
||||
pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(msg.InitialDeposit.AmountOf(denom)))
|
||||
sk.SetPool(ctx, pool)
|
||||
write()
|
||||
}
|
||||
event(fmt.Sprintf("gov/MsgSubmitProposal/%v", result.IsOK()))
|
||||
action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
|
||||
sender := simulation.RandomKey(r, keys)
|
||||
msg := simulationCreateMsgSubmitProposal(tb, r, sender, log)
|
||||
action = simulateHandleMsgSubmitProposal(msg, sk, handler, ctx, event)
|
||||
return action, nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func simulationCreateMsgSubmitProposal(tb testing.TB, r *rand.Rand, keys []crypto.PrivKey, log string) gov.MsgSubmitProposal {
|
||||
key := simulation.RandomKey(r, keys)
|
||||
addr := sdk.AccAddress(key.PubKey().Address())
|
||||
func simulateHandleMsgSubmitProposal(msg gov.MsgSubmitProposal, sk stake.Keeper, handler sdk.Handler, ctx sdk.Context, event func(string)) (action string) {
|
||||
ctx, write := ctx.CacheContext()
|
||||
result := handler(ctx, msg)
|
||||
if result.IsOK() {
|
||||
// Update pool to keep invariants
|
||||
pool := sk.GetPool(ctx)
|
||||
pool.LooseTokens = pool.LooseTokens.Sub(sdk.NewDecFromInt(msg.InitialDeposit.AmountOf(denom)))
|
||||
sk.SetPool(ctx, pool)
|
||||
write()
|
||||
}
|
||||
event(fmt.Sprintf("gov/MsgSubmitProposal/%v", result.IsOK()))
|
||||
action = fmt.Sprintf("TestMsgSubmitProposal: ok %v, msg %s", result.IsOK(), msg.GetSignBytes())
|
||||
return action
|
||||
}
|
||||
|
||||
func simulationCreateMsgSubmitProposal(tb testing.TB, r *rand.Rand, sender crypto.PrivKey, log string) gov.MsgSubmitProposal {
|
||||
addr := sdk.AccAddress(sender.PubKey().Address())
|
||||
deposit := randomDeposit(r)
|
||||
msg := gov.NewMsgSubmitProposal(
|
||||
simulation.RandStringOfLength(r, 5),
|
||||
|
@ -88,13 +148,22 @@ func SimulateMsgDeposit(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
|||
// SimulateMsgVote
|
||||
// nolint: unparam
|
||||
func SimulateMsgVote(k gov.Keeper, sk stake.Keeper) simulation.Operation {
|
||||
return operationSimulateMsgVote(k, sk, nil, -1)
|
||||
}
|
||||
|
||||
func operationSimulateMsgVote(k gov.Keeper, sk stake.Keeper, key crypto.PrivKey, proposalID int64) simulation.Operation {
|
||||
return func(tb testing.TB, r *rand.Rand, app *baseapp.BaseApp, ctx sdk.Context, keys []crypto.PrivKey, log string, event func(string)) (action string, fOp []simulation.FutureOperation, err sdk.Error) {
|
||||
key := simulation.RandomKey(r, keys)
|
||||
addr := sdk.AccAddress(key.PubKey().Address())
|
||||
proposalID, ok := randomProposalID(r, k, ctx)
|
||||
if !ok {
|
||||
return "no-operation", nil, nil
|
||||
if key == nil {
|
||||
key = simulation.RandomKey(r, keys)
|
||||
}
|
||||
var ok bool
|
||||
if proposalID < 0 {
|
||||
proposalID, ok = randomProposalID(r, k, ctx)
|
||||
if !ok {
|
||||
return "no-operation", nil, nil
|
||||
}
|
||||
}
|
||||
addr := sdk.AccAddress(key.PubKey().Address())
|
||||
option := randomVotingOption(r)
|
||||
msg := gov.NewMsgVote(addr, proposalID, option)
|
||||
if msg.ValidateBasic() != nil {
|
||||
|
|
|
@ -53,6 +53,7 @@ func TestGovWithRandomMessages(t *testing.T) {
|
|||
gov.InitGenesis(ctx, govKeeper, gov.DefaultGenesisState())
|
||||
}
|
||||
|
||||
// Test with unscheduled votes
|
||||
simulation.Simulate(
|
||||
t, mapp.BaseApp, appStateFn,
|
||||
[]simulation.Operation{
|
||||
|
@ -66,4 +67,18 @@ func TestGovWithRandomMessages(t *testing.T) {
|
|||
}, 10, 100,
|
||||
false,
|
||||
)
|
||||
|
||||
// Test with scheduled votes
|
||||
simulation.Simulate(
|
||||
t, mapp.BaseApp, appStateFn,
|
||||
[]simulation.Operation{
|
||||
SimulateSubmittingVotingAndSlashingForProposal(govKeeper, stakeKeeper),
|
||||
SimulateMsgDeposit(govKeeper, stakeKeeper),
|
||||
}, []simulation.RandSetup{
|
||||
setup,
|
||||
}, []simulation.Invariant{
|
||||
AllInvariants(),
|
||||
}, 10, 100,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue