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

Submitter and Retries for Fraud #1288

Merged
merged 12 commits into from
Aug 31, 2023
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
Loading