Merge pull request #552 from cosmos/rigel/basecoin-upgrade
Basecoin Upgrade + tiny bit client refactor
This commit is contained in:
commit
032a0b4539
14
CHANGELOG.md
14
CHANGELOG.md
|
@ -1,5 +1,19 @@
|
||||||
# Changelog
|
# 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)
|
## 0.11.0 (March 1, 2017)
|
||||||
|
|
||||||
BREAKING CHANGES
|
BREAKING CHANGES
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -9,6 +9,8 @@ const (
|
||||||
FlagHeight = "height"
|
FlagHeight = "height"
|
||||||
FlagTrustNode = "trust-node"
|
FlagTrustNode = "trust-node"
|
||||||
FlagName = "name"
|
FlagName = "name"
|
||||||
|
FlagSequence = "sequence"
|
||||||
|
FlagFee = "fee"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LineBreak can be included in a command list to provide a blank line
|
// 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 {
|
func PostCommands(cmds ...*cobra.Command) []*cobra.Command {
|
||||||
for _, c := range cmds {
|
for _, c := range cmds {
|
||||||
c.Flags().String(FlagName, "", "Name of private key with which to sign")
|
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(FlagChainID, "", "Chain ID of tendermint node")
|
||||||
c.Flags().String(FlagNode, "tcp://localhost:46657", "<host>:<port> to tendermint rpc interface for this chain")
|
c.Flags().String(FlagNode, "tcp://localhost:46657", "<host>:<port> to tendermint rpc interface for this chain")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
package client
|
package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
rpcclient "github.com/tendermint/tendermint/rpc/client"
|
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
|
// GetNode prepares a simple rpc.Client from the flags
|
||||||
|
@ -19,53 +15,3 @@ func GetNode() (rpcclient.Client, error) {
|
||||||
}
|
}
|
||||||
return rpcclient.NewHTTP(uri, "/websocket"), nil
|
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -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
|
"Basecoin" is not affiliated with [Coinbase](http://www.getbasecoin.com/), nor
|
||||||
the [stable coin](http://www.getbasecoin.com/).
|
the [stable coin](http://www.getbasecoin.com/).
|
||||||
|
|
||||||
Assuming you've run `make get_tools && make get_vendor_deps` from the root of this repository,
|
Assuming you've run `make get_tools && make get_vendor_deps` from the root of
|
||||||
run `make build` here to build the `basecoind` and `basecli` binaries.
|
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.
|
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.
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
"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"
|
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/sketchy"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -53,8 +54,10 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
||||||
|
|
||||||
// add handlers
|
// add handlers
|
||||||
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
coinKeeper := bank.NewCoinKeeper(app.accountMapper)
|
||||||
|
coolMapper := cool.NewMapper(app.capKeyMainStore)
|
||||||
app.Router().
|
app.Router().
|
||||||
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
AddRoute("bank", bank.NewHandler(coinKeeper)).
|
||||||
|
AddRoute("cool", cool.NewHandler(coinKeeper, coolMapper)).
|
||||||
AddRoute("sketchy", sketchy.NewHandler())
|
AddRoute("sketchy", sketchy.NewHandler())
|
||||||
|
|
||||||
// initialize BaseApp
|
// initialize BaseApp
|
||||||
|
@ -73,39 +76,38 @@ func NewBasecoinApp(logger log.Logger, db dbm.DB) *BasecoinApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom tx codec
|
// custom tx codec
|
||||||
|
// TODO: use new go-wire
|
||||||
func MakeCodec() *wire.Codec {
|
func MakeCodec() *wire.Codec {
|
||||||
|
|
||||||
// XXX: Using old wire for now :)
|
const msgTypeSend = 0x1
|
||||||
const (
|
const msgTypeIssue = 0x2
|
||||||
msgTypeSend = 0x1
|
const msgTypeWhatCool = 0x3
|
||||||
msgTypeIssue = 0x2
|
const msgTypeSetWhatCool = 0x4
|
||||||
)
|
|
||||||
var _ = oldwire.RegisterInterface(
|
var _ = oldwire.RegisterInterface(
|
||||||
struct{ sdk.Msg }{},
|
struct{ sdk.Msg }{},
|
||||||
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
|
oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend},
|
||||||
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
|
oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue},
|
||||||
|
oldwire.ConcreteType{cool.WhatCoolMsg{}, msgTypeWhatCool},
|
||||||
|
oldwire.ConcreteType{cool.SetWhatCoolMsg{}, msgTypeSetWhatCool},
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const accTypeApp = 0x1
|
||||||
accTypeApp = 0x1
|
|
||||||
)
|
|
||||||
var _ = oldwire.RegisterInterface(
|
var _ = oldwire.RegisterInterface(
|
||||||
struct{ sdk.Account }{},
|
struct{ sdk.Account }{},
|
||||||
oldwire.ConcreteType{&types.AppAccount{}, accTypeApp},
|
oldwire.ConcreteType{&types.AppAccount{}, accTypeApp},
|
||||||
)
|
)
|
||||||
|
|
||||||
cdc := wire.NewCodec()
|
cdc := wire.NewCodec()
|
||||||
// TODO: use new go-wire
|
|
||||||
// cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
// cdc.RegisterInterface((*sdk.Msg)(nil), nil)
|
||||||
// bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types.
|
// bank.RegisterWire(cdc) // Register bank.[SendMsg,IssueMsg] types.
|
||||||
// crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types.
|
// crypto.RegisterWire(cdc) // Register crypto.[PubKey,PrivKey,Signature] types.
|
||||||
return cdc
|
return cdc
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// custom logic for transaction decoding
|
// custom logic for transaction decoding
|
||||||
func (app *BasecoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
|
func (app *BasecoinApp) txDecoder(txBytes []byte) (sdk.Tx, sdk.Error) {
|
||||||
var tx = sdk.StdTx{}
|
var tx = sdk.StdTx{}
|
||||||
|
|
||||||
// StdTx.Msg is an interface. The concrete types
|
// StdTx.Msg is an interface. The concrete types
|
||||||
// are registered by MakeTxCodec in bank.RegisterWire.
|
// are registered by MakeTxCodec in bank.RegisterWire.
|
||||||
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
err := app.cdc.UnmarshalBinary(txBytes, &tx)
|
||||||
|
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
"github.com/cosmos/cosmos-sdk/examples/basecoin/types"
|
||||||
|
"github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"github.com/cosmos/cosmos-sdk/x/auth"
|
||||||
"github.com/cosmos/cosmos-sdk/x/bank"
|
"github.com/cosmos/cosmos-sdk/x/bank"
|
||||||
|
@ -20,54 +21,88 @@ import (
|
||||||
"github.com/tendermint/tmlibs/log"
|
"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 {
|
func newBasecoinApp() *BasecoinApp {
|
||||||
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout)).With("module", "sdk/app")
|
||||||
db := dbm.NewMemDB()
|
db := dbm.NewMemDB()
|
||||||
return NewBasecoinApp(logger, db)
|
return NewBasecoinApp(logger, db)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSendMsg(t *testing.T) {
|
//_______________________________________________________________________
|
||||||
|
|
||||||
|
func TestMsgs(t *testing.T) {
|
||||||
bapp := newBasecoinApp()
|
bapp := newBasecoinApp()
|
||||||
|
|
||||||
// Construct a SendMsg
|
msgs := []struct {
|
||||||
var msg = bank.SendMsg{
|
msg sdk.Msg
|
||||||
Inputs: []bank.Input{
|
}{
|
||||||
{
|
{sendMsg},
|
||||||
Address: sdk.Address([]byte("input")),
|
{whatCoolMsg1},
|
||||||
Coins: sdk.Coins{{"atom", 10}},
|
{setWhatCoolMsg},
|
||||||
Sequence: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Outputs: []bank.Output{
|
|
||||||
{
|
|
||||||
Address: sdk.Address([]byte("output")),
|
|
||||||
Coins: sdk.Coins{{"atom", 10}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
priv := crypto.GenPrivKeyEd25519()
|
for i, m := range msgs {
|
||||||
sig := priv.Sign(msg.GetSignBytes())
|
sig := priv1.Sign(m.msg.GetSignBytes())
|
||||||
tx := sdk.NewStdTx(msg, []sdk.StdSignature{{
|
tx := sdk.NewStdTx(m.msg, []sdk.StdSignature{{
|
||||||
PubKey: priv.PubKey(),
|
PubKey: priv1.PubKey(),
|
||||||
Signature: sig,
|
Signature: sig,
|
||||||
}})
|
}})
|
||||||
|
|
||||||
// just marshal/unmarshal!
|
// just marshal/unmarshal!
|
||||||
cdc := MakeCodec()
|
cdc := MakeCodec()
|
||||||
txBytes, err := cdc.MarshalBinary(tx)
|
txBytes, err := cdc.MarshalBinary(tx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err, "i: %v", i)
|
||||||
|
|
||||||
// Run a Check
|
// Run a Check
|
||||||
cres := bapp.CheckTx(txBytes)
|
cres := bapp.CheckTx(txBytes)
|
||||||
assert.Equal(t, sdk.CodeUnrecognizedAddress,
|
assert.Equal(t, sdk.CodeUnrecognizedAddress,
|
||||||
sdk.CodeType(cres.Code), cres.Log)
|
sdk.CodeType(cres.Code), "i: %v, log: %v", i, cres.Log)
|
||||||
|
|
||||||
// Simulate a Block
|
// Simulate a Block
|
||||||
bapp.BeginBlock(abci.RequestBeginBlock{})
|
bapp.BeginBlock(abci.RequestBeginBlock{})
|
||||||
dres := bapp.DeliverTx(txBytes)
|
dres := bapp.DeliverTx(txBytes)
|
||||||
assert.Equal(t, sdk.CodeUnrecognizedAddress,
|
assert.Equal(t, sdk.CodeUnrecognizedAddress,
|
||||||
sdk.CodeType(dres.Code), dres.Log)
|
sdk.CodeType(dres.Code), "i: %v, log: %v", i, dres.Log)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenesis(t *testing.T) {
|
func TestGenesis(t *testing.T) {
|
||||||
|
@ -114,15 +149,6 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
||||||
bapp := newBasecoinApp()
|
bapp := newBasecoinApp()
|
||||||
|
|
||||||
// Construct some genesis bytes to reflect basecoin/types/AppAccount
|
// 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
|
// Give 77 foocoin to the first key
|
||||||
coins, err := sdk.ParseCoins("77foocoin")
|
coins, err := sdk.ParseCoins("77foocoin")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
@ -139,6 +165,7 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
stateBytes, err := json.MarshalIndent(genesisState, "", "\t")
|
||||||
|
require.Nil(t, err)
|
||||||
|
|
||||||
// Initialize the chain
|
// Initialize the chain
|
||||||
vals := []abci.Validator{}
|
vals := []abci.Validator{}
|
||||||
|
@ -147,32 +174,13 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
||||||
|
|
||||||
// A checkTx context (true)
|
// A checkTx context (true)
|
||||||
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
ctxCheck := bapp.BaseApp.NewContext(true, abci.Header{})
|
||||||
|
|
||||||
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
res1 := bapp.accountMapper.GetAccount(ctxCheck, addr1)
|
||||||
assert.Equal(t, acc1, res1)
|
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
|
// Sign the tx
|
||||||
sig := priv1.Sign(msg.GetSignBytes())
|
tx := sdk.NewStdTx(sendMsg, []sdk.StdSignature{{
|
||||||
tx := sdk.NewStdTx(msg, []sdk.StdSignature{{
|
|
||||||
PubKey: priv1.PubKey(),
|
PubKey: priv1.PubKey(),
|
||||||
Signature: sig,
|
Signature: priv1.Sign(sendMsg.GetSignBytes()),
|
||||||
}})
|
}})
|
||||||
|
|
||||||
// Run a Check
|
// Run a Check
|
||||||
|
@ -184,13 +192,64 @@ func TestSendMsgWithAccounts(t *testing.T) {
|
||||||
res = bapp.Deliver(tx)
|
res = bapp.Deliver(tx)
|
||||||
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
assert.Equal(t, sdk.CodeOK, res.Code, res.Log)
|
||||||
|
|
||||||
// A deliverTx context
|
|
||||||
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
|
|
||||||
|
|
||||||
// Check balances
|
// Check balances
|
||||||
|
ctxDeliver := bapp.BaseApp.NewContext(false, abci.Header{})
|
||||||
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
res2 := bapp.accountMapper.GetAccount(ctxDeliver, addr1)
|
||||||
res3 := bapp.accountMapper.GetAccount(ctxDeliver, addr2)
|
res3 := bapp.accountMapper.GetAccount(ctxDeliver, addr2)
|
||||||
|
|
||||||
assert.Equal(t, fmt.Sprintf("%v", res2.GetCoins()), "67foocoin")
|
assert.Equal(t, fmt.Sprintf("%v", res2.GetCoins()), "67foocoin")
|
||||||
assert.Equal(t, fmt.Sprintf("%v", res3.GetCoins()), "10foocoin")
|
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()))
|
||||||
|
//}
|
||||||
|
|
|
@ -13,6 +13,7 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/client/lcd"
|
"github.com/cosmos/cosmos-sdk/client/lcd"
|
||||||
"github.com/cosmos/cosmos-sdk/client/rpc"
|
"github.com/cosmos/cosmos-sdk/client/rpc"
|
||||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||||
|
coolcmd "github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool/commands"
|
||||||
"github.com/cosmos/cosmos-sdk/version"
|
"github.com/cosmos/cosmos-sdk/version"
|
||||||
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
authcmd "github.com/cosmos/cosmos-sdk/x/auth/commands"
|
||||||
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
|
bankcmd "github.com/cosmos/cosmos-sdk/x/bank/commands"
|
||||||
|
@ -55,6 +56,14 @@ func main() {
|
||||||
client.PostCommands(
|
client.PostCommands(
|
||||||
bankcmd.SendTxCmd(cdc),
|
bankcmd.SendTxCmd(cdc),
|
||||||
)...)
|
)...)
|
||||||
|
basecliCmd.AddCommand(
|
||||||
|
client.PostCommands(
|
||||||
|
coolcmd.WhatCoolTxCmd(cdc),
|
||||||
|
)...)
|
||||||
|
basecliCmd.AddCommand(
|
||||||
|
client.PostCommands(
|
||||||
|
coolcmd.SetWhatCoolTxCmd(cdc),
|
||||||
|
)...)
|
||||||
|
|
||||||
// add proxy, version and key info
|
// add proxy, version and key info
|
||||||
basecliCmd.AddCommand(
|
basecliCmd.AddCommand(
|
||||||
|
|
|
@ -27,6 +27,9 @@ func GetParseAccount(cdc *wire.Codec) sdk.ParseAccount {
|
||||||
return func(accBytes []byte) (res sdk.Account, err error) {
|
return func(accBytes []byte) (res sdk.Account, err error) {
|
||||||
acct := new(AppAccount)
|
acct := new(AppAccount)
|
||||||
err = cdc.UnmarshalBinary(accBytes, &acct)
|
err = cdc.UnmarshalBinary(accBytes, &acct)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return acct, err
|
return acct, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
|
@ -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{}
|
||||||
|
}
|
|
@ -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))
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -5,6 +5,7 @@ import (
|
||||||
cmn "github.com/tendermint/tmlibs/common"
|
cmn "github.com/tendermint/tmlibs/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Address in go-crypto style
|
||||||
type Address = cmn.HexBytes
|
type Address = cmn.HexBytes
|
||||||
|
|
||||||
// Account is a standard account using a sequence number for replay protection
|
// Account is a standard account using a sequence number for replay protection
|
||||||
|
|
|
@ -23,6 +23,8 @@ type Msg interface {
|
||||||
GetSigners() []Address
|
GetSigners() []Address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//__________________________________________________________
|
||||||
|
|
||||||
// Transactions objects must fulfill the Tx
|
// Transactions objects must fulfill the Tx
|
||||||
type Tx interface {
|
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) GetFeePayer() Address { return tx.Signatures[0].PubKey.Address() } // XXX but PubKey is optional!
|
||||||
func (tx StdTx) GetSignatures() []StdSignature { return tx.Signatures }
|
func (tx StdTx) GetSignatures() []StdSignature { return tx.Signatures }
|
||||||
|
|
||||||
//-------------------------------------
|
//__________________________________________________________
|
||||||
|
|
||||||
// Application function variable used to unmarshal transaction bytes
|
// Application function variable used to unmarshal transaction bytes
|
||||||
type TxDecoder func(txBytes []byte) (Tx, Error)
|
type TxDecoder func(txBytes []byte) (Tx, Error)
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
"github.com/cosmos/cosmos-sdk/x/auth"
|
"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 {
|
func getParseAccount(cdc *wire.Codec) sdk.ParseAccount {
|
||||||
return func(accBytes []byte) (sdk.Account, error) {
|
return func(accBytes []byte) (sdk.Account, error) {
|
||||||
acct := new(auth.BaseAccount)
|
acct := new(auth.BaseAccount)
|
||||||
err := cdc.UnmarshalBinary(accBytes, acct)
|
err := cdc.UnmarshalBinary(accBytes, &acct)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
return acct, err
|
return acct, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,7 +64,7 @@ func (c commander) getAccountCmd(cmd *cobra.Command, args []string) error {
|
||||||
}
|
}
|
||||||
key := sdk.Address(bz)
|
key := sdk.Address(bz)
|
||||||
|
|
||||||
res, err := client.Query(key, c.storeName)
|
res, err := builder.Query(key, c.storeName)
|
||||||
|
|
||||||
// parse out the value
|
// parse out the value
|
||||||
account, err := c.parser(res)
|
account, err := c.parser(res)
|
||||||
|
|
|
@ -4,12 +4,10 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/cosmos/cosmos-sdk/client"
|
"github.com/cosmos/cosmos-sdk/client/builder"
|
||||||
"github.com/cosmos/cosmos-sdk/client/keys"
|
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
|
|
||||||
|
@ -17,10 +15,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
flagTo = "to"
|
flagTo = "to"
|
||||||
flagAmount = "amount"
|
flagAmount = "amount"
|
||||||
flagFee = "fee"
|
|
||||||
flagSequence = "seq"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SendTxCommand will create a send tx and sign it with the given key
|
// 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(flagTo, "", "Address to send coins")
|
||||||
cmd.Flags().String(flagAmount, "", "Amount of coins to send")
|
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
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,12 +37,21 @@ type commander struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c commander) sendTxCmd(cmd *cobra.Command, args []string) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -57,52 +60,6 @@ func (c commander) sendTxCmd(cmd *cobra.Command, args []string) error {
|
||||||
return nil
|
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) {
|
func buildMsg(from sdk.Address) (sdk.Msg, error) {
|
||||||
|
|
||||||
// parse coins
|
// parse coins
|
||||||
|
|
14
x/bank/tx.go
14
x/bank/tx.go
|
@ -15,6 +15,8 @@ type SendMsg struct {
|
||||||
Outputs []Output `json:"outputs"`
|
Outputs []Output `json:"outputs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ sdk.Msg = SendMsg{}
|
||||||
|
|
||||||
// NewSendMsg - construct arbitrary multi-in, multi-out send msg.
|
// NewSendMsg - construct arbitrary multi-in, multi-out send msg.
|
||||||
func NewSendMsg(in []Input, out []Output) SendMsg {
|
func NewSendMsg(in []Input, out []Output) SendMsg {
|
||||||
return SendMsg{Inputs: in, Outputs: out}
|
return SendMsg{Inputs: in, Outputs: out}
|
||||||
|
@ -87,7 +89,7 @@ func (msg SendMsg) GetSigners() []sdk.Address {
|
||||||
// IssueMsg - high level transaction of the coin module
|
// IssueMsg - high level transaction of the coin module
|
||||||
type IssueMsg struct {
|
type IssueMsg struct {
|
||||||
Banker sdk.Address `json:"banker"`
|
Banker sdk.Address `json:"banker"`
|
||||||
Outputs []Output `json:"outputs"`
|
Outputs []Output `json:"outputs"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewIssueMsg - construct arbitrary multi-in, multi-out send msg.
|
// NewIssueMsg - construct arbitrary multi-in, multi-out send msg.
|
||||||
|
@ -96,7 +98,7 @@ func NewIssueMsg(banker sdk.Address, out []Output) IssueMsg {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implements Msg.
|
// Implements Msg.
|
||||||
func (msg IssueMsg) Type() string { return "bank" } // TODO: "bank/send"
|
func (msg IssueMsg) Type() string { return "bank" } // TODO: "bank/issue"
|
||||||
|
|
||||||
// Implements Msg.
|
// Implements Msg.
|
||||||
func (msg IssueMsg) ValidateBasic() sdk.Error {
|
func (msg IssueMsg) ValidateBasic() sdk.Error {
|
||||||
|
@ -138,10 +140,11 @@ func (msg IssueMsg) GetSigners() []sdk.Address {
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
// Input
|
// Input
|
||||||
|
|
||||||
|
// Transaction Output
|
||||||
type Input struct {
|
type Input struct {
|
||||||
Address sdk.Address `json:"address"`
|
Address sdk.Address `json:"address"`
|
||||||
Coins sdk.Coins `json:"coins"`
|
Coins sdk.Coins `json:"coins"`
|
||||||
Sequence int64 `json:"sequence"`
|
Sequence int64 `json:"sequence"`
|
||||||
|
|
||||||
signature crypto.Signature
|
signature crypto.Signature
|
||||||
}
|
}
|
||||||
|
@ -186,9 +189,10 @@ func NewInputWithSequence(addr sdk.Address, coins sdk.Coins, seq int64) Input {
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
// Output
|
// Output
|
||||||
|
|
||||||
|
// Transaction Output
|
||||||
type Output struct {
|
type Output struct {
|
||||||
Address sdk.Address `json:"address"`
|
Address sdk.Address `json:"address"`
|
||||||
Coins sdk.Coins `json:"coins"`
|
Coins sdk.Coins `json:"coins"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateBasic - validate transaction output
|
// ValidateBasic - validate transaction output
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"github.com/cosmos/cosmos-sdk/wire"
|
"github.com/cosmos/cosmos-sdk/wire"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Register concrete types on wire codec
|
||||||
func RegisterWire(cdc *wire.Codec) {
|
func RegisterWire(cdc *wire.Codec) {
|
||||||
// TODO: bring this back ...
|
// TODO: bring this back ...
|
||||||
/*
|
/*
|
||||||
|
|
Loading…
Reference in New Issue