summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Lee <john@onetechnical.com>2023-01-20 11:35:44 -0500
committerGitHub <noreply@github.com>2023-01-20 11:35:44 -0500
commit9a7499dd82d991104dcbd0188cae1acc454ea611 (patch)
treee0300c763dfc1fd1660870fd37f667bbdb384565
parent181490e38f9c00f511300d325a07fb674b726588 (diff)
parent9e73616af78432ccf79fedb909c452efd17e0527 (diff)
Merge pull request #1 from algorand/relstable3.12.3v3.12.3-stable-backportv3.12.3-stablebackport/3.12.3
go-algorand v3.12.3-stable backport
-rw-r--r--buildnumber.dat2
-rw-r--r--ledger/accountdb.go10
-rw-r--r--ledger/applications_test.go133
3 files changed, 141 insertions, 4 deletions
diff --git a/buildnumber.dat b/buildnumber.dat
index 0cfbf0888..00750edc0 100644
--- a/buildnumber.dat
+++ b/buildnumber.dat
@@ -1 +1 @@
-2
+3
diff --git a/ledger/accountdb.go b/ledger/accountdb.go
index 9b8d41828..f769e394a 100644
--- a/ledger/accountdb.go
+++ b/ledger/accountdb.go
@@ -2626,7 +2626,7 @@ func accountsInitDbQueries(q db.Queryable) (*accountsDbQueries, error) {
return nil, err
}
- qs.lookupKvPairStmt, err = q.Prepare("SELECT acctrounds.rnd, kvstore.value FROM acctrounds LEFT JOIN kvstore ON key = ? WHERE id='acctbase';")
+ qs.lookupKvPairStmt, err = q.Prepare("SELECT acctrounds.rnd, kvstore.key, kvstore.value FROM acctrounds LEFT JOIN kvstore ON key = ? WHERE id='acctbase';")
if err != nil {
return nil, err
}
@@ -2714,9 +2714,10 @@ func (qs *accountsDbQueries) listCreatables(maxIdx basics.CreatableIndex, maxRes
func (qs *accountsDbQueries) lookupKeyValue(key string) (pv persistedKVData, err error) {
err = db.Retry(func() error {
+ var rawkey []byte
var val []byte
// Cast to []byte to avoid interpretation as character string, see note in upsertKvPair
- err := qs.lookupKvPairStmt.QueryRow([]byte(key)).Scan(&pv.round, &val)
+ err := qs.lookupKvPairStmt.QueryRow([]byte(key)).Scan(&pv.round, &rawkey, &val)
if err != nil {
// this should never happen; it indicates that we don't have a current round in the acctrounds table.
if err == sql.ErrNoRows {
@@ -2725,7 +2726,10 @@ func (qs *accountsDbQueries) lookupKeyValue(key string) (pv persistedKVData, err
}
return err
}
- if val != nil { // We got a non-null value, so it exists
+ if rawkey != nil { // We got a non-null key, so it exists
+ if val == nil {
+ val = []byte{}
+ }
pv.value = val
return nil
}
diff --git a/ledger/applications_test.go b/ledger/applications_test.go
index 012e291b4..e324f6115 100644
--- a/ledger/applications_test.go
+++ b/ledger/applications_test.go
@@ -18,12 +18,16 @@ package ledger
import (
"encoding/hex"
+ "fmt"
+ "path/filepath"
"testing"
"time"
"github.com/stretchr/testify/require"
+ "github.com/algorand/go-algorand/agreement"
"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"
@@ -1302,3 +1306,132 @@ int 1
err = l.appendUnvalidatedSignedTx(t, genesisInitState.Accounts, signedLsigPayment, transactions.ApplyData{})
a.NoError(err)
}
+
+func TestAppEmptyBox(t *testing.T) {
+ partitiontest.PartitionTest(t)
+
+ a := require.New(t)
+ source := `#pragma version 8
+txn ApplicationID
+int 0
+==
+bnz create_box
+// otherwise delete the box
+byte "boxname"
+box_del
+return
+create_box:
+byte "boxname"
+int 0
+box_create
+return
+`
+
+ ops, err := logic.AssembleString(source)
+ a.NoError(err, ops.Errors)
+ a.Greater(len(ops.Program), 1)
+ program := ops.Program
+
+ proto := config.Consensus[protocol.ConsensusCurrentVersion]
+ genesisInitState, initKeys := ledgertesting.GenerateInitState(t, protocol.ConsensusCurrentVersion, 1000000)
+
+ creator, err := basics.UnmarshalChecksumAddress("3LN5DBFC2UTPD265LQDP3LMTLGZCQ5M3JV7XTVTGRH5CKSVNQVDFPN6FG4")
+ a.NoError(err)
+ a.Contains(genesisInitState.Accounts, creator)
+
+ dbName := fmt.Sprintf("%s.%d", t.Name(), crypto.RandUint64())
+ dbPrefix := filepath.Join(t.TempDir(), dbName)
+
+ cfg := config.GetDefaultLocal()
+ cfg.MaxAcctLookback = 2
+ l1, err := OpenLedger(logging.Base(), dbPrefix, false, genesisInitState, cfg)
+ a.NoError(err)
+ defer l1.Close()
+
+ genesisID := t.Name()
+ txHeader := transactions.Header{
+ Sender: creator,
+ Fee: basics.MicroAlgos{Raw: proto.MinTxnFee * 2},
+ FirstValid: l1.Latest() + 1,
+ LastValid: l1.Latest() + 10,
+ GenesisID: genesisID,
+ GenesisHash: genesisInitState.GenesisHash,
+ }
+
+ appIdx := basics.AppIndex(2) // second tnx => idx = 2
+
+ // fund app account
+ fundingPayment := transactions.Transaction{
+ Type: protocol.PaymentTx,
+ Header: txHeader,
+ PaymentTxnFields: transactions.PaymentTxnFields{
+ Receiver: appIdx.Address(),
+ Amount: basics.MicroAlgos{Raw: 100*proto.MinBalance + proto.MinTxnFee},
+ },
+ }
+ err = l1.appendUnvalidatedTx(t, genesisInitState.Accounts, initKeys, fundingPayment, transactions.ApplyData{})
+ a.NoError(err)
+
+ // create application
+ approvalProgram := program
+ clearStateProgram := []byte("\x08") // empty
+ appCreateFields := transactions.ApplicationCallTxnFields{
+ ApprovalProgram: approvalProgram,
+ ClearStateProgram: clearStateProgram,
+ GlobalStateSchema: basics.StateSchema{NumByteSlice: 0},
+ LocalStateSchema: basics.StateSchema{NumByteSlice: 0},
+ Boxes: []transactions.BoxRef{{Name: []byte("boxname")}},
+ }
+ appCreate := transactions.Transaction{
+ Type: protocol.ApplicationCallTx,
+ Header: txHeader,
+ ApplicationCallTxnFields: appCreateFields,
+ }
+ err = l1.appendUnvalidatedTx(t, genesisInitState.Accounts, initKeys, appCreate, transactions.ApplyData{ApplicationID: 2})
+ a.NoError(err)
+
+ // few empty blocks to reset deltas and flush
+ for i := 0; i < 10; i++ {
+ blk := makeNewEmptyBlock(t, l1, genesisID, genesisInitState.Accounts)
+ l1.AddBlock(blk, agreement.Certificate{})
+ }
+
+ app, err := l1.LookupApplication(l1.Latest(), creator, appIdx)
+ a.NoError(err)
+ a.Greater(len(app.AppParams.ApprovalProgram), 0)
+
+ commitRound(10, 0, l1)
+
+ // restart
+ l1.Close()
+
+ l2, err := OpenLedger(logging.Base(), dbPrefix, false, genesisInitState, cfg)
+ a.NoError(err)
+ defer l2.Close()
+
+ app, err = l2.LookupApplication(l2.Latest(), creator, appIdx)
+ a.NoError(err)
+ a.Greater(len(app.AppParams.ApprovalProgram), 0)
+
+ txHeader = transactions.Header{
+ Sender: creator,
+ Fee: basics.MicroAlgos{Raw: proto.MinTxnFee * 2},
+ FirstValid: l2.Latest() + 1,
+ LastValid: l2.Latest() + 10,
+ GenesisID: genesisID,
+ GenesisHash: genesisInitState.GenesisHash,
+ }
+
+ appCallFields := transactions.ApplicationCallTxnFields{
+ ApplicationID: appIdx,
+ Boxes: []transactions.BoxRef{{Name: []byte("boxname")}},
+ }
+ appCall := transactions.Transaction{
+ Type: protocol.ApplicationCallTx,
+ Header: txHeader,
+ ApplicationCallTxnFields: appCallFields,
+ }
+ err = l2.appendUnvalidatedTx(t, genesisInitState.Accounts, initKeys, appCall, transactions.ApplyData{})
+ a.NoError(err)
+
+}