Coins and fees and gas...
This commit is contained in:
+1
-1
@@ -9,7 +9,7 @@ import (
|
||||
type Account struct {
|
||||
PubKey crypto.PubKey `json:"pub_key"` // May be nil, if not known.
|
||||
Sequence int `json:"sequence"`
|
||||
Balance int64 `json:"balance"`
|
||||
Balance Coins `json:"coins"`
|
||||
}
|
||||
|
||||
func (acc *Account) Copy() *Account {
|
||||
|
||||
+130
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
+3
-7
@@ -5,8 +5,6 @@ import (
|
||||
)
|
||||
|
||||
// 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 {
|
||||
SetOption(key string, value string) (log string)
|
||||
RunTx(ctx CallContext, txBytes []byte) (res tmsp.Result)
|
||||
@@ -25,16 +23,14 @@ type NamedPlugin struct {
|
||||
type CallContext struct {
|
||||
Cache AccountCacher
|
||||
Caller *Account
|
||||
Value int64
|
||||
Gas *int64
|
||||
Coins Coins
|
||||
}
|
||||
|
||||
func NewCallContext(cache AccountCacher, caller *Account, value int64, gas *int64) CallContext {
|
||||
func NewCallContext(cache AccountCacher, caller *Account, coins Coins) CallContext {
|
||||
return CallContext{
|
||||
Cache: cache,
|
||||
Caller: caller,
|
||||
Value: value,
|
||||
Gas: gas,
|
||||
Coins: coins,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
-16
@@ -42,7 +42,7 @@ var _ = wire.RegisterInterface(
|
||||
|
||||
type TxInput struct {
|
||||
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
|
||||
Signature crypto.Signature `json:"signature"` // Depends on the PubKey type and the whole Tx
|
||||
PubKey crypto.PubKey `json:"pub_key"` // May be nil
|
||||
@@ -50,42 +50,50 @@ type TxInput struct {
|
||||
|
||||
func (txIn TxInput) ValidateBasic() tmsp.Result {
|
||||
if len(txIn.Address) != 20 {
|
||||
return tmsp.ErrBaseInvalidAddress.AppendLog("(in TxInput)")
|
||||
return tmsp.ErrBaseInvalidInput.AppendLog("Invalid address length")
|
||||
}
|
||||
if txIn.Amount == 0 {
|
||||
return tmsp.ErrBaseInvalidAmount.AppendLog("(in TxInput)")
|
||||
if !txIn.Coins.IsValid() {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
if len(txOut.Address) != 20 {
|
||||
return tmsp.ErrBaseInvalidAddress.AppendLog("(in TxOutput)")
|
||||
return tmsp.ErrBaseInvalidOutput.AppendLog("Invalid address length")
|
||||
}
|
||||
if txOut.Amount == 0 {
|
||||
return tmsp.ErrBaseInvalidAmount.AppendLog("(in TxOutput)")
|
||||
if !txOut.Coins.IsValid() {
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
Fee int64 `json:"fee"` // Fee
|
||||
Gas int64 `json:"gas"` // Gas
|
||||
Inputs []TxInput `json:"inputs"`
|
||||
Outputs []TxOutput `json:"outputs"`
|
||||
}
|
||||
@@ -105,16 +113,16 @@ func (tx *SendTx) SignBytes(chainID string) []byte {
|
||||
}
|
||||
|
||||
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 byte `json:"type"` // Which app
|
||||
Gas int64 `json:"gas"`
|
||||
Fee int64 `json:"fee"`
|
||||
Input TxInput `json:"input"`
|
||||
Fee int64 `json:"fee"` // Fee
|
||||
Gas int64 `json:"gas"` // Gas
|
||||
Type byte `json:"type"` // Which app
|
||||
Input TxInput `json:"input"` // Hmmm do we want coins?
|
||||
Data []byte `json:"data"`
|
||||
}
|
||||
|
||||
@@ -128,7 +136,7 @@ func (tx *AppTx) SignBytes(chainID string) []byte {
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
+11
-9
@@ -10,32 +10,34 @@ var chainID string = "test_chain"
|
||||
|
||||
func TestSendTxSignable(t *testing.T) {
|
||||
sendTx := &SendTx{
|
||||
Fee: 111,
|
||||
Gas: 222,
|
||||
Inputs: []TxInput{
|
||||
TxInput{
|
||||
Address: []byte("input1"),
|
||||
Amount: 12345,
|
||||
Coins: Coins{{"", 12345}},
|
||||
Sequence: 67890,
|
||||
},
|
||||
TxInput{
|
||||
Address: []byte("input2"),
|
||||
Amount: 111,
|
||||
Coins: Coins{{"", 111}},
|
||||
Sequence: 222,
|
||||
},
|
||||
},
|
||||
Outputs: []TxOutput{
|
||||
TxOutput{
|
||||
Address: []byte("output1"),
|
||||
Amount: 333,
|
||||
Coins: Coins{{"", 333}},
|
||||
},
|
||||
TxOutput{
|
||||
Address: []byte("output2"),
|
||||
Amount: 444,
|
||||
Coins: Coins{{"", 444}},
|
||||
},
|
||||
},
|
||||
}
|
||||
signBytes := sendTx.SignBytes(chainID)
|
||||
signBytesHex := Fmt("%X", signBytes)
|
||||
expected := "010A746573745F636861696E0101020106696E7075743100000000000030390301093200000106696E70757432000000000000006F01DE0000010201076F757470757431000000000000014D01076F75747075743200000000000001BC"
|
||||
expected := "010A746573745F636861696E01000000000000006F00000000000000DE01020106696E7075743101010000000000000030390301093200000106696E70757432010100000000000000006F01DE0000010201076F757470757431010100000000000000014D01076F75747075743201010000000000000001BC"
|
||||
if signBytesHex != expected {
|
||||
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) {
|
||||
callTx := &AppTx{
|
||||
Fee: 111,
|
||||
Gas: 222,
|
||||
Type: 0x01,
|
||||
Gas: 111,
|
||||
Fee: 222,
|
||||
Input: TxInput{
|
||||
Address: []byte("input1"),
|
||||
Amount: 12345,
|
||||
Coins: Coins{{"", 12345}},
|
||||
Sequence: 67890,
|
||||
},
|
||||
Data: []byte("data1"),
|
||||
}
|
||||
signBytes := callTx.SignBytes(chainID)
|
||||
signBytesHex := Fmt("%X", signBytes)
|
||||
expected := "010A746573745F636861696E0101000000000000006F00000000000000DE0106696E70757431000000000000303903010932000001056461746131"
|
||||
expected := "010A746573745F636861696E01000000000000006F00000000000000DE010106696E70757431010100000000000000303903010932000001056461746131"
|
||||
if signBytesHex != expected {
|
||||
t.Errorf("Got unexpected sign string for AppTx. Expected:\n%v\nGot:\n%v", expected, signBytesHex)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user