diff --git a/app/app.go b/app/app.go index 59f900347..72f8a61c4 100644 --- a/app/app.go +++ b/app/app.go @@ -51,14 +51,15 @@ func (app *Basecoin) RegisterPlugin(plugin types.Plugin) { } // ABCI::SetOption -func (app *Basecoin) SetOption(key string, value string) (log string) { - PluginName, key := splitKey(key) - if PluginName != PluginNameBase { +func (app *Basecoin) SetOption(key string, value string) string { + pluginName, key := splitKey(key) + if pluginName != PluginNameBase { // Set option on plugin - plugin := app.plugins.GetByName(PluginName) + plugin := app.plugins.GetByName(pluginName) if plugin == nil { - return "Invalid plugin name: " + PluginName + return "Invalid plugin name: " + pluginName } + log.Info("SetOption on plugin", "plugin", pluginName, "key", key, "value", value) return plugin.SetOption(app.state, key, value) } else { // Set option on basecoin @@ -74,6 +75,7 @@ func (app *Basecoin) SetOption(key string, value string) (log string) { return "Error decoding acc message: " + err.Error() } app.state.SetAccount(acc.PubKey.Address(), acc) + log.Info("SetAccount", "addr", acc.PubKey.Address(), "acc", acc) return "Success" } return "Unrecognized option key " + key @@ -194,9 +196,3 @@ func splitKey(key string) (prefix string, suffix string) { } return key, "" } - -// (not meant to be called) -// assert that Basecoin implements `abci.Application` at compile-time -func _assertABCIApplication(basecoin *Basecoin) abci.Application { - return basecoin -} diff --git a/app/genesis.go b/app/genesis.go index 93848c893..6933780b3 100644 --- a/app/genesis.go +++ b/app/genesis.go @@ -17,7 +17,7 @@ func (app *Basecoin) LoadGenesis(path string) error { for _, kv := range kvz { log := app.SetOption(kv.Key, kv.Value) // TODO: remove debug output - fmt.Printf("Set %v=%v. Log: %v", kv.Key, kv.Value, log) + fmt.Printf("Set %v=%v. Log: %v\n", kv.Key, kv.Value, log) } return nil } diff --git a/app/log.go b/app/log.go new file mode 100644 index 000000000..52dc2ddfa --- /dev/null +++ b/app/log.go @@ -0,0 +1,7 @@ +package app + +import ( + "github.com/tendermint/go-logger" +) + +var log = logger.New("module", "app") diff --git a/cmd/basecoin/main.go b/cmd/basecoin/main.go index 77f9827fd..8a39a4902 100644 --- a/cmd/basecoin/main.go +++ b/cmd/basecoin/main.go @@ -17,8 +17,8 @@ func main() { commands.TxCmd, commands.QueryCmd, commands.KeyCmd, - commands.VerifyCmd, // TODO: move to merkleeyes? - commands.BlockCmd, // TODO: move to adam? + commands.VerifyCmd, + commands.BlockCmd, commands.AccountCmd, } app.Run(os.Args) diff --git a/cmd/commands/ibc.go b/cmd/commands/ibc.go index f8d0e0b61..34f4b7923 100644 --- a/cmd/commands/ibc.go +++ b/cmd/commands/ibc.go @@ -9,7 +9,6 @@ import ( "github.com/urfave/cli" "github.com/tendermint/basecoin/plugins/ibc" - "github.com/tendermint/basecoin/types" cmn "github.com/tendermint/go-common" "github.com/tendermint/go-merkle" @@ -17,10 +16,9 @@ import ( tmtypes "github.com/tendermint/tendermint/types" ) -// Register the IBC plugin at start and for transactions -func RegisterIBC() { - RegisterTxSubcommand(IbcCmd) - RegisterStartPlugin("ibc", func() types.Plugin { return ibc.New() }) +// returns a new IBC plugin to be registered with Basecoin +func NewIBCPlugin() *ibc.IBCPlugin { + return ibc.New() } //--------------------------------------------------------------------- @@ -104,9 +102,9 @@ var ( // ibc commands var ( - IbcCmd = cli.Command{ + IbcTxCmd = cli.Command{ Name: "ibc", - Usage: "Send a transaction to the interblockchain (ibc) plugin", + Usage: "an IBC transaction, for InterBlockchain Communication", Flags: TxFlags, Subcommands: []cli.Command{ IbcRegisterTxCmd, diff --git a/cmd/commands/query.go b/cmd/commands/query.go index 97eff6727..231d32f9f 100644 --- a/cmd/commands/query.go +++ b/cmd/commands/query.go @@ -138,11 +138,7 @@ func cmdBlock(c *cli.Context) error { return errors.New(cmn.Fmt("Height must be an int, got %v: %v", heightString, err)) } - /*block, err := getBlock(c, height) - if err != nil { - return err - }*/ - nextBlock, err := getBlock(c, height+1) + header, commit, err := getHeaderAndCommit(c, height) if err != nil { return err } @@ -152,12 +148,12 @@ func cmdBlock(c *cli.Context) error { JSON BlockJSON `json:"json"` }{ BlockHex{ - Header: wire.BinaryBytes(nextBlock.Header), - Commit: wire.BinaryBytes(nextBlock.LastCommit), + Header: wire.BinaryBytes(header), + Commit: wire.BinaryBytes(commit), }, BlockJSON{ - Header: nextBlock.Header, - Commit: nextBlock.LastCommit, + Header: header, + Commit: commit, }, }))) diff --git a/cmd/commands/start.go b/cmd/commands/start.go index 16e47fa60..72a13107d 100644 --- a/cmd/commands/start.go +++ b/cmd/commands/start.go @@ -72,7 +72,10 @@ func cmdStart(c *cli.Context) error { // Create Basecoin app basecoinApp := app.NewBasecoin(eyesCli) - // register all plugins + // register IBC plugn + basecoinApp.RegisterPlugin(NewIBCPlugin()) + + // register all other plugins for _, p := range plugins { basecoinApp.RegisterPlugin(p.newPlugin()) } @@ -91,7 +94,9 @@ func cmdStart(c *cli.Context) error { if c.Bool("in-proc") { startTendermint(c, basecoinApp) } else { - startBasecoinABCI(c, basecoinApp) + if err := startBasecoinABCI(c, basecoinApp); err != nil { + return err + } } return nil diff --git a/cmd/commands/tx.go b/cmd/commands/tx.go index f54b6d0c2..d1c8980c6 100644 --- a/cmd/commands/tx.go +++ b/cmd/commands/tx.go @@ -36,12 +36,13 @@ var ( Subcommands: []cli.Command{ SendTxCmd, AppTxCmd, + IbcTxCmd, }, } SendTxCmd = cli.Command{ Name: "send", - Usage: "Create, sign, and broadcast a SendTx transaction", + Usage: "a SendTx transaction, for sending tokens around", ArgsUsage: "", Action: func(c *cli.Context) error { return cmdSendTx(c) @@ -51,7 +52,7 @@ var ( AppTxCmd = cli.Command{ Name: "app", - Usage: "Create, sign, and broadcast a raw AppTx transaction", + Usage: "an AppTx transaction, for sending raw data to plugins", ArgsUsage: "", Action: func(c *cli.Context) error { return cmdAppTx(c) diff --git a/cmd/commands/utils.go b/cmd/commands/utils.go index 0cbd5c8d7..bfd3d8564 100644 --- a/cmd/commands/utils.go +++ b/cmd/commands/utils.go @@ -122,15 +122,19 @@ func getAcc(tmAddr string, address []byte) (*types.Account, error) { return acc, nil } -func getBlock(c *cli.Context, height int) (*tmtypes.Block, error) { +func getHeaderAndCommit(c *cli.Context, height int) (*tmtypes.Header, *tmtypes.Commit, error) { tmResult := new(ctypes.TMResult) tmAddr := c.String("node") clientURI := client.NewClientURI(tmAddr) - _, err := clientURI.Call("block", map[string]interface{}{"height": height}, tmResult) + method := "commit" + _, err := clientURI.Call(method, map[string]interface{}{"height": height}, tmResult) if err != nil { - return nil, errors.New(cmn.Fmt("Error on broadcast tx: %v", err)) + return nil, nil, errors.New(cmn.Fmt("Error on %s: %v", method, err)) } - res := (*tmResult).(*ctypes.ResultBlock) - return res.Block, nil + resCommit := (*tmResult).(*ctypes.ResultCommit) + header := resCommit.Header + commit := resCommit.Commit + + return header, commit, nil } diff --git a/demo/start.sh b/demo/start.sh index ad5da68d1..f2b85a581 100644 --- a/demo/start.sh +++ b/demo/start.sh @@ -9,6 +9,32 @@ function removeQuotes() { echo "$temp" } +function waitForNode() { + addr=$1 + set +e + curl -s $addr/status > /dev/null + ERR=$? + while [ "$ERR" != 0 ]; do + sleep 1 + curl -s $addr/status > /dev/null + ERR=$? + done + set -e + echo "... node $addr is up" +} + +function waitForBlock() { + addr=$1 + b1=`curl -s $addr/status | jq .result[1].latest_block_height` + b2=$b1 + while [ "$b2" == "$b1" ]; do + echo "Waiting for node $addr to commit a block ..." + sleep 1 + b2=`curl -s $addr/status | jq .result[1].latest_block_height` + done +} + + # grab the chain ids CHAIN_ID1=$(cat ./data/chain1/basecoin/genesis.json | jq .[1]) CHAIN_ID1=$(removeQuotes $CHAIN_ID1) @@ -35,7 +61,9 @@ basecoin start --address tcp://localhost:36658 --dir ./data/chain2/basecoin &> c echo "" echo "... waiting for chains to start" echo "" -sleep 10 + +waitForNode localhost:46657 +waitForNode localhost:36657 echo "... registering chain1 on chain2" echo "" @@ -66,10 +94,14 @@ echo "PACKET: $PACKET" echo "PROOF: $PROOF" +# the query returns the height of the next block, which contains the app hash +# but which may not be committed yet, so we have to wait for it to query the commit echo "" -echo "... waiting for some blocks to be mined" +echo "... waiting for a block to be committed" echo "" -sleep 5 + +waitForBlock localhost:46657 +waitForBlock localhost:36657 echo "" echo "... querying for block data" @@ -95,12 +127,12 @@ echo "" echo "... posting packet from chain1 on chain2" echo "" # post the packet from chain1 to chain2 -basecoin tx ibc --amount 10 $CHAIN_FLAGS2 packet post --from $CHAIN_ID1 --height $((HEIGHT + 1)) --packet 0x$PACKET --proof 0x$PROOF +basecoin tx ibc --amount 10 $CHAIN_FLAGS2 packet post --from $CHAIN_ID1 --height $HEIGHT --packet 0x$PACKET --proof 0x$PROOF echo "" echo "... checking if the packet is present on chain2" echo "" -# query for the packet on chain2 ! +# query for the packet on chain2 basecoin query --node tcp://localhost:36657 ibc,ingress,test_chain_2,test_chain_1,1 echo "" diff --git a/glide.lock b/glide.lock index 90bae6db4..83d5df539 100644 --- a/glide.lock +++ b/glide.lock @@ -1,12 +1,10 @@ hash: 3869944d14a8df914ffcad02c2ef3548173daba51c5ea697767f8af77c07b348 -updated: 2017-02-17T13:24:05.234809983+01:00 +updated: 2017-02-17T09:41:20.209551862-05:00 imports: - name: github.com/btcsuite/btcd - version: afec1bd1245a4a19e6dfe1306974b733e7cbb9b8 + version: d06c0bb181529331be8f8d9350288c420d9e60e4 subpackages: - btcec -- name: github.com/btcsuite/fastsha256 - version: 637e656429416087660c84436a2a035d69d54e2e - name: github.com/BurntSushi/toml version: 99064174e013895bbd9b025c31100bd1d9b590ca - name: github.com/ebuchman/fail-test @@ -18,7 +16,7 @@ imports: subpackages: - proto - name: github.com/golang/snappy - version: 7db9049039a047d955fe8c19b83c8ff5abd765c7 + version: 553a641470496b2327abcac10b36396bd98e45c9 - name: github.com/gorilla/websocket version: 3ab3a8b8831546bd18fd182c20687ca853b2bb13 - name: github.com/jmhodges/levigo @@ -26,7 +24,7 @@ imports: - name: github.com/mattn/go-colorable version: 5411d3eea5978e6cdc258b30de592b60df6aba96 - name: github.com/mattn/go-isatty - version: 281032e84ae07510239465db46bf442aa44b953a + version: dda3de49cbfcec471bd7a70e6cc01fcc3ff90109 - name: github.com/pkg/errors version: 248dadf4e9068a0b3e79f02ed0a610d935de5302 - name: github.com/syndtr/goleveldb @@ -115,7 +113,7 @@ imports: - types - version - name: github.com/urfave/cli - version: 347a9884a87374d000eec7e6445a34487c1f4a2b + version: 2526b57c56f30b50466c96c4133b1a4ad0f0191f - name: golang.org/x/crypto version: 453249f01cfeb54c3d549ddb75ff152ca243f9d8 subpackages: @@ -128,7 +126,7 @@ imports: - ripemd160 - salsa20/salsa - name: golang.org/x/net - version: 61557ac0112b576429a0df080e1c2cef5dfbb642 + version: b4690f45fa1cafc47b1c280c2e75116efe40cc13 subpackages: - context - http2 @@ -138,11 +136,11 @@ imports: - lex/httplex - trace - name: golang.org/x/sys - version: e24f485414aeafb646f6fca458b0bf869c0880a1 + version: 075e574b89e4c2d22f2286a7e2b919519c6f3547 subpackages: - unix - name: google.golang.org/grpc - version: cbcceb2942a489498cf22b2f918536e819d33f0a + version: d0c32ee6a441117d49856d6120ca9552af413ee0 subpackages: - codes - credentials @@ -156,14 +154,15 @@ imports: - transport testImports: - name: github.com/davecgh/go-spew - version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9 + version: 346938d642f2ec3594ed81d874461961cd0faa76 subpackages: - spew - name: github.com/pmezard/go-difflib - version: d8ed2627bdf02c080bf22230dbb337003b7aba2d + version: 792786c7400a136282c1664665ae0a8db921c6c2 subpackages: - difflib - name: github.com/stretchr/testify version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0 subpackages: - assert + - require diff --git a/plugins/ibc/ibc.go b/plugins/ibc/ibc.go index 91b8c5498..25f5a6da8 100644 --- a/plugins/ibc/ibc.go +++ b/plugins/ibc/ibc.go @@ -1,6 +1,7 @@ package ibc import ( + "bytes" "errors" "net/url" "strings" @@ -329,6 +330,7 @@ func (sm *IBCStateMachine) runPacketPostTx(tx IBCPacketPostTx) { save(sm.store, packetKeyIngress, packet) // Load Header and make sure it exists + // If it exists, we already checked a valid commit for it in UpdateChainTx var header tm.Header exists, err := load(sm.store, headerKey, &header) if err != nil { @@ -341,16 +343,6 @@ func (sm *IBCStateMachine) runPacketPostTx(tx IBCPacketPostTx) { return } - /* - // Read Proof - var proof *merkle.IAVLProof - err = wire.ReadBinaryBytes(tx.Proof, &proof) - if err != nil { - sm.res.Code = IBCEncodingError - sm.res.Log = cmn.Fmt("Reading Proof: %v", err.Error()) - return - } - */ proof := tx.Proof if proof == nil { sm.res.Code = IBCCodeInvalidProof @@ -368,7 +360,6 @@ func (sm *IBCStateMachine) runPacketPostTx(tx IBCPacketPostTx) { } return - } func (ibc *IBCPlugin) InitChain(store types.KVStore, vals []*abci.Validator) { @@ -432,6 +423,7 @@ func verifyCommit(chainState BlockchainState, header *tm.Header, commit *tm.Comm if chainState.ChainID != header.ChainID { return errors.New(cmn.Fmt("Expected header.ChainID %v, got %v", chainState.ChainID, header.ChainID)) } + // Ensure things aren't empty if len(chainState.Validators) == 0 { return errors.New(cmn.Fmt("Blockchain has no validators")) // NOTE: Why would this happen? } @@ -439,18 +431,23 @@ func verifyCommit(chainState BlockchainState, header *tm.Header, commit *tm.Comm return errors.New(cmn.Fmt("Commit has no signatures")) } chainID := chainState.ChainID - vote0 := commit.Precommits[0] vals := chainState.Validators valSet := tm.NewValidatorSet(vals) + blockID := commit.Precommits[0].BlockID // XXX: incorrect // NOTE: Currently this only works with the exact same validator set. // Not this, but perhaps "ValidatorSet.VerifyCommitAny" should expose // the functionality to verify commits even after validator changes. - err := valSet.VerifyCommit(chainID, vote0.BlockID, vote0.Height, commit) + err := valSet.VerifyCommit(chainID, blockID, header.Height, commit) if err != nil { return err } + // Ensure the committed blockID matches the header + if !bytes.Equal(header.Hash(), blockID.Hash) { + return errors.New(cmn.Fmt("blockID.Hash (%X) does not match header.Hash (%X)", blockID.Hash, header.Hash())) + } + // All ok! return nil } diff --git a/state/execution.go b/state/execution.go index 6e722bdc9..0b464c45a 100644 --- a/state/execution.go +++ b/state/execution.go @@ -95,7 +95,7 @@ func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc e } if !tx.Input.Coins.IsGTE(types.Coins{tx.Fee}) { log.Info(Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address)) - return abci.ErrBaseInsufficientFunds + return abci.ErrBaseInsufficientFunds.AppendLog(Fmt("input coins is %d, but fee is %d", tx.Input.Coins, types.Coins{tx.Fee})) } // Validate call address @@ -238,7 +238,7 @@ func validateInputAdvanced(acc *types.Account, signBytes []byte, in types.TxInpu } // Check amount if !balance.IsGTE(in.Coins) { - return abci.ErrBaseInsufficientFunds + return abci.ErrBaseInsufficientFunds.AppendLog(Fmt("balance is %d, tried to send %d", balance, in.Coins)) } // Check signatures if !acc.PubKey.VerifyBytes(signBytes, in.Signature) {