Skip to content

Commit

Permalink
refactor database models for scribe
Browse files Browse the repository at this point in the history
  • Loading branch information
CryptoMaxPlanck committed Aug 25, 2022
1 parent d4c210a commit 96b7890
Show file tree
Hide file tree
Showing 5 changed files with 9 additions and 166 deletions.
2 changes: 1 addition & 1 deletion scribe/db/datastore/sql/base/base_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ func (s Store) DB() *gorm.DB {
// see: https://medium.com/@SaifAbid/slice-interfaces-8c78f8b6345d for an explanation of why we can't do this at initialization time
func GetAllModels() (allModels []interface{}) {
allModels = append(allModels,
&Log{}, &Receipt{}, &RawEthTX{}, &ProcessedEthTx{}, &LastIndexedInfo{},
&Log{}, &Receipt{}, &EthTx{}, &LastIndexedInfo{},
)
return allModels
}
Expand Down
24 changes: 2 additions & 22 deletions scribe/db/datastore/sql/base/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,31 +87,11 @@ type Receipt struct {
TransactionIndex uint64 `gorm:"transaction_index"`
}

// RawEthTX contains a raw evm transaction that is unsigned
// note: idx_id contains a composite index of (chain_id,nonce)
type RawEthTX struct {
gorm.Model
// From is the sender of the transaction
From string `gorm:"from"`
// To is the contract address the transaction was sent to.
To string `gorm:"index"`
// ChainID is the chain id the transaction hash will be sent on
ChainID uint64 `gorm:"column:chain_id;uniqueIndex:idx_id"`
// Nonce is the nonce of the raw evm tx
Nonce uint64 `gorm:"column:nonce;uniqueIndex:idx_id"`
// RawTx is the raw serialized transaction
RawTx []byte `gorm:"column:raw_tx"`
}

// ProcessedEthTx contains a processed ethereum transaction.
type ProcessedEthTx struct {
// EthTx contains a processed ethereum transaction.
type EthTx struct {
TxHash string `gorm:"tx_hash"`
// RawTx is the raw serialized transaction
RawTx []byte `gorm:"column:raw_tx"`
// RawEthTx is the txid that caused the event
RawEthTx uint
// OriginatingEvent is the event that originated the tx
EthTx RawEthTX `gorm:"foreignkey:RawEthTx"`
// GasFeeCap contains the gas fee cap stored in wei
GasFeeCap uint64
// GasTipCap contains the gas tip cap stored in wei
Expand Down
109 changes: 3 additions & 106 deletions scribe/db/datastore/sql/base/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,22 @@ package base

import (
"context"
"database/sql"
"fmt"
"github.com/synapsecns/sanguine/agents/db"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
)

// StoreProcessedTx stores a processed text.
func (s Store) StoreProcessedTx(ctx context.Context, tx *types.Transaction) error {
// StoreEthTx stores a processed text.
func (s Store) StoreEthTx(ctx context.Context, tx *types.Transaction) error {
marshalledTx, err := tx.MarshalBinary()
if err != nil {
return fmt.Errorf("could not marshall tx to binary: %w", err)
}

signer := types.LatestSignerForChainID(tx.ChainId())
sender, err := types.Sender(signer, tx)
if err != nil {
return fmt.Errorf("could not get sender for tx: %s: %w", tx.Hash(), err)
}

parentID, err := s.getRawTXIDByParams(ctx, tx.Nonce(), tx.ChainId(), sender)
if err != nil {
return fmt.Errorf("could not get parent tx: %w", err)
}

dbTx := s.DB().WithContext(ctx).Create(&ProcessedEthTx{
dbTx := s.DB().WithContext(ctx).Create(&EthTx{
TxHash: tx.Hash().String(),
RawTx: marshalledTx,
RawEthTx: parentID,
GasFeeCap: tx.GasFeeCap().Uint64(),
GasTipCap: tx.GasTipCap().Uint64(),
})
Expand All @@ -44,62 +29,6 @@ func (s Store) StoreProcessedTx(ctx context.Context, tx *types.Transaction) erro
return nil
}

// StoreRawTx stores a raw transaction.
func (s Store) StoreRawTx(ctx context.Context, tx *types.Transaction, chainID *big.Int, from common.Address) error {
toAddress := ""
if tx != nil {
toAddress = tx.To().String()
}

// sanity check for making sure transaction marshaled chainid matches derived chain id (if present)
hasID, newID := getChainID(tx)
if hasID {
if newID.Cmp(chainID) != 0 {
return fmt.Errorf("chainid mismatch, expected %d, got %d", chainID, newID)
}
}

marshalledTx, err := tx.MarshalBinary()
if err != nil {
return fmt.Errorf("could not marshall tx to binary: %w", err)
}

dbTx := s.DB().WithContext(ctx).Create(&RawEthTX{
From: from.String(),
To: toAddress,
ChainID: chainID.Uint64(),
Nonce: tx.Nonce(),
RawTx: marshalledTx,
})

if dbTx.Error != nil {
return fmt.Errorf("could not create raw tx: %w", dbTx.Error)
}

return nil
}

// getRawTXIDByParams by nonce/chain id gets the raw transaction id by a combination of the nonce and chain id
// this is used for storing processed txes.
func (s Store) getRawTXIDByParams(ctx context.Context, nonce uint64, chainID *big.Int, sender common.Address) (id uint, err error) {
var res RawEthTX
dbTx := s.DB().Select("ID").WithContext(ctx).Model(&RawEthTX{}).Where(RawEthTX{
ChainID: chainID.Uint64(),
Nonce: nonce,
From: sender.String(),
}).Find(&res)

if dbTx.RowsAffected == 0 {
return 0, db.ErrNotFound
}

if dbTx.Error != nil {
return 0, fmt.Errorf("could not get %T by chainID: %d and nonce: %d. error: %w", &RawEthTX{}, chainID.Uint64(), nonce, dbTx.Error)
}

return res.ID, nil
}

// getChainID gets the chain id from non-legacy transaction types
// it is used to check chainids against the chainid passed in the raw id.
func getChainID(tx *types.Transaction) (hasType bool, chainID *big.Int) {
Expand All @@ -115,35 +44,3 @@ func getChainID(tx *types.Transaction) (hasType bool, chainID *big.Int) {
return true, tx.ChainId()
}
}

// GetNonceForChainID gets a nonce for a chainid.
func (s Store) GetNonceForChainID(ctx context.Context, fromAddress common.Address, chainID *big.Int) (nonce uint64, err error) {
var newNonce sql.NullInt64

selectMaxNonce := "max(`nonce`)"

dbTx := s.DB().WithContext(ctx).Model(&RawEthTX{}).Select(selectMaxNonce).Where(RawEthTX{
From: fromAddress.String(),
ChainID: chainID.Uint64(),
}).Scan(&newNonce)

if dbTx.Error != nil {
return 0, fmt.Errorf("could not get nonce for chain id: %w", dbTx.Error)
}

// if no nonces, return the corresponding error.
if newNonce.Int64 == 0 {
// we need to check if any nonces exist first
var count int64
dbTx = s.DB().WithContext(ctx).Model(&RawEthTX{}).Where(RawEthTX{ChainID: chainID.Uint64(), From: fromAddress.String()}).Count(&count)
if dbTx.Error != nil {
return 0, fmt.Errorf("error getting count on %T: %w", &RawEthTX{}, dbTx.Error)
}

if count == 0 {
return 0, db.ErrNoNonceForChain
}
}

return uint64(newNonce.Int64), nil
}
9 changes: 2 additions & 7 deletions scribe/db/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package db

import (
"context"
"math/big"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
Expand All @@ -18,12 +17,8 @@ type EventDB interface {
StoreReceipt(ctx context.Context, receipt types.Receipt) error
// RetrieveReceiptByTxHash retrieves a receipt by tx hash
RetrieveReceiptByTxHash(ctx context.Context, txHash common.Hash) (receipt types.Receipt, err error)
// StoreRawTx stores a raw transaction
StoreRawTx(ctx context.Context, tx *types.Transaction, chainID *big.Int, from common.Address) error
// StoreProcessedTx stores a processed transaction
StoreProcessedTx(ctx context.Context, tx *types.Transaction) error
// GetNonceForChainID returns the nonce for a chain id
GetNonceForChainID(ctx context.Context, fromAddress common.Address, chainID *big.Int) (nonce uint64, err error)
// StoreEthTx stores a processed transaction
StoreEthTx(ctx context.Context, tx *types.Transaction) error
// StoreLastIndexed stores the last indexed for a contract address
StoreLastIndexed(ctx context.Context, contractAddress common.Address, chainID uint32, blockNumber uint64) error
// RetrieveLastIndexed retrieves the last indexed for a contract address
Expand Down
31 changes: 1 addition & 30 deletions scribe/db/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,44 +70,15 @@ func (t *DBSuite) TestTxInsertion() {

t.RunOnAllDBs(func(testDB db.EventDB) {
for _, testTx := range testTxes {
err := testDB.StoreRawTx(t.GetTestContext(), testTx, testTx.ChainId(), signer.Address())
Nil(t.T(), err)

// TODO: retrieve raw tx

transactor, err := signer.GetTransactor(testTx.ChainId())
Nil(t.T(), err)

signedTx, err := transactor.Signer(signer.Address(), testTx)
Nil(t.T(), err)

err = testDB.StoreProcessedTx(t.GetTestContext(), signedTx)
err = testDB.StoreEthTx(t.GetTestContext(), signedTx)
Nil(t.T(), err)
// TODO: retrieve the processed tx
}
})
}

// / make sure tx doesn't conflict on both chains.
func (t *DBSuite) TestTxNonceQueryMultiChain() {
fakeTx := testTxes[0]
fakeTx2 := testTxes[1]

t.RunOnAllDBs(func(testDB db.EventDB) {
from := common.BigToAddress(new(big.Int).SetUint64(gofakeit.Uint64()))

err := testDB.StoreRawTx(t.GetTestContext(), fakeTx, fakeTx.ChainId(), from)
Nil(t.T(), err)

err = testDB.StoreRawTx(t.GetTestContext(), fakeTx2, fakeTx2.ChainId(), from)
Nil(t.T(), err)

nonce1, err := testDB.GetNonceForChainID(t.GetTestContext(), from, fakeTx.ChainId())
Nil(t.T(), err)
Equal(t.T(), nonce1, fakeTx.Nonce())

nonce2, err := testDB.GetNonceForChainID(t.GetTestContext(), from, fakeTx2.ChainId())
Nil(t.T(), err)
Equal(t.T(), nonce2, fakeTx2.Nonce())
})
}

0 comments on commit 96b7890

Please sign in to comment.