Skip to content

Commit

Permalink
Submitter and Retries for Fraud (#1288)
Browse files Browse the repository at this point in the history
* wrap each func. need to fix test

* working tests with submitter

* remove test file

* start to sql queries for crosstable dispute handling

* retries and print statements to be deleted

* undo generic retry

* remove test files

* remove other test file

* working tests

* remove print statement in executor

* implement wasse suggestions
  • Loading branch information
CryptoMaxPlanck authored Aug 31, 2023
1 parent 6ad19c1 commit 7322033
Show file tree
Hide file tree
Showing 11 changed files with 449 additions and 404 deletions.
465 changes: 350 additions & 115 deletions agents/agents/guard/fraud.go

Large diffs are not rendered by default.

44 changes: 29 additions & 15 deletions agents/agents/guard/fraud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/ethereum/go-ethereum/common"
. "github.com/stretchr/testify/assert"
"github.com/synapsecns/sanguine/agents/agents/executor"
"github.com/synapsecns/sanguine/agents/agents/executor/db"
"github.com/synapsecns/sanguine/agents/agents/guard"
"github.com/synapsecns/sanguine/agents/config"
execConfig "github.com/synapsecns/sanguine/agents/config/executor"
Expand Down Expand Up @@ -97,14 +98,15 @@ func (g GuardSuite) bumpBackend(backend backends.SimulatedTestBackend, contract
backend.WaitForConfirmation(g.GetTestContext(), bumpTx)
}

func (g GuardSuite) updateAgentStatus(lightManager domains.LightManagerContract, bondedSigner, unbondedSigner signer.Signer) {
func (g GuardSuite) updateAgentStatus(lightManager domains.LightManagerContract, bondedSigner, unbondedSigner signer.Signer, chainID uint32) {
agentStatus, err := g.SummitDomainClient.BondingManager().GetAgentStatus(g.GetTestContext(), bondedSigner.Address())
Nil(g.T(), err)
agentProof, err := g.SummitDomainClient.BondingManager().GetProof(g.GetTestContext(), bondedSigner.Address())
Nil(g.T(), err)
transactor, err := unbondedSigner.GetTransactor(g.GetTestContext(), big.NewInt(int64(chainID)))
Nil(g.T(), err)
_, err = lightManager.UpdateAgentStatus(
g.GetTestContext(),
unbondedSigner,
transactor,
bondedSigner.Address(),
agentStatus,
agentProof,
Expand Down Expand Up @@ -161,7 +163,7 @@ func (g GuardSuite) TestFraudulentStateInSnapshot() {
}()

// Update the agent status on Origin.
g.updateAgentStatus(g.OriginDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner)
g.updateAgentStatus(g.OriginDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner, uint32(g.TestBackendOrigin.GetChainID()))

// Verify that the agent is marked as Active
status, err := g.OriginDomainClient.LightManager().GetAgentStatus(g.GetTestContext(), g.NotaryBondedSigner.Address())
Expand Down Expand Up @@ -336,8 +338,8 @@ func (g GuardSuite) TestFraudulentAttestationOnDestination() {
NotNil(g.T(), err)

// Update the agent status of the Guard and Notary.
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.GuardBondedSigner, g.GuardUnbondedSigner)
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner)
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.GuardBondedSigner, g.GuardUnbondedSigner, uint32(g.TestBackendDestination.GetChainID()))
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner, uint32(g.TestBackendDestination.GetChainID()))

// Submit the attestation
tx, err := g.DestinationDomainClient.LightInbox().SubmitAttestation(
Expand Down Expand Up @@ -439,9 +441,9 @@ func (g GuardSuite) TestReportFraudulentStateInAttestation() {
NotNil(g.T(), err)

// Update the agent status of the Guard and Notary.
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.GuardBondedSigner, g.GuardUnbondedSigner)
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner)
g.updateAgentStatus(g.OriginDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner)
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.GuardBondedSigner, g.GuardUnbondedSigner, uint32(g.TestBackendDestination.GetChainID()))
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner, uint32(g.TestBackendDestination.GetChainID()))
g.updateAgentStatus(g.OriginDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner, uint32(g.TestBackendOrigin.GetChainID()))

// Submit the snapshot with a guard
guardSnapshotSignature, encodedSnapshot, _, err := fraudulentSnapshot.SignSnapshot(g.GetTestContext(), g.GuardBondedSigner)
Expand Down Expand Up @@ -638,7 +640,7 @@ func (g GuardSuite) TestInvalidReceipt() {
// Build and sign a receipt
snapshotRoot, _, err := snapshot.SnapshotRootAndProofs()
Nil(g.T(), err)
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner)
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner, uint32(g.TestBackendDestination.GetChainID()))
messageHash, err := message.ToLeaf()
Nil(g.T(), err)
receipt := types.NewReceipt(
Expand Down Expand Up @@ -862,11 +864,11 @@ func (g GuardSuite) TestUpdateAgentStatusOnRemote() {
NotNil(g.T(), err)

// Update the agent status of the Guard and Notaries.
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.GuardBondedSigner, g.GuardUnbondedSigner)
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner)
g.updateAgentStatus(g.OriginDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner)
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.NotaryOnDestinationBondedSigner, g.NotaryOnDestinationUnbondedSigner)
g.updateAgentStatus(g.OriginDomainClient.LightManager(), g.NotaryOnDestinationBondedSigner, g.NotaryOnDestinationUnbondedSigner)
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.GuardBondedSigner, g.GuardUnbondedSigner, destination)
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner, destination)
g.updateAgentStatus(g.OriginDomainClient.LightManager(), g.NotaryBondedSigner, g.NotaryUnbondedSigner, uint32(g.TestBackendOrigin.GetChainID()))
g.updateAgentStatus(g.DestinationDomainClient.LightManager(), g.NotaryOnDestinationBondedSigner, g.NotaryOnDestinationUnbondedSigner, destination)
g.updateAgentStatus(g.OriginDomainClient.LightManager(), g.NotaryOnDestinationBondedSigner, g.NotaryOnDestinationUnbondedSigner, uint32(g.TestBackendOrigin.GetChainID()))

// Submit the snapshot with a guard
guardSnapshotSignature, encodedSnapshot, _, err := fraudulentSnapshot.SignSnapshot(g.GetTestContext(), g.GuardBondedSigner)
Expand Down Expand Up @@ -952,6 +954,18 @@ func (g GuardSuite) TestUpdateAgentStatusOnRemote() {
g.TestBackendSummit.WaitForConfirmation(g.GetTestContext(), tx)
g.bumpBackends()

// Wait for the executor to have attestations before increasing time.
summitChainID := uint32(g.TestBackendSummit.GetChainID())
attestationNonce := uint32(2)
g.Eventually(func() bool {
attest, err := g.ExecutorTestDB.GetAttestation(g.GetTestContext(), db.DBAttestation{
Destination: &summitChainID,
AttestationNonce: &attestationNonce,
})
Nil(g.T(), err)
return attest != nil
})

// Increase EVM time to allow agent status to be updated to Slashed on summit.
optimisticPeriodSeconds := int64(86400)
increaseEvmTime := func(backend backends.SimulatedTestBackend, seconds int64) {
Expand Down
5 changes: 5 additions & 0 deletions agents/agents/guard/guard.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/synapsecns/sanguine/agents/contracts/lightinbox"
"github.com/synapsecns/sanguine/agents/contracts/origin"
"github.com/synapsecns/sanguine/core/metrics"
"github.com/synapsecns/sanguine/core/retry"
signerConfig "github.com/synapsecns/sanguine/ethergo/signer/config"
"github.com/synapsecns/sanguine/ethergo/submitter"
omnirpcClient "github.com/synapsecns/sanguine/services/omnirpc/client"
Expand Down Expand Up @@ -57,6 +58,7 @@ type Guard struct {
lightManagerParser lightmanager.Parser
boundOrigins map[uint32]*origin.Origin
txSubmitter submitter.TransactionSubmitter
retryConfig []retry.WithBackoffConfigurator
guardDB db.GuardDB
}

Expand Down Expand Up @@ -176,6 +178,9 @@ func NewGuard(ctx context.Context, cfg config.AgentConfig, omniRPCClient omnirpc
guard.originLatestStates = make(map[uint32]types.State, len(guard.domains))
guard.handler = handler
guard.txSubmitter = submitter.NewTransactionSubmitter(handler, guard.unbondedSigner, omniRPCClient, guardDB.SubmitterDB(), &cfg.SubmitterConfig)
guard.retryConfig = []retry.WithBackoffConfigurator{
retry.WithMaxAttemptTime(time.Second * time.Duration(cfg.MaxRetrySeconds)),
}
guard.guardDB = guardDB

return guard, nil
Expand Down
19 changes: 13 additions & 6 deletions agents/agents/notary/notary.go
Original file line number Diff line number Diff line change
Expand Up @@ -437,12 +437,19 @@ func (n *Notary) registerNotaryOnDestination(parentCtx context.Context) bool {
))
return false
}
_, err = n.destinationDomain.LightManager().UpdateAgentStatus(
ctx,
n.unbondedSigner,
n.bondedSigner.Address(),
agentStatus,
agentProof)
_, err = n.txSubmitter.SubmitTransaction(ctx, big.NewInt(int64(n.destinationDomain.Config().DomainID)), func(transactor *bind.TransactOpts) (tx *ethTypes.Transaction, err error) {
tx, err = n.destinationDomain.LightManager().UpdateAgentStatus(
transactor,
n.bondedSigner.Address(),
agentStatus,
agentProof,
)
if err != nil {
return nil, fmt.Errorf("could not update agent status: %w", err)
}

return
})
if err != nil {
span.AddEvent("Error updating agent status", trace.WithAttributes(
attribute.String("err", err.Error()),
Expand Down
2 changes: 2 additions & 0 deletions agents/config/agent_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ type AgentConfig struct {
DBPrefix string `yaml:"db_prefix"`
// SubmitterConfig is the config for the submitter.
SubmitterConfig submitterConfig.Config `yaml:"submitter_config"`
// MaxRetrySeconds is the maximum number of seconds to retry an RPC call (not a transaction).
MaxRetrySeconds uint32 `yaml:"max_retry_seconds"`
}

// IsValid makes sure the config is valid. This is done by calling IsValid() on each
Expand Down
29 changes: 14 additions & 15 deletions agents/domains/domain.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,23 @@ type SummitContract interface {
// InboxContract contains the interface for the inbox.
type InboxContract interface {
// SubmitStateReportWithSnapshot reports to the inbox that a state within a snapshot is invalid.
SubmitStateReportWithSnapshot(ctx context.Context, signer signer.Signer, stateIndex int64, signature signer.Signature, snapPayload []byte, snapSignature []byte) (tx *ethTypes.Transaction, err error)
SubmitStateReportWithSnapshot(transactor *bind.TransactOpts, stateIndex int64, signature signer.Signature, snapPayload []byte, snapSignature []byte) (tx *ethTypes.Transaction, err error)
// SubmitSnapshot submits a snapshot to the inbox (via the Inbox).
SubmitSnapshot(transactor *bind.TransactOpts, signer signer.Signer, encodedSnapshot []byte, signature signer.Signature) (tx *ethTypes.Transaction, err error)
// SubmitSnapshotCtx
SubmitSnapshotCtx(ctx context.Context, signer signer.Signer, encodedSnapshot []byte, signature signer.Signature) (tx *ethTypes.Transaction, err error)
// VerifyAttestation verifies a snapshot on the inbox.
VerifyAttestation(ctx context.Context, signer signer.Signer, attestation []byte, attSignature []byte) (tx *ethTypes.Transaction, err error)
VerifyAttestation(transactor *bind.TransactOpts, attestation []byte, attSignature []byte) (tx *ethTypes.Transaction, err error)
// VerifyStateWithAttestation verifies a state with attestation.
VerifyStateWithAttestation(ctx context.Context, signer signer.Signer, stateIndex int64, snapPayload []byte, attPayload []byte, attSignature []byte) (tx *ethTypes.Transaction, err error)
// SubmitStateReportWithAttestation submits a state report corresponding to an attesation for an invalid state.
SubmitStateReportWithAttestation(ctx context.Context, signer signer.Signer, stateIndex int64, signature signer.Signature, snapPayload, attPayload, attSignature []byte) (tx *ethTypes.Transaction, err error)
SubmitStateReportWithAttestation(transactor *bind.TransactOpts, stateIndex int64, signature signer.Signature, snapPayload, attPayload, attSignature []byte) (tx *ethTypes.Transaction, err error)
// SubmitReceipt submits a receipt to the inbox.
SubmitReceipt(ctx context.Context, signer signer.Signer, rcptPayload []byte, rcptSignature signer.Signature, paddedTips *big.Int, headerHash [32]byte, bodyHash [32]byte) (tx *ethTypes.Transaction, err error)
// VerifyReceipt verifies a receipt on the inbox.
VerifyReceipt(ctx context.Context, signer signer.Signer, rcptPayload []byte, rcptSignature []byte) (tx *ethTypes.Transaction, err error)
VerifyReceipt(transactor *bind.TransactOpts, rcptPayload []byte, rcptSignature []byte) (tx *ethTypes.Transaction, err error)
// SubmitReceiptReport submits a receipt report to the inbox.
SubmitReceiptReport(ctx context.Context, signer signer.Signer, rcptPayload []byte, rcptSignature []byte, rrSignature []byte) (tx *ethTypes.Transaction, err error)
SubmitReceiptReport(transactor *bind.TransactOpts, rcptPayload []byte, rcptSignature []byte, rrSignature []byte) (tx *ethTypes.Transaction, err error)
}

// BondingManagerContract contains the interface for the bonding manager.
Expand All @@ -110,7 +110,7 @@ type BondingManagerContract interface {
// GetDisputeStatus gets the dispute status for the given agent.
GetDisputeStatus(ctx context.Context, agent common.Address) (disputeStatus types.DisputeStatus, err error)
// CompleteSlashing completes the slashing of an agent.
CompleteSlashing(ctx context.Context, signer signer.Signer, domain uint32, agent common.Address, proof [][32]byte) (tx *ethTypes.Transaction, err error)
CompleteSlashing(transactor *bind.TransactOpts, domain uint32, agent common.Address, proof [][32]byte) (tx *ethTypes.Transaction, err error)
// GetAgent gets an agent status and address for a given agent index.
GetAgent(ctx context.Context, index *big.Int) (types.AgentStatus, common.Address, error)
}
Expand All @@ -134,7 +134,7 @@ type DestinationContract interface {
// LightInboxContract contains the interface for the light inbox.
type LightInboxContract interface {
// SubmitStateReportWithSnapshot reports to the inbox that a state within a snapshot is invalid.
SubmitStateReportWithSnapshot(ctx context.Context, signer signer.Signer, stateIndex int64, signature signer.Signature, snapPayload []byte, snapSignature []byte) (tx *ethTypes.Transaction, err error)
SubmitStateReportWithSnapshot(transactor *bind.TransactOpts, stateIndex int64, signature signer.Signature, snapPayload []byte, snapSignature []byte) (tx *ethTypes.Transaction, err error)
// SubmitAttestation submits an attestation to the destination chain (via the light inbox contract)
SubmitAttestation(
transactor *bind.TransactOpts,
Expand All @@ -144,15 +144,15 @@ type LightInboxContract interface {
snapGas []*big.Int,
) (tx *ethTypes.Transaction, err error)
// SubmitStateReportWithAttestation submits a state report corresponding to an attesation for an invalid state.
SubmitStateReportWithAttestation(ctx context.Context, signer signer.Signer, stateIndex int64, signature signer.Signature, snapPayload, attPayload, attSignature []byte) (tx *ethTypes.Transaction, err error)
SubmitStateReportWithAttestation(transactor *bind.TransactOpts, stateIndex int64, signature signer.Signature, snapPayload, attPayload, attSignature []byte) (tx *ethTypes.Transaction, err error)
// VerifyStateWithSnapshot verifies a state within a snapshot.
VerifyStateWithSnapshot(ctx context.Context, signer signer.Signer, stateIndex int64, snapPayload []byte, snapSignature []byte) (tx *ethTypes.Transaction, err error)
VerifyStateWithSnapshot(transactor *bind.TransactOpts, stateIndex int64, snapPayload []byte, snapSignature []byte) (tx *ethTypes.Transaction, err error)
// SubmitAttestationReport submits an attestation report to the inbox (via the light inbox contract)
SubmitAttestationReport(ctx context.Context, signer signer.Signer, attestation, arSignature, attSignature []byte) (tx *ethTypes.Transaction, err error)
SubmitAttestationReport(transactor *bind.TransactOpts, attestation, arSignature, attSignature []byte) (tx *ethTypes.Transaction, err error)
// VerifyStateWithAttestation verifies a state with attestation.
VerifyStateWithAttestation(ctx context.Context, signer signer.Signer, stateIndex int64, snapPayload []byte, attPayload []byte, attSignature []byte) (tx *ethTypes.Transaction, err error)
VerifyStateWithAttestation(transactor *bind.TransactOpts, stateIndex int64, snapPayload []byte, attPayload []byte, attSignature []byte) (tx *ethTypes.Transaction, err error)
// VerifyReceipt verifies a receipt on the inbox.
VerifyReceipt(ctx context.Context, signer signer.Signer, rcptPayload []byte, rcptSignature []byte) (tx *ethTypes.Transaction, err error)
VerifyReceipt(transactor *bind.TransactOpts, signer signer.Signer, rcptPayload []byte, rcptSignature []byte) (tx *ethTypes.Transaction, err error)
}

// LightManagerContract contains the interface for the light manager.
Expand All @@ -163,11 +163,10 @@ type LightManagerContract interface {
GetAgentRoot(ctx context.Context) ([32]byte, error)
// UpdateAgentStatus updates the agent status on the remote chain.
UpdateAgentStatus(
ctx context.Context,
unbondedSigner signer.Signer,
transactor *bind.TransactOpts,
agentAddress common.Address,
agentStatus types.AgentStatus,
agentProof [][32]byte) (tx *ethTypes.Transaction, err error)
agentProof [][32]byte) (*ethTypes.Transaction, error)
// GetDispute gets the dispute for a given dispute index.
// TODO: Add more returned values here as needed.
GetDispute(ctx context.Context, index *big.Int) (err error)
Expand Down
26 changes: 2 additions & 24 deletions agents/domains/evm/bondingmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"context"
"fmt"
"math/big"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
Expand All @@ -17,7 +16,6 @@ import (
"github.com/synapsecns/sanguine/agents/types"
"github.com/synapsecns/sanguine/ethergo/chain"
"github.com/synapsecns/sanguine/ethergo/signer/nonce"
"github.com/synapsecns/sanguine/ethergo/signer/signer"
)

// NewBondingManagerContract returns a bound bonding manager contract.
Expand Down Expand Up @@ -102,29 +100,9 @@ func (a bondingManagerContract) GetDispute(ctx context.Context, index *big.Int)
return nil
}

func (a bondingManagerContract) CompleteSlashing(ctx context.Context, signer signer.Signer, domain uint32, agent common.Address, proof [][32]byte) (tx *ethTypes.Transaction, err error) {
transactor, err := signer.GetTransactor(ctx, a.client.GetBigChainID())
func (a bondingManagerContract) CompleteSlashing(transactor *bind.TransactOpts, domain uint32, agent common.Address, proof [][32]byte) (tx *ethTypes.Transaction, err error) {
tx, err = a.contract.CompleteSlashing(transactor, domain, agent, proof)
if err != nil {
return nil, fmt.Errorf("could not sign tx: %w", err)
}

// TODO: why do we do this?
a.nonceManager.ClearNonce(signer.Address())
transactOpts, err := a.nonceManager.NewKeyedTransactor(transactor)
if err != nil {
return nil, fmt.Errorf("could not create tx: %w", err)
}

transactOpts.Context = ctx

transactOpts.GasLimit = 5000000

tx, err = a.contract.CompleteSlashing(transactOpts, domain, agent, proof)
if err != nil {
// TODO: Why is this done? And if it is necessary, we should functionalize it.
if strings.Contains(err.Error(), "nonce too low") {
a.nonceManager.ClearNonce(signer.Address())
}
return nil, fmt.Errorf("could not submit state report: %w", err)
}

Expand Down
Loading

0 comments on commit 7322033

Please sign in to comment.