Skip to content

Commit

Permalink
Implement contractStore interface for sqlite
Browse files Browse the repository at this point in the history
  • Loading branch information
seanmcgary committed Sep 7, 2024
1 parent 51a662e commit e5e5ac9
Show file tree
Hide file tree
Showing 4 changed files with 280 additions and 31 deletions.
1 change: 0 additions & 1 deletion internal/contractStore/contractStore.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (
type ContractStore interface {
GetContractForAddress(address string) (*Contract, error)
FindOrCreateContract(address string, abiJson string, verified bool, bytecodeHash string, matchingContractAddress string) (*Contract, bool, error)
GetUnverifiedContractForAddress(address string) (*UnverifiedContract, error)
FindVerifiedContractWithMatchingBytecodeHash(bytecodeHash string, address string) (*Contract, error)
FindOrCreateProxyContract(blockNumber uint64, contractAddress string, proxyContractAddress string) (*ProxyContract, bool, error)
GetContractWithProxyContract(address string, atBlockNumber uint64) (*ContractsTree, error)
Expand Down
32 changes: 2 additions & 30 deletions internal/contractStore/pgContractStore/pgContractStore.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import (
)

type PgContractStore struct {
Db *gorm.DB
migrated bool
Logger *zap.Logger
Db *gorm.DB
Logger *zap.Logger
}

func NewPgContractStore(db *gorm.DB, l *zap.Logger) (*PgContractStore, error) {
Expand All @@ -23,21 +22,9 @@ func NewPgContractStore(db *gorm.DB, l *zap.Logger) (*PgContractStore, error) {
Logger: l,
}

cs.autoMigrate()

return cs, nil
}

func (p *PgContractStore) autoMigrate() {
if p.migrated {
return
}

// p.Db.AutoMigrate(&contractStore.Contract{})

p.migrated = true
}

func (p *PgContractStore) GetContractForAddress(address string) (*contractStore.Contract, error) {
var contract *contractStore.Contract

Expand Down Expand Up @@ -90,21 +77,6 @@ func (p *PgContractStore) FindOrCreateContract(
return upsertedContract, found, err
}

func (p *PgContractStore) GetUnverifiedContractForAddress(address string) (*contractStore.UnverifiedContract, error) {
var contract *contractStore.UnverifiedContract

result := p.Db.First(&contract, "contract_address = ?", strings.ToLower(address))
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
p.Logger.Sugar().Debugf("Verified contract not found in store '%s'", address)
return nil, nil
}
return nil, result.Error
}

return contract, nil
}

func (p *PgContractStore) FindVerifiedContractWithMatchingBytecodeHash(bytecodeHash string, address string) (*contractStore.Contract, error) {
query := `
select
Expand Down
257 changes: 257 additions & 0 deletions internal/contractStore/sqliteContractStore/sqliteContractStore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
package sqliteContractStore

import (
"errors"
"fmt"
"github.com/Layr-Labs/sidecar/internal/contractStore"
pg "github.com/Layr-Labs/sidecar/internal/postgres"
"github.com/Layr-Labs/sidecar/internal/sqlite"
"go.uber.org/zap"
"gorm.io/gorm"
"gorm.io/gorm/clause"
"strings"
)

type SqliteContractStore struct {
Db *gorm.DB
Logger *zap.Logger
}

func NewSqliteContractStore(db *gorm.DB, l *zap.Logger) *SqliteContractStore {
cs := &SqliteContractStore{
Db: db,
Logger: l,
}
return cs
}

func (s *SqliteContractStore) GetContractForAddress(address string) (*contractStore.Contract, error) {
var contract *contractStore.Contract

result := s.Db.First(&contract, "contract_address = ?", address)
if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound {
s.Logger.Sugar().Debugf("Contract not found in store '%s'", address)
return nil, nil
}
return nil, result.Error
}

return contract, nil
}

func (s *SqliteContractStore) FindOrCreateContract(
address string,
abiJson string,
verified bool,
bytecodeHash string,
matchingContractAddress string,
) (*contractStore.Contract, bool, error) {
found := false
upsertedContract, err := sqlite.WrapTxAndCommit[*contractStore.Contract](func(tx *gorm.DB) (*contractStore.Contract, error) {
contract := &contractStore.Contract{}
result := s.Db.First(&contract, "contract_address = ?", strings.ToLower(address))
if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, result.Error
}

// found contract
if contract.ContractAddress == address && contract.Id != 0 {
found = true
return contract, nil
}
contract = &contractStore.Contract{
ContractAddress: strings.ToLower(address),
ContractAbi: abiJson,
Verified: verified,
BytecodeHash: bytecodeHash,
MatchingContractAddress: matchingContractAddress,
}

result = s.Db.Create(contract)
if result.Error != nil {
return nil, result.Error
}

return contract, nil
}, nil, s.Db)
return upsertedContract, found, err
}

func (s *SqliteContractStore) indVerifiedContractWithMatchingBytecodeHash(bytecodeHash string, address string) (*contractStore.Contract, error) {
query := `
select
*
from contracts
where
bytecode_hash = ?
and verified = true
and matching_contract_address = ''
and contract_address != ?
order by id asc
limit 1`

var contract *contractStore.Contract
result := s.Db.Raw(query, bytecodeHash, address).Scan(&contract)
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
s.Logger.Sugar().Debugf("Verified contract not found in store '%s'", bytecodeHash)
return nil, nil
}
return nil, result.Error
}
return contract, nil
}

func (s *SqliteContractStore) FindOrCreateProxyContract(
blockNumber uint64,
contractAddress string,
proxyContractAddress string,
) (*contractStore.ProxyContract, bool, error) {
found := false
contractAddress = strings.ToLower(contractAddress)
proxyContractAddress = strings.ToLower(proxyContractAddress)

upsertedContract, err := pg.WrapTxAndCommit[*contractStore.ProxyContract](func(tx *gorm.DB) (*contractStore.ProxyContract, error) {
contract := &contractStore.ProxyContract{}
// Proxy contracts are unique on block_number && contract
result := tx.First(&contract, "contract_address = ? and block_number = ?", contractAddress, blockNumber)
if result.Error != nil && !errors.Is(result.Error, gorm.ErrRecordNotFound) {
return nil, result.Error
}

// found contract
if contract.ContractAddress == contractAddress {
found = true
return contract, nil
}
proxyContract := &contractStore.ProxyContract{
BlockNumber: int64(blockNumber),
ContractAddress: contractAddress,
ProxyContractAddress: proxyContractAddress,
}

result = tx.Model(&contractStore.ProxyContract{}).Clauses(clause.Returning{}).Create(proxyContract)
if result.Error != nil {
return nil, result.Error
}

return proxyContract, nil
}, nil, s.Db)
return upsertedContract, found, err
}

func (s *SqliteContractStore) GetContractWithProxyContract(address string, atBlockNumber uint64) (*contractStore.ContractsTree, error) {
address = strings.ToLower(address)

query := `select
c.contract_address as base_address,
c.contract_abi as base_abi,
pcc.contract_address as base_proxy_address,
pcc.contract_abi as base_proxy_abi,
pcclike.contract_address as base_proxy_like_address,
pcclike.contract_abi as base_proxy_like_abi,
clike.contract_address as base_like_address,
clike.contract_abi as base_like_abi
from contracts as c
left join (
select
*
from proxy_contracts
where contract_address = ? and block_number <= ?
order by block_number desc limit 1
) as pc on (1=1)
left join contracts as pcc on (pcc.contract_address = pc.proxy_contract_address)
left join contracts as pcclike on (pcc.matching_contract_address = pcclike.contract_address)
left join contracts as clike on (c.matching_contract_address = clike.contract_address)
where c.contract_address = ?`

contractTree := &contractStore.ContractsTree{}
result := s.Db.Raw(query,
address,
atBlockNumber,
address,
).Scan(&contractTree)

if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
s.Logger.Sugar().Debug(fmt.Sprintf("Contract not found '%s'", address))
return nil, nil
}
return nil, result.Error
}
if contractTree.BaseAddress == "" {
s.Logger.Sugar().Debug(fmt.Sprintf("Contract not found in store '%s'", address))
return nil, nil
}

return contractTree, nil
}

func (s *SqliteContractStore) SetContractCheckedForProxy(address string) (*contractStore.Contract, error) {
contract := &contractStore.Contract{}

result := s.Db.Model(contract).
Clauses(clause.Returning{}).
Where("contract_address = ?", strings.ToLower(address)).
Updates(&contractStore.Contract{
CheckedForProxy: true,
})

if result.Error != nil {
return nil, result.Error
}

return contract, nil
}

func (s *SqliteContractStore) GetProxyContract(address string) (*contractStore.ProxyContract, error) {
var proxyContract *contractStore.ProxyContract

result := s.Db.First(&proxyContract, "contract_address = ?", strings.ToLower(address))
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
s.Logger.Sugar().Debugf("Proxy contract not found in store '%s'", address)
return nil, nil
}
return nil, result.Error
}

return proxyContract, nil
}

func (s *SqliteContractStore) SetContractAbi(address string, abi string, verified bool) (*contractStore.Contract, error) {
contract := &contractStore.Contract{}

result := s.Db.Model(contract).
Clauses(clause.Returning{}).
Where("contract_address = ?", strings.ToLower(address)).
Updates(&contractStore.Contract{
ContractAbi: abi,
Verified: verified,
CheckedForAbi: true,
})

if result.Error != nil {
return nil, result.Error
}

return contract, nil
}

func (s *SqliteContractStore) SetContractMatchingContractAddress(address string, matchingContractAddress string) (*contractStore.Contract, error) {
contract := &contractStore.Contract{}

result := s.Db.Model(&contract).
Clauses(clause.Returning{}).
Where("contract_address = ?", strings.ToLower(address)).
Updates(&contractStore.Contract{
MatchingContractAddress: matchingContractAddress,
})

if result.Error != nil {
return nil, result.Error
}

return contract, nil
}
21 changes: 21 additions & 0 deletions internal/sqlite/sqlite.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package sqlite

import (
"fmt"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
Expand Down Expand Up @@ -32,3 +33,23 @@ func NewGormSqliteFromSqlite(sqlite gorm.Dialector) (*gorm.DB, error) {
}
return db, nil
}

func WrapTxAndCommit[T any](fn func(*gorm.DB) (T, error), db *gorm.DB, tx *gorm.DB) (T, error) {
exists := tx != nil

if !exists {
tx = db.Begin()
}

res, err := fn(tx)

if err != nil && !exists {
fmt.Printf("Rollback transaction\n")
tx.Rollback()
}
if err == nil && !exists {
fmt.Printf("Commit transaction\n")
tx.Commit()
}
return res, err
}

0 comments on commit e5e5ac9

Please sign in to comment.