Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement contractStore interface for sqlite #11

Merged
merged 2 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading