Skip to content

Commit c788459

Browse files
Add datastore write API for ccip (#23600)
* Add datastore write API for ccip * Address review comments
1 parent 6a48da7 commit c788459

12 files changed

Lines changed: 2286 additions & 44 deletions

File tree

deployment/ccip/changeset/cs_orchestrate_changesets.go

Lines changed: 543 additions & 14 deletions
Large diffs are not rendered by default.

deployment/ccip/changeset/merge_test.go

Lines changed: 536 additions & 0 deletions
Large diffs are not rendered by default.

deployment/ccip/changeset/solana_v0_1_0/cs_e2e.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
1212
cldfproposalutils "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalutils"
1313

14+
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
1415
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_5_1"
1516
"github.com/smartcontractkit/chainlink/deployment/ccip/shared"
1617
"github.com/smartcontractkit/chainlink/deployment/ccip/shared/stateview"
@@ -98,7 +99,7 @@ func ProcessConfig[T any](
9899
if err != nil {
99100
return err
100101
}
101-
err = cldf.MergeChangesetOutput(*e, finalOutput, output)
102+
err = changeset.MergeChangesetOutput(*e, finalOutput, output)
102103
if err != nil {
103104
return fmt.Errorf("failed to merge changeset output: %w", err)
104105
}
@@ -321,42 +322,42 @@ func E2ETokenPoolv2(env cldf.Environment, cfg E2ETokenPoolConfigv2) (cldf.Change
321322
if err != nil {
322323
return cldf.ChangesetOutput{}, fmt.Errorf("failed to add token pool and lookup table: %w", err)
323324
}
324-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
325+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
325326
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running AddTokenPoolAndLookupTable: %w", err)
326327
}
327328
output, err = SetupTokenPoolForRemoteChain(e, remotePoolConfig)
328329
if err != nil {
329330
return cldf.ChangesetOutput{}, fmt.Errorf("failed to setup token pool for remote chain: %w", err)
330331
}
331-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
332+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
332333
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running SetupTokenPoolForRemoteChain: %w", err)
333334
}
334335
output, err = RegisterTokenAdminRegistry(e, registerTokenAdminRegistryCfg)
335336
if err != nil {
336337
return cldf.ChangesetOutput{}, fmt.Errorf("failed to register token admin registry: %w", err)
337338
}
338-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
339+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
339340
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running RegisterTokenAdminRegistry: %w", err)
340341
}
341342
output, err = AcceptAdminRoleTokenAdminRegistry(e, acceptAdminRoleTokenAdminRegistryCfg)
342343
if err != nil {
343344
return cldf.ChangesetOutput{}, fmt.Errorf("failed to accept admin role: %w", err)
344345
}
345-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
346+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
346347
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running AcceptAdminRoleTokenAdminRegistry: %w", err)
347348
}
348349
output, err = SetPool(e, setPoolCfg)
349350
if err != nil {
350351
return cldf.ChangesetOutput{}, fmt.Errorf("failed to set pool: %w", err)
351352
}
352-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
353+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
353354
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running SetPool: %w", err)
354355
}
355356
output, err = v1_5_1.ConfigureMultiplePoolLogic(e, evmToSolanaRemotePoolCfg)
356357
if err != nil {
357358
return cldf.ChangesetOutput{}, fmt.Errorf("failed to configure token pool contracts: %w", err)
358359
}
359-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
360+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
360361
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running ConfigureTokenPoolContractsChangeset: %w", err)
361362
}
362363
// and finally lets transfer away the pool to timelock
@@ -365,7 +366,7 @@ func E2ETokenPoolv2(env cldf.Environment, cfg E2ETokenPoolConfigv2) (cldf.Change
365366
if err != nil {
366367
return cldf.ChangesetOutput{}, fmt.Errorf("failed to transfer ccip to mcms with timelock: %w", err)
367368
}
368-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
369+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
369370
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running TransferCCIPToMCMSWithTimelockSolana: %w", err)
370371
}
371372
}
@@ -390,11 +391,20 @@ func E2ETokenPoolv2(env cldf.Environment, cfg E2ETokenPoolConfigv2) (cldf.Change
390391
}
391392
}
392393

393-
ds, err := shared.PopulateDataStore(finalCSOut.AddressBook) //nolint:staticcheck //SA1019 ignoring deprecated
394+
// Preserve the datastore accumulated by MergeChangesetOutput: it already carries the
395+
// sub-changesets' semantic qualifiers, metadata, and deletions. Rebuilding it from the
396+
// address book would discard all of that. The address book has no qualifiers, so it can only
397+
// contribute the singleton refs the sub-changesets did not write to the datastore; merge
398+
// those in rather than replacing the accumulated store.
399+
singletons, err := shared.PopulateDataStore(finalCSOut.AddressBook) //nolint:staticcheck //SA1019 ignoring deprecated
394400
if err != nil {
395401
return cldf.ChangesetOutput{}, fmt.Errorf("failed to populate in-memory DataStore: %w", err)
396402
}
397403

398-
finalCSOut.DataStore = ds
404+
if finalCSOut.DataStore == nil {
405+
finalCSOut.DataStore = singletons
406+
} else if err := finalCSOut.DataStore.Merge(singletons.Seal()); err != nil {
407+
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge address-book refs into DataStore: %w", err)
408+
}
399409
return *finalCSOut, nil
400410
}

deployment/ccip/changeset/solana_v0_1_1/cs_e2e.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
1313
cldfproposalutils "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalutils"
1414

15+
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
1516
"github.com/smartcontractkit/chainlink/deployment/ccip/changeset/v1_5_1"
1617
"github.com/smartcontractkit/chainlink/deployment/ccip/shared"
1718
"github.com/smartcontractkit/chainlink/deployment/ccip/shared/stateview"
@@ -104,7 +105,7 @@ func ProcessConfig[T any](
104105
if err != nil {
105106
return err
106107
}
107-
err = cldf.MergeChangesetOutput(*e, finalOutput, output)
108+
err = changeset.MergeChangesetOutput(*e, finalOutput, output)
108109
if err != nil {
109110
return fmt.Errorf("failed to merge changeset output: %w", err)
110111
}
@@ -342,50 +343,50 @@ func E2ETokenPoolv2(env cldf.Environment, cfg E2ETokenPoolConfigv2) (cldf.Change
342343
if err != nil {
343344
return cldf.ChangesetOutput{}, fmt.Errorf("failed to initialize global config for token pool: %w", err)
344345
}
345-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
346+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
346347
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running InitGlobalConfigTokenPoolProgram: %w", err)
347348
}
348349
}
349350
output, err := AddTokenPoolAndLookupTable(e, tokenPoolAndLookupTableCfg)
350351
if err != nil {
351352
return cldf.ChangesetOutput{}, fmt.Errorf("failed to add token pool and lookup table: %w", err)
352353
}
353-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
354+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
354355
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running AddTokenPoolAndLookupTable: %w", err)
355356
}
356357
output, err = SetupTokenPoolForRemoteChain(e, remotePoolConfig)
357358
if err != nil {
358359
return cldf.ChangesetOutput{}, fmt.Errorf("failed to setup token pool for remote chain: %w", err)
359360
}
360-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
361+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
361362
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running SetupTokenPoolForRemoteChain: %w", err)
362363
}
363364
output, err = RegisterTokenAdminRegistry(e, registerTokenAdminRegistryCfg)
364365
if err != nil {
365366
return cldf.ChangesetOutput{}, fmt.Errorf("failed to register token admin registry: %w", err)
366367
}
367-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
368+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
368369
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running RegisterTokenAdminRegistry: %w", err)
369370
}
370371
output, err = AcceptAdminRoleTokenAdminRegistry(e, acceptAdminRoleTokenAdminRegistryCfg)
371372
if err != nil {
372373
return cldf.ChangesetOutput{}, fmt.Errorf("failed to accept admin role: %w", err)
373374
}
374-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
375+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
375376
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running AcceptAdminRoleTokenAdminRegistry: %w", err)
376377
}
377378
output, err = SetPool(e, setPoolCfg)
378379
if err != nil {
379380
return cldf.ChangesetOutput{}, fmt.Errorf("failed to set pool: %w", err)
380381
}
381-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
382+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
382383
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running SetPool: %w", err)
383384
}
384385
output, err = v1_5_1.ConfigureMultiplePoolLogic(e, evmToSolanaRemotePoolCfg)
385386
if err != nil {
386387
return cldf.ChangesetOutput{}, fmt.Errorf("failed to configure token pool contracts: %w", err)
387388
}
388-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
389+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
389390
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running ConfigureTokenPoolContractsChangeset: %w", err)
390391
}
391392
// and finally lets transfer away the pool to timelock
@@ -394,7 +395,7 @@ func E2ETokenPoolv2(env cldf.Environment, cfg E2ETokenPoolConfigv2) (cldf.Change
394395
if err != nil {
395396
return cldf.ChangesetOutput{}, fmt.Errorf("failed to transfer ccip to mcms with timelock: %w", err)
396397
}
397-
if err = cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
398+
if err = changeset.MergeChangesetOutput(e, finalCSOut, output); err != nil {
398399
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after running TransferCCIPToMCMSWithTimelockSolana: %w", err)
399400
}
400401
}
@@ -419,11 +420,20 @@ func E2ETokenPoolv2(env cldf.Environment, cfg E2ETokenPoolConfigv2) (cldf.Change
419420
}
420421
}
421422

422-
ds, err := shared.PopulateDataStore(finalCSOut.AddressBook) //nolint:staticcheck //SA1019 ignoring deprecated
423+
// Preserve the datastore accumulated by MergeChangesetOutput: it already carries the
424+
// sub-changesets' semantic qualifiers, metadata, and deletions. Rebuilding it from the
425+
// address book would discard all of that. The address book has no qualifiers, so it can only
426+
// contribute the singleton refs the sub-changesets did not write to the datastore; merge
427+
// those in rather than replacing the accumulated store.
428+
singletons, err := shared.PopulateDataStore(finalCSOut.AddressBook) //nolint:staticcheck //SA1019 ignoring deprecated
423429
if err != nil {
424430
return cldf.ChangesetOutput{}, fmt.Errorf("failed to populate in-memory DataStore: %w", err)
425431
}
426432

427-
finalCSOut.DataStore = ds
433+
if finalCSOut.DataStore == nil {
434+
finalCSOut.DataStore = singletons
435+
} else if err := finalCSOut.DataStore.Merge(singletons.Seal()); err != nil {
436+
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge address-book refs into DataStore: %w", err)
437+
}
428438
return *finalCSOut, nil
429439
}

deployment/ccip/changeset/v1_5_1/cs_add_token_e2e.go

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/smartcontractkit/chainlink-deployments-framework/operations"
2828

2929
"github.com/smartcontractkit/chainlink/deployment"
30+
ccipcommoncs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
3031
"github.com/smartcontractkit/chainlink/deployment/ccip/internal/opsutils"
3132
ccipops "github.com/smartcontractkit/chainlink/deployment/ccip/operation/evm"
3233
"github.com/smartcontractkit/chainlink/deployment/ccip/shared"
@@ -332,7 +333,7 @@ func addTokenE2ELogic(env cldf.Environment, config AddTokensE2EConfig) (cldf.Cha
332333
if err != nil {
333334
return cldf.ChangesetOutput{}, fmt.Errorf("failed to deploy token pool for token %s: %w", token, err)
334335
}
335-
if err := cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
336+
if err := ccipcommoncs.MergeChangesetOutput(e, finalCSOut, output); err != nil {
336337
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge address book for token %s: %w", token, err)
337338
}
338339
newAddresses, err := output.AddressBook.Addresses() //nolint:staticcheck // Addressbook is deprecated, but we still use it for the time being
@@ -366,7 +367,7 @@ func addTokenE2ELogic(env cldf.Environment, config AddTokensE2EConfig) (cldf.Cha
366367
return cldf.ChangesetOutput{}, fmt.Errorf("failed to run TransferToMCMSWithTimelock on chain with selector %d: %w", chainID, err)
367368
}
368369

369-
if err := cldf.MergeChangesetOutput(e, finalCSOut, transferOwnershipProposalOutput); err != nil {
370+
if err := ccipcommoncs.MergeChangesetOutput(e, finalCSOut, transferOwnershipProposalOutput); err != nil {
370371
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after transferring ownership of pool(s): %w", err)
371372
}
372373
}
@@ -382,7 +383,7 @@ func addTokenE2ELogic(env cldf.Environment, config AddTokensE2EConfig) (cldf.Cha
382383
if err != nil {
383384
return cldf.ChangesetOutput{}, fmt.Errorf("failed to propose admin role for token %s: %w", token, err)
384385
}
385-
if err := cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
386+
if err := ccipcommoncs.MergeChangesetOutput(e, finalCSOut, output); err != nil {
386387
return cldf.ChangesetOutput{}, fmt.Errorf("failed to changeset output after configuring token admin reg for token %s: %w",
387388
token, err)
388389
}
@@ -414,15 +415,15 @@ func addTokenE2ELogic(env cldf.Environment, config AddTokensE2EConfig) (cldf.Cha
414415
if err != nil {
415416
return cldf.ChangesetOutput{}, fmt.Errorf("failed to accept admin role for token %s: %w", token, err)
416417
}
417-
if err := cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
418+
if err := ccipcommoncs.MergeChangesetOutput(e, finalCSOut, output); err != nil {
418419
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge address book for token %s: %w", token, err)
419420
}
420421
e.Logger.Infow("accepted admin role", "token", token, "config", updatedConfigureTokenAdminReg)
421422
output, err = SetPoolChangeset(e, updatedConfigureTokenAdminReg)
422423
if err != nil {
423424
return cldf.ChangesetOutput{}, fmt.Errorf("failed to set pool for token %s: %w", token, err)
424425
}
425-
if err := cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
426+
if err := ccipcommoncs.MergeChangesetOutput(e, finalCSOut, output); err != nil {
426427
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge address book for token %s: %w", token, err)
427428
}
428429
e.Logger.Infow("set pool", "token", token, "config", updatedConfigureTokenAdminReg)
@@ -444,7 +445,7 @@ func addTokenE2ELogic(env cldf.Environment, config AddTokensE2EConfig) (cldf.Cha
444445
if err != nil {
445446
return cldf.ChangesetOutput{}, fmt.Errorf("failed to configure token pool for token %s: %w", token, err)
446447
}
447-
if err := cldf.MergeChangesetOutput(e, finalCSOut, output); err != nil {
448+
if err := ccipcommoncs.MergeChangesetOutput(e, finalCSOut, output); err != nil {
448449
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output after configuring token pool for token %s: %w", token, err)
449450
}
450451
e.Logger.Infow("configured token pool", "token", token)
@@ -465,12 +466,21 @@ func addTokenE2ELogic(env cldf.Environment, config AddTokensE2EConfig) (cldf.Cha
465466
finalCSOut.MCMSTimelockProposals = []mcms.TimelockProposal{*aggregatedProposals}
466467
}
467468

468-
ds, err := shared.PopulateDataStore(finalCSOut.AddressBook) //nolint:staticcheck //SA1019 ignoring deprecated
469+
// Preserve the datastore accumulated by MergeChangesetOutput: it already carries the
470+
// sub-changesets' semantic qualifiers, metadata, and deletions. Rebuilding it from the
471+
// address book would discard all of that. The address book has no qualifiers, so it can only
472+
// contribute the singleton refs the sub-changesets did not write to the datastore; merge
473+
// those in rather than replacing the accumulated store.
474+
singletons, err := shared.PopulateDataStore(finalCSOut.AddressBook) //nolint:staticcheck //SA1019 ignoring deprecated
469475
if err != nil {
470476
return cldf.ChangesetOutput{}, fmt.Errorf("failed to populate in-memory DataStore: %w", err)
471477
}
472478

473-
finalCSOut.DataStore = ds
479+
if finalCSOut.DataStore == nil {
480+
finalCSOut.DataStore = singletons
481+
} else if err := finalCSOut.DataStore.Merge(singletons.Seal()); err != nil {
482+
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge address-book refs into DataStore: %w", err)
483+
}
474484
return *finalCSOut, nil
475485
}
476486

deployment/ccip/changeset/v1_5_1/cs_configure_token_pools.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
2323
cldfproposalutils "github.com/smartcontractkit/chainlink-deployments-framework/engine/cld/mcms/proposalutils"
2424

25+
ccipcommoncs "github.com/smartcontractkit/chainlink/deployment/ccip/changeset"
2526
"github.com/smartcontractkit/chainlink/deployment/ccip/shared"
2627
"github.com/smartcontractkit/chainlink/deployment/ccip/shared/deployergroup"
2728
"github.com/smartcontractkit/chainlink/deployment/ccip/shared/stateview"
@@ -1035,7 +1036,7 @@ func ConfigureMultiplePoolLogic(env cldf.Environment, c ConfigureMultipleTokenPo
10351036
if err != nil {
10361037
return cldf.ChangesetOutput{}, fmt.Errorf("failed to configure token pool: %w", err)
10371038
}
1038-
err = cldf.MergeChangesetOutput(env, &finalOutput, output)
1039+
err = ccipcommoncs.MergeChangesetOutput(env, &finalOutput, output)
10391040
if err != nil {
10401041
return cldf.ChangesetOutput{}, fmt.Errorf("failed to merge changeset output: %w", err)
10411042
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package shared
2+
3+
import (
4+
"strings"
5+
6+
chainsel "github.com/smartcontractkit/chain-selectors"
7+
)
8+
9+
// NormalizeAddress lower-cases an address only on families where case carries no meaning.
10+
//
11+
// EVM addresses are hex and are commonly written in EIP-55 checksum form, so the same contract
12+
// can appear in two casings and must compare equal. Solana and Sui addresses are case-sensitive,
13+
// where folding case would map two different contracts onto one value. An unknown selector is
14+
// left alone: preserving a distinction that does not exist is harmless, whereas erasing one that
15+
// does silently merges two contracts.
16+
func NormalizeAddress(chainSelector uint64, address string) string {
17+
family, err := chainsel.GetSelectorFamily(chainSelector)
18+
if err != nil || family != chainsel.FamilyEVM {
19+
return address
20+
}
21+
22+
return strings.ToLower(address)
23+
}
24+
25+
// AddressesEqual reports whether two addresses on the same chain refer to the same contract,
26+
// using the case rules of that chain's family. Use it instead of == whenever an address that came
27+
// from one source is compared against an address that came from another, since only one of them
28+
// may be checksummed.
29+
func AddressesEqual(chainSelector uint64, a, b string) bool {
30+
return NormalizeAddress(chainSelector, a) == NormalizeAddress(chainSelector, b)
31+
}

0 commit comments

Comments
 (0)