s/CallTx/AppTx/g; NamedPlugins
This commit is contained in:
parent
601a654b7d
commit
fa39c9da5c
38
app/app.go
38
app/app.go
|
@ -12,23 +12,34 @@ import (
|
||||||
tmsp "github.com/tendermint/tmsp/types"
|
tmsp "github.com/tendermint/tmsp/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const version = "0.1"
|
const (
|
||||||
const maxTxSize = 10240
|
version = "0.1"
|
||||||
|
maxTxSize = 10240
|
||||||
|
|
||||||
|
typeByteBase = 0x01
|
||||||
|
typeByteGov = 0x02
|
||||||
|
|
||||||
|
pluginNameBase = "base"
|
||||||
|
pluginNameGov = "gov"
|
||||||
|
)
|
||||||
|
|
||||||
type Basecoin struct {
|
type Basecoin struct {
|
||||||
eyesCli *eyes.Client
|
eyesCli *eyes.Client
|
||||||
govMint *gov.Governmint
|
govMint *gov.Governmint
|
||||||
state *state.State
|
state *state.State
|
||||||
|
plugins *types.Plugins
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
|
func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
|
||||||
state_ := state.NewState(eyesCli)
|
|
||||||
govMint := gov.NewGovernmint(eyesCli)
|
govMint := gov.NewGovernmint(eyesCli)
|
||||||
state_.RegisterPlugin("GOV", govMint)
|
state_ := state.NewState(eyesCli)
|
||||||
|
plugins := types.NewPlugins()
|
||||||
|
plugins.RegisterPlugin(typeByteGov, pluginNameGov, govMint) // TODO: make constants
|
||||||
return &Basecoin{
|
return &Basecoin{
|
||||||
eyesCli: eyesCli,
|
eyesCli: eyesCli,
|
||||||
govMint: govMint,
|
govMint: govMint,
|
||||||
state: state_,
|
state: state_,
|
||||||
|
plugins: plugins,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,11 +50,10 @@ 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 != "BASE" {
|
if pluginName != pluginNameBase {
|
||||||
// Set option on plugin
|
// Set option on plugin
|
||||||
plugin := app.state.GetPlugin(pluginName)
|
plugin := app.plugins.GetByName(pluginName)
|
||||||
if plugin == nil {
|
if plugin == nil {
|
||||||
return "Invalid plugin name: " + pluginName
|
return "Invalid plugin name: " + pluginName
|
||||||
}
|
}
|
||||||
|
@ -84,7 +94,7 @@ func (app *Basecoin) AppendTx(txBytes []byte) (res tmsp.Result) {
|
||||||
return tmsp.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
|
return tmsp.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
|
||||||
}
|
}
|
||||||
// Validate and exec tx
|
// Validate and exec tx
|
||||||
res = state.ExecTx(app.state, tx, false, nil)
|
res = state.ExecTx(app.state, app.plugins, tx, false, nil)
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
return res.PrependLog("Error in AppendTx")
|
return res.PrependLog("Error in AppendTx")
|
||||||
}
|
}
|
||||||
|
@ -103,7 +113,7 @@ func (app *Basecoin) CheckTx(txBytes []byte) (res tmsp.Result) {
|
||||||
return tmsp.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
|
return tmsp.ErrBaseEncodingError.AppendLog("Error decoding tx: " + err.Error())
|
||||||
}
|
}
|
||||||
// Validate tx
|
// Validate tx
|
||||||
res = state.ExecTx(app.state, tx, true, nil)
|
res = state.ExecTx(app.state, app.plugins, tx, true, nil)
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
return res.PrependLog("Error in CheckTx")
|
return res.PrependLog("Error in CheckTx")
|
||||||
}
|
}
|
||||||
|
@ -113,8 +123,8 @@ func (app *Basecoin) CheckTx(txBytes []byte) (res tmsp.Result) {
|
||||||
// TMSP::Query
|
// TMSP::Query
|
||||||
func (app *Basecoin) Query(query []byte) (res tmsp.Result) {
|
func (app *Basecoin) Query(query []byte) (res tmsp.Result) {
|
||||||
pluginName, queryStr := splitKey(string(query))
|
pluginName, queryStr := splitKey(string(query))
|
||||||
if pluginName != "BASE" {
|
if pluginName != pluginNameBase {
|
||||||
plugin := app.state.GetPlugin(pluginName)
|
plugin := app.plugins.GetByName(pluginName)
|
||||||
if plugin == nil {
|
if plugin == nil {
|
||||||
return tmsp.ErrBaseUnknownPlugin.SetLog(Fmt("Unknown plugin %v", pluginName))
|
return tmsp.ErrBaseUnknownPlugin.SetLog(Fmt("Unknown plugin %v", pluginName))
|
||||||
}
|
}
|
||||||
|
@ -132,7 +142,7 @@ func (app *Basecoin) Query(query []byte) (res tmsp.Result) {
|
||||||
// TMSP::Commit
|
// TMSP::Commit
|
||||||
func (app *Basecoin) Commit() (res tmsp.Result) {
|
func (app *Basecoin) Commit() (res tmsp.Result) {
|
||||||
// First, commit all the plugins
|
// First, commit all the plugins
|
||||||
for _, plugin := range app.state.GetPlugins() {
|
for _, plugin := range app.plugins.GetList() {
|
||||||
res = plugin.Commit()
|
res = plugin.Commit()
|
||||||
if res.IsErr() {
|
if res.IsErr() {
|
||||||
PanicSanity(Fmt("Error committing plugin %v", plugin.Name))
|
PanicSanity(Fmt("Error committing plugin %v", plugin.Name))
|
||||||
|
@ -162,8 +172,8 @@ func (app *Basecoin) EndBlock(height uint64) []*tmsp.Validator {
|
||||||
// Splits the string at the first :.
|
// Splits the string at the first :.
|
||||||
// if there are none, the second string is nil.
|
// if there are none, the second string is nil.
|
||||||
func splitKey(key string) (prefix string, sufix string) {
|
func splitKey(key string) (prefix string, sufix string) {
|
||||||
if strings.Contains(key, ":") {
|
if strings.Contains(key, "/") {
|
||||||
keyParts := strings.SplitN(key, ":", 2)
|
keyParts := strings.SplitN(key, "/", 2)
|
||||||
return keyParts[0], keyParts[1]
|
return keyParts[0], keyParts[1]
|
||||||
}
|
}
|
||||||
return key, ""
|
return key, ""
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/tendermint/basecoin/app"
|
"github.com/tendermint/basecoin/app"
|
||||||
. "github.com/tendermint/go-common"
|
. "github.com/tendermint/go-common"
|
||||||
|
@ -58,14 +59,36 @@ type KeyValue struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadGenesis(filePath string) (kvz []KeyValue) {
|
func loadGenesis(filePath string) (kvz []KeyValue) {
|
||||||
|
kvz_ := []interface{}{}
|
||||||
bytes, err := ReadFile(filePath)
|
bytes, err := ReadFile(filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit("loading genesis file: " + err.Error())
|
Exit("loading genesis file: " + err.Error())
|
||||||
}
|
}
|
||||||
fmt.Println(">>", string(bytes))
|
err = json.Unmarshal(bytes, &kvz_)
|
||||||
err = json.Unmarshal(bytes, &kvz)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Exit("parsing genesis file: " + err.Error())
|
Exit("parsing genesis file: " + err.Error())
|
||||||
}
|
}
|
||||||
return
|
if len(kvz_)%2 != 0 {
|
||||||
|
Exit("genesis cannot have an odd number of items. Format = [key1, value1, key2, value2, ...]")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(kvz_)/2; i++ {
|
||||||
|
keyIfc := kvz_[i]
|
||||||
|
valueIfc := kvz_[i+1]
|
||||||
|
var key, value string
|
||||||
|
key, ok := keyIfc.(string)
|
||||||
|
if !ok {
|
||||||
|
Exit(Fmt("genesis had invalid key %v of type %v", keyIfc, reflect.TypeOf(keyIfc)))
|
||||||
|
}
|
||||||
|
if value_, ok := valueIfc.(string); ok {
|
||||||
|
value = value_
|
||||||
|
} else {
|
||||||
|
valueBytes, err := json.Marshal(value_)
|
||||||
|
if err != nil {
|
||||||
|
Exit(Fmt("genesis had invalid value %v: %v", value_, err.Error()))
|
||||||
|
}
|
||||||
|
value = string(valueBytes)
|
||||||
|
}
|
||||||
|
kvz = append(kvz, KeyValue{key, value})
|
||||||
|
}
|
||||||
|
return kvz
|
||||||
}
|
}
|
||||||
|
|
11
genesis.json
11
genesis.json
|
@ -1,10 +1,7 @@
|
||||||
[
|
[
|
||||||
{
|
"base/chainID", "test_chain_id",
|
||||||
"key": "chainID",
|
"base/account", {
|
||||||
"value": "test_chain_id"
|
"pub_key", [1, "67D3B5EAF0C0BF6B5A602D359DAECC86A7A74053490EC37AE08E71360587C870"],
|
||||||
},
|
"balance": 1000
|
||||||
{
|
|
||||||
"key": "account",
|
|
||||||
"value": "{\"pub_key\":[1,\"3E8A80E5412FD1711995D5888F5FA2EFB26EDD0970F1E27CE0B55CD237439E18\"],\"balance\":1000}"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -10,7 +10,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// If the tx is invalid, a TMSP error will be returned.
|
// If the tx is invalid, a TMSP error will be returned.
|
||||||
func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp.Result {
|
func ExecTx(state *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp.Result {
|
||||||
|
|
||||||
// TODO: do something with fees
|
// TODO: do something with fees
|
||||||
fees := int64(0)
|
fees := int64(0)
|
||||||
|
@ -68,7 +68,7 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
|
||||||
|
|
||||||
return tmsp.OK
|
return tmsp.OK
|
||||||
|
|
||||||
case *types.CallTx:
|
case *types.AppTx:
|
||||||
// First, get input account
|
// First, get input account
|
||||||
inAcc := state.GetAccount(tx.Input.Address)
|
inAcc := state.GetAccount(tx.Input.Address)
|
||||||
if inAcc == nil {
|
if inAcc == nil {
|
||||||
|
@ -93,9 +93,10 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate call address
|
// Validate call address
|
||||||
plugin := state.GetPlugin(string(tx.Address))
|
plugin := pgz.GetByByte(tx.Type)
|
||||||
if plugin != nil {
|
if plugin != nil {
|
||||||
return tmsp.ErrBaseUnknownAddress.AppendLog(Fmt("Unrecognized address %X (%v)", tx.Address, string(tx.Address)))
|
return tmsp.ErrBaseUnknownAddress.AppendLog(
|
||||||
|
Fmt("Unrecognized type byte %v", tx.Type))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Good!
|
// Good!
|
||||||
|
@ -105,7 +106,7 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
|
||||||
state.SetCheckAccount(tx.Input.Address, inAcc.Sequence, inAcc.Balance)
|
state.SetCheckAccount(tx.Input.Address, inAcc.Sequence, inAcc.Balance)
|
||||||
inAccCopy := inAcc.Copy()
|
inAccCopy := inAcc.Copy()
|
||||||
|
|
||||||
// If this is AppendTx, actually save accounts
|
// If this is a CheckTx, stop now.
|
||||||
if isCheckTx {
|
if isCheckTx {
|
||||||
return tmsp.OK
|
return tmsp.OK
|
||||||
}
|
}
|
||||||
|
@ -115,7 +116,7 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
|
||||||
cache.SetAccount(tx.Input.Address, inAcc)
|
cache.SetAccount(tx.Input.Address, inAcc)
|
||||||
gas := int64(1) // TODO
|
gas := int64(1) // TODO
|
||||||
ctx := types.NewCallContext(cache, inAcc, value, &gas)
|
ctx := types.NewCallContext(cache, inAcc, value, &gas)
|
||||||
res = plugin.CallTx(ctx, tx.Data)
|
res = plugin.RunTx(ctx, tx.Data)
|
||||||
if res.IsOK() {
|
if res.IsOK() {
|
||||||
cache.Sync()
|
cache.Sync()
|
||||||
log.Info("Successful execution")
|
log.Info("Successful execution")
|
||||||
|
@ -131,7 +132,7 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
log.Info("CallTx failed", "error", res)
|
log.Info("AppTx failed", "error", res)
|
||||||
// Just return the value and return.
|
// Just return the value and return.
|
||||||
// TODO: return gas?
|
// TODO: return gas?
|
||||||
inAccCopy.Balance += value
|
inAccCopy.Balance += value
|
||||||
|
|
|
@ -8,11 +8,9 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type State struct {
|
type State struct {
|
||||||
chainID string
|
chainID string
|
||||||
eyesCli *eyes.Client
|
eyesCli *eyes.Client
|
||||||
checkCache map[string]checkAccount
|
checkCache map[string]checkAccount
|
||||||
plugins map[string]types.Plugin
|
|
||||||
pluginsList []types.NamedPlugin
|
|
||||||
|
|
||||||
LastBlockHeight uint64
|
LastBlockHeight uint64
|
||||||
LastBlockHash []byte
|
LastBlockHash []byte
|
||||||
|
@ -24,7 +22,6 @@ func NewState(eyesCli *eyes.Client) *State {
|
||||||
chainID: "",
|
chainID: "",
|
||||||
eyesCli: eyesCli,
|
eyesCli: eyesCli,
|
||||||
checkCache: make(map[string]checkAccount),
|
checkCache: make(map[string]checkAccount),
|
||||||
plugins: make(map[string]types.Plugin),
|
|
||||||
}
|
}
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
@ -40,22 +37,6 @@ func (s *State) GetChainID() string {
|
||||||
return s.chainID
|
return s.chainID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) RegisterPlugin(name string, plugin types.Plugin) {
|
|
||||||
s.plugins[name] = plugin
|
|
||||||
s.pluginsList = append(s.pluginsList, types.NamedPlugin{
|
|
||||||
Name: name,
|
|
||||||
Plugin: plugin,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) GetPlugin(name string) types.Plugin {
|
|
||||||
return s.plugins[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) GetPlugins() []types.NamedPlugin {
|
|
||||||
return s.pluginsList
|
|
||||||
}
|
|
||||||
|
|
||||||
//----------------------------------------
|
//----------------------------------------
|
||||||
// CheckTx state
|
// CheckTx state
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
//testSendTx()
|
testSendTx()
|
||||||
testGov()
|
testGov()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ func testSendTx() {
|
||||||
// Seed Basecoin with account
|
// Seed Basecoin with account
|
||||||
tAcc := tPriv.Account
|
tAcc := tPriv.Account
|
||||||
tAcc.Balance = 1000
|
tAcc.Balance = 1000
|
||||||
bcApp.SetOption("chainID", "test_chain_id")
|
fmt.Println(bcApp.SetOption("base/chainID", "test_chain_id"))
|
||||||
bcApp.SetOption("account", string(wire.JSONBytes(tAcc)))
|
fmt.Println(bcApp.SetOption("base/account", string(wire.JSONBytes(tAcc))))
|
||||||
|
|
||||||
// Construct a SendTx signature
|
// Construct a SendTx signature
|
||||||
tx := &types.SendTx{
|
tx := &types.SendTx{
|
||||||
|
@ -51,10 +51,11 @@ func testSendTx() {
|
||||||
|
|
||||||
// Sign request
|
// Sign request
|
||||||
signBytes := tx.SignBytes("test_chain_id")
|
signBytes := tx.SignBytes("test_chain_id")
|
||||||
fmt.Printf("SIGNBYTES %X", signBytes)
|
fmt.Printf("Sign bytes: %X\n", signBytes)
|
||||||
sig := tPriv.PrivKey.Sign(signBytes)
|
sig := tPriv.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(tx))
|
||||||
|
|
||||||
// Write request
|
// Write request
|
||||||
txBytes := wire.BinaryBytes(tx)
|
txBytes := wire.BinaryBytes(tx)
|
||||||
|
@ -78,7 +79,7 @@ func testGov() {
|
||||||
ID: "",
|
ID: "",
|
||||||
PubKey: tAcc.PubKey,
|
PubKey: tAcc.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" {
|
||||||
Exit(Fmt("Failed to set option: %v", log))
|
Exit(Fmt("Failed to set option: %v", log))
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,16 +9,19 @@ import (
|
||||||
// if any gas is left the user is
|
// if any gas is left the user is
|
||||||
type Plugin interface {
|
type Plugin interface {
|
||||||
SetOption(key string, value string) (log string)
|
SetOption(key string, value string) (log string)
|
||||||
CallTx(ctx CallContext, txBytes []byte) (res tmsp.Result)
|
RunTx(ctx CallContext, txBytes []byte) (res tmsp.Result)
|
||||||
Query(query []byte) (res tmsp.Result)
|
Query(query []byte) (res tmsp.Result)
|
||||||
Commit() (res tmsp.Result)
|
Commit() (res tmsp.Result)
|
||||||
}
|
}
|
||||||
|
|
||||||
type NamedPlugin struct {
|
type NamedPlugin struct {
|
||||||
|
Byte byte
|
||||||
Name string
|
Name string
|
||||||
Plugin
|
Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------
|
||||||
|
|
||||||
type CallContext struct {
|
type CallContext struct {
|
||||||
Cache AccountCacher
|
Cache AccountCacher
|
||||||
Caller *Account
|
Caller *Account
|
||||||
|
@ -34,3 +37,40 @@ func NewCallContext(cache AccountCacher, caller *Account, value int64, gas *int6
|
||||||
Gas: gas,
|
Gas: gas,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//----------------------------------------
|
||||||
|
|
||||||
|
type Plugins struct {
|
||||||
|
byByte map[byte]Plugin
|
||||||
|
byName map[string]Plugin
|
||||||
|
plist []NamedPlugin
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPlugins() *Plugins {
|
||||||
|
return &Plugins{
|
||||||
|
byByte: make(map[byte]Plugin),
|
||||||
|
byName: make(map[string]Plugin),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pgz *Plugins) RegisterPlugin(typeByte byte, name string, plugin Plugin) {
|
||||||
|
pgz.byByte[typeByte] = plugin
|
||||||
|
pgz.byName[name] = plugin
|
||||||
|
pgz.plist = append(pgz.plist, NamedPlugin{
|
||||||
|
Byte: typeByte,
|
||||||
|
Name: name,
|
||||||
|
Plugin: plugin,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pgz *Plugins) GetByByte(typeByte byte) Plugin {
|
||||||
|
return pgz.byByte[typeByte]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pgz *Plugins) GetByName(name string) Plugin {
|
||||||
|
return pgz.byName[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pgz *Plugins) GetList() []NamedPlugin {
|
||||||
|
return pgz.plist
|
||||||
|
}
|
||||||
|
|
83
types/tx.go
83
types/tx.go
|
@ -1,14 +1,12 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
. "github.com/tendermint/go-common"
|
. "github.com/tendermint/go-common"
|
||||||
"github.com/tendermint/go-crypto"
|
"github.com/tendermint/go-crypto"
|
||||||
"github.com/tendermint/go-wire"
|
"github.com/tendermint/go-wire"
|
||||||
tmsp "github.com/tendermint/tmsp/types"
|
tmsp "github.com/tendermint/tmsp/types"
|
||||||
"golang.org/x/crypto/ripemd160"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -16,7 +14,7 @@ Tx (Transaction) is an atomic operation on the ledger state.
|
||||||
|
|
||||||
Account Types:
|
Account Types:
|
||||||
- SendTx Send coins to address
|
- SendTx Send coins to address
|
||||||
- CallTx Send a msg to a contract that runs in the vm
|
- AppTx Send a msg to a contract that runs in the vm
|
||||||
*/
|
*/
|
||||||
|
|
||||||
type Tx interface {
|
type Tx interface {
|
||||||
|
@ -27,13 +25,13 @@ type Tx interface {
|
||||||
const (
|
const (
|
||||||
// Account transactions
|
// Account transactions
|
||||||
TxTypeSend = byte(0x01)
|
TxTypeSend = byte(0x01)
|
||||||
TxTypeCall = byte(0x02)
|
TxTypeApp = byte(0x02)
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = wire.RegisterInterface(
|
var _ = wire.RegisterInterface(
|
||||||
struct{ Tx }{},
|
struct{ Tx }{},
|
||||||
wire.ConcreteType{&SendTx{}, TxTypeSend},
|
wire.ConcreteType{&SendTx{}, TxTypeSend},
|
||||||
wire.ConcreteType{&CallTx{}, TxTypeCall},
|
wire.ConcreteType{&AppTx{}, TxTypeApp},
|
||||||
)
|
)
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
@ -56,11 +54,6 @@ func (txIn TxInput) ValidateBasic() tmsp.Result {
|
||||||
return tmsp.OK
|
return tmsp.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
func (txIn TxInput) SignBytes() []byte {
|
|
||||||
return []byte(Fmt(`{"address":"%X","amount":%v,"sequence":%v}`,
|
|
||||||
txIn.Address, txIn.Amount, txIn.Sequence))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (txIn TxInput) String() string {
|
func (txIn TxInput) String() string {
|
||||||
return Fmt("TxInput{%X,%v,%v,%v,%v}", txIn.Address, txIn.Amount, txIn.Sequence, txIn.Signature, txIn.PubKey)
|
return Fmt("TxInput{%X,%v,%v,%v,%v}", txIn.Address, txIn.Amount, txIn.Sequence, txIn.Signature, txIn.PubKey)
|
||||||
}
|
}
|
||||||
|
@ -82,11 +75,6 @@ func (txOut TxOutput) ValidateBasic() tmsp.Result {
|
||||||
return tmsp.OK
|
return tmsp.OK
|
||||||
}
|
}
|
||||||
|
|
||||||
func (txOut TxOutput) SignBytes() []byte {
|
|
||||||
return []byte(Fmt(`{"address":"%X","amount":%v}`,
|
|
||||||
txOut.Address, txOut.Amount))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (txOut TxOutput) String() string {
|
func (txOut TxOutput) String() string {
|
||||||
return Fmt("TxOutput{%X,%v}", txOut.Address, txOut.Amount)
|
return Fmt("TxOutput{%X,%v}", txOut.Address, txOut.Amount)
|
||||||
}
|
}
|
||||||
|
@ -99,24 +87,17 @@ type SendTx struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *SendTx) SignBytes(chainID string) []byte {
|
func (tx *SendTx) SignBytes(chainID string) []byte {
|
||||||
var buf = new(bytes.Buffer)
|
signBytes := wire.BinaryBytes(chainID)
|
||||||
buf.Write([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))))
|
sigz := make([]crypto.Signature, len(tx.Inputs))
|
||||||
buf.Write([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeSend)))
|
for i, input := range tx.Inputs {
|
||||||
for i, in := range tx.Inputs {
|
sigz[i] = input.Signature
|
||||||
buf.Write(in.SignBytes())
|
tx.Inputs[i].Signature = nil
|
||||||
if i != len(tx.Inputs)-1 {
|
|
||||||
buf.Write([]byte(","))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
buf.Write([]byte(`],"outputs":[`))
|
signBytes = append(signBytes, wire.BinaryBytes(tx)...)
|
||||||
for i, out := range tx.Outputs {
|
for i := range tx.Inputs {
|
||||||
buf.Write(out.SignBytes())
|
tx.Inputs[i].Signature = sigz[i]
|
||||||
if i != len(tx.Outputs)-1 {
|
|
||||||
buf.Write([]byte(","))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
buf.Write([]byte(`]}]}`))
|
return signBytes
|
||||||
return buf.Bytes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *SendTx) String() string {
|
func (tx *SendTx) String() string {
|
||||||
|
@ -125,35 +106,25 @@ func (tx *SendTx) String() string {
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
type CallTx struct {
|
type AppTx struct {
|
||||||
Input TxInput `json:"input"`
|
Type byte `json:"type"` // Which app
|
||||||
Address []byte `json:"address"`
|
Gas int64 `json:"gas"`
|
||||||
GasLimit int64 `json:"gas_limit"`
|
Fee int64 `json:"fee"`
|
||||||
Fee int64 `json:"fee"`
|
Input TxInput `json:"input"`
|
||||||
Data []byte `json:"data"`
|
Data []byte `json:"data"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *CallTx) SignBytes(chainID string) []byte {
|
func (tx *AppTx) SignBytes(chainID string) []byte {
|
||||||
var buf = new(bytes.Buffer)
|
signBytes := wire.BinaryBytes(chainID)
|
||||||
buf.Write([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))))
|
sig := tx.Input.Signature
|
||||||
buf.Write([]byte(Fmt(`,"tx":[%v,{"address":"%X","data":"%X"`, TxTypeCall, tx.Address, tx.Data)))
|
tx.Input.Signature = nil
|
||||||
buf.Write([]byte(Fmt(`,"fee":%v,"gas_limit":%v,"input":`, tx.Fee, tx.GasLimit)))
|
signBytes = append(signBytes, wire.BinaryBytes(tx)...)
|
||||||
buf.Write(tx.Input.SignBytes())
|
tx.Input.Signature = sig
|
||||||
buf.Write([]byte(`}]}`))
|
return signBytes
|
||||||
return buf.Bytes()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *CallTx) String() string {
|
func (tx *AppTx) String() string {
|
||||||
return Fmt("CallTx{%v -> %x: %x}", tx.Input, tx.Address, tx.Data)
|
return Fmt("AppTx{%v %v %v %v -> %X}", tx.Type, tx.Gas, tx.Fee, tx.Input, tx.Data)
|
||||||
}
|
|
||||||
|
|
||||||
func NewContractAddress(caller []byte, nonce int) []byte {
|
|
||||||
temp := make([]byte, 32+8)
|
|
||||||
copy(temp, caller)
|
|
||||||
PutInt64BE(temp[32:], int64(nonce))
|
|
||||||
hasher := ripemd160.New()
|
|
||||||
hasher.Write(temp) // does not error
|
|
||||||
return hasher.Sum(nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------
|
||||||
|
|
|
@ -34,31 +34,29 @@ func TestSendTxSignable(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
signBytes := sendTx.SignBytes(chainID)
|
signBytes := sendTx.SignBytes(chainID)
|
||||||
signStr := string(signBytes)
|
signBytesHex := Fmt("%X", signBytes)
|
||||||
expected := Fmt(`{"chain_id":"%s","tx":[1,{"inputs":[{"address":"696E70757431","amount":12345,"sequence":67890},{"address":"696E70757432","amount":111,"sequence":222}],"outputs":[{"address":"6F757470757431","amount":333},{"address":"6F757470757432","amount":444}]}]}`,
|
expected := "010A746573745F636861696E0101020106696E7075743100000000000030390301093200000106696E70757432000000000000006F01DE0000010201076F757470757431000000000000014D01076F75747075743200000000000001BC"
|
||||||
chainID)
|
if signBytesHex != expected {
|
||||||
if signStr != expected {
|
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex)
|
||||||
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCallTxSignable(t *testing.T) {
|
func TestAppTxSignable(t *testing.T) {
|
||||||
callTx := &CallTx{
|
callTx := &AppTx{
|
||||||
|
Type: 0x01,
|
||||||
|
Gas: 111,
|
||||||
|
Fee: 222,
|
||||||
Input: TxInput{
|
Input: TxInput{
|
||||||
Address: []byte("input1"),
|
Address: []byte("input1"),
|
||||||
Amount: 12345,
|
Amount: 12345,
|
||||||
Sequence: 67890,
|
Sequence: 67890,
|
||||||
},
|
},
|
||||||
Address: []byte("contract1"),
|
Data: []byte("data1"),
|
||||||
GasLimit: 111,
|
|
||||||
Fee: 222,
|
|
||||||
Data: []byte("data1"),
|
|
||||||
}
|
}
|
||||||
signBytes := callTx.SignBytes(chainID)
|
signBytes := callTx.SignBytes(chainID)
|
||||||
signStr := string(signBytes)
|
signBytesHex := Fmt("%X", signBytes)
|
||||||
expected := Fmt(`{"chain_id":"%s","tx":[2,{"address":"636F6E747261637431","data":"6461746131","fee":222,"gas_limit":111,"input":{"address":"696E70757431","amount":12345,"sequence":67890}}]}`,
|
expected := "010A746573745F636861696E0101000000000000006F00000000000000DE0106696E70757431000000000000303903010932000001056461746131"
|
||||||
chainID)
|
if signBytesHex != expected {
|
||||||
if signStr != expected {
|
t.Errorf("Got unexpected sign string for AppTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex)
|
||||||
t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue