From 17de35e38a243bd2490b04a47a2f7148e551e429 Mon Sep 17 00:00:00 2001 From: Tudor Malene Date: Wed, 1 May 2024 12:13:45 +0100 Subject: [PATCH] fix --- go/common/enclave.go | 1 + go/enclave/storage/enclavedb/batch.go | 70 +++++++------------ go/enclave/storage/enclavedb/block.go | 50 ++++--------- go/enclave/storage/enclavedb/events.go | 31 +++----- go/enclave/storage/enclavedb/keyvalue.go | 43 +++++++----- go/enclave/storage/enclavedb/utils.go | 14 +++- .../storage/init/edgelessdb/001_init.sql | 21 +++--- go/enclave/storage/init/sqlite/001_init.sql | 23 +++--- go/enclave/storage/storage.go | 6 +- 9 files changed, 121 insertions(+), 138 deletions(-) diff --git a/go/common/enclave.go b/go/common/enclave.go index 59a9d06be4..54952a2aa3 100644 --- a/go/common/enclave.go +++ b/go/common/enclave.go @@ -140,6 +140,7 @@ type EnclaveScan interface { GetTotalContractCount(context.Context) (*big.Int, SystemError) // GetCustomQuery returns the data of a custom query + // todo - better name and description GetCustomQuery(ctx context.Context, encryptedParams EncryptedParamsGetStorageAt) (*responses.PrivateQueryResponse, SystemError) // GetPublicTransactionData returns a list of public transaction data diff --git a/go/enclave/storage/enclavedb/batch.go b/go/enclave/storage/enclavedb/batch.go index e5828aa4aa..a718c2c3aa 100644 --- a/go/enclave/storage/enclavedb/batch.go +++ b/go/enclave/storage/enclavedb/batch.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "math/big" - "strings" "github.com/ethereum/go-ethereum/params" @@ -20,30 +19,9 @@ import ( ) const ( - bodyInsert = "replace into batch_body values (?,?)" - txInsert = "replace into tx (hash, full_hash, content, sender_address, nonce, idx, body) values " - txInsertValue = "(?,?,?,?,?,?,?)" - - batchInsert = "insert into batch values (?,?,?,?,?,?,?,?,?,?)" - updateBatchExecuted = "update batch set is_executed=true where sequence=?" - selectBatch = "select b.header, bb.content from batch b join batch_body bb on b.body=bb.id" - txExecInsert = "insert into exec_tx (created_contract_address, receipt, tx, batch) values " - txExecInsertValue = "(?,?,?,?)" - queryReceipts = "select exec_tx.receipt, tx.content, batch.full_hash, batch.height from exec_tx join tx on tx.id=exec_tx.tx join batch on batch.sequence=exec_tx.batch " - queryReceiptsCount = "select count(1) from exec_tx join tx on tx.id=exec_tx.tx join batch on batch.sequence=exec_tx.batch " - - selectTxQuery = "select tx.content, batch.full_hash, batch.height, tx.idx from exec_tx join tx on tx.id=exec_tx.tx join batch on batch.sequence=exec_tx.batch where batch.is_canonical=true and tx.hash=? and tx.full_hash=?" - - selectContractCreationTx = "select tx.full_hash from exec_tx join tx on tx.id=exec_tx.tx where created_contract_address=?" - selectTotalCreatedContracts = "select count( distinct created_contract_address) from exec_tx " - queryBatchWasExecuted = "select is_executed from batch where is_canonical=true and hash=? and full_hash=?" - - isCanonQuery = "select is_canonical from block where hash=? and full_hash=?" - - queryTxList = "select tx.full_hash, batch.height, batch.header from exec_tx join batch on batch.sequence=exec_tx.batch join tx on tx.id=exec_tx.tx where batch.is_canonical=true" - queryTxCountList = "select count(1) from exec_tx join batch on batch.sequence=exec_tx.batch where batch.is_canonical=true" + queryReceipts = "select exec_tx.receipt, tx.content, batch.full_hash, batch.height from exec_tx join tx on tx.id=exec_tx.tx join batch on batch.sequence=exec_tx.batch " ) // WriteBatchAndTransactions - persists the batch and the transactions @@ -60,17 +38,17 @@ func WriteBatchAndTransactions(ctx context.Context, dbtx DBTransaction, batch *c return fmt.Errorf("could not encode batch header. Cause: %w", err) } - dbtx.ExecuteSQL(bodyInsert, batchBodyID, body) + dbtx.ExecuteSQL("replace into batch_body values (?,?)", batchBodyID, body) var isCanon bool - err = dbtx.GetDB().QueryRowContext(ctx, isCanonQuery, truncTo4(batch.Header.L1Proof), batch.Header.L1Proof.Bytes()).Scan(&isCanon) + err = dbtx.GetDB().QueryRowContext(ctx, "select is_canonical from block where hash=? and full_hash=?", truncTo4(batch.Header.L1Proof), batch.Header.L1Proof.Bytes()).Scan(&isCanon) if err != nil { // if the block is not found, we assume it is non-canonical // fmt.Printf("IsCanon %s err: %s\n", batch.Header.L1Proof, err) isCanon = false } - dbtx.ExecuteSQL(batchInsert, + dbtx.ExecuteSQL("insert into batch values (?,?,?,?,?,?,?,?,?,?)", batch.Header.SequencerOrderNo.Uint64(), // sequence batch.Hash(), // full hash convertedHash, // converted_hash @@ -85,8 +63,7 @@ func WriteBatchAndTransactions(ctx context.Context, dbtx DBTransaction, batch *c // creates a big insert statement for all transactions if len(batch.Transactions) > 0 { - insert := txInsert + strings.Repeat(txInsertValue+",", len(batch.Transactions)) - insert = insert[0 : len(insert)-1] // remove trailing comma + insert := "replace into tx (hash, full_hash, content, sender_address, nonce, idx, body) values " + repeat("(?,?,?,?,?,?,?)", ",", len(batch.Transactions)) args := make([]any, 0) for i, transaction := range batch.Transactions { @@ -116,7 +93,7 @@ func WriteBatchAndTransactions(ctx context.Context, dbtx DBTransaction, batch *c // WriteBatchExecution - insert all receipts to the db func WriteBatchExecution(ctx context.Context, dbtx DBTransaction, seqNo *big.Int, receipts []*types.Receipt) error { - dbtx.ExecuteSQL(updateBatchExecuted, seqNo.Uint64()) + dbtx.ExecuteSQL("update batch set is_executed=true where sequence=?", seqNo.Uint64()) args := make([]any, 0) for _, receipt := range receipts { @@ -127,18 +104,16 @@ func WriteBatchExecution(ctx context.Context, dbtx DBTransaction, seqNo *big.Int return fmt.Errorf("failed to encode block receipts. Cause: %w", err) } + // ignore the error because synthetic transactions will not be inserted txId, _ := ReadTxId(ctx, dbtx, storageReceipt.TxHash) - //if err != nil { - // return err - //} - args = append(args, receipt.ContractAddress.Bytes()) // created_contract_address - args = append(args, receiptBytes) // the serialised receipt - args = append(args, txId) // tx id - args = append(args, seqNo.Uint64()) // batch_seq + args = append(args, truncBTo4(receipt.ContractAddress.Bytes())) // created_contract_address + args = append(args, receipt.ContractAddress.Bytes()) // created_contract_address + args = append(args, receiptBytes) // the serialised receipt + args = append(args, txId) // tx id + args = append(args, seqNo.Uint64()) // batch_seq } if len(args) > 0 { - insert := txExecInsert + strings.Repeat(txExecInsertValue+",", len(receipts)) - insert = insert[0 : len(insert)-1] // remove trailing comma + insert := "insert into exec_tx (created_contract_address,created_contract_address_full, receipt, tx, batch) values " + repeat("(?,?,?,?,?)", ",", len(receipts)) dbtx.ExecuteSQL(insert, args...) } return nil @@ -369,7 +344,9 @@ func ReadReceipt(ctx context.Context, db *sql.DB, txHash common.L2TxHash, config } func ReadTransaction(ctx context.Context, db *sql.DB, txHash gethcommon.Hash) (*types.Transaction, common.L2BatchHash, uint64, uint64, error) { - row := db.QueryRowContext(ctx, selectTxQuery, truncTo4(txHash), txHash.Bytes()) + row := db.QueryRowContext(ctx, + "select tx.content, batch.full_hash, batch.height, tx.idx from exec_tx join tx on tx.id=exec_tx.tx join batch on batch.sequence=exec_tx.batch where batch.is_canonical=true and tx.hash=? and tx.full_hash=?", + truncTo4(txHash), txHash.Bytes()) // tx, batch, height, idx var txData []byte @@ -394,7 +371,7 @@ func ReadTransaction(ctx context.Context, db *sql.DB, txHash gethcommon.Hash) (* } func GetContractCreationTx(ctx context.Context, db *sql.DB, address gethcommon.Address) (*gethcommon.Hash, error) { - row := db.QueryRowContext(ctx, selectContractCreationTx, address.Bytes()) + row := db.QueryRowContext(ctx, "select tx.full_hash from exec_tx join tx on tx.id=exec_tx.tx where created_contract_address=? and created_contract_address_full=?", truncBTo4(address.Bytes()), address.Bytes()) var txHashBytes []byte err := row.Scan(&txHashBytes) @@ -411,7 +388,7 @@ func GetContractCreationTx(ctx context.Context, db *sql.DB, address gethcommon.A } func ReadContractCreationCount(ctx context.Context, db *sql.DB) (*big.Int, error) { - row := db.QueryRowContext(ctx, selectTotalCreatedContracts) + row := db.QueryRowContext(ctx, "select count( distinct created_contract_address) from exec_tx ") var count int64 err := row.Scan(&count) @@ -427,7 +404,7 @@ func ReadUnexecutedBatches(ctx context.Context, db *sql.DB, from *big.Int) ([]*c } func BatchWasExecuted(ctx context.Context, db *sql.DB, hash common.L2BatchHash) (bool, error) { - row := db.QueryRowContext(ctx, queryBatchWasExecuted, truncTo4(hash), hash.Bytes()) + row := db.QueryRowContext(ctx, "select is_executed from batch where is_canonical=true and hash=? and full_hash=?", truncTo4(hash), hash.Bytes()) var result bool err := row.Scan(&result) @@ -443,11 +420,13 @@ func BatchWasExecuted(ctx context.Context, db *sql.DB, hash common.L2BatchHash) } func GetReceiptsPerAddress(ctx context.Context, db *sql.DB, config *params.ChainConfig, address *gethcommon.Address, pagination *common.QueryPagination) (types.Receipts, error) { + // todo - not indexed return selectReceipts(ctx, db, config, "where tx.sender_address = ? ORDER BY height DESC LIMIT ? OFFSET ? ", address.Bytes(), pagination.Size, pagination.Offset) } func GetReceiptsPerAddressCount(ctx context.Context, db *sql.DB, address *gethcommon.Address) (uint64, error) { - row := db.QueryRowContext(ctx, queryReceiptsCount+" where tx.sender_address = ?", address.Bytes()) + // todo - this is not indexed and will do a full table scan! + row := db.QueryRowContext(ctx, "select count(1) from exec_tx join tx on tx.id=exec_tx.tx join batch on batch.sequence=exec_tx.batch "+" where tx.sender_address = ?", address.Bytes()) var count uint64 err := row.Scan(&count) @@ -465,7 +444,8 @@ func GetPublicTransactionData(ctx context.Context, db *sql.DB, pagination *commo func selectPublicTxsBySender(ctx context.Context, db *sql.DB, query string, args ...any) ([]common.PublicTransaction, error) { var publicTxs []common.PublicTransaction - rows, err := db.QueryContext(ctx, queryTxList+" "+query, args...) + q := "select tx.full_hash, batch.height, batch.header from exec_tx join batch on batch.sequence=exec_tx.batch join tx on tx.id=exec_tx.tx where batch.is_canonical=true " + query + rows, err := db.QueryContext(ctx, q, args...) if err != nil { if errors.Is(err, sql.ErrNoRows) { // make sure the error is converted to obscuro-wide not found error @@ -503,7 +483,7 @@ func selectPublicTxsBySender(ctx context.Context, db *sql.DB, query string, args } func GetPublicTransactionCount(ctx context.Context, db *sql.DB) (uint64, error) { - row := db.QueryRowContext(ctx, queryTxCountList) + row := db.QueryRowContext(ctx, "select count(1) from exec_tx join batch on batch.sequence=exec_tx.batch where batch.is_canonical=true") var count uint64 err := row.Scan(&count) diff --git a/go/enclave/storage/enclavedb/block.go b/go/enclave/storage/enclavedb/block.go index a31a701d3d..0ce693a507 100644 --- a/go/enclave/storage/enclavedb/block.go +++ b/go/enclave/storage/enclavedb/block.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "math/big" - "strings" "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/rlp" @@ -15,30 +14,13 @@ import ( "github.com/ten-protocol/go-ten/go/common/errutil" ) -const ( - blockInsert = "insert into block (hash,full_hash,is_canonical,header,height) values (?,?,?,?,?)" - selectBlockHeader = "select header from block " - - l1msgInsert = "insert into l1_msg (message, block, is_transfer) values " - l1msgValue = "(?,?,?)" - selectL1Msg = "select message from l1_msg m join block b on m.block=b.id " - - rollupInsert = "replace into rollup (hash, full_hash, start_seq, end_seq, time_stamp, header, compression_block) values (?,?,?,?,?,?,?)" - rollupSelect = "select full_hash from rollup r join block b on r.compression_block=b.id where " - rollupSelectMetadata = "select start_seq, time_stamp from rollup where hash = ? and full_hash=?" - - updateCanonicalBlock = "update block set is_canonical=? where " - // todo - do we need the is_canonical field? - updateCanonicalBatches = "update batch set is_canonical=? where l1_proof in " -) - func WriteBlock(_ context.Context, dbtx DBTransaction, b *types.Header) error { header, err := rlp.EncodeToBytes(b) if err != nil { return fmt.Errorf("could not encode block header. Cause: %w", err) } - dbtx.ExecuteSQL(blockInsert, + dbtx.ExecuteSQL("insert into block (hash,full_hash,is_canonical,header,height) values (?,?,?,?,?)", truncTo4(b.Hash()), // hash b.Hash().Bytes(), // full_hash true, // is_canonical @@ -58,20 +40,18 @@ func UpdateCanonicalBlocks(ctx context.Context, dbtx DBTransaction, canonical [] } func updateCanonicalValue(_ context.Context, dbtx DBTransaction, isCanonical bool, blocks []common.L1BlockHash) { - token := "(hash=? and full_hash=?) OR " - updateBlocksWhere := strings.Repeat(token, len(blocks)) - updateBlocksWhere = updateBlocksWhere + "1=0" - - updateBlocks := updateCanonicalBlock + updateBlocksWhere + canonicalBlocks := repeat("(hash=? and full_hash=?)", "OR", len(blocks)) args := make([]any, 0) args = append(args, isCanonical) for _, blockHash := range blocks { args = append(args, truncTo4(blockHash), blockHash.Bytes()) } + + updateBlocks := "update block set is_canonical=? where " + canonicalBlocks dbtx.ExecuteSQL(updateBlocks, args...) - updateBatches := updateCanonicalBatches + "(" + "select id from block where " + updateBlocksWhere + ")" + updateBatches := "update batch set is_canonical=? where l1_proof in (select id from block where " + canonicalBlocks + ")" dbtx.ExecuteSQL(updateBatches, args...) } @@ -81,6 +61,7 @@ func FetchBlock(ctx context.Context, db *sql.DB, hash common.L1BlockHash) (*type } func FetchHeadBlock(ctx context.Context, db *sql.DB) (*types.Block, error) { + // todo - just read the one with the max id return fetchBlock(ctx, db, "where is_canonical=true and height=(select max(b.height) from block b where is_canonical=true)") } @@ -95,8 +76,7 @@ func GetBlockId(ctx context.Context, db *sql.DB, hash common.L1BlockHash) (uint6 } func WriteL1Messages[T any](ctx context.Context, db *sql.DB, blockId uint64, messages []T, isValueTransfer bool) error { - insert := l1msgInsert + strings.Repeat(l1msgValue+",", len(messages)) - insert = insert[0 : len(insert)-1] // remove trailing comma + insert := "insert into l1_msg (message, block, is_transfer) values " + repeat("(?,?,?)", ",", len(messages)) args := make([]any, 0) @@ -118,7 +98,7 @@ func WriteL1Messages[T any](ctx context.Context, db *sql.DB, blockId uint64, mes func FetchL1Messages[T any](ctx context.Context, db *sql.DB, blockHash common.L1BlockHash, isTransfer bool) ([]T, error) { var result []T - query := selectL1Msg + " where b.hash = ? and b.full_hash = ? and is_transfer = ?" + query := "select message from l1_msg m join block b on m.block=b.id where b.hash = ? and b.full_hash = ? and is_transfer = ?" rows, err := db.QueryContext(ctx, query, truncTo4(blockHash), blockHash.Bytes(), isTransfer) if err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -153,7 +133,7 @@ func WriteRollup(_ context.Context, dbtx DBTransaction, rollup *common.RollupHea if err != nil { return fmt.Errorf("could not encode batch header. Cause: %w", err) } - dbtx.ExecuteSQL(rollupInsert, + dbtx.ExecuteSQL("replace into rollup (hash, full_hash, start_seq, end_seq, time_stamp, header, compression_block) values (?,?,?,?,?,?,?)", truncTo4(rollup.Hash()), rollup.Hash().Bytes(), internalHeader.FirstBatchSequence.Uint64(), @@ -166,11 +146,9 @@ func WriteRollup(_ context.Context, dbtx DBTransaction, rollup *common.RollupHea } func FetchReorgedRollup(ctx context.Context, db *sql.DB, reorgedBlocks []common.L1BlockHash) (*common.L2BatchHash, error) { - token := "(b.hash=? and b.full_hash=?) OR " - whereClause := strings.Repeat(token, len(reorgedBlocks)) - whereClause = whereClause + "1=0" + whereClause := repeat("(b.hash=? and b.full_hash=?)", "OR", len(reorgedBlocks)) - query := rollupSelect + whereClause + query := "select full_hash from rollup r join block b on r.compression_block=b.id where " + whereClause args := make([]any, 0) for _, blockHash := range reorgedBlocks { @@ -193,7 +171,9 @@ func FetchRollupMetadata(ctx context.Context, db *sql.DB, hash common.L2RollupHa var startTime uint64 rollup := new(common.PublicRollupMetadata) - err := db.QueryRowContext(ctx, rollupSelectMetadata, truncTo4(hash), hash.Bytes()).Scan(&startSeq, &startTime) + err := db.QueryRowContext(ctx, + "select start_seq, time_stamp from rollup where hash = ? and full_hash=?", truncTo4(hash), hash.Bytes(), + ).Scan(&startSeq, &startTime) if err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, errutil.ErrNotFound @@ -207,7 +187,7 @@ func FetchRollupMetadata(ctx context.Context, db *sql.DB, hash common.L2RollupHa func fetchBlockHeader(ctx context.Context, db *sql.DB, whereQuery string, args ...any) (*types.Header, error) { var header string - query := selectBlockHeader + " " + whereQuery + query := "select header from block " + whereQuery var err error if len(args) > 0 { err = db.QueryRowContext(ctx, query, args...).Scan(&header) diff --git a/go/enclave/storage/enclavedb/events.go b/go/enclave/storage/enclavedb/events.go index 008b100ddc..27b646dcc7 100644 --- a/go/enclave/storage/enclavedb/events.go +++ b/go/enclave/storage/enclavedb/events.go @@ -5,7 +5,6 @@ import ( "database/sql" "fmt" "math/big" - "strings" gethcommon "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" @@ -15,12 +14,7 @@ import ( ) const ( - baseEventsQuerySelect = "select topic0_full, topic1_full, topic2_full, topic3_full, topic4_full, datablob, b.full_hash, b.height, tx.full_hash, tx.idx, log_idx, address_full" - baseDebugEventsQuerySelect = "select rel_address1_full, rel_address2_full, rel_address3_full, rel_address4_full, lifecycle_event, topic0_full, topic1_full, topic2_full, topic3_full, topic4_full, datablob, b.full_hash, b.height, tx.full_hash, tx.idx, log_idx, address_full" - baseEventsJoin = "from events e join exec_tx extx on e.tx=extx.tx and e.batch=extx.batch join tx on extx.tx=tx.id join batch b on extx.batch=b.sequence where b.is_canonical=true " - insertEvent = "insert into events values " - insertEventValues = "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)" - orderBy = " order by b.height, tx.idx asc" + baseEventsJoin = "from events e join exec_tx extx on e.tx=extx.tx and e.batch=extx.batch join tx on extx.tx=tx.id join batch b on extx.batch=b.sequence where b.is_canonical=true " ) func StoreEventLogs(ctx context.Context, dbtx DBTransaction, receipts []*types.Receipt, stateDB *state.StateDB) error { @@ -29,9 +23,6 @@ func StoreEventLogs(ctx context.Context, dbtx DBTransaction, receipts []*types.R for _, receipt := range receipts { for _, l := range receipt.Logs { txId, _ := ReadTxId(ctx, dbtx, l.TxHash) - //if err != nil { - // return err - //} batchId, err := ReadBatchId(ctx, dbtx, receipt.BlockHash) if err != nil { return err @@ -47,9 +38,7 @@ func StoreEventLogs(ctx context.Context, dbtx DBTransaction, receipts []*types.R } } if totalLogs > 0 { - query := insertEvent + " " + strings.Repeat(insertEventValues+",", totalLogs) - query = query[0 : len(query)-1] // remove trailing comma - + query := "insert into events values " + repeat("(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", ",", totalLogs) dbtx.ExecuteSQL(query, args...) } return nil @@ -180,8 +169,8 @@ func FilterLogs( } if len(addresses) > 0 { - token := "(address=? AND address_full=?) OR " - query += " AND (" + strings.Repeat(token, len(addresses)) + " 1=0)" + cond := repeat("(address=? AND address_full=?)", " OR ", len(addresses)) + query += " AND (" + cond + ")" for _, address := range addresses { queryParams = append(queryParams, truncBTo4(address.Bytes())) queryParams = append(queryParams, address.Bytes()) @@ -195,8 +184,8 @@ func FilterLogs( // empty rule set == wildcard if len(sub) > 0 { topicColumn := fmt.Sprintf("topic%d", i) - token := fmt.Sprintf("(%s=? AND %s_full=?) OR ", topicColumn, topicColumn) - query += " AND (" + strings.Repeat(token, len(sub)) + " 1=0)" + cond := repeat(fmt.Sprintf("(%s=? AND %s_full=?)", topicColumn, topicColumn), "OR", len(sub)) + query += " AND (" + cond + ")" for _, topic := range sub { queryParams = append(queryParams, truncBTo4(topic.Bytes())) queryParams = append(queryParams, topic.Bytes()) @@ -211,7 +200,9 @@ func FilterLogs( func DebugGetLogs(ctx context.Context, db *sql.DB, txHash common.TxHash) ([]*tracers.DebugLogs, error) { var queryParams []any - query := baseDebugEventsQuerySelect + " " + baseEventsJoin + "AND tx.hash = ? AND tx.full_hash = ?" + query := "select rel_address1_full, rel_address2_full, rel_address3_full, rel_address4_full, lifecycle_event, topic0_full, topic1_full, topic2_full, topic3_full, topic4_full, datablob, b.full_hash, b.height, tx.full_hash, tx.idx, log_idx, address_full" + + baseEventsJoin + + "AND tx.hash = ? AND tx.full_hash = ?" queryParams = append(queryParams, truncTo4(txHash), txHash.Bytes()) @@ -327,7 +318,7 @@ func loadLogs(ctx context.Context, db *sql.DB, requestingAccount *gethcommon.Add } result := make([]*types.Log, 0) - query := baseEventsQuerySelect + " " + baseEventsJoin + query := "select topic0_full, topic1_full, topic2_full, topic3_full, topic4_full, datablob, b.full_hash, b.height, tx.full_hash, tx.idx, log_idx, address_full" + " " + baseEventsJoin var queryParams []any // Add relevancy rules @@ -346,7 +337,7 @@ func loadLogs(ctx context.Context, db *sql.DB, requestingAccount *gethcommon.Add query += whereCondition queryParams = append(queryParams, whereParams...) - query += orderBy + query += " order by b.height, tx.idx asc" rows, err := db.QueryContext(ctx, query, queryParams...) if err != nil { diff --git a/go/enclave/storage/enclavedb/keyvalue.go b/go/enclave/storage/enclavedb/keyvalue.go index cb78491501..366b098011 100644 --- a/go/enclave/storage/enclavedb/keyvalue.go +++ b/go/enclave/storage/enclavedb/keyvalue.go @@ -5,24 +5,25 @@ import ( "database/sql" "errors" "fmt" - "strings" + "hash/fnv" "github.com/ethereum/go-ethereum/ethdb" "github.com/ten-protocol/go-ten/go/common/errutil" ) const ( - getQry = `select keyvalue.val from keyvalue where keyvalue.ky = ?;` + getQry = `select keyvalue.val from keyvalue where keyvalue.ky = ? and keyvalue.ky_full = ?;` // `replace` will perform insert or replace if existing and this syntax works for both sqlite and edgeless db - putQry = `replace into keyvalue values(?, ?);` - putQryBatch = `replace into keyvalue values` - putQryValues = `(?,?)` - delQry = `delete from keyvalue where keyvalue.ky = ?;` - searchQry = `select * from keyvalue where substring(keyvalue.ky, 1, ?) = ? and keyvalue.ky >= ? order by keyvalue.ky asc` + putQry = `replace into keyvalue (ky, ky_full, val) values(?, ?, ?);` + putQryBatch = `replace into keyvalue (ky, ky_full, val) values` + putQryValues = `(?,?,?)` + delQry = `delete from keyvalue where keyvalue.ky = ? and keyvalue.ky_full = ?;` + // todo - how is the performance of this? + searchQry = `select ky_full, val from keyvalue where substring(keyvalue.ky_full, 1, ?) = ? and keyvalue.ky_full >= ? order by keyvalue.ky_full asc` ) func Has(ctx context.Context, db *sql.DB, key []byte) (bool, error) { - err := db.QueryRowContext(ctx, getQry, key).Scan() + err := db.QueryRowContext(ctx, getQry, hash(key), key).Scan() if err != nil { if errors.Is(err, sql.ErrNoRows) { return false, nil @@ -35,7 +36,7 @@ func Has(ctx context.Context, db *sql.DB, key []byte) (bool, error) { func Get(ctx context.Context, db *sql.DB, key []byte) ([]byte, error) { var res []byte - err := db.QueryRowContext(ctx, getQry, key).Scan(&res) + err := db.QueryRowContext(ctx, getQry, hash(key), key).Scan(&res) if err != nil { if errors.Is(err, sql.ErrNoRows) { // make sure the error is converted to obscuro-wide not found error @@ -47,7 +48,7 @@ func Get(ctx context.Context, db *sql.DB, key []byte) ([]byte, error) { } func Put(ctx context.Context, db *sql.DB, key []byte, value []byte) error { - _, err := db.ExecContext(ctx, putQry, key, value) + _, err := db.ExecContext(ctx, putQry, hash(key), key, value) return err } @@ -58,14 +59,13 @@ func PutKeyValues(ctx context.Context, tx *sql.Tx, keys [][]byte, vals [][]byte) if len(keys) > 0 { // write the kv updates as a single update statement for increased efficiency - update := putQryBatch + strings.Repeat(putQryValues+",", len(keys)) - update = update[0 : len(update)-1] // remove trailing comma + update := putQryBatch + repeat(putQryValues, ",", len(keys)) values := make([]any, 0) for i := range keys { - values = append(values, keys[i], vals[i]) + values = append(values, hash(keys[i]), keys[i], vals[i]) } - _, err := tx.Exec(update, values...) + _, err := tx.ExecContext(ctx, update, values...) if err != nil { return fmt.Errorf("failed to exec k/v transaction statement. kv=%v, err=%w", values, err) } @@ -75,13 +75,13 @@ func PutKeyValues(ctx context.Context, tx *sql.Tx, keys [][]byte, vals [][]byte) } func Delete(ctx context.Context, db *sql.DB, key []byte) error { - _, err := db.ExecContext(ctx, delQry, key) + _, err := db.ExecContext(ctx, delQry, hash(key), key) return err } func DeleteKeys(ctx context.Context, db *sql.Tx, keys [][]byte) error { for _, del := range keys { - _, err := db.ExecContext(ctx, delQry, del) + _, err := db.ExecContext(ctx, delQry, hash(del), del) if err != nil { return err } @@ -108,3 +108,14 @@ func NewIterator(ctx context.Context, db *sql.DB, prefix []byte, start []byte) e rows: rows, } } + +// hash returns 4 bytes "hash" of the key to be indexed +// truncating is not sufficient because the keys are not random +func hash(key []byte) []byte { + h := fnv.New32() + _, err := h.Write(key) + if err != nil { + return nil + } + return h.Sum([]byte{}) +} diff --git a/go/enclave/storage/enclavedb/utils.go b/go/enclave/storage/enclavedb/utils.go index a47bc75d57..57f0dcbaf3 100644 --- a/go/enclave/storage/enclavedb/utils.go +++ b/go/enclave/storage/enclavedb/utils.go @@ -1,6 +1,10 @@ package enclavedb -import gethcommon "github.com/ethereum/go-ethereum/common" +import ( + "strings" + + gethcommon "github.com/ethereum/go-ethereum/common" +) func truncTo4(hash gethcommon.Hash) []byte { return truncBTo4(hash.Bytes()) @@ -15,3 +19,11 @@ func truncBTo4(bytes []byte) []byte { copy(c, b) return c } + +func repeat(token string, sep string, count int) string { + elems := make([]string, count) + for i := 0; i < count; i++ { + elems[i] = token + } + return strings.Join(elems, sep) +} diff --git a/go/enclave/storage/init/edgelessdb/001_init.sql b/go/enclave/storage/init/edgelessdb/001_init.sql index 7a20e05ef4..a865b9ea38 100644 --- a/go/enclave/storage/init/edgelessdb/001_init.sql +++ b/go/enclave/storage/init/edgelessdb/001_init.sql @@ -3,9 +3,12 @@ CREATE DATABASE obsdb; create table if not exists obsdb.keyvalue ( - ky varbinary(64), - val mediumblob NOT NULL, - primary key (ky) + id INTEGER AUTO_INCREMENT, + ky binary(4), + ky_full varbinary(64), + val mediumblob NOT NULL, + primary key (id), + INDEX (ky) ); GRANT ALL ON obsdb.keyvalue TO obscuro; @@ -114,13 +117,15 @@ GRANT ALL ON obsdb.tx TO obscuro; create table if not exists obsdb.exec_tx ( - id INTEGER AUTO_INCREMENT, - created_contract_address binary(20), - receipt mediumblob, - tx int NOT NULL, - batch int NOT NULL, + id INTEGER AUTO_INCREMENT, + created_contract_address binary(4), + created_contract_address_full binary(20), + receipt mediumblob, + tx int NOT NULL, + batch int NOT NULL, INDEX (batch), INDEX (tx), + INDEX (created_contract_address), primary key (id) ); GRANT ALL ON obsdb.exec_tx TO obscuro; diff --git a/go/enclave/storage/init/sqlite/001_init.sql b/go/enclave/storage/init/sqlite/001_init.sql index dd792cfa4a..dddff11d09 100644 --- a/go/enclave/storage/init/sqlite/001_init.sql +++ b/go/enclave/storage/init/sqlite/001_init.sql @@ -1,8 +1,11 @@ create table if not exists keyvalue ( - ky varbinary(64) primary key, - val mediumblob NOT NULL + id INTEGER PRIMARY KEY AUTOINCREMENT, + ky binary(4), + ky_full varbinary(64), + val mediumblob NOT NULL ); +create index IDX_KV on keyvalue (ky); create table if not exists config ( @@ -94,15 +97,17 @@ create index IDX_Tx_HASH on tx (hash); create table if not exists exec_tx ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - created_contract_address binary(20), - receipt mediumblob, + id INTEGER PRIMARY KEY AUTOINCREMENT, + created_contract_address binary(4), + created_contract_address_full binary(20), + receipt mediumblob, -- commenting out the fk until synthetic transactions are also stored --- tx binary(16) REFERENCES tx, - tx INTEGER NOT NULL, - batch INTEGER NOT NULL REFERENCES batch + tx INTEGER NOT NULL, + batch INTEGER NOT NULL REFERENCES batch ); -create index IX_EX_TX1 on exec_tx (tx); +create index IDX_EX_TX_TX on exec_tx (tx); +create index IDX_EX_TX_BATCH on exec_tx (batch); +create index IDX_EX_TX_CCA on exec_tx (created_contract_address); -- todo denormalize. Extract contract and user table and point topic0 and rel_addreses to it create table if not exists events diff --git a/go/enclave/storage/storage.go b/go/enclave/storage/storage.go index 1388403d5a..f48779fbb4 100644 --- a/go/enclave/storage/storage.go +++ b/go/enclave/storage/storage.go @@ -417,10 +417,8 @@ func (s *storageImpl) StoreBatch(ctx context.Context, batch *core.Batch, convert dbTx := s.db.NewDBTransaction() s.logger.Trace("write batch", log.BatchHashKey, batch.Hash(), "l1Proof", batch.Header.L1Proof, log.BatchSeqNoKey, batch.SeqNo()) - blockId, err := enclavedb.GetBlockId(ctx, s.db.GetSQLDB(), batch.Header.L1Proof) - if err != nil { - return err - } + // it is possible that the block is not available if this is a validator + blockId, _ := enclavedb.GetBlockId(ctx, s.db.GetSQLDB(), batch.Header.L1Proof) if err := enclavedb.WriteBatchAndTransactions(ctx, dbTx, batch, convertedHash, blockId); err != nil { return fmt.Errorf("could not write batch. Cause: %w", err)