Skip to content

Commit 46d4c82

Browse files
committed
analyzer/evmverifier: Fix missing bytecode for verified contracts
Insert address preimages and queue verified contracts for bytecode analysis. Reprocess contracts missing preimages to backfill historical data from Sourcify.
1 parent c36222d commit 46d4c82

File tree

19 files changed

+10059
-17
lines changed

19 files changed

+10059
-17
lines changed

.changelog/1228.bugfix.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
analyzer/evmverifier: Fix missing bytecode for verified contracts
2+
3+
Insert address preimages and queue verified contracts for bytecode analysis.
4+
Reprocess contracts missing preimages to backfill historical data from
5+
Sourcify.

.github/workflows/ci-test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ jobs:
135135

136136
test-e2e-regression:
137137
env:
138-
E2E_REGRESSION_ARTIFACTS_VERSION: 2025-10-21
138+
E2E_REGRESSION_ARTIFACTS_VERSION: 2025-12-23
139139
strategy:
140140
matrix:
141141
suite:

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ E2E_REGRESSION_SUITES_NO_LINKS := eden_testnet_2025 eden_2025 eden damask
6565
# make E2E_REGRESSION_SUITES='suite1 suite2' test-e2e-regression
6666
E2E_REGRESSION_SUITES := $(E2E_REGRESSION_SUITES_NO_LINKS) edenfast
6767

68-
E2E_REGRESSION_ARTIFACTS_VERSION = 2025-10-21
68+
E2E_REGRESSION_ARTIFACTS_VERSION = 2025-12-23
6969

7070
upload-e2e-regression-caches:
7171
for suite in $(E2E_REGRESSION_SUITES); do \

analyzer/evmverifier/evmverifier.go

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,21 +121,54 @@ func (p *processor) getNexusVerifiedContracts(ctx context.Context) (map[oasisAdd
121121
return nexusVerifiedContracts, nil
122122
}
123123

124+
// getContractsMissingPreimage returns the set of verified contract addresses
125+
// that are missing their address preimage (ETH address mapping).
126+
func (p *processor) getContractsMissingPreimage(ctx context.Context) (map[oasisAddress]struct{}, error) {
127+
rows, err := p.target.Query(ctx, queries.RuntimeEVMVerifiedContractsMissingPreimage, p.runtime)
128+
if err != nil {
129+
return nil, fmt.Errorf("querying contracts missing preimage: %w", err)
130+
}
131+
defer rows.Close()
132+
133+
missing := map[oasisAddress]struct{}{}
134+
for rows.Next() {
135+
var addr oasisAddress
136+
if err = rows.Scan(&addr); err != nil {
137+
return nil, fmt.Errorf("scanning contract missing preimage: %w", err)
138+
}
139+
missing[addr] = struct{}{}
140+
}
141+
return missing, nil
142+
}
143+
124144
func (p *processor) GetItems(ctx context.Context, limit uint64) ([]contract, error) {
125145
// Load all nexus-verified contracts from the DB.
126146
nexusLevels, err := p.getNexusVerifiedContracts(ctx)
127147
if err != nil {
128148
return nil, fmt.Errorf("failed to get nexus verified contracts: %w", err)
129149
}
130150

151+
// Load contracts that are missing their address preimage.
152+
// These need to be reprocessed to insert the preimage.
153+
missingPreimage, err := p.getContractsMissingPreimage(ctx)
154+
if err != nil {
155+
return nil, fmt.Errorf("failed to get contracts missing preimage: %w", err)
156+
}
157+
if len(missingPreimage) > 0 {
158+
p.logger.Info("found verified contracts missing preimage", "count", len(missingPreimage))
159+
}
160+
131161
// Query Sourcify for list of all verified contracts.
132162
sourcifyLevels, err := p.source.GetVerifiedContractAddresses(ctx, p.runtime)
133163
if err != nil {
134164
return nil, fmt.Errorf("failed to get verified contract addresses: %w", err)
135165
}
136166
p.logger.Debug("got verified contract addresses", "addresses", sourcifyLevels)
137167

138-
// Find contracts that are verified in Sourcify and not yet verified in Nexus.
168+
// Find contracts that need processing:
169+
// 1. Verified in Sourcify but not yet verified in Nexus
170+
// 2. Upgrading from partial to full verification
171+
// 3. Missing their address preimage (need to reprocess to insert it)
139172
var items []contract
140173
for ethAddr, sourcifyLevel := range sourcifyLevels {
141174
oasisAddr, err := addresses.FromEthAddress(ethAddr.Bytes())
@@ -145,7 +178,13 @@ func (p *processor) GetItems(ctx context.Context, limit uint64) ([]contract, err
145178
}
146179

147180
nexusLevel, isKnownToNexus := nexusLevels[oasisAddress(oasisAddr)]
148-
if !isKnownToNexus || (nexusLevel == sourcify.VerificationLevelPartial && sourcifyLevel == sourcify.VerificationLevelFull) {
181+
_, isMissingPreimage := missingPreimage[oasisAddress(oasisAddr)]
182+
183+
needsProcessing := !isKnownToNexus ||
184+
(nexusLevel == sourcify.VerificationLevelPartial && sourcifyLevel == sourcify.VerificationLevelFull) ||
185+
isMissingPreimage
186+
187+
if needsProcessing {
149188
items = append(items, contract{
150189
Addr: oasisAddress(oasisAddr),
151190
EthAddr: ethAddr,
@@ -209,6 +248,29 @@ func (p *processor) ProcessItem(ctx context.Context, batch *storage.QueryBatch,
209248
item.VerificationLevel,
210249
)
211250

251+
// Insert the address preimage (ETH address -> Oasis address mapping).
252+
// This is needed for the evm_contract_code analyzer to fetch bytecode.
253+
// For contracts that were never seen by the block analyzer (e.g., created
254+
// via encrypted transactions on Sapphire), this is the only way the
255+
// preimage gets inserted.
256+
batch.Queue(
257+
queries.AddressPreimageInsert,
258+
item.Addr,
259+
"oasis-runtime-sdk/address: secp256k1eth",
260+
0, // context_version
261+
item.EthAddr.Bytes(),
262+
)
263+
264+
// Queue the contract for bytecode analysis. This ensures contracts discovered
265+
// via Sourcify verification get their bytecode fetched, even if the block analyzer
266+
// never saw the contract (e.g., contracts only called internally by other contracts,
267+
// or contracts created via encrypted transactions on Sapphire).
268+
batch.Queue(
269+
queries.RuntimeEVMContractCodeAnalysisInsert,
270+
p.runtime,
271+
item.Addr,
272+
)
273+
212274
return nil
213275
}
214276

analyzer/queries/queries.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -847,18 +847,25 @@ var (
847847
WHERE runtime = $1 AND contract_candidate = $2`
848848

849849
RuntimeEVMContractCodeAnalysisStale = `
850+
WITH download_round AS (
851+
SELECT MAX(height) AS height
852+
FROM analysis.processed_blocks
853+
WHERE analyzer = $1::runtime::text AND processed_time IS NOT NULL
854+
)
850855
SELECT
851856
code_analysis.contract_candidate,
852857
pre.address_data AS eth_contract_candidate,
853-
(SELECT MAX(height) FROM analysis.processed_blocks WHERE analyzer = $1::runtime::text AND processed_time IS NOT NULL) AS download_round
858+
download_round.height AS download_round
854859
FROM analysis.evm_contract_code AS code_analysis
855860
JOIN chain.address_preimages AS pre ON
856861
pre.address = code_analysis.contract_candidate AND
857862
pre.context_identifier = 'oasis-runtime-sdk/address: secp256k1eth' AND
858863
pre.context_version = 0
864+
CROSS JOIN download_round
859865
WHERE
860866
code_analysis.runtime = $1::runtime AND
861-
code_analysis.is_contract IS NULL
867+
code_analysis.is_contract IS NULL AND
868+
download_round.height IS NOT NULL
862869
LIMIT $2`
863870

864871
RuntimeEVMContractCodeAnalysisStaleCount = `
@@ -1187,6 +1194,20 @@ var (
11871194
WHERE
11881195
runtime = $1 AND verification_level IS NOT NULL`
11891196

1197+
// RuntimeEVMVerifiedContractsMissingPreimage returns verified contracts that
1198+
// are missing their address preimage (ETH address mapping). These need to be
1199+
// reprocessed by the verifier to insert the preimage.
1200+
RuntimeEVMVerifiedContractsMissingPreimage = `
1201+
SELECT contracts.contract_address
1202+
FROM chain.evm_contracts AS contracts
1203+
LEFT JOIN chain.address_preimages AS preimages
1204+
ON contracts.contract_address = preimages.address
1205+
AND preimages.context_identifier = 'oasis-runtime-sdk/address: secp256k1eth'
1206+
WHERE
1207+
contracts.runtime = $1
1208+
AND contracts.verification_level IS NOT NULL
1209+
AND preimages.address IS NULL`
1210+
11901211
RuntimeEVMVerifyContractUpsert = `
11911212
INSERT INTO chain.evm_contracts (runtime, contract_address, verification_info_downloaded_at, abi, compilation_metadata, source_files, verification_level)
11921213
VALUES ($1, $2, CURRENT_TIMESTAMP, $3, $4, $5, $6)
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
-- Backfill verified contracts into the bytecode analysis queue.
2+
--
3+
-- Some verified contracts were never queued for bytecode download because:
4+
-- 1. They were only called internally by other contracts (not as direct tx recipients)
5+
-- 2. They were created via encrypted transactions on Sapphire
6+
-- 3. They were processed before commit 83c02272 which added detection of
7+
-- contract candidates via log-emitting addresses
8+
--
9+
-- The Sourcify verifier now:
10+
-- - Inserts address preimages for verified contracts
11+
-- - Queues contracts for bytecode analysis
12+
-- - Reprocesses contracts that are missing preimages
13+
--
14+
-- This migration queues any verified contracts (that have preimages) for
15+
-- bytecode analysis if they're not already in the queue.
16+
--
17+
-- Reference: https://github.com/oasisprotocol/nexus/issues/1227
18+
19+
INSERT INTO analysis.evm_contract_code (runtime, contract_candidate)
20+
SELECT
21+
c.runtime,
22+
c.contract_address
23+
FROM chain.evm_contracts c
24+
-- Only include contracts with preimages (required for bytecode fetching)
25+
JOIN chain.address_preimages p
26+
ON c.contract_address = p.address
27+
AND p.context_identifier = 'oasis-runtime-sdk/address: secp256k1eth'
28+
-- Exclude contracts already in the analysis queue
29+
LEFT JOIN analysis.evm_contract_code a
30+
ON a.runtime = c.runtime
31+
AND a.contract_candidate = c.contract_address
32+
WHERE
33+
-- Only verified contracts
34+
c.verification_level IS NOT NULL
35+
-- Missing bytecode
36+
AND c.runtime_bytecode IS NULL
37+
-- Not already in queue
38+
AND a.contract_candidate IS NULL
39+
ON CONFLICT (runtime, contract_candidate) DO NOTHING;

tests/e2e_regression/damask/e2e_config_1.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ analysis:
1212
nodes:
1313
# Node will be accessed only when the filling up the cache.
1414
# In CI, the cache is expected to have all the node responses as the node is not reachable.
15-
damask: { default: { rpc: unix:/tmp/node.sock } }
15+
damask: { default: { rpc: internal-mainnet-20220411-20231129:443 } } #{ rpc: unix:/tmp/node.sock } }
1616
ipfs:
1717
gateway: https://ipfs.io
1818
fast_startup: true

tests/e2e_regression/damask/e2e_config_2.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ analysis:
55
cache: { cache_dir: tests/e2e_regression/damask/rpc-cache }
66
chain_name: mainnet
77
nodes:
8-
damask: { default: { rpc: unix:/tmp/node.sock } }
8+
damask: { default: { rpc: internal-mainnet-20220411-20231129:443 } } #{ rpc: unix:/tmp/node.sock } }
99
ipfs:
1010
gateway: https://ipfs.io
1111
fast_startup: true
@@ -17,7 +17,7 @@ analysis:
1717
evm_tokens_emerald: { stop_if_queue_empty_for: 1s }
1818
evm_nfts_emerald: { stop_if_queue_empty_for: 1s }
1919
evm_token_balances_emerald: { stop_if_queue_empty_for: 1s }
20-
evm_contract_code_emerald: { stop_if_queue_empty_for: 1s }
20+
# evm_contract_code_emerald: { stop_if_queue_empty_for: 1s } # Disabled: emerald block analyzer is disabled in phase 1
2121
evm_abi_emerald: { stop_if_queue_empty_for: 10s } # Give evm_contract_verifier time to fetch ABIs first. The 10s has been enough in practice, but might need to be tuned in the future, especially if the caching proxy has an empty cache.
2222
evm_contract_verifier_emerald: { stop_if_queue_empty_for: 1s, sourcify_server_url: http://localhost:9191 }
2323
validator_staking_history: { from: 8_048_956, stop_if_queue_empty_for: 1s, max_backoff_time: 6s }

0 commit comments

Comments
 (0)