summaryrefslogtreecommitdiff
path: root/ledger/applications_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'ledger/applications_test.go')
-rw-r--r--ledger/applications_test.go426
1 files changed, 24 insertions, 402 deletions
diff --git a/ledger/applications_test.go b/ledger/applications_test.go
index 038207749..776832968 100644
--- a/ledger/applications_test.go
+++ b/ledger/applications_test.go
@@ -17,356 +17,26 @@
package ledger
import (
- "crypto/rand"
"encoding/hex"
- "fmt"
"testing"
+ "time"
"github.com/stretchr/testify/require"
"github.com/algorand/go-algorand/config"
- "github.com/algorand/go-algorand/crypto"
"github.com/algorand/go-algorand/data/basics"
"github.com/algorand/go-algorand/data/transactions"
"github.com/algorand/go-algorand/data/transactions/logic"
+ ledgertesting "github.com/algorand/go-algorand/ledger/testing"
"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/protocol"
"github.com/algorand/go-algorand/test/partitiontest"
)
-func getRandomAddress(a *require.Assertions) basics.Address {
- const rl = 16
- b := make([]byte, rl)
- n, err := rand.Read(b)
- a.NoError(err)
- a.Equal(rl, n)
-
- address := crypto.Hash(b)
- return basics.Address(address)
-}
-
-type creatableLocator struct {
- cidx basics.CreatableIndex
- ctype basics.CreatableType
-}
-type storeLocator struct {
- addr basics.Address
- aidx basics.AppIndex
- global bool
-}
-type mockCowForLogicLedger struct {
- rnd basics.Round
- ts int64
- cr map[creatableLocator]basics.Address
- brs map[basics.Address]basics.AccountData
- stores map[storeLocator]basics.TealKeyValue
- tcs map[int]basics.CreatableIndex
- txc uint64
-}
-
-func (c *mockCowForLogicLedger) Get(addr basics.Address, withPendingRewards bool) (basics.AccountData, error) {
- br, ok := c.brs[addr]
- if !ok {
- return basics.AccountData{}, fmt.Errorf("addr %s not in mock cow", addr.String())
- }
- return br, nil
-}
-
-func (c *mockCowForLogicLedger) GetCreatableID(groupIdx int) basics.CreatableIndex {
- return c.tcs[groupIdx]
-}
-
-func (c *mockCowForLogicLedger) GetCreator(cidx basics.CreatableIndex, ctype basics.CreatableType) (basics.Address, bool, error) {
- addr, found := c.cr[creatableLocator{cidx, ctype}]
- return addr, found, nil
-}
-
-func (c *mockCowForLogicLedger) GetKey(addr basics.Address, aidx basics.AppIndex, global bool, key string, accountIdx uint64) (basics.TealValue, bool, error) {
- kv, ok := c.stores[storeLocator{addr, aidx, global}]
- if !ok {
- return basics.TealValue{}, false, fmt.Errorf("no store for (%s %d %v) in mock cow", addr.String(), aidx, global)
- }
- tv, found := kv[key]
- return tv, found, nil
-}
-
-func (c *mockCowForLogicLedger) BuildEvalDelta(aidx basics.AppIndex, txn *transactions.Transaction) (evalDelta transactions.EvalDelta, err error) {
- return transactions.EvalDelta{}, nil
-}
-
-func (c *mockCowForLogicLedger) SetKey(addr basics.Address, aidx basics.AppIndex, global bool, key string, value basics.TealValue, accountIdx uint64) error {
- kv, ok := c.stores[storeLocator{addr, aidx, global}]
- if !ok {
- return fmt.Errorf("no store for (%s %d %v) in mock cow", addr.String(), aidx, global)
- }
- kv[key] = value
- c.stores[storeLocator{addr, aidx, global}] = kv
- return nil
-}
-
-func (c *mockCowForLogicLedger) DelKey(addr basics.Address, aidx basics.AppIndex, global bool, key string, accountIdx uint64) error {
- kv, ok := c.stores[storeLocator{addr, aidx, global}]
- if !ok {
- return fmt.Errorf("no store for (%s %d %v) in mock cow", addr.String(), aidx, global)
- }
- delete(kv, key)
- c.stores[storeLocator{addr, aidx, global}] = kv
- return nil
-}
-
-func (c *mockCowForLogicLedger) round() basics.Round {
- return c.rnd
-}
-
-func (c *mockCowForLogicLedger) prevTimestamp() int64 {
- return c.ts
-}
-
-func (c *mockCowForLogicLedger) allocated(addr basics.Address, aidx basics.AppIndex, global bool) (bool, error) {
- _, found := c.stores[storeLocator{addr, aidx, global}]
- return found, nil
-}
-
-func (c *mockCowForLogicLedger) incTxnCount() {
- c.txc++
-}
-
-func (c *mockCowForLogicLedger) txnCounter() uint64 {
- return c.txc
-}
-
-func newCowMock(creatables []modsData) *mockCowForLogicLedger {
- var m mockCowForLogicLedger
- m.cr = make(map[creatableLocator]basics.Address, len(creatables))
- for _, e := range creatables {
- m.cr[creatableLocator{e.cidx, e.ctype}] = e.addr
- }
- return &m
-}
-
-func TestLogicLedgerMake(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- a := require.New(t)
-
- _, err := newLogicLedger(nil, 0)
- a.Error(err)
- a.Contains(err.Error(), "cannot make logic ledger for app index 0")
-
- addr := getRandomAddress(a)
- aidx := basics.AppIndex(1)
-
- c := &mockCowForLogicLedger{}
- _, err = newLogicLedger(c, 0)
- a.Error(err)
- a.Contains(err.Error(), "cannot make logic ledger for app index 0")
-
- _, err = newLogicLedger(c, aidx)
- a.Error(err)
- a.Contains(err.Error(), fmt.Sprintf("app %d does not exist", aidx))
-
- c = newCowMock([]modsData{{addr, basics.CreatableIndex(aidx), basics.AppCreatable}})
- l, err := newLogicLedger(c, aidx)
- a.NoError(err)
- a.NotNil(l)
- a.Equal(aidx, l.aidx)
- a.Equal(c, l.cow)
-}
-
-func TestLogicLedgerBalances(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- a := require.New(t)
-
- addr := getRandomAddress(a)
- aidx := basics.AppIndex(1)
- c := newCowMock([]modsData{{addr, basics.CreatableIndex(aidx), basics.AppCreatable}})
- l, err := newLogicLedger(c, aidx)
- a.NoError(err)
- a.NotNil(l)
-
- addr1 := getRandomAddress(a)
- ble := basics.MicroAlgos{Raw: 100}
- c.brs = map[basics.Address]basics.AccountData{addr1: {MicroAlgos: ble}}
- bla, err := l.Balance(addr1)
- a.NoError(err)
- a.Equal(ble, bla)
-}
-
-func TestLogicLedgerGetters(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- a := require.New(t)
-
- addr := getRandomAddress(a)
- aidx := basics.AppIndex(1)
- c := newCowMock([]modsData{{addr, basics.CreatableIndex(aidx), basics.AppCreatable}})
- l, err := newLogicLedger(c, aidx)
- a.NoError(err)
- a.NotNil(l)
-
- round := basics.Round(1234)
- c.rnd = round
- ts := int64(11223344)
- c.ts = ts
-
- addr1 := getRandomAddress(a)
- c.stores = map[storeLocator]basics.TealKeyValue{{addr1, aidx, false}: {}}
- a.Equal(aidx, l.ApplicationID())
- a.Equal(round, l.Round())
- a.Equal(ts, l.LatestTimestamp())
- a.True(l.OptedIn(addr1, 0))
- a.True(l.OptedIn(addr1, aidx))
- a.False(l.OptedIn(addr, 0))
- a.False(l.OptedIn(addr, aidx))
-}
-
-func TestLogicLedgerAsset(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- a := require.New(t)
-
- addr := getRandomAddress(a)
- addr1 := getRandomAddress(a)
- aidx := basics.AppIndex(1)
- assetIdx := basics.AssetIndex(2)
- c := newCowMock([]modsData{
- {addr, basics.CreatableIndex(aidx), basics.AppCreatable},
- {addr1, basics.CreatableIndex(assetIdx), basics.AssetCreatable},
- })
- l, err := newLogicLedger(c, aidx)
- a.NoError(err)
- a.NotNil(l)
-
- _, _, err = l.AssetParams(basics.AssetIndex(aidx))
- a.Error(err)
- a.Contains(err.Error(), fmt.Sprintf("asset %d does not exist", aidx))
-
- c.brs = map[basics.Address]basics.AccountData{
- addr1: {AssetParams: map[basics.AssetIndex]basics.AssetParams{assetIdx: {Total: 1000}}},
- }
-
- ap, creator, err := l.AssetParams(assetIdx)
- a.NoError(err)
- a.Equal(addr1, creator)
- a.Equal(uint64(1000), ap.Total)
-
- _, err = l.AssetHolding(addr1, assetIdx)
- a.Error(err)
- a.Contains(err.Error(), "has not opted in to asset")
-
- c.brs = map[basics.Address]basics.AccountData{
- addr1: {
- AssetParams: map[basics.AssetIndex]basics.AssetParams{assetIdx: {Total: 1000}},
- Assets: map[basics.AssetIndex]basics.AssetHolding{assetIdx: {Amount: 99}},
- },
- }
-
- ah, err := l.AssetHolding(addr1, assetIdx)
- a.NoError(err)
- a.Equal(uint64(99), ah.Amount)
-}
-
-func TestLogicLedgerGetKey(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- a := require.New(t)
-
- addr := getRandomAddress(a)
- addr1 := getRandomAddress(a)
- aidx := basics.AppIndex(1)
- assetIdx := basics.AssetIndex(2)
- c := newCowMock([]modsData{
- {addr, basics.CreatableIndex(aidx), basics.AppCreatable},
- {addr1, basics.CreatableIndex(assetIdx), basics.AssetCreatable},
- })
- l, err := newLogicLedger(c, aidx)
- a.NoError(err)
- a.NotNil(l)
-
- _, ok, err := l.GetGlobal(basics.AppIndex(assetIdx), "gkey")
- a.Error(err)
- a.False(ok)
- a.Contains(err.Error(), fmt.Sprintf("app %d does not exist", assetIdx))
-
- tv := basics.TealValue{Type: basics.TealUintType, Uint: 1}
- c.stores = map[storeLocator]basics.TealKeyValue{{addr, aidx + 1, true}: {"gkey": tv}}
- val, ok, err := l.GetGlobal(aidx, "gkey")
- a.Error(err)
- a.False(ok)
- a.Contains(err.Error(), fmt.Sprintf("no store for (%s %d %v) in mock cow", addr, aidx, true))
-
- c.stores = map[storeLocator]basics.TealKeyValue{{addr, aidx, true}: {"gkey": tv}}
- val, ok, err = l.GetGlobal(aidx, "gkey")
- a.NoError(err)
- a.True(ok)
- a.Equal(tv, val)
-
- // check local
- c.stores = map[storeLocator]basics.TealKeyValue{{addr, aidx, false}: {"lkey": tv}}
- val, ok, err = l.GetLocal(addr, aidx, "lkey", 0)
- a.NoError(err)
- a.True(ok)
- a.Equal(tv, val)
-}
-
-func TestLogicLedgerSetKey(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- a := require.New(t)
-
- addr := getRandomAddress(a)
- aidx := basics.AppIndex(1)
- c := newCowMock([]modsData{
- {addr, basics.CreatableIndex(aidx), basics.AppCreatable},
- })
- l, err := newLogicLedger(c, aidx)
- a.NoError(err)
- a.NotNil(l)
-
- tv := basics.TealValue{Type: basics.TealUintType, Uint: 1}
- err = l.SetGlobal("gkey", tv)
- a.Error(err)
- a.Contains(err.Error(), fmt.Sprintf("no store for (%s %d %v) in mock cow", addr, aidx, true))
-
- tv2 := basics.TealValue{Type: basics.TealUintType, Uint: 2}
- c.stores = map[storeLocator]basics.TealKeyValue{{addr, aidx, true}: {"gkey": tv}}
- err = l.SetGlobal("gkey", tv2)
- a.NoError(err)
-
- // check local
- c.stores = map[storeLocator]basics.TealKeyValue{{addr, aidx, false}: {"lkey": tv}}
- err = l.SetLocal(addr, "lkey", tv2, 0)
- a.NoError(err)
-}
-
-func TestLogicLedgerDelKey(t *testing.T) {
- partitiontest.PartitionTest(t)
-
- a := require.New(t)
-
- addr := getRandomAddress(a)
- aidx := basics.AppIndex(1)
- c := newCowMock([]modsData{
- {addr, basics.CreatableIndex(aidx), basics.AppCreatable},
- })
- l, err := newLogicLedger(c, aidx)
- a.NoError(err)
- a.NotNil(l)
-
- err = l.DelGlobal("gkey")
- a.Error(err)
- a.Contains(err.Error(), fmt.Sprintf("no store for (%s %d %v) in mock cow", addr, aidx, true))
-
- tv := basics.TealValue{Type: basics.TealUintType, Uint: 1}
- c.stores = map[storeLocator]basics.TealKeyValue{{addr, aidx, true}: {"gkey": tv}}
- err = l.DelGlobal("gkey")
- a.NoError(err)
-
- addr1 := getRandomAddress(a)
- c.stores = map[storeLocator]basics.TealKeyValue{{addr1, aidx, false}: {"lkey": tv}}
- err = l.DelLocal(addr1, "lkey", 0)
- a.NoError(err)
+func commitRound(offset uint64, dbRound basics.Round, l *Ledger) {
+ l.trackers.lastFlushTime = time.Time{}
+ l.trackers.scheduleCommit(l.Latest(), l.Latest()-(dbRound+basics.Round(offset)))
+ l.trackers.waitAccountsWriting()
}
// test ensures that
@@ -374,6 +44,7 @@ func TestLogicLedgerDelKey(t *testing.T) {
// before and after application code refactoring
// 2) writing into empty (opted-in) local state's KeyValue works after reloading
// Hardcoded values are from commit 9a0b439 (pre app refactor commit)
+
func TestAppAccountDataStorage(t *testing.T) {
partitiontest.PartitionTest(t)
@@ -433,7 +104,7 @@ return`
program := ops.Program
proto := config.Consensus[protocol.ConsensusCurrentVersion]
- genesisInitState, initKeys := testGenerateInitState(t, protocol.ConsensusCurrentVersion, 100)
+ genesisInitState, initKeys := ledgertesting.GenerateInitState(t, protocol.ConsensusCurrentVersion, 100)
creator, err := basics.UnmarshalChecksumAddress("3LN5DBFC2UTPD265LQDP3LMTLGZCQ5M3JV7XTVTGRH5CKSVNQVDFPN6FG4")
a.NoError(err)
@@ -460,14 +131,6 @@ return`
l, err := OpenLedger(logging.Base(), "TestAppAccountData", true, genesisInitState, cfg)
a.NoError(err)
defer l.Close()
- l.accts.ctxCancel() // force commitSyncer to exit
-
- // wait commitSyncer to exit
- // the test calls commitRound directly and does not need commitSyncer/committedUpTo
- select {
- case <-l.accts.commitSyncerClosed:
- break
- }
txHeader := transactions.Header{
Sender: creator,
@@ -522,9 +185,7 @@ return`
a.NoError(err)
// save data into DB and write into local state
- l.accts.accountsWriting.Add(1)
- l.accts.commitRound(3, 0, 0)
- l.accts.accountsWriting.Wait()
+ commitRound(3, 0, l)
appCallFields = transactions.ApplicationCallTxnFields{
OnCompletion: 0,
@@ -543,9 +204,7 @@ return`
a.NoError(err)
// save data into DB
- l.accts.accountsWriting.Add(1)
- l.accts.commitRound(1, 3, 0)
- l.accts.accountsWriting.Wait()
+ commitRound(1, 3, l)
// dump accounts
var rowid int64
@@ -553,18 +212,23 @@ return`
var buf []byte
err = l.accts.accountsq.lookupStmt.QueryRow(creator[:]).Scan(&rowid, &dbRound, &buf)
a.NoError(err)
+ a.Equal(basics.Round(4), dbRound)
a.Equal(expectedCreator, buf)
err = l.accts.accountsq.lookupStmt.QueryRow(userOptin[:]).Scan(&rowid, &dbRound, &buf)
a.NoError(err)
+ a.Equal(basics.Round(4), dbRound)
a.Equal(expectedUserOptIn, buf)
pad, err := l.accts.accountsq.lookup(userOptin)
+ a.NoError(err)
a.Nil(pad.accountData.AppLocalStates[appIdx].KeyValue)
ad, err := l.Lookup(dbRound, userOptin)
+ a.NoError(err)
a.Nil(ad.AppLocalStates[appIdx].KeyValue)
err = l.accts.accountsq.lookupStmt.QueryRow(userLocal[:]).Scan(&rowid, &dbRound, &buf)
a.NoError(err)
+ a.Equal(basics.Round(4), dbRound)
a.Equal(expectedUserLocal, buf)
ad, err = l.Lookup(dbRound, userLocal)
@@ -664,7 +328,7 @@ return`
program := ops.Program
proto := config.Consensus[protocol.ConsensusCurrentVersion]
- genesisInitState, initKeys := testGenerateInitState(t, protocol.ConsensusCurrentVersion, 100)
+ genesisInitState, initKeys := ledgertesting.GenerateInitState(t, protocol.ConsensusCurrentVersion, 100)
creator, err := basics.UnmarshalChecksumAddress("3LN5DBFC2UTPD265LQDP3LMTLGZCQ5M3JV7XTVTGRH5CKSVNQVDFPN6FG4")
a.NoError(err)
@@ -678,14 +342,6 @@ return`
l, err := OpenLedger(logging.Base(), t.Name(), true, genesisInitState, cfg)
a.NoError(err)
defer l.Close()
- l.accts.ctxCancel() // force commitSyncer to exit
-
- // wait commitSyncer to exit
- // the test calls commitRound directly and does not need commitSyncer/committedUpTo
- select {
- case <-l.accts.commitSyncerClosed:
- break
- }
genesisID := t.Name()
txHeader := transactions.Header{
@@ -751,9 +407,7 @@ return`
a.NoError(err)
// save data into DB and write into local state
- l.accts.accountsWriting.Add(1)
- l.accts.commitRound(3, 0, 0)
- l.accts.accountsWriting.Wait()
+ commitRound(3, 0, l)
// check first write
blk, err := l.Block(2)
@@ -807,9 +461,7 @@ return`
a.NoError(err)
// save data into DB
- l.accts.accountsWriting.Add(1)
- l.accts.commitRound(2, 3, 0)
- l.accts.accountsWriting.Wait()
+ commitRound(2, 3, l)
// check first write
blk, err = l.Block(4)
@@ -919,7 +571,7 @@ return`
program := ops.Program
proto := config.Consensus[protocol.ConsensusCurrentVersion]
- genesisInitState, initKeys := testGenerateInitState(t, protocol.ConsensusCurrentVersion, 100)
+ genesisInitState, initKeys := ledgertesting.GenerateInitState(t, protocol.ConsensusCurrentVersion, 100)
creator, err := basics.UnmarshalChecksumAddress("3LN5DBFC2UTPD265LQDP3LMTLGZCQ5M3JV7XTVTGRH5CKSVNQVDFPN6FG4")
a.NoError(err)
@@ -933,14 +585,6 @@ return`
l, err := OpenLedger(logging.Base(), t.Name(), true, genesisInitState, cfg)
a.NoError(err)
defer l.Close()
- l.accts.ctxCancel() // force commitSyncer to exit
-
- // wait commitSyncer to exit
- // the test calls commitRound directly and does not need commitSyncer/committedUpTo
- select {
- case <-l.accts.commitSyncerClosed:
- break
- }
genesisID := t.Name()
txHeader := transactions.Header{
@@ -1034,9 +678,7 @@ return`
l.WaitForCommit(3)
// save data into DB and write into local state
- l.accts.accountsWriting.Add(1)
- l.accts.commitRound(3, 0, 0)
- l.accts.accountsWriting.Wait()
+ commitRound(3, 0, l)
// check first write
blk, err = l.Block(2)
@@ -1078,7 +720,7 @@ return`
program := ops.Program
proto := config.Consensus[protocol.ConsensusCurrentVersion]
- genesisInitState, initKeys := testGenerateInitState(t, protocol.ConsensusCurrentVersion, 100)
+ genesisInitState, initKeys := ledgertesting.GenerateInitState(t, protocol.ConsensusCurrentVersion, 100)
creator, err := basics.UnmarshalChecksumAddress("3LN5DBFC2UTPD265LQDP3LMTLGZCQ5M3JV7XTVTGRH5CKSVNQVDFPN6FG4")
a.NoError(err)
@@ -1092,14 +734,6 @@ return`
l, err := OpenLedger(logging.Base(), t.Name(), true, genesisInitState, cfg)
a.NoError(err)
defer l.Close()
- l.accts.ctxCancel() // force commitSyncer to exit
-
- // wait commitSyncer to exit
- // the test calls commitRound directly and does not need commitSyncer/committedUpTo
- select {
- case <-l.accts.commitSyncerClosed:
- break
- }
genesisID := t.Name()
txHeader := transactions.Header{
@@ -1174,9 +808,7 @@ return`
a.NoError(err)
// save data into DB and write into local state
- l.accts.accountsWriting.Add(1)
- l.accts.commitRound(2, 0, 0)
- l.accts.accountsWriting.Wait()
+ commitRound(2, 0, l)
// check first write
blk, err = l.Block(1)
@@ -1279,7 +911,7 @@ func testAppAccountDeltaIndicesCompatibility(t *testing.T, source string, accoun
// explicitly trigger compatibility mode
proto := config.Consensus[protocol.ConsensusV24]
- genesisInitState, initKeys := testGenerateInitState(t, protocol.ConsensusV24, 100)
+ genesisInitState, initKeys := ledgertesting.GenerateInitState(t, protocol.ConsensusV24, 100)
creator, err := basics.UnmarshalChecksumAddress("3LN5DBFC2UTPD265LQDP3LMTLGZCQ5M3JV7XTVTGRH5CKSVNQVDFPN6FG4")
a.NoError(err)
@@ -1293,14 +925,6 @@ func testAppAccountDeltaIndicesCompatibility(t *testing.T, source string, accoun
l, err := OpenLedger(logging.Base(), t.Name(), true, genesisInitState, cfg)
a.NoError(err)
defer l.Close()
- l.accts.ctxCancel() // force commitSyncer to exit
-
- // wait commitSyncer to exit
- // the test calls commitRound directly and does not need commitSyncer/committedUpTo
- select {
- case <-l.accts.commitSyncerClosed:
- break
- }
genesisID := t.Name()
txHeader := transactions.Header{
@@ -1361,9 +985,7 @@ func testAppAccountDeltaIndicesCompatibility(t *testing.T, source string, accoun
a.NoError(err)
// save data into DB and write into local state
- l.accts.accountsWriting.Add(1)
- l.accts.commitRound(2, 0, 0)
- l.accts.accountsWriting.Wait()
+ commitRound(2, 0, l)
// check first write
blk, err := l.Block(2)