From 63279a897cd277e0c29ba11815090a03c99e714b Mon Sep 17 00:00:00 2001 From: Jae Kwon Date: Tue, 15 Mar 2016 15:01:53 -0700 Subject: [PATCH] Begin integrating Governmint --- app/app.go | 142 +++++++++++++++++++++++---------------- tests/common.go | 2 +- tests/tendermint/main.go | 7 +- types/types.go | 68 +++++++++++++++++-- 4 files changed, 151 insertions(+), 68 deletions(-) diff --git a/app/app.go b/app/app.go index 562634403..82ebfb47b 100644 --- a/app/app.go +++ b/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 +} diff --git a/tests/common.go b/tests/common.go index 80dbcfc3b..4e4608a20 100644 --- a/tests/common.go +++ b/tests/common.go @@ -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(), diff --git a/tests/tendermint/main.go b/tests/tendermint/main.go index 4dc0e1ad5..b4f11f91c 100644 --- a/tests/tendermint/main.go +++ b/tests/tendermint/main.go @@ -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, diff --git a/types/types.go b/types/types.go index 15d04b745..27c51127a 100644 --- a/types/types.go +++ b/types/types.go @@ -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