Skip to content

Commit

Permalink
Merge pull request #11 from Layr-Labs/sm-contractSqlite
Browse files Browse the repository at this point in the history
Implement contractStore interface for sqlite
  • Loading branch information
seanmcgary authored Sep 7, 2024
2 parents 51a662e + 6ddfaff commit 373bc35
Show file tree
Hide file tree
Showing 6 changed files with 463 additions and 53 deletions.
3 changes: 0 additions & 3 deletions internal/contractStore/contractStore.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,16 @@ 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)
SetContractCheckedForProxy(address string) (*Contract, error)
GetProxyContract(address string) (*ProxyContract, error)
SetContractAbi(address string, abi string, verified bool) (*Contract, error)
SetContractMatchingContractAddress(address string, matchingContractAddress string) (*Contract, error)
}

// Tables
type Contract struct {
Id uint64
ContractAddress string
ContractAbi string
MatchingContractAddress string
Expand Down
49 changes: 3 additions & 46 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 @@ -68,7 +55,7 @@ func (p *PgContractStore) FindOrCreateContract(
}

// found contract
if contract.ContractAddress == address && contract.Id != 0 {
if contract.ContractAddress == address {
found = true
return contract, nil
}
Expand All @@ -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 Expand Up @@ -232,21 +204,6 @@ func (p *PgContractStore) SetContractCheckedForProxy(address string) (*contractS
return contract, nil
}

func (p *PgContractStore) GetProxyContract(address string) (*contractStore.ProxyContract, error) {
var proxyContract *contractStore.ProxyContract

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

return proxyContract, nil
}

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

Expand Down
243 changes: 243 additions & 0 deletions internal/contractStore/sqliteContractStore/sqliteContractStore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
package sqliteContractStore

import (
"database/sql"
"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 {
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) FindVerifiedContractWithMatchingBytecodeHash(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 rowid 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 = @contractAddress and block_number <= @blockNumber
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 = @contractAddress
`
contractTree := &contractStore.ContractsTree{}
result := s.Db.Raw(query,
sql.Named("contractAddress", address),
sql.Named("blockNumber", atBlockNumber),
).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) 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
}
Loading

0 comments on commit 373bc35

Please sign in to comment.