Skip to content

Commit

Permalink
Extra state reports (#1287)
Browse files Browse the repository at this point in the history
* Feat: submit state reports to remote chains

* Feat: add SubmitStateReportWithAttestation on LightInbox

* WIP: verify number of reports on agent domain

* Feat: add prepareStateReport() for all report submissions

* Feat: add working state report verification to TestReportFraudulentStateInAttestation

* Feat: add verifyStateReport() helper

* Feat: add verifyStateReport() helper

* WIP: add RelayableAgentStatus model

* Cleanup: remove unused dispute handling

* WIP: working TestFraudulentStateInSnapshot with new agent status model

* WIP: fix crosstable logic, logs

* Feat: add NotaryOnDestination to test setup

* WIP: working test with NotaryOnDestination and Order clause in db query

* Cleanup: logs, dead code

* Fix: db tests

* Cleanup: lints

* Cleanup: remove Debug() gorm flags

* Feat: add updateAgentStatus() test helper

* Cleanup: update db comment

* Cleanup: correct db comment

* Fix: some lints

* Cleanup: lint

* Cleanup: clear nonce
  • Loading branch information
dwasse authored Aug 30, 2023
1 parent ce22b61 commit 6ad19c1
Show file tree
Hide file tree
Showing 19 changed files with 480 additions and 442 deletions.
4 changes: 2 additions & 2 deletions agents/agents/guard/db/agent_root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ func (t *DBSuite) TestGetSummitBlockNumberForRoot() {
Nil(t.T(), err)

// Call GetSummitBlockNumberForRoot for each agent root.
blockNumber, err := testDB.GetSummitBlockNumberForRoot(t.GetTestContext(), agentRootA)
blockNumber, err := testDB.GetSummitBlockNumberForRoot(t.GetTestContext(), agentRootA.String())
Nil(t.T(), err)
Equal(t.T(), blockNumberA, blockNumber)

blockNumber, err = testDB.GetSummitBlockNumberForRoot(t.GetTestContext(), agentRootB)
blockNumber, err = testDB.GetSummitBlockNumberForRoot(t.GetTestContext(), agentRootB.String())
Nil(t.T(), err)
Equal(t.T(), blockNumberB, blockNumber)
})
Expand Down
37 changes: 17 additions & 20 deletions agents/agents/guard/db/crosstable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ import (
"github.com/synapsecns/sanguine/agents/types"
)

func (t *DBSuite) TestGetUpdateAgentStatusParameters() {
func (t *DBSuite) TestGetRelayableAgentStatuses() {
t.RunOnAllDBs(func(testDB db.GuardDB) {
guardAddress := common.BigToAddress(big.NewInt(gofakeit.Int64()))

addressA := common.BigToAddress(big.NewInt(gofakeit.Int64()))
addressB := common.BigToAddress(big.NewInt(gofakeit.Int64()))
addressC := common.BigToAddress(big.NewInt(gofakeit.Int64()))
Expand Down Expand Up @@ -48,37 +46,36 @@ func (t *DBSuite) TestGetUpdateAgentStatusParameters() {
)
Nil(t.T(), err)

// Insert three rows into `Dispute`, two will have matching agent address to `AgentTree` rows and with status `Resolved`.
err = testDB.StoreDispute(
// Insert three rows into `RelayableAgentStatus`, two will have matching agent address to `AgentTree` rows and with status `Queued`.
chainA := gofakeit.Uint32()
chainB := chainA + 1
err = testDB.StoreRelayableAgentStatus(
t.GetTestContext(),
big.NewInt(gofakeit.Int64()),
types.Resolved,
guardAddress,
gofakeit.Uint32(),
addressA,
types.AgentFlagUnknown,
types.AgentFlagActive,
chainA,
)
Nil(t.T(), err)
err = testDB.StoreDispute(
err = testDB.StoreRelayableAgentStatus(
t.GetTestContext(),
big.NewInt(gofakeit.Int64()),
types.Resolved,
guardAddress,
gofakeit.Uint32(),
addressB,
types.AgentFlagUnknown,
types.AgentFlagActive,
chainA,
)
Nil(t.T(), err)
err = testDB.StoreDispute(
err = testDB.StoreRelayableAgentStatus(
t.GetTestContext(),
big.NewInt(gofakeit.Int64()),
types.Opened,
guardAddress,
gofakeit.Uint32(),
addressC,
types.AgentFlagUnknown,
types.AgentFlagActive,
chainB,
)
Nil(t.T(), err)

// Get the matching agent tree from the database.
agentTrees, err := testDB.GetUpdateAgentStatusParameters(t.GetTestContext())
agentTrees, err := testDB.GetRelayableAgentStatuses(t.GetTestContext(), chainA)
Nil(t.T(), err)

Equal(t.T(), 2, len(agentTrees))
Expand Down
29 changes: 13 additions & 16 deletions agents/agents/guard/db/guard_db.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"
agentTypes "github.com/synapsecns/sanguine/agents/types"
Expand All @@ -11,22 +10,20 @@ import (

// GuardDBWriter is the interface for writing to the guard's database.
type GuardDBWriter interface {
// StoreDispute stores a dispute.
StoreDispute(
// StoreRelayableAgentStatus stores a relayable agent status.
StoreRelayableAgentStatus(
ctx context.Context,
disputeIndex *big.Int,
disputeProcessedStatus agentTypes.DisputeProcessedStatus,
guardAddress common.Address,
notaryIndex uint32,
notaryAddress common.Address,
agentAddress common.Address,
staleFlag agentTypes.AgentFlagType,
updatedFlag agentTypes.AgentFlagType,
domain uint32,
) error

// UpdateDisputeProcessedStatus updates the DisputedProcessedStatus for a dispute.
UpdateDisputeProcessedStatus(
// UpdateAgentStatusRelayedState updates the relayed state for a relayable agent status.
UpdateAgentStatusRelayedState(
ctx context.Context,
guardAddress *common.Address,
notaryAddress *common.Address,
flag agentTypes.DisputeProcessedStatus,
agentAddress common.Address,
state agentTypes.AgentStatusRelayedState,
) error

// StoreAgentTree stores an agent tree.
Expand All @@ -48,10 +45,10 @@ type GuardDBWriter interface {

// GuardDBReader is the interface for reading from the guard's database.
type GuardDBReader interface {
// GetUpdateAgentStatusParameters gets eligible parameters for the updateAgentStatus() contract call.
GetUpdateAgentStatusParameters(ctx context.Context) ([]agentTypes.AgentTree, error)
// GetRelayableAgentStatuses gets eligible parameters for the updateAgentStatus() contract call.
GetRelayableAgentStatuses(ctx context.Context, chainID uint32) ([]agentTypes.AgentTree, error)
// GetSummitBlockNumberForRoot gets the summit block number for a given agent root.
GetSummitBlockNumberForRoot(ctx context.Context, agentRoot [32]byte) (uint64, error)
GetSummitBlockNumberForRoot(ctx context.Context, agentRoot string) (uint64, error)
}

// GuardDB is the interface for the guard's database.
Expand Down
7 changes: 3 additions & 4 deletions agents/agents/guard/db/sql/base/agent_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,11 @@ func (s Store) StoreAgentRoot(
}

// GetSummitBlockNumberForRoot gets the summit block number for a given agent root.
func (s Store) GetSummitBlockNumberForRoot(ctx context.Context, agentRoot [32]byte) (uint64, error) {
dbAgentRoot := common.BytesToHash(agentRoot[:]).String()

func (s Store) GetSummitBlockNumberForRoot(ctx context.Context, agentRoot string) (uint64, error) {
var blockNumber uint64
dbTx := s.DB().WithContext(ctx).
Where(fmt.Sprintf("%s = ?", AgentRootFieldName), dbAgentRoot).
Where(fmt.Sprintf("%s = ?", AgentRootFieldName), agentRoot).
Order(fmt.Sprintf("%s ASC", BlockNumberFieldName)).
Limit(1).
Model(&AgentRoot{}).
Pluck(BlockNumberFieldName, &blockNumber)
Expand Down
2 changes: 1 addition & 1 deletion agents/agents/guard/db/sql/base/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ func (s Store) SubmitterDB() submitterDB.Service {
// GetAllModels gets all models to migrate.
// 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, &Dispute{}, &AgentTree{}, &AgentRoot{})
allModels = append(allModels, &RelayableAgentStatus{}, &AgentTree{}, &AgentRoot{})
allModels = append(allModels, txdb.GetAllModels()...)
return allModels
}
57 changes: 35 additions & 22 deletions agents/agents/guard/db/sql/base/crosstable.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,42 +11,53 @@ import (
"github.com/synapsecns/sanguine/core/dbcommon"
)

// GetUpdateAgentStatusParameters gets the parameters for updating the agent status with the following steps:
// 1. Outer join the `AgentTree` table on the `Dispute` table on the `NotaryAddress` <-> `AgentAddress` fields.
// 2. Filter the rows where the `DisputeProcessedStatus` is `Resolved`.
// 3. Return each of remaining rows' `AgentRoot`, `AgentAddress`, and `Proof` fields.
func (s Store) GetUpdateAgentStatusParameters(ctx context.Context) ([]agentTypes.AgentTree, error) {
type agentTreeWithStatus struct {
AgentTree
AgentDomain uint32
UpdatedAgentFlag agentTypes.AgentFlagType
}

// GetRelayableAgentStatuses gets the parameters for updating the agent status with the following steps:
// 1. Load the `AgentTree` table.
// 2. Filter the rows where the `AgentStatusRelayedState` is `Queued`, and the `Domain` is the given `chainID`.
// 3. Outer join the `AgentTree` table on the `RelayableAgentStatus` table on the `AgentAddress` fields.
// 4. Return all fields in the `AgentTree` table as well as `UpdatedFlag` from the `RelayableAgentStatus` table.
func (s Store) GetRelayableAgentStatuses(ctx context.Context, chainID uint32) ([]agentTypes.AgentTree, error) {
agentTreesTableName, err := dbcommon.GetModelName(s.DB(), &AgentTree{})
if err != nil {
return nil, fmt.Errorf("failed to get agent trees table name: %w", err)
}

disputesTableName, err := dbcommon.GetModelName(s.DB(), &Dispute{})
relayableAgentStatusesTableName, err := dbcommon.GetModelName(s.DB(), &RelayableAgentStatus{})
if err != nil {
return nil, fmt.Errorf("failed to get disputes table name: %w", err)
return nil, fmt.Errorf("failed to get relayable agent statuses table name: %w", err)
}

query, err := interpol.WithMap(
`
SELECT * FROM {agentTreesTable} AS aTable
SELECT aTable.*, rTable.{updatedFlag}
FROM {agentTreesTable} AS aTable
JOIN (
SELECT * FROM {disputesTable} WHERE {disputeProcessedStatus} = ?
) AS dTable
ON aTable.{agentAddress} = dTable.{notaryAddress}
SELECT * FROM {relayableAgentStatusesTable}
WHERE {agentStatusRelayedState} = ?
AND {domain} = ?
) AS rTable
ON aTable.{agentAddress} = rTable.{agentAddress}
`,
map[string]string{
"agentTreesTable": agentTreesTableName,
"disputesTable": disputesTableName,
"agentAddress": AgentAddressFieldName,
"notaryAddress": NotaryAddressFieldName,
"disputeProcessedStatus": DisputeProcessedStatusFieldName,
"domain": DomainFieldName,
"updatedFlag": UpdatedFlagFieldName,
"agentTreesTable": agentTreesTableName,
"relayableAgentStatusesTable": relayableAgentStatusesTableName,
"agentStatusRelayedState": AgentStatusRelayedStateFieldName,
"agentAddress": AgentAddressFieldName,
})
if err != nil {
return nil, fmt.Errorf("failed to interpolate query: %w", err)
}

var dbAgentTrees []AgentTree
err = s.DB().WithContext(ctx).Raw(query, agentTypes.Resolved).Scan(&dbAgentTrees).Error
var dbAgentTrees []agentTreeWithStatus
err = s.DB().WithContext(ctx).Raw(query, agentTypes.Queued, chainID).Scan(&dbAgentTrees).Error
if err != nil {
return nil, fmt.Errorf("failed to get agent trees: %w", err)
}
Expand All @@ -60,10 +71,12 @@ func (s Store) GetUpdateAgentStatusParameters(ctx context.Context) ([]agentTypes
return nil, fmt.Errorf("could not unmarshal proof: %w", err)
}
agentTrees = append(agentTrees, agentTypes.AgentTree{
AgentRoot: tree.AgentRoot,
AgentAddress: common.HexToAddress(tree.AgentAddress),
BlockNumber: tree.BlockNumber,
Proof: proofBytes,
AgentRoot: tree.AgentRoot,
AgentAddress: common.HexToAddress(tree.AgentAddress),
AgentDomain: chainID,
UpdatedAgentFlag: tree.UpdatedAgentFlag,
BlockNumber: tree.BlockNumber,
Proof: proofBytes,
})
}
return agentTrees, nil
Expand Down
71 changes: 0 additions & 71 deletions agents/agents/guard/db/sql/base/dispute.go

This file was deleted.

42 changes: 21 additions & 21 deletions agents/agents/guard/db/sql/base/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,40 @@ import (
func init() {
namer := dbcommon.NewNamer(GetAllModels())
AgentRootFieldName = namer.GetConsistentName("AgentRoot")
DisputeIndexFieldName = namer.GetConsistentName("DisputeIndex")
AgentAddressFieldName = namer.GetConsistentName("AgentAddress")
BlockNumberFieldName = namer.GetConsistentName("BlockNumber")
DisputeProcessedStatusFieldName = namer.GetConsistentName("DisputeProcessedStatus")
NotaryAddressFieldName = namer.GetConsistentName("NotaryAddress")
AgentStatusRelayedStateFieldName = namer.GetConsistentName("AgentStatusRelayedState")
DomainFieldName = namer.GetConsistentName("Domain")
UpdatedFlagFieldName = namer.GetConsistentName("UpdatedFlag")
}

var (
// AgentRootFieldName is the field name of the agent root.
AgentRootFieldName string
// DisputeIndexFieldName is the field name of the agent root.
DisputeIndexFieldName string
// AgentAddressFieldName gets the agent address field name.
AgentAddressFieldName string
// BlockNumberFieldName gets the agent block number field name.
BlockNumberFieldName string
// DisputeProcessedStatusFieldName gets the dispute processed status field name.
DisputeProcessedStatusFieldName string
// NotaryAddressFieldName gets the notary address field name.
NotaryAddressFieldName string
// AgentStatusRelayedStateFieldName gets the relayable agent status field name.
AgentStatusRelayedStateFieldName string
// DomainFieldName gets the agent domain field name.
DomainFieldName string
// UpdatedFlagFieldName gets the updated flag field name.
UpdatedFlagFieldName string
)

// Dispute is a dispute between two agents.
type Dispute struct {
// DisputeIndex is the index of the dispute on the BondingManager.
DisputeIndex uint64 `gorm:"column:dispute_index;primaryKey"`
// DisputeProcessedStatus indicates the status of the dispute.
DisputeProcessedStatus agentTypes.DisputeProcessedStatus `gorm:"column:dispute_processed_status"`
// GuardAddress is the address of the guard.
GuardAddress string `gorm:"column:guard_address"`
// NotaryIndex is the index of the notary on the BondingManager.
NotaryIndex uint64 `gorm:"column:notary_index"`
// NotaryAddress is the address of the notary.
NotaryAddress string `gorm:"column:notary_address"`
// RelayableAgentStatus is used for tracking agent statuses that are out of
// sync and need to be relayed to a remote chain.
type RelayableAgentStatus struct {
AgentAddress string `gorm:"column:agent_address"`
// StaleFlag is the old flag that needs to be updated.
StaleFlag agentTypes.AgentFlagType `gorm:"column:stale_flag"`
// UpdatedFlag is the new flag value that should be relayed.
UpdatedFlag agentTypes.AgentFlagType `gorm:"column:updated_flag"`
// Domain is the domain of the agent status.
Domain uint32 `gorm:"column:domain"`
// AgentStatusRelayedState is the state of the relayable agent status.
AgentStatusRelayedState agentTypes.AgentStatusRelayedState `gorm:"column:agent_status_relayed_state"`
}

// AgentTree is the state of an agent tree on Summit.
Expand Down
Loading

0 comments on commit 6ad19c1

Please sign in to comment.