From 6dec9353bed9457b2758bdf1a21e050dbbb63e0d Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Fri, 2 Mar 2018 17:21:49 +0000 Subject: [PATCH 01/10] working new cool module in basecoin --- examples/basecoin/x/coolmodule/handler.go | 64 ++++++++++++++++ examples/basecoin/x/coolmodule/types.go | 92 +++++++++++++++++++++++ types/account.go | 1 + types/tx_msg.go | 4 +- x/bank/tx.go | 14 ++-- x/bank/wire.go | 1 + 6 files changed, 170 insertions(+), 6 deletions(-) create mode 100644 examples/basecoin/x/coolmodule/handler.go create mode 100644 examples/basecoin/x/coolmodule/types.go diff --git a/examples/basecoin/x/coolmodule/handler.go b/examples/basecoin/x/coolmodule/handler.go new file mode 100644 index 000000000..615d91bb8 --- /dev/null +++ b/examples/basecoin/x/coolmodule/handler.go @@ -0,0 +1,64 @@ +package coolmodule + +import ( + "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) sdk.Handler { + return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { + switch msg := msg.(type) { + case CoolMsg: + return handleCoolMsg(ctx, ck, msg) + case CoolerThanCoolMsg: + return handleMeltMsg(ctx, ck, msg) + default: + errMsg := "Unrecognized bank Msg type: " + reflect.TypeOf(msg).Name() + return sdk.ErrUnknownRequest(errMsg).Result() + } + } +} + +// Handle CoolMsg This is the engine of your module +func handleCoolMsg(ctx sdk.Context, ck CoinKeeper, msg CoolMsg) sdk.Result { + + if msg.coolerthancool == "icecold" { + + bonusCoins := sdk.Coins{{"icecold", 69}} + _, err := ck.AddCoins(ctx, msg.Address, bonusCoins) + if err != nil { + return err.Result() + } + } + + return sdk.Result{} +} + +// Handle CoolMsg This is the engine of your module +func handleMeltMsg(ctx sdk.Context, ck CoinKeeper, msg CoolMsg) sdk.Result { + + // checks for existence should already have occured + if strings.Prefix(msg.what, "ice") { + return bank.ErrInvalidInput("only frozen coins can use the blow dryer") + } + + bonusCoins := sdk.Coins{{"icecold", 69}} + _, err := ck.SubtractCoins(ctx, msg.Address, bonusCoins) + if err != nil { + return err.Result() + } + return sdk.Result{} +} diff --git a/examples/basecoin/x/coolmodule/types.go b/examples/basecoin/x/coolmodule/types.go new file mode 100644 index 000000000..62be2d49a --- /dev/null +++ b/examples/basecoin/x/coolmodule/types.go @@ -0,0 +1,92 @@ +package coolmodule + +import ( + "encoding/json" + "fmt" + + 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 CoolMsg struct { + Sender sdk.Address + Coolerthancool string +} + +// New cool message +func NewCoolMsg(sender sdk.Address, coolerthancool string) CoolMsg { + return CoolMsg{ + Sender: sender, + Coolerthancool: coolerthancool, + } +} + +// enforce the msg type at compile time +var _ CoolMsg = sdk.Msg + +// nolint +func (msg CoolMsg) Type() string { return "cool" } +func (msg CoolMsg) Get(key interface{}) (value interface{}) { return nil } +func (msg CoolMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} } +func (msg CoolMsg) String() string { return fmt.Sprintf("CoolMsg{%v}", msg) } + +// Validate Basic is used to quickly disqualify obviously invalid messages quickly +func (msg CoolMsg) ValidateBasic() sdk.Error { + if msg.Signer.Empty() { + return ErrNoOutputs().Trace("") + } + return nil +} + +// Get the bytes for the message signer to sign on +func (msg CoolMsg) 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 BlowDryMsg struct { + Sender sdk.Address + What sdk.Coin +} + +// New cool message +func NewBlowDryMsg(sender sdk.Address, what sdk.Coin) BlowDryMsg { + return BlowDryMsg{ + Sender: sender, + What: what, + } +} + +// enforce the msg type at compile time +var _ BlowDryMsg = sdk.Msg + +// nolint +func (msg BlowDryMsg) Type() string { return "cool" } +func (msg BlowDryMsg) Get(key interface{}) (value interface{}) { return nil } +func (msg BlowDryMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} } +func (msg BlowDryMsg) String() string { return fmt.Sprintf("BlowDryMsg{%v}", msg) } + +// Validate Basic is used to quickly disqualify obviously invalid messages quickly +func (msg BlowDryMsg) ValidateBasic() sdk.Error { + if msg.Signer.Empty() { + return ErrNoOutputs().Trace("") + } + return nil +} + +// Get the bytes for the message signer to sign on +func (msg BlowDryMsg) GetSignBytes() []byte { + b, err := json.Marshal(msg) + if err != nil { + panic(err) + } + return b +} diff --git a/types/account.go b/types/account.go index 4368ef0aa..266b62d40 100644 --- a/types/account.go +++ b/types/account.go @@ -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 diff --git a/types/tx_msg.go b/types/tx_msg.go index c919f6fc5..c6c96c364 100644 --- a/types/tx_msg.go +++ b/types/tx_msg.go @@ -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) diff --git a/x/bank/tx.go b/x/bank/tx.go index 48dcba85a..0f2d15c13 100644 --- a/x/bank/tx.go +++ b/x/bank/tx.go @@ -15,6 +15,8 @@ type SendMsg struct { Outputs []Output `json:"outputs"` } +var _ SendMsg = (nil)(sdk.Msg) + // 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 diff --git a/x/bank/wire.go b/x/bank/wire.go index a69398ea3..e229e0450 100644 --- a/x/bank/wire.go +++ b/x/bank/wire.go @@ -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 ... /* From 131130b7a4f03fb8b22e46db84bdc2a1934018e7 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Sat, 3 Mar 2018 17:30:08 +0000 Subject: [PATCH 02/10] basecoin upgrade without CLI --- examples/basecoin/app/app.go | 20 ++--- examples/basecoin/x/cool/handler.go | 57 ++++++++++++ examples/basecoin/x/cool/mapper.go | 30 +++++++ examples/basecoin/x/cool/types.go | 103 ++++++++++++++++++++++ examples/basecoin/x/coolmodule/handler.go | 64 -------------- examples/basecoin/x/coolmodule/types.go | 92 ------------------- x/bank/tx.go | 2 +- 7 files changed, 200 insertions(+), 168 deletions(-) create mode 100644 examples/basecoin/x/cool/handler.go create mode 100644 examples/basecoin/x/cool/mapper.go create mode 100644 examples/basecoin/x/cool/types.go delete mode 100644 examples/basecoin/x/coolmodule/handler.go delete mode 100644 examples/basecoin/x/coolmodule/types.go diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 420e0632f..4f1cb2fb0 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -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,34 @@ 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 var _ = oldwire.RegisterInterface( struct{ sdk.Msg }{}, oldwire.ConcreteType{bank.SendMsg{}, msgTypeSend}, oldwire.ConcreteType{bank.IssueMsg{}, msgTypeIssue}, ) - 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) diff --git a/examples/basecoin/x/cool/handler.go b/examples/basecoin/x/cool/handler.go new file mode 100644 index 000000000..152ae2f33 --- /dev/null +++ b/examples/basecoin/x/cool/handler.go @@ -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{} +} diff --git a/examples/basecoin/x/cool/mapper.go b/examples/basecoin/x/cool/mapper.go new file mode 100644 index 000000000..f437f8ca4 --- /dev/null +++ b/examples/basecoin/x/cool/mapper.go @@ -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)) +} diff --git a/examples/basecoin/x/cool/types.go b/examples/basecoin/x/cool/types.go new file mode 100644 index 000000000..872afe7f8 --- /dev/null +++ b/examples/basecoin/x/cool/types.go @@ -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 +} diff --git a/examples/basecoin/x/coolmodule/handler.go b/examples/basecoin/x/coolmodule/handler.go deleted file mode 100644 index 615d91bb8..000000000 --- a/examples/basecoin/x/coolmodule/handler.go +++ /dev/null @@ -1,64 +0,0 @@ -package coolmodule - -import ( - "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) sdk.Handler { - return func(ctx sdk.Context, msg sdk.Msg) sdk.Result { - switch msg := msg.(type) { - case CoolMsg: - return handleCoolMsg(ctx, ck, msg) - case CoolerThanCoolMsg: - return handleMeltMsg(ctx, ck, msg) - default: - errMsg := "Unrecognized bank Msg type: " + reflect.TypeOf(msg).Name() - return sdk.ErrUnknownRequest(errMsg).Result() - } - } -} - -// Handle CoolMsg This is the engine of your module -func handleCoolMsg(ctx sdk.Context, ck CoinKeeper, msg CoolMsg) sdk.Result { - - if msg.coolerthancool == "icecold" { - - bonusCoins := sdk.Coins{{"icecold", 69}} - _, err := ck.AddCoins(ctx, msg.Address, bonusCoins) - if err != nil { - return err.Result() - } - } - - return sdk.Result{} -} - -// Handle CoolMsg This is the engine of your module -func handleMeltMsg(ctx sdk.Context, ck CoinKeeper, msg CoolMsg) sdk.Result { - - // checks for existence should already have occured - if strings.Prefix(msg.what, "ice") { - return bank.ErrInvalidInput("only frozen coins can use the blow dryer") - } - - bonusCoins := sdk.Coins{{"icecold", 69}} - _, err := ck.SubtractCoins(ctx, msg.Address, bonusCoins) - if err != nil { - return err.Result() - } - return sdk.Result{} -} diff --git a/examples/basecoin/x/coolmodule/types.go b/examples/basecoin/x/coolmodule/types.go deleted file mode 100644 index 62be2d49a..000000000 --- a/examples/basecoin/x/coolmodule/types.go +++ /dev/null @@ -1,92 +0,0 @@ -package coolmodule - -import ( - "encoding/json" - "fmt" - - 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 CoolMsg struct { - Sender sdk.Address - Coolerthancool string -} - -// New cool message -func NewCoolMsg(sender sdk.Address, coolerthancool string) CoolMsg { - return CoolMsg{ - Sender: sender, - Coolerthancool: coolerthancool, - } -} - -// enforce the msg type at compile time -var _ CoolMsg = sdk.Msg - -// nolint -func (msg CoolMsg) Type() string { return "cool" } -func (msg CoolMsg) Get(key interface{}) (value interface{}) { return nil } -func (msg CoolMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} } -func (msg CoolMsg) String() string { return fmt.Sprintf("CoolMsg{%v}", msg) } - -// Validate Basic is used to quickly disqualify obviously invalid messages quickly -func (msg CoolMsg) ValidateBasic() sdk.Error { - if msg.Signer.Empty() { - return ErrNoOutputs().Trace("") - } - return nil -} - -// Get the bytes for the message signer to sign on -func (msg CoolMsg) 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 BlowDryMsg struct { - Sender sdk.Address - What sdk.Coin -} - -// New cool message -func NewBlowDryMsg(sender sdk.Address, what sdk.Coin) BlowDryMsg { - return BlowDryMsg{ - Sender: sender, - What: what, - } -} - -// enforce the msg type at compile time -var _ BlowDryMsg = sdk.Msg - -// nolint -func (msg BlowDryMsg) Type() string { return "cool" } -func (msg BlowDryMsg) Get(key interface{}) (value interface{}) { return nil } -func (msg BlowDryMsg) GetSigners() []sdk.Address { return []sdk.Address{msg.Sender} } -func (msg BlowDryMsg) String() string { return fmt.Sprintf("BlowDryMsg{%v}", msg) } - -// Validate Basic is used to quickly disqualify obviously invalid messages quickly -func (msg BlowDryMsg) ValidateBasic() sdk.Error { - if msg.Signer.Empty() { - return ErrNoOutputs().Trace("") - } - return nil -} - -// Get the bytes for the message signer to sign on -func (msg BlowDryMsg) GetSignBytes() []byte { - b, err := json.Marshal(msg) - if err != nil { - panic(err) - } - return b -} diff --git a/x/bank/tx.go b/x/bank/tx.go index 0f2d15c13..eef2a087d 100644 --- a/x/bank/tx.go +++ b/x/bank/tx.go @@ -15,7 +15,7 @@ type SendMsg struct { Outputs []Output `json:"outputs"` } -var _ SendMsg = (nil)(sdk.Msg) +var _ sdk.Msg = SendMsg{} // NewSendMsg - construct arbitrary multi-in, multi-out send msg. func NewSendMsg(in []Input, out []Output) SendMsg { From b6347db664103d6adc4de248ddb5dcac1afe06f0 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Sat, 3 Mar 2018 19:07:50 +0000 Subject: [PATCH 03/10] refactor client --- client/builder/builder.go | 127 ++++++++++++++++++++ client/flags.go | 4 + client/helpers.go | 54 --------- examples/basecoin/x/cool/commands/cooltx.go | 51 ++++++++ x/auth/commands/account.go | 4 +- x/bank/commands/sendtx.go | 3 +- 6 files changed, 186 insertions(+), 57 deletions(-) create mode 100644 client/builder/builder.go create mode 100644 examples/basecoin/x/cool/commands/cooltx.go diff --git a/client/builder/builder.go b/client/builder/builder.go new file mode 100644 index 000000000..65c2ffe43 --- /dev/null +++ b/client/builder/builder.go @@ -0,0 +1,127 @@ +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) + 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) +} diff --git a/client/flags.go b/client/flags.go index 843cb52d1..ceaf5a3a9 100644 --- a/client/flags.go +++ b/client/flags.go @@ -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", ": to tendermint rpc interface for this chain") } diff --git a/client/helpers.go b/client/helpers.go index 10ffcc88e..f383b95f7 100644 --- a/client/helpers.go +++ b/client/helpers.go @@ -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 -} diff --git a/examples/basecoin/x/cool/commands/cooltx.go b/examples/basecoin/x/cool/commands/cooltx.go new file mode 100644 index 000000000..8a8452a00 --- /dev/null +++ b/examples/basecoin/x/cool/commands/cooltx.go @@ -0,0 +1,51 @@ +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" +) + +// SendTxCommand will create a send tx and sign it with the given key +func WhatCoolTxCmd(cdc *wire.Codec) *cobra.Command { + cmdr := commander{cdc} + return &cobra.Command{ + Use: "whatcool [answer]", + Short: "What's cooler than being cool?", + RunE: cmdr.whatCoolTxCmd, + } +} + +type commander struct { + cdc *wire.Codec +} + +func (c commander) whatCoolTxCmd(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, c.cdc) + if err != nil { + return err + } + + fmt.Printf("Committed at block %d. Hash: %s\n", res.Height, res.Hash.String()) + return nil +} diff --git a/x/auth/commands/account.go b/x/auth/commands/account.go index 86df60a38..a8bbfa226 100644 --- a/x/auth/commands/account.go +++ b/x/auth/commands/account.go @@ -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" @@ -61,7 +61,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) diff --git a/x/bank/commands/sendtx.go b/x/bank/commands/sendtx.go index af171fb72..8d0c0be6e 100644 --- a/x/bank/commands/sendtx.go +++ b/x/bank/commands/sendtx.go @@ -9,6 +9,7 @@ import ( "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" "github.com/cosmos/cosmos-sdk/wire" @@ -48,7 +49,7 @@ func (c commander) sendTxCmd(cmd *cobra.Command, args []string) error { return err } - res, err := client.BroadcastTx(txBytes) + res, err := builder.BroadcastTx(txBytes) if err != nil { return err } From a2e4479dcc64352d92a03e941527b417efc61c94 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Sat, 3 Mar 2018 19:20:58 +0000 Subject: [PATCH 04/10] complete cool CLI --- examples/basecoin/x/cool/commands/cooltx.go | 82 +++++++++++++-------- x/bank/commands/sendtx.go | 62 +++------------- 2 files changed, 64 insertions(+), 80 deletions(-) diff --git a/examples/basecoin/x/cool/commands/cooltx.go b/examples/basecoin/x/cool/commands/cooltx.go index 8a8452a00..e1f1b866f 100644 --- a/examples/basecoin/x/cool/commands/cooltx.go +++ b/examples/basecoin/x/cool/commands/cooltx.go @@ -12,40 +12,64 @@ import ( "github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool" ) -// SendTxCommand will create a send tx and sign it with the given key +// what cool trasaction func WhatCoolTxCmd(cdc *wire.Codec) *cobra.Command { - cmdr := commander{cdc} return &cobra.Command{ Use: "whatcool [answer]", Short: "What's cooler than being cool?", - RunE: cmdr.whatCoolTxCmd, + 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 + }, } } -type commander struct { - cdc *wire.Codec -} - -func (c commander) whatCoolTxCmd(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, c.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 trasaction +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 + }, + } } diff --git a/x/bank/commands/sendtx.go b/x/bank/commands/sendtx.go index 8d0c0be6e..e74361231 100644 --- a/x/bank/commands/sendtx.go +++ b/x/bank/commands/sendtx.go @@ -4,13 +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/builder" - "github.com/cosmos/cosmos-sdk/client/keys" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/wire" @@ -44,12 +41,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 := builder.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 } @@ -58,52 +64,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 From fb199e293e394a98660dc388d08beeca51fb7ff2 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Sat, 3 Mar 2018 19:41:43 +0000 Subject: [PATCH 05/10] fixes, add to changelog --- CHANGELOG.md | 14 ++++++++++++++ client/builder/builder.go | 4 ++++ examples/basecoin/cmd/basecli/main.go | 9 +++++++++ examples/basecoin/x/cool/commands/cooltx.go | 4 ++-- x/bank/commands/sendtx.go | 8 ++------ 5 files changed, 31 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f71b5cc7..f2746b60a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/client/builder/builder.go b/client/builder/builder.go index 65c2ffe43..f36cc159d 100644 --- a/client/builder/builder.go +++ b/client/builder/builder.go @@ -75,6 +75,10 @@ func GetFromAddress() (from sdk.Address, err error) { } 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) diff --git a/examples/basecoin/cmd/basecli/main.go b/examples/basecoin/cmd/basecli/main.go index 638071d14..4a2cdd6f4 100644 --- a/examples/basecoin/cmd/basecli/main.go +++ b/examples/basecoin/cmd/basecli/main.go @@ -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( diff --git a/examples/basecoin/x/cool/commands/cooltx.go b/examples/basecoin/x/cool/commands/cooltx.go index e1f1b866f..c2a1acee9 100644 --- a/examples/basecoin/x/cool/commands/cooltx.go +++ b/examples/basecoin/x/cool/commands/cooltx.go @@ -12,7 +12,7 @@ import ( "github.com/cosmos/cosmos-sdk/examples/basecoin/x/cool" ) -// what cool trasaction +// what cool transaction func WhatCoolTxCmd(cdc *wire.Codec) *cobra.Command { return &cobra.Command{ Use: "whatcool [answer]", @@ -43,7 +43,7 @@ func WhatCoolTxCmd(cdc *wire.Codec) *cobra.Command { } } -// set what cool trasaction +// set what cool transaction func SetWhatCoolTxCmd(cdc *wire.Codec) *cobra.Command { return &cobra.Command{ Use: "setwhatcool [answer]", diff --git a/x/bank/commands/sendtx.go b/x/bank/commands/sendtx.go index e74361231..7e6dd463d 100644 --- a/x/bank/commands/sendtx.go +++ b/x/bank/commands/sendtx.go @@ -15,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 @@ -31,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 } From 9349d95cd92e91d372f5edabad57b5fd0ee82fde Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Sat, 3 Mar 2018 21:03:34 +0000 Subject: [PATCH 06/10] add some unmarshal panics --- examples/basecoin/types/account.go | 3 +++ x/auth/commands/account.go | 3 +++ 2 files changed, 6 insertions(+) diff --git a/examples/basecoin/types/account.go b/examples/basecoin/types/account.go index 2c54a5fbc..2e0b8397f 100644 --- a/examples/basecoin/types/account.go +++ b/examples/basecoin/types/account.go @@ -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 } } diff --git a/x/auth/commands/account.go b/x/auth/commands/account.go index a8bbfa226..2e2a3bf92 100644 --- a/x/auth/commands/account.go +++ b/x/auth/commands/account.go @@ -23,6 +23,9 @@ func getParseAccount(cdc *wire.Codec) sdk.ParseAccount { return func(accBytes []byte) (sdk.Account, error) { acct := new(auth.BaseAccount) err := cdc.UnmarshalBinary(accBytes, acct) + if err != nil { + panic(err) + } return acct, err } } From 85316e9388660c4b4ab995bfeec99fa7c9ba7ffe Mon Sep 17 00:00:00 2001 From: Adrian Brink Date: Sat, 3 Mar 2018 20:38:24 +0100 Subject: [PATCH 07/10] Explanation on how the module system works --- examples/basecoin/README.md | 68 +++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/examples/basecoin/README.md b/examples/basecoin/README.md index ed667535d..e6de9480d 100644 --- a/examples/basecoin/README.md +++ b/examples/basecoin/README.md @@ -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. + From b68500e9480bb7a6efde8c95fc2149d10c252d43 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Mon, 5 Mar 2018 00:45:31 +0000 Subject: [PATCH 08/10] ... --- x/auth/commands/account.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/auth/commands/account.go b/x/auth/commands/account.go index 2e2a3bf92..e9e1a5438 100644 --- a/x/auth/commands/account.go +++ b/x/auth/commands/account.go @@ -22,7 +22,7 @@ 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) } From 7b0a5fb043c2672e0048341c5a1c1baf839ede60 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Mon, 5 Mar 2018 00:55:23 +0000 Subject: [PATCH 09/10] add cool msg to codec --- examples/basecoin/app/app.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/examples/basecoin/app/app.go b/examples/basecoin/app/app.go index 4f1cb2fb0..d315f977e 100644 --- a/examples/basecoin/app/app.go +++ b/examples/basecoin/app/app.go @@ -81,10 +81,14 @@ func MakeCodec() *wire.Codec { 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 From b861b4692d3cd95847b889f5dc7bd82fe010bf60 Mon Sep 17 00:00:00 2001 From: rigelrozanski Date: Tue, 6 Mar 2018 00:56:03 +0000 Subject: [PATCH 10/10] wip app tests --- examples/basecoin/app/app_test.go | 197 +++++++++++++++++++----------- 1 file changed, 128 insertions(+), 69 deletions(-) diff --git a/examples/basecoin/app/app_test.go b/examples/basecoin/app/app_test.go index 0073d3dde..affa01415 100644 --- a/examples/basecoin/app/app_test.go +++ b/examples/basecoin/app/app_test.go @@ -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())) +//}