s/CallTx/AppTx/g; NamedPlugins

This commit is contained in:
Jae Kwon 2016-03-27 12:47:50 -07:00
parent 601a654b7d
commit fa39c9da5c
9 changed files with 153 additions and 131 deletions

View File

@ -12,23 +12,34 @@ import (
tmsp "github.com/tendermint/tmsp/types"
)
const version = "0.1"
const maxTxSize = 10240
const (
version = "0.1"
maxTxSize = 10240
typeByteBase = 0x01
typeByteGov = 0x02
pluginNameBase = "base"
pluginNameGov = "gov"
)
type Basecoin struct {
eyesCli *eyes.Client
govMint *gov.Governmint
state *state.State
plugins *types.Plugins
}
func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
state_ := state.NewState(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{
eyesCli: eyesCli,
govMint: govMint,
state: state_,
plugins: plugins,
}
}
@ -39,11 +50,10 @@ func (app *Basecoin) Info() string {
// TMSP::SetOption
func (app *Basecoin) SetOption(key string, value string) (log string) {
pluginName, key := splitKey(key)
if pluginName != "BASE" {
if pluginName != pluginNameBase {
// Set option on plugin
plugin := app.state.GetPlugin(pluginName)
plugin := app.plugins.GetByName(pluginName)
if plugin == nil {
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())
}
// 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() {
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())
}
// Validate tx
res = state.ExecTx(app.state, tx, true, nil)
res = state.ExecTx(app.state, app.plugins, tx, true, nil)
if res.IsErr() {
return res.PrependLog("Error in CheckTx")
}
@ -113,8 +123,8 @@ func (app *Basecoin) CheckTx(txBytes []byte) (res tmsp.Result) {
// TMSP::Query
func (app *Basecoin) Query(query []byte) (res tmsp.Result) {
pluginName, queryStr := splitKey(string(query))
if pluginName != "BASE" {
plugin := app.state.GetPlugin(pluginName)
if pluginName != pluginNameBase {
plugin := app.plugins.GetByName(pluginName)
if plugin == nil {
return tmsp.ErrBaseUnknownPlugin.SetLog(Fmt("Unknown plugin %v", pluginName))
}
@ -132,7 +142,7 @@ func (app *Basecoin) Query(query []byte) (res tmsp.Result) {
// TMSP::Commit
func (app *Basecoin) Commit() (res tmsp.Result) {
// First, commit all the plugins
for _, plugin := range app.state.GetPlugins() {
for _, plugin := range app.plugins.GetList() {
res = plugin.Commit()
if res.IsErr() {
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 :.
// if there are none, the second string is nil.
func splitKey(key string) (prefix string, sufix string) {
if strings.Contains(key, ":") {
keyParts := strings.SplitN(key, ":", 2)
if strings.Contains(key, "/") {
keyParts := strings.SplitN(key, "/", 2)
return keyParts[0], keyParts[1]
}
return key, ""

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"flag"
"fmt"
"reflect"
"github.com/tendermint/basecoin/app"
. "github.com/tendermint/go-common"
@ -58,14 +59,36 @@ type KeyValue struct {
}
func loadGenesis(filePath string) (kvz []KeyValue) {
kvz_ := []interface{}{}
bytes, err := ReadFile(filePath)
if err != nil {
Exit("loading genesis file: " + err.Error())
}
fmt.Println(">>", string(bytes))
err = json.Unmarshal(bytes, &kvz)
err = json.Unmarshal(bytes, &kvz_)
if err != nil {
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
}

View File

@ -1,10 +1,7 @@
[
{
"key": "chainID",
"value": "test_chain_id"
},
{
"key": "account",
"value": "{\"pub_key\":[1,\"3E8A80E5412FD1711995D5888F5FA2EFB26EDD0970F1E27CE0B55CD237439E18\"],\"balance\":1000}"
"base/chainID", "test_chain_id",
"base/account", {
"pub_key", [1, "67D3B5EAF0C0BF6B5A602D359DAECC86A7A74053490EC37AE08E71360587C870"],
"balance": 1000
}
]

View File

@ -10,7 +10,7 @@ import (
)
// 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
fees := int64(0)
@ -68,7 +68,7 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
return tmsp.OK
case *types.CallTx:
case *types.AppTx:
// First, get input account
inAcc := state.GetAccount(tx.Input.Address)
if inAcc == nil {
@ -93,9 +93,10 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
}
// Validate call address
plugin := state.GetPlugin(string(tx.Address))
plugin := pgz.GetByByte(tx.Type)
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!
@ -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)
inAccCopy := inAcc.Copy()
// If this is AppendTx, actually save accounts
// If this is a CheckTx, stop now.
if isCheckTx {
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)
gas := int64(1) // TODO
ctx := types.NewCallContext(cache, inAcc, value, &gas)
res = plugin.CallTx(ctx, tx.Data)
res = plugin.RunTx(ctx, tx.Data)
if res.IsOK() {
cache.Sync()
log.Info("Successful execution")
@ -131,7 +132,7 @@ func ExecTx(state *State, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp
}
*/
} else {
log.Info("CallTx failed", "error", res)
log.Info("AppTx failed", "error", res)
// Just return the value and return.
// TODO: return gas?
inAccCopy.Balance += value

View File

@ -8,11 +8,9 @@ import (
)
type State struct {
chainID string
eyesCli *eyes.Client
checkCache map[string]checkAccount
plugins map[string]types.Plugin
pluginsList []types.NamedPlugin
chainID string
eyesCli *eyes.Client
checkCache map[string]checkAccount
LastBlockHeight uint64
LastBlockHash []byte
@ -24,7 +22,6 @@ func NewState(eyesCli *eyes.Client) *State {
chainID: "",
eyesCli: eyesCli,
checkCache: make(map[string]checkAccount),
plugins: make(map[string]types.Plugin),
}
return s
}
@ -40,22 +37,6 @@ func (s *State) GetChainID() string {
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

View File

@ -13,7 +13,7 @@ import (
)
func main() {
//testSendTx()
testSendTx()
testGov()
}
@ -28,8 +28,8 @@ func testSendTx() {
// Seed Basecoin with account
tAcc := tPriv.Account
tAcc.Balance = 1000
bcApp.SetOption("chainID", "test_chain_id")
bcApp.SetOption("account", string(wire.JSONBytes(tAcc)))
fmt.Println(bcApp.SetOption("base/chainID", "test_chain_id"))
fmt.Println(bcApp.SetOption("base/account", string(wire.JSONBytes(tAcc))))
// Construct a SendTx signature
tx := &types.SendTx{
@ -51,10 +51,11 @@ func testSendTx() {
// Sign request
signBytes := tx.SignBytes("test_chain_id")
fmt.Printf("SIGNBYTES %X", signBytes)
fmt.Printf("Sign bytes: %X\n", signBytes)
sig := tPriv.PrivKey.Sign(signBytes)
tx.Inputs[0].Signature = sig
//fmt.Println("tx:", tx)
fmt.Printf("Signed TX bytes: %X\n", wire.BinaryBytes(tx))
// Write request
txBytes := wire.BinaryBytes(tx)
@ -78,7 +79,7 @@ func testGov() {
ID: "",
PubKey: tAcc.PubKey,
}
log := bcApp.SetOption("GOV:admin", string(wire.JSONBytes(adminEntity)))
log := bcApp.SetOption("gov/admin", string(wire.JSONBytes(adminEntity)))
if log != "Success" {
Exit(Fmt("Failed to set option: %v", log))
}

View File

@ -9,16 +9,19 @@ import (
// if any gas is left the user is
type Plugin interface {
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)
Commit() (res tmsp.Result)
}
type NamedPlugin struct {
Byte byte
Name string
Plugin
}
//----------------------------------------
type CallContext struct {
Cache AccountCacher
Caller *Account
@ -34,3 +37,40 @@ func NewCallContext(cache AccountCacher, caller *Account, value int64, gas *int6
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
}

View File

@ -1,14 +1,12 @@
package types
import (
"bytes"
"encoding/json"
. "github.com/tendermint/go-common"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire"
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:
- 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 {
@ -27,13 +25,13 @@ type Tx interface {
const (
// Account transactions
TxTypeSend = byte(0x01)
TxTypeCall = byte(0x02)
TxTypeApp = byte(0x02)
)
var _ = wire.RegisterInterface(
struct{ Tx }{},
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
}
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 {
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
}
func (txOut TxOutput) SignBytes() []byte {
return []byte(Fmt(`{"address":"%X","amount":%v}`,
txOut.Address, txOut.Amount))
}
func (txOut TxOutput) String() string {
return Fmt("TxOutput{%X,%v}", txOut.Address, txOut.Amount)
}
@ -99,24 +87,17 @@ type SendTx struct {
}
func (tx *SendTx) SignBytes(chainID string) []byte {
var buf = new(bytes.Buffer)
buf.Write([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))))
buf.Write([]byte(Fmt(`,"tx":[%v,{"inputs":[`, TxTypeSend)))
for i, in := range tx.Inputs {
buf.Write(in.SignBytes())
if i != len(tx.Inputs)-1 {
buf.Write([]byte(","))
}
signBytes := wire.BinaryBytes(chainID)
sigz := make([]crypto.Signature, len(tx.Inputs))
for i, input := range tx.Inputs {
sigz[i] = input.Signature
tx.Inputs[i].Signature = nil
}
buf.Write([]byte(`],"outputs":[`))
for i, out := range tx.Outputs {
buf.Write(out.SignBytes())
if i != len(tx.Outputs)-1 {
buf.Write([]byte(","))
}
signBytes = append(signBytes, wire.BinaryBytes(tx)...)
for i := range tx.Inputs {
tx.Inputs[i].Signature = sigz[i]
}
buf.Write([]byte(`]}]}`))
return buf.Bytes()
return signBytes
}
func (tx *SendTx) String() string {
@ -125,35 +106,25 @@ func (tx *SendTx) String() string {
//-----------------------------------------------------------------------------
type CallTx struct {
Input TxInput `json:"input"`
Address []byte `json:"address"`
GasLimit int64 `json:"gas_limit"`
Fee int64 `json:"fee"`
Data []byte `json:"data"`
type AppTx struct {
Type byte `json:"type"` // Which app
Gas int64 `json:"gas"`
Fee int64 `json:"fee"`
Input TxInput `json:"input"`
Data []byte `json:"data"`
}
func (tx *CallTx) SignBytes(chainID string) []byte {
var buf = new(bytes.Buffer)
buf.Write([]byte(Fmt(`{"chain_id":%s`, jsonEscape(chainID))))
buf.Write([]byte(Fmt(`,"tx":[%v,{"address":"%X","data":"%X"`, TxTypeCall, tx.Address, tx.Data)))
buf.Write([]byte(Fmt(`,"fee":%v,"gas_limit":%v,"input":`, tx.Fee, tx.GasLimit)))
buf.Write(tx.Input.SignBytes())
buf.Write([]byte(`}]}`))
return buf.Bytes()
func (tx *AppTx) SignBytes(chainID string) []byte {
signBytes := wire.BinaryBytes(chainID)
sig := tx.Input.Signature
tx.Input.Signature = nil
signBytes = append(signBytes, wire.BinaryBytes(tx)...)
tx.Input.Signature = sig
return signBytes
}
func (tx *CallTx) String() string {
return Fmt("CallTx{%v -> %x: %x}", tx.Input, tx.Address, 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)
func (tx *AppTx) String() string {
return Fmt("AppTx{%v %v %v %v -> %X}", tx.Type, tx.Gas, tx.Fee, tx.Input, tx.Data)
}
//-----------------------------------------------------------------------------

View File

@ -34,31 +34,29 @@ func TestSendTxSignable(t *testing.T) {
},
}
signBytes := sendTx.SignBytes(chainID)
signStr := string(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}]}]}`,
chainID)
if signStr != expected {
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signStr)
signBytesHex := Fmt("%X", signBytes)
expected := "010A746573745F636861696E0101020106696E7075743100000000000030390301093200000106696E70757432000000000000006F01DE0000010201076F757470757431000000000000014D01076F75747075743200000000000001BC"
if signBytesHex != expected {
t.Errorf("Got unexpected sign string for SendTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex)
}
}
func TestCallTxSignable(t *testing.T) {
callTx := &CallTx{
func TestAppTxSignable(t *testing.T) {
callTx := &AppTx{
Type: 0x01,
Gas: 111,
Fee: 222,
Input: TxInput{
Address: []byte("input1"),
Amount: 12345,
Sequence: 67890,
},
Address: []byte("contract1"),
GasLimit: 111,
Fee: 222,
Data: []byte("data1"),
Data: []byte("data1"),
}
signBytes := callTx.SignBytes(chainID)
signStr := string(signBytes)
expected := Fmt(`{"chain_id":"%s","tx":[2,{"address":"636F6E747261637431","data":"6461746131","fee":222,"gas_limit":111,"input":{"address":"696E70757431","amount":12345,"sequence":67890}}]}`,
chainID)
if signStr != expected {
t.Errorf("Got unexpected sign string for CallTx. Expected:\n%v\nGot:\n%v", expected, signStr)
signBytesHex := Fmt("%X", signBytes)
expected := "010A746573745F636861696E0101000000000000006F00000000000000DE0106696E70757431000000000000303903010932000001056461746131"
if signBytesHex != expected {
t.Errorf("Got unexpected sign string for AppTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex)
}
}