Coins and fees and gas...

This commit is contained in:
Jae Kwon 2016-04-01 15:19:07 -07:00
parent f81718eea4
commit a16b96062b
10 changed files with 278 additions and 72 deletions

View File

@ -13,7 +13,7 @@ import (
func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc events.Fireable) tmsp.Result { func ExecTx(s *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 := types.Coins{}
chainID := s.GetChainID() chainID := s.GetChainID()
// Get the state. If isCheckTx, then we use a cache. // Get the state. If isCheckTx, then we use a cache.
@ -50,11 +50,10 @@ func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc event
if res.IsErr() { if res.IsErr() {
return res.PrependLog("in validateOutputs()") return res.PrependLog("in validateOutputs()")
} }
if outTotal > inTotal { if !inTotal.IsEqual(outTotal.Plus(types.Coins{{"", tx.Fee}})) {
return tmsp.ErrBaseInsufficientFunds return tmsp.ErrBaseInvalidOutput.AppendLog("Input total != output total + fees")
} }
fee := inTotal - outTotal fees = fees.Plus(types.Coins{{"", tx.Fee}})
fees += fee
// TODO: Fee validation for SendTx // TODO: Fee validation for SendTx
@ -97,7 +96,7 @@ func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc event
log.Info(Fmt("validateInput failed on %X: %v", tx.Input.Address, res)) log.Info(Fmt("validateInput failed on %X: %v", tx.Input.Address, res))
return res.PrependLog("in validateInput()") return res.PrependLog("in validateInput()")
} }
if tx.Input.Amount < 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))
return tmsp.ErrBaseInsufficientFunds return tmsp.ErrBaseInsufficientFunds
} }
@ -110,9 +109,9 @@ func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc event
} }
// Good! // Good!
value := tx.Input.Amount - tx.Fee coins := tx.Input.Coins.Minus(types.Coins{{"", tx.Fee}})
inAcc.Sequence += 1 inAcc.Sequence += 1
inAcc.Balance -= tx.Input.Amount inAcc.Balance = inAcc.Balance.Minus(tx.Input.Coins)
// If this is a CheckTx, stop now. // If this is a CheckTx, stop now.
if isCheckTx { if isCheckTx {
@ -126,8 +125,7 @@ func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc event
// Run the tx. // Run the tx.
cache := types.NewAccountCache(state) cache := types.NewAccountCache(state)
cache.SetAccount(tx.Input.Address, inAcc) cache.SetAccount(tx.Input.Address, inAcc)
gas := int64(1) // TODO ctx := types.NewCallContext(cache, inAcc, coins)
ctx := types.NewCallContext(cache, inAcc, value, &gas)
res = plugin.RunTx(ctx, tx.Data) res = plugin.RunTx(ctx, tx.Data)
if res.IsOK() { if res.IsOK() {
cache.Sync() cache.Sync()
@ -145,9 +143,10 @@ func ExecTx(s *State, pgz *types.Plugins, tx types.Tx, isCheckTx bool, evc event
*/ */
} else { } else {
log.Info("AppTx failed", "error", res) log.Info("AppTx failed", "error", res)
// Just return the value and return. // Just return the coins and return.
// TODO: return gas? inAccCopy.Balance = inAccCopy.Balance.Plus(coins)
inAccCopy.Balance += value // But take the gas
// TODO
state.SetAccount(tx.Input.Address, inAccCopy) state.SetAccount(tx.Input.Address, inAccCopy)
} }
return res return res
@ -199,7 +198,6 @@ func getOrMakeOutputs(state types.AccountGetter, accounts map[string]*types.Acco
acc = &types.Account{ acc = &types.Account{
PubKey: nil, PubKey: nil,
Sequence: 0, Sequence: 0,
Balance: 0,
} }
} }
accounts[string(out.Address)] = acc accounts[string(out.Address)] = acc
@ -229,8 +227,8 @@ func checkInputPubKey(address []byte, acc *types.Account, in types.TxInput) tmsp
return tmsp.OK return tmsp.OK
} }
// Validate inputs and compute total amount // Validate inputs and compute total amount of coins
func validateInputs(accounts map[string]*types.Account, signBytes []byte, ins []types.TxInput) (total int64, res tmsp.Result) { func validateInputs(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)]
@ -242,7 +240,7 @@ func validateInputs(accounts map[string]*types.Account, signBytes []byte, ins []
return return
} }
// Good. Add amount to total // Good. Add amount to total
total += in.Amount total = total.Plus(in.Coins)
} }
return total, tmsp.OK return total, tmsp.OK
} }
@ -252,13 +250,13 @@ func validateInput(acc *types.Account, signBytes []byte, in types.TxInput) (res
if res := in.ValidateBasic(); res.IsErr() { if res := in.ValidateBasic(); res.IsErr() {
return res return res
} }
// Check sequence/balance // 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 {
return tmsp.ErrBaseInvalidSequence.AppendLog(Fmt("Got %v, expected %v. (acc.seq=%v)", in.Sequence, seq+1, acc.Sequence)) return tmsp.ErrBaseInvalidSequence.AppendLog(Fmt("Got %v, expected %v. (acc.seq=%v)", in.Sequence, seq+1, acc.Sequence))
} }
// Check amount // Check amount
if balance < in.Amount { if !balance.IsGTE(in.Coins) {
return tmsp.ErrBaseInsufficientFunds return tmsp.ErrBaseInsufficientFunds
} }
// Check signatures // Check signatures
@ -268,14 +266,14 @@ func validateInput(acc *types.Account, signBytes []byte, in types.TxInput) (res
return tmsp.OK return tmsp.OK
} }
func validateOutputs(outs []types.TxOutput) (total int64, res tmsp.Result) { func validateOutputs(outs []types.TxOutput) (total types.Coins, 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 0, res return nil, res
} }
// Good. Add amount to total // Good. Add amount to total
total += out.Amount total = total.Plus(out.Coins)
} }
return total, tmsp.OK return total, tmsp.OK
} }
@ -286,10 +284,10 @@ func adjustByInputs(state types.AccountSetter, accounts map[string]*types.Accoun
if acc == nil { if acc == nil {
PanicSanity("adjustByInputs() expects account in accounts") PanicSanity("adjustByInputs() expects account in accounts")
} }
if acc.Balance < in.Amount { if !acc.Balance.IsGTE(in.Coins) {
PanicSanity("adjustByInputs() expects sufficient funds") PanicSanity("adjustByInputs() expects sufficient funds")
} }
acc.Balance -= in.Amount acc.Balance = acc.Balance.Minus(in.Coins)
acc.Sequence += 1 acc.Sequence += 1
state.SetAccount(in.Address, acc) state.SetAccount(in.Address, acc)
} }
@ -301,7 +299,7 @@ func adjustByOutputs(state types.AccountSetter, accounts map[string]*types.Accou
if acc == nil { if acc == nil {
PanicSanity("adjustByOutputs() expects account in accounts") PanicSanity("adjustByOutputs() expects account in accounts")
} }
acc.Balance += out.Amount acc.Balance = acc.Balance.Plus(out.Coins)
if !isCheckTx { if !isCheckTx {
state.SetAccount(out.Address, acc) state.SetAccount(out.Address, acc)
} }

View File

@ -15,7 +15,6 @@ func PrivAccountFromSecret(secret string) types.PrivAccount {
Account: types.Account{ Account: types.Account{
PubKey: privKey.PubKey(), PubKey: privKey.PubKey(),
Sequence: 0, Sequence: 0,
Balance: 0,
}, },
} }
return privAccount return privAccount
@ -38,7 +37,7 @@ func RandAccounts(num int, minAmount int64, maxAmount int64) []types.PrivAccount
Account: types.Account{ Account: types.Account{
PubKey: pubKey, PubKey: pubKey,
Sequence: 0, Sequence: 0,
Balance: balance, Balance: types.Coins{types.Coin{"", balance}},
}, },
} }
} }

View File

@ -51,14 +51,14 @@ func main() {
types.TxInput{ types.TxInput{
Address: root.Account.PubKey.Address(), Address: root.Account.PubKey.Address(),
PubKey: root.Account.PubKey, // TODO is this needed? PubKey: root.Account.PubKey, // TODO is this needed?
Amount: 1000002, Coins: types.Coins{{"", 1000002}},
Sequence: sequence, Sequence: sequence,
}, },
}, },
Outputs: []types.TxOutput{ Outputs: []types.TxOutput{
types.TxOutput{ types.TxOutput{
Address: privAccount.Account.PubKey.Address(), Address: privAccount.Account.PubKey.Address(),
Amount: 1000000, Coins: types.Coins{{"", 1000000}},
}, },
}, },
} }
@ -102,14 +102,14 @@ func main() {
types.TxInput{ types.TxInput{
Address: privAccountA.Account.PubKey.Address(), Address: privAccountA.Account.PubKey.Address(),
PubKey: privAccountA.Account.PubKey, PubKey: privAccountA.Account.PubKey,
Amount: 3, Coins: types.Coins{{"", 3}},
Sequence: privAccountASequence + 1, Sequence: privAccountASequence + 1,
}, },
}, },
Outputs: []types.TxOutput{ Outputs: []types.TxOutput{
types.TxOutput{ types.TxOutput{
Address: privAccountB.Account.PubKey.Address(), Address: privAccountB.Account.PubKey.Address(),
Amount: 1, Coins: types.Coins{{"", 1}},
}, },
}, },
} }

View File

@ -28,24 +28,26 @@ func testSendTx() {
// Seed Basecoin with account // Seed Basecoin with account
tAcc := tPriv.Account tAcc := tPriv.Account
tAcc.Balance = 1000 tAcc.Balance = types.Coins{{"", 1000}}
fmt.Println(bcApp.SetOption("base/chainID", "test_chain_id")) fmt.Println(bcApp.SetOption("base/chainID", "test_chain_id"))
fmt.Println(bcApp.SetOption("base/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{
Fee: 0,
Gas: 0,
Inputs: []types.TxInput{ Inputs: []types.TxInput{
types.TxInput{ types.TxInput{
Address: tPriv.Account.PubKey.Address(), Address: tPriv.Account.PubKey.Address(),
PubKey: tPriv.Account.PubKey, // TODO is this needed? PubKey: tPriv.Account.PubKey, // TODO is this needed?
Amount: 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: tPriv2.Account.PubKey.Address(),
Amount: 1, Coins: types.Coins{{"", 1}},
}, },
}, },
} }
@ -95,7 +97,7 @@ func testSequence() {
// Get the root account // Get the root account
root := tests.PrivAccountFromSecret("test") root := tests.PrivAccountFromSecret("test")
rootAcc := root.Account rootAcc := root.Account
rootAcc.Balance = 1 << 53 rootAcc.Balance = types.Coins{{"", 1 << 53}}
fmt.Println(bcApp.SetOption("base/chainID", "test_chain_id")) fmt.Println(bcApp.SetOption("base/chainID", "test_chain_id"))
fmt.Println(bcApp.SetOption("base/account", string(wire.JSONBytes(rootAcc)))) fmt.Println(bcApp.SetOption("base/account", string(wire.JSONBytes(rootAcc))))
@ -108,18 +110,20 @@ func testSequence() {
for i := 0; i < len(privAccounts); i++ { for i := 0; i < len(privAccounts); i++ {
privAccount := privAccounts[i] privAccount := privAccounts[i]
tx := &types.SendTx{ tx := &types.SendTx{
Fee: 2,
Gas: 2,
Inputs: []types.TxInput{ Inputs: []types.TxInput{
types.TxInput{ types.TxInput{
Address: root.Account.PubKey.Address(), Address: root.Account.PubKey.Address(),
PubKey: root.Account.PubKey, // TODO is this needed? PubKey: root.Account.PubKey, // TODO is this needed?
Amount: 1000002, Coins: types.Coins{{"", 1000002}},
Sequence: sequence, Sequence: sequence,
}, },
}, },
Outputs: []types.TxOutput{ Outputs: []types.TxOutput{
types.TxOutput{ types.TxOutput{
Address: privAccount.Account.PubKey.Address(), Address: privAccount.Account.PubKey.Address(),
Amount: 1000000, Coins: types.Coins{{"", 1000000}},
}, },
}, },
} }
@ -155,18 +159,20 @@ func testSequence() {
privAccountB := privAccounts[randB] privAccountB := privAccounts[randB]
tx := &types.SendTx{ tx := &types.SendTx{
Fee: 2,
Gas: 2,
Inputs: []types.TxInput{ Inputs: []types.TxInput{
types.TxInput{ types.TxInput{
Address: privAccountA.Account.PubKey.Address(), Address: privAccountA.Account.PubKey.Address(),
PubKey: privAccountA.Account.PubKey, PubKey: privAccountA.Account.PubKey,
Amount: 3, Coins: types.Coins{{"", 3}},
Sequence: privAccountASequence + 1, Sequence: privAccountASequence + 1,
}, },
}, },
Outputs: []types.TxOutput{ Outputs: []types.TxOutput{
types.TxOutput{ types.TxOutput{
Address: privAccountB.Account.PubKey.Address(), Address: privAccountB.Account.PubKey.Address(),
Amount: 1, Coins: types.Coins{{"", 1}},
}, },
}, },
} }

View File

@ -9,7 +9,7 @@ import (
type Account struct { type Account struct {
PubKey crypto.PubKey `json:"pub_key"` // May be nil, if not known. PubKey crypto.PubKey `json:"pub_key"` // May be nil, if not known.
Sequence int `json:"sequence"` Sequence int `json:"sequence"`
Balance int64 `json:"balance"` Balance Coins `json:"coins"`
} }
func (acc *Account) Copy() *Account { func (acc *Account) Copy() *Account {

130
types/coin.go Normal file
View File

@ -0,0 +1,130 @@
package types
import (
"fmt"
"strings"
)
type Coin struct {
Denom string `json:"denom"`
Amount int64 `json:"amount"`
}
func (coin Coin) String() string {
return fmt.Sprintf("(%v %v)",
coin.Denom, coin.Amount)
}
//----------------------------------------
type Coins []Coin
// Must be sorted, and not have 0 amounts
func (coins Coins) IsValid() bool {
switch len(coins) {
case 0:
return true
case 1:
return coins[0].Amount != 0
default:
lowDenom := coins[0].Denom
for _, coin := range coins[1:] {
if coin.Denom <= lowDenom {
return false
}
if coin.Amount == 0 {
return false
}
}
return true
}
}
func (coinsA Coins) Plus(coinsB Coins) Coins {
sum := []Coin{}
indexA, indexB := 0, 0
lenA, lenB := len(coinsA), len(coinsB)
for {
if indexA == lenA {
if indexB == lenB {
return sum
} else {
return append(sum, coinsB[indexB:]...)
}
} else if indexB == lenB {
return append(sum, coinsA[indexA:]...)
}
coinA, coinB := coinsA[indexA], coinsB[indexB]
switch strings.Compare(coinA.Denom, coinB.Denom) {
case -1:
sum = append(sum, coinA)
indexA += 1
case 0:
if coinA.Amount+coinB.Amount == 0 {
// ignore 0 sum coin type
} else {
sum = append(sum, Coin{
Denom: coinA.Denom,
Amount: coinA.Amount + coinB.Amount,
})
}
indexA += 1
indexB += 1
case 1:
sum = append(sum, coinB)
indexB += 1
}
}
return sum
}
func (coins Coins) Negative() Coins {
res := make([]Coin, 0, len(coins))
for _, coin := range coins {
res = append(res, Coin{
Denom: coin.Denom,
Amount: -coin.Amount,
})
}
return res
}
func (coinsA Coins) Minus(coinsB Coins) Coins {
return coinsA.Plus(coinsB.Negative())
}
func (coinsA Coins) IsGTE(coinsB Coins) bool {
diff := coinsA.Minus(coinsB)
if len(diff) == 0 {
return true
}
return diff.IsPositive()
}
func (coins Coins) IsZero() bool {
return len(coins) == 0
}
func (coinsA Coins) IsEqual(coinsB Coins) bool {
if len(coinsA) != len(coinsB) {
return false
}
for i := 0; i < len(coinsA); i++ {
if coinsA[i] != coinsB[i] {
return false
}
}
return true
}
func (coins Coins) IsPositive() bool {
if len(coins) == 0 {
return false
}
for _, coinAmount := range coins {
if coinAmount.Amount <= 0 {
return false
}
}
return true
}

67
types/coin_test.go Normal file
View File

@ -0,0 +1,67 @@
package types
import (
"testing"
)
func TestCoins(t *testing.T) {
coins := Coins{
Coin{"GAS", 1},
Coin{"MINERAL", 1},
Coin{"TREE", 1},
}
if !coins.IsValid() {
t.Fatal("Coins are valid")
}
if !coins.IsPositive() {
t.Fatalf("Expected coins to be positive: %v", coins)
}
negCoins := coins.Negative()
if negCoins.IsPositive() {
t.Fatalf("Expected neg coins to not be positive: %v", negCoins)
}
sumCoins := coins.Plus(negCoins)
if len(sumCoins) != 0 {
t.Fatal("Expected 0 coins")
}
}
func TestCoinsBadSort(t *testing.T) {
coins := Coins{
Coin{"TREE", 1},
Coin{"GAS", 1},
Coin{"MINERAL", 1},
}
if coins.IsValid() {
t.Fatal("Coins are not sorted")
}
}
func TestCoinsBadAmount(t *testing.T) {
coins := Coins{
Coin{"GAS", 1},
Coin{"TREE", 0},
Coin{"MINERAL", 1},
}
if coins.IsValid() {
t.Fatal("Coins cannot include 0 amounts")
}
}
func TestCoinsDuplicate(t *testing.T) {
coins := Coins{
Coin{"GAS", 1},
Coin{"GAS", 1},
Coin{"MINERAL", 1},
}
if coins.IsValid() {
t.Fatal("Duplicate coin")
}
}

View File

@ -5,8 +5,6 @@ import (
) )
// Value is any floating value. It must be given to someone. // Value is any floating value. It must be given to someone.
// Gas is a pointer to remainig gas. Decrement as you go,
// 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)
RunTx(ctx CallContext, txBytes []byte) (res tmsp.Result) RunTx(ctx CallContext, txBytes []byte) (res tmsp.Result)
@ -25,16 +23,14 @@ type NamedPlugin struct {
type CallContext struct { type CallContext struct {
Cache AccountCacher Cache AccountCacher
Caller *Account Caller *Account
Value int64 Coins Coins
Gas *int64
} }
func NewCallContext(cache AccountCacher, caller *Account, value int64, gas *int64) CallContext { func NewCallContext(cache AccountCacher, caller *Account, coins Coins) CallContext {
return CallContext{ return CallContext{
Cache: cache, Cache: cache,
Caller: caller, Caller: caller,
Value: value, Coins: coins,
Gas: gas,
} }
} }

View File

@ -42,7 +42,7 @@ var _ = wire.RegisterInterface(
type TxInput struct { type TxInput struct {
Address []byte `json:"address"` // Hash of the PubKey Address []byte `json:"address"` // Hash of the PubKey
Amount int64 `json:"amount"` // Must not exceed account balance 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"` // May be nil
@ -50,42 +50,50 @@ type TxInput struct {
func (txIn TxInput) ValidateBasic() tmsp.Result { func (txIn TxInput) ValidateBasic() tmsp.Result {
if len(txIn.Address) != 20 { if len(txIn.Address) != 20 {
return tmsp.ErrBaseInvalidAddress.AppendLog("(in TxInput)") return tmsp.ErrBaseInvalidInput.AppendLog("Invalid address length")
} }
if txIn.Amount == 0 { if !txIn.Coins.IsValid() {
return tmsp.ErrBaseInvalidAmount.AppendLog("(in TxInput)") return tmsp.ErrBaseInvalidInput.AppendLog(Fmt("Invalid coins %v", txIn.Coins))
}
if txIn.Coins.IsZero() {
return tmsp.ErrBaseInvalidInput.AppendLog("Coins cannot be zero")
} }
return tmsp.OK return tmsp.OK
} }
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.Coins, txIn.Sequence, txIn.Signature, txIn.PubKey)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type TxOutput struct { type TxOutput struct {
Address []byte `json:"address"` // Hash of the PubKey Address []byte `json:"address"` // Hash of the PubKey
Amount int64 `json:"amount"` // The sum of all outputs must not exceed the inputs. Coins Coins `json:"coins"` //
} }
func (txOut TxOutput) ValidateBasic() tmsp.Result { func (txOut TxOutput) ValidateBasic() tmsp.Result {
if len(txOut.Address) != 20 { if len(txOut.Address) != 20 {
return tmsp.ErrBaseInvalidAddress.AppendLog("(in TxOutput)") return tmsp.ErrBaseInvalidOutput.AppendLog("Invalid address length")
} }
if txOut.Amount == 0 { if !txOut.Coins.IsValid() {
return tmsp.ErrBaseInvalidAmount.AppendLog("(in TxOutput)") return tmsp.ErrBaseInvalidOutput.AppendLog(Fmt("Invalid coins %v", txOut.Coins))
}
if txOut.Coins.IsZero() {
return tmsp.ErrBaseInvalidOutput.AppendLog("Coins cannot be zero")
} }
return tmsp.OK return tmsp.OK
} }
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.Coins)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type SendTx struct { type SendTx struct {
Fee int64 `json:"fee"` // Fee
Gas int64 `json:"gas"` // Gas
Inputs []TxInput `json:"inputs"` Inputs []TxInput `json:"inputs"`
Outputs []TxOutput `json:"outputs"` Outputs []TxOutput `json:"outputs"`
} }
@ -105,16 +113,16 @@ func (tx *SendTx) SignBytes(chainID string) []byte {
} }
func (tx *SendTx) String() string { func (tx *SendTx) String() string {
return Fmt("SendTx{%v -> %v}", tx.Inputs, tx.Outputs) return Fmt("SendTx{%v/%v %v->%v}", tx.Fee, tx.Gas, tx.Inputs, tx.Outputs)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
type AppTx struct { type AppTx struct {
Type byte `json:"type"` // Which app Fee int64 `json:"fee"` // Fee
Gas int64 `json:"gas"` Gas int64 `json:"gas"` // Gas
Fee int64 `json:"fee"` Type byte `json:"type"` // Which app
Input TxInput `json:"input"` Input TxInput `json:"input"` // Hmmm do we want coins?
Data []byte `json:"data"` Data []byte `json:"data"`
} }
@ -128,7 +136,7 @@ func (tx *AppTx) SignBytes(chainID string) []byte {
} }
func (tx *AppTx) String() string { func (tx *AppTx) String() string {
return Fmt("AppTx{%v %v %v %v -> %X}", tx.Type, tx.Gas, tx.Fee, tx.Input, tx.Data) return Fmt("AppTx{%v/%v %v %v %X}", tx.Fee, tx.Gas, tx.Type, tx.Input, tx.Data)
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------

View File

@ -10,32 +10,34 @@ var chainID string = "test_chain"
func TestSendTxSignable(t *testing.T) { func TestSendTxSignable(t *testing.T) {
sendTx := &SendTx{ sendTx := &SendTx{
Fee: 111,
Gas: 222,
Inputs: []TxInput{ Inputs: []TxInput{
TxInput{ TxInput{
Address: []byte("input1"), Address: []byte("input1"),
Amount: 12345, Coins: Coins{{"", 12345}},
Sequence: 67890, Sequence: 67890,
}, },
TxInput{ TxInput{
Address: []byte("input2"), Address: []byte("input2"),
Amount: 111, Coins: Coins{{"", 111}},
Sequence: 222, Sequence: 222,
}, },
}, },
Outputs: []TxOutput{ Outputs: []TxOutput{
TxOutput{ TxOutput{
Address: []byte("output1"), Address: []byte("output1"),
Amount: 333, Coins: Coins{{"", 333}},
}, },
TxOutput{ TxOutput{
Address: []byte("output2"), Address: []byte("output2"),
Amount: 444, Coins: Coins{{"", 444}},
}, },
}, },
} }
signBytes := sendTx.SignBytes(chainID) signBytes := sendTx.SignBytes(chainID)
signBytesHex := Fmt("%X", signBytes) signBytesHex := Fmt("%X", signBytes)
expected := "010A746573745F636861696E0101020106696E7075743100000000000030390301093200000106696E70757432000000000000006F01DE0000010201076F757470757431000000000000014D01076F75747075743200000000000001BC" expected := "010A746573745F636861696E01000000000000006F00000000000000DE01020106696E7075743101010000000000000030390301093200000106696E70757432010100000000000000006F01DE0000010201076F757470757431010100000000000000014D01076F75747075743201010000000000000001BC"
if signBytesHex != expected { if signBytesHex != 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, signBytesHex)
} }
@ -43,19 +45,19 @@ func TestSendTxSignable(t *testing.T) {
func TestAppTxSignable(t *testing.T) { func TestAppTxSignable(t *testing.T) {
callTx := &AppTx{ callTx := &AppTx{
Fee: 111,
Gas: 222,
Type: 0x01, Type: 0x01,
Gas: 111,
Fee: 222,
Input: TxInput{ Input: TxInput{
Address: []byte("input1"), Address: []byte("input1"),
Amount: 12345, Coins: Coins{{"", 12345}},
Sequence: 67890, Sequence: 67890,
}, },
Data: []byte("data1"), Data: []byte("data1"),
} }
signBytes := callTx.SignBytes(chainID) signBytes := callTx.SignBytes(chainID)
signBytesHex := Fmt("%X", signBytes) signBytesHex := Fmt("%X", signBytes)
expected := "010A746573745F636861696E0101000000000000006F00000000000000DE0106696E70757431000000000000303903010932000001056461746131" expected := "010A746573745F636861696E01000000000000006F00000000000000DE010106696E70757431010100000000000000303903010932000001056461746131"
if signBytesHex != expected { if signBytesHex != expected {
t.Errorf("Got unexpected sign string for AppTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex) t.Errorf("Got unexpected sign string for AppTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex)
} }