diff --git a/app/app.go b/app/app.go index 940520285a..b29186a24a 100644 --- a/app/app.go +++ b/app/app.go @@ -18,6 +18,7 @@ import ( const ( PluginNameBase = "base" + ChainKey = "base/chain_id" ) type Basecoin struct { @@ -67,41 +68,16 @@ func (app *Basecoin) Info() abci.ResponseInfo { // ABCI::SetOption func (app *Basecoin) SetOption(key string, value string) string { - // TODO - return "todo" - // pluginName, key := splitKey(key) - // if pluginName != PluginNameBase { - // // Set option on plugin - // plugin := app.plugins.GetByName(pluginName) - // if plugin == nil { - // return "Invalid plugin name: " + pluginName - // } - // app.logger.Info("SetOption on plugin", "plugin", pluginName, "key", key, "value", value) - // return plugin.SetOption(app.state, key, value) - // } else { - // // Set option on basecoin - // switch key { - // case "chain_id": - // app.state.SetChainID(value) - // return "Success" - // case "account": - // var acc GenesisAccount - // err := json.Unmarshal([]byte(value), &acc) - // if err != nil { - // return "Error decoding acc message: " + err.Error() - // } - // acc.Balance.Sort() - // addr, err := acc.GetAddr() - // if err != nil { - // return "Invalid address: " + err.Error() - // } - // app.state.SetAccount(addr, acc.ToAccount()) - // app.logger.Info("SetAccount", "addr", hex.EncodeToString(addr), "acc", acc) + if key == ChainKey { + app.state.SetChainID(value) + return "Success" + } - // return "Success" - // } - // return "Unrecognized option key " + key - // } + log, err := app.handler.SetOption(app.logger, app.state, key, value) + if err == nil { + return log + } + return "Error: " + err.Error() } // ABCI::DeliverTx diff --git a/app/genesis.go b/app/genesis.go index 3901fa6898..05913c15b0 100644 --- a/app/genesis.go +++ b/app/genesis.go @@ -1,14 +1,10 @@ package app import ( - "bytes" "encoding/json" "github.com/pkg/errors" - "github.com/tendermint/basecoin/types" - crypto "github.com/tendermint/go-crypto" - "github.com/tendermint/go-wire/data" cmn "github.com/tendermint/tmlibs/common" ) @@ -22,20 +18,13 @@ func (app *Basecoin) LoadGenesis(path string) error { app.SetOption("base/chain_id", genDoc.ChainID) // set accounts - for _, acc := range genDoc.AppOptions.Accounts { - accBytes, err := json.Marshal(acc) - if err != nil { - return err - } - r := app.SetOption("base/account", string(accBytes)) - // TODO: SetOption returns an error - app.logger.Info("Done setting Account via SetOption", "result", r) + for _, acct := range genDoc.AppOptions.Accounts { + _ = app.SetOption("base/account", string(acct)) } // set plugin options for _, kv := range genDoc.AppOptions.pluginOptions { - r := app.SetOption(kv.Key, kv.Value) - app.logger.Info("Done setting Plugin key-value pair via SetOption", "result", r, "k", kv.Key, "v", kv.Value) + _ = app.SetOption(kv.Key, kv.Value) } return nil @@ -53,7 +42,7 @@ type FullGenesisDoc struct { } type GenesisDoc struct { - Accounts []GenesisAccount `json:"accounts"` + Accounts []json.RawMessage `json:"accounts"` PluginOptions []json.RawMessage `json:"plugin_options"` pluginOptions []keyValue // unmarshaled rawmessages @@ -106,40 +95,3 @@ func parseGenesisList(kvz_ []json.RawMessage) (kvz []keyValue, err error) { } return kvz, nil } - -/**** code to parse accounts from genesis docs ***/ - -type GenesisAccount struct { - Address data.Bytes `json:"address"` - // this from types.Account (don't know how to embed this properly) - PubKey crypto.PubKey `json:"pub_key"` // May be nil, if not known. - Sequence int `json:"sequence"` - Balance types.Coins `json:"coins"` -} - -func (g GenesisAccount) ToAccount() *types.Account { - return &types.Account{ - PubKey: g.PubKey, - Sequence: g.Sequence, - Balance: g.Balance, - } -} - -func (g GenesisAccount) GetAddr() ([]byte, error) { - noAddr, noPk := len(g.Address) == 0, g.PubKey.Empty() - - if noAddr { - if noPk { - return nil, errors.New("No address given") - } - return g.PubKey.Address(), nil - } - if noPk { // but is addr... - return g.Address, nil - } - // now, we have both, make sure they check out - if bytes.Equal(g.Address, g.PubKey.Address()) { - return g.Address, nil - } - return nil, errors.New("Address and pubkey don't match") -} diff --git a/modules/coin/genesis.go b/modules/coin/genesis.go new file mode 100644 index 0000000000..7ce32cd8d2 --- /dev/null +++ b/modules/coin/genesis.go @@ -0,0 +1,48 @@ +package coin + +import ( + "bytes" + + "github.com/pkg/errors" + + crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/go-wire/data" + + "github.com/tendermint/basecoin/types" +) + +/**** code to parse accounts from genesis docs ***/ + +type GenesisAccount struct { + Address data.Bytes `json:"address"` + // this from types.Account (don't know how to embed this properly) + PubKey crypto.PubKey `json:"pub_key"` // May be nil, if not known. + Sequence int `json:"sequence"` + Balance types.Coins `json:"coins"` +} + +func (g GenesisAccount) ToAccount() Account { + return Account{ + Sequence: g.Sequence, + Coins: g.Balance, + } +} + +func (g GenesisAccount) GetAddr() ([]byte, error) { + noAddr, noPk := len(g.Address) == 0, g.PubKey.Empty() + + if noAddr { + if noPk { + return nil, errors.New("No address given") + } + return g.PubKey.Address(), nil + } + if noPk { // but is addr... + return g.Address, nil + } + // now, we have both, make sure they check out + if bytes.Equal(g.Address, g.PubKey.Address()) { + return g.Address, nil + } + return nil, errors.New("Address and pubkey don't match") +} diff --git a/modules/coin/handler.go b/modules/coin/handler.go index cabc6a062c..a8919d835f 100644 --- a/modules/coin/handler.go +++ b/modules/coin/handler.go @@ -1,6 +1,9 @@ package coin import ( + "fmt" + + "github.com/tendermint/go-wire/data" "github.com/tendermint/tmlibs/log" "github.com/tendermint/basecoin" @@ -77,8 +80,28 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoi } func (h Handler) SetOption(l log.Logger, store types.KVStore, key, value string) (log string, err error) { - // TODO - return "ok", nil + if key == "base/account" { + var acc GenesisAccount + err = data.FromJSON([]byte(value), &acc) + if err != nil { + return "", err + } + acc.Balance.Sort() + addr, err := acc.GetAddr() + if err != nil { + return "", ErrInvalidAddress() + } + actor := basecoin.Actor{App: NameCoin, Address: addr} + err = storeAccount(store, h.makeKey(actor), acc.ToAccount()) + if err != nil { + return "", err + } + return "Success", nil + + } else { + msg := fmt.Sprintf("Unknown key: %s", key) + return "", errors.ErrInternal(msg) + } } func checkTx(ctx basecoin.Context, tx basecoin.Tx) (send SendTx, err error) { diff --git a/modules/coin/handler_test.go b/modules/coin/handler_test.go index 8027b489c6..7f1e0b12b5 100644 --- a/modules/coin/handler_test.go +++ b/modules/coin/handler_test.go @@ -1,10 +1,15 @@ package coin import ( + "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + crypto "github.com/tendermint/go-crypto" + "github.com/tendermint/tmlibs/log" + "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/stack" "github.com/tendermint/basecoin/types" @@ -158,5 +163,57 @@ func TestDeliverTx(t *testing.T) { } } +} + +func TestSetOption(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + // some sample settings + pk := crypto.GenPrivKeySecp256k1().Wrap() + addr := pk.PubKey().Address() + actor := basecoin.Actor{App: "coin", Address: addr} + // actor2 := basecoin.Actor{App: "foo", Address: addr} + + someCoins := types.Coins{{"atom", 123}} + otherCoins := types.Coins{{"eth", 11}} + mixedCoins := someCoins.Plus(otherCoins) + + type money struct { + addr basecoin.Actor + coins types.Coins + } + + cases := []struct { + init []GenesisAccount + expected []money + }{ + { + []GenesisAccount{{Address: addr, Balance: mixedCoins}}, + []money{{actor, mixedCoins}}, + }, + } + + h := NewHandler() + l := log.NewNopLogger() + for i, tc := range cases { + store := types.NewMemKVStore() + key := "base/account" + + // set the options + for j, gen := range tc.init { + value, err := json.Marshal(gen) + require.Nil(err, "%d,%d: %+v", i, j, err) + _, err = h.SetOption(l, store, key, string(value)) + require.Nil(err) + } + + // check state is proper + for _, f := range tc.expected { + acct, err := loadAccount(store, h.makeKey(f.addr)) + assert.Nil(err, "%d: %+v", i, err) + assert.Equal(f.coins, acct.Coins) + } + } }