Refactor basecoin example (#332)

Refactor basecoin example
This commit is contained in:
Jae Kwon 2018-01-17 16:59:40 -08:00 committed by GitHub
parent fdc7316fda
commit 14a0dce920
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 297 additions and 125 deletions

View File

@ -261,6 +261,8 @@ func (app *App) runTx(isCheckTx bool, txBytes []byte) (result sdk.Result) {
}
}
// TODO: override default ante handler w/ custom ante handler.
// Run the ante handler.
ctx, result, abort := app.defaultAnteHandler(ctx, tx)
if isCheckTx || abort {

View File

@ -0,0 +1,97 @@
package app
import (
"fmt"
"os"
apm "github.com/cosmos/cosmos-sdk/app"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/tendermint/abci/server"
"github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common"
)
const appName = "BasecoinApp"
type BasecoinApp struct {
*apm.App
cdc *wire.Codec
multiStore sdk.CommitMultiStore
// The key to access the substores.
mainStoreKey *sdk.KVStoreKey
ibcStoreKey *sdk.KVStoreKey
// Additional stores:
accStore sdk.AccountStore
}
// TODO: This should take in more configuration options.
func NewBasecoinApp() *BasecoinApp {
// Create and configure app.
var app = &BasecoinApp{}
app.initKeys()
app.initMultiStore()
app.initAppStore()
app.initSDKApp()
app.initCodec()
app.initTxDecoder()
app.initAnteHandler()
app.initRoutes()
// TODO: Load genesis
// TODO: InitChain with validators
// TODO: Set the genesis accounts
app.loadStores()
return app
}
func (app *BasecoinApp) RunForever() {
// Start the ABCI server
srv, err := server.NewServer("0.0.0.0:46658", "socket", app)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
srv.Start()
// Wait forever
cmn.TrapSignal(func() {
// Cleanup
srv.Stop()
})
}
func (app *BasecoinApp) initKeys() {
app.mainStoreKey = sdk.NewKVStoreKey("main")
app.ibcStoreKey = sdk.NewKVStoreKey("ibc")
}
// depends on initMultiStore()
func (app *BasecoinApp) initSDKApp() {
app.App = apm.NewApp(appName, app.multiStore)
}
func (app *BasecoinApp) initCodec() {
app.cdc = wire.NewCodec()
}
// depends on initSDKApp()
func (app *BasecoinApp) initTxDecoder() {
app.App.SetTxDecoder(app.decodeTx)
}
// initAnteHandler defined in app/routes.go
// initRoutes defined in app/routes.go
// Load the stores.
func (app *BasecoinApp) loadStores() {
if err := app.LoadLatestVersion(app.mainStoreKey); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

View File

@ -0,0 +1,28 @@
package app
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
)
// Set via `app.App.SetTxDecoder(app.decodeTx)`
func (app *BasecoinApp) decodeTx(txBytes []byte) (sdk.Tx, error) {
var tx = sdk.StdTx{}
err := app.cdc.UnmarshalBinary(txBytes, &tx)
return tx, err
}
// Wire requires registration of interfaces & concrete types.
func (app *BasecoinApp) registerMsgs() {
cdc := app.cdc
// Register the Msg interface.
cdc.RegisterInterface((*sdk.Msg)(nil), nil)
cdc.RegisterConcrete(bank.SendMsg{}, "cosmos-sdk/SendMsg", nil) // XXX refactor out
cdc.RegisterConcrete(bank.IssueMsg{}, "cosmos-sdk/IssueMsg", nil) // XXX refactor out to bank/msgs.go
// more msgs here...
// All interfaces to be encoded/decoded in a Msg must be
// registered here, along with all the concrete types that
// implement them.
}

View File

@ -0,0 +1,22 @@
package app
import (
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
)
// Handle charging tx fees and checking signatures.
func (app *BasecoinApp) initAnteHandler() {
var authAnteHandler = auth.NewAnteHandler(app.accStore)
app.App.SetDefaultAnteHandler(authAnteHandler)
}
// Constructs router to route handling of msgs.
func (app *BasecoinApp) initRoutes() {
var router = app.App.Router()
// var multiStore = app.multiStore
var accStore = app.accStore
router.AddRoute("bank", bank.NewHandler(accStore))
// more routes here... (order matters)
}

View File

@ -0,0 +1,45 @@
package app
import (
"fmt"
"os"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/x/auth"
dbm "github.com/tendermint/tmlibs/db"
)
// depends on initKeys()
func (app *BasecoinApp) initMultiStore() {
// Create the underlying leveldb datastore which will
// persist the Merkle tree inner & leaf nodes.
db, err := dbm.NewGoLevelDB("basecoin", "basecoin-data")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Create CommitStoreLoader.
cacheSize := 10000
numHistory := int64(100)
mainLoader := store.NewIAVLStoreLoader(db, cacheSize, numHistory)
ibcLoader := store.NewIAVLStoreLoader(db, cacheSize, numHistory)
// Create MultiStore
multiStore := store.NewCommitMultiStore(db)
multiStore.SetSubstoreLoader(app.mainStoreKey, mainLoader)
multiStore.SetSubstoreLoader(app.ibcStoreKey, ibcLoader)
// Finally,
app.multiStore = multiStore
}
// depends on initKeys()
func (app *BasecoinApp) initAppStore() {
app.accStore = auth.NewAccountStore(
app.mainStoreKey,
types.AppAccountCodec{},
)
}

View File

@ -1,99 +0,0 @@
package main
import (
"fmt"
"os"
"github.com/cosmos/cosmos-sdk/app"
"github.com/cosmos/cosmos-sdk/store"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/tendermint/abci/server"
"github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common"
dbm "github.com/tendermint/tmlibs/db"
bcm "github.com/cosmos/cosmos-sdk/examples/basecoin/types"
)
func main() {
// Create the underlying leveldb datastore which will
// persist the Merkle tree inner & leaf nodes.
db, err := dbm.NewGoLevelDB("basecoin", "basecoin-data")
if err != nil {
fmt.Println(err)
os.Exit(1)
}
// Create CommitStoreLoader.
cacheSize := 10000
numHistory := int64(100)
mainLoader := store.NewIAVLStoreLoader(db, cacheSize, numHistory)
ibcLoader := store.NewIAVLStoreLoader(db, cacheSize, numHistory)
// The key to access the main KVStore.
var mainStoreKey = sdk.NewKVStoreKey("main")
var ibcStoreKey = sdk.NewKVStoreKey("ibc")
// Create MultiStore
multiStore := store.NewCommitMultiStore(db)
multiStore.SetSubstoreLoader(mainStoreKey, mainLoader)
multiStore.SetSubstoreLoader(ibcStoreKey, ibcLoader)
// Create the Application.
app := app.NewApp("basecoin", multiStore)
// Set Tx decoder
app.SetTxDecoder(decodeTx)
var accStore = auth.NewAccountStore(mainStoreKey, bcm.AppAccountCodec{})
var authAnteHandler = auth.NewAnteHandler(accStore)
// Handle charging fees and checking signatures.
app.SetDefaultAnteHandler(authAnteHandler)
// Add routes to App.
app.Router().AddRoute("bank", bank.NewHandler(accStore))
// TODO: load genesis
// TODO: InitChain with validators
// accounts := auth.NewAccountStore(multiStore.GetKVStore("main"))
// TODO: set the genesis accounts
// Load the stores.
if err := app.LoadLatestVersion(mainStoreKey); err != nil {
fmt.Println(err)
os.Exit(1)
}
// Start the ABCI server
srv, err := server.NewServer("0.0.0.0:46658", "socket", app)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
srv.Start()
// Wait forever
cmn.TrapSignal(func() {
// Cleanup
srv.Stop()
})
return
}
//----------------------------------------
// Misc.
func registerMsgs() {
wire.RegisterInterface((*sdk.Msg)(nil), nil)
wire.RegisterConcrete(&bank.SendMsg{}, "com.cosmos.basecoin.send_msg", nil)
}
func decodeTx(txBytes []byte) (sdk.Tx, error) {
var tx = sdk.StdTx{}
err := wire.UnmarshalBinary(txBytes, &tx)
return tx, err
}

View File

@ -39,6 +39,9 @@ func NewCommitMultiStore(db dbm.DB) *rootMultiStore {
// Implements CommitMultiStore.
func (rs *rootMultiStore) SetSubstoreLoader(key SubstoreKey, loader CommitStoreLoader) {
if key == nil {
panic("SetSubstoreLoader() key cannot be nil")
}
if _, ok := rs.storeLoaders[key]; ok {
panic(fmt.Sprintf("rootMultiStore duplicate substore key", key))
}

View File

@ -54,9 +54,11 @@ type CommitMultiStore interface {
MultiStore
// Add a substore loader.
// Panics on a nil key.
SetSubstoreLoader(key SubstoreKey, loader CommitStoreLoader)
// Gets the substore, which is a CommitSubstore.
// Panics on a nil key.
GetSubstore(key SubstoreKey) CommitStore
// Load the latest persisted version.

View File

@ -55,7 +55,7 @@ func NewAnteHandler(store sdk.AccountStore) sdk.AnteHandler {
}
}
// Check and incremenet sequence number.
// Check and increment sequence number.
seq := signerAcc.GetSequence()
if seq != sig.Sequence {
return ctx, sdk.Result{

View File

@ -2,38 +2,56 @@ package bank
import (
sdk "github.com/cosmos/cosmos-sdk/types"
"reflect"
)
// Handle all "bank" type messages.
func NewHandler(accStore sdk.AccountStore) sdk.Handler {
return func(ctx sdk.Context, tx sdk.Tx) sdk.Result {
cs := CoinStore{accStore}
sendTx, ok := tx.(sdk.Msg).(SendMsg)
if !ok {
panic("tx is not SendTx") // ?
}
// NOTE: totalIn == totalOut should already have been checked
for _, in := range sendTx.Inputs {
_, err := cs.SubtractCoins(ctx, in.Address, in.Coins)
if err != nil {
return sdk.Result{
Code: 1, // TODO
}
msg := tx.(sdk.Msg)
switch msg := msg.(type) {
case SendMsg:
return handleSendMsg(ctx, cs, msg)
case IssueMsg:
return handleIssueMsg(ctx, cs, msg)
default:
return sdk.Result{
Code: 1, // TODO
Log: "Unrecognized bank Tx type: " + reflect.TypeOf(tx).Name(),
}
}
for _, out := range sendTx.Outputs {
_, err := cs.AddCoins(ctx, out.Address, out.Coins)
if err != nil {
return sdk.Result{
Code: 1, // TODO
}
}
}
return sdk.Result{} // TODO
}
}
// Handle SendMsg.
func handleSendMsg(ctx sdk.Context, cs CoinStore, msg SendMsg) sdk.Result {
// NOTE: totalIn == totalOut should already have been checked
for _, in := range msg.Inputs {
_, err := cs.SubtractCoins(ctx, in.Address, in.Coins)
if err != nil {
return sdk.Result{
Code: 1, // TODO
}
}
}
for _, out := range msg.Outputs {
_, err := cs.AddCoins(ctx, out.Address, out.Coins)
if err != nil {
return sdk.Result{
Code: 1, // TODO
}
}
}
return sdk.Result{} // TODO
}
// Handle IssueMsg.
func handleIssueMsg(ctx sdk.Context, cs CoinStore, msg IssueMsg) sdk.Result {
panic("not implemented yet")
}

View File

@ -81,6 +81,60 @@ func (msg SendMsg) GetSigners() []crypto.Address {
return addrs
}
//----------------------------------------
// IssueMsg
// IssueMsg - high level transaction of the coin module
type IssueMsg struct {
Banker crypto.Address `json:"banker"`
Outputs []Output `json:"outputs"`
}
// NewIssueMsg - construct arbitrary multi-in, multi-out send msg.
func NewIssueMsg(banker crypto.Address, out []Output) IssueMsg {
return IssueMsg{Banker: banker, Outputs: out}
}
// Implements Msg.
func (msg IssueMsg) Type() string { return "bank" } // TODO: "bank/send"
// Implements Msg.
func (msg IssueMsg) ValidateBasic() error {
// XXX
if len(msg.Outputs) == 0 {
return ErrNoOutputs()
}
for _, out := range msg.Outputs {
if err := out.ValidateBasic(); err != nil {
return err
}
}
return nil
}
func (msg IssueMsg) String() string {
return fmt.Sprintf("IssueMsg{%v#%v}", msg.Banker, msg.Outputs)
}
// Implements Msg.
func (msg IssueMsg) Get(key interface{}) (value interface{}) {
return nil
}
// Implements Msg.
func (msg IssueMsg) GetSignBytes() []byte {
b, err := json.Marshal(msg) // XXX: ensure some canonical form
if err != nil {
panic(err)
}
return b
}
// Implements Msg.
func (msg IssueMsg) GetSigners() []crypto.Address {
return []crypto.Address{msg.Banker}
}
//----------------------------------------
// Input