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

Update mercury e2e tests to support new mercury plugin and hmac auth #8683

Merged
merged 52 commits into from
Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
be701da
Update mercury setup
lukaszcl Mar 6, 2023
3800f5c
Update contract bindings
lukaszcl Mar 7, 2023
bec073f
wip contracts not working
lukaszcl Mar 7, 2023
7ea3478
minor refactor
lukaszcl Mar 8, 2023
77cd3b0
Fix contracts
lukaszcl Mar 8, 2023
84fafa7
Fix job specs
lukaszcl Mar 8, 2023
b6d4955
ocr fixed
lukaszcl Mar 8, 2023
c272861
Use ip for ms url, increase mockserver resources and do small refacto…
lukaszcl Mar 8, 2023
ca8deb3
Fix BuildGeneralOCR2Config for other tests
lukaszcl Mar 8, 2023
26247d6
Refactoring 1
lukaszcl Mar 8, 2023
6d24db9
Refactoring 2
lukaszcl Mar 8, 2023
9f9b01d
Refactoring 3
lukaszcl Mar 9, 2023
019a244
wip
lukaszcl Mar 9, 2023
e3579a1
increase observation timeout
lukaszcl Mar 9, 2023
5c016ca
finish mercury client endpoints for new auth
lukaszcl Mar 9, 2023
aaf6e54
Merge branch 'update-mercury-e2e-tests-for-new-plugin' into update-me…
lukaszcl Mar 9, 2023
565575c
wip - hmac api call works!
lukaszcl Mar 9, 2023
15c06f1
Use init sql helm value to initialize mercury server db
lukaszcl Mar 9, 2023
8bac760
Refactor mercury test environment setup
lukaszcl Mar 10, 2023
e363538
Refactor test setup
lukaszcl Mar 10, 2023
4cf0a94
Fix
lukaszcl Mar 10, 2023
e3cb959
Remove unused file
lukaszcl Mar 10, 2023
329ca7b
Minor refactoring
lukaszcl Mar 10, 2023
a1710dd
Update mercury load test (wip)
lukaszcl Mar 10, 2023
0b8cf21
Add keep mercury env flag
lukaszcl Mar 10, 2023
b13336e
Fixes
lukaszcl Mar 10, 2023
8c45840
Update load tests to work with HMAC
lukaszcl Mar 10, 2023
06fe3c6
Update contract setup
lukaszcl Mar 11, 2023
e1b9288
Fix
lukaszcl Mar 13, 2023
7c48979
Merge branch 'develop' into update-mercury-e2e-tests-for-new-plugin-a…
lukaszcl Mar 13, 2023
c421f04
Fix go mod
lukaszcl Mar 13, 2023
63f2a95
Validate existing of new fields in the report
lukaszcl Mar 13, 2023
214b3f0
Fix OCR kickoff by adding fromBlock to bootstrap node
lukaszcl Mar 13, 2023
a5646b2
Check contract err
lukaszcl Mar 13, 2023
e503b96
Update ocr config values
lukaszcl Mar 13, 2023
f468b1e
Bump mockserver resources
lukaszcl Mar 13, 2023
b352726
Clean up http gun
lukaszcl Mar 13, 2023
fdb8025
New Exchanger contract that fixes ResolveTradeWithReport
lukaszcl Mar 13, 2023
040a238
Merge branch 'develop' into update-mercury-e2e-tests-for-new-plugin-a…
lukaszcl Mar 13, 2023
85a2851
Do not save mercury env config if config already used
lukaszcl Mar 14, 2023
dcd89da
Fix automation_test.go
lukaszcl Mar 14, 2023
995192d
Clean up env setup from file
lukaszcl Mar 14, 2023
3f424ac
Use gomega to wait for reports in mercury db
lukaszcl Mar 14, 2023
a17d523
Merge branch 'develop' into update-mercury-e2e-tests-for-new-plugin-a…
lukaszcl Mar 14, 2023
fb16f29
update load tests
skudasov Mar 14, 2023
70d5bec
Merge remote-tracking branch 'origin/update-mercury-e2e-tests-for-new…
skudasov Mar 14, 2023
d5867c1
Decouple testing.T from env setup
lukaszcl Mar 14, 2023
18559af
use chart path as an env var
skudasov Mar 15, 2023
bae5af4
Merge remote-tracking branch 'origin/update-mercury-e2e-tests-for-new…
skudasov Mar 15, 2023
24fb429
fix after merge
skudasov Mar 15, 2023
6fc3cf0
Merge branch 'develop' into update-mercury-e2e-tests-for-new-plugin-a…
skudasov Mar 15, 2023
0275560
handle errors, fix lint
skudasov Mar 15, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 122 additions & 0 deletions integration-tests/actions/mercury/report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package mercury

import (
"fmt"
"math/big"

"github.com/ava-labs/coreth/accounts/abi"
"github.com/pkg/errors"
"github.com/rs/zerolog/log"
)

func getReportTypes() abi.Arguments {
mustNewType := func(t string) abi.Type {
result, err := abi.NewType(t, "", []abi.ArgumentMarshaling{})
if err != nil {
panic(fmt.Sprintf("Unexpected error during abi.NewType: %s", err))
}
return result
}
return abi.Arguments([]abi.Argument{
{Name: "feedId", Type: mustNewType("bytes32")},
{Name: "observationsTimestamp", Type: mustNewType("uint32")},
{Name: "benchmarkPrice", Type: mustNewType("int192")},
{Name: "bid", Type: mustNewType("int192")},
{Name: "ask", Type: mustNewType("int192")},
{Name: "currentBlockNum", Type: mustNewType("uint64")},
{Name: "currentBlockHash", Type: mustNewType("bytes32")},
{Name: "validFromBlockNum", Type: mustNewType("uint64")},
})
}

var ReportTypes = getReportTypes()

func ValidateReport(reportBlob []byte) error {
r := map[string]interface{}{}
err := ReportTypes.UnpackIntoMap(r, []byte(reportBlob))
if err != nil {
return err
}

feedIdInterface, ok := r["feedId"]
if !ok {
return errors.Errorf("unpacked report has no 'feedId'")
}
feedID, ok := feedIdInterface.([32]byte)
if !ok {
return errors.Errorf("cannot cast feedId to [32]byte, type is %T", feedID)
}
log.Trace().Str("FeedID", string(feedID[:])).Msg("Feed ID")

benchmarkPriceInterface, ok := r["benchmarkPrice"]
if !ok {
return errors.Errorf("unpacked report has no 'benchmarkPrice'")
}
benchmarkPrice, ok := benchmarkPriceInterface.(*big.Int)
if !ok {
return errors.Errorf("cannot cast 'benchmarkPrice' to *big.Int, type is %T", benchmarkPrice)
}
log.Trace().Int64("benchmarkPrice", benchmarkPrice.Int64()).Msg("Benchmark price")

bidInterface, ok := r["bid"]
if !ok {
return errors.Errorf("unpacked report has no 'bid'")
}
bidPrice, ok := bidInterface.(*big.Int)
if !ok {
return errors.Errorf("cannot cast 'bid' to *big.Int, type is %T", bidPrice)
}
log.Trace().Int64("bid", benchmarkPrice.Int64()).Msg("Bid price")

askInterface, ok := r["ask"]
if !ok {
return errors.Errorf("unpacked report has no 'ask'")
}
askPrice, ok := askInterface.(*big.Int)
if !ok {
return errors.Errorf("cannot cast 'bid' to *big.Int, type is %T", askPrice)
}
log.Trace().Int64("ask", benchmarkPrice.Int64()).Msg("Ask price")

currentBlockNumInterface, ok := r["currentBlockNum"]
if !ok {
return errors.Errorf("unpacked report has no 'currentBlockNum'")
}
currentBlockNumber, ok := currentBlockNumInterface.(uint64)
if !ok {
return errors.Errorf("cannot cast 'currentBlockNum' to uint64, type is %T", currentBlockNumber)
}
log.Trace().Uint64("currentBlockNumber", currentBlockNumber).Msg("Observation current block number")

validFromBlockNumInterface, ok := r["validFromBlockNum"]
if !ok {
return errors.Errorf("unpacked report has no 'validFromBlockNum'")
}
validFromBlockNum, ok := validFromBlockNumInterface.(uint64)
if !ok {
return errors.Errorf("cannot cast 'validFromBlockNum' to uint64, type is %T", validFromBlockNum)
}
log.Trace().Uint64("validFromBlockNum", currentBlockNumber).Msg("Valid from block number")

currentBlockHashInterface, ok := r["currentBlockHash"]
if !ok {
return errors.Errorf("unpacked report has no 'currentBlockHash'")
}
currentBlockHash, ok := currentBlockHashInterface.([32]uint8)
if !ok {
return errors.Errorf("cannot cast 'currentBlockHash' to uint64, type is %v", currentBlockHash)
}
log.Trace().Any("currentBlockHash", currentBlockHash).Msg("currentBlockHash")

observationsTimestampInterface, ok := r["observationsTimestamp"]
if !ok {
return errors.Errorf("unpacked report has no 'observationsTimestamp'")
}
observationsTimestamp, ok := observationsTimestampInterface.(uint32)
if !ok {
return errors.Errorf("cannot cast observationsTimestamp to uint32, type is %T", observationsTimestamp)
}
log.Trace().Uint32("Timestamp", observationsTimestamp).Msg("Observation timestamp")

return nil
}
1 change: 1 addition & 0 deletions integration-tests/client/chainlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ func (c *Chainlink) MustCreateJob(spec JobSpec) (*Job, error) {
func (c *Chainlink) CreateJob(spec JobSpec) (*Job, *http.Response, error) {
job := &Job{}
specString, err := spec.String()
log.Info().Msgf("Creating job spec: %s", specString)
if err != nil {
return nil, nil, err
}
Expand Down
141 changes: 132 additions & 9 deletions integration-tests/client/mercury_server.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,163 @@
package client

import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"
"strconv"
"time"

"github.com/go-resty/resty/v2"
"github.com/rs/zerolog/log"
"nhooyr.io/websocket"
)

type GetReportsResult struct {
ChainlinkBlob string `json:"chainlinkBlob"`
}

type User struct {
Id string `json:"id"`
Secret string `json:"secret" db:"secret"`
Role string `json:"role" db:"role"` // 0 = user, 1 = admin
Disabled bool `json:"disabled" db:"disabled"`
CreatedAt string `json:"createdAt" db:"created_at"`
UpdatedAt string `json:"updatedAt" db:"updated_at"`
}

type MercuryServer struct {
URL string
UserId string
UserKey string
APIClient *resty.Client
}

func NewMercuryServer(url string) *MercuryServer {
// Create new mercury server client for userId and userKey that are used for HMAC authentication
func NewMercuryServerClient(url string, userId string, userKey string) *MercuryServer {
rc := resty.New().SetBaseURL(url)
return &MercuryServer{
URL: url,
APIClient: rc,
UserId: userId,
UserKey: userKey,
}
}

func (ms *MercuryServer) GetReports(feedIDStr string, blockNumber uint64) (*GetReportsResult, *http.Response, error) {
result := &GetReportsResult{}
resp, err := ms.APIClient.R().
SetPathParams(map[string]string{
"feedIDStr": feedIDStr,
"L2Blocknumber": strconv.FormatUint(blockNumber, 10),
}).
func (s *MercuryServer) DialWS() (*websocket.Conn, *http.Response, error) {
timestamp := genReqTimestamp()
hmacSignature := genHmacSignature("GET", "/ws", []byte{}, []byte(s.UserKey), s.UserId, timestamp)
return websocket.Dial(context.Background(), fmt.Sprintf("%s/ws", s.URL), &websocket.DialOptions{
HTTPHeader: http.Header{
"Authorization": []string{s.UserId},
"X-Authorization-Timestamp": []string{timestamp},
"X-Authorization-Signature-SHA256": []string{hmacSignature},
},
})
}

func (s *MercuryServer) CallGet(path string) (map[string]interface{}, *http.Response, error) {
timestamp := genReqTimestamp()
hmacSignature := genHmacSignature("GET", path, []byte{}, []byte(s.UserKey), s.UserId, timestamp)
result := map[string]interface{}{}
resp, err := s.APIClient.R().
SetHeader("Authorization", s.UserId).
SetHeader("X-Authorization-Timestamp", timestamp).
SetHeader("X-Authorization-Signature-SHA256", hmacSignature).
SetResult(&result).
Get("/client?feedIDStr={feedIDStr}&L2Blocknumber={L2Blocknumber}")
Get(path)
if err != nil {
return nil, nil, err
}
return result, resp.RawResponse, nil
}

// Add new user with "admin" or "user" role
func (s *MercuryServer) AddUser(newUserSecret string, newUserRole string, newUserDisabled bool) (*User, *http.Response, error) {
request := map[string]interface{}{
"secret": newUserSecret,
"role": newUserRole,
"disabled": newUserDisabled,
}
result := struct {
User User
}{}
path := "/admin/user"
timestamp := genReqTimestamp()
b, _ := json.Marshal(request)
hmacSignature := genHmacSignature("POST", path, b, []byte(s.UserKey), s.UserId, timestamp)
resp, err := s.APIClient.R().
SetHeader("Authorization", s.UserId).
SetHeader("X-Authorization-Timestamp", timestamp).
SetHeader("X-Authorization-Signature-SHA256", hmacSignature).
SetBody(request).
SetResult(result).
Post(path)
if err != nil {
return nil, nil, err
}
return &result.User, resp.RawResponse, err
}

// Need admin role
func (s *MercuryServer) GetUsers() (*[]User, *http.Response, error) {
var result []User
path := "/admin/user"
timestamp := genReqTimestamp()
hmacSignature := genHmacSignature("GET", path, []byte{}, []byte(s.UserKey), s.UserId, timestamp)
resp, err := s.APIClient.R().
SetHeader("Accept", "application/json").
SetHeader("Authorization", s.UserId).
SetHeader("X-Authorization-Timestamp", timestamp).
SetHeader("X-Authorization-Signature-SHA256", hmacSignature).
SetResult(&result).
Get(path)
if err != nil {
return nil, nil, err
}
return &result, resp.RawResponse, err
}

func (s *MercuryServer) GetReports(feedIDStr string, blockNumber uint64) (*GetReportsResult, *http.Response, error) {
result := &GetReportsResult{}
path := fmt.Sprintf("/client?feedIDStr=%s&L2Blocknumber=%d", feedIDStr, blockNumber)
timestamp := genReqTimestamp()
hmacSignature := genHmacSignature("GET", path, []byte{}, []byte(s.UserKey), s.UserId, timestamp)
resp, err := s.APIClient.R().
SetHeader("Accept", "application/json").
SetHeader("Authorization", s.UserId).
SetHeader("X-Authorization-Timestamp", timestamp).
SetHeader("X-Authorization-Signature-SHA256", hmacSignature).
SetResult(&result).
Get(path)
if err != nil {
return nil, resp.RawResponse, err
}
return result, resp.RawResponse, err
}

func genReqTimestamp() string {
// The timestamp of the request. This is used to prevent replay attacks.
// The timestamp should be within 5 seconds of the server's time (by default).
// The server will reject requests with timestamps in the future.
return strconv.FormatInt(time.Now().UTC().UnixMilli(), 10)
}

func genHmacSignature(method string, path string, body []byte, secret []byte, clientId string, timestamp string) string {
// Get the hash for the body
bodyHash := sha256.New()
bodyHash.Write(body)
bodyHashString := hex.EncodeToString(bodyHash.Sum(nil))

// Generate the message to be signed
message := fmt.Sprintf("%s %s %s %s %s", method, path, bodyHashString, clientId, timestamp)
log.Debug().Msgf("message: %s", message)

// Generate the signature
signedMessage := hmac.New(sha256.New, secret)
signedMessage.Write([]byte(message))
return hex.EncodeToString(signedMessage.Sum(nil))
}
5 changes: 4 additions & 1 deletion integration-tests/contracts/contract_deployer.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,11 @@ type ContractDeployer interface {
DeployStaking(params ethereum2.StakingPoolConstructorParams) (Staking, error)
DeployBatchBlockhashStore(blockhashStoreAddr string) (BatchBlockhashStore, error)
DeployAtlasFunctions() (AtlasFunctions, error)
LoadVerifierProxy(address common.Address) (VerifierProxy, error)
DeployVerifierProxy(accessControllerAddr string) (VerifierProxy, error)
DeployVerifier(feedId [32]byte, verifierProxyAddr string) (Verifier, error)
LoadVerifier(address common.Address) (Verifier, error)
DeployVerifier(verifierProxyAddr string) (Verifier, error)
LoadExchanger(address common.Address) (Exchanger, error)
DeployExchanger(verifierProxyAddr string, lookupURL string, maxDelay uint8) (Exchanger, error)
}

Expand Down

Large diffs are not rendered by default.

369 changes: 89 additions & 280 deletions integration-tests/contracts/ethereum/mercury/verifier/Verifier.go

Large diffs are not rendered by default.

Loading