From 0ae0b691466687ac7be2b49d9d4a1bd3136d6424 Mon Sep 17 00:00:00 2001
From: Erik Burton
Date: Mon, 6 Nov 2023 13:24:15 -0800
Subject: [PATCH 01/13] chore: bump sigstore/cosign-installer from 2.1.0 to
3.1.2 (#11192)
---
.github/actions/build-sign-publish-chainlink/action.yml | 2 +-
.github/actions/goreleaser-build-sign-publish/action.yml | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/actions/build-sign-publish-chainlink/action.yml b/.github/actions/build-sign-publish-chainlink/action.yml
index fe4ef858f58..62add53092a 100644
--- a/.github/actions/build-sign-publish-chainlink/action.yml
+++ b/.github/actions/build-sign-publish-chainlink/action.yml
@@ -223,7 +223,7 @@ runs:
- if: inputs.sign-images == 'true'
name: Install cosign
- uses: sigstore/cosign-installer@581838fbedd492d2350a9ecd427a95d6de1e5d01 # v2.1.0
+ uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2
with:
cosign-release: "v1.6.0"
diff --git a/.github/actions/goreleaser-build-sign-publish/action.yml b/.github/actions/goreleaser-build-sign-publish/action.yml
index 845d2443fc1..b2d42c1234e 100644
--- a/.github/actions/goreleaser-build-sign-publish/action.yml
+++ b/.github/actions/goreleaser-build-sign-publish/action.yml
@@ -84,7 +84,7 @@ runs:
version: ${{ inputs.zig-version }}
- name: Setup cosign
if: inputs.enable-cosign == 'true'
- uses: sigstore/cosign-installer@581838fbedd492d2350a9ecd427a95d6de1e5d01 # v2.1.0
+ uses: sigstore/cosign-installer@11086d25041f77fe8fe7b9ea4e48e3b9192b8f19 # v3.1.2
with:
cosign-release: ${{ inputs.cosign-version }}
- name: Login to docker registry
From c71ead762d71e8952698b1be303c158cbd6f74e0 Mon Sep 17 00:00:00 2001
From: Tate
Date: Mon, 6 Nov 2023 17:00:15 -0700
Subject: [PATCH 02/13] [TT-668] Use Internal Mirror for docker images used in
e2e tests (#11189)
Part of the effor to remove the e2e flakes caused by dockerhub rate limit issues
---
.github/workflows/integration-tests.yml | 4 ----
integration-tests/go.mod | 2 +-
integration-tests/go.sum | 4 ++--
3 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml
index 928c716dd11..125ddb3f4f3 100644
--- a/.github/workflows/integration-tests.yml
+++ b/.github/workflows/integration-tests.yml
@@ -228,8 +228,6 @@ jobs:
cl_repo: ${{ env.CHAINLINK_IMAGE }}
cl_image_tag: ${{ github.sha }}
aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}
- dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }}
- dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }}
artifacts_location: ./integration-tests/smoke/logs/
publish_check_name: ${{ matrix.product.name }}
token: ${{ secrets.GITHUB_TOKEN }}
@@ -429,8 +427,6 @@ jobs:
cl_repo: ${{ env.CHAINLINK_IMAGE }}
cl_image_tag: ${{ github.sha }}${{ matrix.product.tag_suffix }}
aws_registries: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }}
- dockerhub_username: ${{ secrets.DOCKERHUB_READONLY_USERNAME }}
- dockerhub_password: ${{ secrets.DOCKERHUB_READONLY_PASSWORD }}
artifacts_name: ${{ matrix.product.name }}-test-logs
artifacts_location: ./integration-tests/smoke/logs/
publish_check_name: ${{ matrix.product.name }}
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index 936729944f8..8daa5a29e5b 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -22,7 +22,7 @@ require (
github.com/scylladb/go-reflectx v1.0.1
github.com/segmentio/ksuid v1.0.4
github.com/slack-go/slack v0.12.2
- github.com/smartcontractkit/chainlink-testing-framework v1.18.4-0.20231106173929-20fe04d6ad66
+ github.com/smartcontractkit/chainlink-testing-framework v1.18.4
github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000
github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545
github.com/smartcontractkit/ocr2keepers v0.7.28
diff --git a/integration-tests/go.sum b/integration-tests/go.sum
index 7bd733023c5..696e06b6c91 100644
--- a/integration-tests/go.sum
+++ b/integration-tests/go.sum
@@ -2376,8 +2376,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo=
-github.com/smartcontractkit/chainlink-testing-framework v1.18.4-0.20231106173929-20fe04d6ad66 h1:AOqcHiAppMoIvM2WSJNIZzJDnOQNXyElbLFK3ZqoJeM=
-github.com/smartcontractkit/chainlink-testing-framework v1.18.4-0.20231106173929-20fe04d6ad66/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk=
+github.com/smartcontractkit/chainlink-testing-framework v1.18.4 h1:IAalKSqRDSGj10zE/JvFrngKGp7mEIVTPh5jTnsaCec=
+github.com/smartcontractkit/chainlink-testing-framework v1.18.4/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk=
github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss=
github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4=
github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU=
From 0e5b5232e378263964f202ff4bb1712c4ad77813 Mon Sep 17 00:00:00 2001
From: Anirudh Warrier <12178754+anirudhwarrier@users.noreply.github.com>
Date: Tue, 7 Nov 2023 11:00:37 +0400
Subject: [PATCH 03/13] Fix Automation benchmark test (#11191)
* fix benchmark test
* bump runner
---
.github/workflows/automation-benchmark-tests.yml | 2 +-
integration-tests/benchmark/keeper_test.go | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/automation-benchmark-tests.yml b/.github/workflows/automation-benchmark-tests.yml
index 7bdb66c919e..a4338d642bc 100644
--- a/.github/workflows/automation-benchmark-tests.yml
+++ b/.github/workflows/automation-benchmark-tests.yml
@@ -57,7 +57,7 @@ jobs:
id-token: write
contents: read
name: ${{ inputs.network }} Automation Benchmark Test
- runs-on: ubuntu-latest
+ runs-on: ubuntu20.04-16cores-64GB
env:
SELECTED_NETWORKS: ${{ inputs.network }}
SLACK_API_KEY: ${{ secrets.QA_SLACK_API_KEY }}
diff --git a/integration-tests/benchmark/keeper_test.go b/integration-tests/benchmark/keeper_test.go
index 25a147fbf0b..9342f3629b9 100644
--- a/integration-tests/benchmark/keeper_test.go
+++ b/integration-tests/benchmark/keeper_test.go
@@ -227,7 +227,7 @@ func addRegistry(registryToTest string) []eth_contracts.KeeperRegistryVersion {
case "2_0-Multiple":
return repeatRegistries(eth_contracts.RegistryVersion_2_0, NumberOfRegistries)
case "2_1-Multiple":
- return repeatRegistries(eth_contracts.RegistryVersion_1_0, NumberOfRegistries)
+ return repeatRegistries(eth_contracts.RegistryVersion_2_1, NumberOfRegistries)
default:
return []eth_contracts.KeeperRegistryVersion{eth_contracts.RegistryVersion_2_0}
}
From fcaf226947ceece466df04e0524428f132ecc267 Mon Sep 17 00:00:00 2001
From: ilija42 <57732589+ilija42@users.noreply.github.com>
Date: Tue, 7 Nov 2023 10:12:36 +0100
Subject: [PATCH 04/13] Add to delegate job spec CaptureEATelemetry override
from evm ocr cfg (#11197)
* Add to delegate job spec CaptureEATelemetry override from evm ocr cfg
* Add logs for when Ea telemetry is disabled
* Update core/services/ocr/delegate.go
Co-authored-by: Jordan Krage
* Update core/services/ocr2/delegate.go
* Update core/services/ocrcommon/data_source.go
---------
Co-authored-by: Patrick
Co-authored-by: Jordan Krage
Co-authored-by: Gheorghe Strimtu
---
core/services/ocr/delegate.go | 3 +++
core/services/ocr2/delegate.go | 2 ++
core/services/ocrcommon/data_source.go | 2 ++
3 files changed, 7 insertions(+)
diff --git a/core/services/ocr/delegate.go b/core/services/ocr/delegate.go
index bbed43c151b..6eb6714a474 100644
--- a/core/services/ocr/delegate.go
+++ b/core/services/ocr/delegate.go
@@ -295,10 +295,13 @@ func (d *Delegate) ServicesForSpec(jb job.Job) (services []job.ServiceCtx, err e
configOverrider = configOverriderService
}
+ jb.OCROracleSpec.CaptureEATelemetry = chain.Config().OCR().CaptureEATelemetry()
enhancedTelemChan := make(chan ocrcommon.EnhancedTelemetryData, 100)
if ocrcommon.ShouldCollectEnhancedTelemetry(&jb) {
enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, enhancedTelemChan, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint("EVM", chain.ID().String(), concreteSpec.ContractAddress.String(), synchronization.EnhancedEA), lggr.Named("EnhancedTelemetry"))
services = append(services, enhancedTelemService)
+ } else {
+ lggr.Infow("Enhanced telemetry is disabled for job", "job", jb.Name)
}
oracle, err := ocr.NewOracle(ocr.OracleArgs{
diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go
index 39a8c84d6b9..99aa492bc7b 100644
--- a/core/services/ocr2/delegate.go
+++ b/core/services/ocr2/delegate.go
@@ -746,6 +746,8 @@ func (d *Delegate) newServicesMedian(
if ocrcommon.ShouldCollectEnhancedTelemetry(&jb) {
enhancedTelemService := ocrcommon.NewEnhancedTelemetryService(&jb, enhancedTelemChan, make(chan struct{}), d.monitoringEndpointGen.GenMonitoringEndpoint(rid.Network, rid.ChainID, spec.ContractID, synchronization.EnhancedEA), lggr.Named("EnhancedTelemetry"))
medianServices = append(medianServices, enhancedTelemService)
+ } else {
+ lggr.Infow("Enhanced telemetry is disabled for job", "job", jb.Name)
}
return medianServices, err2
diff --git a/core/services/ocrcommon/data_source.go b/core/services/ocrcommon/data_source.go
index ed832e45fcf..0363a7124b6 100644
--- a/core/services/ocrcommon/data_source.go
+++ b/core/services/ocrcommon/data_source.go
@@ -144,6 +144,8 @@ func (ds *inMemoryDataSource) executeRun(ctx context.Context, timestamp Observat
FinalResults: finalResult,
RepTimestamp: timestamp,
})
+ } else {
+ ds.lggr.Infow("Enhanced telemetry is disabled for job", "job", ds.jb.Name)
}
return run, finalResult, err
From 48b9902f5dc931c6a2d06699ea26f660b65630f1 Mon Sep 17 00:00:00 2001
From: Mohamed Mehany <7327188+mohamed-mehany@users.noreply.github.com>
Date: Tue, 7 Nov 2023 12:11:36 +0100
Subject: [PATCH 05/13] Adds WeMix chain config (#10793)
* Adds WeMix testnet config
* Extending NoNewHeads threshold to 3s
* Adds WeMix contract loader
* Downgrading to go 1.20.5 bullseye
* Revert "Downgrading to go 1.20.5 bullseye"
This reverts commit 7eab839819456513dcf7f83ca8f08c97bdeb3835.
* Update testing branch
* Excludes fee delegation transactions for Wemix
* Update testing branch
* Linting
* Fix typo
* Update CTF
---------
Co-authored-by: davidcauchi
---
.github/workflows/on-demand-ocr-soak-test.yml | 2 +
.../config/toml/defaults/WeMix_Mainnet.toml | 14 ++
.../config/toml/defaults/WeMix_Testnet.toml | 14 ++
.../evm/gas/block_history_estimator_test.go | 6 +
core/chains/evm/gas/chain_specific.go | 7 +
core/config/chaintype.go | 6 +-
core/config/docs/chains-evm.toml | 2 +-
core/services/chainlink/config_test.go | 4 +-
core/services/ocr/contract_tracker.go | 2 +-
docs/CONFIG.md | 160 +++++++++++++++++-
.../contracts/contract_deployer.go | 6 +
.../contracts/contract_loader.go | 7 +
integration-tests/go.mod | 2 +-
integration-tests/go.sum | 4 +-
14 files changed, 225 insertions(+), 11 deletions(-)
create mode 100644 core/chains/evm/config/toml/defaults/WeMix_Mainnet.toml
create mode 100644 core/chains/evm/config/toml/defaults/WeMix_Testnet.toml
diff --git a/.github/workflows/on-demand-ocr-soak-test.yml b/.github/workflows/on-demand-ocr-soak-test.yml
index 1e510c23be3..b6ccad22467 100644
--- a/.github/workflows/on-demand-ocr-soak-test.yml
+++ b/.github/workflows/on-demand-ocr-soak-test.yml
@@ -28,6 +28,8 @@ on:
- "FANTOM_MAINNET"
- "KROMA_MAINNET"
- "KROMA_SEPOLIA"
+ - "WEMIX_TESTNET"
+ - "WEMIX_MAINNET"
fundingPrivateKey:
description: Private funding key (Skip for Simulated)
required: false
diff --git a/core/chains/evm/config/toml/defaults/WeMix_Mainnet.toml b/core/chains/evm/config/toml/defaults/WeMix_Mainnet.toml
new file mode 100644
index 00000000000..ee50a9844a4
--- /dev/null
+++ b/core/chains/evm/config/toml/defaults/WeMix_Mainnet.toml
@@ -0,0 +1,14 @@
+ChainID = '1111'
+ChainType = 'wemix'
+FinalityDepth = 1
+MinIncomingConfirmations = 1
+# WeMix emits a block every 1 second, regardless of transactions
+LogPollInterval = '3s'
+NoNewHeadsThreshold = '30s'
+
+[OCR]
+ContractConfirmations = 1
+
+[GasEstimator]
+EIP1559DynamicFees = true
+TipCapDefault = '100 gwei'
diff --git a/core/chains/evm/config/toml/defaults/WeMix_Testnet.toml b/core/chains/evm/config/toml/defaults/WeMix_Testnet.toml
new file mode 100644
index 00000000000..6cdb451eb1d
--- /dev/null
+++ b/core/chains/evm/config/toml/defaults/WeMix_Testnet.toml
@@ -0,0 +1,14 @@
+ChainID = '1112'
+ChainType = 'wemix'
+FinalityDepth = 1
+MinIncomingConfirmations = 1
+# WeMix emits a block every 1 second, regardless of transactions
+LogPollInterval = '3s'
+NoNewHeadsThreshold = '30s'
+
+[OCR]
+ContractConfirmations = 1
+
+[GasEstimator]
+EIP1559DynamicFees = true
+TipCapDefault = '100 gwei'
diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go
index 7f4d157e37a..decb68dbe99 100644
--- a/core/chains/evm/gas/block_history_estimator_test.go
+++ b/core/chains/evm/gas/block_history_estimator_test.go
@@ -1329,6 +1329,12 @@ func TestBlockHistoryEstimator_IsUsable(t *testing.T) {
assert.Equal(t, true, bhe.IsUsable(tx2, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t)))
})
+ t.Run("returns false if transaction is of type 0x16 only on WeMix", func(t *testing.T) {
+ cfg.ChainTypeF = "wemix"
+ tx := evmtypes.Transaction{Type: 0x16, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()}
+ assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t)))
+ })
+
t.Run("returns false if transaction has base fee higher than the gas price only on Celo", func(t *testing.T) {
cfg.ChainTypeF = "celo"
tx := evmtypes.Transaction{Type: 0x0, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()}
diff --git a/core/chains/evm/gas/chain_specific.go b/core/chains/evm/gas/chain_specific.go
index 4d87b8b454e..5c0b256bd3f 100644
--- a/core/chains/evm/gas/chain_specific.go
+++ b/core/chains/evm/gas/chain_specific.go
@@ -42,5 +42,12 @@ func chainSpecificIsUsable(tx evmtypes.Transaction, baseFee *assets.Wei, chainTy
return false
}
}
+ if chainType == config.ChainWeMix {
+ // WeMix specific transaction types that enables fee delegation.
+ // https://docs.wemix.com/v/en/design/fee-delegation
+ if tx.Type == 0x16 {
+ return false
+ }
+ }
return true
}
diff --git a/core/config/chaintype.go b/core/config/chaintype.go
index c99099ee616..0110f247b73 100644
--- a/core/config/chaintype.go
+++ b/core/config/chaintype.go
@@ -15,18 +15,18 @@ const (
ChainOptimismBedrock ChainType = "optimismBedrock"
ChainXDai ChainType = "xdai"
ChainCelo ChainType = "celo"
+ ChainWeMix ChainType = "wemix"
ChainKroma ChainType = "kroma"
)
var ErrInvalidChainType = fmt.Errorf("must be one of %s or omitted", strings.Join([]string{
string(ChainArbitrum), string(ChainMetis), string(ChainXDai), string(ChainOptimismBedrock), string(ChainCelo),
- string(ChainKroma),
-}, ", "))
+ string(ChainKroma), string(ChainWeMix)}, ", "))
// IsValid returns true if the ChainType value is known or empty.
func (c ChainType) IsValid() bool {
switch c {
- case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo, ChainKroma:
+ case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo, ChainKroma, ChainWeMix:
return true
}
return false
diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml
index 082bbd6cd19..c0cdfc7a310 100644
--- a/core/config/docs/chains-evm.toml
+++ b/core/config/docs/chains-evm.toml
@@ -14,7 +14,7 @@ BlockBackfillDepth = 10 # Default
# BlockBackfillSkip enables skipping of very long backfills.
BlockBackfillSkip = false # Default
# ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID.
-# Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma
+# Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix
ChainType = 'arbitrum' # Example
# FinalityDepth is the number of blocks after which an ethereum transaction is considered "final". Note that the default is automatically set based on chain ID so it should not be necessary to change this under normal operation.
# BlocksConsideredFinal determines how deeply we look back to ensure that transactions are confirmed onto the longest chain
diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go
index 59a02f1dcf9..cc3fda167de 100644
--- a/core/services/chainlink/config_test.go
+++ b/core/services/chainlink/config_test.go
@@ -1190,7 +1190,7 @@ func TestConfig_Validate(t *testing.T) {
- 1: 6 errors:
- ChainType: invalid value (Foo): must not be set with this chain id
- Nodes: missing: must have at least one node
- - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma or omitted
+ - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix or omitted
- HeadTracker.HistoryDepth: invalid value (30): must be equal to or greater than FinalityDepth
- GasEstimator: 2 errors:
- FeeCapDefault: invalid value (101 wei): must be equal to PriceMax (99 wei) since you are using FixedPrice estimation with gas bumping disabled in EIP1559 mode - PriceMax will be used as the FeeCap for transactions instead of FeeCapDefault
@@ -1199,7 +1199,7 @@ func TestConfig_Validate(t *testing.T) {
- 2: 5 errors:
- ChainType: invalid value (Arbitrum): only "optimismBedrock" can be used with this chain id
- Nodes: missing: must have at least one node
- - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma or omitted
+ - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix or omitted
- FinalityDepth: invalid value (0): must be greater than or equal to 1
- MinIncomingConfirmations: invalid value (0): must be greater than or equal to 1
- 3.Nodes: 5 errors:
diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go
index 3a216e025f0..2308fa3035d 100644
--- a/core/services/ocr/contract_tracker.go
+++ b/core/services/ocr/contract_tracker.go
@@ -401,7 +401,7 @@ func (t *OCRContractTracker) LatestBlockHeight(ctx context.Context) (blockheight
// care about the block height; we have no way of getting the L1 block
// height anyway
return 0, nil
- case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai, config.ChainKroma:
+ case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai, config.ChainKroma, config.ChainWeMix:
// continue
}
latestBlockHeight := t.getLatestBlockHeight()
diff --git a/docs/CONFIG.md b/docs/CONFIG.md
index 4b55c804a3d..fd8822c1626 100644
--- a/docs/CONFIG.md
+++ b/docs/CONFIG.md
@@ -3302,6 +3302,164 @@ GasLimit = 5300000
+Simulated (1337)
```toml
@@ -5074,7 +5232,7 @@ BlockBackfillSkip enables skipping of very long backfills.
ChainType = 'arbitrum' # Example
```
ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID.
-Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma
+Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix
### FinalityDepth
```toml
diff --git a/integration-tests/contracts/contract_deployer.go b/integration-tests/contracts/contract_deployer.go
index 0c36a260815..916971f82d3 100644
--- a/integration-tests/contracts/contract_deployer.go
+++ b/integration-tests/contracts/contract_deployer.go
@@ -175,6 +175,8 @@ func NewContractDeployer(bcClient blockchain.EVMClient, logger zerolog.Logger) (
return &FantomContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil
case *blockchain.KromaClient:
return &KromaContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil
+ case *blockchain.WeMixClient:
+ return &WeMixContractDeployer{NewEthereumContractDeployer(clientImpl, logger)}, nil
}
return nil, errors.New("unknown blockchain client implementation for contract deployer, register blockchain client in NewContractDeployer")
}
@@ -246,6 +248,10 @@ type KromaContractDeployer struct {
*EthereumContractDeployer
}
+type WeMixContractDeployer struct {
+ *EthereumContractDeployer
+}
+
// NewEthereumContractDeployer returns an instantiated instance of the ETH contract deployer
func NewEthereumContractDeployer(ethClient blockchain.EVMClient, logger zerolog.Logger) *EthereumContractDeployer {
return &EthereumContractDeployer{
diff --git a/integration-tests/contracts/contract_loader.go b/integration-tests/contracts/contract_loader.go
index 4dda2d3f0c4..cfe7a35467e 100644
--- a/integration-tests/contracts/contract_loader.go
+++ b/integration-tests/contracts/contract_loader.go
@@ -64,6 +64,8 @@ func NewContractLoader(bcClient blockchain.EVMClient, logger zerolog.Logger) (Co
return &OptimismContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil
case *blockchain.PolygonZkEvmClient:
return &PolygonZkEvmContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil
+ case *blockchain.WeMixClient:
+ return &WeMixContractLoader{NewEthereumContractLoader(clientImpl, logger)}, nil
}
return nil, errors.New("unknown blockchain client implementation for contract Loader, register blockchain client in NewContractLoader")
}
@@ -107,6 +109,11 @@ type PolygonZKEVMContractLoader struct {
*EthereumContractLoader
}
+// WeMixContractLoader wraps for WeMix
+type WeMixContractLoader struct {
+ *EthereumContractLoader
+}
+
// NewEthereumContractLoader returns an instantiated instance of the ETH contract Loader
func NewEthereumContractLoader(ethClient blockchain.EVMClient, logger zerolog.Logger) *EthereumContractLoader {
return &EthereumContractLoader{
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index 8daa5a29e5b..dab3cfa64b5 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -22,7 +22,7 @@ require (
github.com/scylladb/go-reflectx v1.0.1
github.com/segmentio/ksuid v1.0.4
github.com/slack-go/slack v0.12.2
- github.com/smartcontractkit/chainlink-testing-framework v1.18.4
+ github.com/smartcontractkit/chainlink-testing-framework v1.18.5-0.20231107092923-3aa655167f65
github.com/smartcontractkit/chainlink/v2 v2.0.0-00010101000000-000000000000
github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545
github.com/smartcontractkit/ocr2keepers v0.7.28
diff --git a/integration-tests/go.sum b/integration-tests/go.sum
index 696e06b6c91..e8ee06ff49e 100644
--- a/integration-tests/go.sum
+++ b/integration-tests/go.sum
@@ -2376,8 +2376,8 @@ github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab0
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb/go.mod h1:/30flFG4L/iCYAFeA3DUzR0xuHSxAMONiWTzyzvsNwo=
-github.com/smartcontractkit/chainlink-testing-framework v1.18.4 h1:IAalKSqRDSGj10zE/JvFrngKGp7mEIVTPh5jTnsaCec=
-github.com/smartcontractkit/chainlink-testing-framework v1.18.4/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk=
+github.com/smartcontractkit/chainlink-testing-framework v1.18.5-0.20231107092923-3aa655167f65 h1:/iRhwYy5KFsaS9Zo1T64QxAd11HGZB5p/LHI5oVc4BU=
+github.com/smartcontractkit/chainlink-testing-framework v1.18.5-0.20231107092923-3aa655167f65/go.mod h1:zScXRqmvbyTFUooyLYrOp4+V/sFPUbFJNRc72YmnuIk=
github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306 h1:ko88+ZznniNJZbZPWAvHQU8SwKAdHngdDZ+pvVgB5ss=
github.com/smartcontractkit/go-plugin v0.0.0-20231003134350-e49dad63b306/go.mod h1:w1sAEES3g3PuV/RzUrgow20W2uErMly84hhD3um1WL4=
github.com/smartcontractkit/grpc-proxy v0.0.0-20230731113816-f1be6620749f h1:hgJif132UCdjo8u43i7iPN1/MFnu49hv7lFGFftCHKU=
From cc308a14d879478933de95c3a153e3cb7cf1a3b2 Mon Sep 17 00:00:00 2001
From: Rens Rooimans
Date: Tue, 7 Nov 2023 13:22:56 +0100
Subject: [PATCH 06/13] fix mockery version (#11199)
---
contracts/GNUmakefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/contracts/GNUmakefile b/contracts/GNUmakefile
index b477164a496..e41d6422c2f 100644
--- a/contracts/GNUmakefile
+++ b/contracts/GNUmakefile
@@ -34,7 +34,7 @@ abigen: ## Build & install abigen.
.PHONY: mockery
mockery: $(mockery) ## Install mockery.
- go install github.com/vektra/mockery/v2@v2.28.1
+ go install github.com/vektra/mockery/v2@v2.35.4
.PHONY: foundry
foundry: ## Install foundry.
From 302eb05d592132309b264e316f443f1ceb81b6c3 Mon Sep 17 00:00:00 2001
From: Cedric
Date: Tue, 7 Nov 2023 13:57:26 +0000
Subject: [PATCH 07/13] [BCF-2632] Adapt median to POC (#11178)
---
core/scripts/go.mod | 2 +-
core/scripts/go.sum | 4 +-
core/services/ocr2/delegate.go | 9 +-
.../generic/pipeline_runner_adapter.go | 12 +-
go.mod | 2 +-
go.sum | 4 +-
integration-tests/go.mod | 2 +-
integration-tests/go.sum | 4 +-
plugins/cmd/chainlink-medianpoc/main.go | 42 ++++++
plugins/medianpoc/data_source.go | 79 +++++++++++
plugins/medianpoc/data_source_test.go | 115 ++++++++++++++++
plugins/medianpoc/plugin.go | 126 ++++++++++++++++++
plugins/medianpoc/plugin_test.go | 105 +++++++++++++++
13 files changed, 489 insertions(+), 17 deletions(-)
create mode 100644 plugins/cmd/chainlink-medianpoc/main.go
create mode 100644 plugins/medianpoc/data_source.go
create mode 100644 plugins/medianpoc/data_source_test.go
create mode 100644 plugins/medianpoc/plugin.go
create mode 100644 plugins/medianpoc/plugin_test.go
diff --git a/core/scripts/go.mod b/core/scripts/go.mod
index 8d731f3f41d..7766697c608 100644
--- a/core/scripts/go.mod
+++ b/core/scripts/go.mod
@@ -305,7 +305,7 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect
- github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de // indirect
+ github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 // indirect
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect
github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect
diff --git a/core/scripts/go.sum b/core/scripts/go.sum
index ebab8c990d3..5149148d030 100644
--- a/core/scripts/go.sum
+++ b/core/scripts/go.sum
@@ -1466,8 +1466,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de h1:CeVpn5xEdmuEsYE8ss2b7bSq9h3BY4OPvpqXeYIPnHw=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8=
diff --git a/core/services/ocr2/delegate.go b/core/services/ocr2/delegate.go
index 99aa492bc7b..75147ca2333 100644
--- a/core/services/ocr2/delegate.go
+++ b/core/services/ocr2/delegate.go
@@ -596,10 +596,11 @@ func (d *Delegate) newServicesGenericPlugin(
}
pluginConfig := types.ReportingPluginServiceConfig{
- PluginName: cconf.PluginName,
- Command: command,
- ProviderType: cconf.ProviderType,
- PluginConfig: string(p.PluginConfig),
+ PluginName: cconf.PluginName,
+ Command: command,
+ ProviderType: cconf.ProviderType,
+ TelemetryType: cconf.TelemetryType,
+ PluginConfig: string(p.PluginConfig),
}
pr := generic.NewPipelineRunnerAdapter(pluginLggr, jb, d.pipelineRunner)
diff --git a/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go b/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go
index 6afb35ca758..def33114e8c 100644
--- a/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go
+++ b/core/services/ocr2/plugins/generic/pipeline_runner_adapter.go
@@ -23,7 +23,7 @@ type PipelineRunnerAdapter struct {
logger logger.Logger
}
-func (p *PipelineRunnerAdapter) ExecuteRun(ctx context.Context, spec string, vars types.Vars, options types.Options) ([]types.TaskResult, error) {
+func (p *PipelineRunnerAdapter) ExecuteRun(ctx context.Context, spec string, vars types.Vars, options types.Options) (types.TaskResults, error) {
s := pipeline.Spec{
DotDagSource: spec,
CreatedAt: time.Now(),
@@ -54,9 +54,13 @@ func (p *PipelineRunnerAdapter) ExecuteRun(ctx context.Context, spec string, var
taskResults[i] = types.TaskResult{
ID: trr.ID.String(),
Type: string(trr.Task.Type()),
- Value: trr.Result.Value,
- Error: trr.Result.Error,
- Index: int(trr.TaskRun.Index),
+ Index: int(trr.Task.OutputIndex()),
+
+ TaskValue: types.TaskValue{
+ Value: trr.Result.Value,
+ Error: trr.Result.Error,
+ IsTerminal: len(trr.Task.Outputs()) == 0,
+ },
}
}
return taskResults, nil
diff --git a/go.mod b/go.mod
index 4d8a9294020..a5628eb4b80 100644
--- a/go.mod
+++ b/go.mod
@@ -66,7 +66,7 @@ require (
github.com/shopspring/decimal v1.3.1
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353
- github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de
+ github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb
github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545
diff --git a/go.sum b/go.sum
index acd966e8aa3..a35e0f32bd9 100644
--- a/go.sum
+++ b/go.sum
@@ -1467,8 +1467,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de h1:CeVpn5xEdmuEsYE8ss2b7bSq9h3BY4OPvpqXeYIPnHw=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8=
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index dab3cfa64b5..3c5a5e1f2e7 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -388,7 +388,7 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect
- github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de // indirect
+ github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 // indirect
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect
github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect
diff --git a/integration-tests/go.sum b/integration-tests/go.sum
index e8ee06ff49e..cd5cb84a686 100644
--- a/integration-tests/go.sum
+++ b/integration-tests/go.sum
@@ -2370,8 +2370,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de h1:CeVpn5xEdmuEsYE8ss2b7bSq9h3BY4OPvpqXeYIPnHw=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231102162027-5fdce33763de/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8=
diff --git a/plugins/cmd/chainlink-medianpoc/main.go b/plugins/cmd/chainlink-medianpoc/main.go
new file mode 100644
index 00000000000..325de6538fa
--- /dev/null
+++ b/plugins/cmd/chainlink-medianpoc/main.go
@@ -0,0 +1,42 @@
+package main
+
+import (
+ "github.com/hashicorp/go-plugin"
+
+ "github.com/smartcontractkit/chainlink-relay/pkg/loop"
+ "github.com/smartcontractkit/chainlink-relay/pkg/loop/reportingplugins"
+ "github.com/smartcontractkit/chainlink-relay/pkg/types"
+ "github.com/smartcontractkit/chainlink/v2/plugins/medianpoc"
+)
+
+const (
+ loggerName = "PluginMedianPoc"
+)
+
+func main() {
+ s := loop.MustNewStartedServer(loggerName)
+ defer s.Stop()
+
+ p := medianpoc.NewPlugin(s.Logger)
+ defer s.Logger.ErrorIfFn(p.Close, "Failed to close")
+
+ s.MustRegister(p)
+
+ stop := make(chan struct{})
+ defer close(stop)
+
+ plugin.Serve(&plugin.ServeConfig{
+ HandshakeConfig: reportingplugins.ReportingPluginHandshakeConfig(),
+ Plugins: map[string]plugin.Plugin{
+ reportingplugins.PluginServiceName: &reportingplugins.GRPCService[types.MedianProvider]{
+ PluginServer: p,
+ BrokerConfig: loop.BrokerConfig{
+ Logger: s.Logger,
+ StopCh: stop,
+ GRPCOpts: s.GRPCOpts,
+ },
+ },
+ },
+ GRPCServer: s.GRPCOpts.NewServer,
+ })
+}
diff --git a/plugins/medianpoc/data_source.go b/plugins/medianpoc/data_source.go
new file mode 100644
index 00000000000..7b20f1e5eb3
--- /dev/null
+++ b/plugins/medianpoc/data_source.go
@@ -0,0 +1,79 @@
+package medianpoc
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "math/big"
+ "sync"
+ "time"
+
+ ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
+
+ "github.com/smartcontractkit/chainlink-relay/pkg/logger"
+ "github.com/smartcontractkit/chainlink-relay/pkg/types"
+ "github.com/smartcontractkit/chainlink/v2/core/bridges"
+ "github.com/smartcontractkit/chainlink/v2/core/utils"
+)
+
+type DataSource struct {
+ pipelineRunner types.PipelineRunnerService
+ spec string
+ lggr logger.Logger
+
+ current bridges.BridgeMetaData
+ mu sync.RWMutex
+}
+
+func (d *DataSource) Observe(ctx context.Context, reportTimestamp ocrtypes.ReportTimestamp) (*big.Int, error) {
+ md, err := bridges.MarshalBridgeMetaData(d.currentAnswer())
+ if err != nil {
+ d.lggr.Warnw("unable to attach metadata for run", "err", err)
+ }
+
+ // NOTE: job metadata is automatically attached by the pipeline runner service
+ vars := types.Vars{
+ Vars: map[string]interface{}{
+ "jobRun": md,
+ },
+ }
+
+ results, err := d.pipelineRunner.ExecuteRun(ctx, d.spec, vars, types.Options{})
+ if err != nil {
+ return nil, err
+ }
+
+ finalResults := results.FinalResults()
+ if len(finalResults) == 0 {
+ return nil, errors.New("pipeline execution failed: not enough results")
+ }
+
+ finalResult := finalResults[0]
+ if finalResult.Error != nil {
+ return nil, fmt.Errorf("pipeline execution failed: %w", finalResult.Error)
+ }
+
+ asDecimal, err := utils.ToDecimal(finalResult.Value)
+ if err != nil {
+ return nil, errors.New("cannot convert observation to decimal")
+ }
+
+ resultAsBigInt := asDecimal.BigInt()
+ d.updateAnswer(resultAsBigInt)
+ return resultAsBigInt, nil
+}
+
+func (d *DataSource) currentAnswer() (*big.Int, *big.Int) {
+ d.mu.RLock()
+ defer d.mu.RUnlock()
+ return d.current.LatestAnswer, d.current.UpdatedAt
+}
+
+func (d *DataSource) updateAnswer(latestAnswer *big.Int) {
+ d.mu.Lock()
+ defer d.mu.Unlock()
+ d.current = bridges.BridgeMetaData{
+ LatestAnswer: latestAnswer,
+ UpdatedAt: big.NewInt(time.Now().Unix()),
+ }
+}
diff --git a/plugins/medianpoc/data_source_test.go b/plugins/medianpoc/data_source_test.go
new file mode 100644
index 00000000000..e9a7945cee4
--- /dev/null
+++ b/plugins/medianpoc/data_source_test.go
@@ -0,0 +1,115 @@
+package medianpoc
+
+import (
+ "context"
+ "errors"
+ "math/big"
+ "testing"
+
+ ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
+
+ "github.com/smartcontractkit/chainlink-relay/pkg/types"
+)
+
+type mockPipelineRunner struct {
+ results types.TaskResults
+ err error
+ spec string
+ vars types.Vars
+ options types.Options
+}
+
+func (m *mockPipelineRunner) ExecuteRun(ctx context.Context, spec string, vars types.Vars, options types.Options) (types.TaskResults, error) {
+ m.spec = spec
+ m.vars = vars
+ m.options = options
+ return m.results, m.err
+}
+
+func TestDataSource(t *testing.T) {
+ lggr := logger.TestLogger(t)
+ expect := int64(3)
+ pr := &mockPipelineRunner{
+ results: types.TaskResults{
+ {
+ TaskValue: types.TaskValue{
+ Value: expect,
+ Error: nil,
+ IsTerminal: true,
+ },
+ Index: 2,
+ },
+ {
+ TaskValue: types.TaskValue{
+ Value: int(4),
+ Error: nil,
+ IsTerminal: false,
+ },
+ Index: 1,
+ },
+ },
+ }
+ spec := "SPEC"
+ ds := &DataSource{
+ pipelineRunner: pr,
+ spec: spec,
+ lggr: lggr,
+ }
+ res, err := ds.Observe(context.Background(), ocrtypes.ReportTimestamp{})
+ require.NoError(t, err)
+ assert.Equal(t, big.NewInt(expect), res)
+ assert.Equal(t, spec, pr.spec)
+ assert.Equal(t, big.NewInt(expect), ds.current.LatestAnswer)
+}
+
+func TestDataSource_ResultErrors(t *testing.T) {
+ lggr := logger.TestLogger(t)
+ pr := &mockPipelineRunner{
+ results: types.TaskResults{
+ {
+ TaskValue: types.TaskValue{
+ Error: errors.New("something went wrong"),
+ IsTerminal: true,
+ },
+ Index: 0,
+ },
+ },
+ }
+ spec := "SPEC"
+ ds := &DataSource{
+ pipelineRunner: pr,
+ spec: spec,
+ lggr: lggr,
+ }
+ _, err := ds.Observe(context.Background(), ocrtypes.ReportTimestamp{})
+ assert.ErrorContains(t, err, "something went wrong")
+}
+
+func TestDataSource_ResultNotAnInt(t *testing.T) {
+ lggr := logger.TestLogger(t)
+
+ expect := "string-result"
+ pr := &mockPipelineRunner{
+ results: types.TaskResults{
+ {
+ TaskValue: types.TaskValue{
+ Value: expect,
+ IsTerminal: true,
+ },
+ Index: 0,
+ },
+ },
+ }
+ spec := "SPEC"
+ ds := &DataSource{
+ pipelineRunner: pr,
+ spec: spec,
+ lggr: lggr,
+ }
+ _, err := ds.Observe(context.Background(), ocrtypes.ReportTimestamp{})
+ assert.ErrorContains(t, err, "cannot convert observation to decimal")
+}
diff --git a/plugins/medianpoc/plugin.go b/plugins/medianpoc/plugin.go
new file mode 100644
index 00000000000..ceea1eb84f5
--- /dev/null
+++ b/plugins/medianpoc/plugin.go
@@ -0,0 +1,126 @@
+package medianpoc
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+
+ "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median"
+
+ ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
+
+ "github.com/smartcontractkit/chainlink-relay/pkg/logger"
+ "github.com/smartcontractkit/chainlink-relay/pkg/loop"
+ "github.com/smartcontractkit/chainlink-relay/pkg/loop/reportingplugins"
+ "github.com/smartcontractkit/chainlink-relay/pkg/services"
+ "github.com/smartcontractkit/chainlink-relay/pkg/types"
+ "github.com/smartcontractkit/chainlink/v2/core/utils"
+)
+
+func NewPlugin(lggr logger.Logger) *Plugin {
+ return &Plugin{
+ Plugin: loop.Plugin{Logger: lggr},
+ MedianProviderServer: reportingplugins.MedianProviderServer{},
+ stop: make(utils.StopChan),
+ }
+}
+
+type Plugin struct {
+ loop.Plugin
+ stop utils.StopChan
+ reportingplugins.MedianProviderServer
+}
+
+type jsonConfig struct {
+ Pipelines map[string]string `json:"pipelines"`
+}
+
+func (j jsonConfig) defaultPipeline() (string, error) {
+ return j.getPipeline("__DEFAULT_PIPELINE__")
+}
+
+func (j jsonConfig) getPipeline(key string) (string, error) {
+ v, ok := j.Pipelines[key]
+ if ok {
+ return v, nil
+ }
+ return "", fmt.Errorf("no pipeline found for %s", key)
+}
+
+func (p *Plugin) NewReportingPluginFactory(
+ ctx context.Context,
+ config types.ReportingPluginServiceConfig,
+ provider types.MedianProvider,
+ pipelineRunner types.PipelineRunnerService,
+ telemetry types.TelemetryClient,
+ errorLog types.ErrorLog,
+) (types.ReportingPluginFactory, error) {
+ f, err := p.newFactory(ctx, config, provider, pipelineRunner, telemetry, errorLog)
+ if err != nil {
+ return nil, err
+ }
+ s := &reportingPluginFactoryService{lggr: p.Logger, ReportingPluginFactory: f}
+ p.SubService(s)
+ return s, nil
+}
+
+func (p *Plugin) newFactory(ctx context.Context, config types.ReportingPluginServiceConfig, provider types.MedianProvider, pipelineRunner types.PipelineRunnerService, telemetry types.TelemetryClient, errorLog types.ErrorLog) (*median.NumericalMedianFactory, error) {
+ jc := &jsonConfig{}
+ err := json.Unmarshal([]byte(config.PluginConfig), jc)
+ if err != nil {
+ return nil, err
+ }
+
+ dp, err := jc.defaultPipeline()
+ if err != nil {
+ return nil, err
+ }
+ ds := &DataSource{
+ pipelineRunner: pipelineRunner,
+ spec: dp,
+ lggr: p.Logger,
+ }
+
+ jfp, err := jc.getPipeline("juelsPerFeeCoinPipeline")
+ if err != nil {
+ return nil, err
+ }
+ jds := &DataSource{
+ pipelineRunner: pipelineRunner,
+ spec: jfp,
+ lggr: p.Logger,
+ }
+ factory := &median.NumericalMedianFactory{
+ ContractTransmitter: provider.MedianContract(),
+ DataSource: ds,
+ JuelsPerFeeCoinDataSource: jds,
+ Logger: logger.NewOCRWrapper(
+ p.Logger,
+ true,
+ func(msg string) {},
+ ),
+ OnchainConfigCodec: provider.OnchainConfigCodec(),
+ ReportCodec: provider.ReportCodec(),
+ }
+ return factory, nil
+}
+
+type reportingPluginFactoryService struct {
+ services.StateMachine
+ lggr logger.Logger
+ ocrtypes.ReportingPluginFactory
+}
+
+func (r *reportingPluginFactoryService) Name() string { return r.lggr.Name() }
+
+func (r *reportingPluginFactoryService) Start(ctx context.Context) error {
+ return r.StartOnce("ReportingPluginFactory", func() error { return nil })
+}
+
+func (r *reportingPluginFactoryService) Close() error {
+ return r.StopOnce("ReportingPluginFactory", func() error { return nil })
+}
+
+func (r *reportingPluginFactoryService) HealthReport() map[string]error {
+ return map[string]error{r.Name(): r.Healthy()}
+}
diff --git a/plugins/medianpoc/plugin_test.go b/plugins/medianpoc/plugin_test.go
new file mode 100644
index 00000000000..74a0695c6c9
--- /dev/null
+++ b/plugins/medianpoc/plugin_test.go
@@ -0,0 +1,105 @@
+package medianpoc
+
+import (
+ "context"
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ ocrtypes "github.com/smartcontractkit/libocr/offchainreporting2plus/types"
+
+ "github.com/smartcontractkit/libocr/offchainreporting2/reportingplugin/median"
+
+ "github.com/smartcontractkit/chainlink-relay/pkg/types"
+ "github.com/smartcontractkit/chainlink/v2/core/logger"
+)
+
+type mockErrorLog struct {
+ types.ErrorLog
+}
+
+type mockOffchainConfigDigester struct {
+ ocrtypes.OffchainConfigDigester
+}
+
+type mockContractTransmitter struct {
+ ocrtypes.ContractTransmitter
+}
+
+type mockContractConfigTracker struct {
+ ocrtypes.ContractConfigTracker
+}
+
+type mockReportCodec struct {
+ median.ReportCodec
+}
+
+type mockMedianContract struct {
+ median.MedianContract
+}
+
+type mockOnchainConfigCodec struct {
+ median.OnchainConfigCodec
+}
+
+type provider struct {
+ types.Service
+}
+
+func (p provider) OffchainConfigDigester() ocrtypes.OffchainConfigDigester {
+ return mockOffchainConfigDigester{}
+}
+
+func (p provider) ContractTransmitter() ocrtypes.ContractTransmitter {
+ return mockContractTransmitter{}
+}
+
+func (p provider) ContractConfigTracker() ocrtypes.ContractConfigTracker {
+ return mockContractConfigTracker{}
+}
+
+func (p provider) ReportCodec() median.ReportCodec {
+ return mockReportCodec{}
+}
+
+func (p provider) MedianContract() median.MedianContract {
+ return mockMedianContract{}
+}
+
+func (p provider) OnchainConfigCodec() median.OnchainConfigCodec {
+ return mockOnchainConfigCodec{}
+}
+
+func TestNewPlugin(t *testing.T) {
+ lggr := logger.TestLogger(t)
+ p := NewPlugin(lggr)
+
+ defaultSpec := "default-spec"
+ juelsPerFeeCoinSpec := "jpfc-spec"
+ config := types.ReportingPluginServiceConfig{
+ PluginConfig: fmt.Sprintf(
+ `{"pipelines": {"__DEFAULT_PIPELINE__": "%s", "juelsPerFeeCoinPipeline": "%s"}}`,
+ defaultSpec,
+ juelsPerFeeCoinSpec,
+ ),
+ }
+ pr := &mockPipelineRunner{}
+ prov := provider{}
+
+ f, err := p.newFactory(
+ context.Background(),
+ config,
+ prov,
+ pr,
+ nil,
+ mockErrorLog{},
+ )
+ require.NoError(t, err)
+
+ ds := f.DataSource.(*DataSource)
+ assert.Equal(t, defaultSpec, ds.spec)
+ jpfcDs := f.JuelsPerFeeCoinDataSource.(*DataSource)
+ assert.Equal(t, juelsPerFeeCoinSpec, jpfcDs.spec)
+}
From b1c5a856d71f560883d2cb61d633c47660d41365 Mon Sep 17 00:00:00 2001
From: Cedric
Date: Tue, 7 Nov 2023 14:59:44 +0000
Subject: [PATCH 08/13] [fix] Pull in correct commit sha for pull request
events (#11202)
---
tools/flakeytests/utils.go | 44 +++++++++++++++++++++------------
tools/flakeytests/utils_test.go | 29 ++++++++++++++++++++++
2 files changed, 57 insertions(+), 16 deletions(-)
diff --git a/tools/flakeytests/utils.go b/tools/flakeytests/utils.go
index 18ab43980b3..7ead45c8587 100644
--- a/tools/flakeytests/utils.go
+++ b/tools/flakeytests/utils.go
@@ -29,23 +29,16 @@ func DigString(mp map[string]interface{}, path []string) (string, error) {
return vs, nil
}
-func GetGithubMetadata(repo string, eventName string, sha string, path string) Context {
- event := map[string]interface{}{}
- if path != "" {
- r, err := os.Open(path)
- if err != nil {
- log.Fatalf("Error reading gh event at path: %s", path)
- }
-
- d, err := io.ReadAll(r)
- if err != nil {
- log.Fatal("Error reading gh event into string")
- }
+func getGithubMetadata(repo string, eventName string, sha string, e io.Reader) Context {
+ d, err := io.ReadAll(e)
+ if err != nil {
+ log.Fatal("Error reading gh event into string")
+ }
- err = json.Unmarshal(d, &event)
- if err != nil {
- log.Fatalf("Error unmarshaling gh event at path: %s", path)
- }
+ event := map[string]interface{}{}
+ err = json.Unmarshal(d, &event)
+ if err != nil {
+ log.Fatalf("Error unmarshaling gh event at path")
}
basicCtx := &Context{Repository: repo, CommitSHA: sha, Type: eventName}
@@ -58,8 +51,27 @@ func GetGithubMetadata(repo string, eventName string, sha string, path string) C
}
basicCtx.PullRequestURL = prURL
+
+ // For pull request events, the $GITHUB_SHA variable doesn't actually
+ // contain the sha for the latest commit, as documented here:
+ // https://stackoverflow.com/a/68068674
+ var newSha string
+ s, err := DigString(event, []string{"pull_request", "head", "sha"})
+ if err == nil {
+ newSha = s
+ }
+
+ basicCtx.CommitSHA = newSha
return *basicCtx
default:
return *basicCtx
}
}
+
+func GetGithubMetadata(repo string, eventName string, sha string, path string) Context {
+ event, err := os.Open(path)
+ if err != nil {
+ log.Fatalf("Error reading gh event at path: %s", path)
+ }
+ return getGithubMetadata(repo, eventName, sha, event)
+}
diff --git a/tools/flakeytests/utils_test.go b/tools/flakeytests/utils_test.go
index d3ef8eb602d..17d597c3c02 100644
--- a/tools/flakeytests/utils_test.go
+++ b/tools/flakeytests/utils_test.go
@@ -1,6 +1,8 @@
package flakeytests
import (
+ "fmt"
+ "strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -17,3 +19,30 @@ func TestDigString(t *testing.T) {
require.NoError(t, err)
assert.Equal(t, "some-url", out)
}
+
+var prEventTemplate = `
+{
+ "pull_request": {
+ "head": {
+ "sha": "%s"
+ },
+ "_links": {
+ "html": {
+ "href": "%s"
+ }
+ }
+ }
+}
+`
+
+func TestGetGithubMetadata(t *testing.T) {
+ repo, eventName, sha, event := "chainlink", "merge_group", "a-sha", `{}`
+ ctx := getGithubMetadata(repo, eventName, sha, strings.NewReader(event))
+ assert.Equal(t, Context{Repository: repo, CommitSHA: sha, Type: eventName}, ctx)
+
+ anotherSha, eventName, url := "another-sha", "pull_request", "a-url"
+ event = fmt.Sprintf(prEventTemplate, anotherSha, url)
+ sha = "302eb05d592132309b264e316f443f1ceb81b6c3"
+ ctx = getGithubMetadata(repo, eventName, sha, strings.NewReader(event))
+ assert.Equal(t, Context{Repository: repo, CommitSHA: anotherSha, Type: eventName, PullRequestURL: url}, ctx)
+}
From 031d9d26c32171152dd29bcad10ccc2e93478a6d Mon Sep 17 00:00:00 2001
From: Cedric
Date: Tue, 7 Nov 2023 15:23:43 +0000
Subject: [PATCH 09/13] [chore] Pin to chainlink-relay@main (#11203)
---
core/scripts/go.mod | 2 +-
core/scripts/go.sum | 4 ++--
go.mod | 2 +-
go.sum | 4 ++--
integration-tests/go.mod | 2 +-
integration-tests/go.sum | 4 ++--
6 files changed, 9 insertions(+), 9 deletions(-)
diff --git a/core/scripts/go.mod b/core/scripts/go.mod
index 7766697c608..9dbe132346c 100644
--- a/core/scripts/go.mod
+++ b/core/scripts/go.mod
@@ -305,7 +305,7 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect
- github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 // indirect
+ github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 // indirect
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect
github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect
diff --git a/core/scripts/go.sum b/core/scripts/go.sum
index 5149148d030..7025d38c20d 100644
--- a/core/scripts/go.sum
+++ b/core/scripts/go.sum
@@ -1466,8 +1466,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8=
diff --git a/go.mod b/go.mod
index a5628eb4b80..d61b7b6f61b 100644
--- a/go.mod
+++ b/go.mod
@@ -66,7 +66,7 @@ require (
github.com/shopspring/decimal v1.3.1
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353
- github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41
+ github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb
github.com/smartcontractkit/libocr v0.0.0-20231020123319-d255366a6545
diff --git a/go.sum b/go.sum
index a35e0f32bd9..f2737ab3aea 100644
--- a/go.sum
+++ b/go.sum
@@ -1467,8 +1467,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8=
diff --git a/integration-tests/go.mod b/integration-tests/go.mod
index 3c5a5e1f2e7..6f2809df8c1 100644
--- a/integration-tests/go.mod
+++ b/integration-tests/go.mod
@@ -388,7 +388,7 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 // indirect
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 // indirect
- github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 // indirect
+ github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 // indirect
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 // indirect
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb // indirect
github.com/smartcontractkit/tdh2/go/ocr2/decryptionplugin v0.0.0-20230906073235-9e478e5e19f1 // indirect
diff --git a/integration-tests/go.sum b/integration-tests/go.sum
index cd5cb84a686..9e1be8b8144 100644
--- a/integration-tests/go.sum
+++ b/integration-tests/go.sum
@@ -2370,8 +2370,8 @@ github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704 h1:T3lFWumv
github.com/smartcontractkit/caigo v0.0.0-20230621050857-b29a4ca8c704/go.mod h1:2QuJdEouTWjh5BDy5o/vgGXQtR4Gz8yH1IYB5eT7u4M=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353 h1:4iO3Ei1b/Lb0yprzclk93e1aQnYF92sIe+EJzMG87y4=
github.com/smartcontractkit/chainlink-cosmos v0.4.1-0.20231101160906-7acebcc1b353/go.mod h1:hMhGr9ok3p4442keFtK6u6Ei9yWfG66fmDwsFi3aHcw=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41 h1:nhhEtc7+u/92CGVE36/mpQCVB8MhrC3ZE3pAFbOvhd4=
-github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231106145532-206ff03d1d41/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264 h1:64bH7MmWzcy5tB16x40266DzgKr2iIVcDPjOro6Q3Us=
+github.com/smartcontractkit/chainlink-relay v0.1.7-0.20231107132621-6de9cc4fb264/go.mod h1:M9U1JV7IQi8Sfj4JR1qSi1tIh6omgW78W/8SHN/8BUQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05 h1:DaPSVnxe7oz1QJ+AVIhQWs1W3ubQvwvGo9NbHpMs1OQ=
github.com/smartcontractkit/chainlink-solana v1.0.3-0.20231023133638-72f4e799ab05/go.mod h1:o0Pn1pbaUluboaK6/yhf8xf7TiFCkyFl6WUOdwqamuU=
github.com/smartcontractkit/chainlink-starknet/relayer v0.0.1-beta-test.0.20231024133459-1ef3a11319eb h1:HiluOfEVGOQTM6BTDImOqYdMZZ7qq7fkZ3TJdmItNr8=
From f6f3cfefec2d2a44e23a000c1a6dbd64f40edd2f Mon Sep 17 00:00:00 2001
From: Sam
Date: Tue, 7 Nov 2023 10:25:51 -0500
Subject: [PATCH 10/13] Simple delete queue for mercury transmitter (#11182)
---
.../services/relay/evm/mercury/transmitter.go | 85 +++++++++++++++++--
docs/CHANGELOG.md | 5 ++
2 files changed, 84 insertions(+), 6 deletions(-)
diff --git a/core/services/relay/evm/mercury/transmitter.go b/core/services/relay/evm/mercury/transmitter.go
index 88c3113abc6..557210e58a5 100644
--- a/core/services/relay/evm/mercury/transmitter.go
+++ b/core/services/relay/evm/mercury/transmitter.go
@@ -33,6 +33,7 @@ import (
var (
maxTransmitQueueSize = 10_000
+ maxDeleteQueueSize = 10_000
transmitTimeout = 5 * time.Second
)
@@ -60,6 +61,24 @@ var (
},
[]string{"feedID"},
)
+ transmitQueueDeleteErrorCount = promauto.NewCounterVec(prometheus.CounterOpts{
+ Name: "mercury_transmit_queue_delete_error_count",
+ Help: "Running count of DB errors when trying to delete an item from the queue DB",
+ },
+ []string{"feedID"},
+ )
+ transmitQueueInsertErrorCount = promauto.NewCounterVec(prometheus.CounterOpts{
+ Name: "mercury_transmit_queue_insert_error_count",
+ Help: "Running count of DB errors when trying to insert an item into the queue DB",
+ },
+ []string{"feedID"},
+ )
+ transmitQueuePushErrorCount = promauto.NewCounterVec(prometheus.CounterOpts{
+ Name: "mercury_transmit_queue_push_error_count",
+ Help: "Running count of DB errors when trying to push an item onto the queue",
+ },
+ []string{"feedID"},
+ )
transmitServerErrorCount = promauto.NewCounterVec(prometheus.CounterOpts{
Name: "mercury_transmit_server_error_count",
Help: "Number of errored transmissions that failed due to an error returned by the mercury server",
@@ -99,9 +118,14 @@ type mercuryTransmitter struct {
queue *TransmitQueue
wg sync.WaitGroup
- transmitSuccessCount prometheus.Counter
- transmitDuplicateCount prometheus.Counter
- transmitConnectionErrorCount prometheus.Counter
+ deleteQueue chan *pb.TransmitRequest
+
+ transmitSuccessCount prometheus.Counter
+ transmitDuplicateCount prometheus.Counter
+ transmitConnectionErrorCount prometheus.Counter
+ transmitQueueDeleteErrorCount prometheus.Counter
+ transmitQueueInsertErrorCount prometheus.Counter
+ transmitQueuePushErrorCount prometheus.Counter
}
var PayloadTypes = getPayloadTypes()
@@ -139,9 +163,13 @@ func NewTransmitter(lggr logger.Logger, cfgTracker ConfigTracker, rpcClient wsrp
make(chan (struct{})),
nil,
sync.WaitGroup{},
+ make(chan *pb.TransmitRequest, maxDeleteQueueSize),
transmitSuccessCount.WithLabelValues(feedIDHex),
transmitDuplicateCount.WithLabelValues(feedIDHex),
transmitConnectionErrorCount.WithLabelValues(feedIDHex),
+ transmitQueueDeleteErrorCount.WithLabelValues(feedIDHex),
+ transmitQueueInsertErrorCount.WithLabelValues(feedIDHex),
+ transmitQueuePushErrorCount.WithLabelValues(feedIDHex),
}
}
@@ -164,6 +192,8 @@ func (mt *mercuryTransmitter) Start(ctx context.Context) (err error) {
return err
}
mt.wg.Add(1)
+ go mt.runDeleteQueueLoop()
+ mt.wg.Add(1)
go mt.runQueueLoop()
return nil
})
@@ -192,6 +222,46 @@ func (mt *mercuryTransmitter) HealthReport() map[string]error {
return report
}
+func (mt *mercuryTransmitter) runDeleteQueueLoop() {
+ defer mt.wg.Done()
+ runloopCtx, cancel := mt.stopCh.Ctx(context.Background())
+ defer cancel()
+
+ // Exponential backoff for very rarely occurring errors (DB disconnect etc)
+ b := backoff.Backoff{
+ Min: 1 * time.Second,
+ Max: 120 * time.Second,
+ Factor: 2,
+ Jitter: true,
+ }
+
+ for {
+ select {
+ case req := <-mt.deleteQueue:
+ for {
+ if err := mt.persistenceManager.Delete(runloopCtx, req); err != nil {
+ mt.lggr.Errorw("Failed to delete transmit request record", "error", err, "req", req)
+ mt.transmitQueueDeleteErrorCount.Inc()
+ select {
+ case <-time.After(b.Duration()):
+ // Wait a backoff duration before trying to delete again
+ continue
+ case <-mt.stopCh:
+ // abort and return immediately on stop even if items remain in queue
+ return
+ }
+ }
+ break
+ }
+ // success
+ b.Reset()
+ case <-mt.stopCh:
+ // abort and return immediately on stop even if items remain in queue
+ return
+ }
+ }
+}
+
func (mt *mercuryTransmitter) runQueueLoop() {
defer mt.wg.Done()
// Exponential backoff with very short retry interval (since latency is a priority)
@@ -253,9 +323,10 @@ func (mt *mercuryTransmitter) runQueueLoop() {
}
}
- if err := mt.persistenceManager.Delete(runloopCtx, t.Req); err != nil {
- mt.lggr.Errorw("Failed to delete transmit request record", "error", err, "reportCtx", t.ReportCtx)
- return
+ select {
+ case mt.deleteQueue <- t.Req:
+ default:
+ mt.lggr.Criticalw("Delete queue is full", "reportCtx", t.ReportCtx)
}
}
}
@@ -288,9 +359,11 @@ func (mt *mercuryTransmitter) Transmit(ctx context.Context, reportCtx ocrtypes.R
mt.lggr.Tracew("Transmit enqueue", "req", req, "report", report, "reportCtx", reportCtx, "signatures", signatures)
if err := mt.persistenceManager.Insert(ctx, req, reportCtx); err != nil {
+ mt.transmitQueueInsertErrorCount.Inc()
return err
}
if ok := mt.queue.Push(req, reportCtx); !ok {
+ mt.transmitQueuePushErrorCount.Inc()
return errors.New("transmit queue is closed")
}
return nil
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index a9f9d080f42..a10f9dd1c6d 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -12,6 +12,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Added a new, optional WebServer authentication option that supports LDAP as a user identity provider. This enables user login access and user roles to be managed and provisioned via a centralized remote server that supports the LDAP protocol, which can be helpful when running multiple nodes. See the documentation for more information and config setup instructions. There is a new `[WebServer].AuthenticationMethod` config option, when set to `ldap` requires the new `[WebServer.LDAP]` config section to be defined, see the reference `docs/core.toml`.
+- New prom metrics for mercury:
+ `mercury_transmit_queue_delete_error_count`
+ `mercury_transmit_queue_insert_error_count`
+ `mercury_transmit_queue_push_error_count`
+ Nops should consider alerting on these.
### Changed
From 8b4e0f8db32502f928860b928e6102a96db78b07 Mon Sep 17 00:00:00 2001
From: Awbrey Hughlett
Date: Tue, 7 Nov 2023 12:00:25 -0500
Subject: [PATCH 11/13] Update SimulatedBackendClient CallContext (#11164)
* Update SimulatedBackendClient CallContext
The function `CallContext` has different supported contract function calls than
`BatchCallContext` even though the latter is simply a batch version of the
former.
This commit makes the two functions match both in the supported calls, but also
in the validation and execution of those calls.
* Update core/chains/evm/client/simulated_backend_client.go
Use suggestion on default address returned on error.
Co-authored-by: Jordan Krage
* replace errors with panics
* align parameters to documentation, real-world RPCs, and the simulated backend client
* allow different receipt types
* comment on strong typing on result
* remove logic for handling incomplete transaction receipt handling on simulated backend client
* reduce panics in favor of errors
* remove commented code
---------
Co-authored-by: Jordan Krage
---
.../evm/client/simulated_backend_client.go | 503 ++++++++----------
core/chains/evm/txmgr/transmitchecker.go | 2 +-
.../plugins/ocr2keeper/evm21/core/utils.go | 17 +-
3 files changed, 235 insertions(+), 287 deletions(-)
diff --git a/core/chains/evm/client/simulated_backend_client.go b/core/chains/evm/client/simulated_backend_client.go
index 78239089676..d542e98e6eb 100644
--- a/core/chains/evm/client/simulated_backend_client.go
+++ b/core/chains/evm/client/simulated_backend_client.go
@@ -25,6 +25,41 @@ import (
"github.com/smartcontractkit/chainlink/v2/core/utils"
)
+func init() {
+ var err error
+
+ balanceOfABI, err = abi.JSON(strings.NewReader(balanceOfABIString))
+ if err != nil {
+ panic(fmt.Errorf("%w: while parsing erc20ABI", err))
+ }
+}
+
+var (
+ balanceOfABIString = `[
+ {
+ "constant": true,
+ "inputs": [
+ {
+ "name": "_owner",
+ "type": "address"
+ }
+ ],
+ "name": "balanceOf",
+ "outputs": [
+ {
+ "name": "balance",
+ "type": "uint256"
+ }
+ ],
+ "payable": false,
+ "stateMutability": "view",
+ "type": "function"
+ }
+]`
+
+ balanceOfABI abi.ABI
+)
+
// SimulatedBackendClient is an Client implementation using a simulated
// blockchain backend. Note that not all RPC methods are implemented here.
type SimulatedBackendClient struct {
@@ -51,69 +86,6 @@ func (c *SimulatedBackendClient) Dial(context.Context) error {
// other simulated clients might still be using it
func (c *SimulatedBackendClient) Close() {}
-// checkEthCallArgs extracts and verifies the arguments for an eth_call RPC
-func (c *SimulatedBackendClient) checkEthCallArgs(
- args []interface{}) (*CallArgs, *big.Int, error) {
- if len(args) != 2 {
- return nil, nil, fmt.Errorf(
- "should have two arguments after \"eth_call\", got %d", len(args))
- }
- callArgs, ok := args[0].(map[string]interface{})
- if !ok {
- return nil, nil, fmt.Errorf("third arg to SimulatedBackendClient.Call "+
- "must be an eth.CallArgs, got %+#v", args[0])
- }
- blockNumber, err := c.blockNumber(args[1])
- if err != nil {
- return nil, nil, fmt.Errorf("fourth arg to SimulatedBackendClient.Call "+
- "must be the string \"latest\", or a *big.Int, got %#+v", args[1])
- }
-
- // to and from need to map to a common.Address but could come in as a string
- var (
- toAddr common.Address
- frmAddr common.Address
- )
-
- toAddr, err = interfaceToAddress(callArgs["to"])
- if err != nil {
- return nil, nil, err
- }
-
- // from is optional in the standard client; default to 0x when missing
- if value, ok := callArgs["from"]; ok {
- addr, err := interfaceToAddress(value)
- if err != nil {
- return nil, nil, err
- }
-
- frmAddr = addr
- } else {
- frmAddr = common.HexToAddress("0x")
- }
-
- ca := CallArgs{
- To: toAddr,
- From: frmAddr,
- Data: callArgs["data"].(hexutil.Bytes),
- }
-
- return &ca, blockNumber, nil
-}
-
-func interfaceToAddress(value interface{}) (common.Address, error) {
- switch v := value.(type) {
- case common.Address:
- return v, nil
- case string:
- return common.HexToAddress(v), nil
- case *big.Int:
- return common.BigToAddress(v), nil
- default:
- return common.HexToAddress("0x"), fmt.Errorf("unrecognized value type for converting value to common.Address; try string, *big.Int, or common.Address")
- }
-}
-
// CallContext mocks the ethereum client RPC calls used by chainlink, copying the
// return value into result.
// The simulated client avoids the old block error from the simulated backend by
@@ -121,41 +93,16 @@ func interfaceToAddress(value interface{}) (common.Address, error) {
// and will not return an error when an old block is used.
func (c *SimulatedBackendClient) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
switch method {
+ case "eth_getTransactionReceipt":
+ return c.ethGetTransactionReceipt(ctx, result, args...)
+ case "eth_getBlockByNumber":
+ return c.ethGetBlockByNumber(ctx, result, args...)
case "eth_call":
- var (
- callArgs *CallArgs
- b []byte
- err error
- )
-
- if callArgs, _, err = c.checkEthCallArgs(args); err != nil {
- return err
- }
-
- callMsg := ethereum.CallMsg{From: callArgs.From, To: &callArgs.To, Data: callArgs.Data}
-
- if b, err = c.b.CallContract(ctx, callMsg, nil /* always latest block */); err != nil {
- return fmt.Errorf("%w: while calling contract at address %x with "+
- "data %x", err, callArgs.To, callArgs.Data)
- }
-
- switch r := result.(type) {
- case *hexutil.Bytes:
- *r = append(*r, b...)
-
- if !bytes.Equal(*r, b) {
- return fmt.Errorf("was passed a non-empty array, or failed to copy "+
- "answer. Expected %x = %x", *r, b)
- }
- return nil
- default:
- return fmt.Errorf("first arg to SimulatedBackendClient.Call is an "+
- "unrecognized type: %T; add processing logic for it here", result)
- }
+ return c.ethCall(ctx, result, args...)
+ case "eth_getHeaderByNumber":
+ return c.ethGetHeaderByNumber(ctx, result, args...)
default:
- return fmt.Errorf("second arg to SimulatedBackendClient.Call is an RPC "+
- "API method which has not yet been implemented: %s. Add processing for "+
- "it here", method)
+ return fmt.Errorf("second arg to SimulatedBackendClient.Call is an RPC API method which has not yet been implemented: %s. Add processing for it here", method)
}
}
@@ -175,38 +122,6 @@ func (c *SimulatedBackendClient) currentBlockNumber() *big.Int {
return c.b.Blockchain().CurrentBlock().Number
}
-var balanceOfABIString = `[
- {
- "constant": true,
- "inputs": [
- {
- "name": "_owner",
- "type": "address"
- }
- ],
- "name": "balanceOf",
- "outputs": [
- {
- "name": "balance",
- "type": "uint256"
- }
- ],
- "payable": false,
- "stateMutability": "view",
- "type": "function"
- }
-]`
-
-var balanceOfABI abi.ABI
-
-func init() {
- var err error
- balanceOfABI, err = abi.JSON(strings.NewReader(balanceOfABIString))
- if err != nil {
- panic(fmt.Errorf("%w: while parsing erc20ABI", err))
- }
-}
-
func (c *SimulatedBackendClient) TokenBalance(ctx context.Context, address common.Address, contractAddress common.Address) (balance *big.Int, err error) {
callData, err := balanceOfABI.Pack("balanceOf", address)
if err != nil {
@@ -251,13 +166,12 @@ func (c *SimulatedBackendClient) blockNumber(number interface{}) (blockNumber *b
case "earliest":
return big.NewInt(0), nil
case "pending":
- panic("not implemented") // I don't understand the semantics of this.
+ panic("pending block not supported by simulated backend client") // I don't understand the semantics of this.
// return big.NewInt(0).Add(c.currentBlockNumber(), big.NewInt(1)), nil
default:
- blockNumber, err = utils.HexToUint256(n)
+ blockNumber, err := hexutil.DecodeBig(n)
if err != nil {
- return nil, fmt.Errorf("%w: while parsing '%s' as hex-encoded"+
- "block number", err, n)
+ return nil, fmt.Errorf("%w: while parsing '%s' as hex-encoded block number", err, n)
}
return blockNumber, nil
}
@@ -521,114 +435,18 @@ func (c *SimulatedBackendClient) BatchCallContext(ctx context.Context, b []rpc.B
for i, elem := range b {
switch elem.Method {
case "eth_getTransactionReceipt":
- if _, ok := elem.Result.(*evmtypes.Receipt); !ok {
- return fmt.Errorf("SimulatedBackendClient expected return type of *evmtypes.Receipt for eth_getTransactionReceipt, got type %T", elem.Result)
- }
- if len(elem.Args) != 1 {
- return fmt.Errorf("SimulatedBackendClient expected 1 arg, got %d for eth_getTransactionReceipt", len(elem.Args))
- }
- hash, is := elem.Args[0].(common.Hash)
- if !is {
- return fmt.Errorf("SimulatedBackendClient expected arg to be a hash, got: %T", elem.Args[0])
- }
- receipt, err := c.b.TransactionReceipt(ctx, hash)
- if receipt != nil {
- *(b[i].Result.(*evmtypes.Receipt)) = *evmtypes.FromGethReceipt(receipt)
- }
- b[i].Error = err
+ b[i].Error = c.ethGetTransactionReceipt(ctx, b[i].Result, b[i].Args...)
case "eth_getBlockByNumber":
- switch v := elem.Result.(type) {
- case *evmtypes.Head:
- case *evmtypes.Block:
- default:
- return fmt.Errorf("SimulatedBackendClient expected return type of [*evmtypes.Head] or [*evmtypes.Block] for eth_getBlockByNumber, got type %T", v)
- }
- if len(elem.Args) != 2 {
- return fmt.Errorf("SimulatedBackendClient expected 2 args, got %d for eth_getBlockByNumber", len(elem.Args))
- }
- blockNumOrTag, is := elem.Args[0].(string)
- if !is {
- return fmt.Errorf("SimulatedBackendClient expected first arg to be a string for eth_getBlockByNumber, got: %T", elem.Args[0])
- }
- _, is = elem.Args[1].(bool)
- if !is {
- return fmt.Errorf("SimulatedBackendClient expected second arg to be a boolean for eth_getBlockByNumber, got: %T", elem.Args[1])
- }
- header, err := c.fetchHeader(ctx, blockNumOrTag)
- if err != nil {
- return err
- }
- switch res := elem.Result.(type) {
- case *evmtypes.Head:
- res.Number = header.Number.Int64()
- res.Hash = header.Hash()
- res.ParentHash = header.ParentHash
- res.Timestamp = time.Unix(int64(header.Time), 0).UTC()
- case *evmtypes.Block:
- res.Number = header.Number.Int64()
- res.Hash = header.Hash()
- res.ParentHash = header.ParentHash
- res.Timestamp = time.Unix(int64(header.Time), 0).UTC()
- default:
- return fmt.Errorf("SimulatedBackendClient Unexpected Type %T", elem.Result)
- }
- b[i].Error = err
+ b[i].Error = c.ethGetBlockByNumber(ctx, b[i].Result, b[i].Args...)
case "eth_call":
- if len(elem.Args) != 2 {
- return fmt.Errorf("SimulatedBackendClient expected 2 args, got %d for eth_call", len(elem.Args))
- }
-
- _, ok := elem.Result.(*string)
- if !ok {
- return fmt.Errorf("SimulatedBackendClient expected result to be *string for eth_call, got: %T", elem.Result)
- }
-
- params, ok := elem.Args[0].(map[string]interface{})
- if !ok {
- return fmt.Errorf("SimulatedBackendClient expected first arg to be map[string]interface{} for eth_call, got: %T", elem.Args[0])
- }
-
- blockNum, ok := elem.Args[1].(string)
- if !ok {
- return fmt.Errorf("SimulatedBackendClient expected second arg to be a string for eth_call, got: %T", elem.Args[1])
- }
-
- if blockNum != "" {
- if _, ok = new(big.Int).SetString(blockNum, 0); !ok {
- return fmt.Errorf("error while converting block number string: %s to big.Int ", blockNum)
- }
- }
-
- callMsg := toCallMsg(params)
- resp, err := c.b.CallContract(ctx, callMsg, nil)
- *(b[i].Result.(*string)) = hexutil.Encode(resp)
- b[i].Error = err
+ b[i].Error = c.ethCall(ctx, b[i].Result, b[i].Args...)
case "eth_getHeaderByNumber":
- if len(elem.Args) != 1 {
- return fmt.Errorf("SimulatedBackendClient expected 2 args, got %d for eth_getHeaderByNumber", len(elem.Args))
- }
- blockNum, is := elem.Args[0].(string)
- if !is {
- return fmt.Errorf("SimulatedBackendClient expected first arg to be a string for eth_getHeaderByNumber, got: %T", elem.Args[0])
- }
- n, err := hexutil.DecodeBig(blockNum)
- if err != nil {
- return fmt.Errorf("error while converting hex block number %s to big.Int ", blockNum)
- }
- header, err := c.b.HeaderByNumber(ctx, n)
- if err != nil {
- return err
- }
- switch v := elem.Result.(type) {
- case *types.Header:
- b[i].Result = header
- default:
- return fmt.Errorf("SimulatedBackendClient Unexpected Type %T", v)
- }
+ b[i].Error = c.ethGetHeaderByNumber(ctx, b[i].Result, b[i].Args...)
default:
return fmt.Errorf("SimulatedBackendClient got unsupported method %s", elem.Method)
}
}
+
return nil
}
@@ -655,32 +473,175 @@ func (c *SimulatedBackendClient) Commit() common.Hash {
return c.b.Commit()
}
-func toCallMsg(params map[string]interface{}) ethereum.CallMsg {
- var callMsg ethereum.CallMsg
+func (c *SimulatedBackendClient) IsL2() bool {
+ return false
+}
- switch to := params["to"].(type) {
- case string:
- toAddr := common.HexToAddress(to)
- callMsg.To = &toAddr
- case common.Address:
- callMsg.To = &to
- case *common.Address:
- callMsg.To = to
+func (c *SimulatedBackendClient) fetchHeader(ctx context.Context, blockNumOrTag string) (*types.Header, error) {
+ switch blockNumOrTag {
+ case rpc.SafeBlockNumber.String():
+ return c.b.Blockchain().CurrentSafeBlock(), nil
+ case rpc.LatestBlockNumber.String():
+ return c.b.Blockchain().CurrentHeader(), nil
+ case rpc.FinalizedBlockNumber.String():
+ return c.b.Blockchain().CurrentFinalBlock(), nil
default:
- panic("unexpected type of 'to' parameter")
+ blockNum, ok := new(big.Int).SetString(blockNumOrTag, 0)
+ if !ok {
+ return nil, fmt.Errorf("error while converting block number string: %s to big.Int ", blockNumOrTag)
+ }
+ return c.b.HeaderByNumber(ctx, blockNum)
}
+}
- switch from := params["from"].(type) {
- case nil:
- // This parameter is not required so nil is acceptable
- case string:
- callMsg.From = common.HexToAddress(from)
- case common.Address:
- callMsg.From = from
- case *common.Address:
- callMsg.From = *from
+func (c *SimulatedBackendClient) ethGetTransactionReceipt(ctx context.Context, result interface{}, args ...interface{}) error {
+ if len(args) != 1 {
+ return fmt.Errorf("SimulatedBackendClient expected 1 arg, got %d for eth_getTransactionReceipt", len(args))
+ }
+
+ hash, is := args[0].(common.Hash)
+ if !is {
+ return fmt.Errorf("SimulatedBackendClient expected arg to be a hash, got: %T", args[0])
+ }
+
+ receipt, err := c.b.TransactionReceipt(ctx, hash)
+ if err != nil {
+ return err
+ }
+
+ // strongly typing the result here has the consequence of not being flexible in
+ // custom types where a real-world RPC client would allow for custom types with
+ // custom marshalling.
+ switch typed := result.(type) {
+ case *types.Receipt:
+ *typed = *receipt
+ case *evmtypes.Receipt:
+ *typed = *evmtypes.FromGethReceipt(receipt)
+ default:
+ return fmt.Errorf("SimulatedBackendClient expected return type of *evmtypes.Receipt for eth_getTransactionReceipt, got type %T", result)
+ }
+
+ return nil
+}
+
+func (c *SimulatedBackendClient) ethGetBlockByNumber(ctx context.Context, result interface{}, args ...interface{}) error {
+ if len(args) != 2 {
+ return fmt.Errorf("SimulatedBackendClient expected 2 args, got %d for eth_getBlockByNumber", len(args))
+ }
+
+ blockNumOrTag, is := args[0].(string)
+ if !is {
+ return fmt.Errorf("SimulatedBackendClient expected first arg to be a string for eth_getBlockByNumber, got: %T", args[0])
+ }
+
+ _, is = args[1].(bool)
+ if !is {
+ return fmt.Errorf("SimulatedBackendClient expected second arg to be a boolean for eth_getBlockByNumber, got: %T", args[1])
+ }
+
+ header, err := c.fetchHeader(ctx, blockNumOrTag)
+ if err != nil {
+ return err
+ }
+
+ switch res := result.(type) {
+ case *evmtypes.Head:
+ res.Number = header.Number.Int64()
+ res.Hash = header.Hash()
+ res.ParentHash = header.ParentHash
+ res.Timestamp = time.Unix(int64(header.Time), 0).UTC()
+ case *evmtypes.Block:
+ res.Number = header.Number.Int64()
+ res.Hash = header.Hash()
+ res.ParentHash = header.ParentHash
+ res.Timestamp = time.Unix(int64(header.Time), 0).UTC()
+ default:
+ return fmt.Errorf("SimulatedBackendClient Unexpected Type %T", res)
+ }
+
+ return nil
+}
+
+func (c *SimulatedBackendClient) ethCall(ctx context.Context, result interface{}, args ...interface{}) error {
+ if len(args) != 2 {
+ return fmt.Errorf("SimulatedBackendClient expected 2 args, got %d for eth_call", len(args))
+ }
+
+ params, ok := args[0].(map[string]interface{})
+ if !ok {
+ return fmt.Errorf("SimulatedBackendClient expected first arg to be map[string]interface{} for eth_call, got: %T", args[0])
+ }
+
+ if _, err := c.blockNumber(args[1]); err != nil {
+ return fmt.Errorf("SimulatedBackendClient expected second arg to be the string 'latest' or a *big.Int for eth_call, got: %T", args[1])
+ }
+
+ resp, err := c.b.CallContract(ctx, toCallMsg(params), nil /* always latest block on simulated backend */)
+ if err != nil {
+ return err
+ }
+
+ switch typedResult := result.(type) {
+ case *hexutil.Bytes:
+ *typedResult = append(*typedResult, resp...)
+
+ if !bytes.Equal(*typedResult, resp) {
+ return fmt.Errorf("SimulatedBackendClient was passed a non-empty array, or failed to copy answer. Expected %x = %x", *typedResult, resp)
+ }
+ case *string:
+ *typedResult = hexutil.Encode(resp)
default:
- panic("unexpected type of 'from' parameter")
+ return fmt.Errorf("SimulatedBackendClient unexpected type %T", result)
+ }
+
+ return nil
+}
+
+func (c *SimulatedBackendClient) ethGetHeaderByNumber(ctx context.Context, result interface{}, args ...interface{}) error {
+ if len(args) != 1 {
+ return fmt.Errorf("SimulatedBackendClient expected 1 arg, got %d for eth_getHeaderByNumber", len(args))
+ }
+
+ blockNumber, err := c.blockNumber(args[0])
+ if err != nil {
+ return fmt.Errorf("SimulatedBackendClient expected first arg to be a string for eth_getHeaderByNumber: %w", err)
+ }
+
+ header, err := c.b.HeaderByNumber(ctx, blockNumber)
+ if err != nil {
+ return err
+ }
+
+ switch typedResult := result.(type) {
+ case *types.Header:
+ *typedResult = *header
+ default:
+ return fmt.Errorf("SimulatedBackendClient unexpected Type %T", typedResult)
+ }
+
+ return nil
+}
+
+func toCallMsg(params map[string]interface{}) ethereum.CallMsg {
+ var callMsg ethereum.CallMsg
+
+ toAddr, err := interfaceToAddress(params["to"])
+ if err != nil {
+ panic(fmt.Errorf("unexpected 'to' parameter: %s", err))
+ }
+
+ callMsg.To = &toAddr
+
+ // from is optional in the standard client; default to 0x when missing
+ if value, ok := params["from"]; ok {
+ addr, err := interfaceToAddress(value)
+ if err != nil {
+ panic(fmt.Errorf("unexpected 'from' parameter: %s", err))
+ }
+
+ callMsg.From = addr
+ } else {
+ callMsg.From = common.HexToAddress("0x")
}
switch data := params["data"].(type) {
@@ -691,7 +652,7 @@ func toCallMsg(params map[string]interface{}) ethereum.CallMsg {
case []byte:
callMsg.Data = data
default:
- panic("unexpected type of 'data' parameter")
+ panic("unexpected type of 'data' parameter; try hexutil.Bytes, []byte, or nil")
}
if value, ok := params["value"].(*big.Int); ok {
@@ -709,23 +670,23 @@ func toCallMsg(params map[string]interface{}) ethereum.CallMsg {
return callMsg
}
-func (c *SimulatedBackendClient) IsL2() bool {
- return false
-}
+func interfaceToAddress(value interface{}) (common.Address, error) {
+ switch v := value.(type) {
+ case common.Address:
+ return v, nil
+ case string:
+ if ok := common.IsHexAddress(v); !ok {
+ return common.Address{}, fmt.Errorf("string not formatted as a hex encoded evm address")
+ }
-func (c *SimulatedBackendClient) fetchHeader(ctx context.Context, blockNumOrTag string) (*types.Header, error) {
- switch blockNumOrTag {
- case rpc.SafeBlockNumber.String():
- return c.b.Blockchain().CurrentSafeBlock(), nil
- case rpc.LatestBlockNumber.String():
- return c.b.Blockchain().CurrentHeader(), nil
- case rpc.FinalizedBlockNumber.String():
- return c.b.Blockchain().CurrentFinalBlock(), nil
- default:
- blockNum, ok := new(big.Int).SetString(blockNumOrTag, 0)
- if !ok {
- return nil, fmt.Errorf("error while converting block number string: %s to big.Int ", blockNumOrTag)
+ return common.HexToAddress(v), nil
+ case *big.Int:
+ if v.Uint64() > 0 || len(v.Bytes()) > 20 {
+ return common.Address{}, fmt.Errorf("invalid *big.Int; value must be larger than 0 with a byte length <= 20")
}
- return c.b.HeaderByNumber(ctx, blockNum)
+
+ return common.BigToAddress(v), nil
+ default:
+ return common.Address{}, fmt.Errorf("unrecognized value type for converting value to common.Address; use hex encoded string, *big.Int, or common.Address")
}
}
diff --git a/core/chains/evm/txmgr/transmitchecker.go b/core/chains/evm/txmgr/transmitchecker.go
index 4636b708489..eb6edd3f587 100644
--- a/core/chains/evm/txmgr/transmitchecker.go
+++ b/core/chains/evm/txmgr/transmitchecker.go
@@ -217,7 +217,7 @@ func (v *VRFV1Checker) Check(
requestTransactionReceipt := &gethtypes.Receipt{}
batch := []rpc.BatchElem{{
Method: "eth_getBlockByNumber",
- Args: []interface{}{nil},
+ Args: []interface{}{"latest", false},
Result: mostRecentHead,
}, {
Method: "eth_getTransactionReceipt",
diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/core/utils.go b/core/services/ocr2/plugins/ocr2keeper/evm21/core/utils.go
index 6a31b938fc6..1da28c1ad09 100644
--- a/core/services/ocr2/plugins/ocr2keeper/evm21/core/utils.go
+++ b/core/services/ocr2/plugins/ocr2keeper/evm21/core/utils.go
@@ -3,7 +3,6 @@ package core
import (
"context"
"math/big"
- "strings"
"github.com/ethereum/go-ethereum/common"
@@ -14,20 +13,8 @@ import (
// GetTxBlock calls eth_getTransactionReceipt on the eth client to obtain a tx receipt
func GetTxBlock(ctx context.Context, client client.Client, txHash common.Hash) (*big.Int, common.Hash, error) {
receipt := types.Receipt{}
- err := client.CallContext(ctx, &receipt, "eth_getTransactionReceipt", txHash)
- if err != nil {
- if strings.Contains(err.Error(), "not yet been implemented") {
- // workaround for simulated chains
- // Exploratory: fix this properly (e.g. in the simulated backend)
- r, err1 := client.TransactionReceipt(ctx, txHash)
- if err1 != nil {
- return nil, common.Hash{}, err1
- }
- if r.Status != 1 {
- return nil, common.Hash{}, nil
- }
- return r.BlockNumber, r.BlockHash, nil
- }
+
+ if err := client.CallContext(ctx, &receipt, "eth_getTransactionReceipt", txHash); err != nil {
return nil, common.Hash{}, err
}
From 38de9a61c0233b412e9cf8a32011909022da6320 Mon Sep 17 00:00:00 2001
From: FelixFan1992
Date: Tue, 7 Nov 2023 12:08:01 -0500
Subject: [PATCH 12/13] add a TODO for 206 response parsing (#11188)
---
.../plugins/ocr2keeper/evm21/streams_lookup.go | 15 +--------------
1 file changed, 1 insertion(+), 14 deletions(-)
diff --git a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go
index f183e1f6bbe..660550afe97 100644
--- a/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go
+++ b/core/services/ocr2/plugins/ocr2keeper/evm21/streams_lookup.go
@@ -514,20 +514,7 @@ func (r *EvmRegistry) multiFeedsRequest(ctx context.Context, ch chan<- MercuryDa
state = encoding.MercuryFlakyFailure
return fmt.Errorf("%d", resp.StatusCode)
} else if resp.StatusCode == http.StatusPartialContent {
- //var response MercuryV03Response
- //err1 = json.Unmarshal(body, &response)
- //if err1 != nil {
- // lggr.Warnf("at timestamp %s upkeep %s failed to unmarshal body to MercuryV03Response from mercury v0.3: %v", sl.Time.String(), sl.upkeepId.String(), err1)
- // retryable = false
- // state = encoding.MercuryUnmarshalError
- // return err1
- //}
- // in v0.3, if some feeds are not available, the server will only return available feeds, but we need to make sure ALL feeds are retrieved before calling user contract
- // hence, retry in this case. retry will help when we send a very new timestamp and reports are not yet generated
- //var receivedFeeds []string
- //for _, f := range response.Reports {
- // receivedFeeds = append(receivedFeeds, f.FeedID)
- //}
+ // TODO (AUTO-5044): handle response code 206 entirely with errors field parsing
lggr.Warnf("at timestamp %s upkeep %s requested [%s] feeds but mercury v0.3 server returned 206 status, treating it as 404 and retrying", sl.Time.String(), sl.upkeepId.String(), sl.Feeds)
retryable = true
state = encoding.MercuryFlakyFailure
From 4e45a2ac05849703f34a4f5ce4ae539e9cfdcbcf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Friedemann=20F=C3=BCrst?=
<59653747+friedemannf@users.noreply.github.com>
Date: Tue, 7 Nov 2023 18:43:02 +0100
Subject: [PATCH 13/13] Add zkSync support (#11162)
* feat(zksync): add custom error messages
* feat(zksync): add custom chain type to ignore 0x71 transaction type and commit default node config
* fix: add exhaustive case stmt for zkSync ChainType
* docs: regenerate CONFIG.md
* test: add zksync ChainType to expected output
* test: add test for custom zkSync tx type
* fix: reduce HistoryDepth setting to 5
* ci: trigger pipelines
* fix(zksync): also ignore tx type 0xff
* docs(zksync): regenerate CONFIG.md
---
core/chains/evm/client/errors.go | 18 +-
core/chains/evm/client/errors_test.go | 16 ++
.../config/toml/defaults/zkSync_Goerli.toml | 14 ++
.../config/toml/defaults/zkSync_Mainnet.toml | 14 ++
.../evm/gas/block_history_estimator_test.go | 16 ++
core/chains/evm/gas/chain_specific.go | 7 +
core/config/chaintype.go | 5 +-
core/config/docs/chains-evm.toml | 2 +-
core/scripts/common/helpers.go | 5 +
core/services/chainlink/config_test.go | 4 +-
core/services/ocr/contract_tracker.go | 2 +-
docs/CONFIG.md | 160 +++++++++++++++++-
12 files changed, 255 insertions(+), 8 deletions(-)
create mode 100644 core/chains/evm/config/toml/defaults/zkSync_Goerli.toml
create mode 100644 core/chains/evm/config/toml/defaults/zkSync_Mainnet.toml
diff --git a/core/chains/evm/client/errors.go b/core/chains/evm/client/errors.go
index 0d177455e33..4cb505dc9eb 100644
--- a/core/chains/evm/client/errors.go
+++ b/core/chains/evm/client/errors.go
@@ -207,7 +207,23 @@ var harmony = ClientErrors{
Fatal: harmonyFatal,
}
-var clients = []ClientErrors{parity, geth, arbitrum, metis, substrate, avalanche, nethermind, harmony, besu, erigon, klaytn, celo}
+var zkSync = ClientErrors{
+ NonceTooLow: regexp.MustCompile(`(?:: |^)nonce too low\..+actual: \d*$`),
+ NonceTooHigh: regexp.MustCompile(`(?:: |^)nonce too high\..+actual: \d*$`),
+ TerminallyUnderpriced: regexp.MustCompile(`(?:: |^)max fee per gas less than block base fee$`),
+ InsufficientEth: regexp.MustCompile(`(?:: |^)(?:insufficient balance for transfer$|insufficient funds for gas + value)`),
+ TxFeeExceedsCap: regexp.MustCompile(`(?:: |^)max priority fee per gas higher than max fee per gas$`),
+ // intrinsic gas too low - gas limit less than 14700
+ // Not enough gas for transaction validation - gas limit less than L2 fee
+ // Failed to pay the fee to the operator - gas limit less than L2+L1 fee
+ // Error function_selector = 0x, data = 0x - contract call with gas limit of 0
+ // can't start a transaction from a non-account - trying to send from an invalid address, e.g. estimating a contract -> contract tx
+ // max fee per gas higher than 2^64-1 - uint64 overflow
+ // oversized data - data too large
+ Fatal: regexp.MustCompile(`(?:: |^)(?:exceeds block gas limit|intrinsic gas too low|Not enough gas for transaction validation|Failed to pay the fee to the operator|Error function_selector = 0x, data = 0x|invalid sender. can't start a transaction from a non-account|max(?: priority)? fee per (?:gas|pubdata byte) higher than 2\^64-1|oversized data. max: \d+; actual: \d+)$`),
+}
+
+var clients = []ClientErrors{parity, geth, arbitrum, metis, substrate, avalanche, nethermind, harmony, besu, erigon, klaytn, celo, zkSync}
func (s *SendError) is(errorType int) bool {
if s == nil || s.err == nil {
diff --git a/core/chains/evm/client/errors_test.go b/core/chains/evm/client/errors_test.go
index a5a3cc15eb6..ad8079824ab 100644
--- a/core/chains/evm/client/errors_test.go
+++ b/core/chains/evm/client/errors_test.go
@@ -40,6 +40,7 @@ func Test_Eth_Errors(t *testing.T) {
{"call failed: nonce too low: address 0x0499BEA33347cb62D79A9C0b1EDA01d8d329894c current nonce (5833) > tx nonce (5511)", true, "Avalanche"},
{"call failed: OldNonce", true, "Nethermind"},
{"call failed: OldNonce, Current nonce: 22, nonce of rejected tx: 17", true, "Nethermind"},
+ {"nonce too low. allowed nonce range: 427 - 447, actual: 426", true, "zkSync"},
}
for _, test := range tests {
@@ -60,6 +61,7 @@ func Test_Eth_Errors(t *testing.T) {
{"nonce too high: address 0x336394A3219e71D9d9bd18201d34E95C1Bb7122C, tx: 8089 state: 8090", true, "Arbitrum"},
{"nonce too high", true, "Geth"},
{"nonce too high", true, "Erigon"},
+ {"nonce too high. allowed nonce range: 427 - 477, actual: 527", true, "zkSync"},
}
for _, test := range tests {
@@ -152,6 +154,7 @@ func Test_Eth_Errors(t *testing.T) {
{"FeeTooLowToCompete", true, "Nethermind"},
{"transaction underpriced", true, "Klaytn"},
{"intrinsic gas too low", true, "Klaytn"},
+ {"max fee per gas less than block base fee", true, "zkSync"},
}
for _, test := range tests {
@@ -194,6 +197,8 @@ func Test_Eth_Errors(t *testing.T) {
{"call failed: InsufficientFunds, Account balance: 4740799397601480913, cumulative cost: 22019342038993800000", true, "Nethermind"},
{"insufficient funds", true, "Klaytn"},
{"insufficient funds for gas * price + value + gatewayFee", true, "celo"},
+ {"insufficient balance for transfer", true, "zkSync"},
+ {"insufficient funds for gas + value. balance: 42719769622667482000, fee: 48098250000000, value: 42719769622667482000", true, "celo"},
}
for _, test := range tests {
err = evmclient.NewSendErrorS(test.message)
@@ -213,6 +218,7 @@ func Test_Eth_Errors(t *testing.T) {
{"invalid gas fee cap", true, "Klaytn"},
{"max fee per gas higher than max priority fee per gas", true, "Klaytn"},
{"tx fee (1.10 of currency celo) exceeds the configured cap (1.00 celo)", true, "celo"},
+ {"max priority fee per gas higher than max fee per gas", true, "zkSync"},
}
for _, test := range tests {
err = evmclient.NewSendErrorS(test.message)
@@ -329,6 +335,16 @@ func Test_Eth_Errors_Fatal(t *testing.T) {
{"`to` address of transaction in blacklist", true, "Harmony"},
{"`from` address of transaction in blacklist", true, "Harmony"},
{"staking message does not match directive message", true, "Harmony"},
+
+ {"intrinsic gas too low", true, "zkSync"},
+ {"failed to validate the transaction. reason: Validation revert: Account validation error: Not enough gas for transaction validation", true, "zkSync"},
+ {"failed to validate the transaction. reason: Validation revert: Failed to pay for the transaction: Failed to pay the fee to the operator", true, "zkSync"},
+ {"failed to validate the transaction. reason: Validation revert: Account validation error: Error function_selector = 0x, data = 0x", true, "zkSync"},
+ {"invalid sender. can't start a transaction from a non-account", true, "zkSync"},
+ {"Failed to serialize transaction: max fee per gas higher than 2^64-1", true, "zkSync"},
+ {"Failed to serialize transaction: max fee per pubdata byte higher than 2^64-1", true, "zkSync"},
+ {"Failed to serialize transaction: max priority fee per gas higher than 2^64-1", true, "zkSync"},
+ {"Failed to serialize transaction: oversized data. max: 1000000; actual: 1000000", true, "zkSync"},
}
for _, test := range tests {
diff --git a/core/chains/evm/config/toml/defaults/zkSync_Goerli.toml b/core/chains/evm/config/toml/defaults/zkSync_Goerli.toml
new file mode 100644
index 00000000000..04529a41b81
--- /dev/null
+++ b/core/chains/evm/config/toml/defaults/zkSync_Goerli.toml
@@ -0,0 +1,14 @@
+ChainID = '280'
+ChainType = 'zksync'
+FinalityDepth = 1
+LogPollInterval = '5s'
+MinIncomingConfirmations = 1
+NoNewHeadsThreshold = '1m'
+
+[GasEstimator]
+LimitDefault = 3_500_000
+PriceMax = 18446744073709551615
+PriceMin = 0
+
+[HeadTracker]
+HistoryDepth = 5
diff --git a/core/chains/evm/config/toml/defaults/zkSync_Mainnet.toml b/core/chains/evm/config/toml/defaults/zkSync_Mainnet.toml
new file mode 100644
index 00000000000..d7808edd15f
--- /dev/null
+++ b/core/chains/evm/config/toml/defaults/zkSync_Mainnet.toml
@@ -0,0 +1,14 @@
+ChainID = '324'
+ChainType = 'zksync'
+FinalityDepth = 1
+LogPollInterval = '5s'
+MinIncomingConfirmations = 1
+NoNewHeadsThreshold = '1m'
+
+[GasEstimator]
+LimitDefault = 3_500_000
+PriceMax = 18446744073709551615
+PriceMin = 0
+
+[HeadTracker]
+HistoryDepth = 5
diff --git a/core/chains/evm/gas/block_history_estimator_test.go b/core/chains/evm/gas/block_history_estimator_test.go
index decb68dbe99..c8b193c4435 100644
--- a/core/chains/evm/gas/block_history_estimator_test.go
+++ b/core/chains/evm/gas/block_history_estimator_test.go
@@ -23,6 +23,7 @@ import (
evmclient "github.com/smartcontractkit/chainlink/v2/core/chains/evm/client"
"github.com/smartcontractkit/chainlink/v2/core/chains/evm/gas"
evmtypes "github.com/smartcontractkit/chainlink/v2/core/chains/evm/types"
+ "github.com/smartcontractkit/chainlink/v2/core/config"
"github.com/smartcontractkit/chainlink/v2/core/internal/cltest"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils"
"github.com/smartcontractkit/chainlink/v2/core/internal/testutils/evmtest"
@@ -1348,6 +1349,21 @@ func TestBlockHistoryEstimator_IsUsable(t *testing.T) {
assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t)))
assert.Equal(t, true, bhe.IsUsable(tx2, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t)))
})
+
+ t.Run("returns false if transaction is of type 0x71 or 0xff only on zkSync", func(t *testing.T) {
+ cfg.ChainTypeF = string(config.ChainZkSync)
+ tx := evmtypes.Transaction{Type: 0x71, GasPrice: assets.NewWeiI(10), GasLimit: 42, Hash: utils.NewHash()}
+ assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t)))
+
+ tx.Type = 0x02
+ assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t)))
+
+ tx.Type = 0xff
+ assert.Equal(t, false, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t)))
+
+ cfg.ChainTypeF = ""
+ assert.Equal(t, true, bhe.IsUsable(tx, block, cfg.ChainType(), geCfg.PriceMin(), logger.TestLogger(t)))
+ })
}
func TestBlockHistoryEstimator_EffectiveTipCap(t *testing.T) {
diff --git a/core/chains/evm/gas/chain_specific.go b/core/chains/evm/gas/chain_specific.go
index 5c0b256bd3f..4f0d2e6b2f8 100644
--- a/core/chains/evm/gas/chain_specific.go
+++ b/core/chains/evm/gas/chain_specific.go
@@ -49,5 +49,12 @@ func chainSpecificIsUsable(tx evmtypes.Transaction, baseFee *assets.Wei, chainTy
return false
}
}
+ if chainType == config.ChainZkSync {
+ // zKSync specific type for contract deployment & priority transactions
+ // https://era.zksync.io/docs/reference/concepts/transactions.html#eip-712-0x71
+ if tx.Type == 0x71 || tx.Type == 0xff {
+ return false
+ }
+ }
return true
}
diff --git a/core/config/chaintype.go b/core/config/chaintype.go
index 0110f247b73..21fb8cd297d 100644
--- a/core/config/chaintype.go
+++ b/core/config/chaintype.go
@@ -17,16 +17,17 @@ const (
ChainCelo ChainType = "celo"
ChainWeMix ChainType = "wemix"
ChainKroma ChainType = "kroma"
+ ChainZkSync ChainType = "zksync"
)
var ErrInvalidChainType = fmt.Errorf("must be one of %s or omitted", strings.Join([]string{
string(ChainArbitrum), string(ChainMetis), string(ChainXDai), string(ChainOptimismBedrock), string(ChainCelo),
- string(ChainKroma), string(ChainWeMix)}, ", "))
+ string(ChainKroma), string(ChainWeMix), string(ChainZkSync)}, ", "))
// IsValid returns true if the ChainType value is known or empty.
func (c ChainType) IsValid() bool {
switch c {
- case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo, ChainKroma, ChainWeMix:
+ case "", ChainArbitrum, ChainMetis, ChainOptimismBedrock, ChainXDai, ChainCelo, ChainKroma, ChainWeMix, ChainZkSync:
return true
}
return false
diff --git a/core/config/docs/chains-evm.toml b/core/config/docs/chains-evm.toml
index c0cdfc7a310..381ab794d60 100644
--- a/core/config/docs/chains-evm.toml
+++ b/core/config/docs/chains-evm.toml
@@ -14,7 +14,7 @@ BlockBackfillDepth = 10 # Default
# BlockBackfillSkip enables skipping of very long backfills.
BlockBackfillSkip = false # Default
# ChainType is automatically detected from chain ID. Set this to force a certain chain type regardless of chain ID.
-# Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix
+# Available types: arbitrum, metis, optimismBedrock, xdai, celo, kroma, wemix, zksync
ChainType = 'arbitrum' # Example
# FinalityDepth is the number of blocks after which an ethereum transaction is considered "final". Note that the default is automatically set based on chain ID so it should not be necessary to change this under normal operation.
# BlocksConsideredFinal determines how deeply we look back to ensure that transactions are confirmed onto the longest chain
diff --git a/core/scripts/common/helpers.go b/core/scripts/common/helpers.go
index d03dcec097f..c141e8a29c4 100644
--- a/core/scripts/common/helpers.go
+++ b/core/scripts/common/helpers.go
@@ -219,6 +219,11 @@ func explorerLinkPrefix(chainID int64) (prefix string) {
case 8453:
prefix = "https://basescan.org"
+ case 280: // zkSync Goerli testnet
+ prefix = "https://goerli.explorer.zksync.io"
+ case 324: // zkSync mainnet
+ prefix = "https://explorer.zksync.io"
+
default: // Unknown chain, return prefix as-is
prefix = ""
}
diff --git a/core/services/chainlink/config_test.go b/core/services/chainlink/config_test.go
index cc3fda167de..34fcc4bbe91 100644
--- a/core/services/chainlink/config_test.go
+++ b/core/services/chainlink/config_test.go
@@ -1190,7 +1190,7 @@ func TestConfig_Validate(t *testing.T) {
- 1: 6 errors:
- ChainType: invalid value (Foo): must not be set with this chain id
- Nodes: missing: must have at least one node
- - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix or omitted
+ - ChainType: invalid value (Foo): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix, zksync or omitted
- HeadTracker.HistoryDepth: invalid value (30): must be equal to or greater than FinalityDepth
- GasEstimator: 2 errors:
- FeeCapDefault: invalid value (101 wei): must be equal to PriceMax (99 wei) since you are using FixedPrice estimation with gas bumping disabled in EIP1559 mode - PriceMax will be used as the FeeCap for transactions instead of FeeCapDefault
@@ -1199,7 +1199,7 @@ func TestConfig_Validate(t *testing.T) {
- 2: 5 errors:
- ChainType: invalid value (Arbitrum): only "optimismBedrock" can be used with this chain id
- Nodes: missing: must have at least one node
- - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix or omitted
+ - ChainType: invalid value (Arbitrum): must be one of arbitrum, metis, xdai, optimismBedrock, celo, kroma, wemix, zksync or omitted
- FinalityDepth: invalid value (0): must be greater than or equal to 1
- MinIncomingConfirmations: invalid value (0): must be greater than or equal to 1
- 3.Nodes: 5 errors:
diff --git a/core/services/ocr/contract_tracker.go b/core/services/ocr/contract_tracker.go
index 2308fa3035d..f49e556d4e8 100644
--- a/core/services/ocr/contract_tracker.go
+++ b/core/services/ocr/contract_tracker.go
@@ -401,7 +401,7 @@ func (t *OCRContractTracker) LatestBlockHeight(ctx context.Context) (blockheight
// care about the block height; we have no way of getting the L1 block
// height anyway
return 0, nil
- case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai, config.ChainKroma, config.ChainWeMix:
+ case "", config.ChainArbitrum, config.ChainCelo, config.ChainOptimismBedrock, config.ChainXDai, config.ChainKroma, config.ChainWeMix, config.ChainZkSync:
// continue
}
latestBlockHeight := t.getLatestBlockHeight()
diff --git a/docs/CONFIG.md b/docs/CONFIG.md
index fd8822c1626..1eb9cd5023d 100644
--- a/docs/CONFIG.md
+++ b/docs/CONFIG.md
@@ -2986,6 +2986,164 @@ GasLimit = 5300000
+