Skip to content

Commit

Permalink
Simulation must not require signatures
Browse files Browse the repository at this point in the history
Ref: #2110
  • Loading branch information
alessio committed Aug 29, 2018
1 parent fd8c1e5 commit 8c49003
Show file tree
Hide file tree
Showing 16 changed files with 131 additions and 93 deletions.
23 changes: 11 additions & 12 deletions client/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,18 +54,18 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg)
txCtx = txCtx.WithSequence(accSeq)
}

passphrase, err := keys.GetPassphrase(cliCtx.FromAddressName)
if err != nil {
return err
}

if cliCtx.Gas == 0 {
txCtx, err = EnrichCtxWithGas(txCtx, cliCtx, cliCtx.FromAddressName, passphrase, msgs)
txCtx, err = EnrichCtxWithGas(txCtx, cliCtx, cliCtx.FromAddressName, msgs)
if err != nil {
return err
}
}

passphrase, err := keys.GetPassphrase(cliCtx.FromAddressName)
if err != nil {
return err
}

// build and sign the transaction
txBytes, err := txCtx.BuildAndSign(cliCtx.FromAddressName, passphrase, msgs)
if err != nil {
Expand All @@ -77,22 +77,21 @@ func SendTx(txCtx authctx.TxContext, cliCtx context.CLIContext, msgs []sdk.Msg)

// EnrichCtxWithGas calculates the gas estimate that would be consumed by the
// transaction and set the transaction's respective value accordingly.
func EnrichCtxWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name, passphrase string, msgs []sdk.Msg) (authctx.TxContext, error) {
txBytes, err := BuildAndSignTxWithZeroGas(txCtx, name, passphrase, msgs)
func EnrichCtxWithGas(txCtx authctx.TxContext, cliCtx context.CLIContext, name string, msgs []sdk.Msg) (authctx.TxContext, error) {
txBytes, err := BuildAndSignTxWithZeroGas(txCtx, name, msgs)
if err != nil {
return txCtx, err
}
estimate, adjusted, err := CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment)
_, adjusted, err := CalculateGas(cliCtx.Query, cliCtx.Codec, txBytes, cliCtx.GasAdjustment)
if err != nil {
return txCtx, err
}
fmt.Fprintf(os.Stderr, "gas: [estimated = %v] [adjusted = %v]\n", estimate, adjusted)
return txCtx.WithGas(adjusted), nil
}

// BuildAndSignTxWithZeroGas builds transactions with GasWanted set to 0.
func BuildAndSignTxWithZeroGas(txCtx authctx.TxContext, name, passphrase string, msgs []sdk.Msg) ([]byte, error) {
return txCtx.WithGas(0).BuildAndSign(name, passphrase, msgs)
func BuildAndSignTxWithZeroGas(txCtx authctx.TxContext, name string, msgs []sdk.Msg) ([]byte, error) {
return txCtx.WithGas(0).BuildWithPubKey(name, msgs)
}

// CalculateGas simulates the execution of a transaction and returns
Expand Down
16 changes: 8 additions & 8 deletions examples/democoin/x/cool/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,17 @@ func TestMsgQuiz(t *testing.T) {
require.Equal(t, acc1, res1)

// Set the trend, submit a really cool quiz and check for reward
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg1}, []int64{0}, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{1}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg1}, []int64{0}, []int64{0}, true, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{1}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(69)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{2}, false, priv1) // result without reward
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{2}, false, false, priv1) // result without reward
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(69)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{3}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{3}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(138)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []int64{0}, []int64{4}, true, priv1) // reset the trend
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{5}, false, priv1) // the same answer will nolonger do!
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg2}, []int64{0}, []int64{4}, true, true, priv1) // reset the trend
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg1}, []int64{0}, []int64{5}, false, false, priv1) // the same answer will nolonger do!
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"icecold", sdk.NewInt(138)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{6}, true, priv1) // earlier answer now relevant again
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{quizMsg2}, []int64{0}, []int64{6}, true, true, priv1) // earlier answer now relevant again
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"badvibesonly", sdk.NewInt(69)}, {"icecold", sdk.NewInt(138)}})
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []int64{0}, []int64{7}, false, priv1) // expect to fail to set the trend to something which is not cool
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{setTrendMsg3}, []int64{0}, []int64{7}, false, false, priv1) // expect to fail to set the trend to something which is not cool
}
6 changes: 3 additions & 3 deletions examples/democoin/x/pow/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ func TestMsgMine(t *testing.T) {

// Mine and check for reward
mineMsg1 := GenerateMsgMine(addr1, 1, 2)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []int64{0}, []int64{0}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg1}, []int64{0}, []int64{0}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(1)}})
// Mine again and check for reward
mineMsg2 := GenerateMsgMine(addr1, 2, 3)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, true, true, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}})
// Mine again - should be invalid
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, false, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{mineMsg2}, []int64{0}, []int64{1}, false, false, priv1)
mock.CheckBalance(t, mapp, addr1, sdk.Coins{{"pow", sdk.NewInt(2)}})
}
33 changes: 19 additions & 14 deletions x/auth/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {
sigs := stdTx.GetSignatures()
signerAddrs := stdTx.GetSigners()
msgs := tx.GetMsgs()
if simulate {
sigs = make([]StdSignature, len(signerAddrs))
}

// charge gas for the memo
newCtx.GasMeter().ConsumeGas(memoCostPerByte*sdk.Gas(len(stdTx.GetMemo())), "memo")
Expand All @@ -88,10 +91,7 @@ func NewAnteHandler(am AccountMapper, fck FeeCollectionKeeper) sdk.AnteHandler {

// check signature, return account with incremented nonce
signBytes := StdSignBytes(newCtx.ChainID(), accNums[i], sequences[i], fee, msgs, stdTx.GetMemo())
signerAcc, res := processSig(
newCtx, am,
signerAddr, sig, signBytes,
)
signerAcc, res := processSig(newCtx, am, signerAddr, sig, signBytes, simulate)
if !res.IsOK() {
return newCtx, res, true
}
Expand Down Expand Up @@ -147,11 +147,11 @@ func validateBasic(tx StdTx) (err sdk.Error) {

// verify the signature and increment the sequence.
// if the account doesn't have a pubkey, set it.
// nolint: gocyclo
func processSig(
ctx sdk.Context, am AccountMapper,
addr sdk.AccAddress, sig StdSignature, signBytes []byte) (
addr sdk.AccAddress, sig StdSignature, signBytes []byte, simulate bool) (
acc Account, res sdk.Result) {

// Get the account.
acc = am.GetAccount(ctx, addr)
if acc == nil {
Expand All @@ -160,14 +160,14 @@ func processSig(

// Check account number.
accnum := acc.GetAccountNumber()
if accnum != sig.AccountNumber {
if !simulate && (accnum != sig.AccountNumber) {
return nil, sdk.ErrInvalidSequence(
fmt.Sprintf("Invalid account number. Got %d, expected %d", sig.AccountNumber, accnum)).Result()
}

// Check and increment sequence number.
seq := acc.GetSequence()
if seq != sig.Sequence {
if !simulate && (seq != sig.Sequence) {
return nil, sdk.ErrInvalidSequence(
fmt.Sprintf("Invalid sequence. Got %d, expected %d", sig.Sequence, seq)).Result()
}
Expand All @@ -179,7 +179,7 @@ func processSig(
// If pubkey is not known for account,
// set it from the StdSignature.
pubKey := acc.GetPubKey()
if pubKey == nil {
if !simulate && (pubKey == nil) {
pubKey = sig.PubKey
if pubKey == nil {
return nil, sdk.ErrInvalidPubKey("PubKey not found").Result()
Expand All @@ -188,15 +188,20 @@ func processSig(
return nil, sdk.ErrInvalidPubKey(
fmt.Sprintf("PubKey does not match Signer address %v", addr)).Result()
}
err = acc.SetPubKey(pubKey)
if err != nil {
return nil, sdk.ErrInternal("setting PubKey on signer's account").Result()
}
}
if simulate && (pubKey == nil) {
// In case pubkey is nil, both signature verification and gasKVStore.Set()
// will consume the largest amount
pubKey = secp256k1.GenPrivKey().PubKey()
}
err = acc.SetPubKey(pubKey)
if err != nil {
return nil, sdk.ErrInternal("setting PubKey on signer's account").Result()
}

// Check sig.
consumeSignatureVerificationGas(ctx.GasMeter(), pubKey)
if !pubKey.VerifyBytes(signBytes, sig.Signature) {
if !simulate && !pubKey.VerifyBytes(signBytes, sig.Signature) {
return nil, sdk.ErrUnauthorized("signature verification failed").Result()
}

Expand Down
27 changes: 27 additions & 0 deletions x/auth/client/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,3 +148,30 @@ func (ctx TxContext) BuildAndSign(name, passphrase string, msgs []sdk.Msg) ([]by

return ctx.Sign(name, passphrase, msg)
}

// BuildWithPubKey builds a single message to be signed and
// attach the public key associated to the given name.
func (ctx TxContext) BuildWithPubKey(name string, msgs []sdk.Msg) ([]byte, error) {
msg, err := ctx.Build(msgs)
if err != nil {
return nil, err
}

keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}

info, err := keybase.Get(name)
if err != nil {
return nil, err
}

sigs := []auth.StdSignature{{
AccountNumber: msg.AccountNumber,
Sequence: msg.Sequence,
PubKey: info.GetPubKey(),
}}

return ctx.Codec.MarshalBinary(auth.NewStdTx(msg.Msgs, msg.Fee, sigs, msg.Memo))
}
77 changes: 42 additions & 35 deletions x/bank/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ type (
}

appTestCase struct {
expSimPass bool
expPass bool
msgs []sdk.Msg
accNums []int64
Expand Down Expand Up @@ -107,27 +108,29 @@ func TestMsgSendWithAccounts(t *testing.T) {

testCases := []appTestCase{
{
msgs: []sdk.Msg{sendMsg1},
accNums: []int64{0},
accSeqs: []int64{0},
expPass: true,
privKeys: []crypto.PrivKey{priv1},
msgs: []sdk.Msg{sendMsg1},
accNums: []int64{0},
accSeqs: []int64{0},
expSimPass: true,
expPass: true,
privKeys: []crypto.PrivKey{priv1},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 57)}},
{addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}},
},
},
{
msgs: []sdk.Msg{sendMsg1, sendMsg2},
accNums: []int64{0},
accSeqs: []int64{0},
expPass: false,
privKeys: []crypto.PrivKey{priv1},
msgs: []sdk.Msg{sendMsg1, sendMsg2},
accNums: []int64{0},
accSeqs: []int64{0},
expSimPass: true,
expPass: false,
privKeys: []crypto.PrivKey{priv1},
},
}

for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)

for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
Expand All @@ -144,7 +147,7 @@ func TestMsgSendWithAccounts(t *testing.T) {
require.Equal(t, sdk.ToABCICode(sdk.CodespaceRoot, sdk.CodeUnauthorized), res.Code, res.Log)

// resigning the tx with the bumped sequence should work
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1, sendMsg2}, []int64{0}, []int64{1}, true, priv1)
mock.SignCheckDeliver(t, mapp.BaseApp, []sdk.Msg{sendMsg1, sendMsg2}, []int64{0}, []int64{1}, true, true, priv1)
}

func TestMsgSendMultipleOut(t *testing.T) {
Expand All @@ -163,11 +166,12 @@ func TestMsgSendMultipleOut(t *testing.T) {

testCases := []appTestCase{
{
msgs: []sdk.Msg{sendMsg2},
accNums: []int64{0},
accSeqs: []int64{0},
expPass: true,
privKeys: []crypto.PrivKey{priv1},
msgs: []sdk.Msg{sendMsg2},
accNums: []int64{0},
accSeqs: []int64{0},
expSimPass: true,
expPass: true,
privKeys: []crypto.PrivKey{priv1},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}},
{addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 47)}},
Expand All @@ -177,7 +181,7 @@ func TestMsgSendMultipleOut(t *testing.T) {
}

for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)

for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
Expand Down Expand Up @@ -205,11 +209,12 @@ func TestSengMsgMultipleInOut(t *testing.T) {

testCases := []appTestCase{
{
msgs: []sdk.Msg{sendMsg3},
accNums: []int64{0, 2},
accSeqs: []int64{0, 0},
expPass: true,
privKeys: []crypto.PrivKey{priv1, priv4},
msgs: []sdk.Msg{sendMsg3},
accNums: []int64{0, 2},
accSeqs: []int64{0, 0},
expSimPass: true,
expPass: true,
privKeys: []crypto.PrivKey{priv1, priv4},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}},
{addr4, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}},
Expand All @@ -220,7 +225,7 @@ func TestSengMsgMultipleInOut(t *testing.T) {
}

for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)

for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
Expand All @@ -240,30 +245,32 @@ func TestMsgSendDependent(t *testing.T) {

testCases := []appTestCase{
{
msgs: []sdk.Msg{sendMsg1},
accNums: []int64{0},
accSeqs: []int64{0},
expPass: true,
privKeys: []crypto.PrivKey{priv1},
msgs: []sdk.Msg{sendMsg1},
accNums: []int64{0},
accSeqs: []int64{0},
expSimPass: true,
expPass: true,
privKeys: []crypto.PrivKey{priv1},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 32)}},
{addr2, sdk.Coins{sdk.NewInt64Coin("foocoin", 10)}},
},
},
{
msgs: []sdk.Msg{sendMsg4},
accNums: []int64{1},
accSeqs: []int64{0},
expPass: true,
privKeys: []crypto.PrivKey{priv2},
msgs: []sdk.Msg{sendMsg4},
accNums: []int64{1},
accSeqs: []int64{0},
expSimPass: true,
expPass: true,
privKeys: []crypto.PrivKey{priv2},
expectedBalances: []expectedBalance{
{addr1, sdk.Coins{sdk.NewInt64Coin("foocoin", 42)}},
},
},
}

for _, tc := range testCases {
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expPass, tc.privKeys...)
mock.SignCheckDeliver(t, mapp.BaseApp, tc.msgs, tc.accNums, tc.accSeqs, tc.expSimPass, tc.expPass, tc.privKeys...)

for _, eb := range tc.expectedBalances {
mock.CheckBalance(t, mapp, eb.addr, eb.coins)
Expand Down
2 changes: 1 addition & 1 deletion x/bank/client/rest/sendtx.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func SendRequestHandlerFn(cdc *wire.Codec, kb keys.Keybase, cliCtx context.CLICo
}

if m.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, m.Password, []sdk.Msg{msg})
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, m.LocalAccountName, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
return
Expand Down
2 changes: 1 addition & 1 deletion x/gov/client/rest/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func signAndBuild(w http.ResponseWriter, cliCtx context.CLIContext, baseReq base
}

if baseReq.Gas == 0 {
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, baseReq.Name, baseReq.Password, []sdk.Msg{msg})
newCtx, err := utils.EnrichCtxWithGas(txCtx, cliCtx, baseReq.Name, []sdk.Msg{msg})
if err != nil {
utils.WriteErrorResponse(&w, http.StatusInternalServerError, err.Error())
return
Expand Down
Loading

0 comments on commit 8c49003

Please sign in to comment.