diff options
Diffstat (limited to 'ledger/applications_test.go')
-rw-r--r-- | ledger/applications_test.go | 426 |
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) |