gecko/vms/spdagvm/vm_test.go

811 lines
24 KiB
Go

// (c) 2019-2020, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package spdagvm
import (
"math"
"testing"
"github.com/ava-labs/gecko/database/memdb"
"github.com/ava-labs/gecko/ids"
"github.com/ava-labs/gecko/snow"
"github.com/ava-labs/gecko/snow/consensus/snowstorm"
"github.com/ava-labs/gecko/snow/engine/common"
"github.com/ava-labs/gecko/utils/crypto"
"github.com/ava-labs/gecko/utils/formatting"
"github.com/ava-labs/gecko/utils/units"
)
var keys []*crypto.PrivateKeySECP256K1R
var ctx *snow.Context
var avaChainID = ids.NewID([32]byte{'y', 'e', 'e', 't'})
var defaultInitBalances = make(map[string]uint64)
const txFeeTest = 0 // Tx fee to use for tests
const (
defaultInitBalance = uint64(5000000000) // Measured in NanoAva
)
func init() {
ctx = snow.DefaultContextTest()
ctx.ChainID = avaChainID
cb58 := formatting.CB58{}
factory := crypto.FactorySECP256K1R{}
// String reprs. of private keys. Copy-pasted from genesis.go.
for _, key := range []string{
"24jUJ9vZexUM6expyMcT48LBx27k1m7xpraoV62oSQAHdziao5",
"2MMvUMsxx6zsHSNXJdFD8yc5XkancvwyKPwpw4xUK3TCGDuNBY",
"cxb7KpGWhDMALTjNNSJ7UQkkomPesyWAPUaWRGdyeBNzR6f35",
} {
ctx.Log.AssertNoError(cb58.FromString(key))
pk, err := factory.ToPrivateKey(cb58.Bytes)
ctx.Log.AssertNoError(err)
keys = append(keys, pk.(*crypto.PrivateKeySECP256K1R))
defaultInitBalances[pk.PublicKey().Address().String()] = defaultInitBalance
}
}
// GenesisTx is the genesis transaction
// The amount given to each address is determined by [initBalances]
// [initBalances] keys are string reprs. of addresses
// [initBalances] values are the amount of NanoAva they have at genesis
func GenesisTx(initBalances map[string]uint64) *Tx {
builder := Builder{
NetworkID: 0,
ChainID: avaChainID,
}
outputs := []Output(nil)
for _, key := range keys {
addr := key.PublicKey().Address()
if balance, ok := initBalances[addr.String()]; ok {
outputs = append(outputs,
builder.NewOutputPayment(
/*amount=*/ balance,
/*locktime=*/ 0,
/*threshold=*/ 1,
/*addresses=*/ []ids.ShortID{addr},
),
)
}
}
result, _ := builder.NewTx(
/*ins=*/ nil,
/*outs=*/ outputs,
/*signers=*/ nil,
)
return result
}
func TestAva(t *testing.T) {
// Give
genesisTx := GenesisTx(defaultInitBalances)
vmDB := memdb.New()
msgChan := make(chan common.Message, 1)
vm := &VM{}
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
vm.batchTimeout = 0
builder := Builder{
NetworkID: 0,
ChainID: avaChainID,
}
tx1, err := builder.NewTx(
/*ins=*/ []Input{
builder.NewInputPayment(
/*txID=*/ genesisTx.ID(),
/*txIndex=*/ 0,
/*amount=*/ 5*units.Ava,
/*sigs=*/ []*Sig{builder.NewSig(0 /*=index*/)},
),
},
/*outs=*/ []Output{
builder.NewOutputPayment(
/*amount=*/ 3*units.Ava,
/*locktime=*/ 0,
/*threshold=*/ 0,
/*addresses=*/ nil,
),
},
/*signers=*/ []*InputSigner{
&InputSigner{Keys: []*crypto.PrivateKeySECP256K1R{
keys[1],
}},
},
)
ctx.Log.AssertNoError(err)
tx1Bytes := tx1.Bytes()
ctx.Lock.Lock()
vm.IssueTx(tx1Bytes, nil)
ctx.Lock.Unlock()
if msg := <-msgChan; msg != common.PendingTxs {
t.Fatalf("Wrong message")
}
ctx.Lock.Lock()
if txs := vm.PendingTxs(); len(txs) != 1 {
t.Fatalf("Should have returned a tx")
} else if tx := txs[0]; !tx.ID().Equals(tx1.ID()) {
t.Fatalf("Should have returned %s", tx1.ID())
}
ctx.Lock.Unlock()
tx2, err := builder.NewTx(
/*ins=*/ []Input{
builder.NewInputPayment(
/*txID=*/ tx1.ID(),
/*txIndex=*/ 0,
/*amount=*/ 3*units.Ava,
/*sigs=*/ []*Sig{},
),
},
/*outs=*/ nil,
/*signers=*/ []*InputSigner{&InputSigner{}},
)
ctx.Log.AssertNoError(err)
tx2Bytes := tx2.Bytes()
ctx.Lock.Lock()
vm.IssueTx(tx2Bytes, nil)
ctx.Lock.Unlock()
if msg := <-msgChan; msg != common.PendingTxs {
t.Fatalf("Wrong message")
}
}
func TestInvalidSpentTx(t *testing.T) {
genesisTx := GenesisTx(defaultInitBalances)
vmDB := memdb.New()
msgChan := make(chan common.Message, 1)
vm := &VM{}
ctx.Lock.Lock()
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
vm.batchTimeout = 0
builder := Builder{
NetworkID: 0,
ChainID: avaChainID,
}
tx1, _ := builder.NewTx(
/*ins=*/ []Input{
builder.NewInputPayment(
/*txID=*/ genesisTx.ID(),
/*txIndex=*/ 0,
/*amount=*/ 5*units.Ava,
/*sigs=*/ []*Sig{builder.NewSig(0 /*=index*/)},
),
},
/*outs=*/ []Output{
builder.NewOutputPayment(
/*amount=*/ 3*units.Ava,
/*locktime=*/ 0,
/*threshold=*/ 0,
/*addresses=*/ nil,
),
},
/*signers=*/ []*InputSigner{
&InputSigner{Keys: []*crypto.PrivateKeySECP256K1R{
keys[1],
}},
},
)
tx2, _ := builder.NewTx(
/*ins=*/ []Input{
builder.NewInputPayment(
/*txID=*/ genesisTx.ID(),
/*txIndex=*/ 0,
/*amount=*/ 5*units.Ava,
/*sigs=*/ []*Sig{builder.NewSig(0 /*=index*/)},
),
},
/*outs=*/ []Output{
builder.NewOutputPayment(
/*amount=*/ 2*units.Ava,
/*locktime=*/ 0,
/*threshold=*/ 0,
/*addresses=*/ nil,
),
},
/*signers=*/ []*InputSigner{
&InputSigner{Keys: []*crypto.PrivateKeySECP256K1R{
keys[1],
}},
},
)
wrappedTx1, err := vm.wrapTx(tx1, nil)
if err != nil {
t.Fatal(err)
}
if err := wrappedTx1.Verify(); err != nil {
t.Fatal(err)
}
wrappedTx1.Accept()
wrappedTx2, err := vm.wrapTx(tx2, nil)
if err != nil {
t.Fatal(err)
}
if err := wrappedTx2.Verify(); err == nil {
t.Fatalf("Should have failed verification")
}
ctx.Lock.Unlock()
}
func TestInvalidTxVerification(t *testing.T) {
genesisTx := GenesisTx(defaultInitBalances)
vmDB := memdb.New()
msgChan := make(chan common.Message, 1)
vm := &VM{}
ctx.Lock.Lock()
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
vm.batchTimeout = 0
builder := Builder{
NetworkID: 0,
ChainID: avaChainID,
}
tx, _ := builder.NewTx(
/*ins=*/ []Input{
builder.NewInputPayment(
/*txID=*/ genesisTx.ID(),
/*txIndex=*/ 2345,
/*amount=*/ 50000+txFeeTest,
/*sigs=*/ []*Sig{builder.NewSig(0 /*=index*/)},
),
},
/*outs=*/ []Output{
builder.NewOutputPayment(
/*amount=*/ 50000,
/*locktime=*/ 0,
/*threshold=*/ 0,
/*addresses=*/ nil,
),
},
/*signers=*/ []*InputSigner{
&InputSigner{Keys: []*crypto.PrivateKeySECP256K1R{
keys[1],
}},
},
)
wrappedTx, err := vm.wrapTx(tx, nil)
if err != nil {
t.Fatal(err)
}
if err := wrappedTx.Verify(); err == nil {
t.Fatalf("Should have failed verification")
}
vm.state.uniqueTx.Flush()
wrappedTx2, err := vm.wrapTx(tx, nil)
if err != nil {
t.Fatal(err)
}
if err := wrappedTx2.Verify(); err == nil {
t.Fatalf("Should have failed verification")
}
ctx.Lock.Unlock()
}
func TestRPCAPI(t *testing.T) {
// Initialize ava vm with the genesis transaction
genesisTx := GenesisTx(defaultInitBalances)
vmDB := memdb.New()
msgChan := make(chan common.Message, 1)
vm := &VM{}
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
vm.batchTimeout = 0
// Key: string repr. of an address
// Value: string repr. of the private key that controls the address
addrToPK := map[string]string{}
// Inverse of the above map
pkToAddr := map[string]string{}
pks := []string{} // List of private keys
addresses := []string{} // List of addresses controlled by the private keys
// Populate the above data structures using [keys]
for _, v := range keys {
cb58 := formatting.CB58{Bytes: v.Bytes()}
pk := cb58.String()
address := v.PublicKey().Address().String()
addrToPK[address] = pk
pkToAddr[pk] = address
pks = append(pks, pk)
addresses = append(addresses, address)
}
// Ensure GetAddress and GetBalance return the correct values for the
// addresses in the genesis transactions
for addr, pk := range addrToPK {
ctx.Lock.Lock()
if a, err := vm.GetAddress(pk); err != nil {
t.Fatalf("GetAddress(%q): %s", pk, err)
} else if a != addr {
t.Fatalf("GetAddress(%q): Addresses Not Equal(%q,%q)", pk, addr, a)
} else if balance, err := vm.GetBalance(addr, ""); err != nil {
t.Fatalf("GetBalance(%q): %s", addr, err)
} else if balance != defaultInitBalance {
t.Fatalf("GetBalance(%q,%q): Balance Not Equal(%d,%d)", addr, "", defaultInitBalance, balance)
}
ctx.Lock.Unlock()
}
// Create a new key
ctx.Lock.Lock()
addr1PrivKey, err := vm.CreateKey()
if err != nil {
t.Fatalf("CreateKey(): %s", err)
}
// The address of the key we just created
addr1, err := vm.GetAddress(addr1PrivKey)
if err != nil {
t.Fatalf("GetAddress(%q): %s", addr1PrivKey, err)
}
send1Amt := uint64(10000)
// Ensure the balance of the new address is 0
if testbal, err := vm.GetBalance(addr1, ""); err != nil {
t.Fatalf("GetBalance(%q): %s", addr1, err)
} else if testbal != 0 {
t.Fatalf("GetBalance(%q,%q): Balance Not Equal(%d,%d)", addr1, "", 0, testbal)
// The only valid asset ID is ava
} else if _, err = vm.GetBalance(addr1, "thisshouldfail"); err == nil {
t.Fatalf("GetBalance(%q): passed when it should have failed on bad assetID", addr1)
} else if _, err = vm.Send(100, "thisshouldfail", addr1, pks); err == nil || err != errAsset {
t.Fatalf("Send(%d,%q,%q,%v): passed when it should have failed on bad assetID", 100, "thisshouldfail", addr1, pks)
// Ensure we can't send more funds from this address than the address has
} else if _, err = vm.Send(4000000000000000, "", addr1, pks); err == nil || err != errInsufficientFunds {
t.Fatalf("Send(%d,%q,%q,%v): passed when it should have failed on insufficient funds", 4000000000000000, "", addr1, pks)
// Send [send1Amt] NanoAva from [pks[0]] to [addr1]
} else if _, err = vm.Send(send1Amt, "", addr1, []string{pks[0]}); err != nil {
t.Fatalf("Send(%d,%q,%q,%v): failed with error - %s", send1Amt, "", addr1, []string{pks[0]}, err)
}
ctx.Lock.Unlock()
if msg := <-msgChan; msg != common.PendingTxs {
t.Fatalf("Wrong message")
}
// There should be one pending transaction (the send we just did).
// Accept that transaction.
ctx.Lock.Lock()
if txs := vm.PendingTxs(); len(txs) != 1 {
t.Fatalf("PendingTxs(): returned wrong number of transactions - expected: %d ; returned: %d", 1, len(txs))
} else {
txs[0].Accept()
}
if txs := vm.PendingTxs(); len(txs) != 0 {
t.Fatalf("PendingTxs(): there should not have been any pending transactions")
}
send2Amt := uint64(10000)
// Ensure that the balance of the address we sent [send1Amt] to is [send1Amt]
if testbal, err := vm.GetBalance(addr1, ""); err != nil {
t.Fatalf("GetBalance(%q): %s", addr1, err)
} else if testbal != send1Amt {
t.Fatalf("GetBalance(%q): returned wrong balance - expected: %d ; returned: %d", addr1, send1Amt, testbal)
// Send [send2Amt] from [pks[0]] to [addr1]
} else if _, err = vm.Send(send1Amt, "", addr1, []string{pks[0]}); err != nil {
t.Fatalf("Send(%d,%q,%q,%v): failed with error - %s", send2Amt, "", addr1, []string{pks[0]}, err)
}
ctx.Lock.Unlock()
if msg := <-msgChan; msg != common.PendingTxs {
t.Fatalf("Wrong message")
}
// There should be one pending transaction (the send we just did).
// Accept that transaction.
ctx.Lock.Lock()
if txs := vm.PendingTxs(); len(txs) != 1 {
t.Fatalf("PendingTxs: returned wrong number of transactions - expected: %d ; returned: %d", 1, len(txs))
} else {
txs[0].Accept()
}
if txs := vm.PendingTxs(); len(txs) != 0 {
t.Fatalf("PendingTxs(): there should not have been any pending transactions")
}
// Ensure [addr1] has [send1Amt+send2Amt]
if testbal, err := vm.GetBalance(addr1, ""); err != nil {
t.Fatalf("GetBalance(%q): %s", addr1, err)
} else if testbal != send1Amt+send2Amt {
t.Fatalf("GetBalance(%q): returned wrong balance - expected: %d; returned: %d", addr1, send1Amt+send2Amt, testbal)
}
send3Amt := uint64(10000)
// Ensure the balance of the address controlled by [pks[0]] is the initial amount
// it had (from genesis) minus the 2 amounts it sent to [addr1] minus 2 tx fees
if testbal, err := vm.GetBalance(pkToAddr[pks[0]], ""); err != nil {
t.Fatalf("GetBalance(%q): %s", pkToAddr[pks[0]], err)
} else if testbal != defaultInitBalance-send1Amt-send2Amt-2*txFeeTest { // TODO generalize
t.Fatalf("GetBalance(%q): returned wrong balance - expected: %d; returned: %d", pkToAddr[pks[0]], defaultInitBalance-send1Amt-send2Amt-2*txFeeTest, testbal)
// Send [send3Amt] from [addr1] to the address controlled by [pks[0]]
} else if _, err = vm.Send(send3Amt, "", pkToAddr[pks[0]], []string{addr1PrivKey}); err != nil {
t.Fatalf("Send(%d,%q,%q,%v): failed with error - %s", send3Amt-txFeeTest, "", pkToAddr[pks[0]], []string{addr1PrivKey}, err)
}
ctx.Lock.Unlock()
if msg := <-msgChan; msg != common.PendingTxs {
t.Fatalf("Wrong message")
}
ctx.Lock.Lock()
if txs := vm.PendingTxs(); len(txs) != 1 {
t.Fatalf("PendingTxs(): returned wrong number of transactions - expected: %d; returned: %d", 1, len(txs))
} else {
txs[0].Accept()
}
if txs := vm.PendingTxs(); len(txs) != 0 {
t.Fatalf("PendingTxs(): there should not have been any pending transactions")
}
send4Amt := uint64(30000)
// Ensure the balance of the address controlled by [pk[0]] is:
// [initial balance] - [send1Amt] - [send2Amt] + [send3Amt] - 2 * [txFeeTest]
if testbal, err := vm.GetBalance(pkToAddr[pks[0]], ""); err != nil {
t.Fatalf("GetBalance(%q): %s", pkToAddr[pks[0]], err)
} else if testbal != defaultInitBalance-send1Amt-send2Amt+send3Amt-2*txFeeTest {
t.Fatalf("GetBalance(%q): returned wrong balance - expected: %d; returned: %d", pkToAddr[pks[0]], defaultInitBalance-send1Amt-send2Amt+send3Amt-2*txFeeTest, testbal)
// Send [send4Amt] to [addr1] from addresses controlled by [pks[1]] and [pks[2]]
} else if _, err = vm.Send(send4Amt, "", addr1, []string{pks[1], pks[2]}); err != nil {
t.Fatalf("Send(%d,%q,%q,%v): failed with error - %s", send4Amt, "", addr1, []string{pks[1], pks[2]}, err)
}
ctx.Lock.Unlock()
if msg := <-msgChan; msg != common.PendingTxs {
t.Fatalf("Wrong message")
}
ctx.Lock.Lock()
if txs := vm.PendingTxs(); len(txs) != 1 {
t.Fatalf("<-txChan: returned wrong number of transactions - expected: %d ; returned: %d", 1, len(txs))
} else {
txs[0].Accept()
}
if txs := vm.PendingTxs(); len(txs) != 0 {
t.Fatalf("PendingTxs(): there should not have been any pending transactions")
}
// Ensure the balance of [addr1] is:
// [send1Amt] + [send2Amt] - [send3Amt] + [send4Amt] - [txFeeTest]
if testbal, err := vm.GetBalance(addr1, ""); err != nil {
t.Fatalf("GetBalance(%q): %s", addr1, err)
} else if testbal != send1Amt+send2Amt-send3Amt+send4Amt-txFeeTest {
t.Fatalf("GetBalance(%q): returned wrong balance - expected: %d; returned: %d", addr1, send1Amt+send2Amt-send3Amt+send4Amt-txFeeTest, testbal)
}
// Ensure the sum of the balances of the addresses controlled by [pks[1]] and [pks[2]] is:
// [sum of their initial balances] - [send4Amt] - [txFeeTest]
if testbal1, err := vm.GetBalance(pkToAddr[pks[1]], ""); err != nil {
t.Fatalf("GetBalance(%q): %s", pkToAddr[pks[1]], err)
} else if testbal2, err := vm.GetBalance(pkToAddr[pks[2]], ""); err != nil {
t.Fatalf("GetBalance(%q): %s", pkToAddr[pks[2]], err)
} else if testbal1+testbal2 != defaultInitBalance*2-send4Amt-txFeeTest {
t.Fatalf("GetBalance(%q) + GetBalance(%q): returned wrong balance - expected: %d ; returned: %d", pkToAddr[pks[1]], pkToAddr[pks[2]], defaultInitBalance*2-send4Amt-txFeeTest, testbal1+testbal2)
}
ctx.Lock.Unlock()
}
func TestMultipleSend(t *testing.T) {
// Initialize the vm
genesisTx := GenesisTx(defaultInitBalances)
vmDB := memdb.New()
msgChan := make(chan common.Message, 1)
vm := &VM{}
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
// Initialize these data structures
addrToPK := map[string]string{}
pkToAddr := map[string]string{}
pks := []string{}
addresses := []string{}
for _, v := range keys {
cb58 := formatting.CB58{Bytes: v.Bytes()}
pk := cb58.String()
address := v.PublicKey().Address().String()
addrToPK[address] = pk
pkToAddr[pk] = address
pks = append(pks, pk)
addresses = append(addresses, address)
}
ctx.Lock.Lock()
// Ensure GetAddress and GetBalance return the correct values for
// the addresses mentioned in the genesis tx
for addr, pk := range addrToPK {
if a, err := vm.GetAddress(pk); err != nil {
t.Fatalf("GetAddress(%q): %s", pk, err)
} else if a != addr {
t.Fatalf("GetAddress(%q): Addresses Not Equal(%q,%q)", pk, addr, a)
// Ensure the balances of the addresses are [initAddrBalance]
} else if balance, err := vm.GetBalance(addr, ""); err != nil {
t.Fatalf("GetBalance(%q): %s", addr, err)
} else if balance != defaultInitBalance {
t.Fatalf("GetBalance(%q,%q): Balance Not Equal(%d,%d)", addr, "", defaultInitBalance, balance)
}
}
// Create a new private key
testPK, err := vm.CreateKey()
if err != nil {
t.Fatalf("CreateKey(): %s", err)
}
// Get the address controlled by the new private key
testaddr, err := vm.GetAddress(testPK)
if err != nil {
t.Fatalf("GetAddress(%q): %s", testPK, err)
}
if testbal, err := vm.GetBalance(testaddr, ""); err != nil {
t.Fatalf("GetBalance(%q): %s", testaddr, err)
} else if testbal != 0 {
// Balance of new address should be 0
t.Fatalf("GetBalance(%q,%q): Balance Not Equal(%d,%d)", testaddr, "", 0, testbal)
}
if _, err = vm.GetBalance(testaddr, "thisshouldfail"); err == nil {
t.Fatalf("GetBalance(%q): passed when it should have failed on bad assetID", testaddr)
}
if _, err = vm.Send(100, "thisshouldfail", testaddr, pks); err == nil || err != errAsset {
t.Fatalf("Send(%d,%q,%q,%v): passed when it should have failed on bad assetID", 100, "thisshouldfail", testaddr, pks)
}
if _, err = vm.Send(4000000000000000, "", testaddr, pks); err == nil || err != errInsufficientFunds {
t.Fatalf("Send(%d,%q,%q,%v): passed when it should have failed on insufficient funds", 4000000000000000, "", testaddr, pks)
}
// Send [send1Amt] and [send2Amt] from address controlled by [pks[0]] to [testAddr]
send1Amt := uint64(10000)
if _, err = vm.Send(send1Amt, "", testaddr, []string{pks[0]}); err != nil {
t.Fatalf("Send(%d,%q,%q,%v): failed with error - %s", send1Amt, "", testaddr, []string{pks[0]}, err)
}
send2Amt := uint64(10000)
if _, err = vm.Send(send2Amt, "", testaddr, []string{pks[0]}); err != nil {
t.Fatalf("Send(%d,%q,%q,%v): failed with error - %s", send2Amt, "", testaddr, []string{pks[0]}, err)
}
ctx.Lock.Unlock()
if msg := <-msgChan; msg != common.PendingTxs {
t.Fatalf("Wrong message")
}
ctx.Lock.Lock()
if txs := vm.PendingTxs(); len(txs) != 2 {
t.Fatalf("PendingTxs(): returned wrong number of transactions - expected: %d ; returned: %d", 2, len(txs))
} else if inputs1 := txs[0].InputIDs(); inputs1.Len() != 1 {
t.Fatalf("inputs1: returned wrong number of inputs - expected: %d ; returned: %d", 1, inputs1.Len())
} else if inputs2 := txs[1].InputIDs(); inputs2.Len() != 1 {
t.Fatalf("inputs2: returned wrong number of inputs - expected: %d ; returned: %d", 1, inputs2.Len())
} else if !inputs1.Overlaps(inputs2) {
t.Fatalf("inputs1 doesn't conflict with inputs2 but it should")
}
_, _, _, _, err = vm.GetTxHistory(testaddr)
if err != nil {
t.Fatalf("GetTxHistory(%s): %s", testaddr, err)
}
_, _, _, _, err = vm.GetTxHistory(pkToAddr[pks[0]])
if err != nil {
t.Fatalf("GetTxHistory(%s): %s", pkToAddr[pks[0]], err)
}
ctx.Lock.Unlock()
}
func TestIssuePendingDependency(t *testing.T) {
// Initialize vm with genesis info
genesisTx := GenesisTx(defaultInitBalances)
vmDB := memdb.New()
msgChan := make(chan common.Message, 1)
ctx.Lock.Lock()
vm := &VM{}
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
vm.batchTimeout = 0
builder := Builder{
NetworkID: 0,
ChainID: avaChainID,
}
tx1, _ := builder.NewTx(
/*ins=*/ []Input{
builder.NewInputPayment(
/*txID=*/ genesisTx.ID(),
/*txIndex=*/ 0,
/*amount=*/ 5*units.Ava,
/*sigs=*/ []*Sig{builder.NewSig(0 /*=index*/)},
),
},
/*outs=*/ []Output{
builder.NewOutputPayment(
/*amount=*/ 3*units.Ava,
/*locktime=*/ 0,
/*threshold=*/ 0,
/*addresses=*/ nil,
),
},
/*signers=*/ []*InputSigner{
&InputSigner{Keys: []*crypto.PrivateKeySECP256K1R{
keys[1],
}},
},
)
tx1Bytes := tx1.Bytes()
tx2, _ := builder.NewTx(
/*ins=*/ []Input{
builder.NewInputPayment(
/*txID=*/ tx1.ID(),
/*txIndex=*/ 0,
/*amount=*/ 3*units.Ava,
/*sigs=*/ nil,
),
},
/*outs=*/ []Output{
builder.NewOutputPayment(
/*amount=*/ 1*units.Ava,
/*locktime=*/ 0,
/*threshold=*/ 0,
/*addresses=*/ nil,
),
},
/*signers=*/ []*InputSigner{
&InputSigner{},
},
)
tx2Bytes := tx2.Bytes()
vm.IssueTx(tx1Bytes, nil)
vm.IssueTx(tx2Bytes, nil)
ctx.Lock.Unlock()
if msg := <-msgChan; msg != common.PendingTxs {
t.Fatalf("Wrong message")
}
ctx.Lock.Lock()
txs := vm.PendingTxs()
var avlTx1 snowstorm.Tx
var avlTx2 snowstorm.Tx
if txs[0].ID().Equals(tx1.ID()) {
avlTx1 = txs[0]
avlTx2 = txs[1]
} else {
avlTx1 = txs[1]
avlTx2 = txs[0]
}
if err := avlTx1.Verify(); err != nil {
t.Fatal(err)
}
if err := avlTx2.Verify(); err != nil {
t.Fatal(err)
}
ctx.Lock.Unlock()
}
// Ensure that an error is returned if an address will have more than
// math.MaxUint64 NanoAva
func TestTxOutputOverflow(t *testing.T) {
// Modify the genesis tx so the address controlled by [keys[0]]
// has math.MaxUint64 NanoAva
initBalances := map[string]uint64{
keys[0].PublicKey().Address().String(): math.MaxUint64,
keys[1].PublicKey().Address().String(): defaultInitBalance,
keys[2].PublicKey().Address().String(): defaultInitBalance,
}
genesisTx := GenesisTx(initBalances)
// Initialize vm
vmDB := memdb.New()
msgChan := make(chan common.Message, 1)
ctx.Lock.Lock()
vm := &VM{}
vm.Initialize(ctx, vmDB, genesisTx.Bytes(), msgChan, nil)
vm.batchTimeout = 0
// Create a new private key
testPK, err := vm.CreateKey()
if err != nil {
t.Fatalf("CreateKey(): %s", err)
}
// Get the address controlled by the new private key
testAddr, err := vm.GetAddress(testPK)
if err != nil {
t.Fatalf("GetAddress(%q): %s", testPK, err)
}
// Get string repr. of keys[0]
cb58 := formatting.CB58{Bytes: keys[0].Bytes()}
privKey0 := cb58.String()
// Send [math.MaxUint64 - txFeeTest] NanoAva from [privKey0] to [testAddr]
_, err = vm.Send(math.MaxUint64-txFeeTest, "", testAddr, []string{privKey0})
if err != nil {
t.Fatalf("Send(%d,%q,%q,%v): failed with error - %s", uint64(math.MaxUint64-txFeeTest), "", testAddr, []string{privKey0}, err)
}
ctx.Lock.Unlock()
if msg := <-msgChan; msg != common.PendingTxs {
t.Fatalf("Wrong message")
}
// Accept the transaction
ctx.Lock.Lock()
if txs := vm.PendingTxs(); len(txs) != 1 {
t.Fatalf("PendingTxs(): returned wrong number of transactions - expected: %d; returned: %d", 1, len(txs))
} else {
txs[0].Accept()
}
if txs := vm.PendingTxs(); len(txs) != 0 {
t.Fatalf("PendingTxs(): there should not have been any pending transactions")
}
// Ensure that [testAddr] has balance [math.MaxUint64 - txFeeTest]
if testbal, err := vm.GetBalance(testAddr, ""); err != nil {
t.Fatalf("GetBalance(%q): %s", testAddr, err)
} else if testbal != math.MaxUint64-txFeeTest {
t.Fatalf("GetBalance(%q,%q): Balance Not Equal(%d,%d)", testAddr, "", uint64(math.MaxUint64-txFeeTest), testbal)
}
// Ensure that the address controlled by [keys[0]] has balance 0
if testbal, err := vm.GetBalance(keys[0].PublicKey().Address().String(), ""); err != nil {
t.Fatalf("GetBalance(%q): %s", keys[0].PublicKey().Address().String(), err)
} else if testbal != 0 {
// Balance of new address should be 0
t.Fatalf("GetBalance(%q,%q): Balance Not Equal(%d,%d)", keys[0].PublicKey().Address().String(), "", 0, testbal)
}
cb58.Bytes = keys[1].Bytes()
privKey1 := cb58.String()
// Send [2*txFeeTest+1] NanoAva from [key1Str] to [testAddr]
// Should overflow [testAddr] by 1
_, err = vm.Send(2*txFeeTest+1, "", testAddr, []string{privKey1})
if err == errOutputOverflow {
t.Fatalf("Expected output to overflow but it did not")
}
ctx.Lock.Unlock()
}