s/CallTx/AppTx/g; NamedPlugins

This commit is contained in:
Jae Kwon 2016-03-27 12:47:50 -07:00
parent 601a654b7d
commit fa39c9da5c
9 changed files with 153 additions and 131 deletions

View File

@ -12,23 +12,34 @@ import (
tmsp "github.com/tendermint/tmsp/types" tmsp "github.com/tendermint/tmsp/types"
) )
const version = "0.1" const (
const maxTxSize = 10240 version = "0.1"
maxTxSize = 10240
typeByteBase = 0x01
typeByteGov = 0x02
pluginNameBase = "base"
pluginNameGov = "gov"
)
type Basecoin struct { type Basecoin struct {
eyesCli *eyes.Client eyesCli *eyes.Client
govMint *gov.Governmint govMint *gov.Governmint
state *state.State state *state.State
plugins *types.Plugins
} }
func NewBasecoin(eyesCli *eyes.Client) *Basecoin { func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
state_ := state.NewState(eyesCli)
govMint := gov.NewGovernmint(eyesCli) govMint := gov.NewGovernmint(eyesCli)
state_.RegisterPlugin("GOV", govMint) state_ := state.NewState(eyesCli)
plugins := types.NewPlugins()
plugins.RegisterPlugin(typeByteGov, pluginNameGov, govMint) // TODO: make constants
return &Basecoin{ return &Basecoin{
eyesCli: eyesCli, eyesCli: eyesCli,
govMint: govMint, govMint: govMint,
state: state_, state: state_,
plugins: plugins,
} }
} }
@ -39,11 +50,10 @@ func (app *Basecoin) Info() string {
// TMSP::SetOption // TMSP::SetOption
func (app *Basecoin) SetOption(key string, value string) (log string) { func (app *Basecoin) SetOption(key string, value string) (log string) {
pluginName, key := splitKey(key) pluginName, key := splitKey(key)
if pluginName != "BASE" { if pluginName != pluginNameBase {
// Set option on plugin // Set option on plugin
plugin := app.state.GetPlugin(pluginName) plugin := app.plugins.GetByName(pluginName)
if plugin == nil { if plugin == nil {
return "Invalid plugin name: " + pluginName return "Invalid plugin name: " + pluginName
} }
@ -84,7 +94,7 @@ func (app *Basecoin) AppendTx(txBytes []byte) (res tmsp.Result) {
return tmsp.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error()) return tmsp.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
} }
// Validate and exec tx // Validate and exec tx
res = state.ExecTx(app.state, tx, false, nil) res = state.ExecTx(app.state, app.plugins, tx, false, nil)
if res.IsErr() { if res.IsErr() {
return res.PrependLog("Error in AppendTx") return res.PrependLog("Error in AppendTx")
} }
@ -103,7 +113,7 @@ func (app *Basecoin) CheckTx(txBytes []byte) (res tmsp.Result) {
return tmsp.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error()) return tmsp.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
} }
// Validate tx // Validate tx
res = state.ExecTx(app.state, tx, true, nil) res = state.ExecTx(app.state, app.plugins, tx, true, nil)
if res.IsErr() { if res.IsErr() {
return res.PrependLog("Error in CheckTx") return res.PrependLog("Error in CheckTx")
} }
@ -113,8 +123,8 @@ func (app *Basecoin) CheckTx(txBytes []byte) (res tmsp.Result) {
// TMSP::Query // TMSP::Query
func (app *Basecoin) Query(query []byte) (res tmsp.Result) { func (app *Basecoin) Query(query []byte) (res tmsp.Result) {
pluginName, queryStr := splitKey(string(query)) pluginName, queryStr := splitKey(string(query))
if pluginName != "BASE" { if pluginName != pluginNameBase {
plugin := app.state.GetPlugin(pluginName) plugin := app.plugins.GetByName(pluginName)
if plugin == nil { if plugin == nil {
return tmsp.ErrBaseUnknownPlugin.SetLog(Fmt("Unknown plugin %v", pluginName)) return tmsp.ErrBaseUnknownPlugin.SetLog(Fmt("Unknown plugin %v", pluginName))
} }
@ -132,7 +142,7 @@ func (app *Basecoin) Query(query []byte) (res tmsp.Result) {
// TMSP::Commit // TMSP::Commit
func (app *Basecoin) Commit() (res tmsp.Result) { func (app *Basecoin) Commit() (res tmsp.Result) {
// First, commit all the plugins // First, commit all the plugins
for _, plugin := range app.state.GetPlugins() { for _, plugin := range app.plugins.GetList() {
res = plugin.Commit() res = plugin.Commit()
if res.IsErr() { if res.IsErr() {
PanicSanity(Fmt("Error committing plugin %v", plugin.Name)) PanicSanity(Fmt("Error committing plugin %v", plugin.Name))
@ -162,8 +172,8 @@ func (app *Basecoin) EndBlock(height uint64) []*tmsp.Validator {
// Splits the string at the first :. // Splits the string at the first :.
// if there are none, the second string is nil. // if there are none, the second string is nil.
func splitKey(key string) (prefix string, sufix string) { func splitKey(key string) (prefix string, sufix string) {
if strings.Contains(key, ":") { if strings.Contains(key, "/") {
keyParts := strings.SplitN(key, ":", 2) keyParts := strings.SplitN(key, "/", 2)
return keyParts[0], keyParts[1] return keyParts[0], keyParts[1]
} }
return key, "" return key, ""

View File

@ -4,6 +4,7 @@ import (
"encoding/json" "encoding/json"
"flag" "flag"
"fmt" "fmt"
"reflect"
"github.com/tendermint/basecoin/app" "github.com/tendermint/basecoin/app"
. "github.com/tendermint/go-common" . "github.com/tendermint/go-common"
@ -58,14 +59,36 @@ type KeyValue struct {
} }
func loadGenesis(filePath string) (kvz []KeyValue) { func loadGenesis(filePath string) (kvz []KeyValue) {
kvz_ := []interface{}{}
bytes, err := ReadFile(filePath) bytes, err := ReadFile(filePath)
if err != nil { if err != nil {
Exit("loading genesis file: " + err.Error()) Exit("loading genesis file: " + err.Error())
} }
fmt.Println(">>", string(bytes)) err = json.Unmarshal(bytes, &kvz_)
err = json.Unmarshal(bytes, &kvz)
if err != nil { if err != nil {
Exit("parsing genesis file: " + err.Error()) Exit("parsing genesis file: " + err.Error())
} }
return if len(kvz_)%2 != 0 {
Exit("genesis cannot have an odd number of items. Format = [key1, value1, key2, value2, ...]")
}
for i := 0; i < len(kvz_)/2; i++ {
keyIfc := kvz_[i]
valueIfc := kvz_[i+1]
var key, value string
key, ok := keyIfc.(string)
if !ok {
Exit(Fmt("genesis had invalid key %v of type %v", keyIfc, reflect.TypeOf(keyIfc)))
}
if value_, ok := valueIfc.(string); ok {
value = value_
} else {
valueBytes, err := json.Marshal(value_)
if err != nil {
Exit(Fmt("genesis had invalid value %v: %v", value_, err.Error()))
}
value = string(valueBytes)
}
kvz = append(kvz, KeyValue{key, value})
}
return kvz
} }

View File

@ -1,10 +1,7 @@
[ [
{ "base/chainID", "test_chain_id",
"key": "chainID", "base/account", {
"value": "test_chain_id" "pub_key", [1, "67D3B5EAF0C0BF6B5A602D359DAECC86A7A74053490EC37AE08E71360587C870"],
}, "balance": 1000
{
"key": "account",
"value": "{\"pub_key\":[1,\"3E8A80E5412FD1711995D5888F5FA2EFB26EDD0970F1E27CE0B55CD237439E18\"],\"balance\":1000}"
} }
] ]

View File

@ -10,7 +10,7 @@ import (
) )
// If the tx is invalid, a TMSP error will be returned. // If the tx is invalid, a TMSP error will be returned.
func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp.Result { func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp.Result {
// TODO: do something with fees // TODO: do something with fees
fees := int64(0) fees := int64(0)
@ -68,7 +68,7 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
return tmsp.OK return tmsp.OK
case *types.CallTx: case *types.AppTx:
// First, get input account // First, get input account
inAcc := state.GetAccount(tx.Input.Address) inAcc := state.GetAccount(tx.Input.Address)
if inAcc == nil { if inAcc == nil {
@ -93,9 +93,10 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
} }
// Validate call address // Validate call address
plugin := state.GetPlugin(string(tx.Address)) plugin := pgz.GetByByte(tx.Type)
if plugin != nil { if plugin != nil {
return tmsp.ErrBaseUnknownAddress.AppendLog(Fmt("Unrecognized address %X (%v)", tx.Address, string(tx.Address))) return tmsp.ErrBaseUnknownAddress.AppendLog(
Fmt("Unrecognized type byte %v", tx.Type))
} }
// Good! // Good!
@ -105,7 +106,7 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
state.SetCheckAccount(tx.Input.Address, inAcc.Sequence, inAcc.Balance) state.SetCheckAccount(tx.Input.Address, inAcc.Sequence, inAcc.Balance)
inAccCopy := inAcc.Copy() inAccCopy := inAcc.Copy()
// If this is AppendTx, actually save accounts // If this is a CheckTx, stop now.
if isCheckTx { if isCheckTx {
return tmsp.OK return tmsp.OK
} }
@ -115,7 +116,7 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
cache.SetAccount(tx.Input.Address, inAcc) cache.SetAccount(tx.Input.Address, inAcc)
gas := int64(1) // TODO gas := int64(1) // TODO
ctx := types.NewCallContext(cache, inAcc, value, &gas) ctx := types.NewCallContext(cache, inAcc, value, &gas)
res = plugin.CallTx(ctx, tx.Data) res = plugin.RunTx(ctx, tx.Data)
if res.IsOK() { if res.IsOK() {
cache.Sync() cache.Sync()
log.Info("Successful execution") log.Info("Successful execution")
@ -131,7 +132,7 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
} }
*/ */
} else { } else {
log.Info("CallTx failed", "error", res) log.Info("AppTx failed", "error", res)
// Just return the value and return. // Just return the value and return.
// TODO: return gas? // TODO: return gas?
inAccCopy.Balance += value inAccCopy.Balance += value

View File

@ -8,11 +8,9 @@ import (
) )
type State struct { type State struct {
chainID string chainID string
eyesCli *eyes.Client eyesCli *eyes.Client
checkCache map[string]checkAccount checkCache map[string]checkAccount
plugins map[string]types.Plugin
pluginsList []types.NamedPlugin
LastBlockHeight uint64 LastBlockHeight uint64
LastBlockHash []byte LastBlockHash []byte
@ -24,7 +22,6 @@ func NewState(eyesCli *eyes.Client) *State {
chainID: "", chainID: "",
eyesCli: eyesCli, eyesCli: eyesCli,
checkCache: make(map[string]checkAccount), checkCache: make(map[string]checkAccount),
plugins: make(map[string]types.Plugin),
} }
return s return s
} }
@ -40,22 +37,6 @@ func (s *State) GetChainID() string {
return s.chainID return s.chainID
} }
func (s *State) RegisterPlugin(name string, plugin types.Plugin) {
s.plugins[name] = plugin
s.pluginsList = append(s.pluginsList, types.NamedPlugin{
Name: name,
Plugin: plugin,
})
}
func (s *State) GetPlugin(name string) types.Plugin {
return s.plugins[name]
}
func (s *State) GetPlugins() []types.NamedPlugin {
return s.pluginsList
}
//---------------------------------------- //----------------------------------------
// CheckTx state // CheckTx state

View File

@ -13,7 +13,7 @@ import (
) )
func main() { func main() {
//testSendTx() testSendTx()
testGov() testGov()
} }
@ -28,8 +28,8 @@ func testSendTx() {
// Seed Basecoin with account // Seed Basecoin with account
tAcc := tPriv.Account tAcc := tPriv.Account
tAcc.Balance = 1000 tAcc.Balance = 1000
bcApp.SetOption("chainID", "test_chain_id") fmt.Println(bcApp.SetOption("base/chainID", "test_chain_id"))
bcApp.SetOption("account", string(wire.JSONBytes(tAcc))) fmt.Println(bcApp.SetOption("base/account", string(wire.JSONBytes(tAcc))))
// Construct a SendTx signature // Construct a SendTx signature
tx := &types.SendTx{ tx := &types.SendTx{
@ -51,10 +51,11 @@ func testSendTx() {
// Sign request // Sign request
signBytes := tx.SignBytes("test_chain_id") signBytes := tx.SignBytes("test_chain_id")
fmt.Printf("SIGNBYTES %X", signBytes) fmt.Printf("Sign bytes: %X\n", signBytes)
sig := tPriv.PrivKey.Sign(signBytes) sig := tPriv.PrivKey.Sign(signBytes)
tx.Inputs[0].Signature = sig tx.Inputs[0].Signature = sig
//fmt.Println("tx:", tx) //fmt.Println("tx:", tx)
fmt.Printf("Signed TX bytes: %X\n", wire.BinaryBytes(tx))
// Write request // Write request
txBytes := wire.BinaryBytes(tx) txBytes := wire.BinaryBytes(tx)
@ -78,7 +79,7 @@ func testGov() {
ID: "", ID: "",
PubKey: tAcc.PubKey, PubKey: tAcc.PubKey,
} }
log := bcApp.SetOption("GOV:admin", string(wire.JSONBytes(adminEntity))) log := bcApp.SetOption("gov/admin", string(wire.JSONBytes(adminEntity)))
if log != "Success" { if log != "Success" {
Exit(Fmt("Failed to set option: %v", log)) Exit(Fmt("Failed to set option: %v", log))
} }

View File

@ -9,16 +9,19 @@ import (
// if any gas is left the user is // if any gas is left the user is
type Plugin interface { type Plugin interface {
SetOption(key string, value string) (log string) SetOption(key string, value string) (log string)
CallTx(ctx CallContext, txBytes []byte) (res tmsp.Result) RunTx(ctx CallContext, txBytes []byte) (res tmsp.Result)
Query(query []byte) (res tmsp.Result) Query(query []byte) (res tmsp.Result)
Commit() (res tmsp.Result) Commit() (res tmsp.Result)
} }
type NamedPlugin struct { type NamedPlugin struct {
Byte byte
Name string Name string
Plugin Plugin
} }
//----------------------------------------
type CallContext struct { type CallContext struct {
Cache AccountCacher Cache AccountCacher
Caller *Account Caller *Account
@ -34,3 +37,40 @@ func NewCallContext(cache AccountCacher, caller *Account, value int64, gas *int6
Gas: gas, Gas: gas,
} }
} }
//----------------------------------------
type Plugins struct {
byByte map[byte]Plugin
byName map[string]Plugin
plist []NamedPlugin
}
func NewPlugins() *Plugins {
return &Plugins{
byByte: make(map[byte]Plugin),
byName: make(map[string]Plugin),
}
}
func (pgz *Plugins) RegisterPlugin(typeByte byte, name string, plugin Plugin) {
pgz.byByte[typeByte] = plugin
pgz.byName[name] = plugin
pgz.plist = append(pgz.plist, NamedPlugin{
Byte: typeByte,
Name: name,
Plugin: plugin,
})
}
func (pgz *Plugins) GetByByte(typeByte byte) Plugin {
return pgz.byByte[typeByte]
}
func (pgz *Plugins) GetByName(name string) Plugin {
return pgz.byName[name]
}
func (pgz *Plugins) GetList() []NamedPlugin {
return pgz.plist
}

View File

@ -1,14 +1,12 @@
package types package types
import ( import (
"bytes"
"encoding/json" "encoding/json"
. "github.com/tendermint/go-common" . "github.com/tendermint/go-common"
"github.com/tendermint/go-crypto" "github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire" "github.com/tendermint/go-wire"
tmsp "github.com/tendermint/tmsp/types" tmsp "github.com/tendermint/tmsp/types"
"golang.org/x/crypto/ripemd160"
) )
/* /*
@ -16,7 +14,7 @@ Tx (Transaction) is an atomic operation on the ledger state.
Account Types: Account Types:
- SendTx Send coins to address - SendTx Send coins to address
- CallTx Send a msg to a contract that runs in the vm - AppTx Send a msg to a contract that runs in the vm
*/ */
type Tx interface { type Tx interface {
@ -27,13 +25,13 @@ type Tx interface {
const ( const (
// Account transactions // Account transactions
TxTypeSend = byte(0x01) TxTypeSend = byte(0x01)
TxTypeCall = byte(0x02) TxTypeApp = byte(0x02)
) )
var _ = wire.RegisterInterface( var _ = wire.RegisterInterface(
struct{ Tx }{}, struct{ Tx }{},
wire.ConcreteType{&SendTx{}, TxTypeSend}, wire.ConcreteType{&SendTx{}, TxTypeSend},
wire.ConcreteType{&CallTx{}, TxTypeCall}, wire.ConcreteType{&AppTx{}, TxTypeApp},
) )
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -56,11 +54,6 @@ func (txIn TxInput) ValidateBasic() tmsp.Result {
return tmsp.OK return tmsp.OK
} }
func (txIn TxInput) SignBytes() []byte {
return []byte(Fmt(`{"address":"%X","amount":%v,"sequence":%v}`,
txIn.Address, txIn.Amount, txIn.Sequence))
}
func (txIn TxInput) String() string { func (txIn TxInput) String() string {
return Fmt("TxInput{%X,%v,%v,%v,%v}", txIn.Address, txIn.Amount, txIn.Sequence, txIn.Signature, txIn.PubKey) return Fmt("TxInput{%X,%v,%v,%v,%v}", txIn.Address, txIn.Amount, txIn.Sequence, txIn.Signature, txIn.PubKey)
} }
@ -82,11 +75,6 @@ func (txOut TxOutput) ValidateBasic() tmsp.Result {
return tmsp.OK return tmsp.OK
} }
func (txOut TxOutput) SignBytes() []byte {
return []byte(Fmt(`{"address":"%X","amount":%v}`,
txOut.Address, txOut.Amount))
}
func (txOut TxOutput) String() string { func (txOut TxOutput) String() string {
return Fmt("TxOutput{%X,%v}", txOut.Address, txOut.Amount) return Fmt("TxOutput{%X,%v}", txOut.Address, txOut.Amount)
} }
@ -99,24 +87,17 @@ type SendTx struct {
} }
func (tx *SendTx) SignBytes(chainID string) []byte { func (tx *SendTx) SignBytes(chainID string) []byte {
var buf = new(bytes.Buffer) signBytes := wire.BinaryBytes(chainID)
buf.Write([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID)))) sigz := make([]crypto.Signature, len(tx.Inputs))
buf.Write([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeSend))) for i, input := range tx.Inputs {
for i, in := range tx.Inputs { sigz[i] = input.Signature
buf.Write(in.SignBytes()) tx.Inputs[i].Signature = nil
if i != len(tx.Inputs)-1 {
buf.Write([]byte(","))
}
} }
buf.Write([]byte(`],"outputs":[`)) signBytes = append(signBytes, wire.BinaryBytes(tx)...)
for i, out := range tx.Outputs { for i := range tx.Inputs {
buf.Write(out.SignBytes()) tx.Inputs[i].Signature = sigz[i]
if i != len(tx.Outputs)-1 {
buf.Write([]byte(","))
}
} }
buf.Write([]byte(`]}]}`)) return signBytes
return buf.Bytes()
} }
func (tx *SendTx) String() string { func (tx *SendTx) String() string {
@ -125,35 +106,25 @@ func (tx *SendTx) String() string {
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type CallTx struct { type AppTx struct {
Input TxInput `json:"input"` Type byte `json:"type"` // Which app
Address []byte `json:"address"` Gas int64 `json:"gas"`
GasLimit int64 `json:"gas_limit"` Fee int64 `json:"fee"`
Fee int64 `json:"fee"` Input TxInput `json:"input"`
Data []byte `json:"data"` Data []byte `json:"data"`
} }
func (tx *CallTx) SignBytes(chainID string) []byte { func (tx *AppTx) SignBytes(chainID string) []byte {
var buf = new(bytes.Buffer) signBytes := wire.BinaryBytes(chainID)
buf.Write([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID)))) sig := tx.Input.Signature
buf.Write([]byte(Fmt(`,"tx":[%v,{"address":"%X","data":"%X"`, TxTypeCall, tx.Address, tx.Data))) tx.Input.Signature = nil
buf.Write([]byte(Fmt(`,"fee":%v,"gas_limit":%v,"input":`, tx.Fee, tx.GasLimit))) signBytes = append(signBytes, wire.BinaryBytes(tx)...)
buf.Write(tx.Input.SignBytes()) tx.Input.Signature = sig
buf.Write([]byte(`}]}`)) return signBytes
return buf.Bytes()
} }
func (tx *CallTx) String() string { func (tx *AppTx) String() string {
return Fmt("CallTx{%v -> %x: %x}", tx.Input, tx.Address, tx.Data) return Fmt("AppTx{%v %v %v %v -> %X}", tx.Type, tx.Gas, tx.Fee, tx.Input, tx.Data)
}
func NewContractAddress(caller []byte, nonce int) []byte {
temp := make([]byte, 32+8)
copy(temp, caller)
PutInt64BE(temp[32:], int64(nonce))
hasher := ripemd160.New()
hasher.Write(temp) // does not error
return hasher.Sum(nil)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -34,31 +34,29 @@ func TestSendTxSignable(t *testing.T) {
}, },
} }
signBytes := sendTx.SignBytes(chainID) signBytes := sendTx.SignBytes(chainID)
signStr := string(signBytes) signBytesHex := Fmt("%X", signBytes)
expected := Fmt(`{"chain_id":"%s","tx":[1,{"inputs":[{"address":"696E70757431","amount":12345,"sequence":67890},{"address":"696E70757432","amount":111,"sequence":222}],"outputs":[{"address":"6F757470757431","amount":333},{"address":"6F757470757432","amount":444}]}]}`, expected := "010A746573745F636861696E0101020106696E7075743100000000000030390301093200000106696E70757432000000000000006F01DE0000010201076F757470757431000000000000014D01076F75747075743200000000000001BC"
chainID) if signBytesHex != expected {
if signStr != expected { t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex)
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
} }
} }
func TestCallTxSignable(t *testing.T) { func TestAppTxSignable(t *testing.T) {
callTx := &CallTx{ callTx := &AppTx{
Type: 0x01,
Gas: 111,
Fee: 222,
Input: TxInput{ Input: TxInput{
Address: []byte("input1"), Address: []byte("input1"),
Amount: 12345, Amount: 12345,
Sequence: 67890, Sequence: 67890,
}, },
Address: []byte("contract1"), Data: []byte("data1"),
GasLimit: 111,
Fee: 222,
Data: []byte("data1"),
} }
signBytes := callTx.SignBytes(chainID) signBytes := callTx.SignBytes(chainID)
signStr := string(signBytes) signBytesHex := Fmt("%X", signBytes)
expected := Fmt(`{"chain_id":"%s","tx":[2,{"address":"636F6E747261637431","data":"6461746131","fee":222,"gas_limit":111,"input":{"address":"696E70757431","amount":12345,"sequence":67890}}]}`, expected := "010A746573745F636861696E0101000000000000006F00000000000000DE0106696E70757431000000000000303903010932000001056461746131"
chainID) if signBytesHex != expected {
if signStr != expected { t.Errorf("Got unexpected sign string for AppTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex)
t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)
} }
} }