diff --git a/cmd/basecoin/commands/init.go b/cmd/basecoin/commands/init.go index ec0d62435..bd63ca2ad 100644 --- a/cmd/basecoin/commands/init.go +++ b/cmd/basecoin/commands/init.go @@ -120,7 +120,10 @@ func GetGenesisJSON(chainID, addr string) string { "amount": 9007199254740992 } ] - }] + }], + "plugin_options": [ + "coin/issuer", {"app": "sigs", "address": "%s"} + ] } -}`, chainID, addr) +}`, chainID, addr, addr) } diff --git a/modules/coin/handler.go b/modules/coin/handler.go index ffb75aa00..823bfb78a 100644 --- a/modules/coin/handler.go +++ b/modules/coin/handler.go @@ -1,8 +1,6 @@ package coin import ( - "fmt" - "github.com/tendermint/go-wire/data" "github.com/tendermint/tmlibs/log" @@ -83,7 +81,6 @@ func (h Handler) DeliverTx(ctx basecoin.Context, store state.SimpleDB, out.Address.ChainID = "" } - fmt.Printf("Giving %#v to %#v\n\n", out.Coins, out.Address) _, err = ChangeCoins(store, out.Address, out.Coins) if err != nil { return res, err @@ -117,25 +114,11 @@ func (h Handler) SetOption(l log.Logger, store state.SimpleDB, if module != NameCoin { return "", errors.ErrUnknownModule(module) } - if key == "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() - } - // this sets the permission for a public key signature, use that app - actor := auth.SigPerm(addr) - err = storeAccount(store, actor.Bytes(), acc.ToAccount()) - if err != nil { - return "", err - } - return "Success", nil - + switch key { + case "account": + return setAccount(store, value) + case "issuer": + return setIssuer(store, value) } return "", errors.ErrUnknownKey(key) } @@ -159,3 +142,38 @@ func checkTx(ctx basecoin.Context, tx basecoin.Tx) (send SendTx, err error) { } return send, nil } + +func setAccount(store state.KVStore, value string) (log string, err error) { + 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() + } + // this sets the permission for a public key signature, use that app + actor := auth.SigPerm(addr) + err = storeAccount(store, actor.Bytes(), acc.ToAccount()) + if err != nil { + return "", err + } + return "Success", nil +} + +// setIssuer sets a permission for some super-powerful account to +// mint money +func setIssuer(store state.KVStore, value string) (log string, err error) { + var issuer basecoin.Actor + err = data.FromJSON([]byte(value), &issuer) + if err != nil { + return "", err + } + err = storeIssuer(store, issuer) + if err != nil { + return "", err + } + return "Success", nil +} diff --git a/modules/coin/handler_test.go b/modules/coin/handler_test.go index 1edd12d87..dc0086fd3 100644 --- a/modules/coin/handler_test.go +++ b/modules/coin/handler_test.go @@ -216,3 +216,34 @@ func TestSetOption(t *testing.T) { } } } + +func TestSetIssuer(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + cases := []struct { + issuer basecoin.Actor + }{ + {basecoin.Actor{App: "sig", Address: []byte("gwkfgk")}}, + // and set back to empty (nil is valid, but assert.Equals doesn't match) + {basecoin.Actor{Address: []byte{}}}, + {basecoin.Actor{ChainID: "other", App: "role", Address: []byte("vote")}}, + } + + h := NewHandler() + l := log.NewNopLogger() + for i, tc := range cases { + store := state.NewMemKVStore() + key := "issuer" + + value, err := json.Marshal(tc.issuer) + require.Nil(err, "%d,%d: %+v", i, err) + _, err = h.SetOption(l, store, NameCoin, key, string(value), nil) + require.Nil(err, "%+v", err) + + // check state is proper + info, err := loadHandlerInfo(store) + assert.Nil(err, "%d: %+v", i, err) + assert.Equal(tc.issuer, info.Issuer) + } +} diff --git a/modules/coin/store.go b/modules/coin/store.go index 9aa951340..58e031961 100644 --- a/modules/coin/store.go +++ b/modules/coin/store.go @@ -84,7 +84,11 @@ func updateCoins(store state.SimpleDB, addr basecoin.Actor, coins Coins) (acct A // Account - coin account structure type Account struct { + // Coins is how much is on the account Coins Coins `json:"coins"` + // Credit is how much has been "fronted" to the account + // (this is usually 0 except for trusted chains) + Credit Coins `json:"credit"` } func loadAccount(store state.SimpleDB, key []byte) (acct Account, err error) { @@ -107,3 +111,35 @@ func storeAccount(store state.SimpleDB, key []byte, acct Account) error { store.Set(key, bin) return nil // real stores can return error... } + +// HandlerInfo - this is global info on the coin handler +type HandlerInfo struct { + Issuer basecoin.Actor `json:"issuer"` +} + +// TODO: where to store these special pieces?? +var handlerKey = []byte{12, 34} + +func loadHandlerInfo(store state.KVStore) (info HandlerInfo, err error) { + data := store.Get(handlerKey) + if len(data) == 0 { + return info, nil + } + err = wire.ReadBinaryBytes(data, &info) + if err != nil { + msg := "Error reading handler info" + return info, errors.ErrInternal(msg) + } + return info, nil +} + +func storeIssuer(store state.KVStore, issuer basecoin.Actor) error { + info, err := loadHandlerInfo(store) + if err != nil { + return err + } + info.Issuer = issuer + d := wire.BinaryBytes(info) + store.Set(handlerKey, d) + return nil // real stores can return error... +} diff --git a/modules/coin/tx.go b/modules/coin/tx.go index b3598970e..9dfcffc11 100644 --- a/modules/coin/tx.go +++ b/modules/coin/tx.go @@ -157,3 +157,30 @@ func (tx SendTx) String() string { func (tx SendTx) Wrap() basecoin.Tx { return basecoin.Tx{tx} } + +//----------------------------------------------------------------------------- + +// CreditTx - this allows a special issuer to give an account credit +// Satisfies: TxInner +type CreditTx struct { + Debitor basecoin.Actor `json:"debitor"` + // Credit is the amount to change the credit... + // This may be negative to remove some over-issued credit, + // but can never bring the credit or the balance to negative + Credit Coins `json:"credit"` +} + +// NewCreditTx - modify the credit granted to a given account +func NewCreditTx(debitor basecoin.Actor, credit Coins) basecoin.Tx { + return CreditTx{Debitor: debitor, Credit: credit}.Wrap() +} + +// Wrap - used to satisfy TxInner +func (tx CreditTx) Wrap() basecoin.Tx { + return basecoin.Tx{tx} +} + +// ValidateBasic - used to satisfy TxInner +func (tx CreditTx) ValidateBasic() error { + return nil +}