Skip to content

Commit

Permalink
feature: accurate tx fee calculation with unittests
Browse files Browse the repository at this point in the history
  • Loading branch information
randomshinichi committed Oct 10, 2019
1 parent 40e2171 commit ba9403b
Show file tree
Hide file tree
Showing 10 changed files with 412 additions and 268 deletions.
8 changes: 5 additions & 3 deletions aeternity/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"math/big"
"time"

"github.com/aeternity/aepp-sdk-go/transactions"
"github.com/aeternity/aepp-sdk-go/v5/account"
"github.com/aeternity/aepp-sdk-go/v5/config"
)
Expand Down Expand Up @@ -36,9 +37,10 @@ func ExampleContext() {
}

// Optional: minimize the fee to save money!
est, _ := tx.FeeEstimate()
fmt.Println("Estimated vs Actual Fee:", est, tx.Fee)
tx.Fee = est
err = transactions.CalculateFee(tx)
if err != nil {
fmt.Println("Could not calculate the transaction fee", err)
}

_, _, _, _, _, err = SignBroadcastWaitTransaction(tx, alice, node, config.Node.NetworkID, 10)
if err != nil {
Expand Down
6 changes: 0 additions & 6 deletions integration_test/spend_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package integrationtest

import (
"fmt"
"math/big"
"testing"

Expand Down Expand Up @@ -40,11 +39,6 @@ func TestSpendTx(t *testing.T) {
t.Error(err)
}

// minimize the fee to save money!
est, _ := tx.FeeEstimate()
fmt.Println("Estimated vs Actual Fee:", est, tx.Fee)
tx.Fee = est

// sign the transaction, output params for debugging
_, hash, _, err := aeternity.SignBroadcastTransaction(tx, alice, node, networkID)
if err != nil {
Expand Down
113 changes: 77 additions & 36 deletions transactions/transactions.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,17 @@ type Transaction interface {
rlp.Encoder
}

// TransactionFeeCalculable is a set of methods that simplify calculating the tx
// fee. In particular, SetFee and GetFee let the code increase the fee further
// in case the newer, calculated fee ends up increasing the size of the
// transaction (and thus necessitates an even larger fee)
type TransactionFeeCalculable interface {
Transaction
SetFee(*big.Int)
GetFee() *big.Int
GetGasLimit() *big.Int
}

// calculateSignature calculates the signature of the SignedTx.Tx. Although it does not use
// the SignedTx itself, it takes a SignedTx as an argument because if it took a
// rlp.Encoder as an interface, one might expect the signature to be of the
Expand Down Expand Up @@ -246,52 +257,82 @@ func buildPointers(pointers []string) (ptrs []*NamePointer, err error) {
return
}

func calcFeeStd(tx rlp.Encoder, txLen int) *big.Int {
// (config.Client.BaseGas + len(txRLP) * config.Client.GasPerByte) * config.Client.GasPrice
// txLenGasPerByte
fee := new(big.Int)
txLenGasPerByte := new(big.Int)
// calcSizeEstimate returns the size of the transaction when RLP serialized, assuming the Fee has a length of 8 bytes.
func calcSizeEstimate(tx TransactionFeeCalculable) (estSize int64, err error) {
feeRlpLen, err := calcRlpLen(tx.GetFee())
if err != nil {
return
}

txLenGasPerByte.Mul(utils.NewIntFromUint64(uint64(txLen)), config.Client.GasPerByte)
fee.Add(config.Client.BaseGas, txLenGasPerByte)
fee.Mul(fee, config.Client.GasPrice)
return fee
}
txRlpLen, err := calcRlpLen(tx)
if err != nil {
return
}

func calcFeeContract(gas *big.Int, baseGasMultiplier int64, length int) *big.Int {
// (config.Client.BaseGas * 5) + gaslimit + (len(txRLP) * config.Client.GasPerByte) * config.Client.GasPrice
// baseGas5 txLenGasPerByte
baseGas5 := new(big.Int)
txLenBig := new(big.Int)
answer := new(big.Int)

baseGas5.Mul(config.Client.BaseGas, big.NewInt(baseGasMultiplier))
txLenBig.SetUint64(uint64(length))
txLenGasPerByte := new(big.Int)
txLenGasPerByte.Mul(txLenBig, config.Client.GasPerByte)

answer.Add(baseGas5, gas)
answer.Add(answer, txLenGasPerByte)
answer.Mul(answer, config.Client.GasPrice)
return answer
estSize = int64(txRlpLen - feeRlpLen + 8)
return
}

// sizeEstimate returns the size of the transaction when RLP serialized, assuming the Fee has a length of 8 bytes.
func calcSizeEstimate(tx rlp.Encoder, fee *big.Int) (int, error) {
feeRlp, err := rlp.EncodeToBytes(fee)
func calcRlpLen(o interface{}) (size int, err error) {
rlpEncoded, err := rlp.EncodeToBytes(o)
if err != nil {
return 0, err
return
}
feeRlpLen := len(feeRlp)
size = len(rlpEncoded)
return
}

w := &bytes.Buffer{}
err = rlp.Encode(w, tx)
func baseGasForTxType(tx Transaction) (baseGas *big.Int) {
baseGas = big.NewInt(0)
switch tx.(type) {
case *ContractCreateTx, *GAAttachTx, *GAMetaTx:
return baseGas.Mul(big.NewInt(5), config.Client.BaseGas)
case *ContractCallTx:
return baseGas.Mul(big.NewInt(30), config.Client.BaseGas)
default:
return config.Client.BaseGas
}
}

func calcFee(tx TransactionFeeCalculable) (fee *big.Int, err error) {
gas := big.NewInt(0)
// baseGas + len(tx)*GasPerByte + contractExecutionGas
baseGas := baseGasForTxType(tx)
gas.Add(gas, baseGas)

s, err := calcSizeEstimate(tx)
if err != nil {
return 0, err
return
}
txLenGasPerByte := big.NewInt(s)
txLenGasPerByte.Mul(txLenGasPerByte, config.Client.GasPerByte)
gas.Add(gas, txLenGasPerByte)

rlpRawMsg := w.Bytes()
return len(rlpRawMsg) - feeRlpLen + 8, nil
gas.Add(gas, tx.GetGasLimit())

fee = new(big.Int)
fee = gas.Mul(gas, config.Client.GasPrice)

tx.SetFee(fee)
return
}

// CalculateFee calculates the required transaction fee, and increases the fee
// further in case the newer fee ends up increasing the transaction size.
func CalculateFee(tx TransactionFeeCalculable) (err error) {
var fee, newFee *big.Int
for {
fee = tx.GetFee()
newFee, err = calcFee(tx)
if err != nil {
break
}

if fee.Cmp(newFee) == 0 {
break
}
}
return
}

// SerializeTx takes a Tx, runs its RLP() method, and base encodes the result.
Expand Down
149 changes: 149 additions & 0 deletions transactions/transactions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/aeternity/aepp-sdk-go/v5/account"
"github.com/aeternity/aepp-sdk-go/v5/config"

"github.com/aeternity/aepp-sdk-go/v5/binary"
"github.com/aeternity/aepp-sdk-go/v5/utils"
Expand Down Expand Up @@ -373,6 +374,154 @@ func Test_readIDTag(t *testing.T) {
}
}

func Test_CalculateFee(t *testing.T) {
tests := []TransactionFeeCalculable{
&SpendTx{
SenderID: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
RecipientID: "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v",
Amount: big.NewInt(10),
Fee: big.NewInt(10),
Payload: []byte("Hello World"),
TTL: uint64(10),
Nonce: uint64(1),
},
&NamePreclaimTx{
AccountID: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
CommitmentID: "cm_2jrPGyFKCEFFrsVvQsUzfnSURV5igr2WxvMR679S5DnuFEjet4", // name: fdsa.test, salt: 12345
Fee: big.NewInt(10),
TTL: uint64(10),
AccountNonce: uint64(1),
},
&NameClaimTx{
AccountID: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
Name: "fdsa.test",
NameSalt: utils.RequireIntFromString("9795159241593061970"),
Fee: utils.NewIntFromUint64(10),
TTL: uint64(10),
AccountNonce: uint64(1),
},
&NameUpdateTx{
AccountID: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
NameID: "nm_ie148R2qZYBfo1Ek3sZpfTLwBhkkqCRKi2Ce8JJ7yyWVRw2Sb", // fdsa.test
Pointers: []*NamePointer{
&NamePointer{Key: "account_pubkey", ID: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi"},
},
NameTTL: uint64(0),
ClientTTL: uint64(6),
Fee: utils.NewIntFromUint64(1),
TTL: 5,
AccountNonce: 5,
},
&NameTransferTx{
AccountID: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
NameID: "nm_ie148R2qZYBfo1Ek3sZpfTLwBhkkqCRKi2Ce8JJ7yyWVRw2Sb", // fdsa.test
RecipientID: "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v",
Fee: utils.NewIntFromUint64(1),
TTL: 5,
AccountNonce: 5,
},
&NameRevokeTx{
AccountID: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
NameID: "nm_ie148R2qZYBfo1Ek3sZpfTLwBhkkqCRKi2Ce8JJ7yyWVRw2Sb", // fdsa.test
Fee: utils.NewIntFromUint64(1),
TTL: 5,
AccountNonce: 5,
},
&OracleRegisterTx{
AccountID: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
AccountNonce: uint64(1),
QuerySpec: "query Specification",
ResponseSpec: "response Specification",
QueryFee: config.Client.Oracles.QueryFee,
OracleTTLType: 0,
OracleTTLValue: uint64(100),
AbiVersion: 1,
Fee: utils.RequireIntFromString("200000000000000"),
TTL: 500,
},
&OracleExtendTx{
OracleID: "ok_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
AccountNonce: 1,
OracleTTLType: 0,
OracleTTLValue: 300,
Fee: utils.NewIntFromUint64(10),
TTL: 0,
},
&OracleQueryTx{
SenderID: "ak_Egp9yVdpxmvAfQ7vsXGvpnyfNq71msbdUpkMNYGTeTe8kPL3v",
AccountNonce: uint64(1),
OracleID: "ok_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
Query: "Are you okay?",
QueryFee: utils.NewIntFromUint64(0),
QueryTTLType: 0,
QueryTTLValue: 300,
ResponseTTLType: 0,
ResponseTTLValue: 300,
Fee: utils.RequireIntFromString("200000000000000"),
TTL: 500,
},
&OracleRespondTx{
OracleID: "ok_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
AccountNonce: uint64(1),
QueryID: "oq_2NhMjBdKHJYnQjDbAxanmxoXiSiWDoG9bqDgk2MfK2X6AB9Bwx",
Response: "Hello back",
ResponseTTLType: 0,
ResponseTTLValue: 100,
Fee: config.Client.Fee,
TTL: config.Client.TTL,
},
&ContractCreateTx{
OwnerID: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
AccountNonce: 1,
// encoded "contract Identity =\n type state = ()\n function main(z : int) = z"
Code: `cb_+QP1RgKgpVq1Ib2r2ug+UktHvfWSQ8P35HJQHM6qikqBu1DwgtT5Avv5ASqgaPJnYzj/UIg5q6R3Se/6i+h+8oTyB/s9mZhwHNU4h8WEbWFpbrjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKD//////////////////////////////////////////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAuEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA+QHLoLnJVvKLMUmp9Zh6pQXz2hsiCcxXOSNABiu2wb2fn5nqhGluaXS4YAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP//////////////////////////////////////////7kBQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEA//////////////////////////////////////////8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA///////////////////////////////////////////uMxiAABkYgAAhJGAgIBRf7nJVvKLMUmp9Zh6pQXz2hsiCcxXOSNABiu2wb2fn5nqFGIAAMBXUIBRf2jyZ2M4/1CIOaukd0nv+ovofvKE8gf7PZmYcBzVOIfFFGIAAK9XUGABGVEAW2AAGVlgIAGQgVJgIJADYAOBUpBZYABRWVJgAFJgAPNbYACAUmAA81tZWWAgAZCBUmAgkANgABlZYCABkIFSYCCQA2ADgVKBUpBWW2AgAVFRWVCAkVBQgJBQkFZbUFCCkVBQYgAAjFaFMi4xLjBJtQib`,
VMVersion: 4,
AbiVersion: 1,
Deposit: config.Client.Contracts.Deposit,
Amount: config.Client.Contracts.Amount,
GasLimit: config.Client.Contracts.GasLimit,
GasPrice: config.Client.GasPrice,
Fee: config.Client.Fee,
TTL: config.Client.TTL,
CallData: "cb_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACC5yVbyizFJqfWYeqUF89obIgnMVzkjQAYrtsG9n5+Z6gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnHQYrA==",
},
&ContractCallTx{
CallerID: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
AccountNonce: uint64(2),
ContractID: "ct_2pfWWzeRzWSdm68HXZJn61KhxdsBA46wzYgvo1swkdJZij1rKm",
Amount: config.Client.Contracts.Amount,
GasLimit: config.Client.Contracts.GasLimit,
GasPrice: config.Client.GasPrice,
AbiVersion: config.Client.Contracts.ABIVersion,
CallData: "cb_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDiIx1s38k5Ft5Ms6mFe/Zc9A/CVvShSYs/fnyYDBmTRAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACo7j+li",
Fee: config.Client.Fee,
TTL: config.Client.TTL,
},
&GAAttachTx{
OwnerID: "ak_oeoYuVx1wmPxSADDCY6GFVorfJHFYBKia9KonSiWjtbvNQv9Y",
AccountNonce: 1,
Code: "cb_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9246K2XN/CAWn0QFGSjvFGIAAYlXUIBRf2vehD2STuvi98Sjn0btXkSUDTn62IZgndZlXQVNK20SFGIAAg5XUGABGVEAW2AAGVlgIAGQgVJgIJADYABZkIFSgVJZYCABkIFSYCCQA2AAWZCBUoFSWWAgAZCBUmAgkANgA4FSkFlgAFFZUmAAUmAA81tgAIBSYADzW2AA/ZBQkFZbYCABUVGQUFlQgJFQUGAAYABgAGEB9FmQgVJgAGAAWvGAUWAAFGIAASBXgFFgARRiAAFZV1BgARlRAFtQf05vdCBpbiBBdXRoIGNvbnRleHQAAAAAAAAAAAAAAAAAWWAgAZCBUmAgkANgE4FSkFBiAADdVltgIAFRYABRgGAgAVFZYCABkIFSYCCQA2ABYABRUQGBUpBQYABSWVBgAZBQkFCQVltgIAFRgFGQYCABUZFQWVCAgpJQklBQYABgAGAAg1lgIAGQgVJgIJADhYFSWWBAAZCBUmAgkANgABlZYCABkIFSYCCQA2AAWZCBUoFSWWAgAZCBUmAgkANgAFmQgVKBUllgIAGQgVJgIJADYAOBUoFSYCCQA2EBk4FSYABgAFrxkVBQkFZbYCABUVGDklCAkVBQgFlgIAGQgVJgIJADYAGBUllgIAGQgVJgIJADYAAZWWAgAZCBUmAgkANgAFmQgVKBUllgIAGQgVJgIJADYABZkIFSgVJZYCABkIFSYCCQA2ADgVKBUpBQkFaFMy4xLjA3jzdH",
AuthFunc: []byte{96, 0, 184, 90, 183, 93, 180, 49, 139, 122, 45, 0, 88, 192, 16, 175, 167, 85, 183, 183, 4, 214, 35, 12, 90, 196, 201, 236, 49, 59, 255, 71},
VMVersion: 4,
AbiVersion: 1,
GasLimit: big.NewInt(500),
GasPrice: big.NewInt(1000000000),
Fee: big.NewInt(126720000000000),
TTL: 0,
CallData: "cb_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACBr3oQ9kk7r4vfEo59G7V5ElA05+tiGYJ3WZV0FTSttEgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABgae21UoMYpb0U8XZCTsTvUGUNeF/kxvl/87SxMDOBYASarBTN",
},
}
for _, tt := range tests {
ttType := reflect.TypeOf(tt).String()
t.Run(ttType, func(t *testing.T) {
err := CalculateFee(tt)
if err != nil {
t.Error(err)
}
})
}
}

func ExampleSerializeTx() {
tx := &SpendTx{
SenderID: "ak_2a1j2Mk9YSmC1gioUq4PWRm3bsv887MbuRVwyv4KaUGoR1eiKi",
Expand Down
Loading

0 comments on commit ba9403b

Please sign in to comment.