Skip to content

Commit

Permalink
Merge pull request #103 from synapsecns/feature/attestationcollector_…
Browse files Browse the repository at this point in the history
…test_and_parser

Add support for attestationcollector with parser and tests for attestationSubmitted event
  • Loading branch information
CryptoMaxPlanck authored Aug 19, 2022
2 parents 9ba01d0 + 5a76c17 commit 6a9a2d5
Show file tree
Hide file tree
Showing 8 changed files with 392 additions and 2 deletions.
103 changes: 103 additions & 0 deletions core/contracts/attestationcollector/attestationcollector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package attestationcollector_test

import (
"context"
"fmt"
"math/big"
"time"

"github.com/brianvoe/gofakeit/v6"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
. "github.com/stretchr/testify/assert"
"github.com/synapsecns/sanguine/core/agents/notary"
"github.com/synapsecns/sanguine/core/contracts/attestationcollector"
"github.com/synapsecns/sanguine/core/types"
"github.com/synapsecns/synapse-node/contracts/bridge"
)

func (a AttestationCollectorSuite) TestAttestationCollectorSuite() {
// Set up the contexts for all contracts, including the destination and attestation collector to get owner.
txContextOrigin := a.testBackendOrigin.GetTxContext(a.GetTestContext(), nil)
txContextDestination := a.testBackendDestination.GetTxContext(a.GetTestContext(), a.destinationContractMetadata.OwnerPtr())
txContextAttestationCollector := a.testBackendDestination.GetTxContext(a.GetTestContext(), a.attestationContractMetadata.OwnerPtr())

// Create a channel and subscription to receive AttestationSubmitted events as they are emitted.
attestationSink := make(chan *attestationcollector.AttestationCollectorAttestationSubmitted)
subAttestation, err := a.attestationContract.WatchAttestationSubmitted(&bind.WatchOpts{Context: a.GetTestContext()}, attestationSink, []common.Address{})
Nil(a.T(), err)

encodedTips, err := types.EncodeTips(types.NewTips(big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0)))
Nil(a.T(), err)

// Dispatch an event from the Origin contract to be accepted on the Destination contract.
tx, err := a.originContract.Dispatch(txContextOrigin.TransactOpts, 1, [32]byte{}, 1, encodedTips, nil)
Nil(a.T(), err)
a.testBackendOrigin.WaitForConfirmation(a.GetTestContext(), tx)

// Create an attestation
localDomain := uint32(a.testBackendOrigin.Config().ChainID)
nonce := gofakeit.Uint32()
root := common.BigToHash(new(big.Int).SetUint64(gofakeit.Uint64()))
unsignedAttestation := types.NewAttestation(localDomain, nonce, root)
hashedAttestation, err := notary.HashAttestation(unsignedAttestation)
Nil(a.T(), err)

signature, err := a.signer.SignMessage(a.GetTestContext(), bridge.KappaToSlice(hashedAttestation), false)
Nil(a.T(), err)

signedAttestation := types.NewSignedAttestation(unsignedAttestation, signature)
encodedSig, err := types.EncodeSignature(signedAttestation.Signature())
Nil(a.T(), err)

attestation, err := a.attestationHarness.FormatAttestation(
&bind.CallOpts{Context: a.GetTestContext()},
signedAttestation.Attestation().Domain(),
signedAttestation.Attestation().Nonce(),
signedAttestation.Attestation().Root(),
encodedSig,
)
Nil(a.T(), err)

// Set notary to the testing address so we can submit attestations.
tx, err = a.destinationContract.SetNotary(txContextDestination.TransactOpts, uint32(a.testBackendOrigin.GetChainID()), a.signer.Address())
Nil(a.T(), err)
a.testBackendDestination.WaitForConfirmation(a.GetTestContext(), tx)

// Submit the attestation to get an AttestationAccepted event.
tx, err = a.destinationContract.SubmitAttestation(txContextDestination.TransactOpts, attestation)
Nil(a.T(), err)
a.testBackendDestination.WaitForConfirmation(a.GetTestContext(), tx)

// Set notary to the testing address so we can submit attestations.
tx, err = a.attestationContract.AddNotary(txContextAttestationCollector.TransactOpts, uint32(a.testBackendOrigin.GetChainID()), a.signer.Address())
Nil(a.T(), err)
a.testBackendDestination.WaitForConfirmation(a.GetTestContext(), tx)

// Submit the attestation to get an AttestationSubmitted event.
tx, err = a.attestationContract.SubmitAttestation(txContextAttestationCollector.TransactOpts, attestation)
Nil(a.T(), err)
a.testBackendDestination.WaitForConfirmation(a.GetTestContext(), tx)

watchCtx, cancel := context.WithTimeout(a.GetTestContext(), time.Second*10)
defer cancel()

select {
// check for errors and fail
case <-watchCtx.Done():
a.T().Error(a.T(), fmt.Errorf("test context completed %w", a.GetTestContext().Err()))
case <-subAttestation.Err():
a.T().Error(a.T(), subAttestation.Err())
// get AttestationSubmitted event
case item := <-attestationSink:
parser, err := attestationcollector.NewParser(a.attestationContract.Address())
Nil(a.T(), err)

// Check to see if the event was an AttestationSubmitted event.
eventType, ok := parser.EventType(item.Raw)
True(a.T(), ok)
Equal(a.T(), eventType, attestationcollector.AttestationSubmittedEvent)

break
}
}
23 changes: 23 additions & 0 deletions core/contracts/attestationcollector/eventtype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 75 additions & 0 deletions core/contracts/attestationcollector/parser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package attestationcollector

import (
"fmt"

"github.com/ethereum/go-ethereum/common"
ethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/synapsecns/sanguine/core/types"
legacyTypes "github.com/synapsecns/synapse-node/pkg/types"
)

// Parser parses events from the attestation collector contract.
type Parser interface {
// EventType determines if an event was initiated by the bridge or the user.
EventType(log ethTypes.Log) (_ EventType, ok bool)
// ParseAttestationSubmitted parses an AttestationSubmitted event
ParseAttestationSubmitted(log ethTypes.Log) (_ types.AttestationSubmitted, ok bool)
}

type parserImpl struct {
// filterer is the parser filterer we use to parse events
filterer *AttestationCollectorFilterer
}

// NewParser creates a new parser for the attestation collector contract.
func NewParser(attestationCollectorAddress common.Address) (Parser, error) {
parser, err := NewAttestationCollectorFilterer(attestationCollectorAddress, nil)
if err != nil {
return nil, fmt.Errorf("could not create %T: %w", AttestationCollectorFilterer{}, err)
}

return &parserImpl{filterer: parser}, nil
}

func (p parserImpl) EventType(log ethTypes.Log) (_ EventType, ok bool) {
for _, logTopic := range log.Topics {
eventType := eventTypeFromTopic(logTopic)
if eventType == nil {
continue
}

return *eventType, true
}
// return an unknown event to avoid cases where user failed to check the event type
return EventType(len(legacyTypes.AllEventTypes()) + 2), false
}

// ParseAttestationSubmitted parses an AttestationSubmitted event.
func (p parserImpl) ParseAttestationSubmitted(log ethTypes.Log) (_ types.AttestationSubmitted, ok bool) {
attestation, err := p.filterer.ParseAttestationSubmitted(log)
if err != nil {
return nil, false
}

attestationSubmitted := types.NewAttestationSubmitted(
attestation.Notary.Hash(),
attestation.Attestation,
)

return attestationSubmitted, true
}

// EventType is the type of the attestation collector events
//go:generate go run golang.org/x/tools/cmd/stringer -type=EventType
type EventType uint

const (
// AttestationSubmittedEvent is a AttestationSubmitted event.
AttestationSubmittedEvent EventType = 0
)

// Int gets the int for an event type.
func (i EventType) Int() uint8 {
return uint8(i)
}
87 changes: 87 additions & 0 deletions core/contracts/attestationcollector/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package attestationcollector_test

import (
"math/big"
"testing"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/params"
"github.com/stretchr/testify/suite"
"github.com/synapsecns/sanguine/core/contracts/attestationcollector"
"github.com/synapsecns/sanguine/core/contracts/destination"
"github.com/synapsecns/sanguine/core/contracts/origin"
"github.com/synapsecns/sanguine/core/contracts/test/attestationharness"
"github.com/synapsecns/sanguine/core/testutil"
"github.com/synapsecns/sanguine/ethergo/signer/signer"
"github.com/synapsecns/sanguine/ethergo/signer/signer/localsigner"
"github.com/synapsecns/sanguine/ethergo/signer/wallet"
"github.com/synapsecns/synapse-node/testutils"
"github.com/synapsecns/synapse-node/testutils/backends"
"github.com/synapsecns/synapse-node/testutils/backends/preset"
)

// AttestationCollectorSuite is the attestation collector test suite.
type AttestationCollectorSuite struct {
*testutils.TestSuite
originContract *origin.OriginRef
destinationContract *destination.DestinationRef
destinationContractMetadata backends.DeployedContract
attestationHarness *attestationharness.AttestationHarnessRef
attestationContract *attestationcollector.AttestationCollectorRef
attestationContractMetadata backends.DeployedContract
testBackendOrigin backends.SimulatedTestBackend
testBackendDestination backends.SimulatedTestBackend
wallet wallet.Wallet
signer signer.Signer
}

// NewAttestationCollectorSuite creates an end-to-end test suite.
func NewAttestationCollectorSuite(tb testing.TB) *AttestationCollectorSuite {
tb.Helper()
return &AttestationCollectorSuite{
TestSuite: testutils.NewTestSuite(tb),
}
}

func (a *AttestationCollectorSuite) SetupTest() {
a.TestSuite.SetupTest()

a.testBackendOrigin = preset.GetRinkeby().Geth(a.GetTestContext(), a.T())
a.testBackendDestination = preset.GetBSCTestnet().Geth(a.GetTestContext(), a.T())
deployManager := testutil.NewDeployManager(a.T())

_, a.originContract = deployManager.GetOrigin(a.GetTestContext(), a.testBackendOrigin)
_, a.attestationHarness = deployManager.GetAttestationHarness(a.GetTestContext(), a.testBackendOrigin)
a.attestationContractMetadata, a.attestationContract = deployManager.GetAttestationCollector(a.GetTestContext(), a.testBackendDestination)
a.destinationContractMetadata, a.destinationContract = deployManager.GetDestination(a.GetTestContext(), a.testBackendDestination)

var err error
a.wallet, err = wallet.FromRandom()
if err != nil {
a.T().Fatal(err)
}

_, notaryManager := deployManager.GetNotaryManager(a.GetTestContext(), a.testBackendOrigin)
owner, err := notaryManager.Owner(&bind.CallOpts{Context: a.GetTestContext()})
if err != nil {
a.T().Fatal(err)
}

a.signer = localsigner.NewSigner(a.wallet.PrivateKey())
a.testBackendOrigin.FundAccount(a.GetTestContext(), a.signer.Address(), *big.NewInt(params.Ether))
a.testBackendDestination.FundAccount(a.GetTestContext(), a.signer.Address(), *big.NewInt(params.Ether))

transactOpts := a.testBackendOrigin.GetTxContext(a.GetTestContext(), &owner)

tx, err := notaryManager.SetNotary(transactOpts.TransactOpts, a.signer.Address())
if err != nil {
a.T().Fatal(err)
}

a.testBackendOrigin.WaitForConfirmation(a.GetTestContext(), tx)
}

// TestAttestationCollectorSuite runs the integration test suite.
func TestAttestationCollectorSuite(t *testing.T) {
suite.Run(t, NewAttestationCollectorSuite(t))
}
44 changes: 44 additions & 0 deletions core/contracts/attestationcollector/topics.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package attestationcollector

import (
"bytes"
"strings"

"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/common"
)

func init() {
// set topics
var err error

parsedAttestationCollector, err := abi.JSON(strings.NewReader(AttestationCollectorABI))
if err != nil {
panic(err)
}

AttestationSubmittedTopic = parsedAttestationCollector.Events["AttestationSubmitted"].ID
}

// AttestationSubmittedTopic is the topic that gets emitted
// when the AttestationSubmitted event is called.
var AttestationSubmittedTopic common.Hash

// topicMap maps events to topics.
// this is returned as a function to assert immutability.
func topicMap() map[EventType]common.Hash {
return map[EventType]common.Hash{
AttestationSubmittedEvent: AttestationSubmittedTopic,
}
}

// eventTypeFromTopic gets the event type from the topic
// returns nil if the topic is not found.
func eventTypeFromTopic(ogTopic common.Hash) *EventType {
for eventType, topic := range topicMap() {
if bytes.Equal(ogTopic.Bytes(), topic.Bytes()) {
return &eventType
}
}
return nil
}
4 changes: 2 additions & 2 deletions core/contracts/destination/destination_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
)

func (d DestinationSuite) TestDestinationSuite() {
// Set up contexts for both Origin and Destination, also getting owner for Destination for reassigning updater role.
// Set up contexts for both Origin and Destination, also getting owner for Destination for reassigning notary role.
txContextOrigin := d.testBackendOrigin.GetTxContext(d.GetTestContext(), nil)
txContextDestination := d.testBackendDestination.GetTxContext(d.GetTestContext(), d.destinationContractMetadata.OwnerPtr())

Expand Down Expand Up @@ -58,7 +58,7 @@ func (d DestinationSuite) TestDestinationSuite() {
)
Nil(d.T(), err)

// Set updater to the testing address so we can submit attestations.
// Set notary to the testing address so we can submit attestations.
tx, err = d.destinationContract.SetNotary(txContextDestination.TransactOpts, uint32(d.testBackendOrigin.GetChainID()), d.signer.Address())
Nil(d.T(), err)
d.testBackendDestination.WaitForConfirmation(d.GetTestContext(), tx)
Expand Down
23 changes: 23 additions & 0 deletions core/contracts/destination/eventtype_string.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 6a9a2d5

Please sign in to comment.