Skip to content

Commit

Permalink
basic attestation service
Browse files Browse the repository at this point in the history
  • Loading branch information
RensR committed Sep 5, 2023
1 parent 069a4a4 commit f478a01
Show file tree
Hide file tree
Showing 5 changed files with 152 additions and 0 deletions.
1 change: 1 addition & 0 deletions core/services/ocr2/plugins/ccip/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ type CommitPluginJobSpecConfig struct {
// ExecutionPluginJobSpecConfig contains the plugin specific variables for the ccip.CCIPExecution plugin.
type ExecutionPluginJobSpecConfig struct {
SourceStartBlock, DestStartBlock int64 // Only for first time job add.
USDCAttestationApi string
}
70 changes: 70 additions & 0 deletions core/services/ocr2/plugins/ccip/customtokens/usdc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package customtokens

import (
"encoding/json"
"fmt"
"io"
"net/http"
)

type USDCService struct {
attestationApi string
}

type USDCAttestationResponse struct {
Status USDCAttestationStatus `json:"status"`
Attestation string `json:"attestation"`
}

const (
version = "v1"
attestationPath = "attestations"
)

type USDCAttestationStatus string

const (
USDCAttestationStatusSuccess USDCAttestationStatus = "complete"
USDCAttestationStatusPending USDCAttestationStatus = "pending_confirmations"
)

func NewUSDCService(usdcAttestationApi string) *USDCService {
return &USDCService{attestationApi: usdcAttestationApi}
}

func (usdc *USDCService) TryGetAttestation(messageHash string) (USDCAttestationResponse, error) {
fullAttestationUrl := fmt.Sprintf("%s/%s/%s/%s", usdc.attestationApi, version, attestationPath, messageHash)
req, err := http.NewRequest("GET", fullAttestationUrl, nil)
if err != nil {
return USDCAttestationResponse{}, err
}
req.Header.Add("accept", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
return USDCAttestationResponse{}, err
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
return USDCAttestationResponse{}, err
}

var response USDCAttestationResponse
err = json.Unmarshal(body, &response)
if err != nil {
return USDCAttestationResponse{}, err
}

return response, nil
}

func (usdc *USDCService) IsAttestationComplete(messageHash string) (bool, string, error) {
response, err := usdc.TryGetAttestation(messageHash)
if err != nil {
return false, "", err
}
if response.Status == USDCAttestationStatusSuccess {
return true, response.Attestation, nil
}
return false, "", nil
}
77 changes: 77 additions & 0 deletions core/services/ocr2/plugins/ccip/customtokens/usdc_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package customtokens

import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"

"github.com/stretchr/testify/require"
)

func TestUSDCService_TryGetAttestation(t *testing.T) {
t.Skipf("Skipping test because it uses the real USDC attestation API")
usdcMessageHash := "0x912f22a13e9ccb979b621500f6952b2afd6e75be7eadaed93fc2625fe11c52a2"
usdcService := NewUSDCService("https://iris-api-sandbox.circle.com")

attestation, err := usdcService.TryGetAttestation(usdcMessageHash)
require.NoError(t, err)

require.Equal(t, USDCAttestationStatusPending, attestation.Status)
require.Equal(t, "PENDING", attestation.Attestation)
}

func TestUSDCService_TryGetAttestationMock(t *testing.T) {
response := USDCAttestationResponse{
Status: USDCAttestationStatusSuccess,
Attestation: "720502893578a89a8a87982982ef781c18b193",
}

ts := getMockUSDCEndpoint(t, response)
defer ts.Close()

usdcService := NewUSDCService(ts.URL)
attestation, err := usdcService.TryGetAttestation("0x912f22a13e9ccb979b621500f6952b2afd6e75be7eadaed93fc2625fe11c52a2")
require.NoError(t, err)

require.Equal(t, response.Status, attestation.Status)
require.Equal(t, response.Attestation, attestation.Attestation)
}

func TestUSDCService_TryGetAttestationMockError(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
}))
defer ts.Close()

usdcService := NewUSDCService(ts.URL)
_, err := usdcService.TryGetAttestation("0x912f22a13e9ccb979b621500f6952b2afd6e75be7eadaed93fc2625fe11c52a2")
require.Error(t, err)
}

func TestUSDCService_IsAttestationComplete(t *testing.T) {
response := USDCAttestationResponse{
Status: USDCAttestationStatusSuccess,
Attestation: "720502893578a89a8a87982982ef781c18b193",
}

ts := getMockUSDCEndpoint(t, response)
defer ts.Close()

usdcService := NewUSDCService(ts.URL)
isReady, attestation, err := usdcService.IsAttestationComplete("0x912f22a13e9ccb979b621500f6952b2afd6e75be7eadaed93fc2625fe11c52a2")
require.NoError(t, err)

require.True(t, isReady)
require.Equal(t, response.Attestation, attestation)
}

func getMockUSDCEndpoint(t *testing.T, response USDCAttestationResponse) *httptest.Server {
responseBytes, err := json.Marshal(response)
require.NoError(t, err)

return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write(responseBytes)
require.NoError(t, err)
}))
}
2 changes: 2 additions & 0 deletions core/services/ocr2/plugins/ccip/execution_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/abihelpers"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/ccipevents"
ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/customtokens"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/hasher"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/observability"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/promwrapper"
Expand Down Expand Up @@ -122,6 +123,7 @@ func NewExecutionServices(lggr logger.Logger, jb job.Job, chainSet evm.LegacyCha
sourceClient: sourceChain.Client(),
destGasEstimator: destChain.GasEstimator(),
leafHasher: hasher.NewLeafHasher(offRampConfig.SourceChainSelector, offRampConfig.ChainSelector, onRamp.Address(), hasher.NewKeccakCtx()),
usdcService: customtokens.NewUSDCService(pluginConfig.USDCAttestationApi),
})

err = wrappedPluginFactory.UpdateLogPollerFilters(zeroAddress)
Expand Down
2 changes: 2 additions & 0 deletions core/services/ocr2/plugins/ccip/execution_reporting_plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/cache"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/ccipevents"
ccipconfig "github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/config"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/customtokens"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/hasher"
"github.com/smartcontractkit/chainlink/v2/core/services/ocr2/plugins/ccip/observability"
"github.com/smartcontractkit/chainlink/v2/core/services/pg"
Expand Down Expand Up @@ -66,6 +67,7 @@ type ExecutionPluginConfig struct {
sourceClient evmclient.Client
destGasEstimator gas.EvmFeeEstimator
leafHasher hasher.LeafHasherInterface[[32]byte]
usdcService *customtokens.USDCService
}

type ExecutionReportingPlugin struct {
Expand Down

0 comments on commit f478a01

Please sign in to comment.