Split validation into basic/advanced
This commit is contained in:
parent
bc78a2d272
commit
ce2b8904d6
57
app/app.go
57
app/app.go
|
@ -16,13 +16,13 @@ const (
|
||||||
version = "0.1"
|
version = "0.1"
|
||||||
maxTxSize = 10240
|
maxTxSize = 10240
|
||||||
|
|
||||||
typeByteBase = 0x01
|
PluginTypeByteBase = 0x01
|
||||||
typeByteEyes = 0x02
|
PluginTypeByteEyes = 0x02
|
||||||
typeByteGov = 0x03
|
PluginTypeByteGov = 0x03
|
||||||
|
|
||||||
pluginNameBase = "base"
|
PluginNameBase = "base"
|
||||||
pluginNameEyes = "eyes"
|
PluginNameEyes = "eyes"
|
||||||
pluginNameGov = "gov"
|
PluginNameGov = "gov"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Basecoin struct {
|
type Basecoin struct {
|
||||||
|
@ -36,7 +36,7 @@ func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
|
||||||
govMint := gov.NewGovernmint(eyesCli)
|
govMint := gov.NewGovernmint(eyesCli)
|
||||||
state_ := state.NewState(eyesCli)
|
state_ := state.NewState(eyesCli)
|
||||||
plugins := types.NewPlugins()
|
plugins := types.NewPlugins()
|
||||||
plugins.RegisterPlugin(typeByteGov, pluginNameGov, govMint) // TODO: make constants
|
plugins.RegisterPlugin(PluginTypeByteGov, PluginNameGov, govMint)
|
||||||
return &Basecoin{
|
return &Basecoin{
|
||||||
eyesCli: eyesCli,
|
eyesCli: eyesCli,
|
||||||
govMint: govMint,
|
govMint: govMint,
|
||||||
|
@ -52,12 +52,12 @@ func (app *Basecoin) Info() string {
|
||||||
|
|
||||||
// TMSP::SetOption
|
// TMSP::SetOption
|
||||||
func (app *Basecoin) SetOption(key string, value string) (log string) {
|
func (app *Basecoin) SetOption(key string, value string) (log string) {
|
||||||
pluginName, key := splitKey(key)
|
PluginName, key := splitKey(key)
|
||||||
if pluginName != pluginNameBase {
|
if PluginName != PluginNameBase {
|
||||||
// Set option on plugin
|
// Set option on plugin
|
||||||
plugin := app.plugins.GetByName(pluginName)
|
plugin := app.plugins.GetByName(PluginName)
|
||||||
if plugin == nil {
|
if plugin == nil {
|
||||||
return "Invalid plugin name: " + pluginName
|
return "Invalid plugin name: " + PluginName
|
||||||
}
|
}
|
||||||
return plugin.SetOption(key, value)
|
return plugin.SetOption(key, value)
|
||||||
} else {
|
} else {
|
||||||
|
@ -126,11 +126,11 @@ func (app *Basecoin) Query(query []byte) (res tmsp.Result) {
|
||||||
typeByte := query[0]
|
typeByte := query[0]
|
||||||
query = query[1:]
|
query = query[1:]
|
||||||
switch typeByte {
|
switch typeByte {
|
||||||
case typeByteBase:
|
case PluginTypeByteBase:
|
||||||
return tmsp.OK.SetLog("This type of query not yet supported")
|
return tmsp.OK.SetLog("This type of query not yet supported")
|
||||||
case typeByteEyes:
|
case PluginTypeByteEyes:
|
||||||
return app.eyesCli.QuerySync(query)
|
return app.eyesCli.QuerySync(query)
|
||||||
case typeByteGov:
|
case PluginTypeByteGov:
|
||||||
return app.govMint.Query(query)
|
return app.govMint.Query(query)
|
||||||
}
|
}
|
||||||
return tmsp.ErrBaseUnknownPlugin.SetLog(
|
return tmsp.ErrBaseUnknownPlugin.SetLog(
|
||||||
|
@ -156,20 +156,35 @@ func (app *Basecoin) Commit() (res tmsp.Result) {
|
||||||
|
|
||||||
// TMSP::InitChain
|
// TMSP::InitChain
|
||||||
func (app *Basecoin) InitChain(validators []*tmsp.Validator) {
|
func (app *Basecoin) InitChain(validators []*tmsp.Validator) {
|
||||||
app.govMint.InitChain(validators)
|
for _, plugin := range app.plugins.GetList() {
|
||||||
|
if _, ok := plugin.Plugin.(tmsp.BlockchainAware); ok {
|
||||||
|
plugin.Plugin.(tmsp.BlockchainAware).InitChain(validators)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TMSP::BeginBlock
|
// TMSP::BeginBlock
|
||||||
func (app *Basecoin) BeginBlock(height uint64) {
|
func (app *Basecoin) BeginBlock(height uint64) {
|
||||||
// app.govMint.BeginBlock(height)
|
app.state.ResetCacheState()
|
||||||
// TODO other plugins?
|
for _, plugin := range app.plugins.GetList() {
|
||||||
|
if _, ok := plugin.Plugin.(tmsp.BlockchainAware); ok {
|
||||||
|
plugin.Plugin.(tmsp.BlockchainAware).BeginBlock(height)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TMSP::EndBlock
|
// TMSP::EndBlock
|
||||||
func (app *Basecoin) EndBlock(height uint64) []*tmsp.Validator {
|
func (app *Basecoin) EndBlock(height uint64) (vals []*tmsp.Validator) {
|
||||||
app.state.ResetCacheState()
|
for _, plugin := range app.plugins.GetList() {
|
||||||
return app.govMint.EndBlock(height)
|
if plugin.Plugin == app.govMint {
|
||||||
// TODO other plugins?
|
vals = plugin.Plugin.(tmsp.BlockchainAware).EndBlock(height)
|
||||||
|
} else {
|
||||||
|
if _, ok := plugin.Plugin.(tmsp.BlockchainAware); ok {
|
||||||
|
plugin.Plugin.(tmsp.BlockchainAware).EndBlock(height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
|
|
Binary file not shown.
|
@ -1,8 +1,6 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/types"
|
"github.com/tendermint/basecoin/types"
|
||||||
. "github.com/tendermint/go-common"
|
. "github.com/tendermint/go-common"
|
||||||
"github.com/tendermint/go-events"
|
"github.com/tendermint/go-events"
|
||||||
|
@ -28,28 +26,35 @@ func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc event
|
||||||
// Exec tx
|
// Exec tx
|
||||||
switch tx := tx.(type) {
|
switch tx := tx.(type) {
|
||||||
case *types.SendTx:
|
case *types.SendTx:
|
||||||
// First, get inputs
|
// Validate inputs and outputs, basic
|
||||||
|
res := validateInputsBasic(tx.Inputs)
|
||||||
|
if res.IsErr() {
|
||||||
|
return res.PrependLog("in validateInputsBasic()")
|
||||||
|
}
|
||||||
|
res = validateOutputsBasic(tx.Outputs)
|
||||||
|
if res.IsErr() {
|
||||||
|
return res.PrependLog("in validateOutputsBasic()")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get inputs
|
||||||
accounts, res := getInputs(state, tx.Inputs)
|
accounts, res := getInputs(state, tx.Inputs)
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
return res.PrependLog("in getInputs()")
|
return res.PrependLog("in getInputs()")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then, get or make outputs.
|
// Get or make outputs.
|
||||||
accounts, res = getOrMakeOutputs(state, accounts, tx.Outputs)
|
accounts, res = getOrMakeOutputs(state, accounts, tx.Outputs)
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
return res.PrependLog("in getOrMakeOutputs()")
|
return res.PrependLog("in getOrMakeOutputs()")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate inputs and outputs
|
// Validate inputs and outputs, advanced
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
inTotal, res := validateInputs(accounts, signBytes, tx.Inputs)
|
inTotal, res := validateInputsAdvanced(accounts, signBytes, tx.Inputs)
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
return res.PrependLog("in validateInputs()")
|
return res.PrependLog("in validateInputsAdvanced()")
|
||||||
}
|
|
||||||
outTotal, res := validateOutputs(tx.Outputs)
|
|
||||||
if res.IsErr() {
|
|
||||||
return res.PrependLog("in validateOutputs()")
|
|
||||||
}
|
}
|
||||||
|
outTotal := sumOutputs(tx.Outputs)
|
||||||
if !inTotal.IsEqual(outTotal.Plus(types.Coins{{"", tx.Fee}})) {
|
if !inTotal.IsEqual(outTotal.Plus(types.Coins{{"", tx.Fee}})) {
|
||||||
return tmsp.ErrBaseInvalidOutput.AppendLog("Input total != output total + fees")
|
return tmsp.ErrBaseInvalidOutput.AppendLog("Input total != output total + fees")
|
||||||
}
|
}
|
||||||
|
@ -78,23 +83,27 @@ func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc event
|
||||||
return tmsp.OK
|
return tmsp.OK
|
||||||
|
|
||||||
case *types.AppTx:
|
case *types.AppTx:
|
||||||
// First, get input account
|
// Validate input, basic
|
||||||
|
res := tx.Input.ValidateBasic()
|
||||||
|
if res.IsErr() {
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get input account
|
||||||
inAcc := state.GetAccount(tx.Input.Address)
|
inAcc := state.GetAccount(tx.Input.Address)
|
||||||
if inAcc == nil {
|
if inAcc == nil {
|
||||||
return tmsp.ErrBaseUnknownAddress
|
return tmsp.ErrBaseUnknownAddress
|
||||||
}
|
}
|
||||||
|
if tx.Input.PubKey != nil {
|
||||||
// Validate input
|
inAcc.PubKey = tx.Input.PubKey
|
||||||
// pubKey should be present in either "inAcc" or "tx.Input"
|
|
||||||
if res := checkInputPubKey(tx.Input.Address, inAcc, tx.Input); res.IsErr() {
|
|
||||||
log.Info(Fmt("Can't find pubkey for %X", tx.Input.Address))
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate input, advanced
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
res := validateInput(inAcc, signBytes, tx.Input)
|
res = validateInputAdvanced(inAcc, signBytes, tx.Input)
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
log.Info(Fmt("validateInput failed on %X: %v", tx.Input.Address, res))
|
log.Info(Fmt("validateInputAdvanced failed on %X: %v", tx.Input.Address, res))
|
||||||
return res.PrependLog("in validateInput()")
|
return res.PrependLog("in validateInputAdvanced()")
|
||||||
}
|
}
|
||||||
if !tx.Input.Coins.IsGTE(types.Coins{{"", tx.Fee}}) {
|
if !tx.Input.Coins.IsGTE(types.Coins{{"", tx.Fee}}) {
|
||||||
log.Info(Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address))
|
log.Info(Fmt("Sender did not send enough to cover the fee %X", tx.Input.Address))
|
||||||
|
@ -160,8 +169,7 @@ func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc event
|
||||||
|
|
||||||
// The accounts from the TxInputs must either already have
|
// The accounts from the TxInputs must either already have
|
||||||
// crypto.PubKey.(type) != nil, (it must be known),
|
// crypto.PubKey.(type) != nil, (it must be known),
|
||||||
// or it must be specified in the TxInput. If redeclared,
|
// or it must be specified in the TxInput.
|
||||||
// the TxInput is modified and input.PubKey set to nil.
|
|
||||||
func getInputs(state types.AccountGetter, ins []types.TxInput) (map[string]*types.Account, tmsp.Result) {
|
func getInputs(state types.AccountGetter, ins []types.TxInput) (map[string]*types.Account, tmsp.Result) {
|
||||||
accounts := map[string]*types.Account{}
|
accounts := map[string]*types.Account{}
|
||||||
for _, in := range ins {
|
for _, in := range ins {
|
||||||
|
@ -173,9 +181,8 @@ func getInputs(state types.AccountGetter, ins []types.TxInput) (map[string]*type
|
||||||
if acc == nil {
|
if acc == nil {
|
||||||
return nil, tmsp.ErrBaseUnknownAddress
|
return nil, tmsp.ErrBaseUnknownAddress
|
||||||
}
|
}
|
||||||
// PubKey should be present in either "account" or "in"
|
if in.PubKey != nil {
|
||||||
if res := checkInputPubKey(in.Address, acc, in); res.IsErr() {
|
acc.PubKey = in.PubKey
|
||||||
return nil, res
|
|
||||||
}
|
}
|
||||||
accounts[string(in.Address)] = acc
|
accounts[string(in.Address)] = acc
|
||||||
}
|
}
|
||||||
|
@ -205,37 +212,25 @@ func getOrMakeOutputs(state types.AccountGetter, accounts map[string]*types.Acco
|
||||||
return accounts, tmsp.OK
|
return accounts, tmsp.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
// Input must not have a redundant PubKey (i.e. Account already has PubKey).
|
// Validate inputs basic structure
|
||||||
// NOTE: Account has PubKey if Sequence > 0
|
func validateInputsBasic(ins []types.TxInput) (res tmsp.Result) {
|
||||||
func checkInputPubKey(address []byte, acc *types.Account, in types.TxInput) tmsp.Result {
|
for _, in := range ins {
|
||||||
if acc.PubKey == nil {
|
// Check TxInput basic
|
||||||
if in.PubKey == nil {
|
if res := in.ValidateBasic(); res.IsErr() {
|
||||||
return tmsp.ErrBaseUnknownPubKey.AppendLog("PubKey not present in either acc or input")
|
return res
|
||||||
}
|
|
||||||
if !bytes.Equal(in.PubKey.Address(), address) {
|
|
||||||
return tmsp.ErrBaseInvalidPubKey.AppendLog("Input PubKey address does not match address")
|
|
||||||
}
|
|
||||||
acc.PubKey = in.PubKey
|
|
||||||
} else {
|
|
||||||
if in.PubKey != nil {
|
|
||||||
// NOTE: allow redundant pubkey.
|
|
||||||
if !bytes.Equal(in.PubKey.Address(), address) {
|
|
||||||
return tmsp.ErrBaseInvalidPubKey.AppendLog("Input PubKey address does not match address")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tmsp.OK
|
return tmsp.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate inputs and compute total amount of coins
|
// Validate inputs and compute total amount of coins
|
||||||
func validateInputs(accounts map[string]*types.Account, signBytes []byte, ins []types.TxInput) (total types.Coins, res tmsp.Result) {
|
func validateInputsAdvanced(accounts map[string]*types.Account, signBytes []byte, ins []types.TxInput) (total types.Coins, res tmsp.Result) {
|
||||||
|
|
||||||
for _, in := range ins {
|
for _, in := range ins {
|
||||||
acc := accounts[string(in.Address)]
|
acc := accounts[string(in.Address)]
|
||||||
if acc == nil {
|
if acc == nil {
|
||||||
PanicSanity("validateInputs() expects account in accounts")
|
PanicSanity("validateInputsAdvanced() expects account in accounts")
|
||||||
}
|
}
|
||||||
res = validateInput(acc, signBytes, in)
|
res = validateInputAdvanced(acc, signBytes, in)
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -245,11 +240,7 @@ func validateInputs(accounts map[string]*types.Account, signBytes []byte, ins []
|
||||||
return total, tmsp.OK
|
return total, tmsp.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateInput(acc *types.Account, signBytes []byte, in types.TxInput) (res tmsp.Result) {
|
func validateInputAdvanced(acc *types.Account, signBytes []byte, in types.TxInput) (res tmsp.Result) {
|
||||||
// Check TxInput basic
|
|
||||||
if res := in.ValidateBasic(); res.IsErr() {
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
// Check sequence/coins
|
// Check sequence/coins
|
||||||
seq, balance := acc.Sequence, acc.Balance
|
seq, balance := acc.Sequence, acc.Balance
|
||||||
if seq+1 != in.Sequence {
|
if seq+1 != in.Sequence {
|
||||||
|
@ -266,16 +257,21 @@ func validateInput(acc *types.Account, signBytes []byte, in types.TxInput) (res
|
||||||
return tmsp.OK
|
return tmsp.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateOutputs(outs []types.TxOutput) (total types.Coins, res tmsp.Result) {
|
func validateOutputsBasic(outs []types.TxOutput) (res tmsp.Result) {
|
||||||
for _, out := range outs {
|
for _, out := range outs {
|
||||||
// Check TxOutput basic
|
// Check TxOutput basic
|
||||||
if res := out.ValidateBasic(); res.IsErr() {
|
if res := out.ValidateBasic(); res.IsErr() {
|
||||||
return nil, res
|
return res
|
||||||
}
|
}
|
||||||
// Good. Add amount to total
|
}
|
||||||
|
return tmsp.OK
|
||||||
|
}
|
||||||
|
|
||||||
|
func sumOutputs(outs []types.TxOutput) (total types.Coins) {
|
||||||
|
for _, out := range outs {
|
||||||
total = total.Plus(out.Coins)
|
total = total.Plus(out.Coins)
|
||||||
}
|
}
|
||||||
return total, tmsp.OK
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
func adjustByInputs(state types.AccountSetter, accounts map[string]*types.Account, ins []types.TxInput) {
|
func adjustByInputs(state types.AccountSetter, accounts map[string]*types.Account, ins []types.TxInput) {
|
||||||
|
|
|
@ -23,17 +23,18 @@ func main() {
|
||||||
|
|
||||||
func testSendTx() {
|
func testSendTx() {
|
||||||
eyesCli := eyescli.NewLocalClient()
|
eyesCli := eyescli.NewLocalClient()
|
||||||
|
chainID := "test_chain_id"
|
||||||
bcApp := app.NewBasecoin(eyesCli)
|
bcApp := app.NewBasecoin(eyesCli)
|
||||||
|
bcApp.SetOption("base/chainID", chainID)
|
||||||
fmt.Println(bcApp.Info())
|
fmt.Println(bcApp.Info())
|
||||||
|
|
||||||
tPriv := tests.PrivAccountFromSecret("test")
|
test1PrivAcc := tests.PrivAccountFromSecret("test1")
|
||||||
tPriv2 := tests.PrivAccountFromSecret("test2")
|
test2PrivAcc := tests.PrivAccountFromSecret("test2")
|
||||||
|
|
||||||
// Seed Basecoin with account
|
// Seed Basecoin with account
|
||||||
tAcc := tPriv.Account
|
test1Acc := test1PrivAcc.Account
|
||||||
tAcc.Balance = types.Coins{{"", 1000}}
|
test1Acc.Balance = types.Coins{{"", 1000}}
|
||||||
fmt.Println(bcApp.SetOption("base/chainID", "test_chain_id"))
|
fmt.Println(bcApp.SetOption("base/account", string(wire.JSONBytes(test1Acc))))
|
||||||
fmt.Println(bcApp.SetOption("base/account", string(wire.JSONBytes(tAcc))))
|
|
||||||
|
|
||||||
// Construct a SendTx signature
|
// Construct a SendTx signature
|
||||||
tx := &types.SendTx{
|
tx := &types.SendTx{
|
||||||
|
@ -41,24 +42,24 @@ func testSendTx() {
|
||||||
Gas: 0,
|
Gas: 0,
|
||||||
Inputs: []types.TxInput{
|
Inputs: []types.TxInput{
|
||||||
types.TxInput{
|
types.TxInput{
|
||||||
Address: tPriv.Account.PubKey.Address(),
|
Address: test1PrivAcc.Account.PubKey.Address(),
|
||||||
PubKey: tPriv.Account.PubKey, // TODO is this needed?
|
PubKey: test1PrivAcc.Account.PubKey, // TODO is this needed?
|
||||||
Coins: types.Coins{{"", 1}},
|
Coins: types.Coins{{"", 1}},
|
||||||
Sequence: 1,
|
Sequence: 1,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Outputs: []types.TxOutput{
|
Outputs: []types.TxOutput{
|
||||||
types.TxOutput{
|
types.TxOutput{
|
||||||
Address: tPriv2.Account.PubKey.Address(),
|
Address: test2PrivAcc.Account.PubKey.Address(),
|
||||||
Coins: types.Coins{{"", 1}},
|
Coins: types.Coins{{"", 1}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sign request
|
// Sign request
|
||||||
signBytes := tx.SignBytes("test_chain_id")
|
signBytes := tx.SignBytes(chainID)
|
||||||
fmt.Printf("Sign bytes: %X\n", signBytes)
|
fmt.Printf("Sign bytes: %X\n", signBytes)
|
||||||
sig := tPriv.PrivKey.Sign(signBytes)
|
sig := test1PrivAcc.PrivKey.Sign(signBytes)
|
||||||
tx.Inputs[0].Signature = sig
|
tx.Inputs[0].Signature = sig
|
||||||
//fmt.Println("tx:", tx)
|
//fmt.Println("tx:", tx)
|
||||||
fmt.Printf("Signed TX bytes: %X\n", wire.BinaryBytes(struct{ types.Tx }{tx}))
|
fmt.Printf("Signed TX bytes: %X\n", wire.BinaryBytes(struct{ types.Tx }{tx}))
|
||||||
|
@ -74,19 +75,21 @@ func testSendTx() {
|
||||||
|
|
||||||
func testGov() {
|
func testGov() {
|
||||||
eyesCli := eyescli.NewLocalClient()
|
eyesCli := eyescli.NewLocalClient()
|
||||||
|
chainID := "test_chain_id"
|
||||||
bcApp := app.NewBasecoin(eyesCli)
|
bcApp := app.NewBasecoin(eyesCli)
|
||||||
|
bcApp.SetOption("base/chainID", chainID)
|
||||||
fmt.Println(bcApp.Info())
|
fmt.Println(bcApp.Info())
|
||||||
|
|
||||||
tPriv := tests.PrivAccountFromSecret("test")
|
adminPrivAcc := tests.PrivAccountFromSecret("admin")
|
||||||
valPrivKey0 := crypto.GenPrivKeyEd25519FromSecret([]byte("val0"))
|
val0PrivKey := crypto.GenPrivKeyEd25519FromSecret([]byte("val0"))
|
||||||
valPrivKey1 := crypto.GenPrivKeyEd25519FromSecret([]byte("val1"))
|
val1PrivKey := crypto.GenPrivKeyEd25519FromSecret([]byte("val1"))
|
||||||
valPrivKey2 := crypto.GenPrivKeyEd25519FromSecret([]byte("val2"))
|
val2PrivKey := crypto.GenPrivKeyEd25519FromSecret([]byte("val2"))
|
||||||
|
|
||||||
// Seed Basecoin with admin using PrivAccount
|
// Seed Basecoin with admin using PrivAccount
|
||||||
tAcc := tPriv.Account
|
adminAcc := adminPrivAcc.Account
|
||||||
adminEntity := govtypes.Entity{
|
adminEntity := govtypes.Entity{
|
||||||
Addr: tAcc.PubKey.Address(),
|
Addr: adminAcc.PubKey.Address(),
|
||||||
PubKey: tAcc.PubKey,
|
PubKey: adminAcc.PubKey,
|
||||||
}
|
}
|
||||||
log := bcApp.SetOption("gov/admin", string(wire.JSONBytes(adminEntity)))
|
log := bcApp.SetOption("gov/admin", string(wire.JSONBytes(adminEntity)))
|
||||||
if log != "Success" {
|
if log != "Success" {
|
||||||
|
@ -95,15 +98,15 @@ func testGov() {
|
||||||
|
|
||||||
// Call InitChain to initialize the validator set
|
// Call InitChain to initialize the validator set
|
||||||
bcApp.InitChain([]*tmsp.Validator{
|
bcApp.InitChain([]*tmsp.Validator{
|
||||||
{PubKey: valPrivKey0.PubKey().Bytes(), Power: 1},
|
{PubKey: val0PrivKey.PubKey().Bytes(), Power: 1},
|
||||||
{PubKey: valPrivKey1.PubKey().Bytes(), Power: 1},
|
{PubKey: val1PrivKey.PubKey().Bytes(), Power: 1},
|
||||||
{PubKey: valPrivKey2.PubKey().Bytes(), Power: 1},
|
{PubKey: val2PrivKey.PubKey().Bytes(), Power: 1},
|
||||||
})
|
})
|
||||||
|
|
||||||
// Query for validator set
|
// Query for validator set
|
||||||
res := bcApp.Query(expr.MustCompile(`x02 x01 "gov/g/validators"`))
|
res := bcApp.Query(expr.MustCompile(`x02 x01 "gov/g/validators"`))
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
Exit(Fmt("Failed: %v", res.Error()))
|
Exit(Fmt("Failed to query validators: %v", res.Error()))
|
||||||
}
|
}
|
||||||
group := govtypes.Group{}
|
group := govtypes.Group{}
|
||||||
err := wire.ReadBinaryBytes(res.Data, &group)
|
err := wire.ReadBinaryBytes(res.Data, &group)
|
||||||
|
@ -111,22 +114,60 @@ func testGov() {
|
||||||
Exit(Fmt("Unexpected query response bytes: %X error: %v",
|
Exit(Fmt("Unexpected query response bytes: %X error: %v",
|
||||||
res.Data, err))
|
res.Data, err))
|
||||||
}
|
}
|
||||||
fmt.Println(">>", group)
|
// fmt.Println("Initialized gov/g/validators", group)
|
||||||
|
|
||||||
|
// Mutate the validator set.
|
||||||
|
proposal := govtypes.Proposal{
|
||||||
|
ID: "my_proposal_id",
|
||||||
|
VoteGroupID: "gov/admin",
|
||||||
|
StartHeight: 0,
|
||||||
|
EndHeight: 0,
|
||||||
|
Info: &govtypes.GroupUpdateProposalInfo{
|
||||||
|
UpdateGroupID: "gov/g/validators",
|
||||||
|
NextVersion: 0,
|
||||||
|
ChangedMembers: []govtypes.Member{
|
||||||
|
{nil, 1}, // TODO Fill this out.
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
proposalTx := &govtypes.ProposalTx{
|
||||||
|
EntityAddr: adminEntity.Addr,
|
||||||
|
Proposal: proposal,
|
||||||
|
}
|
||||||
|
proposalTx.SetSignature(nil, adminPrivAcc.Sign(proposalTx.SignBytes()))
|
||||||
|
tx := &types.AppTx{
|
||||||
|
Fee: 1,
|
||||||
|
Gas: 1,
|
||||||
|
Type: app.PluginTypeByteGov, // XXX Remove typebytes?
|
||||||
|
Input: types.TxInput{
|
||||||
|
Address: adminEntity.Addr,
|
||||||
|
Coins: types.Coins{{"", 1}},
|
||||||
|
Sequence: 1,
|
||||||
|
},
|
||||||
|
Data: nil,
|
||||||
|
}
|
||||||
|
tx.SetSignature(nil, adminPrivAcc.Sign(tx.SignBytes(chainID)))
|
||||||
|
res = bcApp.AppendTx(wire.BinaryBytes(struct{ types.Tx }{tx}))
|
||||||
|
if res.IsErr() {
|
||||||
|
Exit(Fmt("Failed to mutate validators: %v", res.Error()))
|
||||||
|
}
|
||||||
|
fmt.Println(res)
|
||||||
|
|
||||||
// TODO more tests...
|
// TODO more tests...
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSequence() {
|
func testSequence() {
|
||||||
eyesCli := eyescli.NewLocalClient()
|
eyesCli := eyescli.NewLocalClient()
|
||||||
bcApp := app.NewBasecoin(eyesCli)
|
|
||||||
chainID := "test_chain_id"
|
chainID := "test_chain_id"
|
||||||
|
bcApp := app.NewBasecoin(eyesCli)
|
||||||
|
bcApp.SetOption("base/chainID", chainID)
|
||||||
|
fmt.Println(bcApp.Info())
|
||||||
|
|
||||||
// Get the root account
|
// Get the test account
|
||||||
root := tests.PrivAccountFromSecret("test")
|
test1PrivAcc := tests.PrivAccountFromSecret("test1")
|
||||||
rootAcc := root.Account
|
test1Acc := test1PrivAcc.Account
|
||||||
rootAcc.Balance = types.Coins{{"", 1 << 53}}
|
test1Acc.Balance = types.Coins{{"", 1 << 53}}
|
||||||
fmt.Println(bcApp.SetOption("base/chainID", "test_chain_id"))
|
fmt.Println(bcApp.SetOption("base/account", string(wire.JSONBytes(test1Acc))))
|
||||||
fmt.Println(bcApp.SetOption("base/account", string(wire.JSONBytes(rootAcc))))
|
|
||||||
|
|
||||||
sequence := int(1)
|
sequence := int(1)
|
||||||
// Make a bunch of PrivAccounts
|
// Make a bunch of PrivAccounts
|
||||||
|
@ -141,8 +182,8 @@ func testSequence() {
|
||||||
Gas: 2,
|
Gas: 2,
|
||||||
Inputs: []types.TxInput{
|
Inputs: []types.TxInput{
|
||||||
types.TxInput{
|
types.TxInput{
|
||||||
Address: root.Account.PubKey.Address(),
|
Address: test1Acc.PubKey.Address(),
|
||||||
PubKey: root.Account.PubKey, // TODO is this needed?
|
PubKey: test1Acc.PubKey, // TODO is this needed?
|
||||||
Coins: types.Coins{{"", 1000002}},
|
Coins: types.Coins{{"", 1000002}},
|
||||||
Sequence: sequence,
|
Sequence: sequence,
|
||||||
},
|
},
|
||||||
|
@ -158,7 +199,7 @@ func testSequence() {
|
||||||
|
|
||||||
// Sign request
|
// Sign request
|
||||||
signBytes := tx.SignBytes(chainID)
|
signBytes := tx.SignBytes(chainID)
|
||||||
sig := root.PrivKey.Sign(signBytes)
|
sig := test1PrivAcc.PrivKey.Sign(signBytes)
|
||||||
tx.Inputs[0].Signature = sig
|
tx.Inputs[0].Signature = sig
|
||||||
// fmt.Printf("ADDR: %X -> %X\n", tx.Inputs[0].Address, tx.Outputs[0].Address)
|
// fmt.Printf("ADDR: %X -> %X\n", tx.Inputs[0].Address, tx.Outputs[0].Address)
|
||||||
|
|
||||||
|
|
27
types/tx.go
27
types/tx.go
|
@ -45,7 +45,7 @@ type TxInput struct {
|
||||||
Coins Coins `json:"coins"` //
|
Coins Coins `json:"coins"` //
|
||||||
Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput
|
Sequence int `json:"sequence"` // Must be 1 greater than the last committed TxInput
|
||||||
Signature crypto.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx
|
Signature crypto.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx
|
||||||
PubKey crypto.PubKey `json:"pub_key"` // May be nil
|
PubKey crypto.PubKey `json:"pub_key"` // Is present iff Sequence == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (txIn TxInput) ValidateBasic() tmsp.Result {
|
func (txIn TxInput) ValidateBasic() tmsp.Result {
|
||||||
|
@ -58,6 +58,15 @@ func (txIn TxInput) ValidateBasic() tmsp.Result {
|
||||||
if txIn.Coins.IsZero() {
|
if txIn.Coins.IsZero() {
|
||||||
return tmsp.ErrBaseInvalidInput.AppendLog("Coins cannot be zero")
|
return tmsp.ErrBaseInvalidInput.AppendLog("Coins cannot be zero")
|
||||||
}
|
}
|
||||||
|
if txIn.Sequence <= 0 {
|
||||||
|
return tmsp.ErrBaseInvalidInput.AppendLog("Sequence must be greater than 0")
|
||||||
|
}
|
||||||
|
if txIn.Sequence == 1 && txIn.PubKey == nil {
|
||||||
|
return tmsp.ErrBaseInvalidInput.AppendLog("PubKey must be present when Sequence == 1")
|
||||||
|
}
|
||||||
|
if txIn.Sequence > 1 && txIn.PubKey != nil {
|
||||||
|
return tmsp.ErrBaseInvalidInput.AppendLog("PubKey must be nil when Sequence > 1")
|
||||||
|
}
|
||||||
return tmsp.OK
|
return tmsp.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +121,16 @@ func (tx *SendTx) SignBytes(chainID string) []byte {
|
||||||
return signBytes
|
return signBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *SendTx) SetSignature(pubKey crypto.PubKey, sig crypto.Signature) bool {
|
||||||
|
for i, input := range tx.Inputs {
|
||||||
|
if input.PubKey.Equals(pubKey) {
|
||||||
|
tx.Inputs[i].Signature = sig
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (tx *SendTx) String() string {
|
func (tx *SendTx) String() string {
|
||||||
return Fmt("SendTx{%v/%v %v->%v}", tx.Fee, tx.Gas, tx.Inputs, tx.Outputs)
|
return Fmt("SendTx{%v/%v %v->%v}", tx.Fee, tx.Gas, tx.Inputs, tx.Outputs)
|
||||||
}
|
}
|
||||||
|
@ -135,6 +154,12 @@ func (tx *AppTx) SignBytes(chainID string) []byte {
|
||||||
return signBytes
|
return signBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *AppTx) SetSignature(pubKey crypto.PubKey, sig crypto.Signature) bool {
|
||||||
|
// TODO
|
||||||
|
tx.Input.Signature = sig
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func (tx *AppTx) String() string {
|
func (tx *AppTx) String() string {
|
||||||
return Fmt("AppTx{%v/%v %v %v %X}", tx.Fee, tx.Gas, tx.Type, tx.Input, tx.Data)
|
return Fmt("AppTx{%v/%v %v %v %X}", tx.Fee, tx.Gas, tx.Type, tx.Input, tx.Data)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue