Merge pull request #552 from cosmos/rigel/basecoin-upgrade

Basecoin Upgrade + tiny bit client refactor
This commit is contained in:
Ethan Buchman 2018-03-11 00:12:02 +01:00 committed by GitHub
commit 032a0b4539
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 666 additions and 203 deletions

View File

@ -1,5 +1,19 @@
# Changelog
## unrealease
BREAKING CHANGES
FEATURES
* [examples/basecoin] new cool module to demonstrate use of state and custom transactions
IMPROVEMENTS
* [client] refactor to now include more standard code
BUG FIXES
## 0.11.0 (March 1, 2017)
BREAKING CHANGES

131
client/builder/builder.go Normal file
View File

@ -0,0 +1,131 @@
package builder
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/wire"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
cmn "github.com/tendermint/tmlibs/common"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Broadcast the transaction bytes to Tendermint
func BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
node, err := client.GetNode()
if err != nil {
return nil, err
}
res, err := node.BroadcastTxCommit(tx)
if err != nil {
return res, err
}
if res.CheckTx.Code != uint32(0) {
return res, errors.Errorf("CheckTx failed: (%d) %s",
res.CheckTx.Code,
res.CheckTx.Log)
}
if res.DeliverTx.Code != uint32(0) {
return res, errors.Errorf("DeliverTx failed: (%d) %s",
res.DeliverTx.Code,
res.DeliverTx.Log)
}
return res, err
}
// Query from Tendermint with the provided key and storename
func Query(key cmn.HexBytes, storeName string) (res []byte, err error) {
path := fmt.Sprintf("/%s/key", storeName)
node, err := client.GetNode()
if err != nil {
return res, err
}
opts := rpcclient.ABCIQueryOptions{
Height: viper.GetInt64(client.FlagHeight),
Trusted: viper.GetBool(client.FlagTrustNode),
}
result, err := node.ABCIQueryWithOptions(path, key, opts)
if err != nil {
return res, err
}
resp := result.Response
if resp.Code != uint32(0) {
return res, errors.Errorf("Query failed: (%d) %s", resp.Code, resp.Log)
}
return resp.Value, nil
}
// Get the from address from the name flag
func GetFromAddress() (from sdk.Address, err error) {
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
name := viper.GetString(client.FlagName)
if name == "" {
return nil, errors.Errorf("must provide a name using --name")
}
info, err := keybase.Get(name)
if err != nil {
return nil, errors.Errorf("No key for: %s", name)
}
return info.PubKey.Address(), nil
}
// sign and build the transaction from the msg
func SignAndBuild(msg sdk.Msg, cdc *wire.Codec) ([]byte, error) {
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
name := viper.GetString(client.FlagName)
// sign and build
bz := msg.GetSignBytes()
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password to sign with '%s':", name)
passphrase, err := client.GetPassword(prompt, buf)
if err != nil {
return nil, err
}
sig, pubkey, err := keybase.Sign(name, passphrase, bz)
if err != nil {
return nil, err
}
sigs := []sdk.StdSignature{{
PubKey: pubkey,
Signature: sig,
Sequence: viper.GetInt64(client.FlagSequence),
}}
// marshal bytes
tx := sdk.NewStdTx(msg, sigs)
return cdc.MarshalBinary(tx)
}
// sign and build the transaction from the msg
func SignBuildBroadcast(msg sdk.Msg, cdc *wire.Codec) (*ctypes.ResultBroadcastTxCommit, error) {
txBytes, err := SignAndBuild(msg, cdc)
if err != nil {
return nil, err
}
return BroadcastTx(txBytes)
}

View File

@ -9,6 +9,8 @@ const (
FlagHeight = "height"
FlagTrustNode = "trust-node"
FlagName = "name"
FlagSequence = "sequence"
FlagFee = "fee"
)
// LineBreak can be included in a command list to provide a blank line
@ -31,6 +33,8 @@ func GetCommands(cmds ...*cobra.Command) []*cobra.Command {
func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
for _, c := range cmds {
c.Flags().String(FlagName, "", "Name of private key with which to sign")
c.Flags().Int64(FlagSequence, 0, "Sequence number to sign the tx")
c.Flags().String(FlagFee, "", "Fee to pay along with transaction")
c.Flags().String(FlagChainID, "", "Chain ID of tendermint node")
c.Flags().String(FlagNode, "tcp://localhost:46657", "<host>:<port> to tendermint rpc interface for this chain")
}

View File

@ -1,14 +1,10 @@
package client
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/viper"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
cmn "github.com/tendermint/tmlibs/common"
)
// GetNode prepares a simple rpc.Client from the flags
@ -19,53 +15,3 @@ func GetNode() (rpcclient.Client, error) {
}
return rpcclient.NewHTTP(uri, "/websocket"), nil
}
// Broadcast the transaction bytes to Tendermint
func BroadcastTx(tx []byte) (*ctypes.ResultBroadcastTxCommit, error) {
node, err := GetNode()
if err != nil {
return nil, err
}
res, err := node.BroadcastTxCommit(tx)
if err != nil {
return res, err
}
if res.CheckTx.Code != uint32(0) {
return res, errors.Errorf("CheckTx failed: (%d) %s",
res.CheckTx.Code,
res.CheckTx.Log)
}
if res.DeliverTx.Code != uint32(0) {
return res, errors.Errorf("DeliverTx failed: (%d) %s",
res.DeliverTx.Code,
res.DeliverTx.Log)
}
return res, err
}
// Query from Tendermint with the provided key and storename
func Query(key cmn.HexBytes, storeName string) (res []byte, err error) {
path := fmt.Sprintf("/%s/key", storeName)
node, err := GetNode()
if err != nil {
return res, err
}
opts := rpcclient.ABCIQueryOptions{
Height: viper.GetInt64(FlagHeight),
Trusted: viper.GetBool(FlagTrustNode),
}
result, err := node.ABCIQueryWithOptions(path, key, opts)
if err != nil {
return res, err
}
resp := result.Response
if resp.Code != uint32(0) {
return res, errors.Errorf("Query failed: (%d) %s", resp.Code, resp.Log)
}
return resp.Value, nil
}

View File

@ -1,8 +1,70 @@
This is the "Basecoin" example application built on the Cosmos-SDK. This
# Basecoin
This is the "Basecoin" example application built on the Cosmos-Sdk. This
"Basecoin" is not affiliated with [Coinbase](http://www.getbasecoin.com/), nor
the [stable coin](http://www.getbasecoin.com/).
Assuming you've run `make get_tools && make get_vendor_deps` from the root of this repository,
run `make build` here to build the `basecoind` and `basecli` binaries.
Assuming you've run `make get_tools && make get_vendor_deps` from the root of
this repository, run `make build` here to build the `basecoind` and `basecli`
binaries.
If you want to create a new application, start by copying the Basecoin app.
# Building your own Blockchain
Basecoin is the equivalent of an ERC20 token contract for blockchains. In order
to deploy your own application all you need to do is clone `examples/basecoin`
and run it. Now you are already running your own blockchain. In the following
I will explain how to add functionality to your blockchain. This is akin to
defining your own vesting schedule within a contract or setting a specific
multisig. You are just extending the base layer with extra functionality here
and there.
## Structure of Basecoin
Basecoin is build with the cosmos-sdk. It is a sample application that works
with any engine that implements the ABCI protocol. Basecoin defines multiple
unique modules as well as uses modules directly from the sdk. If you want
to modify Basecoin, you either remove or add modules according to your wishes.
## Modules
A module is a fundamental unit in the cosmos-sdk. A module defines its own
transaction, handles its own state as well as its own state transition logic.
Globally, in the `app/app.go` file you just have to define a key for that
module to access some parts of the state, as well as initialise the module
object and finally add it to the transaction router. The router ensures that
every module only gets its own messages.
## Transactions
A user can send a transaction to the running blockchain application. This
transaction can be of any of the ones that are supported by any of the
registered modules.
### CheckTx
Once a user has submitted their transaction to the engine,
the engine will first run `checkTx` to confirm that it is a valid transaction.
The module has to define a handler that knows how to handle every transaction
type. The corresponding handler gets invoked with the checkTx flag set to true.
This means that the handler shouldn't do any expensive operations, but it can
and should write to the checkTx state.
### DeliverTx
The engine calls `deliverTx` when a new block has been agreed upon in
consensus. Again, the corresponding module will have its handler invoked
and the state and context is passed in. During deliverTx execution the
transaction needs to be processed fully and the results are written to the
application state.
## CLI
The cosmos-sdk contains a number of helper libraries in `clients/` to build cli
and RPC interfaces for your specific application.

View File

@ -16,6 +16,7 @@ import (
"github.com/cosmos/cosmos-sdk/x/bank"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/sketchy"
)
@ -53,8 +54,10 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
// add handlers
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
coolMapper := cool.NewMapper(app.capKeyMainStore)
app.Router().
AddRoute("bank", bank.NewHandler(coinKeeper)).
AddRoute("cool", cool.NewHandler(coinKeeper, coolMapper)).
AddRoute("sketchy", sketchy.NewHandler())
// initialize BaseApp
@ -73,39 +76,38 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
}
// custom tx codec
// TODO: use new go-wire
func MakeCodec() *wire.Codec {
// XXX: Using old wire for now :)
const (
msgTypeSend = 0x1
msgTypeIssue = 0x2
)
const msgTypeSend = 0x1
const msgTypeIssue = 0x2
const msgTypeWhatCool = 0x3
const msgTypeSetWhatCool = 0x4
var _ = oldwire.RegisterInterface(
struct{ sdk.Msg }{},
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
oldwire.ConcreteType{cool.WhatCoolMsg{}, msgTypeWhatCool},
oldwire.ConcreteType{cool.SetWhatCoolMsg{}, msgTypeSetWhatCool},
)
const (
accTypeApp = 0x1
)
const accTypeApp = 0x1
var _ = oldwire.RegisterInterface(
struct{ sdk.Account }{},
oldwire.ConcreteType{&types.AppAccount{}, accTypeApp},
)
cdc := wire.NewCodec()
// TODO: use new go-wire
// cdc.RegisterInterface((*sdk.Msg)(nil), nil)
// bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types.
// crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types.
return cdc
}
// custom logic for transaction decoding
func (app *BasecoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
var tx = sdk.StdTx{}
// StdTx.Msg is an interface. The concrete types
// are registered by MakeTxCodec in bank.RegisterWire.
err := app.cdc.UnmarshalBinary(txBytes, &tx)

View File

@ -10,6 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
@ -20,54 +21,88 @@ import (
"github.com/tendermint/tmlibs/log"
)
// helper variables and functions
var (
// Construct genesis key/accounts
priv1 = crypto.GenPrivKeyEd25519()
addr1 = priv1.PubKey().Address()
addr2 = crypto.GenPrivKeyEd25519().PubKey().Address()
sendMsg = bank.SendMsg{
Inputs: []bank.Input{
{
Address: addr1,
Coins: sdk.Coins{{"foocoin", 10}},
Sequence: 1,
},
},
Outputs: []bank.Output{
{
Address: addr2,
Coins: sdk.Coins{{"foocoin", 10}},
},
},
}
whatCoolMsg1 = cool.WhatCoolMsg{
Sender: addr1,
CoolerThanCool: "icecold",
}
whatCoolMsg2 = cool.WhatCoolMsg{
Sender: addr1,
CoolerThanCool: "icecold",
}
setWhatCoolMsg = cool.SetWhatCoolMsg{
Sender: addr1,
WhatCool: "goodbye",
}
)
func newBasecoinApp() *BasecoinApp {
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
db := dbm.NewMemDB()
return NewBasecoinApp(logger, db)
}
func TestSendMsg(t *testing.T) {
//_______________________________________________________________________
func TestMsgs(t *testing.T) {
bapp := newBasecoinApp()
// Construct a SendMsg
var msg = bank.SendMsg{
Inputs: []bank.Input{
{
Address: sdk.Address([]byte("input")),
Coins: sdk.Coins{{"atom", 10}},
Sequence: 1,
},
},
Outputs: []bank.Output{
{
Address: sdk.Address([]byte("output")),
Coins: sdk.Coins{{"atom", 10}},
},
},
msgs := []struct {
msg sdk.Msg
}{
{sendMsg},
{whatCoolMsg1},
{setWhatCoolMsg},
}
priv := crypto.GenPrivKeyEd25519()
sig := priv.Sign(msg.GetSignBytes())
tx := sdk.NewStdTx(msg, []sdk.StdSignature{{
PubKey: priv.PubKey(),
Signature: sig,
}})
for i, m := range msgs {
sig := priv1.Sign(m.msg.GetSignBytes())
tx := sdk.NewStdTx(m.msg, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
}})
// just marshal/unmarshal!
cdc := MakeCodec()
txBytes, err := cdc.MarshalBinary(tx)
require.NoError(t, err)
// just marshal/unmarshal!
cdc := MakeCodec()
txBytes, err := cdc.MarshalBinary(tx)
require.NoError(t, err, "i: %v", i)
// Run a Check
cres := bapp.CheckTx(txBytes)
assert.Equal(t, sdk.CodeUnrecognizedAddress,
sdk.CodeType(cres.Code), cres.Log)
// Run a Check
cres := bapp.CheckTx(txBytes)
assert.Equal(t, sdk.CodeUnrecognizedAddress,
sdk.CodeType(cres.Code), "i: %v, log: %v", i, cres.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
dres := bapp.DeliverTx(txBytes)
assert.Equal(t, sdk.CodeUnrecognizedAddress,
sdk.CodeType(dres.Code), dres.Log)
// Simulate a Block
bapp.BeginBlock(abci.RequestBeginBlock{})
dres := bapp.DeliverTx(txBytes)
assert.Equal(t, sdk.CodeUnrecognizedAddress,
sdk.CodeType(dres.Code), "i: %v, log: %v", i, dres.Log)
}
}
func TestGenesis(t *testing.T) {
@ -114,15 +149,6 @@ func TestSendMsgWithAccounts(t *testing.T) {
bapp := newBasecoinApp()
// Construct some genesis bytes to reflect basecoin/types/AppAccount
// First key goes in genesis, used for sending
priv1 := crypto.GenPrivKeyEd25519()
pk1 := priv1.PubKey()
addr1 := pk1.Address()
// Second key receies
pk2 := crypto.GenPrivKeyEd25519().PubKey()
addr2 := pk2.Address()
// Give 77 foocoin to the first key
coins, err := sdk.ParseCoins("77foocoin")
require.Nil(t, err)
@ -139,6 +165,7 @@ func TestSendMsgWithAccounts(t *testing.T) {
},
}
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
require.Nil(t, err)
// Initialize the chain
vals := []abci.Validator{}
@ -147,32 +174,13 @@ func TestSendMsgWithAccounts(t *testing.T) {
// A checkTx context (true)
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
assert.Equal(t, acc1, res1)
// Construct a SendMsg
var msg = bank.SendMsg{
Inputs: []bank.Input{
{
Address: sdk.Address(addr1),
Coins: sdk.Coins{{"foocoin", 10}},
Sequence: 1,
},
},
Outputs: []bank.Output{
{
Address: sdk.Address(addr2),
Coins: sdk.Coins{{"foocoin", 10}},
},
},
}
// Sign the tx
sig := priv1.Sign(msg.GetSignBytes())
tx := sdk.NewStdTx(msg, []sdk.StdSignature{{
tx := sdk.NewStdTx(sendMsg, []sdk.StdSignature{{
PubKey: priv1.PubKey(),
Signature: sig,
Signature: priv1.Sign(sendMsg.GetSignBytes()),
}})
// Run a Check
@ -184,13 +192,64 @@ func TestSendMsgWithAccounts(t *testing.T) {
res = bapp.Deliver(tx)
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
// A deliverTx context
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
// Check balances
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
res3 := bapp.accountMapper.GetAccount(ctxDeliver, addr2)
assert.Equal(t, fmt.Sprintf("%v", res2.GetCoins()), "67foocoin")
assert.Equal(t, fmt.Sprintf("%v", res3.GetCoins()), "10foocoin")
}
//func TestWhatCoolMsg(t *testing.T) {
//bapp := newBasecoinApp()
//// Construct genesis state
//// Construct some genesis bytes to reflect basecoin/types/AppAccount
//// Give 77 foocoin to the first key
//coins, err := sdk.ParseCoins("1icecold")
//require.Nil(t, err)
//baseAcc := auth.BaseAccount{
//Address: addr1,
//Coins: coins,
//}
//acc1 := &types.AppAccount{baseAcc, "foobart"}
//// Construct genesis state
//genesisState := types.GenesisState{
//Accounts: []*types.GenesisAccount{
//types.NewGenesisAccount(acc1),
//},
//}
//stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
//require.Nil(t, err)
//// Initialize the chain (nil)
//vals := []abci.Validator{}
//bapp.InitChain(abci.RequestInitChain{vals, stateBytes})
//bapp.Commit()
//// A checkTx context (true)
//ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
//res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
//assert.Equal(t, acc1, res1)
//// Sign the tx
//tx := sdk.NewStdTx(whatCoolMsg1, []sdk.StdSignature{{
//PubKey: priv1.PubKey(),
//Signature: priv1.Sign(whatCoolMsg1.GetSignBytes()),
//}})
//// Run a Check
//res := bapp.Check(tx)
//assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
//// Simulate a Block
//bapp.BeginBlock(abci.RequestBeginBlock{})
//res = bapp.Deliver(tx)
//assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
//// Check balances
//ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
//res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
//assert.Equal(t, "70icecold", fmt.Sprintf("%v", res2.GetCoins()))
//}

View File

@ -13,6 +13,7 @@ import (
"github.com/cosmos/cosmos-sdk/client/lcd"
"github.com/cosmos/cosmos-sdk/client/rpc"
"github.com/cosmos/cosmos-sdk/client/tx"
coolcmd "github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool/commands"
"github.com/cosmos/cosmos-sdk/version"
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
@ -55,6 +56,14 @@ func main() {
client.PostCommands(
bankcmd.SendTxCmd(cdc),
)...)
basecliCmd.AddCommand(
client.PostCommands(
coolcmd.WhatCoolTxCmd(cdc),
)...)
basecliCmd.AddCommand(
client.PostCommands(
coolcmd.SetWhatCoolTxCmd(cdc),
)...)
// add proxy, version and key info
basecliCmd.AddCommand(

View File

@ -27,6 +27,9 @@ func GetParseAccount(cdc *wire.Codec) sdk.ParseAccount {
return func(accBytes []byte) (res sdk.Account, err error) {
acct := new(AppAccount)
err = cdc.UnmarshalBinary(accBytes, &acct)
if err != nil {
panic(err)
}
return acct, err
}
}

View File

@ -0,0 +1,75 @@
package commands
import (
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client/builder"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
)
// what cool transaction
func WhatCoolTxCmd(cdc *wire.Codec) *cobra.Command {
return &cobra.Command{
Use: "whatcool [answer]",
Short: "What's cooler than being cool?",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide an answer")
}
// get the from address from the name flag
from, err := builder.GetFromAddress()
if err != nil {
return err
}
// create the message
msg := cool.NewWhatCoolMsg(from, args[0])
// build and sign the transaction, then broadcast to Tendermint
res, err := builder.SignBuildBroadcast(msg, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
}
// set what cool transaction
func SetWhatCoolTxCmd(cdc *wire.Codec) *cobra.Command {
return &cobra.Command{
Use: "setwhatcool [answer]",
Short: "You're so cool, tell us what is cool!",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 || len(args[0]) == 0 {
return errors.New("You must provide an answer")
}
// get the from address from the name flag
from, err := builder.GetFromAddress()
if err != nil {
return err
}
// create the message
msg := cool.NewSetWhatCoolMsg(from, args[0])
// build and sign the transaction, then broadcast to Tendermint
res, err := builder.SignBuildBroadcast(msg, cdc)
if err != nil {
return err
}
fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String())
return nil
},
}
}

View File

@ -0,0 +1,57 @@
package cool
import (
"fmt"
"reflect"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/bank"
)
// This is just an example to demonstrate a functional custom module
// with full feature set functionality.
//
// /$$$$$$$ /$$$$$$ /$$$$$$ /$$
// /$$_____/ /$$__ $$ /$$__ $$| $$
//| $$ | $$ \ $$| $$ \ $$| $$
//| $$ | $$ | $$| $$ | $$| $$
//| $$$$$$$| $$$$$$/| $$$$$$/| $$$$$$$
// \_______/ \______/ \______/ |______/
// Handle all "coolmodule" type objects
func NewHandler(ck bank.CoinKeeper, cm Mapper) sdk.Handler {
return func(ctx sdk.Context, msg sdk.Msg) sdk.Result {
switch msg := msg.(type) {
case SetWhatCoolMsg:
return handleSetWhatCoolMsg(ctx, cm, msg)
case WhatCoolMsg:
return handleWhatCoolMsg(ctx, ck, cm, msg)
default:
errMsg := fmt.Sprintf("Unrecognized cool Msg type: %v", reflect.TypeOf(msg).Name())
return sdk.ErrUnknownRequest(errMsg).Result()
}
}
}
// Handle WhatCoolMsg This is the engine of your module
func handleSetWhatCoolMsg(ctx sdk.Context, cm Mapper, msg SetWhatCoolMsg) sdk.Result {
cm.SetWhatCool(ctx, msg.WhatCool)
return sdk.Result{}
}
// Handle WhatCoolMsg This is the engine of your module
func handleWhatCoolMsg(ctx sdk.Context, ck bank.CoinKeeper, cm Mapper, msg WhatCoolMsg) sdk.Result {
whatsCool := cm.GetWhatCool(ctx)
if msg.CoolerThanCool == whatsCool {
bonusCoins := sdk.Coins{{whatsCool, 69}}
_, err := ck.AddCoins(ctx, msg.Sender, bonusCoins)
if err != nil {
return err.Result()
}
}
return sdk.Result{}
}

View File

@ -0,0 +1,30 @@
package cool
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// This Cool Mapper handlers sets/gets of custom variables for your module
type Mapper struct {
key sdk.StoreKey // The (unexposed) key used to access the store from the Context.
}
func NewMapper(key sdk.StoreKey) Mapper {
return Mapper{key}
}
// Key to knowing whats cool
var whatCoolKey = []byte("WhatsCoolKey")
// Implements sdk.AccountMapper.
func (am Mapper) GetWhatCool(ctx sdk.Context) string {
store := ctx.KVStore(am.key)
bz := store.Get(whatCoolKey)
return string(bz)
}
// Implements sdk.AccountMapper.
func (am Mapper) SetWhatCool(ctx sdk.Context, whatscool string) {
store := ctx.KVStore(am.key)
store.Set(whatCoolKey, []byte(whatscool))
}

View File

@ -0,0 +1,103 @@
package cool
import (
"encoding/json"
"fmt"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// A really cool msg type, these fields are can be entirely arbitrary and
// custom to your message
type SetWhatCoolMsg struct {
Sender sdk.Address
WhatCool string
}
// New cool message
func NewSetWhatCoolMsg(sender sdk.Address, whatcool string) SetWhatCoolMsg {
return SetWhatCoolMsg{
Sender: sender,
WhatCool: whatcool,
}
}
// enforce the msg type at compile time
var _ sdk.Msg = SetWhatCoolMsg{}
// nolint
func (msg SetWhatCoolMsg) Type() string { return "cool" }
func (msg SetWhatCoolMsg) Get(key interface{}) (value interface{}) { return nil }
func (msg SetWhatCoolMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
func (msg SetWhatCoolMsg) String() string {
return fmt.Sprintf("SetWhatCoolMsg{Sender: %v, WhatCool: %v}", msg.Sender, msg.WhatCool)
}
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
func (msg SetWhatCoolMsg) ValidateBasic() sdk.Error {
if len(msg.Sender) == 0 {
return sdk.ErrUnrecognizedAddress(msg.Sender).Trace("")
}
if strings.Contains(msg.WhatCool, "hot") {
return sdk.ErrUnauthorized("").Trace("hot is not cool")
}
if strings.Contains(msg.WhatCool, "warm") {
return sdk.ErrUnauthorized("").Trace("warm is not very cool")
}
return nil
}
// Get the bytes for the message signer to sign on
func (msg SetWhatCoolMsg) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return b
}
//_______________________________________________________________________
// A really cool msg type, these fields are can be entirely arbitrary and
// custom to your message
type WhatCoolMsg struct {
Sender sdk.Address
CoolerThanCool string
}
// New cool message
func NewWhatCoolMsg(sender sdk.Address, coolerthancool string) WhatCoolMsg {
return WhatCoolMsg{
Sender: sender,
CoolerThanCool: coolerthancool,
}
}
// enforce the msg type at compile time
var _ sdk.Msg = WhatCoolMsg{}
// nolint
func (msg WhatCoolMsg) Type() string { return "cool" }
func (msg WhatCoolMsg) Get(key interface{}) (value interface{}) { return nil }
func (msg WhatCoolMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} }
func (msg WhatCoolMsg) String() string {
return fmt.Sprintf("WhatCoolMsg{Sender: %v, CoolerThanCool: %v}", msg.Sender, msg.CoolerThanCool)
}
// Validate Basic is used to quickly disqualify obviously invalid messages quickly
func (msg WhatCoolMsg) ValidateBasic() sdk.Error {
if len(msg.Sender) == 0 {
return sdk.ErrUnrecognizedAddress(msg.Sender).Trace("")
}
return nil
}
// Get the bytes for the message signer to sign on
func (msg WhatCoolMsg) GetSignBytes() []byte {
b, err := json.Marshal(msg)
if err != nil {
panic(err)
}
return b
}

View File

@ -5,6 +5,7 @@ import (
cmn "github.com/tendermint/tmlibs/common"
)
// Address in go-crypto style
type Address = cmn.HexBytes
// Account is a standard account using a sequence number for replay protection

View File

@ -23,6 +23,8 @@ type Msg interface {
GetSigners() []Address
}
//__________________________________________________________
// Transactions objects must fulfill the Tx
type Tx interface {
@ -64,7 +66,7 @@ func (tx StdTx) GetMsg() Msg { return tx.Msg }
func (tx StdTx) GetFeePayer() Address { return tx.Signatures[0].PubKey.Address() } // XXX but PubKey is optional!
func (tx StdTx) GetSignatures() []StdSignature { return tx.Signatures }
//-------------------------------------
//__________________________________________________________
// Application function variable used to unmarshal transaction bytes
type TxDecoder func(txBytes []byte) (Tx, Error)

View File

@ -8,7 +8,7 @@ import (
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/builder"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
"github.com/cosmos/cosmos-sdk/x/auth"
@ -22,7 +22,10 @@ func GetAccountCmdDefault(storeName string, cdc *wire.Codec) *cobra.Command {
func getParseAccount(cdc *wire.Codec) sdk.ParseAccount {
return func(accBytes []byte) (sdk.Account, error) {
acct := new(auth.BaseAccount)
err := cdc.UnmarshalBinary(accBytes, acct)
err := cdc.UnmarshalBinary(accBytes, &acct)
if err != nil {
panic(err)
}
return acct, err
}
}
@ -61,7 +64,7 @@ func (c commander) getAccountCmd(cmd *cobra.Command, args []string) error {
}
key := sdk.Address(bz)
res, err := client.Query(key, c.storeName)
res, err := builder.Query(key, c.storeName)
// parse out the value
account, err := c.parser(res)

View File

@ -4,12 +4,10 @@ import (
"encoding/hex"
"fmt"
"github.com/pkg/errors"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/keys"
"github.com/cosmos/cosmos-sdk/client/builder"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/wire"
@ -17,10 +15,8 @@ import (
)
const (
flagTo = "to"
flagAmount = "amount"
flagFee = "fee"
flagSequence = "seq"
flagTo = "to"
flagAmount = "amount"
)
// SendTxCommand will create a send tx and sign it with the given key
@ -33,8 +29,6 @@ func SendTxCmd(cdc *wire.Codec) *cobra.Command {
}
cmd.Flags().String(flagTo, "", "Address to send coins")
cmd.Flags().String(flagAmount, "", "Amount of coins to send")
cmd.Flags().String(flagFee, "", "Fee to pay along with transaction")
cmd.Flags().Int64(flagSequence, 0, "Sequence number to sign the tx")
return cmd
}
@ -43,12 +37,21 @@ type commander struct {
}
func (c commander) sendTxCmd(cmd *cobra.Command, args []string) error {
txBytes, err := c.buildTx()
// get the from address
from, err := builder.GetFromAddress()
if err != nil {
return err
}
res, err := client.BroadcastTx(txBytes)
// build send msg
msg, err := buildMsg(from)
if err != nil {
return err
}
// build and sign the transaction, then broadcast to Tendermint
res, err := builder.SignBuildBroadcast(msg, c.cdc)
if err != nil {
return err
}
@ -57,52 +60,6 @@ func (c commander) sendTxCmd(cmd *cobra.Command, args []string) error {
return nil
}
func (c commander) buildTx() ([]byte, error) {
keybase, err := keys.GetKeyBase()
if err != nil {
return nil, err
}
name := viper.GetString(client.FlagName)
info, err := keybase.Get(name)
if err != nil {
return nil, errors.Errorf("No key for: %s", name)
}
from := info.PubKey.Address()
msg, err := buildMsg(from)
if err != nil {
return nil, err
}
// sign and build
bz := msg.GetSignBytes()
buf := client.BufferStdin()
prompt := fmt.Sprintf("Password to sign with '%s':", name)
passphrase, err := client.GetPassword(prompt, buf)
if err != nil {
return nil, err
}
sig, pubkey, err := keybase.Sign(name, passphrase, bz)
if err != nil {
return nil, err
}
sigs := []sdk.StdSignature{{
PubKey: pubkey,
Signature: sig,
Sequence: viper.GetInt64(flagSequence),
}}
// marshal bytes
tx := sdk.NewStdTx(msg, sigs)
txBytes, err := c.cdc.MarshalBinary(tx)
if err != nil {
return nil, err
}
return txBytes, nil
}
func buildMsg(from sdk.Address) (sdk.Msg, error) {
// parse coins

View File

@ -15,6 +15,8 @@ type SendMsg struct {
Outputs []Output `json:"outputs"`
}
var _ sdk.Msg = SendMsg{}
// NewSendMsg - construct arbitrary multi-in, multi-out send msg.
func NewSendMsg(in []Input, out []Output) SendMsg {
return SendMsg{Inputs: in, Outputs: out}
@ -87,7 +89,7 @@ func (msg SendMsg) GetSigners() []sdk.Address {
// IssueMsg - high level transaction of the coin module
type IssueMsg struct {
Banker sdk.Address `json:"banker"`
Outputs []Output `json:"outputs"`
Outputs []Output `json:"outputs"`
}
// NewIssueMsg - construct arbitrary multi-in, multi-out send msg.
@ -96,7 +98,7 @@ func NewIssueMsg(banker sdk.Address, out []Output) IssueMsg {
}
// Implements Msg.
func (msg IssueMsg) Type() string { return "bank" } // TODO: "bank/send"
func (msg IssueMsg) Type() string { return "bank" } // TODO: "bank/issue"
// Implements Msg.
func (msg IssueMsg) ValidateBasic() sdk.Error {
@ -138,10 +140,11 @@ func (msg IssueMsg) GetSigners() []sdk.Address {
//----------------------------------------
// Input
// Transaction Output
type Input struct {
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
Sequence int64 `json:"sequence"`
Coins sdk.Coins `json:"coins"`
Sequence int64 `json:"sequence"`
signature crypto.Signature
}
@ -186,9 +189,10 @@ func NewInputWithSequence(addr sdk.Address, coins sdk.Coins, seq int64) Input {
//----------------------------------------
// Output
// Transaction Output
type Output struct {
Address sdk.Address `json:"address"`
Coins sdk.Coins `json:"coins"`
Coins sdk.Coins `json:"coins"`
}
// ValidateBasic - validate transaction output

View File

@ -4,6 +4,7 @@ import (
"github.com/cosmos/cosmos-sdk/wire"
)
// Register concrete types on wire codec
func RegisterWire(cdc *wire.Codec) {
// TODO: bring this back ...
/*