Begin integrating Governmint
This commit is contained in:
parent
479ef98ec6
commit
63279a897c
142
app/app.go
142
app/app.go
|
@ -2,9 +2,12 @@ package app
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/tendermint/basecoin/types"
|
||||
. "github.com/tendermint/go-common"
|
||||
"github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/go-wire"
|
||||
gov "github.com/tendermint/governmint/gov"
|
||||
eyes "github.com/tendermint/merkleeyes/client"
|
||||
tmsp "github.com/tendermint/tmsp/types"
|
||||
)
|
||||
|
@ -14,17 +17,19 @@ const maxTxSize = 10240
|
|||
|
||||
type Basecoin struct {
|
||||
eyesCli *eyes.Client
|
||||
govMint *gov.Governmint
|
||||
}
|
||||
|
||||
func NewBasecoin(eyesCli *eyes.Client) *Basecoin {
|
||||
return &Basecoin{
|
||||
eyesCli: eyesCli,
|
||||
govMint: gov.NewGovernmint(eyesCli),
|
||||
}
|
||||
}
|
||||
|
||||
// TMSP::Info
|
||||
func (app *Basecoin) Info() string {
|
||||
return "Basecoin v" + version
|
||||
return Fmt("Basecoin v%v\n - %v", version, app.govMint.Info())
|
||||
}
|
||||
|
||||
// TMSP::SetOption
|
||||
|
@ -50,13 +55,13 @@ func (app *Basecoin) SetOption(key string, value string) (log string) {
|
|||
// TMSP::AppendTx
|
||||
func (app *Basecoin) AppendTx(txBytes []byte) (code tmsp.CodeType, result []byte, log string) {
|
||||
if len(txBytes) > maxTxSize {
|
||||
return tmsp.CodeType_EncodingError, nil, "Tx size exceeds maximum"
|
||||
return tmsp.CodeType_BaseEncodingError, nil, "Tx size exceeds maximum"
|
||||
}
|
||||
// Decode tx
|
||||
var tx types.Tx
|
||||
err := wire.ReadBinaryBytes(txBytes, &tx)
|
||||
if err != nil {
|
||||
return tmsp.CodeType_EncodingError, nil, "Error decoding tx: " + err.Error()
|
||||
return tmsp.CodeType_BaseEncodingError, nil, "Error decoding tx: " + err.Error()
|
||||
}
|
||||
// Validate tx
|
||||
code, errStr := validateTx(tx)
|
||||
|
@ -66,7 +71,7 @@ func (app *Basecoin) AppendTx(txBytes []byte) (code tmsp.CodeType, result []byte
|
|||
// Load accounts
|
||||
accMap := loadAccounts(app.eyesCli, allPubKeys(tx))
|
||||
// Execute tx
|
||||
accs, code, errStr := execTx(tx, accMap, false)
|
||||
accs, code, errStr := runTx(tx, accMap, false)
|
||||
if errStr != "" {
|
||||
return code, nil, "Error executing tx: " + errStr
|
||||
}
|
||||
|
@ -78,13 +83,13 @@ func (app *Basecoin) AppendTx(txBytes []byte) (code tmsp.CodeType, result []byte
|
|||
// TMSP::CheckTx
|
||||
func (app *Basecoin) CheckTx(txBytes []byte) (code tmsp.CodeType, result []byte, log string) {
|
||||
if len(txBytes) > maxTxSize {
|
||||
return tmsp.CodeType_EncodingError, nil, "Tx size exceeds maximum"
|
||||
return tmsp.CodeType_BaseEncodingError, nil, "Tx size exceeds maximum"
|
||||
}
|
||||
// Decode tx
|
||||
var tx types.Tx
|
||||
err := wire.ReadBinaryBytes(txBytes, &tx)
|
||||
if err != nil {
|
||||
return tmsp.CodeType_EncodingError, nil, "Error decoding tx: " + err.Error()
|
||||
return tmsp.CodeType_BaseEncodingError, nil, "Error decoding tx: " + err.Error()
|
||||
}
|
||||
// Validate tx
|
||||
code, errStr := validateTx(tx)
|
||||
|
@ -94,7 +99,7 @@ func (app *Basecoin) CheckTx(txBytes []byte) (code tmsp.CodeType, result []byte,
|
|||
// Load accounts
|
||||
accMap := loadAccounts(app.eyesCli, allPubKeys(tx))
|
||||
// Execute tx
|
||||
_, code, errStr = execTx(tx, accMap, false)
|
||||
_, code, errStr = runTx(tx, accMap, false)
|
||||
if errStr != "" {
|
||||
return code, nil, "Error (mock) executing tx: " + errStr
|
||||
}
|
||||
|
@ -120,83 +125,80 @@ func (app *Basecoin) Commit() (hash []byte, log string) {
|
|||
return hash, "Success"
|
||||
}
|
||||
|
||||
// TMSP::InitChain
|
||||
func (app *Basecoin) InitChain(validators []*tmsp.Validator) {
|
||||
app.govMint.InitChain(validators)
|
||||
}
|
||||
|
||||
// TMSP::EndBlock
|
||||
func (app *Basecoin) EndBlock(height uint64) []*tmsp.Validator {
|
||||
return app.govMint.EndBlock(height)
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func validateTx(tx types.Tx) (code tmsp.CodeType, errStr string) {
|
||||
if len(tx.Inputs) == 0 {
|
||||
return tmsp.CodeType_EncodingError, "Tx.Inputs length cannot be 0"
|
||||
inputs, outputs := tx.GetInputs(), tx.GetOutputs()
|
||||
if len(inputs) == 0 {
|
||||
return tmsp.CodeType_BaseEncodingError, "Tx.Inputs length cannot be 0"
|
||||
}
|
||||
seenPubKeys := map[string]bool{}
|
||||
signBytes := txSignBytes(tx)
|
||||
for _, input := range tx.Inputs {
|
||||
signBytes := tx.SignBytes()
|
||||
for _, input := range inputs {
|
||||
code, errStr = validateInput(input, signBytes)
|
||||
if errStr != "" {
|
||||
return
|
||||
}
|
||||
keyString := input.PubKey.KeyString()
|
||||
if seenPubKeys[keyString] {
|
||||
return tmsp.CodeType_EncodingError, "Duplicate input pubKey"
|
||||
return tmsp.CodeType_BaseEncodingError, "Duplicate input pubKey"
|
||||
}
|
||||
seenPubKeys[keyString] = true
|
||||
}
|
||||
for _, output := range tx.Outputs {
|
||||
for _, output := range outputs {
|
||||
code, errStr = validateOutput(output)
|
||||
if errStr != "" {
|
||||
return
|
||||
}
|
||||
keyString := output.PubKey.KeyString()
|
||||
if seenPubKeys[keyString] {
|
||||
return tmsp.CodeType_EncodingError, "Duplicate output pubKey"
|
||||
return tmsp.CodeType_BaseEncodingError, "Duplicate output pubKey"
|
||||
}
|
||||
seenPubKeys[keyString] = true
|
||||
}
|
||||
sumInputs, overflow := sumAmounts(tx.Inputs, nil, 0)
|
||||
sumInputs, overflow := sumAmounts(inputs, nil, 0)
|
||||
if overflow {
|
||||
return tmsp.CodeType_EncodingError, "Input amount overflow"
|
||||
return tmsp.CodeType_BaseEncodingError, "Input amount overflow"
|
||||
}
|
||||
sumOutputsPlus, overflow := sumAmounts(nil, tx.Outputs, len(tx.Inputs)+len(tx.Outputs))
|
||||
sumOutputsPlus, overflow := sumAmounts(nil, outputs, len(inputs)+len(outputs))
|
||||
if overflow {
|
||||
return tmsp.CodeType_EncodingError, "Output amount overflow"
|
||||
return tmsp.CodeType_BaseEncodingError, "Output amount overflow"
|
||||
}
|
||||
if sumInputs < sumOutputsPlus {
|
||||
return tmsp.CodeType_InsufficientFees, "Insufficient fees"
|
||||
return tmsp.CodeType_BaseInsufficientFees, "Insufficient fees"
|
||||
}
|
||||
return tmsp.CodeType_OK, ""
|
||||
}
|
||||
|
||||
func txSignBytes(tx types.Tx) []byte {
|
||||
sigs := make([]crypto.Signature, len(tx.Inputs))
|
||||
for i, input := range tx.Inputs {
|
||||
sigs[i] = input.Signature
|
||||
input.Signature = nil
|
||||
tx.Inputs[i] = input
|
||||
}
|
||||
signBytes := wire.BinaryBytes(tx)
|
||||
for i := range tx.Inputs {
|
||||
tx.Inputs[i].Signature = sigs[i]
|
||||
}
|
||||
return signBytes
|
||||
}
|
||||
|
||||
func validateInput(input types.Input, signBytes []byte) (code tmsp.CodeType, errStr string) {
|
||||
if input.Amount == 0 {
|
||||
return tmsp.CodeType_EncodingError, "Input amount cannot be zero"
|
||||
return tmsp.CodeType_BaseEncodingError, "Input amount cannot be zero"
|
||||
}
|
||||
if input.PubKey == nil {
|
||||
return tmsp.CodeType_EncodingError, "Input pubKey cannot be nil"
|
||||
return tmsp.CodeType_BaseEncodingError, "Input pubKey cannot be nil"
|
||||
}
|
||||
if !input.PubKey.VerifyBytes(signBytes, input.Signature) {
|
||||
return tmsp.CodeType_Unauthorized, "Invalid signature"
|
||||
return tmsp.CodeType_BaseUnauthorized, "Invalid signature"
|
||||
}
|
||||
return tmsp.CodeType_OK, ""
|
||||
}
|
||||
|
||||
func validateOutput(output types.Output) (code tmsp.CodeType, errStr string) {
|
||||
if output.Amount == 0 {
|
||||
return tmsp.CodeType_EncodingError, "Output amount cannot be zero"
|
||||
return tmsp.CodeType_BaseEncodingError, "Output amount cannot be zero"
|
||||
}
|
||||
if output.PubKey == nil {
|
||||
return tmsp.CodeType_EncodingError, "Output pubKey cannot be nil"
|
||||
return tmsp.CodeType_BaseEncodingError, "Output pubKey cannot be nil"
|
||||
}
|
||||
return tmsp.CodeType_OK, ""
|
||||
}
|
||||
|
@ -220,39 +222,40 @@ func sumAmounts(inputs []types.Input, outputs []types.Output, more int) (total u
|
|||
return total, false
|
||||
}
|
||||
|
||||
func allPubKeys(tx types.Tx) (pubKeys []crypto.PubKey) {
|
||||
pubKeys = make([]crypto.PubKey, 0, len(tx.Inputs)+len(tx.Outputs))
|
||||
for _, input := range tx.Inputs {
|
||||
pubKeys = append(pubKeys, input.PubKey)
|
||||
}
|
||||
for _, output := range tx.Outputs {
|
||||
pubKeys = append(pubKeys, output.PubKey)
|
||||
}
|
||||
return pubKeys
|
||||
}
|
||||
|
||||
// Returns accounts in order of types.Tx inputs and outputs
|
||||
// appendTx: true if this is for AppendTx.
|
||||
// TODO: create more intelligent sequence-checking. Current impl is just for a throughput demo.
|
||||
func execTx(tx types.Tx, accMap map[string]types.PubAccount, appendTx bool) (accs []types.PubAccount, code tmsp.CodeType, errStr string) {
|
||||
accs = make([]types.PubAccount, 0, len(tx.Inputs)+len(tx.Outputs))
|
||||
func runTx(tx types.Tx, accMap map[string]types.PubAccount, appendTx bool) (accs []types.PubAccount, code tmsp.CodeType, errStr string) {
|
||||
switch tx := tx.(type) {
|
||||
case *types.SendTx:
|
||||
return runSendTx(tx, accMap, appendTx)
|
||||
case *types.GovTx:
|
||||
return runGovTx(tx, accMap, appendTx)
|
||||
}
|
||||
return nil, tmsp.CodeType_InternalError, "Unknown transaction type"
|
||||
}
|
||||
|
||||
func processInputsOutputs(tx types.Tx, accMap map[string]types.PubAccount, appendTx bool) (accs []types.PubAccount, code tmsp.CodeType, errStr string) {
|
||||
inputs, outputs := tx.GetInputs(), tx.GetOutputs()
|
||||
accs = make([]types.PubAccount, 0, len(inputs)+len(outputs))
|
||||
// Deduct from inputs
|
||||
for _, input := range tx.Inputs {
|
||||
// TODO refactor, duplicated code.
|
||||
for _, input := range inputs {
|
||||
var acc, ok = accMap[input.PubKey.KeyString()]
|
||||
if !ok {
|
||||
return nil, tmsp.CodeType_UnknownAccount, "Input account does not exist"
|
||||
return nil, tmsp.CodeType_BaseUnknownAccount, "Input account does not exist"
|
||||
}
|
||||
if appendTx {
|
||||
if acc.Sequence != input.Sequence {
|
||||
return nil, tmsp.CodeType_BadNonce, "Invalid sequence"
|
||||
return nil, tmsp.CodeType_BaseBadNonce, "Invalid sequence"
|
||||
}
|
||||
} else {
|
||||
if acc.Sequence > input.Sequence {
|
||||
return nil, tmsp.CodeType_BadNonce, "Invalid sequence (too low)"
|
||||
return nil, tmsp.CodeType_BaseBadNonce, "Invalid sequence (too low)"
|
||||
}
|
||||
}
|
||||
if acc.Balance < input.Amount {
|
||||
return nil, tmsp.CodeType_InsufficientFunds, "Insufficient funds"
|
||||
return nil, tmsp.CodeType_BaseInsufficientFunds, "Insufficient funds"
|
||||
}
|
||||
// Good!
|
||||
acc.Sequence++
|
||||
|
@ -260,7 +263,7 @@ func execTx(tx types.Tx, accMap map[string]types.PubAccount, appendTx bool) (acc
|
|||
accs = append(accs, acc)
|
||||
}
|
||||
// Add to outputs
|
||||
for _, output := range tx.Outputs {
|
||||
for _, output := range outputs {
|
||||
var acc, ok = accMap[output.PubKey.KeyString()]
|
||||
if !ok {
|
||||
// Create new account if it doesn't already exist.
|
||||
|
@ -275,7 +278,7 @@ func execTx(tx types.Tx, accMap map[string]types.PubAccount, appendTx bool) (acc
|
|||
} else {
|
||||
// Good!
|
||||
if (acc.Balance + output.Amount) < acc.Balance {
|
||||
return nil, tmsp.CodeType_InternalError, "Output balance overflow in execTx"
|
||||
return nil, tmsp.CodeType_InternalError, "Output balance overflow in runTx"
|
||||
}
|
||||
acc.Balance += output.Amount
|
||||
accs = append(accs, acc)
|
||||
|
@ -284,6 +287,16 @@ func execTx(tx types.Tx, accMap map[string]types.PubAccount, appendTx bool) (acc
|
|||
return accs, tmsp.CodeType_OK, ""
|
||||
}
|
||||
|
||||
func runSendTx(tx types.Tx, accMap map[string]types.PubAccount, appendTx bool) (accs []types.PubAccount, code tmsp.CodeType, errStr string) {
|
||||
return processInputsOutputs(tx, accMap, appendTx)
|
||||
}
|
||||
|
||||
func runGovTx(tx *types.GovTx, accMap map[string]types.PubAccount, appendTx bool) (accs []types.PubAccount, code tmsp.CodeType, errStr string) {
|
||||
accs, code, errStr = processInputsOutputs(tx, accMap, appendTx)
|
||||
// XXX run GovTx
|
||||
return
|
||||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func loadAccounts(eyesCli *eyes.Client, pubKeys []crypto.PubKey) (accMap map[string]types.PubAccount) {
|
||||
|
@ -323,3 +336,16 @@ func storeAccounts(eyesCli *eyes.Client, accs []types.PubAccount) {
|
|||
}
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
func allPubKeys(tx types.Tx) (pubKeys []crypto.PubKey) {
|
||||
inputs := tx.GetInputs()
|
||||
outputs := tx.GetOutputs()
|
||||
pubKeys = make([]crypto.PubKey, 0, len(inputs)+len(outputs))
|
||||
for _, input := range inputs {
|
||||
pubKeys = append(pubKeys, input.PubKey)
|
||||
}
|
||||
for _, output := range outputs {
|
||||
pubKeys = append(pubKeys, output.PubKey)
|
||||
}
|
||||
return pubKeys
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import (
|
|||
// Creates a PrivAccount from secret.
|
||||
// The amount is not set.
|
||||
func PrivAccountFromSecret(secret string) types.PrivAccount {
|
||||
privKey := crypto.GenPrivKeyEd25519FromSecret(secret)
|
||||
privKey := crypto.GenPrivKeyEd25519FromSecret([]byte(secret))
|
||||
privAccount := types.PrivAccount{
|
||||
PrivKey: privKey,
|
||||
PubKey: privKey.PubKey(),
|
||||
|
|
|
@ -15,8 +15,7 @@ import (
|
|||
)
|
||||
|
||||
func main() {
|
||||
//ws := rpcclient.NewWSClient("ws://127.0.0.1:46657", "/websocket")
|
||||
ws := rpcclient.NewWSClient("ws://104.131.151.26:46657", "/websocket")
|
||||
ws := rpcclient.NewWSClient("127.0.0.1:46657", "/websocket")
|
||||
_, err := ws.Start()
|
||||
if err != nil {
|
||||
Exit(err.Error())
|
||||
|
@ -44,7 +43,7 @@ func main() {
|
|||
// Send coins to each account
|
||||
for i := 0; i < len(privAccounts); i++ {
|
||||
privAccount := privAccounts[i]
|
||||
tx := types.Tx{
|
||||
tx := &types.SendTx{
|
||||
Inputs: []types.Input{
|
||||
types.Input{
|
||||
PubKey: root.PubKey,
|
||||
|
@ -94,7 +93,7 @@ func main() {
|
|||
privAccountSequences[privAccountA.PubKey.KeyString()] = privAccountASequence + 1
|
||||
privAccountB := privAccounts[randB]
|
||||
|
||||
tx := types.Tx{
|
||||
tx := &types.SendTx{
|
||||
Inputs: []types.Input{
|
||||
types.Input{
|
||||
PubKey: privAccountA.PubKey,
|
||||
|
|
|
@ -2,13 +2,10 @@ package types
|
|||
|
||||
import (
|
||||
"github.com/tendermint/go-crypto"
|
||||
"github.com/tendermint/go-wire"
|
||||
gov "github.com/tendermint/governmint/types"
|
||||
)
|
||||
|
||||
type Tx struct {
|
||||
Inputs []Input
|
||||
Outputs []Output
|
||||
}
|
||||
|
||||
type Input struct {
|
||||
PubKey crypto.PubKey
|
||||
Amount uint64
|
||||
|
@ -21,6 +18,67 @@ type Output struct {
|
|||
Amount uint64
|
||||
}
|
||||
|
||||
type SendTx struct {
|
||||
Inputs []Input
|
||||
Outputs []Output
|
||||
}
|
||||
|
||||
func (tx *SendTx) SignBytes() []byte {
|
||||
sigs := make([]crypto.Signature, len(tx.Inputs))
|
||||
for i, input := range tx.Inputs {
|
||||
sigs[i] = input.Signature
|
||||
input.Signature = nil
|
||||
tx.Inputs[i] = input
|
||||
}
|
||||
signBytes := wire.BinaryBytes(tx)
|
||||
for i := range tx.Inputs {
|
||||
tx.Inputs[i].Signature = sigs[i]
|
||||
}
|
||||
return signBytes
|
||||
}
|
||||
|
||||
func (tx *SendTx) GetInputs() []Input { return tx.Inputs }
|
||||
func (tx *SendTx) GetOutputs() []Output { return tx.Outputs }
|
||||
|
||||
type GovTx struct {
|
||||
Input Input
|
||||
Tx gov.Tx
|
||||
}
|
||||
|
||||
func (tx *GovTx) SignBytes() []byte {
|
||||
sig := tx.Input.Signature
|
||||
tx.Input.Signature = nil
|
||||
signBytes := wire.BinaryBytes(tx)
|
||||
tx.Input.Signature = sig
|
||||
return signBytes
|
||||
}
|
||||
|
||||
func (tx *GovTx) GetInputs() []Input { return []Input{tx.Input} }
|
||||
func (tx *GovTx) GetOutputs() []Output { return nil }
|
||||
|
||||
type Tx interface {
|
||||
AssertIsTx()
|
||||
SignBytes() []byte
|
||||
GetInputs() []Input
|
||||
GetOutputs() []Output
|
||||
}
|
||||
|
||||
func (_ *SendTx) AssertIsTx() {}
|
||||
func (_ *GovTx) AssertIsTx() {}
|
||||
|
||||
const (
|
||||
TxTypeSend = byte(0x01)
|
||||
TxTypeGov = byte(0x02)
|
||||
)
|
||||
|
||||
var _ = wire.RegisterInterface(
|
||||
struct{ Tx }{},
|
||||
wire.ConcreteType{&SendTx{}, TxTypeSend},
|
||||
wire.ConcreteType{&GovTx{}, TxTypeGov},
|
||||
)
|
||||
|
||||
//----------------------------------------
|
||||
|
||||
type Account struct {
|
||||
Sequence uint
|
||||
Balance uint64
|
||||
|
|
Loading…
Reference in New Issue