From 8c8e37e7a8180a2ab20648cc0c13e23f852feefa Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 19 Dec 2024 11:52:38 -0500 Subject: [PATCH 01/10] Finality violation detection. (#387) --- commit/factory.go | 7 +- execute/factory.go | 7 +- .../contractreader/contract_reader_facade.go | 47 ++++ mocks/pkg/contractreader/extended.go | 265 ++++++++++++++++++ pkg/contractreader/contractreader_facade.go | 9 +- pkg/contractreader/extended.go | 124 +++++++- pkg/contractreader/extended_test.go | 11 + pkg/contractreader/extended_unit_test.go | 6 +- pkg/contractreader/observed.go | 5 + pkg/reader/ccip_test.go | 3 + 10 files changed, 472 insertions(+), 12 deletions(-) diff --git a/commit/factory.go b/commit/factory.go index f7555b37d..07be68134 100644 --- a/commit/factory.go +++ b/commit/factory.go @@ -155,14 +155,17 @@ func (p *PluginFactory) NewReportingPlugin(ctx context.Context, config ocr3types oracleIDToP2PID[commontypes.OracleID(oracleID)] = node.P2pID } - // map types to the facade. + // Map contract readers to ContractReaderFacade: + // - Extended reader adds finality violation and contract binding management. + // - Observed reader adds metric reporting. readers := make(map[cciptypes.ChainSelector]contractreader.ContractReaderFacade, len(p.contractReaders)) for chain, cr := range p.contractReaders { chainID, err1 := sel.GetChainIDFromSelector(uint64(chain)) if err1 != nil { return nil, ocr3types.ReportingPluginInfo{}, fmt.Errorf("failed to get chain id from selector: %w", err1) } - readers[chain] = contractreader.NewObserverReader(cr, lggr, chainID) + readers[chain] = contractreader.NewExtendedContractReader( + contractreader.NewObserverReader(cr, lggr, chainID)) } // Bind the RMNHome contract diff --git a/execute/factory.go b/execute/factory.go index 99eeae754..82d3f97b4 100644 --- a/execute/factory.go +++ b/execute/factory.go @@ -149,14 +149,17 @@ func (p PluginFactory) NewReportingPlugin( oracleIDToP2PID[commontypes.OracleID(oracleID)] = node.P2pID } - // map types to the facade. + // Map contract readers to ContractReaderFacade: + // - Extended reader adds finality violation and contract binding management. + // - Observed reader adds metric reporting. readers := make(map[cciptypes.ChainSelector]contractreader.ContractReaderFacade) for chain, cr := range p.contractReaders { chainID, err1 := sel.GetChainIDFromSelector(uint64(chain)) if err1 != nil { return nil, ocr3types.ReportingPluginInfo{}, fmt.Errorf("failed to get chain id from selector: %w", err1) } - readers[chain] = contractreader.NewObserverReader(cr, lggr, chainID) + readers[chain] = contractreader.NewExtendedContractReader( + contractreader.NewObserverReader(cr, lggr, chainID)) } ccipReader := readerpkg.NewCCIPChainReader( diff --git a/mocks/pkg/contractreader/contract_reader_facade.go b/mocks/pkg/contractreader/contract_reader_facade.go index d5bc97a2b..fa561b7ed 100644 --- a/mocks/pkg/contractreader/contract_reader_facade.go +++ b/mocks/pkg/contractreader/contract_reader_facade.go @@ -183,6 +183,53 @@ func (_c *MockContractReaderFacade_GetLatestValue_Call) RunAndReturn(run func(co return _c } +// HealthReport provides a mock function with given fields: +func (_m *MockContractReaderFacade) HealthReport() map[string]error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + + var r0 map[string]error + if rf, ok := ret.Get(0).(func() map[string]error); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string]error) + } + } + + return r0 +} + +// MockContractReaderFacade_HealthReport_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HealthReport' +type MockContractReaderFacade_HealthReport_Call struct { + *mock.Call +} + +// HealthReport is a helper method to define mock.On call +func (_e *MockContractReaderFacade_Expecter) HealthReport() *MockContractReaderFacade_HealthReport_Call { + return &MockContractReaderFacade_HealthReport_Call{Call: _e.mock.On("HealthReport")} +} + +func (_c *MockContractReaderFacade_HealthReport_Call) Run(run func()) *MockContractReaderFacade_HealthReport_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockContractReaderFacade_HealthReport_Call) Return(_a0 map[string]error) *MockContractReaderFacade_HealthReport_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockContractReaderFacade_HealthReport_Call) RunAndReturn(run func() map[string]error) *MockContractReaderFacade_HealthReport_Call { + _c.Call.Return(run) + return _c +} + // QueryKey provides a mock function with given fields: ctx, contract, filter, limitAndSort, sequenceDataType func (_m *MockContractReaderFacade) QueryKey(ctx context.Context, contract types.BoundContract, filter query.KeyFilter, limitAndSort query.LimitAndSort, sequenceDataType interface{}) ([]types.Sequence, error) { ret := _m.Called(ctx, contract, filter, limitAndSort, sequenceDataType) diff --git a/mocks/pkg/contractreader/extended.go b/mocks/pkg/contractreader/extended.go index f562c8cc9..2b748854c 100644 --- a/mocks/pkg/contractreader/extended.go +++ b/mocks/pkg/contractreader/extended.go @@ -28,6 +28,65 @@ func (_m *MockExtended) EXPECT() *MockExtended_Expecter { return &MockExtended_Expecter{mock: &_m.Mock} } +// BatchGetLatestValues provides a mock function with given fields: ctx, request +func (_m *MockExtended) BatchGetLatestValues(ctx context.Context, request types.BatchGetLatestValuesRequest) (types.BatchGetLatestValuesResult, error) { + ret := _m.Called(ctx, request) + + if len(ret) == 0 { + panic("no return value specified for BatchGetLatestValues") + } + + var r0 types.BatchGetLatestValuesResult + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, types.BatchGetLatestValuesRequest) (types.BatchGetLatestValuesResult, error)); ok { + return rf(ctx, request) + } + if rf, ok := ret.Get(0).(func(context.Context, types.BatchGetLatestValuesRequest) types.BatchGetLatestValuesResult); ok { + r0 = rf(ctx, request) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(types.BatchGetLatestValuesResult) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, types.BatchGetLatestValuesRequest) error); ok { + r1 = rf(ctx, request) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockExtended_BatchGetLatestValues_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BatchGetLatestValues' +type MockExtended_BatchGetLatestValues_Call struct { + *mock.Call +} + +// BatchGetLatestValues is a helper method to define mock.On call +// - ctx context.Context +// - request types.BatchGetLatestValuesRequest +func (_e *MockExtended_Expecter) BatchGetLatestValues(ctx interface{}, request interface{}) *MockExtended_BatchGetLatestValues_Call { + return &MockExtended_BatchGetLatestValues_Call{Call: _e.mock.On("BatchGetLatestValues", ctx, request)} +} + +func (_c *MockExtended_BatchGetLatestValues_Call) Run(run func(ctx context.Context, request types.BatchGetLatestValuesRequest)) *MockExtended_BatchGetLatestValues_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(types.BatchGetLatestValuesRequest)) + }) + return _c +} + +func (_c *MockExtended_BatchGetLatestValues_Call) Return(_a0 types.BatchGetLatestValuesResult, _a1 error) *MockExtended_BatchGetLatestValues_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockExtended_BatchGetLatestValues_Call) RunAndReturn(run func(context.Context, types.BatchGetLatestValuesRequest) (types.BatchGetLatestValuesResult, error)) *MockExtended_BatchGetLatestValues_Call { + _c.Call.Return(run) + return _c +} + // Bind provides a mock function with given fields: ctx, bindings func (_m *MockExtended) Bind(ctx context.Context, bindings []types.BoundContract) error { ret := _m.Called(ctx, bindings) @@ -295,6 +354,212 @@ func (_c *MockExtended_GetBindings_Call) RunAndReturn(run func(string) []contrac return _c } +// GetLatestValue provides a mock function with given fields: ctx, readIdentifier, confidenceLevel, params, returnVal +func (_m *MockExtended) GetLatestValue(ctx context.Context, readIdentifier string, confidenceLevel primitives.ConfidenceLevel, params interface{}, returnVal interface{}) error { + ret := _m.Called(ctx, readIdentifier, confidenceLevel, params, returnVal) + + if len(ret) == 0 { + panic("no return value specified for GetLatestValue") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string, primitives.ConfidenceLevel, interface{}, interface{}) error); ok { + r0 = rf(ctx, readIdentifier, confidenceLevel, params, returnVal) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockExtended_GetLatestValue_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLatestValue' +type MockExtended_GetLatestValue_Call struct { + *mock.Call +} + +// GetLatestValue is a helper method to define mock.On call +// - ctx context.Context +// - readIdentifier string +// - confidenceLevel primitives.ConfidenceLevel +// - params interface{} +// - returnVal interface{} +func (_e *MockExtended_Expecter) GetLatestValue(ctx interface{}, readIdentifier interface{}, confidenceLevel interface{}, params interface{}, returnVal interface{}) *MockExtended_GetLatestValue_Call { + return &MockExtended_GetLatestValue_Call{Call: _e.mock.On("GetLatestValue", ctx, readIdentifier, confidenceLevel, params, returnVal)} +} + +func (_c *MockExtended_GetLatestValue_Call) Run(run func(ctx context.Context, readIdentifier string, confidenceLevel primitives.ConfidenceLevel, params interface{}, returnVal interface{})) *MockExtended_GetLatestValue_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string), args[2].(primitives.ConfidenceLevel), args[3].(interface{}), args[4].(interface{})) + }) + return _c +} + +func (_c *MockExtended_GetLatestValue_Call) Return(_a0 error) *MockExtended_GetLatestValue_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockExtended_GetLatestValue_Call) RunAndReturn(run func(context.Context, string, primitives.ConfidenceLevel, interface{}, interface{}) error) *MockExtended_GetLatestValue_Call { + _c.Call.Return(run) + return _c +} + +// HealthReport provides a mock function with given fields: +func (_m *MockExtended) HealthReport() map[string]error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for HealthReport") + } + + var r0 map[string]error + if rf, ok := ret.Get(0).(func() map[string]error); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string]error) + } + } + + return r0 +} + +// MockExtended_HealthReport_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'HealthReport' +type MockExtended_HealthReport_Call struct { + *mock.Call +} + +// HealthReport is a helper method to define mock.On call +func (_e *MockExtended_Expecter) HealthReport() *MockExtended_HealthReport_Call { + return &MockExtended_HealthReport_Call{Call: _e.mock.On("HealthReport")} +} + +func (_c *MockExtended_HealthReport_Call) Run(run func()) *MockExtended_HealthReport_Call { + _c.Call.Run(func(args mock.Arguments) { + run() + }) + return _c +} + +func (_c *MockExtended_HealthReport_Call) Return(_a0 map[string]error) *MockExtended_HealthReport_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockExtended_HealthReport_Call) RunAndReturn(run func() map[string]error) *MockExtended_HealthReport_Call { + _c.Call.Return(run) + return _c +} + +// QueryKey provides a mock function with given fields: ctx, contract, filter, limitAndSort, sequenceDataType +func (_m *MockExtended) QueryKey(ctx context.Context, contract types.BoundContract, filter query.KeyFilter, limitAndSort query.LimitAndSort, sequenceDataType interface{}) ([]types.Sequence, error) { + ret := _m.Called(ctx, contract, filter, limitAndSort, sequenceDataType) + + if len(ret) == 0 { + panic("no return value specified for QueryKey") + } + + var r0 []types.Sequence + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, types.BoundContract, query.KeyFilter, query.LimitAndSort, interface{}) ([]types.Sequence, error)); ok { + return rf(ctx, contract, filter, limitAndSort, sequenceDataType) + } + if rf, ok := ret.Get(0).(func(context.Context, types.BoundContract, query.KeyFilter, query.LimitAndSort, interface{}) []types.Sequence); ok { + r0 = rf(ctx, contract, filter, limitAndSort, sequenceDataType) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]types.Sequence) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, types.BoundContract, query.KeyFilter, query.LimitAndSort, interface{}) error); ok { + r1 = rf(ctx, contract, filter, limitAndSort, sequenceDataType) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MockExtended_QueryKey_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'QueryKey' +type MockExtended_QueryKey_Call struct { + *mock.Call +} + +// QueryKey is a helper method to define mock.On call +// - ctx context.Context +// - contract types.BoundContract +// - filter query.KeyFilter +// - limitAndSort query.LimitAndSort +// - sequenceDataType interface{} +func (_e *MockExtended_Expecter) QueryKey(ctx interface{}, contract interface{}, filter interface{}, limitAndSort interface{}, sequenceDataType interface{}) *MockExtended_QueryKey_Call { + return &MockExtended_QueryKey_Call{Call: _e.mock.On("QueryKey", ctx, contract, filter, limitAndSort, sequenceDataType)} +} + +func (_c *MockExtended_QueryKey_Call) Run(run func(ctx context.Context, contract types.BoundContract, filter query.KeyFilter, limitAndSort query.LimitAndSort, sequenceDataType interface{})) *MockExtended_QueryKey_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(types.BoundContract), args[2].(query.KeyFilter), args[3].(query.LimitAndSort), args[4].(interface{})) + }) + return _c +} + +func (_c *MockExtended_QueryKey_Call) Return(_a0 []types.Sequence, _a1 error) *MockExtended_QueryKey_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *MockExtended_QueryKey_Call) RunAndReturn(run func(context.Context, types.BoundContract, query.KeyFilter, query.LimitAndSort, interface{}) ([]types.Sequence, error)) *MockExtended_QueryKey_Call { + _c.Call.Return(run) + return _c +} + +// Unbind provides a mock function with given fields: ctx, bindings +func (_m *MockExtended) Unbind(ctx context.Context, bindings []types.BoundContract) error { + ret := _m.Called(ctx, bindings) + + if len(ret) == 0 { + panic("no return value specified for Unbind") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, []types.BoundContract) error); ok { + r0 = rf(ctx, bindings) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// MockExtended_Unbind_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Unbind' +type MockExtended_Unbind_Call struct { + *mock.Call +} + +// Unbind is a helper method to define mock.On call +// - ctx context.Context +// - bindings []types.BoundContract +func (_e *MockExtended_Expecter) Unbind(ctx interface{}, bindings interface{}) *MockExtended_Unbind_Call { + return &MockExtended_Unbind_Call{Call: _e.mock.On("Unbind", ctx, bindings)} +} + +func (_c *MockExtended_Unbind_Call) Run(run func(ctx context.Context, bindings []types.BoundContract)) *MockExtended_Unbind_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].([]types.BoundContract)) + }) + return _c +} + +func (_c *MockExtended_Unbind_Call) Return(_a0 error) *MockExtended_Unbind_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *MockExtended_Unbind_Call) RunAndReturn(run func(context.Context, []types.BoundContract) error) *MockExtended_Unbind_Call { + _c.Call.Return(run) + return _c +} + // NewMockExtended creates a new instance of MockExtended. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. // The first argument is typically a *testing.T value. func NewMockExtended(t interface { diff --git a/pkg/contractreader/contractreader_facade.go b/pkg/contractreader/contractreader_facade.go index b2aea2a71..9ea7c93fb 100644 --- a/pkg/contractreader/contractreader_facade.go +++ b/pkg/contractreader/contractreader_facade.go @@ -9,6 +9,7 @@ import ( ) // ContractReaderFacade wraps the public functions of ContractReader in chainlink-common so that we can mock it. +// See types.ContractReader in chainlink-common/pkg/types/contract_reader.go for details. // //nolint:lll // don't read this interface. type ContractReaderFacade interface { @@ -17,5 +18,11 @@ type ContractReaderFacade interface { Bind(ctx context.Context, bindings []types.BoundContract) error Unbind(ctx context.Context, bindings []types.BoundContract) error QueryKey(ctx context.Context, contract types.BoundContract, filter query.KeyFilter, limitAndSort query.LimitAndSort, sequenceDataType any) ([]types.Sequence, error) - //mustEmbedUnimplementedContractReaderServer() + + // HealthReport returns a full health report of the callee including its dependencies. + // Keys are based on Name(), with nil values when healthy or errors otherwise. + // Use CopyHealth to collect reports from sub-services. + // This should run very fast, so avoid doing computation and instead prefer reporting pre-calculated state. + // On finality violation report must contain at least one ErrFinalityViolation. + HealthReport() map[string]error } diff --git a/pkg/contractreader/extended.go b/pkg/contractreader/extended.go index dfd8a9845..ccdd0540d 100644 --- a/pkg/contractreader/extended.go +++ b/pkg/contractreader/extended.go @@ -7,7 +7,9 @@ import ( "sync" "time" + "github.com/smartcontractkit/chainlink-common/pkg/services" "github.com/smartcontractkit/chainlink-common/pkg/types" + clcommontypes "github.com/smartcontractkit/chainlink-common/pkg/types" "github.com/smartcontractkit/chainlink-common/pkg/types/query" "github.com/smartcontractkit/chainlink-common/pkg/types/query/primitives" @@ -15,16 +17,31 @@ import ( ) var ( - ErrTooManyBindings = errors.New("contract binding not found") - ErrNoBindings = errors.New("no bindings found") + ErrFinalityViolated = errors.New("finality violated") + ErrTooManyBindings = errors.New("too many bindings") + ErrNoBindings = errors.New("no bindings found") ) // Extended version of a ContractReader. type Extended interface { + // Unbind is included for compatibility with ContractReader + Unbind(ctx context.Context, bindings []types.BoundContract) error + // HealthReport is included for compatibility with ContractReader + HealthReport() map[string]error + Bind(ctx context.Context, bindings []types.BoundContract) error GetBindings(contractName string) []ExtendedBoundContract + // QueryKey is from the base contract reader interface. + QueryKey( + ctx context.Context, + contract types.BoundContract, + filter query.KeyFilter, + limitAndSort query.LimitAndSort, + sequenceDataType any, + ) ([]types.Sequence, error) + // ExtendedQueryKey performs automatic binding from contractName to the first bound contract. // An error is generated if there are more than one bound contract for the contractName. ExtendedQueryKey( @@ -35,6 +52,14 @@ type Extended interface { sequenceDataType any, ) ([]types.Sequence, error) + // GetLatestValue is from the base contract reader interface. + GetLatestValue( + ctx context.Context, + readIdentifier string, + confidenceLevel primitives.ConfidenceLevel, + params, returnVal any, + ) error + // ExtendedGetLatestValue performs automatic binding from contractName to the first bound contract, and // constructs a read identifier for a given method name. An error is generated if there are more than one // bound contract for the contractName. @@ -45,6 +70,12 @@ type Extended interface { params, returnVal any, ) error + // BatchGetLatestValues is from the base contract reader interface. + BatchGetLatestValues( + ctx context.Context, + request types.BatchGetLatestValuesRequest, + ) (types.BatchGetLatestValuesResult, error) + // ExtendedBatchGetLatestValues performs automatic binding from contractNames to bound contracts, and // contructs a BatchGetLatestValuesRequest with the resolved bindings. ExtendedBatchGetLatestValues( @@ -62,14 +93,18 @@ type ExtendedBoundContract struct { // extendedContractReader is an extended version of the contract reader. type extendedContractReader struct { - ContractReaderFacade + reader ContractReaderFacade contractBindingsByName map[string][]ExtendedBoundContract mu *sync.RWMutex } func NewExtendedContractReader(baseContractReader ContractReaderFacade) Extended { + // avoid double wrapping + if ecr, ok := baseContractReader.(Extended); ok { + return ecr + } return &extendedContractReader{ - ContractReaderFacade: baseContractReader, + reader: baseContractReader, contractBindingsByName: make(map[string][]ExtendedBoundContract), mu: &sync.RWMutex{}, } @@ -89,6 +124,29 @@ func (e *extendedContractReader) getOneBinding(contractName string) (ExtendedBou } } +func (e *extendedContractReader) QueryKey( + ctx context.Context, + contract types.BoundContract, + filter query.KeyFilter, + limitAndSort query.LimitAndSort, + sequenceDataType any, +) ([]types.Sequence, error) { + result, err := e.reader.QueryKey( + ctx, + contract, + filter, + limitAndSort, + sequenceDataType, + ) + + // reads may update the reader health, so check for violations after every read. + if e.hasFinalityViolation() { + return nil, ErrFinalityViolated + } + + return result, err +} + func (e *extendedContractReader) ExtendedQueryKey( ctx context.Context, contractName string, @@ -110,6 +168,28 @@ func (e *extendedContractReader) ExtendedQueryKey( ) } +func (e *extendedContractReader) GetLatestValue( + ctx context.Context, + readIdentifier string, + confidenceLevel primitives.ConfidenceLevel, + params, returnVal any, +) error { + err := e.reader.GetLatestValue( + ctx, + readIdentifier, + confidenceLevel, + params, + returnVal, + ) + + // reads may update the reader health, so check for violations after every read. + if e.hasFinalityViolation() { + return ErrFinalityViolated + } + + return err +} + func (e *extendedContractReader) ExtendedGetLatestValue( ctx context.Context, contractName, methodName string, @@ -131,6 +211,20 @@ func (e *extendedContractReader) ExtendedGetLatestValue( ) } +func (e *extendedContractReader) BatchGetLatestValues( + ctx context.Context, + request types.BatchGetLatestValuesRequest, +) (types.BatchGetLatestValuesResult, error) { + result, err := e.reader.BatchGetLatestValues(ctx, request) + + // reads may update the reader health, so check for violations after every read. + if e.hasFinalityViolation() { + return nil, ErrFinalityViolated + } + + return result, err +} + func (e *extendedContractReader) ExtendedBatchGetLatestValues( ctx context.Context, request ExtendedBatchGetLatestValuesRequest, @@ -150,7 +244,7 @@ func (e *extendedContractReader) ExtendedBatchGetLatestValues( } // Call the underlying BatchGetLatestValues with the converted request - return e.ContractReaderFacade.BatchGetLatestValues(ctx, convertedRequest) + return e.BatchGetLatestValues(ctx, convertedRequest) } func (e *extendedContractReader) Bind(ctx context.Context, allBindings []types.BoundContract) error { @@ -159,7 +253,7 @@ func (e *extendedContractReader) Bind(ctx context.Context, allBindings []types.B return nil } - err := e.ContractReaderFacade.Bind(ctx, validBindings) + err := e.reader.Bind(ctx, validBindings) if err != nil { return fmt.Errorf("failed to call ContractReader.Bind: %w", err) } @@ -201,5 +295,23 @@ func (e *extendedContractReader) bindingExists(b types.BoundContract) bool { return false } +// hasFinalityViolation checks the reader's HealthReport for a finality violated error. +// The report is based on the current known state, it does not proactively check for new errors. +// The state is typically updated as the LogPoller reads events from an rpc. +func (e *extendedContractReader) hasFinalityViolation() bool { + report := e.reader.HealthReport() + return services.ContainsError( + report, + clcommontypes.ErrFinalityViolated) +} + +func (e *extendedContractReader) Unbind(ctx context.Context, bindings []types.BoundContract) error { + return e.reader.Unbind(ctx, bindings) +} + +func (e *extendedContractReader) HealthReport() map[string]error { + return e.reader.HealthReport() +} + // Interface compliance check var _ Extended = (*extendedContractReader)(nil) diff --git a/pkg/contractreader/extended_test.go b/pkg/contractreader/extended_test.go index e218bf162..2b716efa8 100644 --- a/pkg/contractreader/extended_test.go +++ b/pkg/contractreader/extended_test.go @@ -6,6 +6,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-common/pkg/types" @@ -47,3 +48,13 @@ func TestExtendedContractReader(t *testing.T) { assert.Equal(t, "0x123", bindings[0].Binding.Address) assert.Equal(t, "0x124", bindings[1].Binding.Address) } + +func TestDoubleWrap(t *testing.T) { + var cr contractreader.ContractReaderFacade + + wrapped := contractreader.NewExtendedContractReader(cr) + require.NotEqual(t, &cr, &wrapped) + + doubleWrapped := contractreader.NewExtendedContractReader(cr) + require.Equal(t, wrapped, doubleWrapped) +} diff --git a/pkg/contractreader/extended_unit_test.go b/pkg/contractreader/extended_unit_test.go index abef0ea15..a30014260 100644 --- a/pkg/contractreader/extended_unit_test.go +++ b/pkg/contractreader/extended_unit_test.go @@ -180,7 +180,7 @@ func TestExtendedBatchGetLatestValues(t *testing.T) { // Create extended reader with mock extendedReader := &extendedContractReader{ - ContractReaderFacade: mockReader, + reader: mockReader, contractBindingsByName: tt.bindings, mu: &sync.RWMutex{}, } @@ -211,3 +211,7 @@ func (m *mockContractReader) BatchGetLatestValues( ) (types.BatchGetLatestValuesResult, error) { return m.BatchGetLatestValuesResponse, nil } + +func (m *mockContractReader) HealthReport() map[string]error { + return nil +} diff --git a/pkg/contractreader/observed.go b/pkg/contractreader/observed.go index 699f5e1da..b64a5b426 100644 --- a/pkg/contractreader/observed.go +++ b/pkg/contractreader/observed.go @@ -90,6 +90,11 @@ func NewObserverReader( } } +func (o *Observed) HealthReport() map[string]error { + // Health report doesn't seem to be an IO operation, so no need to observe. + return o.ContractReaderFacade.HealthReport() +} + func (o *Observed) GetLatestValue( ctx context.Context, readIdentifier string, diff --git a/pkg/reader/ccip_test.go b/pkg/reader/ccip_test.go index 45271a8a2..09d4ec198 100644 --- a/pkg/reader/ccip_test.go +++ b/pkg/reader/ccip_test.go @@ -44,6 +44,7 @@ func TestCCIPChainReader_getSourceChainsConfig(t *testing.T) { destCR := reader_mocks.NewMockContractReaderFacade(t) destCR.EXPECT().Bind(mock.Anything, mock.Anything).Return(nil) + destCR.EXPECT().HealthReport().Return(nil) destCR.EXPECT().GetLatestValue( mock.Anything, mock.Anything, @@ -837,6 +838,7 @@ func withReturnValueOverridden(mapper func(returnVal interface{})) func(ctx cont func TestCCIPChainReader_getDestFeeQuoterStaticConfig(t *testing.T) { destCR := reader_mocks.NewMockContractReaderFacade(t) destCR.EXPECT().Bind(mock.Anything, mock.Anything).Return(nil) + destCR.EXPECT().HealthReport().Return(nil) destCR.EXPECT().GetLatestValue( mock.Anything, mock.Anything, @@ -882,6 +884,7 @@ func TestCCIPChainReader_getFeeQuoterTokenPriceUSD(t *testing.T) { tokenAddr := []byte{0x3, 0x4} destCR := reader_mocks.NewMockContractReaderFacade(t) destCR.EXPECT().Bind(mock.Anything, mock.Anything).Return(nil) + destCR.EXPECT().HealthReport().Return(nil) destCR.EXPECT().GetLatestValue( mock.Anything, mock.Anything, From 428f005362832904248e56b9a4a3b7c13f930e81 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 20 Dec 2024 10:01:54 -0500 Subject: [PATCH 02/10] Encode sender address according to source chain. (#396) --- commit/report.go | 2 +- execute/report/report.go | 2 +- pkg/reader/ccip.go | 2 +- pkg/reader/ccip_interface.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/commit/report.go b/commit/report.go index 00d51d8d6..c6e0885ea 100644 --- a/commit/report.go +++ b/commit/report.go @@ -8,9 +8,9 @@ import ( "fmt" "time" - "github.com/smartcontractkit/chainlink-common/pkg/logger" "golang.org/x/exp/maps" + "github.com/smartcontractkit/chainlink-common/pkg/logger" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" "github.com/smartcontractkit/chainlink-ccip/commit/committypes" diff --git a/execute/report/report.go b/execute/report/report.go index 287cba799..55182af85 100644 --- a/execute/report/report.go +++ b/execute/report/report.go @@ -236,7 +236,7 @@ func (b *execReportBuilder) checkMessageNonce( } chainNonces := b.sendersNonce[execReport.SourceChain] - sender := typeconv.AddressBytesToString(msg.Sender[:], uint64(b.destChainSelector)) + sender := typeconv.AddressBytesToString(msg.Sender[:], uint64(msg.Header.SourceChainSelector)) if _, ok := chainNonces[sender]; !ok { b.lggr.Errorw("Skipping message - missing nonce", "messageID", msg.Header.MessageID, diff --git a/pkg/reader/ccip.go b/pkg/reader/ccip.go index 5197adc14..d036f9b1e 100644 --- a/pkg/reader/ccip.go +++ b/pkg/reader/ccip.go @@ -435,7 +435,7 @@ func (r *ccipChainReader) Nonces( responses := make([]uint64, len(addresses)) for i, address := range addresses { - sender, err := typeconv.AddressStringToBytes(address, uint64(destChainSelector)) + sender, err := typeconv.AddressStringToBytes(address, uint64(sourceChainSelector)) if err != nil { return nil, fmt.Errorf("failed to convert address %s to bytes: %w", address, err) } diff --git a/pkg/reader/ccip_interface.go b/pkg/reader/ccip_interface.go index 9ec64b3c7..e652b6e60 100644 --- a/pkg/reader/ccip_interface.go +++ b/pkg/reader/ccip_interface.go @@ -114,7 +114,7 @@ type CCIPReader interface { GetContractAddress(contractName string, chain cciptypes.ChainSelector) ([]byte, error) // Nonces fetches all nonces for the provided selector/address pairs. Addresses are a string encoded raw address, - // it must be encoding according to the destination chain requirements with typeconv.AddressBytesToString. + // it must be encoding according to the source chain requirements with typeconv.AddressBytesToString. Nonces( ctx context.Context, source, dest cciptypes.ChainSelector, From 95fc747521fb0b2c9dd178244402e2a902cd56c6 Mon Sep 17 00:00:00 2001 From: dimitris Date: Fri, 20 Dec 2024 18:14:24 +0200 Subject: [PATCH 03/10] No panic on rmn newRequestID() (#397) --- commit/merkleroot/rmn/controller.go | 17 ++++++++++++----- commit/merkleroot/rmn/controller_test.go | 10 ++++++++++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/commit/merkleroot/rmn/controller.go b/commit/merkleroot/rmn/controller.go index 525ca3c12..61d0f5b90 100644 --- a/commit/merkleroot/rmn/controller.go +++ b/commit/merkleroot/rmn/controller.go @@ -16,6 +16,7 @@ import ( mapset "github.com/deckarep/golang-set/v2" "golang.org/x/exp/maps" + rand2 "golang.org/x/exp/rand" "google.golang.org/protobuf/proto" chainsel "github.com/smartcontractkit/chain-selectors" @@ -364,7 +365,7 @@ func (c *controller) sendObservationRequests( } req := &rmnpb.Request{ - RequestId: newRequestID(), + RequestId: newRequestID(c.lggr), Request: &rmnpb.Request_ObservationRequest{ ObservationRequest: &rmnpb.ObservationRequest{ LaneDest: destChain, @@ -810,7 +811,7 @@ func (c *controller) sendReportSignatureRequest( } req := &rmnpb.Request{ - RequestId: newRequestID(), + RequestId: newRequestID(c.lggr), Request: &rmnpb.Request_ReportSignatureRequest{ ReportSignatureRequest: reportSigReq, }, @@ -908,7 +909,7 @@ func (c *controller) listenForRmnReportSignatures( continue } req := &rmnpb.Request{ - RequestId: newRequestID(), + RequestId: newRequestID(c.lggr), Request: &rmnpb.Request_ReportSignatureRequest{ ReportSignatureRequest: reportSigReq, }, @@ -1072,11 +1073,17 @@ func randomShuffle[T any](s []T) []T { return ret } -func newRequestID() uint64 { +// newRequestID generates a new unique request ID. +func newRequestID(lggr logger.Logger) uint64 { b := make([]byte, 8) _, err := crand.Read(b) if err != nil { - panic(err) + // fallback to time-based id in the very rare scenario that the random number generator fails + lggr.Warnw("failed to generate random request id, falling back to golang.org/x/exp/rand", + "err", err, + ) + rand2.Seed(uint64(time.Now().UnixNano())) + return rand2.Uint64() } randomUint64 := binary.LittleEndian.Uint64(b) return randomUint64 diff --git a/commit/merkleroot/rmn/controller_test.go b/commit/merkleroot/rmn/controller_test.go index dc7c44807..c9fd9c5a1 100644 --- a/commit/merkleroot/rmn/controller_test.go +++ b/commit/merkleroot/rmn/controller_test.go @@ -733,6 +733,16 @@ func Test_controller_validateSignedObservationResponse(t *testing.T) { } } +func Test_newRequestID(t *testing.T) { + ids := map[uint64]struct{}{} + for i := 0; i < 1000; i++ { + id := newRequestID(logger.Test(t)) + _, ok := ids[id] + assert.False(t, ok) + ids[id] = struct{}{} + } +} + func (ts *testSetup) waitForObservationRequestsToBeSent( rmnClient *mockPeerClient, homeF int, From 5fa504b51deecfff82c35a27b82c1c8932d843db Mon Sep 17 00:00:00 2001 From: "Abdelrahman Soliman (Boda)" <2677789+asoliman92@users.noreply.github.com> Date: Fri, 20 Dec 2024 19:13:39 +0200 Subject: [PATCH 04/10] Use encoded sizes hash [CCIP-4618] (#376) Truncate observation algorithm to fit max size was encoding whole observation after removing each single message. This operation is CPU heavy and made it very slow even to finish tests in time. Fix: Use a hash to hold encoded messages sizes and while removing messages we do simple arithmetic operations to deduce the current size. Still there's encoding happening but on a higher level and less frequently to reduce any snowball effects from small differences because the arithmetic operations are not 100% accurate. Co-authored-by: Will Winder --- execute/exectypes/observation.go | 16 + execute/internal/gas/gas_estimate_provider.go | 4 +- execute/internal/utils.go | 20 + execute/internal/utils_test.go | 31 ++ execute/observation.go | 7 +- execute/optimizers/type_optimizer.go | 220 ++++++++++ execute/optimizers/type_optimizer_test.go | 395 ++++++++++++++++++ execute/outcome.go | 4 +- execute/plugin.go | 6 +- execute/plugin_functions.go | 167 -------- execute/plugin_functions_test.go | 335 --------------- execute/test_utils.go | 37 -- 12 files changed, 699 insertions(+), 543 deletions(-) create mode 100644 execute/internal/utils.go create mode 100644 execute/internal/utils_test.go create mode 100644 execute/optimizers/type_optimizer.go create mode 100644 execute/optimizers/type_optimizer_test.go diff --git a/execute/exectypes/observation.go b/execute/exectypes/observation.go index 331e4d7dc..543338d2c 100644 --- a/execute/exectypes/observation.go +++ b/execute/exectypes/observation.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" + "github.com/smartcontractkit/chainlink-ccip/execute/internal" dt "github.com/smartcontractkit/chainlink-ccip/internal/plugincommon/discovery/discoverytypes" cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" ) @@ -17,6 +18,8 @@ type MessageObservations map[cciptypes.ChainSelector]map[cciptypes.SeqNum]ccipty type MessageHashes map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Bytes32 +type EncodedMsgAndTokenDataSizes map[cciptypes.ChainSelector]map[cciptypes.SeqNum]int + // Flatten nested maps into a slice of messages. func (mo MessageObservations) Flatten() []cciptypes.Message { var results []cciptypes.Message @@ -43,6 +46,19 @@ func GetHashes(ctx context.Context, mo MessageObservations, hasher cciptypes.Mes return hashes, nil } +// GetEncodedMsgAndTokenDataSizes calculates the encoded sizes of messages and their token data counterpart. +func GetEncodedMsgAndTokenDataSizes(mo MessageObservations, tds TokenDataObservations) EncodedMsgAndTokenDataSizes { + sizes := make(EncodedMsgAndTokenDataSizes) + for chain, msgs := range mo { + sizes[chain] = make(map[cciptypes.SeqNum]int) + for seq, msg := range msgs { + td := tds[chain][seq] + sizes[chain][seq] = internal.EncodedSize(msg) + internal.EncodedSize(td) + } + } + return sizes +} + // NonceObservations contain the latest nonce for senders in the previously observed messages. // Nonces are organized by source chain selector and the string encoded sender address. The address // must be encoding according to the destination chain requirements with typeconv.AddressBytesToString. diff --git a/execute/internal/gas/gas_estimate_provider.go b/execute/internal/gas/gas_estimate_provider.go index 7ac6f0fba..226213e4d 100644 --- a/execute/internal/gas/gas_estimate_provider.go +++ b/execute/internal/gas/gas_estimate_provider.go @@ -1,6 +1,8 @@ package gas -import "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" +import ( + "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" +) // EstimateProvider is used to estimate the gas cost of a message or a merkle tree. // TODO: Move to pkg/types/ccipocr3 or remove. diff --git a/execute/internal/utils.go b/execute/internal/utils.go new file mode 100644 index 000000000..3acb6ce7c --- /dev/null +++ b/execute/internal/utils.go @@ -0,0 +1,20 @@ +package internal + +import "encoding/json" + +func EncodedSize[T any](obj T) int { + enc, err := json.Marshal(obj) + if err != nil { + return 0 + } + return len(enc) +} + +func RemoveIthElement[T any](slice []T, i int) []T { + if i < 0 || i >= len(slice) { + return slice // Return the original slice if index is out of bounds + } + newSlice := make([]T, 0, len(slice)-1) + newSlice = append(newSlice, slice[:i]...) + return append(newSlice, slice[i+1:]...) +} diff --git a/execute/internal/utils_test.go b/execute/internal/utils_test.go new file mode 100644 index 000000000..d7302d212 --- /dev/null +++ b/execute/internal/utils_test.go @@ -0,0 +1,31 @@ +package internal + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRemoveIthElement(t *testing.T) { + tests := []struct { + name string + slice []int + index int + expected []int + }{ + {"Remove middle element", []int{1, 2, 3, 4, 5}, 2, []int{1, 2, 4, 5}}, + {"Remove first element", []int{1, 2, 3, 4, 5}, 0, []int{2, 3, 4, 5}}, + {"Remove last element", []int{1, 2, 3, 4, 5}, 4, []int{1, 2, 3, 4}}, + {"Index out of bounds (negative)", []int{1, 2, 3, 4, 5}, -1, []int{1, 2, 3, 4, 5}}, + {"Index out of bounds (too large)", []int{1, 2, 3, 4, 5}, 5, []int{1, 2, 3, 4, 5}}, + {"Single element slice", []int{1}, 0, []int{}}, + {"Empty slice", []int{}, 0, []int{}}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := RemoveIthElement(tt.slice, tt.index) + assert.Equal(t, tt.expected, result) + }) + } +} diff --git a/execute/observation.go b/execute/observation.go index 771acb527..b03ba2889 100644 --- a/execute/observation.go +++ b/execute/observation.go @@ -236,6 +236,9 @@ func (p *Plugin) getMessagesObservation( if err1 != nil { return exectypes.Observation{}, fmt.Errorf("unable to process token data %w", err1) } + if validateTokenDataObservations(messageObs, tkData) != nil { + return exectypes.Observation{}, fmt.Errorf("invalid token data observations") + } costlyMessages, err := p.costlyMessageObserver.Observe(ctx, messageObs.Flatten(), messageTimestamps) if err != nil { @@ -252,9 +255,11 @@ func (p *Plugin) getMessagesObservation( observation.Hashes = hashes observation.CostlyMessages = costlyMessages observation.TokenData = tkData + //observation.MessageAndTokenDataEncodedSizes = exectypes.GetEncodedMsgAndTokenDataSizes(messageObs, tkData) // Make sure encoded observation fits within the maximum observation size. - observation, err = truncateObservation(observation, maxObservationLength) + //observation, err = truncateObservation(observation, maxObservationLength, p.emptyEncodedSizes) + observation, err = p.observationOptimizer.TruncateObservation(observation) if err != nil { return exectypes.Observation{}, fmt.Errorf("unable to truncate observation: %w", err) } diff --git a/execute/optimizers/type_optimizer.go b/execute/optimizers/type_optimizer.go new file mode 100644 index 000000000..7f4e7ae63 --- /dev/null +++ b/execute/optimizers/type_optimizer.go @@ -0,0 +1,220 @@ +package optimizers + +import ( + "fmt" + "sort" + + "github.com/smartcontractkit/chainlink-ccip/execute/exectypes" + "github.com/smartcontractkit/chainlink-ccip/execute/internal" + + "golang.org/x/exp/maps" + + cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" +) + +type ObservationOptimizer struct { + maxEncodedSize int + emptyEncodedSizes EmptyEncodeSizes +} + +func NewObservationOptimizer(maxEncodedSize int) ObservationOptimizer { + return ObservationOptimizer{ + maxEncodedSize: maxEncodedSize, + emptyEncodedSizes: NewEmptyEncodeSizes(), + } +} + +type EmptyEncodeSizes struct { + MessageAndTokenData int + CommitData int + SeqNumMap int +} + +func NewEmptyEncodeSizes() EmptyEncodeSizes { + emptyMsg := cciptypes.Message{} + emptyTokenData := exectypes.MessageTokenData{} + emptyCommitData := exectypes.CommitData{} + emptySeqNrSize := internal.EncodedSize(make(map[cciptypes.SeqNum]cciptypes.Message)) + + return EmptyEncodeSizes{ + MessageAndTokenData: internal.EncodedSize(emptyMsg) + internal.EncodedSize(emptyTokenData), + CommitData: internal.EncodedSize(emptyCommitData), // 305 + SeqNumMap: emptySeqNrSize, // 2 + } +} + +// TruncateObservation truncates the observation to fit within the given op.maxEncodedSize after encoding. +// It removes data from the observation in the following order: +// For each chain, pick last report and start removing messages one at a time. +// If removed all messages from the report, remove the report. +// If removed last report in the chain, remove the chain. +// After removing full report from a chain, move to the next chain and repeat. This ensures that we don't +// exclude messages from one chain only. +// Keep repeating this process until the encoded observation fits within the op.maxEncodedSize +// Important Note: We can't delete messages completely from single reports as we need them to create merkle proofs. +// +//nolint:gocyclo +func (op ObservationOptimizer) TruncateObservation(observation exectypes.Observation) (exectypes.Observation, error) { + obs := observation + encodedObs, err := obs.Encode() + if err != nil { + return exectypes.Observation{}, err + } + encodedObsSize := len(encodedObs) + if encodedObsSize <= op.maxEncodedSize { + return obs, nil + } + + chains := maps.Keys(obs.CommitReports) + sort.Slice(chains, func(i, j int) bool { + return chains[i] < chains[j] + }) + + messageAndTokenDataEncodedSizes := exectypes.GetEncodedMsgAndTokenDataSizes(obs.Messages, obs.TokenData) + // While the encoded obs is too large, continue filtering data. + for encodedObsSize > op.maxEncodedSize { + // go through each chain and truncate observations for the final commit report. + for _, chain := range chains { + commits := obs.CommitReports[chain] + if len(commits) == 0 { + continue + } + lastCommit := &commits[len(commits)-1] + seqNum := lastCommit.SequenceNumberRange.Start() + // Remove messages one by one starting from the last message of the last commit report. + for seqNum <= lastCommit.SequenceNumberRange.End() { + if _, ok := obs.Messages[chain][seqNum]; !ok { + return exectypes.Observation{}, fmt.Errorf("missing message with seqNr %d from chain %d", seqNum, chain) + } + obs.Messages[chain][seqNum] = cciptypes.Message{} + obs.TokenData[chain][seqNum] = exectypes.NewMessageTokenData() + // Subtract the removed message and token size + encodedObsSize -= messageAndTokenDataEncodedSizes[chain][seqNum] + // Add empty message and token encoded size + encodedObsSize += op.emptyEncodedSizes.MessageAndTokenData + seqNum++ + // Once we assert the estimation is less than the max size we double-check with actual encoding size. + // Otherwise, we short circuit after checking the estimation only + if encodedObsSize <= op.maxEncodedSize && fitsWithinSize(obs, op.maxEncodedSize) { + return obs, nil + } + } + + var bytesTruncated int + // Reaching here means that all messages in the report are truncated, truncate the last commit + obs, bytesTruncated = op.truncateLastCommit(obs, chain) + + encodedObsSize -= bytesTruncated + + if len(obs.CommitReports[chain]) == 0 { + // If the last commit report was truncated, truncate the chain + obs = op.truncateChain(obs, chain) + } + + // Once we assert the estimation is less than the max size we double-check with actual encoding size. + // Otherwise, we short circuit after checking the estimation only + if encodedObsSize <= op.maxEncodedSize && fitsWithinSize(obs, op.maxEncodedSize) { + return obs, nil + } + } + // Truncated all chains. Return obs as is. (it has other data like contract discovery) + if len(obs.CommitReports) == 0 { + return obs, nil + } + // Encoding again after doing a full iteration on all chains and removing messages/commits. + // That is because using encoded sizes is not 100% accurate and there are some missing bytes in the calculation. + encodedObs, err = obs.Encode() + if err != nil { + return exectypes.Observation{}, err + } + encodedObsSize = len(encodedObs) + } + + return obs, nil +} + +func fitsWithinSize(obs exectypes.Observation, maxEncodedSize int) bool { + encodedObs, err := obs.Encode() + if err != nil { + return false + } + return len(encodedObs) <= maxEncodedSize +} + +// truncateLastCommit removes the last commit from the observation. +// returns observation and the number of bytes truncated. +func (op ObservationOptimizer) truncateLastCommit( + obs exectypes.Observation, + chain cciptypes.ChainSelector, +) (exectypes.Observation, int) { + observation := obs + bytesTruncated := 0 + commits := observation.CommitReports[chain] + if len(commits) == 0 { + return observation, bytesTruncated + } + lastCommit := commits[len(commits)-1] + // Remove the last commit from the list. + commits = commits[:len(commits)-1] + observation.CommitReports[chain] = commits + // Remove from the encoded size. + bytesTruncated = bytesTruncated + op.emptyEncodedSizes.CommitData + 4 // brackets, and commas + for seqNum, msg := range observation.Messages[chain] { + if lastCommit.SequenceNumberRange.Contains(seqNum) { + // Remove the message from the observation. + delete(observation.Messages[chain], seqNum) + // Remove the token data from the observation. + delete(observation.TokenData[chain], seqNum) + //delete(observation.Hashes[chain], seqNum) + // Remove the encoded size of the message and token data. + bytesTruncated += op.emptyEncodedSizes.MessageAndTokenData + bytesTruncated = bytesTruncated + 2*op.emptyEncodedSizes.SeqNumMap + bytesTruncated += 4 // for brackets and commas + // Remove costly messages + for i, costlyMessage := range observation.CostlyMessages { + if costlyMessage == msg.Header.MessageID { + observation.CostlyMessages = internal.RemoveIthElement(observation.CostlyMessages, i) + break + } + } + // Leaving Nonces untouched + } + } + + return observation, bytesTruncated +} + +// truncateChain removes all data related to the given chain from the observation. +// returns true if the chain was found and truncated, false otherwise. +func (op ObservationOptimizer) truncateChain( + obs exectypes.Observation, + chain cciptypes.ChainSelector, +) exectypes.Observation { + observation := obs + if _, ok := observation.CommitReports[chain]; !ok { + return observation + } + messageIDs := make(map[cciptypes.Bytes32]struct{}) + // To remove costly message IDs we need to iterate over all messages and find the ones that belong to the chain. + for _, seqNumMap := range observation.Messages { + for _, message := range seqNumMap { + messageIDs[message.Header.MessageID] = struct{}{} + } + } + + deleteCostlyMessages := func() { + for i, costlyMessage := range observation.CostlyMessages { + if _, ok := messageIDs[costlyMessage]; ok { + observation.CostlyMessages = append(observation.CostlyMessages[:i], observation.CostlyMessages[i+1:]...) + } + } + } + + delete(observation.CommitReports, chain) + delete(observation.Messages, chain) + delete(observation.TokenData, chain) + delete(observation.Nonces, chain) + deleteCostlyMessages() + + return observation +} diff --git a/execute/optimizers/type_optimizer_test.go b/execute/optimizers/type_optimizer_test.go new file mode 100644 index 000000000..32abb3030 --- /dev/null +++ b/execute/optimizers/type_optimizer_test.go @@ -0,0 +1,395 @@ +package optimizers + +import ( + "testing" + + "github.com/smartcontractkit/chainlink-ccip/execute/exectypes" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-ccip/internal/libs/testhelpers/rand" + cciptypes "github.com/smartcontractkit/chainlink-ccip/pkg/types/ccipocr3" +) + +func Test_truncateLastCommit(t *testing.T) { + tests := []struct { + name string + chain cciptypes.ChainSelector + observation exectypes.Observation + expected exectypes.Observation + }{ + { + name: "no commits to truncate", + chain: 1, + observation: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: {}, + }, + }, + expected: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: {}, + }, + }, + }, + { + name: "truncate last commit", + chain: 1, + observation: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, + {SequenceNumberRange: cciptypes.NewSeqNumRange(11, 20)}, + }, + }, + Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{ + 1: { + 1: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x01}}}, + 11: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x02}}}, + }, + }, + TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{ + 1: { + 1: exectypes.NewMessageTokenData(), + 11: exectypes.NewMessageTokenData(), + }, + }, + CostlyMessages: []cciptypes.Bytes32{{0x02}}, + }, + expected: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, + }, + }, + Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{ + 1: { + 1: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x01}}}, + }, + }, + TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{ + 1: { + 1: exectypes.NewMessageTokenData(), + }, + }, + CostlyMessages: []cciptypes.Bytes32{}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + op := NewObservationOptimizer(10000) + truncated, _ := op.truncateLastCommit(tt.observation, tt.chain) + require.Equal(t, tt.expected, truncated) + }) + } +} + +func Test_truncateChain(t *testing.T) { + tests := []struct { + name string + chain cciptypes.ChainSelector + observation exectypes.Observation + expected exectypes.Observation + }{ + { + name: "truncate chain data", + chain: 1, + observation: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, + {SequenceNumberRange: cciptypes.NewSeqNumRange(11, 20)}, + }, + }, + Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{ + 1: { + 1: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x01}}}, + }, + }, + TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{ + 1: { + 1: exectypes.NewMessageTokenData(), + }, + }, + CostlyMessages: []cciptypes.Bytes32{{0x01}}, + }, + expected: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{}, + Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{}, + TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{}, + CostlyMessages: []cciptypes.Bytes32{}, + }, + }, + { + name: "truncate non existent chain", + chain: 2, // non existent chain + observation: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, + }, + }, + Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{ + 1: { + 1: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x01}}}, + }, + }, + TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{ + 1: { + 1: exectypes.NewMessageTokenData(), + }, + }, + CostlyMessages: []cciptypes.Bytes32{{0x01}}, + }, + expected: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, + }, + }, + Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{ + 1: { + 1: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x01}}}, + }, + }, + TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{ + 1: { + 1: exectypes.NewMessageTokenData(), + }, + }, + CostlyMessages: []cciptypes.Bytes32{{0x01}}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + op := NewObservationOptimizer(10000) // size not important here + truncated := op.truncateChain(tt.observation, tt.chain) + require.Equal(t, tt.expected, truncated) + }) + } +} + +func Test_truncateObservation(t *testing.T) { + tests := []struct { + name string + observation exectypes.Observation + maxSize int + expected exectypes.Observation + deletedMsgs map[cciptypes.ChainSelector]map[cciptypes.SeqNum]struct{} + wantErr bool + }{ + { + name: "no truncation needed", + observation: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, + }, + }, + }, + maxSize: 1000, + expected: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, + }, + }, + }, + }, + { + name: "truncate last commit", + observation: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 2)}, + {SequenceNumberRange: cciptypes.NewSeqNumRange(3, 4)}, + }, + }, + Messages: makeMessageObservation( + map[cciptypes.ChainSelector]cciptypes.SeqNumRange{ + 1: {1, 4}, + }, + withData(make([]byte, 100)), + ), + }, + maxSize: 2600, // this number is calculated by checking encoded sizes for the observation we expect + expected: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 2)}, + }, + }, + // Not going to check Messages in the test + }, + deletedMsgs: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]struct{}{ + 1: { + 3: struct{}{}, + 4: struct{}{}, + }, + }, + }, + { + name: "truncate entire chain", + observation: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 2)}, + }, + 2: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(3, 4)}, + }, + }, + Messages: makeMessageObservation( + map[cciptypes.ChainSelector]cciptypes.SeqNumRange{ + 1: {1, 2}, + 2: {3, 4}, + }, + withData(make([]byte, 100)), + ), + }, + maxSize: 1789, + expected: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 2: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(3, 4)}, + }, + }, + }, + }, + { + name: "truncate one message from first chain", + observation: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 2)}, + }, + 2: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(3, 4)}, + }, + }, + Messages: makeMessageObservation( + map[cciptypes.ChainSelector]cciptypes.SeqNumRange{ + 1: {1, 2}, + 2: {3, 4}, + }, + withData(make([]byte, 100)), + ), + }, + maxSize: 3159, // chain 1, message 1 will be truncated + expected: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 2)}, + }, + 2: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(3, 4)}, + }, + }, + }, + deletedMsgs: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]struct{}{ + 1: { + 1: struct{}{}, + }, + }, + }, + { + name: "truncate all", + observation: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ + 1: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, + }, + 2: { + {SequenceNumberRange: cciptypes.NewSeqNumRange(11, 20)}, + }, + }, + Messages: makeMessageObservation( + map[cciptypes.ChainSelector]cciptypes.SeqNumRange{ + 1: {1, 10}, + 2: {11, 20}, + }, + ), + }, + maxSize: 50, // less than what can fit a single commit report for single chain + expected: exectypes.Observation{ + CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.observation.TokenData = makeNoopTokenDataObservations(tt.observation.Messages) + tt.expected.TokenData = tt.observation.TokenData + op := NewObservationOptimizer(tt.maxSize) + obs, err := op.TruncateObservation(tt.observation) + if tt.wantErr { + require.Error(t, err) + return + } + require.Equal(t, tt.expected.CommitReports, obs.CommitReports) + + // Check only deleted messages were deleted + for chain, seqNums := range obs.Messages { + for seqNum := range seqNums { + if _, ok := tt.deletedMsgs[chain]; ok { + if _, ok2 := tt.deletedMsgs[chain][seqNum]; ok2 { + require.True(t, obs.Messages[chain][seqNum].IsEmpty()) + continue + } + } + require.False(t, obs.Messages[chain][seqNum].IsEmpty()) + } + } + }) + } +} + +func makeMessageObservation( + srcToSeqNumRange map[cciptypes.ChainSelector]cciptypes.SeqNumRange, + opts ...msgOption) exectypes.MessageObservations { + + obs := make(exectypes.MessageObservations) + for src, seqNumRng := range srcToSeqNumRange { + obs[src] = make(map[cciptypes.SeqNum]cciptypes.Message) + for i := seqNumRng.Start(); i <= seqNumRng.End(); i++ { + msg := cciptypes.Message{ + Header: cciptypes.RampMessageHeader{ + SourceChainSelector: src, + SequenceNumber: i, + MessageID: rand.RandomBytes32(), + }, + FeeValueJuels: cciptypes.NewBigIntFromInt64(100), + } + for _, opt := range opts { + opt(&msg) + } + obs[src][i] = msg + } + } + return obs +} + +func makeNoopTokenDataObservations(msgs exectypes.MessageObservations) exectypes.TokenDataObservations { + tokenData := make(exectypes.TokenDataObservations) + for src, seqNumToMsg := range msgs { + tokenData[src] = make(map[cciptypes.SeqNum]exectypes.MessageTokenData) + for seq := range seqNumToMsg { + tokenData[src][seq] = exectypes.NewMessageTokenData() + } + } + return tokenData + +} + +type msgOption func(*cciptypes.Message) + +func withData(data []byte) msgOption { + return func(m *cciptypes.Message) { + m.Data = data + } +} diff --git a/execute/outcome.go b/execute/outcome.go index 15abd1e22..9f9c74bf8 100644 --- a/execute/outcome.go +++ b/execute/outcome.go @@ -5,6 +5,8 @@ import ( "fmt" "sort" + "github.com/smartcontractkit/chainlink-ccip/execute/internal" + mapset "github.com/deckarep/golang-set/v2" "github.com/smartcontractkit/libocr/offchainreporting2plus/ocr3types" @@ -158,7 +160,7 @@ func (p *Plugin) getMessagesOutcome( } if len(report.Messages) == 0 { // If there are no messages, remove the commit report. - commitReports = append(commitReports[:i], commitReports[i+1:]...) + commitReports = internal.RemoveIthElement(commitReports, i) } commitReports = append(commitReports, report) } diff --git a/execute/plugin.go b/execute/plugin.go index 1bcde3ba1..fd02e3fa9 100644 --- a/execute/plugin.go +++ b/execute/plugin.go @@ -7,6 +7,8 @@ import ( "fmt" "time" + "github.com/smartcontractkit/chainlink-ccip/execute/optimizers" + mapset "github.com/deckarep/golang-set/v2" "golang.org/x/exp/maps" @@ -63,6 +65,7 @@ type Plugin struct { estimateProvider gas.EstimateProvider lggr logger.Logger + observationOptimizer optimizers.ObservationOptimizer // state contractsInitialized bool } @@ -114,7 +117,8 @@ func NewPlugin( reportingCfg.OracleID, destChain, ), - observer: metricsReporter, + observer: metricsReporter, + observationOptimizer: optimizers.NewObservationOptimizer(maxObservationLength), } } diff --git a/execute/plugin_functions.go b/execute/plugin_functions.go index 133507558..924533911 100644 --- a/execute/plugin_functions.go +++ b/execute/plugin_functions.go @@ -6,8 +6,6 @@ import ( "sort" "time" - "golang.org/x/exp/maps" - mapset "github.com/deckarep/golang-set/v2" "github.com/smartcontractkit/libocr/offchainreporting2plus/types" @@ -245,171 +243,6 @@ func filterOutExecutedMessages( return filtered, nil } -// truncateObservation truncates the observation to fit within the given maxSize after encoding. -// It removes data from the observation in the following order: -// For each chain, pick last report and start removing messages one at a time. -// If removed all messages from the report, remove the report. -// If removed last report in the chain, remove the chain. -// After removing full report from a chain, move to the next chain and repeat. This ensures that we don't -// exclude messages from one chain only. -// Keep repeating this process until the encoded observation fits within the maxSize -// Important Note: We can't delete messages completely from single reports as we need them to create merkle proofs. -// -//nolint:gocyclo -func truncateObservation( - obs exectypes.Observation, - maxSize int, -) (exectypes.Observation, error) { - // TODO: Use a hash to store encoding sizes for individual messages - // and use that to determine how many messages to delete. - observation := obs - encodedObs, err := observation.Encode() - if err != nil { - return exectypes.Observation{}, err - } - if len(encodedObs) <= maxSize { - return observation, nil - } - - chains := maps.Keys(observation.CommitReports) - sort.Slice(chains, func(i, j int) bool { - return chains[i] < chains[j] - }) - - // If the encoded observation is too large, start filtering data. - for len(encodedObs) > maxSize { - for _, chain := range chains { - commits := observation.CommitReports[chain] - if len(commits) == 0 { - continue - } - lastCommit := &commits[len(commits)-1] - seqNum := lastCommit.SequenceNumberRange.Start() - - for seqNum <= lastCommit.SequenceNumberRange.End() { - if _, ok := observation.Messages[chain][seqNum]; !ok { - return exectypes.Observation{}, fmt.Errorf("missing message with seqNr %d from chain %d", seqNum, chain) - } - observation.Messages[chain][seqNum] = cciptypes.Message{} - - if _, ok := observation.TokenData[chain][seqNum]; !ok { - return exectypes.Observation{}, fmt.Errorf( - "missing tokenData for message with seqNr %d from chain %d", seqNum, chain, - ) - } - observation.TokenData[chain][seqNum] = exectypes.NewMessageTokenData() - - seqNum++ - // Each report will be deleted completely by maximum looping 8 times as the max report messages is 256. - // TODO: Remove the 32 check once we implement the hash size calculation. - if seqNum%32 == 0 && observationFitsSize(observation, maxSize) { - return observation, nil - } - } - - // Reaching here means that all messages in the report are truncated, truncate the last commit - observation = truncateLastCommit(observation, chain) - - if len(observation.CommitReports[chain]) == 0 { - // If the last commit report was truncated, truncate the chain - observation = truncateChain(observation, chain) - } - - if observationFitsSize(observation, maxSize) { - return observation, nil - } - chains = maps.Keys(observation.CommitReports) - } - // Truncated all chains. Return observation as is. (it has other data like contract discovery) - if len(observation.CommitReports) == 0 { - return observation, nil - } - encodedObs, err = observation.Encode() - if err != nil { - return exectypes.Observation{}, nil - } - } - - return observation, nil -} - -func observationFitsSize(obs exectypes.Observation, maxSize int) bool { - encodedObs, err := obs.Encode() - if err != nil { - return false - } - return len(encodedObs) <= maxSize -} - -// truncateLastCommit removes the last commit from the observation. -// errors if there are no commits to truncate. -func truncateLastCommit( - obs exectypes.Observation, - chain cciptypes.ChainSelector, -) exectypes.Observation { - observation := obs - commits := observation.CommitReports[chain] - if len(commits) == 0 { - return observation - } - lastCommit := commits[len(commits)-1] - // Remove the last commit from the list. - commits = commits[:len(commits)-1] - observation.CommitReports[chain] = commits - for seqNum, msg := range observation.Messages[chain] { - if lastCommit.SequenceNumberRange.Contains(seqNum) { - // Remove the message from the observation. - delete(observation.Messages[chain], seqNum) - // Remove the token data from the observation. - delete(observation.TokenData[chain], seqNum) - // Remove costly messages - for i, costlyMessage := range observation.CostlyMessages { - if costlyMessage == msg.Header.MessageID { - observation.CostlyMessages = append(observation.CostlyMessages[:i], observation.CostlyMessages[i+1:]...) - } - } - // Leaving Nonces untouched - } - } - - return observation -} - -// truncateChain removes all data related to the given chain from the observation. -// returns true if the chain was found and truncated, false otherwise. -func truncateChain( - obs exectypes.Observation, - chain cciptypes.ChainSelector, -) exectypes.Observation { - observation := obs - if _, ok := observation.CommitReports[chain]; !ok { - return observation - } - messageIDs := make(map[cciptypes.Bytes32]struct{}) - // To remove costly message IDs we need to iterate over all messages and find the ones that belong to the chain. - for _, seqNumMap := range observation.Messages { - for _, message := range seqNumMap { - messageIDs[message.Header.MessageID] = struct{}{} - } - } - - deleteCostlyMessages := func() { - for i, costlyMessage := range observation.CostlyMessages { - if _, ok := messageIDs[costlyMessage]; ok { - observation.CostlyMessages = append(observation.CostlyMessages[:i], observation.CostlyMessages[i+1:]...) - } - } - } - - delete(observation.CommitReports, chain) - delete(observation.Messages, chain) - delete(observation.TokenData, chain) - delete(observation.Nonces, chain) - deleteCostlyMessages() - - return observation -} - func decodeAttributedObservations( aos []types.AttributedObservation, ) ([]plugincommon.AttributedObservation[exectypes.Observation], error) { diff --git a/execute/plugin_functions_test.go b/execute/plugin_functions_test.go index 64987354d..63754dcf9 100644 --- a/execute/plugin_functions_test.go +++ b/execute/plugin_functions_test.go @@ -1317,338 +1317,3 @@ func Test_getMessageTimestampMap(t *testing.T) { }) } } - -func Test_truncateLastCommit(t *testing.T) { - tests := []struct { - name string - chain cciptypes.ChainSelector - observation exectypes.Observation - expected exectypes.Observation - }{ - { - name: "no commits to truncate", - chain: 1, - observation: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: {}, - }, - }, - expected: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: {}, - }, - }, - }, - { - name: "truncate last commit", - chain: 1, - observation: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, - {SequenceNumberRange: cciptypes.NewSeqNumRange(11, 20)}, - }, - }, - Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{ - 1: { - 1: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x01}}}, - 11: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x02}}}, - }, - }, - TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{ - 1: { - 1: exectypes.NewMessageTokenData(), - 11: exectypes.NewMessageTokenData(), - }, - }, - CostlyMessages: []cciptypes.Bytes32{{0x02}}, - }, - expected: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, - }, - }, - Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{ - 1: { - 1: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x01}}}, - }, - }, - TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{ - 1: { - 1: exectypes.NewMessageTokenData(), - }, - }, - CostlyMessages: []cciptypes.Bytes32{}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - truncated := truncateLastCommit(tt.observation, tt.chain) - require.Equal(t, tt.expected, truncated) - }) - } -} - -func Test_truncateChain(t *testing.T) { - tests := []struct { - name string - chain cciptypes.ChainSelector - observation exectypes.Observation - expected exectypes.Observation - }{ - { - name: "truncate chain data", - chain: 1, - observation: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, - {SequenceNumberRange: cciptypes.NewSeqNumRange(11, 20)}, - }, - }, - Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{ - 1: { - 1: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x01}}}, - }, - }, - TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{ - 1: { - 1: exectypes.NewMessageTokenData(), - }, - }, - CostlyMessages: []cciptypes.Bytes32{{0x01}}, - }, - expected: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{}, - Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{}, - TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{}, - CostlyMessages: []cciptypes.Bytes32{}, - }, - }, - { - name: "truncate non existent chain", - chain: 2, // non existent chain - observation: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, - }, - }, - Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{ - 1: { - 1: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x01}}}, - }, - }, - TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{ - 1: { - 1: exectypes.NewMessageTokenData(), - }, - }, - CostlyMessages: []cciptypes.Bytes32{{0x01}}, - }, - expected: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, - }, - }, - Messages: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]cciptypes.Message{ - 1: { - 1: {Header: cciptypes.RampMessageHeader{MessageID: cciptypes.Bytes32{0x01}}}, - }, - }, - TokenData: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]exectypes.MessageTokenData{ - 1: { - 1: exectypes.NewMessageTokenData(), - }, - }, - CostlyMessages: []cciptypes.Bytes32{{0x01}}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - truncated := truncateChain(tt.observation, tt.chain) - require.Equal(t, tt.expected, truncated) - }) - } -} - -func Test_truncateObservation(t *testing.T) { - tests := []struct { - name string - observation exectypes.Observation - maxSize int - expected exectypes.Observation - deletedMsgs map[cciptypes.ChainSelector]map[cciptypes.SeqNum]struct{} - wantErr bool - }{ - { - name: "no truncation needed", - observation: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, - }, - }, - }, - maxSize: 1000, - expected: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, - }, - }, - }, - }, - { - name: "truncate last commit", - observation: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 2)}, - {SequenceNumberRange: cciptypes.NewSeqNumRange(3, 4)}, - }, - }, - Messages: makeMessageObservation( - map[cciptypes.ChainSelector]cciptypes.SeqNumRange{ - 1: {1, 4}, - }, - withData(make([]byte, 100)), - ), - }, - maxSize: 2600, // this number is calculated by checking encoded sizes for the observation we expect - expected: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 2)}, - }, - }, - // Not going to check Messages in the test - }, - deletedMsgs: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]struct{}{ - 1: { - 3: struct{}{}, - 4: struct{}{}, - }, - }, - }, - { - name: "truncate entire chain", - observation: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 2)}, - }, - 2: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(3, 4)}, - }, - }, - Messages: makeMessageObservation( - map[cciptypes.ChainSelector]cciptypes.SeqNumRange{ - 1: {1, 2}, - 2: {3, 4}, - }, - withData(make([]byte, 100)), - ), - }, - maxSize: 1789, - expected: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 2: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(3, 4)}, - }, - }, - }, - }, - //{ - // name: "truncate one message from first chain", - // observation: exectypes.Observation{ - // CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - // 1: { - // {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 2)}, - // }, - // 2: { - // {SequenceNumberRange: cciptypes.NewSeqNumRange(3, 4)}, - // }, - // }, - // Messages: makeMessageObservation( - // map[cciptypes.ChainSelector]cciptypes.SeqNumRange{ - // 1: {1, 2}, - // 2: {3, 4}, - // }, - // withData(make([]byte, 100)), - // ), - // }, - // maxSize: 3159, // chain 1, message 1 will be truncated - // expected: exectypes.Observation{ - // CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - // 1: { - // {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 2)}, - // }, - // 2: { - // {SequenceNumberRange: cciptypes.NewSeqNumRange(3, 4)}, - // }, - // }, - // }, - // deletedMsgs: map[cciptypes.ChainSelector]map[cciptypes.SeqNum]struct{}{ - // 1: { - // 1: struct{}{}, - // }, - // }, - //}, - { - name: "truncate all", - observation: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{ - 1: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(1, 10)}, - }, - 2: { - {SequenceNumberRange: cciptypes.NewSeqNumRange(11, 20)}, - }, - }, - Messages: makeMessageObservation( - map[cciptypes.ChainSelector]cciptypes.SeqNumRange{ - 1: {1, 10}, - 2: {11, 20}, - }, - ), - }, - maxSize: 50, // less than what can fit a single commit report for single chain - expected: exectypes.Observation{ - CommitReports: map[cciptypes.ChainSelector][]exectypes.CommitData{}, - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - tt.observation.TokenData = makeNoopTokenDataObservations(tt.observation.Messages) - tt.expected.TokenData = tt.observation.TokenData - obs, err := truncateObservation(tt.observation, tt.maxSize) - if tt.wantErr { - require.Error(t, err) - return - } - require.Equal(t, tt.expected.CommitReports, obs.CommitReports) - - // Check only deleted messages were deleted - for chain, seqNums := range obs.Messages { - for seqNum := range seqNums { - if _, ok := tt.deletedMsgs[chain]; ok { - if _, ok2 := tt.deletedMsgs[chain][seqNum]; ok2 { - require.True(t, obs.Messages[chain][seqNum].IsEmpty()) - continue - } - } - require.False(t, obs.Messages[chain][seqNum].IsEmpty()) - } - } - }) - } -} diff --git a/execute/test_utils.go b/execute/test_utils.go index 518341ebf..2238a6f7e 100644 --- a/execute/test_utils.go +++ b/execute/test_utils.go @@ -461,43 +461,6 @@ func makeMsgWithMetadata( } } -func makeMessageObservation( - srcToSeqNumRange map[cciptypes.ChainSelector]cciptypes.SeqNumRange, - opts ...msgOption) exectypes.MessageObservations { - - obs := make(exectypes.MessageObservations) - for src, seqNumRng := range srcToSeqNumRange { - obs[src] = make(map[cciptypes.SeqNum]cciptypes.Message) - for i := seqNumRng.Start(); i <= seqNumRng.End(); i++ { - msg := cciptypes.Message{ - Header: cciptypes.RampMessageHeader{ - SourceChainSelector: src, - SequenceNumber: i, - MessageID: rand.RandomBytes32(), - }, - FeeValueJuels: cciptypes.NewBigIntFromInt64(100), - } - for _, opt := range opts { - opt(&msg) - } - obs[src][i] = msg - } - } - return obs -} - -func makeNoopTokenDataObservations(msgs exectypes.MessageObservations) exectypes.TokenDataObservations { - tokenData := make(exectypes.TokenDataObservations) - for src, seqNumToMsg := range msgs { - tokenData[src] = make(map[cciptypes.SeqNum]exectypes.MessageTokenData) - for seq := range seqNumToMsg { - tokenData[src][seq] = exectypes.NewMessageTokenData() - } - } - return tokenData - -} - type nodeSetup struct { node *Plugin reportCodec cciptypes.ExecutePluginCodec From bf224dc36b842c635e2849e2c95d3780929c48c5 Mon Sep 17 00:00:00 2001 From: Will Winder Date: Fri, 20 Dec 2024 12:25:40 -0500 Subject: [PATCH 05/10] Check that fChain(dest) exists before using it. (#398) --- internal/plugincommon/discovery/processor.go | 37 +++++++++++--------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/internal/plugincommon/discovery/processor.go b/internal/plugincommon/discovery/processor.go index 8e17d80d3..5dc23d992 100644 --- a/internal/plugincommon/discovery/processor.go +++ b/internal/plugincommon/discovery/processor.go @@ -218,6 +218,7 @@ func (cdp *ContractDiscoveryProcessor) Outcome( ctx context.Context, _ dt.Outcome, _ dt.Query, aos []plugincommon.AttributedObservation[dt.Observation], ) (dt.Outcome, error) { cdp.lggr.Infow("Processing contract discovery outcome", "observations", aos) + contracts := make(reader.ContractAddresses) agg := aggregateObservations(cdp.lggr, cdp.dest, aos) @@ -225,24 +226,28 @@ func (cdp *ContractDiscoveryProcessor) Outcome( donThresh := consensus.MakeConstantThreshold[cciptypes.ChainSelector](consensus.TwoFPlus1(cdp.fRoleDON)) fChain := consensus.GetConsensusMap(cdp.lggr, "fChain", agg.fChain, donThresh) fChainThresh := consensus.MakeMultiThreshold(fChain, consensus.TwoFPlus1) - destThresh := consensus.MakeConstantThreshold[cciptypes.ChainSelector](consensus.TwoFPlus1(fChain[cdp.dest])) - contracts := make(reader.ContractAddresses) - onrampConsensus := consensus.GetConsensusMap( - cdp.lggr, - "onramp", - agg.onrampAddrs, - destThresh, - ) - cdp.lggr.Infow("Determined consensus onramps", - "onrampConsensus", onrampConsensus, - "onrampAddrs", agg.onrampAddrs, - "fChainThresh", fChainThresh, - ) - if len(onrampConsensus) == 0 { - cdp.lggr.Debugw("No consensus on onramps, onrampConsensus map is empty") + if _, exists := fChain[cdp.dest]; !exists { + cdp.lggr.Warnw("missing fChain for dest (fChain[%d]), skipping onramp address lookup", cdp.dest) + } else { + destThresh := consensus.MakeConstantThreshold[cciptypes.ChainSelector](consensus.TwoFPlus1(fChain[cdp.dest])) + + onrampConsensus := consensus.GetConsensusMap( + cdp.lggr, + "onramp", + agg.onrampAddrs, + destThresh, + ) + cdp.lggr.Infow("Determined consensus onramps", + "onrampConsensus", onrampConsensus, + "onrampAddrs", agg.onrampAddrs, + "fChainThresh", fChainThresh, + ) + if len(onrampConsensus) == 0 { + cdp.lggr.Debugw("No consensus on onramps, onrampConsensus map is empty") + } + contracts[consts.ContractNameOnRamp] = onrampConsensus } - contracts[consts.ContractNameOnRamp] = onrampConsensus nonceManagerConsensus := consensus.GetConsensusMap( cdp.lggr, From 8f67f1ca7f0df8699b19d2ec3f6cb47ba2b288dc Mon Sep 17 00:00:00 2001 From: Balamurali Gopalswami <167726375+b-gopalswami@users.noreply.github.com> Date: Fri, 20 Dec 2024 12:56:20 -0500 Subject: [PATCH 06/10] CCIP-4712: Adding nil check to address CL33-16 finding (#399) --- pkg/reader/ccip.go | 7 +++-- pkg/reader/price_reader.go | 52 +++++++++++++++++++++++++++++--------- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/pkg/reader/ccip.go b/pkg/reader/ccip.go index d036f9b1e..e1a9fb2e8 100644 --- a/pkg/reader/ccip.go +++ b/pkg/reader/ccip.go @@ -1028,13 +1028,16 @@ func (r *ccipChainReader) getFeeQuoterTokenPriceUSD(ctx context.Context, tokenAd ) if err != nil { - return cciptypes.BigInt{}, fmt.Errorf("failed to get LINK token price, addr: %v, err: %w", tokenAddr, err) + return cciptypes.BigInt{}, fmt.Errorf("failed to get token price, addr: %v, err: %w", tokenAddr, err) } price := timestampedPrice.Value + if price == nil { + return cciptypes.BigInt{}, fmt.Errorf("token price is nil, addr: %v", tokenAddr) + } if price.Cmp(big.NewInt(0)) == 0 { - return cciptypes.BigInt{}, fmt.Errorf("LINK token price is 0, addr: %v", tokenAddr) + return cciptypes.BigInt{}, fmt.Errorf("token price is 0, addr: %v", tokenAddr) } return cciptypes.NewBigInt(price), nil diff --git a/pkg/reader/price_reader.go b/pkg/reader/price_reader.go index e9359d318..113204cec 100644 --- a/pkg/reader/price_reader.go +++ b/pkg/reader/price_reader.go @@ -177,23 +177,15 @@ func (pr *priceReader) GetFeedPricesUSD( } // Get price data - priceResult, err := contractResults[0].GetResult() + latestRoundData, err := pr.getPriceData(contractResults[0], boundContract) if err != nil { - return nil, fmt.Errorf("get price for contract %s: %w", boundContract.Address, err) - } - latestRoundData, ok := priceResult.(*LatestRoundData) - if !ok { - return nil, fmt.Errorf("invalid price data type for contract %s", boundContract.Address) + return nil, err } // Get decimals - decimalResult, err := contractResults[1].GetResult() + decimals, err := pr.getDecimals(contractResults[1], boundContract) if err != nil { - return nil, fmt.Errorf("get decimals for contract %s: %w", boundContract.Address, err) - } - decimals, ok := decimalResult.(*uint8) - if !ok { - return nil, fmt.Errorf("invalid decimals data type for contract %s", boundContract.Address) + return nil, err } // Normalize price for this contract @@ -215,6 +207,42 @@ func (pr *priceReader) GetFeedPricesUSD( return prices, nil } +func (pr *priceReader) getPriceData( + result commontypes.BatchReadResult, + boundContract commontypes.BoundContract, +) (*LatestRoundData, error) { + priceResult, err := result.GetResult() + if err != nil { + return nil, fmt.Errorf("get price for contract %s: %w", boundContract.Address, err) + } + if priceResult == nil { + return nil, fmt.Errorf("priceResult value is nil for contract %s", boundContract.Address) + } + latestRoundData, ok := priceResult.(*LatestRoundData) + if !ok { + return nil, fmt.Errorf("invalid price data type for contract %s", boundContract.Address) + } + return latestRoundData, nil +} + +func (pr *priceReader) getDecimals( + result commontypes.BatchReadResult, + boundContract commontypes.BoundContract, +) (*uint8, error) { + decimalResult, err := result.GetResult() + if err != nil { + return nil, fmt.Errorf("get decimals for contract %s: %w", boundContract.Address, err) + } + if decimalResult == nil { + return nil, fmt.Errorf("decimalResult value is nil for contract %s", boundContract.Address) + } + decimals, ok := decimalResult.(*uint8) + if !ok { + return nil, fmt.Errorf("invalid decimals data type for contract %s", boundContract.Address) + } + return decimals, nil +} + // prepareBatchRequest creates a batch request grouped by contract and returns the mapping of contracts to token indices func (pr *priceReader) prepareBatchRequest( tokens []ccipocr3.UnknownEncodedAddress, From 334302138062d4b35c7131d12664074f8917f838 Mon Sep 17 00:00:00 2001 From: Aaron Lu <50029043+aalu1418@users.noreply.github.com> Date: Fri, 20 Dec 2024 11:48:11 -0700 Subject: [PATCH 07/10] solana: reorganize utility functions (#388) * solana: reorganize utility functions * remove testing lib from non-test file functions --- .../contracts/tests/ccip/ccip_router_test.go | 1072 +++++++++-------- .../contracts/tests/ccip/tokenpool_test.go | 104 +- ...r_config.go => accesscontroller_config.go} | 0 .../contracts/tests/config/ccip_config.go | 6 +- .../solana/contracts/tests/config/config.go | 41 - .../contracts/tests/lookuptable_test.go | 15 +- .../tests/mcms/mcm_multiple_multisigs_test.go | 73 +- .../tests/mcms/mcm_set_config_test.go | 187 +-- .../tests/mcms/mcm_set_root_execute_test.go | 183 +-- .../contracts/tests/mcms/mcm_timelock_test.go | 481 ++++---- .../mcms/timelock_bypasser_execute_test.go | 65 +- .../tests/mcms/timelock_rbac_test.go | 118 +- .../mcms/timelock_schedule_execute_test.go | 151 +-- .../tests/{utils => testutils}/anchor.go | 126 +- .../{utils => testutils}/localvalidator.go | 2 +- .../tests/{utils => testutils}/ocr.go | 4 +- .../{utils => testutils}/project_path.go | 2 +- .../contracts/tests/testutils/wrapped.go | 83 ++ .../solana/contracts/tests/txsizing_test.go | 10 +- .../contracts/tests/utils/offchainConfig.go | 9 - .../accesscontroller/access_controller.go | 4 +- .../tests => utils}/ccip/ccip_errors.go | 2 +- .../tests => utils}/ccip/ccip_events.go | 2 +- .../tests => utils}/ccip/ccip_messages.go | 30 +- .../ccip/ccip_messages_test.go | 2 +- .../tests => utils}/ccip/ccip_transactions.go | 16 +- chains/solana/utils/common/common.go | 68 ++ chains/solana/utils/common/event.go | 71 ++ .../tests/utils => utils/common}/logparser.go | 2 +- .../utils => utils/common}/logparser_test.go | 2 +- .../utils => utils/common}/lookuptable.go | 26 +- .../utils => utils/common}/transactions.go | 187 +-- .../{contracts/tests => }/utils/eth/signer.go | 0 .../tests => }/utils/fees/computebudget.go | 0 .../utils/fees/computebudget_test.go | 0 .../tests => }/utils/mcms/common.go | 2 +- .../tests => }/utils/mcms/common_test.go | 0 .../{contracts/tests => utils}/mcms/mcm.go | 25 +- .../tests => utils}/mcms/mcm_errors.go | 2 +- .../tests => utils}/mcms/mcm_events.go | 2 +- .../tests => }/utils/mcms/merkle.go | 2 +- .../tests => }/utils/mcms/merkle_test.go | 0 .../mcms/mcm.go => utils/mcms/multisig.go} | 2 +- .../timelock/accounts.go} | 34 +- .../tests/mcms => utils/timelock}/timelock.go | 32 +- .../timelock}/timelock_errors.go | 10 +- .../timelock}/timelock_events.go | 2 +- .../timelock}/timelock_operations.go | 34 +- .../tests/utils => utils/tokens}/token.go | 2 +- .../tests/ccip => utils/tokens}/tokenpool.go | 17 +- 50 files changed, 1696 insertions(+), 1614 deletions(-) rename chains/solana/contracts/tests/config/{access_controller_config.go => accesscontroller_config.go} (100%) delete mode 100644 chains/solana/contracts/tests/config/config.go rename chains/solana/contracts/tests/{utils => testutils}/anchor.go (51%) rename chains/solana/contracts/tests/{utils => testutils}/localvalidator.go (99%) rename chains/solana/contracts/tests/{utils => testutils}/ocr.go (93%) rename chains/solana/contracts/tests/{utils => testutils}/project_path.go (94%) create mode 100644 chains/solana/contracts/tests/testutils/wrapped.go delete mode 100644 chains/solana/contracts/tests/utils/offchainConfig.go rename chains/solana/{contracts/tests => utils}/accesscontroller/access_controller.go (88%) rename chains/solana/{contracts/tests => utils}/ccip/ccip_errors.go (99%) rename chains/solana/{contracts/tests => utils}/ccip/ccip_events.go (98%) rename chains/solana/{contracts/tests => utils}/ccip/ccip_messages.go (86%) rename chains/solana/{contracts/tests => utils}/ccip/ccip_messages_test.go (98%) rename chains/solana/{contracts/tests => utils}/ccip/ccip_transactions.go (81%) create mode 100644 chains/solana/utils/common/common.go create mode 100644 chains/solana/utils/common/event.go rename chains/solana/{contracts/tests/utils => utils/common}/logparser.go (99%) rename chains/solana/{contracts/tests/utils => utils/common}/logparser_test.go (99%) rename chains/solana/{contracts/tests/utils => utils/common}/lookuptable.go (83%) rename chains/solana/{contracts/tests/utils => utils/common}/transactions.go (51%) rename chains/solana/{contracts/tests => }/utils/eth/signer.go (100%) rename chains/solana/{contracts/tests => }/utils/fees/computebudget.go (100%) rename chains/solana/{contracts/tests => }/utils/fees/computebudget_test.go (100%) rename chains/solana/{contracts/tests => }/utils/mcms/common.go (97%) rename chains/solana/{contracts/tests => }/utils/mcms/common_test.go (100%) rename chains/solana/{contracts/tests => utils}/mcms/mcm.go (91%) rename chains/solana/{contracts/tests => utils}/mcms/mcm_errors.go (95%) rename chains/solana/{contracts/tests => utils}/mcms/mcm_events.go (98%) rename chains/solana/{contracts/tests => }/utils/mcms/merkle.go (99%) rename chains/solana/{contracts/tests => }/utils/mcms/merkle_test.go (100%) rename chains/solana/{contracts/tests/utils/mcms/mcm.go => utils/mcms/multisig.go} (97%) rename chains/solana/{contracts/tests/utils/mcms/timelock.go => utils/timelock/accounts.go} (61%) rename chains/solana/{contracts/tests/mcms => utils/timelock}/timelock.go (84%) rename chains/solana/{contracts/tests/mcms => utils/timelock}/timelock_errors.go (56%) rename chains/solana/{contracts/tests/mcms => utils/timelock}/timelock_events.go (98%) rename chains/solana/{contracts/tests/mcms => utils/timelock}/timelock_operations.go (77%) rename chains/solana/{contracts/tests/utils => utils/tokens}/token.go (99%) rename chains/solana/{contracts/tests/ccip => utils/tokens}/tokenpool.go (91%) diff --git a/chains/solana/contracts/tests/ccip/ccip_router_test.go b/chains/solana/contracts/tests/ccip/ccip_router_test.go index df79eab8b..60358b0d4 100644 --- a/chains/solana/contracts/tests/ccip/ccip_router_test.go +++ b/chains/solana/contracts/tests/ccip/ccip_router_test.go @@ -17,10 +17,13 @@ import ( "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_receiver" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/token_pool" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/ccip" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens" ) const MaxCU = 1_400_000 // this is Solana's hard max Compute Unit limit @@ -67,15 +70,15 @@ func TestCCIPRouter(t *testing.T) { token2022 := AccountsPerToken{name: "Token2022 sample token"} billingTokens := []*AccountsPerToken{&wsol, &token2022} - solanaGoClient := utils.DeployAllPrograms(t, utils.PathToAnchorConfig, admin) + solanaGoClient := testutils.DeployAllPrograms(t, testutils.PathToAnchorConfig, admin) // token addresses - token0, gerr := NewTokenPool(config.Token2022Program) + token0, gerr := tokens.NewTokenPool(config.Token2022Program) require.NoError(t, gerr) - token1, gerr := NewTokenPool(config.Token2022Program) + token1, gerr := tokens.NewTokenPool(config.Token2022Program) require.NoError(t, gerr) - signers, transmitters, getTransmitter := utils.GenerateSignersAndTransmitters(t, config.MaxOracles) + signers, transmitters, getTransmitter := testutils.GenerateSignersAndTransmitters(t, config.MaxOracles) signerAddresses := [][20]byte{} transmitterPubKeys := []solana.PublicKey{} @@ -94,7 +97,7 @@ func TestCCIPRouter(t *testing.T) { }) getBalance := func(tokenAccount solana.PublicKey) uint64 { - _, amount, berr := utils.TokenBalance(ctx, solanaGoClient, tokenAccount, config.DefaultCommitment) + _, amount, berr := tokens.TokenBalance(ctx, solanaGoClient, tokenAccount, config.DefaultCommitment) require.NoError(t, berr) return uint64(amount) } @@ -127,7 +130,7 @@ func TestCCIPRouter(t *testing.T) { t.Run("setup", func(t *testing.T) { t.Run("funding", func(t *testing.T) { - utils.FundAccounts(ctx, append(transmitters, user, anotherUser, tokenlessUser, admin, anotherAdmin, tokenPoolAdmin, anotherTokenPoolAdmin), solanaGoClient, t) + testutils.FundAccounts(ctx, append(transmitters, user, anotherUser, tokenlessUser, admin, anotherAdmin, tokenPoolAdmin, anotherTokenPoolAdmin), solanaGoClient, t) }) t.Run("receiver", func(t *testing.T) { @@ -138,40 +141,40 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, ixErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) }) t.Run("token", func(t *testing.T) { - ixs, ixErr := utils.CreateToken(ctx, token0.Program, token0.Mint.PublicKey(), tokenPoolAdmin.PublicKey(), 0, solanaGoClient, config.DefaultCommitment) + ixs, ixErr := tokens.CreateToken(ctx, token0.Program, token0.Mint.PublicKey(), tokenPoolAdmin.PublicKey(), 0, solanaGoClient, config.DefaultCommitment) require.NoError(t, ixErr) - ixsAnotherToken, anotherTokenErr := utils.CreateToken(ctx, token1.Program, token1.Mint.PublicKey(), anotherTokenPoolAdmin.PublicKey(), 0, solanaGoClient, config.DefaultCommitment) + ixsAnotherToken, anotherTokenErr := tokens.CreateToken(ctx, token1.Program, token1.Mint.PublicKey(), anotherTokenPoolAdmin.PublicKey(), 0, solanaGoClient, config.DefaultCommitment) require.NoError(t, anotherTokenErr) // mint tokens to user - ixAta, addr, ataErr := utils.CreateAssociatedTokenAccount(token0.Program, token0.Mint.PublicKey(), user.PublicKey(), tokenPoolAdmin.PublicKey()) + ixAta, addr, ataErr := tokens.CreateAssociatedTokenAccount(token0.Program, token0.Mint.PublicKey(), user.PublicKey(), tokenPoolAdmin.PublicKey()) require.NoError(t, ataErr) - ixMintTo, mintErr := utils.MintTo(10000000, token0.Program, token0.Mint.PublicKey(), addr, tokenPoolAdmin.PublicKey()) + ixMintTo, mintErr := tokens.MintTo(10000000, token0.Program, token0.Mint.PublicKey(), addr, tokenPoolAdmin.PublicKey()) require.NoError(t, mintErr) // create ATA for receiver (receiver program address) - ixAtaReceiver, recAddr, recErr := utils.CreateAssociatedTokenAccount(token0.Program, token0.Mint.PublicKey(), config.ReceiverExternalExecutionConfigPDA, tokenPoolAdmin.PublicKey()) + ixAtaReceiver, recAddr, recErr := tokens.CreateAssociatedTokenAccount(token0.Program, token0.Mint.PublicKey(), config.ReceiverExternalExecutionConfigPDA, tokenPoolAdmin.PublicKey()) require.NoError(t, recErr) token0.User[user.PublicKey()] = addr token0.User[config.ReceiverExternalExecutionConfigPDA] = recAddr ixs = append(ixs, ixAta, ixMintTo, ixAtaReceiver) ixs = append(ixs, ixsAnotherToken...) - utils.SendAndConfirm(ctx, t, solanaGoClient, ixs, tokenPoolAdmin, config.DefaultCommitment, utils.AddSigners(token0.Mint, token1.Mint, anotherTokenPoolAdmin)) + testutils.SendAndConfirm(ctx, t, solanaGoClient, ixs, tokenPoolAdmin, config.DefaultCommitment, common.AddSigners(token0.Mint, token1.Mint, anotherTokenPoolAdmin)) }) t.Run("token-pool", func(t *testing.T) { token0.PoolProgram = config.CcipTokenPoolProgram token0.AdditionalAccounts = append(token0.AdditionalAccounts, solana.MemoProgramID) // add test additional accounts in pool interactions var err error - token0.PoolConfig, err = TokenPoolConfigAddress(token0.Mint.PublicKey()) + token0.PoolConfig, err = tokens.TokenPoolConfigAddress(token0.Mint.PublicKey()) require.NoError(t, err) - token0.PoolSigner, err = TokenPoolSignerAddress(token0.Mint.PublicKey()) + token0.PoolSigner, err = tokens.TokenPoolSignerAddress(token0.Mint.PublicKey()) require.NoError(t, err) ixInit, err := token_pool.NewInitializeInstruction( @@ -185,29 +188,29 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - ixAta, addr, err := utils.CreateAssociatedTokenAccount(token0.Program, token0.Mint.PublicKey(), token0.PoolSigner, tokenPoolAdmin.PublicKey()) + ixAta, addr, err := tokens.CreateAssociatedTokenAccount(token0.Program, token0.Mint.PublicKey(), token0.PoolSigner, tokenPoolAdmin.PublicKey()) require.NoError(t, err) token0.PoolTokenAccount = addr token0.User[token0.PoolSigner] = token0.PoolTokenAccount - ixAuth, err := utils.SetTokenMintAuthority(token0.Program, token0.PoolSigner, token0.Mint.PublicKey(), tokenPoolAdmin.PublicKey()) + ixAuth, err := tokens.SetTokenMintAuthority(token0.Program, token0.PoolSigner, token0.Mint.PublicKey(), tokenPoolAdmin.PublicKey()) require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixInit, ixAta, ixAuth}, tokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixInit, ixAta, ixAuth}, tokenPoolAdmin, config.DefaultCommitment) // Lookup Table for Tokens - require.NoError(t, token0.SetupLookupTable(ctx, t, solanaGoClient, tokenPoolAdmin)) + require.NoError(t, token0.SetupLookupTable(ctx, solanaGoClient, tokenPoolAdmin)) token0Entries := token0.ToTokenPoolEntries() - require.NoError(t, token1.SetupLookupTable(ctx, t, solanaGoClient, anotherTokenPoolAdmin)) + require.NoError(t, token1.SetupLookupTable(ctx, solanaGoClient, anotherTokenPoolAdmin)) token1Entries := token1.ToTokenPoolEntries() // Verify Lookup tables where correctly initialized - lookupTableEntries0, err := utils.GetAddressLookupTable(ctx, solanaGoClient, token0.PoolLookupTable) + lookupTableEntries0, err := common.GetAddressLookupTable(ctx, solanaGoClient, token0.PoolLookupTable) require.NoError(t, err) require.Equal(t, len(token0Entries), len(lookupTableEntries0)) require.Equal(t, token0Entries, lookupTableEntries0) - lookupTableEntries1, err := utils.GetAddressLookupTable(ctx, solanaGoClient, token1.PoolLookupTable) + lookupTableEntries1, err := common.GetAddressLookupTable(ctx, solanaGoClient, token1.PoolLookupTable) require.NoError(t, err) require.Equal(t, len(token1Entries), len(lookupTableEntries1)) require.Equal(t, token1Entries, lookupTableEntries1) @@ -220,13 +223,13 @@ func TestCCIPRouter(t *testing.T) { wsolPDA, _, aerr := solana.FindProgramAddress([][]byte{config.BillingTokenConfigPrefix, solana.SolMint.Bytes()}, ccip_router.ProgramID) require.NoError(t, aerr) - wsolReceiver, _, rerr := utils.FindAssociatedTokenAddress(solana.TokenProgramID, solana.SolMint, config.BillingSignerPDA) + wsolReceiver, _, rerr := tokens.FindAssociatedTokenAddress(solana.TokenProgramID, solana.SolMint, config.BillingSignerPDA) require.NoError(t, rerr) - wsolUserATA, _, uerr := utils.FindAssociatedTokenAddress(solana.TokenProgramID, solana.SolMint, user.PublicKey()) + wsolUserATA, _, uerr := tokens.FindAssociatedTokenAddress(solana.TokenProgramID, solana.SolMint, user.PublicKey()) require.NoError(t, uerr) - wsolAnotherUserATA, _, auerr := utils.FindAssociatedTokenAddress(solana.TokenProgramID, solana.SolMint, anotherUser.PublicKey()) + wsolAnotherUserATA, _, auerr := tokens.FindAssociatedTokenAddress(solana.TokenProgramID, solana.SolMint, anotherUser.PublicKey()) require.NoError(t, auerr) - wsolTokenlessUserATA, _, tuerr := utils.FindAssociatedTokenAddress(solana.TokenProgramID, solana.SolMint, tokenlessUser.PublicKey()) + wsolTokenlessUserATA, _, tuerr := tokens.FindAssociatedTokenAddress(solana.TokenProgramID, solana.SolMint, tokenlessUser.PublicKey()) require.NoError(t, tuerr) // persist the WSOL config for later use @@ -247,19 +250,19 @@ func TestCCIPRouter(t *testing.T) { mintPrivK := solana.MustPrivateKeyFromBase58("32YVeJArcWWWV96fztfkRQhohyFz5Hwno93AeGVrN4g2LuFyvwznrNd9A6tbvaTU6BuyBsynwJEMLre8vSy3CrVU") mintPubK := mintPrivK.PublicKey() - ixToken, terr := utils.CreateToken(ctx, config.Token2022Program, mintPubK, admin.PublicKey(), 9, solanaGoClient, config.DefaultCommitment) + ixToken, terr := tokens.CreateToken(ctx, config.Token2022Program, mintPubK, admin.PublicKey(), 9, solanaGoClient, config.DefaultCommitment) require.NoError(t, terr) - utils.SendAndConfirm(ctx, t, solanaGoClient, ixToken, admin, config.DefaultCommitment, utils.AddSigners(mintPrivK)) + testutils.SendAndConfirm(ctx, t, solanaGoClient, ixToken, admin, config.DefaultCommitment, common.AddSigners(mintPrivK)) token2022PDA, _, aerr := solana.FindProgramAddress([][]byte{config.BillingTokenConfigPrefix, mintPubK.Bytes()}, ccip_router.ProgramID) require.NoError(t, aerr) - token2022Receiver, _, rerr := utils.FindAssociatedTokenAddress(config.Token2022Program, mintPubK, config.BillingSignerPDA) + token2022Receiver, _, rerr := tokens.FindAssociatedTokenAddress(config.Token2022Program, mintPubK, config.BillingSignerPDA) require.NoError(t, rerr) - token2022UserATA, _, uerr := utils.FindAssociatedTokenAddress(config.Token2022Program, mintPubK, user.PublicKey()) + token2022UserATA, _, uerr := tokens.FindAssociatedTokenAddress(config.Token2022Program, mintPubK, user.PublicKey()) require.NoError(t, uerr) - token2022AnotherUserATA, _, auerr := utils.FindAssociatedTokenAddress(config.Token2022Program, mintPubK, anotherUser.PublicKey()) + token2022AnotherUserATA, _, auerr := tokens.FindAssociatedTokenAddress(config.Token2022Program, mintPubK, anotherUser.PublicKey()) require.NoError(t, auerr) - token2022TokenlessUserATA, _, tuerr := utils.FindAssociatedTokenAddress(config.Token2022Program, mintPubK, tokenlessUser.PublicKey()) + token2022TokenlessUserATA, _, tuerr := tokens.FindAssociatedTokenAddress(config.Token2022Program, mintPubK, tokenlessUser.PublicKey()) require.NoError(t, tuerr) // persist the Token2022 billing config for later use @@ -292,7 +295,7 @@ func TestCCIPRouter(t *testing.T) { config.EvmDestChainStatePDA, // to update prices config.SolanaDestChainStatePDA, } - lookupTableAddr, err := utils.SetupLookupTable(ctx, t, solanaGoClient, admin, lookupEntries) + lookupTableAddr, err := common.SetupLookupTable(ctx, solanaGoClient, admin, lookupEntries) require.NoError(t, err) commitLookupTable = map[solana.PublicKey]solana.PublicKeySlice{ @@ -340,12 +343,12 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) // Fetch account data var configAccount ccip_router.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -353,7 +356,7 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, defaultGasLimit, configAccount.DefaultGasLimit) require.Equal(t, uint8(1), configAccount.DefaultAllowOutOfOrderExecution) - nonceEvmPDA, err = getNoncePDA(config.EvmChainSelector, user.PublicKey()) + nonceEvmPDA, err = ccip.GetNoncePDA(config.EvmChainSelector, user.PublicKey()) require.NoError(t, err) }) @@ -367,11 +370,11 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) var configAccount ccip_router.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(17), configAccount.SolanaChainSelector) require.Equal(t, newGasLimit, configAccount.DefaultGasLimit) @@ -386,11 +389,11 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) var configAccount ccip_router.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(17), configAccount.SolanaChainSelector) require.Equal(t, bin.Uint128{Lo: 5000, Hi: 0}, configAccount.DefaultGasLimit) @@ -405,11 +408,11 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) var configAccount ccip_router.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.SolanaChainSelector, configAccount.SolanaChainSelector) require.Equal(t, bin.Uint128{Lo: 5000, Hi: 0}, configAccount.DefaultGasLimit) @@ -461,9 +464,9 @@ func TestCCIPRouter(t *testing.T) { t.Run("When and admin adds a chain selector with invalid dest chain config, it fails", func(t *testing.T) { for _, test := range invalidInputTests { t.Run(test.Name, func(t *testing.T) { - sourceChainStatePDA, serr := GetSourceChainStatePDA(test.Selector) + sourceChainStatePDA, serr := ccip.GetSourceChainStatePDA(test.Selector) require.NoError(t, serr) - destChainStatePDA, derr := GetDestChainStatePDA(test.Selector) + destChainStatePDA, derr := ccip.GetDestChainStatePDA(test.Selector) require.NoError(t, derr) instruction, err := ccip_router.NewAddChainSelectorInstruction( test.Selector, @@ -476,7 +479,7 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidInputs_CcipRouterError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidInputs_CcipRouterError.String()}) require.NotNil(t, result) }) } @@ -494,7 +497,7 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) require.NotNil(t, result) }) @@ -510,18 +513,18 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) var sourceChainStateAccount ccip_router.SourceChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &sourceChainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &sourceChainStateAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(1), sourceChainStateAccount.State.MinSeqNr) require.Equal(t, true, sourceChainStateAccount.Config.IsEnabled) require.Equal(t, config.OnRampAddress, sourceChainStateAccount.Config.OnRamp) var destChainStateAccount ccip_router.DestChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &destChainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &destChainStateAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(0), destChainStateAccount.State.SequenceNumber) require.Equal(t, validDestChainConfig, destChainStateAccount.Config) @@ -549,18 +552,18 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) var sourceChainStateAccount ccip_router.SourceChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.SolanaSourceChainStatePDA, config.DefaultCommitment, &sourceChainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.SolanaSourceChainStatePDA, config.DefaultCommitment, &sourceChainStateAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(1), sourceChainStateAccount.State.MinSeqNr) require.Equal(t, true, sourceChainStateAccount.Config.IsEnabled) require.Equal(t, config.CcipRouterProgram[:], sourceChainStateAccount.Config.OnRamp) var destChainStateAccount ccip_router.DestChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.SolanaDestChainStatePDA, config.DefaultCommitment, &destChainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.SolanaDestChainStatePDA, config.DefaultCommitment, &destChainStateAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(0), destChainStateAccount.State.SequenceNumber) }) @@ -574,7 +577,7 @@ func TestCCIPRouter(t *testing.T) { user.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) require.NotNil(t, result) }) @@ -586,7 +589,7 @@ func TestCCIPRouter(t *testing.T) { user.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) require.NotNil(t, result) }) }) @@ -594,7 +597,7 @@ func TestCCIPRouter(t *testing.T) { t.Run("When an admin disables the chain selector, it is no longer enabled", func(t *testing.T) { t.Run("Source", func(t *testing.T) { var initial ccip_router.SourceChain - err := utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &initial) + err := common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &initial) require.NoError(t, err, "failed to get account info") require.Equal(t, true, initial.Config.IsEnabled) @@ -605,17 +608,17 @@ func TestCCIPRouter(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) var final ccip_router.SourceChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &final) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &final) require.NoError(t, err, "failed to get account info") require.Equal(t, false, final.Config.IsEnabled) }) t.Run("Dest", func(t *testing.T) { var initial ccip_router.DestChain - err := utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &initial) + err := common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &initial) require.NoError(t, err, "failed to get account info") require.Equal(t, true, initial.Config.IsEnabled) @@ -626,10 +629,10 @@ func TestCCIPRouter(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) var final ccip_router.DestChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &final) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &final) require.NoError(t, err, "failed to get account info") require.Equal(t, false, final.Config.IsEnabled) }) @@ -641,7 +644,7 @@ func TestCCIPRouter(t *testing.T) { continue } t.Run(test.Name, func(t *testing.T) { - destChainStatePDA, derr := GetDestChainStatePDA(test.Selector) + destChainStatePDA, derr := ccip.GetDestChainStatePDA(test.Selector) require.NoError(t, derr) instruction, err := ccip_router.NewUpdateDestChainConfigInstruction( test.Selector, @@ -651,7 +654,7 @@ func TestCCIPRouter(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidInputs_CcipRouterError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidInputs_CcipRouterError.String()}) require.NotNil(t, result) }) } @@ -667,7 +670,7 @@ func TestCCIPRouter(t *testing.T) { user.PublicKey(), // unauthorized ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) require.NotNil(t, result) }) @@ -680,14 +683,14 @@ func TestCCIPRouter(t *testing.T) { user.PublicKey(), // unauthorized ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) require.NotNil(t, result) }) }) t.Run("When an admin updates the chain state config, it is configured", func(t *testing.T) { var initialSource ccip_router.SourceChain - serr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &initialSource) + serr := common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &initialSource) require.NoError(t, serr, "failed to get account info") t.Run("Source", func(t *testing.T) { @@ -703,17 +706,17 @@ func TestCCIPRouter(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) var final ccip_router.SourceChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &final) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &final) require.NoError(t, err, "failed to get account info") require.Equal(t, updated, final.Config) }) var initialDest ccip_router.DestChain - derr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &initialDest) + derr := common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &initialDest) require.NoError(t, derr, "failed to get account info") t.Run("Dest", func(t *testing.T) { @@ -729,11 +732,11 @@ func TestCCIPRouter(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) var final ccip_router.DestChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &final) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &final) require.NoError(t, err, "failed to get account info") require.Equal(t, updated, final.Config) }) @@ -747,7 +750,7 @@ func TestCCIPRouter(t *testing.T) { user.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) require.NotNil(t, result) // successfully transfer ownership @@ -757,7 +760,7 @@ func TestCCIPRouter(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) // Fail to accept ownership when not proposed_owner @@ -766,7 +769,7 @@ func TestCCIPRouter(t *testing.T) { user.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result = utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) + result = testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) require.NotNil(t, result) // Successfully accept ownership @@ -776,7 +779,7 @@ func TestCCIPRouter(t *testing.T) { anotherAdmin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) + result = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) require.NotNil(t, result) // Current owner cannot propose self @@ -786,12 +789,12 @@ func TestCCIPRouter(t *testing.T) { anotherAdmin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result = utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidInputs_CcipRouterError.String()}) + result = testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidInputs_CcipRouterError.String()}) require.NotNil(t, result) // Validate proposed set to 0-address var configAccount ccip_router.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -851,12 +854,12 @@ func TestCCIPRouter(t *testing.T) { token.Accounts.billingATA, anotherAdmin.PublicKey(), config.BillingSignerPDA, - utils.AssociatedTokenProgramID, + tokens.AssociatedTokenProgramID, solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, cerr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) }) } }) @@ -892,30 +895,30 @@ func TestCCIPRouter(t *testing.T) { for _, it := range list { for _, token := range billingTokens { // create ATA for user - ixAtaUser, addrUser, uerr := utils.CreateAssociatedTokenAccount(token.program, token.mint, it.user.PublicKey(), it.user.PublicKey()) + ixAtaUser, addrUser, uerr := tokens.CreateAssociatedTokenAccount(token.program, token.mint, it.user.PublicKey(), it.user.PublicKey()) require.NoError(t, uerr) require.Equal(t, it.getATA(token), addrUser, fmt.Sprintf("ATA for user %s and token %s", it.name, token.name)) // Approve CCIP to transfer the user's token for billing - ixApprove, aerr := utils.TokenApproveChecked(1e9, 9, token.program, it.getATA(token), token.mint, config.BillingSignerPDA, it.user.PublicKey(), []solana.PublicKey{}) + ixApprove, aerr := tokens.TokenApproveChecked(1e9, 9, token.program, it.getATA(token), token.mint, config.BillingSignerPDA, it.user.PublicKey(), []solana.PublicKey{}) require.NoError(t, aerr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixAtaUser, ixApprove}, it.user, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixAtaUser, ixApprove}, it.user, config.DefaultCommitment) } if it.shouldFund { // fund user token2022 (mint directly to user ATA) - ixMint, merr := utils.MintTo(1e9, token2022.program, token2022.mint, it.getATA(&token2022), admin.PublicKey()) + ixMint, merr := tokens.MintTo(1e9, token2022.program, token2022.mint, it.getATA(&token2022), admin.PublicKey()) require.NoError(t, merr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixMint}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixMint}, admin, config.DefaultCommitment) // fund user WSOL (transfer SOL + syncNative) transferAmount := 1.0 * solana.LAMPORTS_PER_SOL - ixTransfer, terr := utils.NativeTransfer(wsol.program, transferAmount, it.user.PublicKey(), it.getATA(&wsol)) + ixTransfer, terr := tokens.NativeTransfer(wsol.program, transferAmount, it.user.PublicKey(), it.getATA(&wsol)) require.NoError(t, terr) - ixSync, serr := utils.SyncNative(wsol.program, it.getATA(&wsol)) + ixSync, serr := tokens.SyncNative(wsol.program, it.getATA(&wsol)) require.NoError(t, serr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixTransfer, ixSync}, it.user, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixTransfer, ixSync}, it.user, config.DefaultCommitment) } } }) @@ -924,7 +927,7 @@ func TestCCIPRouter(t *testing.T) { t.Run("Pre-condition: Does not support token0 by default", func(t *testing.T) { token0BillingPDA := getTokenConfigPDA(token0.Mint.PublicKey()) var token0ConfigAccount ccip_router.BillingTokenConfigWrapper - err := utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &token0ConfigAccount) + err := common.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &token0ConfigAccount) require.EqualError(t, err, "not found") }) @@ -937,7 +940,7 @@ func TestCCIPRouter(t *testing.T) { } token0BillingPDA := getTokenConfigPDA(token0.Mint.PublicKey()) - token0Receiver, _, ferr := utils.FindAssociatedTokenAddress(token0.Program, token0.Mint.PublicKey(), config.BillingSignerPDA) + token0Receiver, _, ferr := tokens.FindAssociatedTokenAddress(token0.Program, token0.Mint.PublicKey(), config.BillingSignerPDA) require.NoError(t, ferr) ixConfig, cerr := ccip_router.NewAddBillingTokenConfigInstruction( @@ -949,15 +952,15 @@ func TestCCIPRouter(t *testing.T) { token0Receiver, anotherAdmin.PublicKey(), config.BillingSignerPDA, - utils.AssociatedTokenProgramID, + tokens.AssociatedTokenProgramID, solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, cerr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) var token0ConfigAccount ccip_router.BillingTokenConfigWrapper - aerr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &token0ConfigAccount) + aerr := common.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &token0ConfigAccount) require.NoError(t, aerr) require.Equal(t, token0Config, token0ConfigAccount.Config) @@ -966,7 +969,7 @@ func TestCCIPRouter(t *testing.T) { t.Run("When an unauthorized user updates token0 with correct configuration it fails", func(t *testing.T) { token0BillingPDA := getTokenConfigPDA(token0.Mint.PublicKey()) var initial ccip_router.BillingTokenConfigWrapper - ierr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &initial) + ierr := common.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &initial) require.NoError(t, ierr) token0Config := initial.Config @@ -974,10 +977,10 @@ func TestCCIPRouter(t *testing.T) { ixConfig, cerr := ccip_router.NewUpdateBillingTokenConfigInstruction(token0Config, config.RouterConfigPDA, token0BillingPDA, admin.PublicKey()).ValidateAndBuild() // wrong admin require.NoError(t, cerr) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, admin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, admin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) var final ccip_router.BillingTokenConfigWrapper - ferr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &final) + ferr := common.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &final) require.NoError(t, ferr) require.Equal(t, initial.Config, final.Config) // it was not updated, same values as initial @@ -986,7 +989,7 @@ func TestCCIPRouter(t *testing.T) { t.Run("When admin updates token0 it is updated", func(t *testing.T) { token0BillingPDA := getTokenConfigPDA(token0.Mint.PublicKey()) var initial ccip_router.BillingTokenConfigWrapper - ierr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &initial) + ierr := common.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &initial) require.NoError(t, ierr) token0Config := initial.Config @@ -994,10 +997,10 @@ func TestCCIPRouter(t *testing.T) { ixConfig, cerr := ccip_router.NewUpdateBillingTokenConfigInstruction(token0Config, config.RouterConfigPDA, token0BillingPDA, anotherAdmin.PublicKey()).ValidateAndBuild() require.NoError(t, cerr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) var final ccip_router.BillingTokenConfigWrapper - ferr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, rpc.CommitmentProcessed, &final) + ferr := common.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, rpc.CommitmentProcessed, &final) require.NoError(t, ferr) require.NotEqual(t, initial.Config.PremiumMultiplierWeiPerEth, final.Config.PremiumMultiplierWeiPerEth) // it was updated @@ -1008,10 +1011,10 @@ func TestCCIPRouter(t *testing.T) { token0BillingPDA := getTokenConfigPDA(token0.Mint.PublicKey()) var initial ccip_router.BillingTokenConfigWrapper - ierr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &initial) + ierr := common.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, config.DefaultCommitment, &initial) require.NoError(t, ierr) // it exists, initially - receiver, _, aerr := utils.FindAssociatedTokenAddress(token0.Program, token0.Mint.PublicKey(), config.BillingSignerPDA) + receiver, _, aerr := tokens.FindAssociatedTokenAddress(token0.Program, token0.Mint.PublicKey(), config.BillingSignerPDA) require.NoError(t, aerr) ixConfig, cerr := ccip_router.NewRemoveBillingTokenConfigInstruction( @@ -1025,10 +1028,10 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, cerr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) var final ccip_router.BillingTokenConfigWrapper - ferr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, rpc.CommitmentProcessed, &final) + ferr := common.GetAccountDataBorshInto(ctx, solanaGoClient, token0BillingPDA, rpc.CommitmentProcessed, &final) require.EqualError(t, ferr, "not found") // it no longer exists }) @@ -1038,13 +1041,13 @@ func TestCCIPRouter(t *testing.T) { mint := mintPriv.PublicKey() // use old (pre-2022) token program - ixToken, terr := utils.CreateToken(ctx, solana.TokenProgramID, mint, admin.PublicKey(), 9, solanaGoClient, config.DefaultCommitment) + ixToken, terr := tokens.CreateToken(ctx, solana.TokenProgramID, mint, admin.PublicKey(), 9, solanaGoClient, config.DefaultCommitment) require.NoError(t, terr) - utils.SendAndConfirm(ctx, t, solanaGoClient, ixToken, admin, config.DefaultCommitment, utils.AddSigners(mintPriv)) + testutils.SendAndConfirm(ctx, t, solanaGoClient, ixToken, admin, config.DefaultCommitment, common.AddSigners(mintPriv)) configPDA, _, perr := solana.FindProgramAddress([][]byte{config.BillingTokenConfigPrefix, mint.Bytes()}, ccip_router.ProgramID) require.NoError(t, perr) - receiver, _, terr := utils.FindAssociatedTokenAddress(solana.TokenProgramID, mint, config.BillingSignerPDA) + receiver, _, terr := tokens.FindAssociatedTokenAddress(solana.TokenProgramID, mint, config.BillingSignerPDA) require.NoError(t, terr) tokenConfig := ccip_router.BillingTokenConfig{ @@ -1064,15 +1067,15 @@ func TestCCIPRouter(t *testing.T) { receiver, anotherAdmin.PublicKey(), config.BillingSignerPDA, - utils.AssociatedTokenProgramID, + tokens.AssociatedTokenProgramID, solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, cerr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) var tokenConfigAccount ccip_router.BillingTokenConfigWrapper - aerr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, configPDA, config.DefaultCommitment, &tokenConfigAccount) + aerr := common.GetAccountDataBorshInto(ctx, solanaGoClient, configPDA, config.DefaultCommitment, &tokenConfigAccount) require.NoError(t, aerr) require.Equal(t, tokenConfig, tokenConfigAccount.Config) @@ -1090,10 +1093,10 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, cerr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixConfig}, anotherAdmin, config.DefaultCommitment) var final ccip_router.BillingTokenConfigWrapper - ferr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, configPDA, rpc.CommitmentProcessed, &final) + ferr := common.GetAccountDataBorshInto(ctx, solanaGoClient, configPDA, rpc.CommitmentProcessed, &final) require.EqualError(t, ferr, "not found") // it no longer exists }) }) @@ -1116,22 +1119,22 @@ func TestCCIPRouter(t *testing.T) { user.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip_router.Unauthorized_CcipRouterError.String()}) inputs := []struct { - plugin utils.OcrPlugin + plugin testutils.OcrPlugin signers [][20]byte transmitters []solana.PublicKey verifySig uint8 // use as bool }{ { - utils.OcrCommitPlugin, + testutils.OcrCommitPlugin, signerAddresses, transmitterPubKeys, 1, // true }, { - utils.OcrExecutePlugin, + testutils.OcrExecutePlugin, nil, transmitterPubKeys, 0, // no sign verify needed for execute @@ -1154,12 +1157,12 @@ func TestCCIPRouter(t *testing.T) { anotherAdmin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) require.NotNil(t, result) // Check event ConfigSet - configSetEvent := EventConfigSet{} - require.NoError(t, utils.ParseEvent(result.Meta.LogMessages, "ConfigSet", &configSetEvent, config.PrintEvents)) + configSetEvent := ccip.EventConfigSet{} + require.NoError(t, common.ParseEvent(result.Meta.LogMessages, "ConfigSet", &configSetEvent, config.PrintEvents)) require.Equal(t, uint8(v.plugin), configSetEvent.OcrPluginType) require.Equal(t, config.ConfigDigest, configSetEvent.ConfigDigest) require.Equal(t, config.OcrF, configSetEvent.F) @@ -1168,7 +1171,7 @@ func TestCCIPRouter(t *testing.T) { // check config state var configAccount ccip_router.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterConfigPDA, config.DefaultCommitment, &configAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -1215,13 +1218,13 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidInputs_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidInputs_CcipRouterError.String()}) }) t.Run("It rejects F = 0", func(t *testing.T) { t.Parallel() instruction, err := ccip_router.NewSetOcrConfigInstruction( - uint8(utils.OcrCommitPlugin), + uint8(testutils.OcrCommitPlugin), ccip_router.Ocr3ConfigInfo{ ConfigDigest: config.ConfigDigest, F: 0, @@ -1234,7 +1237,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorInvalidConfigFMustBePositive.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorInvalidConfigFMustBePositive.String()}) }) t.Run("It rejects too many transmitters", func(t *testing.T) { @@ -1244,7 +1247,7 @@ func TestCCIPRouter(t *testing.T) { invalidTransmitters[i] = getTransmitter().PublicKey() } instruction, err := ccip_router.NewSetOcrConfigInstruction( - uint8(utils.OcrCommitPlugin), + uint8(testutils.OcrCommitPlugin), ccip_router.Ocr3ConfigInfo{ ConfigDigest: config.ConfigDigest, F: config.OcrF, @@ -1257,7 +1260,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorInvalidConfigTooManyTransmitters.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorInvalidConfigTooManyTransmitters.String()}) }) t.Run("It rejects too many signers", func(t *testing.T) { @@ -1268,7 +1271,7 @@ func TestCCIPRouter(t *testing.T) { } instruction, err := ccip_router.NewSetOcrConfigInstruction( - uint8(utils.OcrCommitPlugin), + uint8(testutils.OcrCommitPlugin), ccip_router.Ocr3ConfigInfo{ ConfigDigest: config.ConfigDigest, F: config.OcrF, @@ -1281,7 +1284,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorInvalidConfigTooManySigners.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorInvalidConfigTooManySigners.String()}) }) t.Run("It rejects too high of F for signers", func(t *testing.T) { @@ -1290,7 +1293,7 @@ func TestCCIPRouter(t *testing.T) { invalidSigners[0] = signerAddresses[0] instruction, err := ccip_router.NewSetOcrConfigInstruction( - uint8(utils.OcrCommitPlugin), + uint8(testutils.OcrCommitPlugin), ccip_router.Ocr3ConfigInfo{ ConfigDigest: config.ConfigDigest, F: config.OcrF, @@ -1303,7 +1306,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorInvalidConfigFIsTooHigh.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorInvalidConfigFIsTooHigh.String()}) }) t.Run("It rejects duplicate transmitters", func(t *testing.T) { @@ -1315,7 +1318,7 @@ func TestCCIPRouter(t *testing.T) { invalidTransmitters[i] = transmitter } instruction, err := ccip_router.NewSetOcrConfigInstruction( - uint8(utils.OcrCommitPlugin), + uint8(testutils.OcrCommitPlugin), ccip_router.Ocr3ConfigInfo{ ConfigDigest: config.ConfigDigest, F: config.OcrF, @@ -1328,7 +1331,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorInvalidConfigRepeatedOracle.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorInvalidConfigRepeatedOracle.String()}) }) t.Run("It rejects duplicate signers", func(t *testing.T) { @@ -1340,7 +1343,7 @@ func TestCCIPRouter(t *testing.T) { oneTransmitter := []solana.PublicKey{transmitterPubKeys[0]} instruction, err := ccip_router.NewSetOcrConfigInstruction( - uint8(utils.OcrCommitPlugin), + uint8(testutils.OcrCommitPlugin), ccip_router.Ocr3ConfigInfo{ ConfigDigest: config.ConfigDigest, F: config.OcrF, @@ -1353,15 +1356,15 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorInvalidConfigRepeatedOracle.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorInvalidConfigRepeatedOracle.String()}) }) t.Run("It rejects zero transmitter address", func(t *testing.T) { t.Parallel() - invalidTransmitterPubKeys := []solana.PublicKey{transmitterPubKeys[0], utils.ZeroAddress} + invalidTransmitterPubKeys := []solana.PublicKey{transmitterPubKeys[0], common.ZeroAddress} instruction, err := ccip_router.NewSetOcrConfigInstruction( - uint8(utils.OcrCommitPlugin), + uint8(testutils.OcrCommitPlugin), ccip_router.Ocr3ConfigInfo{ ConfigDigest: config.ConfigDigest, F: config.OcrF, @@ -1374,7 +1377,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorOracleCannotBeZeroAddress.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorOracleCannotBeZeroAddress.String()}) }) t.Run("It rejects zero signer address", func(t *testing.T) { @@ -1384,7 +1387,7 @@ func TestCCIPRouter(t *testing.T) { invalidSignerAddresses = append(invalidSignerAddresses, v.Address) } instruction, err := ccip_router.NewSetOcrConfigInstruction( - uint8(utils.OcrCommitPlugin), + uint8(testutils.OcrCommitPlugin), ccip_router.Ocr3ConfigInfo{ ConfigDigest: config.ConfigDigest, F: config.OcrF, @@ -1397,7 +1400,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorOracleCannotBeZeroAddress.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorOracleCannotBeZeroAddress.String()}) }) }) }) @@ -1420,7 +1423,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When transmitter wants to set up the token admin registry, it fails", func(t *testing.T) { @@ -1435,7 +1438,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When admin wants to set up the token admin registry, it succeeds", func(t *testing.T) { @@ -1449,11 +1452,11 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) // Validate Token Pool Registry PDA tokenAdminRegistry := ccip_router.TokenAdminRegistry{} - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, token0.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) require.NoError(t, err) require.Equal(t, tokenPoolAdmin.PublicKey(), tokenAdminRegistry.Administrator) require.Equal(t, uint8(1), tokenAdminRegistry.Version) @@ -1472,7 +1475,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When transmitter wants to set up the pool, it fails", func(t *testing.T) { @@ -1485,7 +1488,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When admin wants to set up the pool, it fails", func(t *testing.T) { @@ -1497,7 +1500,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When Token Pool Admin wants to set up the pool, it succeeds", func(t *testing.T) { @@ -1512,11 +1515,11 @@ func TestCCIPRouter(t *testing.T) { instruction, err := base.ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) // Validate Token Pool Registry PDA tokenAdminRegistry := ccip_router.TokenAdminRegistry{} - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, token0.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) require.NoError(t, err) require.Equal(t, tokenPoolAdmin.PublicKey(), tokenAdminRegistry.Administrator) require.Equal(t, uint8(1), tokenAdminRegistry.Version) @@ -1533,11 +1536,11 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) // Validate Token Pool Registry PDA tokenAdminRegistry := ccip_router.TokenAdminRegistry{} - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, token0.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) require.NoError(t, err) require.Equal(t, tokenPoolAdmin.PublicKey(), tokenAdminRegistry.Administrator) require.Equal(t, uint8(1), tokenAdminRegistry.Version) @@ -1553,7 +1556,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) }) }) @@ -1567,7 +1570,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When admin wants to transfer the token admin registry, it succeeds and permissions stay with no changes", func(t *testing.T) { @@ -1579,11 +1582,11 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) // Validate Token Pool Registry PDA tokenAdminRegistry := ccip_router.TokenAdminRegistry{} - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, token0.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) require.NoError(t, err) require.Equal(t, tokenPoolAdmin.PublicKey(), tokenAdminRegistry.Administrator) require.Equal(t, uint8(1), tokenAdminRegistry.Version) @@ -1599,7 +1602,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) // new one cant make changes yet instruction, err = ccip_router.NewSetPoolInstruction( @@ -1610,7 +1613,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When new admin accepts the token admin registry, it succeeds and permissions are updated", func(t *testing.T) { @@ -1621,11 +1624,11 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) // Validate Token Pool Registry PDA tokenAdminRegistry := ccip_router.TokenAdminRegistry{} - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, token0.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, token0.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) require.NoError(t, err) require.Equal(t, anotherTokenPoolAdmin.PublicKey(), tokenAdminRegistry.Administrator) require.Equal(t, uint8(1), tokenAdminRegistry.Version) @@ -1641,7 +1644,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) // new one can make changes now instruction, err = ccip_router.NewSetPoolInstruction( @@ -1652,7 +1655,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) }) }) }) @@ -1668,7 +1671,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When transmitter wants to set up the token admin registry, it fails", func(t *testing.T) { @@ -1681,7 +1684,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When admin wants to set up the token admin registry, it fails", func(t *testing.T) { @@ -1693,7 +1696,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When invalid mint_authority wants to set up the token admin registry, it fails", func(t *testing.T) { @@ -1705,7 +1708,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When token mint_authority wants to set up the token admin registry, it succeeds", func(t *testing.T) { @@ -1717,11 +1720,11 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) // Validate Token Pool Registry PDA tokenAdminRegistry := ccip_router.TokenAdminRegistry{} - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, token1.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, token1.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) require.NoError(t, err) require.Equal(t, anotherTokenPoolAdmin.PublicKey(), tokenAdminRegistry.Administrator) require.Equal(t, uint8(1), tokenAdminRegistry.Version) @@ -1740,11 +1743,11 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) // Validate Token Pool Registry PDA tokenAdminRegistry := ccip_router.TokenAdminRegistry{} - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, token1.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, token1.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) require.NoError(t, err) require.Equal(t, anotherTokenPoolAdmin.PublicKey(), tokenAdminRegistry.Administrator) require.Equal(t, uint8(1), tokenAdminRegistry.Version) @@ -1763,7 +1766,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When mint authority wants to transfer the token admin registry, it succeeds and permissions stay with no changes", func(t *testing.T) { instruction, err := ccip_router.NewTransferAdminRoleTokenAdminRegistryInstruction( @@ -1774,11 +1777,11 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) // Validate Token Pool Registry PDA tokenAdminRegistry := ccip_router.TokenAdminRegistry{} - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, token1.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, token1.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) require.NoError(t, err) require.Equal(t, anotherTokenPoolAdmin.PublicKey(), tokenAdminRegistry.Administrator) require.Equal(t, uint8(1), tokenAdminRegistry.Version) @@ -1794,7 +1797,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment) // new one cant make changes yet instruction, err = ccip_router.NewSetPoolInstruction( @@ -1805,7 +1808,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) t.Run("When new admin accepts the token admin registry, it succeeds and permissions are updated", func(t *testing.T) { @@ -1816,11 +1819,11 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) // Validate Token Pool Registry PDA tokenAdminRegistry := ccip_router.TokenAdminRegistry{} - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, token1.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, token1.AdminRegistry, config.DefaultCommitment, &tokenAdminRegistry) require.NoError(t, err) require.Equal(t, tokenPoolAdmin.PublicKey(), tokenAdminRegistry.Administrator) require.Equal(t, uint8(1), tokenAdminRegistry.Version) @@ -1836,7 +1839,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherTokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) // new one can make changes now instruction, err = ccip_router.NewSetPoolInstruction( @@ -1847,7 +1850,7 @@ func TestCCIPRouter(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenPoolAdmin, config.DefaultCommitment) }) }) }) @@ -1863,19 +1866,19 @@ func TestCCIPRouter(t *testing.T) { TokenAddress: []byte{1, 2, 3}, }, token0.PoolConfig, token0.Chain[config.EvmChainSelector], tokenPoolAdmin.PublicKey(), solana.SystemProgramID).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, tokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, tokenPoolAdmin, config.DefaultCommitment) }) t.Run("RateLimit", func(t *testing.T) { ix, err := token_pool.NewSetChainRateLimitInstruction(config.EvmChainSelector, token0.Mint.PublicKey(), token_pool.RateLimitConfig{}, token_pool.RateLimitConfig{}, token0.PoolConfig, token0.Chain[config.EvmChainSelector], tokenPoolAdmin.PublicKey(), solana.SystemProgramID).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, tokenPoolAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, tokenPoolAdmin, config.DefaultCommitment) }) t.Run("Billing", func(t *testing.T) { ix, err := ccip_router.NewSetTokenBillingInstruction(config.EvmChainSelector, token0.Mint.PublicKey(), ccip_router.TokenBilling{}, config.RouterConfigPDA, token0.Billing[config.EvmChainSelector], anotherAdmin.PublicKey(), solana.SystemProgramID).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, anotherAdmin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, anotherAdmin, config.DefaultCommitment) }) // validate permissions for setting config @@ -1884,7 +1887,7 @@ func TestCCIPRouter(t *testing.T) { t.Run("Billing can only be set by CCIP admin", func(t *testing.T) { ix, err := ccip_router.NewSetTokenBillingInstruction(config.EvmChainSelector, token0.Mint.PublicKey(), ccip_router.TokenBilling{}, config.RouterConfigPDA, token0.Billing[config.EvmChainSelector], anotherTokenPoolAdmin.PublicKey(), solana.SystemProgramID).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, anotherTokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, anotherTokenPoolAdmin, config.DefaultCommitment, []string{ccip_router.Unauthorized_CcipRouterError.String()}) }) }) }) @@ -1904,10 +1907,11 @@ func TestCCIPRouter(t *testing.T) { instruction, err := ccip_router.NewGetFeeInstruction(config.EvmChainSelector, message, config.EvmDestChainStatePDA, billingTokenConfigPDA).ValidateAndBuild() require.NoError(t, err) - result := utils.SimulateTransaction(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user) + result := testutils.SimulateTransaction(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user) require.NotNil(t, result) - returned := utils.ExtractTypedReturnValue(ctx, t, result.Value.Logs, config.CcipRouterProgram.String(), binary.LittleEndian.Uint64) + returned, err := common.ExtractTypedReturnValue(ctx, result.Value.Logs, config.CcipRouterProgram.String(), binary.LittleEndian.Uint64) + require.NoError(t, err) require.Equal(t, uint64(1), returned) }) @@ -1930,7 +1934,7 @@ func TestCCIPRouter(t *testing.T) { instruction, err := ccip_router.NewGetFeeInstruction(config.EvmChainSelector, message, config.EvmDestChainStatePDA, billingTokenConfigPDA).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: InvalidEVMAddress"}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: InvalidEVMAddress"}) require.NotNil(t, result) } }) @@ -1943,7 +1947,7 @@ func TestCCIPRouter(t *testing.T) { t.Run("OnRamp ccipSend", func(t *testing.T) { t.Run("When sending to an invalid destination chain selector it fails", func(t *testing.T) { destinationChainSelector := uint64(189) - destinationChainStatePDA, err := GetDestChainStatePDA(destinationChainSelector) + destinationChainStatePDA, err := ccip.GetDestChainStatePDA(destinationChainSelector) require.NoError(t, err) message := ccip_router.Solana2AnyMessage{ FeeToken: wsol.mint, @@ -1968,7 +1972,7 @@ func TestCCIPRouter(t *testing.T) { raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: AccountNotInitialized"}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: AccountNotInitialized"}) require.NotNil(t, result) }) @@ -2000,22 +2004,22 @@ func TestCCIPRouter(t *testing.T) { raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) require.NotNil(t, result) var chainStateAccount ccip_router.DestChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) require.NoError(t, err, "failed to get account info") // Do not check source chain config, as it may have been updated by other tests in ccip offramp require.Equal(t, uint64(1), chainStateAccount.State.SequenceNumber) var nonceCounterAccount ccip_router.Nonce - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, nonceEvmPDA, config.DefaultCommitment, &nonceCounterAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, nonceEvmPDA, config.DefaultCommitment, &nonceCounterAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(1), nonceCounterAccount.Counter) - ccipMessageSentEvent := EventCCIPMessageSent{} - require.NoError(t, utils.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) + ccipMessageSentEvent := ccip.EventCCIPMessageSent{} + require.NoError(t, common.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) require.Equal(t, uint64(21), ccipMessageSentEvent.DestinationChainSelector) require.Equal(t, uint64(1), ccipMessageSentEvent.SequenceNumber) require.Equal(t, user.PublicKey(), ccipMessageSentEvent.Message.Sender) @@ -2063,22 +2067,22 @@ func TestCCIPRouter(t *testing.T) { raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) require.NotNil(t, result) var chainStateAccount ccip_router.DestChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) require.NoError(t, err, "failed to get account info") // Do not check source chain config, as it may have been updated by other tests in ccip offramp require.Equal(t, uint64(2), chainStateAccount.State.SequenceNumber) var nonceCounterAccount ccip_router.Nonce - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, nonceEvmPDA, config.DefaultCommitment, &nonceCounterAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, nonceEvmPDA, config.DefaultCommitment, &nonceCounterAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(1), nonceCounterAccount.Counter) - ccipMessageSentEvent := EventCCIPMessageSent{} - require.NoError(t, utils.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) + ccipMessageSentEvent := ccip.EventCCIPMessageSent{} + require.NoError(t, common.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) require.Equal(t, uint64(21), ccipMessageSentEvent.DestinationChainSelector) require.Equal(t, uint64(2), ccipMessageSentEvent.SequenceNumber) require.Equal(t, user.PublicKey(), ccipMessageSentEvent.Message.Sender) @@ -2124,22 +2128,22 @@ func TestCCIPRouter(t *testing.T) { raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) require.NotNil(t, result) var chainStateAccount ccip_router.DestChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) require.NoError(t, err, "failed to get account info") // Do not check source chain config, as it may have been updated by other tests in ccip offramp require.Equal(t, uint64(3), chainStateAccount.State.SequenceNumber) var nonceCounterAccount ccip_router.Nonce - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, nonceEvmPDA, config.DefaultCommitment, &nonceCounterAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, nonceEvmPDA, config.DefaultCommitment, &nonceCounterAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(2), nonceCounterAccount.Counter) - ccipMessageSentEvent := EventCCIPMessageSent{} - require.NoError(t, utils.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) + ccipMessageSentEvent := ccip.EventCCIPMessageSent{} + require.NoError(t, common.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) require.Equal(t, uint64(21), ccipMessageSentEvent.DestinationChainSelector) require.Equal(t, uint64(3), ccipMessageSentEvent.SequenceNumber) require.Equal(t, user.PublicKey(), ccipMessageSentEvent.Message.Sender) @@ -2186,22 +2190,22 @@ func TestCCIPRouter(t *testing.T) { raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) require.NotNil(t, result) var chainStateAccount ccip_router.DestChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) require.NoError(t, err, "failed to get account info") // Do not check source chain config, as it may have been updated by other tests in ccip offramp require.Equal(t, uint64(4), chainStateAccount.State.SequenceNumber) var nonceCounterAccount ccip_router.Nonce - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, nonceEvmPDA, config.DefaultCommitment, &nonceCounterAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, nonceEvmPDA, config.DefaultCommitment, &nonceCounterAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(2), nonceCounterAccount.Counter) - ccipMessageSentEvent := EventCCIPMessageSent{} - require.NoError(t, utils.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) + ccipMessageSentEvent := ccip.EventCCIPMessageSent{} + require.NoError(t, common.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) require.Equal(t, uint64(21), ccipMessageSentEvent.DestinationChainSelector) require.Equal(t, uint64(4), ccipMessageSentEvent.SequenceNumber) require.Equal(t, user.PublicKey(), ccipMessageSentEvent.Message.Sender) @@ -2247,22 +2251,22 @@ func TestCCIPRouter(t *testing.T) { raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) require.NotNil(t, result) var chainStateAccount ccip_router.DestChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) require.NoError(t, err, "failed to get account info") // Do not check source chain config, as it may have been updated by other tests in ccip offramp require.Equal(t, uint64(5), chainStateAccount.State.SequenceNumber) var nonceCounterAccount ccip_router.Nonce - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, nonceEvmPDA, config.DefaultCommitment, &nonceCounterAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, nonceEvmPDA, config.DefaultCommitment, &nonceCounterAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(3), nonceCounterAccount.Counter) - ccipMessageSentEvent := EventCCIPMessageSent{} - require.NoError(t, utils.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) + ccipMessageSentEvent := ccip.EventCCIPMessageSent{} + require.NoError(t, common.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) require.Equal(t, uint64(21), ccipMessageSentEvent.DestinationChainSelector) require.Equal(t, uint64(5), ccipMessageSentEvent.SequenceNumber) require.Equal(t, user.PublicKey(), ccipMessageSentEvent.Message.Sender) @@ -2306,7 +2310,7 @@ func TestCCIPRouter(t *testing.T) { instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherUser, config.DefaultCommitment, []string{"Error Message: A seeds constraint was violated"}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherUser, config.DefaultCommitment, []string{"Error Message: A seeds constraint was violated"}) require.NotNil(t, result) }) @@ -2339,7 +2343,7 @@ func TestCCIPRouter(t *testing.T) { instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWithRPCError(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherUser, config.DefaultCommitment, []string{"Transaction signature verification failure"}) + testutils.SendAndFailWithRPCError(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherUser, config.DefaultCommitment, []string{"Transaction signature verification failure"}) }) t.Run("When sending a message without flagging the user ATA as writable, it fails", func(t *testing.T) { @@ -2373,7 +2377,7 @@ func TestCCIPRouter(t *testing.T) { instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWithRPCError(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.InvalidInputsAtaWritable_CcipRouterError.String()}) + testutils.SendAndFailWithRPCError(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.InvalidInputsAtaWritable_CcipRouterError.String()}) }) t.Run("When sending a message and paying with inconsistent fee token accounts, it fails", func(t *testing.T) { @@ -2382,12 +2386,12 @@ func TestCCIPRouter(t *testing.T) { // These testcases are a quite a lot, this obviously blows up combinatorially and adds many seconds to the suite. // We can remove/reduce this, but I used it during development so for now I'm keeping them here - for i, program := range utils.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.program }) { - for j, mint := range utils.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.mint }) { - for k, messageMint := range utils.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.mint }) { - for l, billingConfigPDA := range utils.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.billingConfigPDA }) { - for m, userATA := range utils.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.userATA }) { - for n, billingATA := range utils.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.billingATA }) { + for i, program := range common.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.program }) { + for j, mint := range common.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.mint }) { + for k, messageMint := range common.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.mint }) { + for l, billingConfigPDA := range common.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.billingConfigPDA }) { + for m, userATA := range common.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.userATA }) { + for n, billingATA := range common.Map(billingTokens, func(t *AccountsPerToken) solana.PublicKey { return t.billingATA }) { if i == j && j == k && k == l && l == m && m == n { // skip cases where everything aligns well, which work continue @@ -2420,7 +2424,7 @@ func TestCCIPRouter(t *testing.T) { require.NoError(t, err) // Given the mixture of inputs, there can be different error types here, so just check that it fails but not each message - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{""}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{""}) }) } } @@ -2438,7 +2442,7 @@ func TestCCIPRouter(t *testing.T) { Receiver: validReceiverAddress[:], Data: []byte{4, 5, 6}, } - anotherUserNonceEVMPDA, err := getNoncePDA(config.EvmChainSelector, anotherUser.PublicKey()) + anotherUserNonceEVMPDA, err := ccip.GetNoncePDA(config.EvmChainSelector, anotherUser.PublicKey()) require.NoError(t, err) raw := ccip_router.NewCcipSendInstruction( @@ -2460,7 +2464,7 @@ func TestCCIPRouter(t *testing.T) { raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherUser, config.DefaultCommitment, []string{ccip_router.InvalidInputs_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherUser, config.DefaultCommitment, []string{ccip_router.InvalidInputs_CcipRouterError.String()}) }) t.Run("When another user sending a Valid CCIP Message Emits CCIPMessageSent", func(t *testing.T) { @@ -2471,7 +2475,7 @@ func TestCCIPRouter(t *testing.T) { Receiver: validReceiverAddress[:], Data: []byte{4, 5, 6}, } - anotherUserNonceEVMPDA, err := getNoncePDA(config.EvmChainSelector, anotherUser.PublicKey()) + anotherUserNonceEVMPDA, err := ccip.GetNoncePDA(config.EvmChainSelector, anotherUser.PublicKey()) require.NoError(t, err) raw := ccip_router.NewCcipSendInstruction( @@ -2493,22 +2497,22 @@ func TestCCIPRouter(t *testing.T) { raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherUser, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherUser, config.DefaultCommitment) require.NotNil(t, result) var chainStateAccount ccip_router.DestChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, destinationChainStatePDA, config.DefaultCommitment, &chainStateAccount) require.NoError(t, err, "failed to get account info") // Do not check source chain config, as it may have been updated by other tests in ccip offramp require.Equal(t, uint64(6), chainStateAccount.State.SequenceNumber) var nonceCounterAccount ccip_router.Nonce - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, anotherUserNonceEVMPDA, config.DefaultCommitment, &nonceCounterAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, anotherUserNonceEVMPDA, config.DefaultCommitment, &nonceCounterAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(1), nonceCounterAccount.Counter) - ccipMessageSentEvent := EventCCIPMessageSent{} - require.NoError(t, utils.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) + ccipMessageSentEvent := ccip.EventCCIPMessageSent{} + require.NoError(t, common.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) require.Equal(t, uint64(21), ccipMessageSentEvent.DestinationChainSelector) require.Equal(t, uint64(6), ccipMessageSentEvent.SequenceNumber) require.Equal(t, anotherUser.PublicKey(), ccipMessageSentEvent.Message.Sender) @@ -2524,9 +2528,9 @@ func TestCCIPRouter(t *testing.T) { }) t.Run("token happy path", func(t *testing.T) { - _, initSupply, err := utils.TokenSupply(ctx, solanaGoClient, token0.Mint.PublicKey(), config.DefaultCommitment) + _, initSupply, err := tokens.TokenSupply(ctx, solanaGoClient, token0.Mint.PublicKey(), config.DefaultCommitment) require.NoError(t, err) - _, initBal, err := utils.TokenBalance(ctx, solanaGoClient, token0.User[user.PublicKey()], config.DefaultCommitment) + _, initBal, err := tokens.TokenBalance(ctx, solanaGoClient, token0.User[user.PublicKey()], config.DefaultCommitment) require.NoError(t, err) destinationChainSelector := config.EvmChainSelector @@ -2565,44 +2569,44 @@ func TestCCIPRouter(t *testing.T) { ) base.GetFeeTokenUserAssociatedAccountAccount().WRITE() - tokenMetas, addressTables, err := ParseTokenLookupTable(ctx, solanaGoClient, token0, userTokenAccount) + tokenMetas, addressTables, err := tokens.ParseTokenLookupTable(ctx, solanaGoClient, token0, userTokenAccount) require.NoError(t, err) base.AccountMetaSlice = append(base.AccountMetaSlice, tokenMetas...) ix, err := base.ValidateAndBuild() require.NoError(t, err) - ixApprove, err := utils.TokenApproveChecked(1, 0, token0.Program, userTokenAccount, token0.Mint.PublicKey(), config.ExternalTokenPoolsSignerPDA, user.PublicKey(), nil) + ixApprove, err := tokens.TokenApproveChecked(1, 0, token0.Program, userTokenAccount, token0.Mint.PublicKey(), config.ExternalTokenPoolsSignerPDA, user.PublicKey(), nil) require.NoError(t, err) - result := utils.SendAndConfirmWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{ixApprove, ix}, user, config.DefaultCommitment, addressTables, utils.AddComputeUnitLimit(300_000)) + result := testutils.SendAndConfirmWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{ixApprove, ix}, user, config.DefaultCommitment, addressTables, common.AddComputeUnitLimit(300_000)) require.NotNil(t, result) // check CCIP event - ccipMessageSentEvent := EventCCIPMessageSent{} - require.NoError(t, utils.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) + ccipMessageSentEvent := ccip.EventCCIPMessageSent{} + require.NoError(t, common.ParseEvent(result.Meta.LogMessages, "CCIPMessageSent", &ccipMessageSentEvent, config.PrintEvents)) require.Equal(t, 1, len(ccipMessageSentEvent.Message.TokenAmounts)) ta := ccipMessageSentEvent.Message.TokenAmounts[0] require.Equal(t, token0.PoolConfig, ta.SourcePoolAddress) require.Equal(t, []byte{1, 2, 3}, ta.DestTokenAddress) require.Equal(t, 0, len(ta.ExtraData)) - require.Equal(t, utils.ToLittleEndianU256(1), ta.Amount) + require.Equal(t, tokens.ToLittleEndianU256(1), ta.Amount) require.Equal(t, 0, len(ta.DestExecData)) // check pool event - poolEvent := EventBurnLock{} - require.NoError(t, utils.ParseEvent(result.Meta.LogMessages, "Burned", &poolEvent, config.PrintEvents)) + poolEvent := tokens.EventBurnLock{} + require.NoError(t, common.ParseEvent(result.Meta.LogMessages, "Burned", &poolEvent, config.PrintEvents)) require.Equal(t, config.ExternalTokenPoolsSignerPDA, poolEvent.Sender) require.Equal(t, uint64(1), poolEvent.Amount) // check balances - _, currSupply, err := utils.TokenSupply(ctx, solanaGoClient, token0.Mint.PublicKey(), config.DefaultCommitment) + _, currSupply, err := tokens.TokenSupply(ctx, solanaGoClient, token0.Mint.PublicKey(), config.DefaultCommitment) require.NoError(t, err) require.Equal(t, 1, initSupply-currSupply) // burned amount - _, currBal, err := utils.TokenBalance(ctx, solanaGoClient, token0.User[user.PublicKey()], config.DefaultCommitment) + _, currBal, err := tokens.TokenBalance(ctx, solanaGoClient, token0.User[user.PublicKey()], config.DefaultCommitment) require.NoError(t, err) require.Equal(t, 1, initBal-currBal) // burned amount - _, poolBal, err := utils.TokenBalance(ctx, solanaGoClient, token0.PoolTokenAccount, config.DefaultCommitment) + _, poolBal, err := tokens.TokenBalance(ctx, solanaGoClient, token0.PoolTokenAccount, config.DefaultCommitment) require.NoError(t, err) require.Equal(t, 0, poolBal) // pool burned any sent to it }) @@ -2714,7 +2718,7 @@ func TestCCIPRouter(t *testing.T) { ) tx.GetFeeTokenUserAssociatedAccountAccount().WRITE() - tokenMetas, addressTables, err := ParseTokenLookupTable(ctx, solanaGoClient, token0, userTokenAccount) + tokenMetas, addressTables, err := tokens.ParseTokenLookupTable(ctx, solanaGoClient, token0, userTokenAccount) require.NoError(t, err) // replace account meta with invalid account to trigger error or append if in.index >= uint(len(tokenMetas)) { @@ -2727,7 +2731,7 @@ func TestCCIPRouter(t *testing.T) { ix, err := tx.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{ix}, user, config.DefaultCommitment, addressTables, []string{in.errorStr.String()}) + testutils.SendAndFailWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{ix}, user, config.DefaultCommitment, addressTables, []string{in.errorStr.String()}) }) } }) @@ -2747,9 +2751,10 @@ func TestCCIPRouter(t *testing.T) { ix, ferr := ccip_router.NewGetFeeInstruction(config.EvmChainSelector, message, config.EvmDestChainStatePDA, token.billingConfigPDA).ValidateAndBuild() require.NoError(t, ferr) - feeResult := utils.SimulateTransaction(ctx, t, solanaGoClient, []solana.Instruction{ix}, user) + feeResult := testutils.SimulateTransaction(ctx, t, solanaGoClient, []solana.Instruction{ix}, user) require.NotNil(t, feeResult) - fee := utils.ExtractTypedReturnValue(ctx, t, feeResult.Value.Logs, config.CcipRouterProgram.String(), binary.LittleEndian.Uint64) + fee, err := common.ExtractTypedReturnValue(ctx, feeResult.Value.Logs, config.CcipRouterProgram.String(), binary.LittleEndian.Uint64) + require.NoError(t, err) require.Equal(t, uint64(1), fee) initialBalance := getBalance(token.billingATA) @@ -2774,7 +2779,7 @@ func TestCCIPRouter(t *testing.T) { raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) require.NotNil(t, result) finalBalance := getBalance(token.billingATA) @@ -2792,7 +2797,7 @@ func TestCCIPRouter(t *testing.T) { Data: []byte{4, 5, 6}, } - noncePDA, err := getNoncePDA(config.EvmChainSelector, tokenlessUser.PublicKey()) + noncePDA, err := ccip.GetNoncePDA(config.EvmChainSelector, tokenlessUser.PublicKey()) require.NoError(t, err) // ccipSend @@ -2815,7 +2820,7 @@ func TestCCIPRouter(t *testing.T) { raw.GetFeeTokenUserAssociatedAccountAccount().WRITE() instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenlessUser, config.DefaultCommitment, []string{"insufficient funds"}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, tokenlessUser, config.DefaultCommitment, []string{"insufficient funds"}) }) t.Run("When sending a valid CCIP message and paying in native SOL, it bills the same amount that getFee previously returned and it's accumulated as Wrapped SOL", func(t *testing.T) { @@ -2837,9 +2842,10 @@ func TestCCIPRouter(t *testing.T) { ix, ferr := ccip_router.NewGetFeeInstruction(config.EvmChainSelector, message, config.EvmDestChainStatePDA, wsol.billingConfigPDA).ValidateAndBuild() require.NoError(t, ferr) - feeResult := utils.SimulateTransaction(ctx, t, solanaGoClient, []solana.Instruction{ix}, user) + feeResult := testutils.SimulateTransaction(ctx, t, solanaGoClient, []solana.Instruction{ix}, user) require.NotNil(t, feeResult) - fee := utils.ExtractTypedReturnValue(ctx, t, feeResult.Value.Logs, config.CcipRouterProgram.String(), binary.LittleEndian.Uint64) + fee, err := common.ExtractTypedReturnValue(ctx, feeResult.Value.Logs, config.CcipRouterProgram.String(), binary.LittleEndian.Uint64) + require.NoError(t, err) require.Greater(t, fee, uint64(0)) initialBalance := getBalance(wsol.billingATA) @@ -2865,7 +2871,7 @@ func TestCCIPRouter(t *testing.T) { instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) require.NotNil(t, result) finalBalance := getBalance(wsol.billingATA) @@ -2901,7 +2907,7 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, anotherAdmin, config.DefaultCommitment, []string{ccip_router.InvalidInputs_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, anotherAdmin, config.DefaultCommitment, []string{ccip_router.InvalidInputs_CcipRouterError.String()}) }) } }) @@ -2919,7 +2925,7 @@ func TestCCIPRouter(t *testing.T) { t.Run("Commit", func(t *testing.T) { currentMinSeqNr := uint64(1) - oldReportContext := CreateReportContext(1) // use old sequence number + oldReportContext := ccip.CreateReportContext(1) // use old sequence number type Comparator int const ( @@ -2943,8 +2949,8 @@ func TestCCIPRouter(t *testing.T) { PriceUpdates: ccip_router.PriceUpdates{}, RemainingAccounts: []solana.PublicKey{}, RunEventValidations: func(t *testing.T, tx *rpc.GetTransactionResult) { - require.ErrorContains(t, utils.ParseEvent(tx.Meta.LogMessages, "UsdPerTokenUpdated", nil, config.PrintEvents), "event not found") - require.ErrorContains(t, utils.ParseEvent(tx.Meta.LogMessages, "UsdPerUnitGasUpdated", nil, config.PrintEvents), "event not found") + require.ErrorContains(t, common.ParseEvent(tx.Meta.LogMessages, "UsdPerTokenUpdated", nil, config.PrintEvents), "event not found") + require.ErrorContains(t, common.ParseEvent(tx.Meta.LogMessages, "UsdPerUnitGasUpdated", nil, config.PrintEvents), "event not found") }, RunStateValidations: func(t *testing.T) {}, PriceSequenceComparator: Greater, // it is a newer commit but with no price update @@ -2954,24 +2960,24 @@ func TestCCIPRouter(t *testing.T) { PriceUpdates: ccip_router.PriceUpdates{ TokenPriceUpdates: []ccip_router.TokenPriceUpdate{{ SourceToken: wsol.mint, - UsdPerToken: utils.To28BytesBE(1), + UsdPerToken: common.To28BytesBE(1), }}, }, RemainingAccounts: []solana.PublicKey{config.RouterStatePDA, wsol.billingConfigPDA}, RunEventValidations: func(t *testing.T, tx *rpc.GetTransactionResult) { // yes token update - var update UsdPerTokenUpdated - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "UsdPerTokenUpdated", &update, config.PrintEvents)) + var update ccip.UsdPerTokenUpdated + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "UsdPerTokenUpdated", &update, config.PrintEvents)) require.Greater(t, update.Timestamp, int64(0)) // timestamp is set - require.Equal(t, utils.To28BytesBE(1), update.Value) + require.Equal(t, common.To28BytesBE(1), update.Value) // no gas updates - require.ErrorContains(t, utils.ParseEvent(tx.Meta.LogMessages, "UsdPerUnitGasUpdated", nil, config.PrintEvents), "event not found") + require.ErrorContains(t, common.ParseEvent(tx.Meta.LogMessages, "UsdPerUnitGasUpdated", nil, config.PrintEvents), "event not found") }, RunStateValidations: func(t *testing.T) { var tokenConfig ccip_router.BillingTokenConfigWrapper - require.NoError(t, utils.GetAccountDataBorshInto(ctx, solanaGoClient, wsol.billingConfigPDA, config.DefaultCommitment, &tokenConfig)) - require.Equal(t, utils.To28BytesBE(1), tokenConfig.Config.UsdPerToken.Value) + require.NoError(t, common.GetAccountDataBorshInto(ctx, solanaGoClient, wsol.billingConfigPDA, config.DefaultCommitment, &tokenConfig)) + require.Equal(t, common.To28BytesBE(1), tokenConfig.Config.UsdPerToken.Value) require.Greater(t, tokenConfig.Config.UsdPerToken.Timestamp, int64(0)) }, PriceSequenceComparator: Equal, @@ -2981,24 +2987,24 @@ func TestCCIPRouter(t *testing.T) { PriceUpdates: ccip_router.PriceUpdates{ GasPriceUpdates: []ccip_router.GasPriceUpdate{{ DestChainSelector: config.EvmChainSelector, - UsdPerUnitGas: utils.To28BytesBE(1), + UsdPerUnitGas: common.To28BytesBE(1), }}, }, RemainingAccounts: []solana.PublicKey{config.RouterStatePDA, config.EvmDestChainStatePDA}, RunEventValidations: func(t *testing.T, tx *rpc.GetTransactionResult) { // no token updates - require.ErrorContains(t, utils.ParseEvent(tx.Meta.LogMessages, "UsdPerTokenUpdated", nil, config.PrintEvents), "event not found") + require.ErrorContains(t, common.ParseEvent(tx.Meta.LogMessages, "UsdPerTokenUpdated", nil, config.PrintEvents), "event not found") // yes gas update - var update UsdPerUnitGasUpdated - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "UsdPerUnitGasUpdated", &update, config.PrintEvents)) + var update ccip.UsdPerUnitGasUpdated + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "UsdPerUnitGasUpdated", &update, config.PrintEvents)) require.Greater(t, update.Timestamp, int64(0)) // timestamp is set - require.Equal(t, utils.To28BytesBE(1), update.Value) + require.Equal(t, common.To28BytesBE(1), update.Value) }, RunStateValidations: func(t *testing.T) { var chainState ccip_router.DestChain - require.NoError(t, utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &chainState)) - require.Equal(t, utils.To28BytesBE(1), chainState.State.UsdPerUnitGas.Value) + require.NoError(t, common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &chainState)) + require.Equal(t, common.To28BytesBE(1), chainState.State.UsdPerUnitGas.Value) require.Greater(t, chainState.State.UsdPerUnitGas.Timestamp, int64(0)) }, PriceSequenceComparator: Equal, @@ -3008,24 +3014,24 @@ func TestCCIPRouter(t *testing.T) { PriceUpdates: ccip_router.PriceUpdates{ GasPriceUpdates: []ccip_router.GasPriceUpdate{{ DestChainSelector: config.SolanaChainSelector, - UsdPerUnitGas: utils.To28BytesBE(2), + UsdPerUnitGas: common.To28BytesBE(2), }}, }, RemainingAccounts: []solana.PublicKey{config.RouterStatePDA, config.SolanaDestChainStatePDA}, RunEventValidations: func(t *testing.T, tx *rpc.GetTransactionResult) { // no token updates - require.ErrorContains(t, utils.ParseEvent(tx.Meta.LogMessages, "UsdPerTokenUpdated", nil, config.PrintEvents), "event not found") + require.ErrorContains(t, common.ParseEvent(tx.Meta.LogMessages, "UsdPerTokenUpdated", nil, config.PrintEvents), "event not found") // yes gas update - var update UsdPerUnitGasUpdated - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "UsdPerUnitGasUpdated", &update, config.PrintEvents)) + var update ccip.UsdPerUnitGasUpdated + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "UsdPerUnitGasUpdated", &update, config.PrintEvents)) require.Greater(t, update.Timestamp, int64(0)) // timestamp is set - require.Equal(t, utils.To28BytesBE(2), update.Value) + require.Equal(t, common.To28BytesBE(2), update.Value) }, RunStateValidations: func(t *testing.T) { var chainState ccip_router.DestChain - require.NoError(t, utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.SolanaDestChainStatePDA, config.DefaultCommitment, &chainState)) - require.Equal(t, utils.To28BytesBE(2), chainState.State.UsdPerUnitGas.Value) + require.NoError(t, common.GetAccountDataBorshInto(ctx, solanaGoClient, config.SolanaDestChainStatePDA, config.DefaultCommitment, &chainState)) + require.Equal(t, common.To28BytesBE(2), chainState.State.UsdPerUnitGas.Value) require.Greater(t, chainState.State.UsdPerUnitGas.Timestamp, int64(0)) }, PriceSequenceComparator: Equal, @@ -3034,18 +3040,18 @@ func TestCCIPRouter(t *testing.T) { Name: "Multiple token & gas updates", PriceUpdates: ccip_router.PriceUpdates{ TokenPriceUpdates: []ccip_router.TokenPriceUpdate{ - {SourceToken: wsol.mint, UsdPerToken: utils.To28BytesBE(3)}, - {SourceToken: token2022.mint, UsdPerToken: utils.To28BytesBE(4)}, + {SourceToken: wsol.mint, UsdPerToken: common.To28BytesBE(3)}, + {SourceToken: token2022.mint, UsdPerToken: common.To28BytesBE(4)}, }, GasPriceUpdates: []ccip_router.GasPriceUpdate{ - {DestChainSelector: config.EvmChainSelector, UsdPerUnitGas: utils.To28BytesBE(5)}, - {DestChainSelector: config.SolanaChainSelector, UsdPerUnitGas: utils.To28BytesBE(6)}, + {DestChainSelector: config.EvmChainSelector, UsdPerUnitGas: common.To28BytesBE(5)}, + {DestChainSelector: config.SolanaChainSelector, UsdPerUnitGas: common.To28BytesBE(6)}, }, }, RemainingAccounts: []solana.PublicKey{config.RouterStatePDA, wsol.billingConfigPDA, token2022.billingConfigPDA, config.EvmDestChainStatePDA, config.SolanaDestChainStatePDA}, RunEventValidations: func(t *testing.T, tx *rpc.GetTransactionResult) { // yes multiple token updates - tokenUpdates, err := utils.ParseMultipleEvents[UsdPerTokenUpdated](tx.Meta.LogMessages, "UsdPerTokenUpdated", config.PrintEvents) + tokenUpdates, err := common.ParseMultipleEvents[ccip.UsdPerTokenUpdated](tx.Meta.LogMessages, "UsdPerTokenUpdated", config.PrintEvents) require.NoError(t, err) require.Len(t, tokenUpdates, 2) var eventWsol, eventToken2022 bool @@ -3053,10 +3059,10 @@ func TestCCIPRouter(t *testing.T) { switch tokenUpdate.Token { case wsol.mint: eventWsol = true - require.Equal(t, utils.To28BytesBE(3), tokenUpdate.Value) + require.Equal(t, common.To28BytesBE(3), tokenUpdate.Value) case token2022.mint: eventToken2022 = true - require.Equal(t, utils.To28BytesBE(4), tokenUpdate.Value) + require.Equal(t, common.To28BytesBE(4), tokenUpdate.Value) default: t.Fatalf("unexpected token update: %v", tokenUpdate) } @@ -3066,7 +3072,7 @@ func TestCCIPRouter(t *testing.T) { require.True(t, eventToken2022, "missing token2022 update event") // yes gas update - gasUpdates, err := utils.ParseMultipleEvents[UsdPerUnitGasUpdated](tx.Meta.LogMessages, "UsdPerUnitGasUpdated", config.PrintEvents) + gasUpdates, err := common.ParseMultipleEvents[ccip.UsdPerUnitGasUpdated](tx.Meta.LogMessages, "UsdPerUnitGasUpdated", config.PrintEvents) require.NoError(t, err) require.Len(t, gasUpdates, 2) var eventEvm, eventSolana bool @@ -3074,10 +3080,10 @@ func TestCCIPRouter(t *testing.T) { switch gasUpdate.DestChain { case config.EvmChainSelector: eventEvm = true - require.Equal(t, utils.To28BytesBE(5), gasUpdate.Value) + require.Equal(t, common.To28BytesBE(5), gasUpdate.Value) case config.SolanaChainSelector: eventSolana = true - require.Equal(t, utils.To28BytesBE(6), gasUpdate.Value) + require.Equal(t, common.To28BytesBE(6), gasUpdate.Value) default: t.Fatalf("unexpected gas update: %v", gasUpdate) } @@ -3088,23 +3094,23 @@ func TestCCIPRouter(t *testing.T) { }, RunStateValidations: func(t *testing.T) { var wsolTokenConfig ccip_router.BillingTokenConfigWrapper - require.NoError(t, utils.GetAccountDataBorshInto(ctx, solanaGoClient, wsol.billingConfigPDA, config.DefaultCommitment, &wsolTokenConfig)) - require.Equal(t, utils.To28BytesBE(3), wsolTokenConfig.Config.UsdPerToken.Value) + require.NoError(t, common.GetAccountDataBorshInto(ctx, solanaGoClient, wsol.billingConfigPDA, config.DefaultCommitment, &wsolTokenConfig)) + require.Equal(t, common.To28BytesBE(3), wsolTokenConfig.Config.UsdPerToken.Value) require.Greater(t, wsolTokenConfig.Config.UsdPerToken.Timestamp, int64(0)) var token2022Config ccip_router.BillingTokenConfigWrapper - require.NoError(t, utils.GetAccountDataBorshInto(ctx, solanaGoClient, token2022.billingConfigPDA, config.DefaultCommitment, &token2022Config)) - require.Equal(t, utils.To28BytesBE(4), token2022Config.Config.UsdPerToken.Value) + require.NoError(t, common.GetAccountDataBorshInto(ctx, solanaGoClient, token2022.billingConfigPDA, config.DefaultCommitment, &token2022Config)) + require.Equal(t, common.To28BytesBE(4), token2022Config.Config.UsdPerToken.Value) require.Greater(t, token2022Config.Config.UsdPerToken.Timestamp, int64(0)) var evmChainState ccip_router.DestChain - require.NoError(t, utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &evmChainState)) - require.Equal(t, utils.To28BytesBE(5), evmChainState.State.UsdPerUnitGas.Value) + require.NoError(t, common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &evmChainState)) + require.Equal(t, common.To28BytesBE(5), evmChainState.State.UsdPerUnitGas.Value) require.Greater(t, evmChainState.State.UsdPerUnitGas.Timestamp, int64(0)) var solanaChainState ccip_router.DestChain - require.NoError(t, utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.SolanaDestChainStatePDA, config.DefaultCommitment, &solanaChainState)) - require.Equal(t, utils.To28BytesBE(6), solanaChainState.State.UsdPerUnitGas.Value) + require.NoError(t, common.GetAccountDataBorshInto(ctx, solanaGoClient, config.SolanaDestChainStatePDA, config.DefaultCommitment, &solanaChainState)) + require.Equal(t, common.To28BytesBE(6), solanaChainState.State.UsdPerUnitGas.Value) require.Greater(t, solanaChainState.State.UsdPerUnitGas.Timestamp, int64(0)) }, PriceSequenceComparator: Equal, @@ -3113,30 +3119,30 @@ func TestCCIPRouter(t *testing.T) { Name: "Valid price updates but old sequence number, so updates are ignored", PriceUpdates: ccip_router.PriceUpdates{ TokenPriceUpdates: []ccip_router.TokenPriceUpdate{ - {SourceToken: wsol.mint, UsdPerToken: utils.To28BytesBE(1)}, + {SourceToken: wsol.mint, UsdPerToken: common.To28BytesBE(1)}, }, GasPriceUpdates: []ccip_router.GasPriceUpdate{ - {DestChainSelector: config.EvmChainSelector, UsdPerUnitGas: utils.To28BytesBE(1)}, + {DestChainSelector: config.EvmChainSelector, UsdPerUnitGas: common.To28BytesBE(1)}, }, }, RemainingAccounts: []solana.PublicKey{config.RouterStatePDA, wsol.billingConfigPDA, config.EvmDestChainStatePDA}, ReportContext: &oldReportContext, RunEventValidations: func(t *testing.T, tx *rpc.GetTransactionResult) { // no events as updates are ignored (but commit is still accepted) - require.ErrorContains(t, utils.ParseEvent(tx.Meta.LogMessages, "UsdPerTokenUpdated", nil, config.PrintEvents), "event not found") - require.ErrorContains(t, utils.ParseEvent(tx.Meta.LogMessages, "UsdPerUnitGasUpdated", nil, config.PrintEvents), "event not found") + require.ErrorContains(t, common.ParseEvent(tx.Meta.LogMessages, "UsdPerTokenUpdated", nil, config.PrintEvents), "event not found") + require.ErrorContains(t, common.ParseEvent(tx.Meta.LogMessages, "UsdPerUnitGasUpdated", nil, config.PrintEvents), "event not found") }, RunStateValidations: func(t *testing.T) { var wsolTokenConfig ccip_router.BillingTokenConfigWrapper - require.NoError(t, utils.GetAccountDataBorshInto(ctx, solanaGoClient, wsol.billingConfigPDA, config.DefaultCommitment, &wsolTokenConfig)) + require.NoError(t, common.GetAccountDataBorshInto(ctx, solanaGoClient, wsol.billingConfigPDA, config.DefaultCommitment, &wsolTokenConfig)) // the price is NOT the one sent in this commit - require.NotEqual(t, utils.To28BytesBE(1), wsolTokenConfig.Config.UsdPerToken.Value) + require.NotEqual(t, common.To28BytesBE(1), wsolTokenConfig.Config.UsdPerToken.Value) require.Greater(t, wsolTokenConfig.Config.UsdPerToken.Timestamp, int64(0)) var evmChainState ccip_router.DestChain - require.NoError(t, utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &evmChainState)) + require.NoError(t, common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmDestChainStatePDA, config.DefaultCommitment, &evmChainState)) // the price is NOT the one sent in this commit - require.NotEqual(t, utils.To28BytesBE(1), evmChainState.State.UsdPerUnitGas.Value) + require.NotEqual(t, common.To28BytesBE(1), evmChainState.State.UsdPerUnitGas.Value) require.Greater(t, evmChainState.State.UsdPerUnitGas.Timestamp, int64(0)) }, PriceSequenceComparator: Less, // it is an older commit, so price update is ignored and state remains ahead of this commit @@ -3147,8 +3153,8 @@ func TestCCIPRouter(t *testing.T) { for i, testcase := range priceUpdatesCases { t.Run(testcase.Name, func(t *testing.T) { - _, root := MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{1, 2, 3, uint8(i)}) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + _, root := testutils.MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{1, 2, 3, uint8(i)}) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := currentMinSeqNr @@ -3171,13 +3177,13 @@ func TestCCIPRouter(t *testing.T) { var reportSequence uint64 if testcase.ReportContext != nil { reportContext = *testcase.ReportContext - reportSequence = ParseSequenceNumber(reportContext) + reportSequence = ccip.ParseSequenceNumber(reportContext) } else { - reportContext = NextCommitReportContext() - reportSequence = ReportSequence() + reportContext = ccip.NextCommitReportContext() + reportSequence = ccip.ReportSequence() } - sigs, err := SignCommitReport(reportContext, report, signers) + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) transmitter := getTransmitter() @@ -3200,34 +3206,34 @@ func TestCCIPRouter(t *testing.T) { instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirmWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, rpc.CommitmentConfirmed, commitLookupTable, utils.AddComputeUnitLimit(MaxCU)) + tx := testutils.SendAndConfirmWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, rpc.CommitmentConfirmed, commitLookupTable, common.AddComputeUnitLimit(MaxCU)) - commitEvent := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &commitEvent, config.PrintEvents)) + commitEvent := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &commitEvent, config.PrintEvents)) require.Equal(t, config.EvmChainSelector, commitEvent.Report.SourceChainSelector) require.Equal(t, root, commitEvent.Report.MerkleRoot) require.Equal(t, minV, commitEvent.Report.MinSeqNr) require.Equal(t, maxV, commitEvent.Report.MaxSeqNr) - transmittedEvent := EventTransmitted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "Transmitted", &transmittedEvent, config.PrintEvents)) + transmittedEvent := ccip.EventTransmitted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "Transmitted", &transmittedEvent, config.PrintEvents)) require.Equal(t, config.ConfigDigest, transmittedEvent.ConfigDigest) - require.Equal(t, uint8(utils.OcrCommitPlugin), transmittedEvent.OcrPluginType) + require.Equal(t, uint8(testutils.OcrCommitPlugin), transmittedEvent.OcrPluginType) require.Equal(t, reportSequence, transmittedEvent.SequenceNumber) var chainStateAccount ccip_router.SourceChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &chainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &chainStateAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, currentMinSeqNr, chainStateAccount.State.MinSeqNr) // state now holds the "advanced outer" sequence number, which is the minimum for the next report // Do not check dest chain config, as it may have been updated by other tests in ccip onramp var rootAccount ccip_router.CommitReport - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) require.NoError(t, err, "failed to get account info") require.NotEqual(t, bin.Uint128{Lo: 0, Hi: 0}, rootAccount.Timestamp) var globalState ccip_router.GlobalState - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterStatePDA, config.DefaultCommitment, &globalState) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.RouterStatePDA, config.DefaultCommitment, &globalState) require.NoError(t, err) switch testcase.PriceSequenceComparator { @@ -3249,10 +3255,10 @@ func TestCCIPRouter(t *testing.T) { t.Run("When committing a report with an invalid source chain selector it fails", func(t *testing.T) { t.Parallel() sourceChainSelector := uint64(34) - sourceChainStatePDA, err := GetSourceChainStatePDA(sourceChainSelector) + sourceChainStatePDA, err := ccip.GetSourceChainStatePDA(sourceChainSelector) require.NoError(t, err) - _, root := MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, sourceChainSelector, config.SolanaChainSelector, []byte{4, 5, 6}) - rootPDA, err := GetCommitReportPDA(sourceChainSelector, root) + _, root := testutils.MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, sourceChainSelector, config.SolanaChainSelector, []byte{4, 5, 6}) + rootPDA, err := ccip.GetCommitReportPDA(sourceChainSelector, root) require.NoError(t, err) minV := currentMinSeqNr @@ -3267,8 +3273,8 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) transmitter := getTransmitter() instruction, err := ccip_router.NewCommitInstruction( @@ -3283,13 +3289,13 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: AccountNotInitialized"}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: AccountNotInitialized"}) }) t.Run("When committing a report with an invalid interval it fails", func(t *testing.T) { t.Parallel() - _, root := MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{4, 5, 6}) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + _, root := testutils.MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{4, 5, 6}) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := currentMinSeqNr @@ -3304,8 +3310,8 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) transmitter := getTransmitter() instruction, err := ccip_router.NewCommitInstruction( @@ -3320,13 +3326,13 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidSequenceInterval_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidSequenceInterval_CcipRouterError.String()}) }) t.Run("When committing a report with an interval size bigger than supported it fails", func(t *testing.T) { t.Parallel() - _, root := MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{4, 5, 6}) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + _, root := testutils.MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{4, 5, 6}) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := currentMinSeqNr @@ -3341,8 +3347,8 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) transmitter := getTransmitter() instruction, err := ccip_router.NewCommitInstruction( @@ -3357,13 +3363,13 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidSequenceInterval_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidSequenceInterval_CcipRouterError.String()}) }) t.Run("When committing a report with a zero merkle root it fails", func(t *testing.T) { t.Parallel() root := [32]byte{} - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := currentMinSeqNr @@ -3378,8 +3384,8 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) transmitter := getTransmitter() instruction, err := ccip_router.NewCommitInstruction( @@ -3394,13 +3400,13 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidProof_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidProof_CcipRouterError.String()}) }) t.Run("When committing a report with a repeated merkle root, it fails", func(t *testing.T) { t.Parallel() - _, root := MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{1, 2, 3, 1}) // repeated root - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + _, root := testutils.MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{1, 2, 3, 1}) // repeated root + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := currentMinSeqNr @@ -3415,8 +3421,8 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) transmitter := getTransmitter() instruction, err := ccip_router.NewCommitInstruction( @@ -3431,14 +3437,14 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Allocate: account Address", "already in use", "failed: custom program error: 0x0"}) }) t.Run("When committing a report with an invalid min interval, it fails", func(t *testing.T) { t.Parallel() - _, root := MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{4, 5, 6}) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + _, root := testutils.MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{4, 5, 6}) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := uint64(8) // this is lower than expected @@ -3453,8 +3459,8 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) transmitter := getTransmitter() instruction, err := ccip_router.NewCommitInstruction( @@ -3469,14 +3475,14 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidSequenceInterval_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip_router.InvalidSequenceInterval_CcipRouterError.String()}) }) t.Run("Invalid price updates", func(t *testing.T) { randomToken := solana.MustPublicKeyFromBase58("AGDpGy7auzgKT8zt6qhfHFm1rDwvqQGGTYxuYn7MtydQ") // just some non-existing token randomChain := uint64(123456) // just some non-existing chain - randomChainPDA, err := GetDestChainStatePDA(randomChain) + randomChainPDA, err := ccip.GetDestChainStatePDA(randomChain) require.NoError(t, err) testcases := []struct { @@ -3535,8 +3541,8 @@ func TestCCIPRouter(t *testing.T) { // TODO right now I'm allowing sending too many remaining_accounts, but if we want to be restrictive with that we can add a test here } - _, root := MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{1, 2, 3}) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + _, root := testutils.MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{1, 2, 3}) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) for _, testcase := range testcases { @@ -3550,13 +3556,13 @@ func TestCCIPRouter(t *testing.T) { for i, token := range testcase.Tokens { priceUpdates.TokenPriceUpdates[i] = ccip_router.TokenPriceUpdate{ SourceToken: token, - UsdPerToken: utils.To28BytesBE(uint64(i)), + UsdPerToken: common.To28BytesBE(uint64(i)), } } for i, chainSelector := range testcase.GasChainSelectors { priceUpdates.GasPriceUpdates[i] = ccip_router.GasPriceUpdate{ DestChainSelector: chainSelector, - UsdPerUnitGas: utils.To28BytesBE(uint64(i)), + UsdPerUnitGas: common.To28BytesBE(uint64(i)), } } @@ -3572,8 +3578,8 @@ func TestCCIPRouter(t *testing.T) { }, PriceUpdates: priceUpdates, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) raw := ccip_router.NewCommitInstruction( @@ -3595,15 +3601,15 @@ func TestCCIPRouter(t *testing.T) { instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, rpc.CommitmentConfirmed, commitLookupTable, []string{testcase.ExpectedError}, utils.AddComputeUnitLimit(MaxCU)) + testutils.SendAndFailWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, rpc.CommitmentConfirmed, commitLookupTable, []string{testcase.ExpectedError}, common.AddComputeUnitLimit(MaxCU)) }) } }) }) t.Run("When committing a report with the exact next interval, it succeeds", func(t *testing.T) { - _, root := MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{4, 5, 6}) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + _, root := testutils.MakeEvmToSolanaMessage(t, config.CcipReceiverProgram, config.EvmChainSelector, config.SolanaChainSelector, []byte{4, 5, 6}) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := currentMinSeqNr @@ -3620,8 +3626,8 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) transmitter := getTransmitter() instruction, err := ccip_router.NewCommitInstruction( @@ -3636,31 +3642,31 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - event := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + event := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) - commitEvent := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &commitEvent, config.PrintEvents)) + commitEvent := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &commitEvent, config.PrintEvents)) require.Equal(t, config.EvmChainSelector, commitEvent.Report.SourceChainSelector) require.Equal(t, root, commitEvent.Report.MerkleRoot) require.Equal(t, minV, commitEvent.Report.MinSeqNr) require.Equal(t, maxV, commitEvent.Report.MaxSeqNr) - transmittedEvent := EventTransmitted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "Transmitted", &transmittedEvent, config.PrintEvents)) + transmittedEvent := ccip.EventTransmitted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "Transmitted", &transmittedEvent, config.PrintEvents)) require.Equal(t, config.ConfigDigest, transmittedEvent.ConfigDigest) - require.Equal(t, uint8(utils.OcrCommitPlugin), transmittedEvent.OcrPluginType) - require.Equal(t, ReportSequence(), transmittedEvent.SequenceNumber) + require.Equal(t, uint8(testutils.OcrCommitPlugin), transmittedEvent.OcrPluginType) + require.Equal(t, ccip.ReportSequence(), transmittedEvent.SequenceNumber) var chainStateAccount ccip_router.SourceChain - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &chainStateAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.EvmSourceChainStatePDA, config.DefaultCommitment, &chainStateAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, currentMinSeqNr, chainStateAccount.State.MinSeqNr) // Do not check dest chain config, as it may have been updated by other tests in ccip onramp var rootAccount ccip_router.CommitReport - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) require.NoError(t, err, "failed to get account info") require.NotEqual(t, bin.Uint128{Lo: 0, Hi: 0}, rootAccount.Timestamp) }) @@ -3668,8 +3674,8 @@ func TestCCIPRouter(t *testing.T) { t.Run("Ocr3Base::Transmit edge cases", func(t *testing.T) { t.Run("It rejects mismatch config digest", func(t *testing.T) { t.Parallel() - msg, root := CreateNextMessage(ctx, solanaGoClient, t) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + msg, root := testutils.CreateNextMessage(ctx, solanaGoClient, t) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := msg.Header.SequenceNumber @@ -3684,8 +3690,8 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) transmitter := getTransmitter() emptyReportContext := [3][32]byte{} @@ -3702,13 +3708,13 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorConfigDigestMismatch.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorConfigDigestMismatch.String()}) }) t.Run("It rejects unauthorized transmitter", func(t *testing.T) { t.Parallel() - msg, root := CreateNextMessage(ctx, solanaGoClient, t) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + msg, root := testutils.CreateNextMessage(ctx, solanaGoClient, t) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := msg.Header.SequenceNumber @@ -3723,8 +3729,8 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) instruction, err := ccip_router.NewCommitInstruction( @@ -3739,13 +3745,13 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorUnauthorizedTransmitter.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorUnauthorizedTransmitter.String()}) }) t.Run("It rejects incorrect signature count", func(t *testing.T) { t.Parallel() - msg, root := CreateNextMessage(ctx, solanaGoClient, t) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + msg, root := testutils.CreateNextMessage(ctx, solanaGoClient, t) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := msg.Header.SequenceNumber @@ -3760,8 +3766,8 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - hash, err := HashCommitReport(reportContext, report) + reportContext := ccip.NextCommitReportContext() + hash, err := ccip.HashCommitReport(reportContext, report) require.NoError(t, err) baseSig := ecdsa.SignCompact(secp256k1.PrivKeyFromBytes(signers[0].PrivateKey), hash, false) @@ -3784,13 +3790,13 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorWrongNumberOfSignatures.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorWrongNumberOfSignatures.String()}) }) t.Run("It rejects invalid signature", func(t *testing.T) { t.Parallel() - msg, root := CreateNextMessage(ctx, solanaGoClient, t) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + msg, root := testutils.CreateNextMessage(ctx, solanaGoClient, t) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := msg.Header.SequenceNumber @@ -3809,7 +3815,7 @@ func TestCCIPRouter(t *testing.T) { transmitter := getTransmitter() instruction, err := ccip_router.NewCommitInstruction( - NextCommitReportContext(), + ccip.NextCommitReportContext(), report, sigs, config.RouterConfigPDA, @@ -3820,13 +3826,13 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorWrongNumberOfSignatures.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorWrongNumberOfSignatures.String()}) }) t.Run("It rejects unauthorized signer", func(t *testing.T) { t.Parallel() - msg, root := CreateNextMessage(ctx, solanaGoClient, t) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + msg, root := testutils.CreateNextMessage(ctx, solanaGoClient, t) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := msg.Header.SequenceNumber @@ -3841,11 +3847,11 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) - hash, err := HashCommitReport(reportContext, report) + hash, err := ccip.HashCommitReport(reportContext, report) require.NoError(t, err) randomPrivateKey, err := secp256k1.GeneratePrivateKey() require.NoError(t, err) @@ -3868,13 +3874,13 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorUnauthorizedSigner.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorUnauthorizedSigner.String()}) }) t.Run("It rejects duplicate signatures", func(t *testing.T) { t.Parallel() - msg, root := CreateNextMessage(ctx, solanaGoClient, t) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + msg, root := testutils.CreateNextMessage(ctx, solanaGoClient, t) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) minV := msg.Header.SequenceNumber @@ -3889,8 +3895,8 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - reportContext := NextCommitReportContext() - sigs, err := SignCommitReport(reportContext, report, signers) + reportContext := ccip.NextCommitReportContext() + sigs, err := ccip.SignCommitReport(reportContext, report, signers) require.NoError(t, err) sigs[0] = sigs[1] transmitter := getTransmitter() @@ -3907,7 +3913,7 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + Ocr3ErrorNonUniqueSignatures.String()}, utils.AddComputeUnitLimit(210_000)) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip.Ocr3ErrorNonUniqueSignatures.String()}, common.AddComputeUnitLimit(210_000)) }) }) }) @@ -3918,13 +3924,13 @@ func TestCCIPRouter(t *testing.T) { t.Run("Execute", func(t *testing.T) { var executedSequenceNumber uint64 - reportContext := NextCommitReportContext() // reuse the same commit for all executions + reportContext := ccip.NextCommitReportContext() // reuse the same commit for all executions t.Run("When executing a report with merkle tree of size 1, it succeeds", func(t *testing.T) { transmitter := getTransmitter() sourceChainSelector := config.EvmChainSelector - message, root := CreateNextMessage(ctx, solanaGoClient, t) + message, root := testutils.CreateNextMessage(ctx, solanaGoClient, t) sequenceNumber := message.Header.SequenceNumber @@ -3939,9 +3945,9 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - sigs, err := SignCommitReport(reportContext, commitReport, signers) + sigs, err := ccip.SignCommitReport(reportContext, commitReport, signers) require.NoError(t, err) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) instruction, err := ccip_router.NewCommitInstruction( @@ -3956,9 +3962,9 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, utils.AddComputeUnitLimit(210_000)) // signature verification compute unit amounts can vary depending on sorting - event := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, common.AddComputeUnitLimit(210_000)) // signature verification compute unit amounts can vary depending on sorting + event := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) executionReport := ccip_router.ExecutionReportSingleChain{ SourceChainSelector: sourceChainSelector, @@ -3988,9 +3994,9 @@ func TestCCIPRouter(t *testing.T) { instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - tx = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - executionEvent := EventExecutionStateChanged{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) + tx = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + executionEvent := ccip.EventExecutionStateChanged{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) require.NoError(t, err) require.NotNil(t, executionEvent) @@ -4001,7 +4007,7 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, ccip_router.Success_MessageExecutionState, executionEvent.State) var rootAccount ccip_router.CommitReport - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) require.NoError(t, err, "failed to get account info") require.NotEqual(t, bin.Uint128{Lo: 0, Hi: 0}, rootAccount.Timestamp) require.Equal(t, bin.Uint128{Lo: 2, Hi: 0}, rootAccount.ExecutionStates) @@ -4012,7 +4018,7 @@ func TestCCIPRouter(t *testing.T) { t.Run("When executing a report with not matching source chain selector in message, it fails", func(t *testing.T) { transmitter := getTransmitter() - message, root := CreateNextMessage(ctx, solanaGoClient, t) + message, root := testutils.CreateNextMessage(ctx, solanaGoClient, t) sequenceNumber := message.Header.SequenceNumber commitReport := ccip_router.CommitInput{ @@ -4024,9 +4030,9 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - sigs, err := SignCommitReport(reportContext, commitReport, signers) + sigs, err := ccip.SignCommitReport(reportContext, commitReport, signers) require.NoError(t, err) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) instruction, err := ccip_router.NewCommitInstruction( @@ -4041,9 +4047,9 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, utils.AddComputeUnitLimit(210_000)) // signature verification compute unit amounts can vary depending on sorting - event := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, common.AddComputeUnitLimit(210_000)) // signature verification compute unit amounts can vary depending on sorting + event := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) message.Header.SourceChainSelector = 89 @@ -4075,14 +4081,14 @@ func TestCCIPRouter(t *testing.T) { instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Message: Source chain selector not supported."}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Message: Source chain selector not supported."}) }) t.Run("When executing a report with unsupported source chain selector account, it fails", func(t *testing.T) { transmitter := getTransmitter() unsupportedChainSelector := uint64(34) - message, root := CreateNextMessage(ctx, solanaGoClient, t) + message, root := testutils.CreateNextMessage(ctx, solanaGoClient, t) sequenceNumber := message.Header.SequenceNumber commitReport := ccip_router.CommitInput{ @@ -4094,9 +4100,9 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - sigs, err := SignCommitReport(reportContext, commitReport, signers) + sigs, err := ccip.SignCommitReport(reportContext, commitReport, signers) require.NoError(t, err) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) instruction, err := ccip_router.NewCommitInstruction( @@ -4111,13 +4117,13 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, utils.AddComputeUnitLimit(210_000)) // signature verification compute unit amounts can vary depending on sorting - event := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, common.AddComputeUnitLimit(210_000)) // signature verification compute unit amounts can vary depending on sorting + event := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) - unsupportedSourceChainStatePDA, err := GetSourceChainStatePDA(unsupportedChainSelector) + unsupportedSourceChainStatePDA, err := ccip.GetSourceChainStatePDA(unsupportedChainSelector) require.NoError(t, err) - unsupportedDestChainStatePDA, err := GetDestChainStatePDA(unsupportedChainSelector) + unsupportedDestChainStatePDA, err := ccip.GetDestChainStatePDA(unsupportedChainSelector) require.NoError(t, err) message.Header.SourceChainSelector = unsupportedChainSelector message.Header.SequenceNumber = 1 @@ -4133,7 +4139,7 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) require.NotNil(t, result) executionReport := ccip_router.ExecutionReportSingleChain{ @@ -4164,16 +4170,16 @@ func TestCCIPRouter(t *testing.T) { instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"AnchorError caused by account: commit_report. Error Code: ConstraintSeeds. Error Number: 2006. Error Message: A seeds constraint was violated."}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"AnchorError caused by account: commit_report. Error Code: ConstraintSeeds. Error Number: 2006. Error Message: A seeds constraint was violated."}) }) t.Run("When executing a report with incorrect solana chain selector, it fails", func(t *testing.T) { transmitter := getTransmitter() - message, _ := CreateNextMessage(ctx, solanaGoClient, t) + message, _ := testutils.CreateNextMessage(ctx, solanaGoClient, t) message.Header.DestChainSelector = 89 // invalid dest chain selector sequenceNumber := message.Header.SequenceNumber - hash, err := HashEvmToSolanaMessage(message, config.OnRampAddress) + hash, err := ccip.HashEvmToSolanaMessage(message, config.OnRampAddress) require.NoError(t, err) root := [32]byte(hash) @@ -4186,9 +4192,9 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - sigs, err := SignCommitReport(reportContext, commitReport, signers) + sigs, err := ccip.SignCommitReport(reportContext, commitReport, signers) require.NoError(t, err) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) instruction, err := ccip_router.NewCommitInstruction( @@ -4203,9 +4209,9 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - event := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + event := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) executionReport := ccip_router.ExecutionReportSingleChain{ SourceChainSelector: config.EvmChainSelector, @@ -4235,14 +4241,14 @@ func TestCCIPRouter(t *testing.T) { instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip_router.UnsupportedDestinationChainSelector_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Code: " + ccip_router.UnsupportedDestinationChainSelector_CcipRouterError.String()}) }) t.Run("When executing a report with nonexisting PDA for the Merkle Root, it fails", func(t *testing.T) { transmitter := getTransmitter() - message, root := CreateNextMessage(ctx, solanaGoClient, t) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + message, root := testutils.CreateNextMessage(ctx, solanaGoClient, t) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) executionReport := ccip_router.ExecutionReportSingleChain{ @@ -4273,7 +4279,7 @@ func TestCCIPRouter(t *testing.T) { instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Message: The program expected this account to be already initialized."}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Error Message: The program expected this account to be already initialized."}) }) t.Run("When executing a report for an already executed message, it is skipped", func(t *testing.T) { @@ -4281,12 +4287,12 @@ func TestCCIPRouter(t *testing.T) { sourceChainSelector := config.EvmChainSelector - message := CreateDefaultMessageWith(sourceChainSelector, executedSequenceNumber) // already executed seq number - hash, err := HashEvmToSolanaMessage(message, config.OnRampAddress) + message := ccip.CreateDefaultMessageWith(sourceChainSelector, executedSequenceNumber) // already executed seq number + hash, err := ccip.HashEvmToSolanaMessage(message, config.OnRampAddress) require.NoError(t, err) root := [32]byte(hash) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) executionReport := ccip_router.ExecutionReportSingleChain{ @@ -4317,9 +4323,9 @@ func TestCCIPRouter(t *testing.T) { instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - executionEvent := EventSkippedAlreadyExecutedMessage{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "SkippedAlreadyExecutedMessage", &executionEvent, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + executionEvent := ccip.EventSkippedAlreadyExecutedMessage{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "SkippedAlreadyExecutedMessage", &executionEvent, config.PrintEvents)) require.NoError(t, err) require.NotNil(t, executionEvent) @@ -4330,12 +4336,12 @@ func TestCCIPRouter(t *testing.T) { t.Run("When executing a report for an already executed root, but not message, it succeeds", func(t *testing.T) { transmitter := getTransmitter() - message1, hash1 := CreateNextMessage(ctx, solanaGoClient, t) - message2 := CreateDefaultMessageWith(config.EvmChainSelector, message1.Header.SequenceNumber+1) - hash2, err := HashEvmToSolanaMessage(message2, config.OnRampAddress) + message1, hash1 := testutils.CreateNextMessage(ctx, solanaGoClient, t) + message2 := ccip.CreateDefaultMessageWith(config.EvmChainSelector, message1.Header.SequenceNumber+1) + hash2, err := ccip.HashEvmToSolanaMessage(message2, config.OnRampAddress) require.NoError(t, err) - root := [32]byte(MerkleFrom([][]byte{hash1[:], hash2[:]})) + root := [32]byte(ccip.MerkleFrom([][]byte{hash1[:], hash2[:]})) commitReport := ccip_router.CommitInput{ MerkleRoot: ccip_router.MerkleRoot{ @@ -4346,9 +4352,9 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - sigs, err := SignCommitReport(reportContext, commitReport, signers) + sigs, err := ccip.SignCommitReport(reportContext, commitReport, signers) require.NoError(t, err) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) instruction, err := ccip_router.NewCommitInstruction( @@ -4363,9 +4369,9 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - event := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + event := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) executionReport1 := ccip_router.ExecutionReportSingleChain{ SourceChainSelector: config.EvmChainSelector, @@ -4395,9 +4401,9 @@ func TestCCIPRouter(t *testing.T) { instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - tx = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - executionEvent := EventExecutionStateChanged{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) + tx = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + executionEvent := ccip.EventExecutionStateChanged{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) executionReport2 := ccip_router.ExecutionReportSingleChain{ SourceChainSelector: config.EvmChainSelector, @@ -4427,9 +4433,9 @@ func TestCCIPRouter(t *testing.T) { instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - tx = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - executionEvent = EventExecutionStateChanged{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) + tx = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + executionEvent = ccip.EventExecutionStateChanged{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) require.NoError(t, err) require.NotNil(t, executionEvent) @@ -4441,7 +4447,7 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, ccip_router.Success_MessageExecutionState, executionEvent.State) var rootAccount ccip_router.CommitReport - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) require.NoError(t, err, "failed to get account info") require.NotEqual(t, bin.Uint128{Lo: 0, Hi: 0}, rootAccount.Timestamp) require.Equal(t, bin.Uint128{Lo: 10, Hi: 0}, rootAccount.ExecutionStates) @@ -4454,7 +4460,7 @@ func TestCCIPRouter(t *testing.T) { stubAccountPDA, _, _ := solana.FindProgramAddress([][]byte{[]byte("counter")}, config.CcipInvalidReceiverProgram) - message, _ := CreateNextMessage(ctx, solanaGoClient, t) + message, _ := testutils.CreateNextMessage(ctx, solanaGoClient, t) message.Receiver = stubAccountPDA sequenceNumber := message.Header.SequenceNumber message.ExtraArgs.Accounts = []ccip_router.SolanaAccountMeta{ @@ -4462,7 +4468,7 @@ func TestCCIPRouter(t *testing.T) { {Pubkey: solana.SystemProgramID, IsWritable: false}, } - hash, err := HashEvmToSolanaMessage(message, config.OnRampAddress) + hash, err := ccip.HashEvmToSolanaMessage(message, config.OnRampAddress) require.NoError(t, err) root := [32]byte(hash) @@ -4475,9 +4481,9 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - sigs, err := SignCommitReport(reportContext, commitReport, signers) + sigs, err := ccip.SignCommitReport(reportContext, commitReport, signers) require.NoError(t, err) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) instruction, err := ccip_router.NewCommitInstruction( @@ -4492,9 +4498,9 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - event := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + event := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) executionReport := ccip_router.ExecutionReportSingleChain{ SourceChainSelector: config.EvmChainSelector, @@ -4526,25 +4532,25 @@ func TestCCIPRouter(t *testing.T) { // failed ccipReceiver - init account requires mutable authority // ccipSigner is not a mutable account - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"writable privilege escalated", "Cross-program invocation with unauthorized signer or writable account"}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"writable privilege escalated", "Cross-program invocation with unauthorized signer or writable account"}) }) t.Run("token happy path", func(t *testing.T) { - _, initSupply, err := utils.TokenSupply(ctx, solanaGoClient, token0.Mint.PublicKey(), config.DefaultCommitment) + _, initSupply, err := tokens.TokenSupply(ctx, solanaGoClient, token0.Mint.PublicKey(), config.DefaultCommitment) require.NoError(t, err) - _, initBal, err := utils.TokenBalance(ctx, solanaGoClient, token0.User[config.ReceiverExternalExecutionConfigPDA], config.DefaultCommitment) + _, initBal, err := tokens.TokenBalance(ctx, solanaGoClient, token0.User[config.ReceiverExternalExecutionConfigPDA], config.DefaultCommitment) require.NoError(t, err) transmitter := getTransmitter() sourceChainSelector := config.EvmChainSelector - message, _ := CreateNextMessage(ctx, solanaGoClient, t) + message, _ := testutils.CreateNextMessage(ctx, solanaGoClient, t) message.TokenAmounts = []ccip_router.Any2SolanaTokenTransfer{{ SourcePoolAddress: []byte{1, 2, 3}, DestTokenAddress: token0.Mint.PublicKey(), - Amount: utils.ToLittleEndianU256(1), + Amount: tokens.ToLittleEndianU256(1), }} - rootBytes, err := HashEvmToSolanaMessage(message, config.OnRampAddress) + rootBytes, err := ccip.HashEvmToSolanaMessage(message, config.OnRampAddress) require.NoError(t, err) root := [32]byte(rootBytes) @@ -4559,9 +4565,9 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - sigs, err := SignCommitReport(reportContext, commitReport, signers) + sigs, err := ccip.SignCommitReport(reportContext, commitReport, signers) require.NoError(t, err) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) instruction, err := ccip_router.NewCommitInstruction( @@ -4576,9 +4582,9 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - event := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + event := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) executionReport := ccip_router.ExecutionReportSingleChain{ SourceChainSelector: sourceChainSelector, @@ -4608,34 +4614,34 @@ func TestCCIPRouter(t *testing.T) { solana.NewAccountMeta(solana.SystemProgramID, false, false), ) - tokenMetas, addressTables, err := ParseTokenLookupTable(ctx, solanaGoClient, token0, token0.User[config.ReceiverExternalExecutionConfigPDA]) + tokenMetas, addressTables, err := tokens.ParseTokenLookupTable(ctx, solanaGoClient, token0, token0.User[config.ReceiverExternalExecutionConfigPDA]) require.NoError(t, err) raw.AccountMetaSlice = append(raw.AccountMetaSlice, tokenMetas...) instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - tx = utils.SendAndConfirmWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, addressTables, utils.AddComputeUnitLimit(300_000)) - executionEvent := EventExecutionStateChanged{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) + tx = testutils.SendAndConfirmWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, addressTables, common.AddComputeUnitLimit(300_000)) + executionEvent := ccip.EventExecutionStateChanged{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) require.Equal(t, ccip_router.Success_MessageExecutionState, executionEvent.State) - mintEvent := EventMintRelease{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "Minted", &mintEvent, config.PrintEvents)) + mintEvent := tokens.EventMintRelease{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "Minted", &mintEvent, config.PrintEvents)) require.Equal(t, config.ReceiverExternalExecutionConfigPDA, mintEvent.Recipient) require.Equal(t, token0.PoolSigner, mintEvent.Sender) require.Equal(t, uint64(1), mintEvent.Amount) - _, finalSupply, err := utils.TokenSupply(ctx, solanaGoClient, token0.Mint.PublicKey(), config.DefaultCommitment) + _, finalSupply, err := tokens.TokenSupply(ctx, solanaGoClient, token0.Mint.PublicKey(), config.DefaultCommitment) require.NoError(t, err) - _, finalBal, err := utils.TokenBalance(ctx, solanaGoClient, token0.User[config.ReceiverExternalExecutionConfigPDA], config.DefaultCommitment) + _, finalBal, err := tokens.TokenBalance(ctx, solanaGoClient, token0.User[config.ReceiverExternalExecutionConfigPDA], config.DefaultCommitment) require.NoError(t, err) require.Equal(t, 1, finalSupply-initSupply) require.Equal(t, 1, finalBal-initBal) }) t.Run("OffRamp Manual Execution: when executing a non-committed report, it fails", func(t *testing.T) { - message, root := CreateNextMessage(ctx, solanaGoClient, t) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + message, root := testutils.CreateNextMessage(ctx, solanaGoClient, t) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) executionReport := ccip_router.ExecutionReportSingleChain{ @@ -4666,21 +4672,21 @@ func TestCCIPRouter(t *testing.T) { instruction, err := raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"The program expected this account to be already initialized"}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"The program expected this account to be already initialized"}) }) t.Run("OffRamp Manual execution", func(t *testing.T) { transmitter := getTransmitter() - message1, _ := CreateNextMessage(ctx, solanaGoClient, t) - hash1, err := HashEvmToSolanaMessage(message1, config.OnRampAddress) + message1, _ := testutils.CreateNextMessage(ctx, solanaGoClient, t) + hash1, err := ccip.HashEvmToSolanaMessage(message1, config.OnRampAddress) require.NoError(t, err) - message2 := CreateDefaultMessageWith(config.EvmChainSelector, message1.Header.SequenceNumber+1) - hash2, err := HashEvmToSolanaMessage(message2, config.OnRampAddress) + message2 := ccip.CreateDefaultMessageWith(config.EvmChainSelector, message1.Header.SequenceNumber+1) + hash2, err := ccip.HashEvmToSolanaMessage(message2, config.OnRampAddress) require.NoError(t, err) - root := [32]byte(MerkleFrom([][]byte{hash1, hash2})) + root := [32]byte(ccip.MerkleFrom([][]byte{hash1, hash2})) commitReport := ccip_router.CommitInput{ MerkleRoot: ccip_router.MerkleRoot{ @@ -4691,9 +4697,9 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - sigs, err := SignCommitReport(reportContext, commitReport, signers) + sigs, err := ccip.SignCommitReport(reportContext, commitReport, signers) require.NoError(t, err) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) instruction, err := ccip_router.NewCommitInstruction( @@ -4708,9 +4714,9 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, utils.AddComputeUnitLimit(210_000)) - event := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, common.AddComputeUnitLimit(210_000)) + event := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) t.Run("Before elapsed time", func(t *testing.T) { t.Run("When user manually executing before the period of time has passed, it fails", func(t *testing.T) { @@ -4744,7 +4750,7 @@ func TestCCIPRouter(t *testing.T) { fmt.Printf("User: %s\n", user.PublicKey().String()) fmt.Printf("Transmitter: %s\n", transmitter.PublicKey().String()) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.ManualExecutionNotAllowed_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{ccip_router.ManualExecutionNotAllowed_CcipRouterError.String()}) }) t.Run("When transmitter manually executing before the period of time has passed, it fails", func(t *testing.T) { @@ -4775,7 +4781,7 @@ func TestCCIPRouter(t *testing.T) { instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{ccip_router.ManualExecutionNotAllowed_CcipRouterError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{ccip_router.ManualExecutionNotAllowed_CcipRouterError.String()}) }) }) @@ -4787,7 +4793,7 @@ func TestCCIPRouter(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) require.NotNil(t, result) t.Run("When user manually executing after the period of time has passed, it succeeds", func(t *testing.T) { @@ -4819,9 +4825,9 @@ func TestCCIPRouter(t *testing.T) { instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - tx = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) - executionEvent := EventExecutionStateChanged{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) + tx = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment) + executionEvent := ccip.EventExecutionStateChanged{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) require.NoError(t, err) require.NotNil(t, executionEvent) @@ -4832,7 +4838,7 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, ccip_router.Success_MessageExecutionState, executionEvent.State) var rootAccount ccip_router.CommitReport - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) require.NoError(t, err, "failed to get account info") require.NotEqual(t, bin.Uint128{Lo: 0, Hi: 0}, rootAccount.Timestamp) require.Equal(t, bin.Uint128{Lo: 2, Hi: 0}, rootAccount.ExecutionStates) @@ -4869,9 +4875,9 @@ func TestCCIPRouter(t *testing.T) { instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - tx = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - executionEvent := EventExecutionStateChanged{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) + tx = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + executionEvent := ccip.EventExecutionStateChanged{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "ExecutionStateChanged", &executionEvent, config.PrintEvents)) require.NoError(t, err) require.NotNil(t, executionEvent) @@ -4882,7 +4888,7 @@ func TestCCIPRouter(t *testing.T) { require.Equal(t, ccip_router.Success_MessageExecutionState, executionEvent.State) var rootAccount ccip_router.CommitReport - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, rootPDA, config.DefaultCommitment, &rootAccount) require.NoError(t, err, "failed to get account info") require.NotEqual(t, bin.Uint128{Lo: 0, Hi: 0}, rootAccount.Timestamp) require.Equal(t, bin.Uint128{Lo: 10, Hi: 0}, rootAccount.ExecutionStates) @@ -4897,10 +4903,10 @@ func TestCCIPRouter(t *testing.T) { // note: simple recursion execute -> ccipSend is currently not possible as the router does not implement the ccipReceive method signature t.Run("failed reentrancy A (execute) -> B (ccipReceive) -> A (ccipSend)", func(t *testing.T) { transmitter := getTransmitter() - receiverContractEvmPDA, err := getNoncePDA(config.EvmChainSelector, config.ReceiverExternalExecutionConfigPDA) + receiverContractEvmPDA, err := ccip.GetNoncePDA(config.EvmChainSelector, config.ReceiverExternalExecutionConfigPDA) require.NoError(t, err) - message, _ := CreateNextMessage(ctx, solanaGoClient, t) + message, _ := testutils.CreateNextMessage(ctx, solanaGoClient, t) // To make the message go through the validations we need to specify all additional accounts used when executing the CPI message.ExtraArgs.Accounts = []ccip_router.SolanaAccountMeta{ @@ -4915,7 +4921,7 @@ func TestCCIPRouter(t *testing.T) { {Pubkey: solana.SystemProgramID, IsWritable: false}, } - hash, err := HashEvmToSolanaMessage(message, config.OnRampAddress) + hash, err := ccip.HashEvmToSolanaMessage(message, config.OnRampAddress) require.NoError(t, err) root := [32]byte(hash) @@ -4929,7 +4935,7 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - sigs, err := SignCommitReport(reportContext, commitReport, signers) + sigs, err := ccip.SignCommitReport(reportContext, commitReport, signers) require.NoError(t, err) rootPDA, _, _ := solana.FindProgramAddress([][]byte{[]byte("commit_report"), config.EvmChainLE, root[:]}, config.CcipRouterProgram) @@ -4945,9 +4951,9 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - event := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + event := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) executionReport := ccip_router.ExecutionReportSingleChain{ SourceChainSelector: sourceChainSelector, @@ -4986,28 +4992,28 @@ func TestCCIPRouter(t *testing.T) { instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Cross-program invocation reentrancy not allowed for this instruction"}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, []string{"Cross-program invocation reentrancy not allowed for this instruction"}) }) t.Run("uninitialized token account can be manually executed", func(t *testing.T) { // create new token receiver + find address (does not actually create account, just instruction) receiver, err := solana.NewRandomPrivateKey() require.NoError(t, err) - ixATA, ata, err := utils.CreateAssociatedTokenAccount(token0.Program, token0.Mint.PublicKey(), receiver.PublicKey(), admin.PublicKey()) + ixATA, ata, err := tokens.CreateAssociatedTokenAccount(token0.Program, token0.Mint.PublicKey(), receiver.PublicKey(), admin.PublicKey()) require.NoError(t, err) token0.User[receiver.PublicKey()] = ata // create commit report --------------------- transmitter := getTransmitter() sourceChainSelector := config.EvmChainSelector - message, _ := CreateNextMessage(ctx, solanaGoClient, t) + message, _ := testutils.CreateNextMessage(ctx, solanaGoClient, t) message.TokenAmounts = []ccip_router.Any2SolanaTokenTransfer{{ SourcePoolAddress: []byte{1, 2, 3}, DestTokenAddress: token0.Mint.PublicKey(), - Amount: utils.ToLittleEndianU256(1), + Amount: tokens.ToLittleEndianU256(1), }} message.Receiver = receiver.PublicKey() - rootBytes, err := HashEvmToSolanaMessage(message, config.OnRampAddress) + rootBytes, err := ccip.HashEvmToSolanaMessage(message, config.OnRampAddress) require.NoError(t, err) root := [32]byte(rootBytes) @@ -5021,9 +5027,9 @@ func TestCCIPRouter(t *testing.T) { MerkleRoot: root, }, } - sigs, err := SignCommitReport(reportContext, commitReport, signers) + sigs, err := ccip.SignCommitReport(reportContext, commitReport, signers) require.NoError(t, err) - rootPDA, err := GetCommitReportPDA(config.EvmChainSelector, root) + rootPDA, err := ccip.GetCommitReportPDA(config.EvmChainSelector, root) require.NoError(t, err) instruction, err := ccip_router.NewCommitInstruction( reportContext, @@ -5037,9 +5043,9 @@ func TestCCIPRouter(t *testing.T) { solana.SysVarInstructionsPubkey, ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) - event := EventCommitReportAccepted{} - require.NoError(t, utils.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment) + event := ccip.EventCommitReportAccepted{} + require.NoError(t, common.ParseEvent(tx.Meta.LogMessages, "CommitReportAccepted", &event, config.PrintEvents)) // try to execute report ---------------------- // should fail because token account does not exist @@ -5064,17 +5070,17 @@ func TestCCIPRouter(t *testing.T) { config.ExternalTokenPoolsSignerPDA, ) - tokenMetas, addressTables, err := ParseTokenLookupTable(ctx, solanaGoClient, token0, token0.User[receiver.PublicKey()]) + tokenMetas, addressTables, err := tokens.ParseTokenLookupTable(ctx, solanaGoClient, token0, token0.User[receiver.PublicKey()]) require.NoError(t, err) raw.AccountMetaSlice = append(raw.AccountMetaSlice, tokenMetas...) instruction, err = raw.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, addressTables, []string{"AccountNotInitialized"}) + testutils.SendAndFailWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{instruction}, transmitter, config.DefaultCommitment, addressTables, []string{"AccountNotInitialized"}) // create associated token account for user -------------------- - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixATA}, admin, config.DefaultCommitment) - _, initBal, err := utils.TokenBalance(ctx, solanaGoClient, token0.User[receiver.PublicKey()], config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixATA}, admin, config.DefaultCommitment) + _, initBal, err := tokens.TokenBalance(ctx, solanaGoClient, token0.User[receiver.PublicKey()], config.DefaultCommitment) require.NoError(t, err) require.Equal(t, 0, initBal) @@ -5092,14 +5098,14 @@ func TestCCIPRouter(t *testing.T) { config.ExternalTokenPoolsSignerPDA, ) - tokenMetas, addressTables, err = ParseTokenLookupTable(ctx, solanaGoClient, token0, token0.User[receiver.PublicKey()]) + tokenMetas, addressTables, err = tokens.ParseTokenLookupTable(ctx, solanaGoClient, token0, token0.User[receiver.PublicKey()]) require.NoError(t, err) rawManual.AccountMetaSlice = append(rawManual.AccountMetaSlice, tokenMetas...) instruction, err = rawManual.ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirmWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment, addressTables) + testutils.SendAndConfirmWithLookupTables(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment, addressTables) - _, finalBal, err := utils.TokenBalance(ctx, solanaGoClient, token0.User[receiver.PublicKey()], config.DefaultCommitment) + _, finalBal, err := tokens.TokenBalance(ctx, solanaGoClient, token0.User[receiver.PublicKey()], config.DefaultCommitment) require.NoError(t, err) require.Equal(t, 1, finalBal-initBal) }) diff --git a/chains/solana/contracts/tests/ccip/tokenpool_test.go b/chains/solana/contracts/tests/ccip/tokenpool_test.go index f6ba3b9de..077dd5e0e 100644 --- a/chains/solana/contracts/tests/ccip/tokenpool_test.go +++ b/chains/solana/contracts/tests/ccip/tokenpool_test.go @@ -12,8 +12,10 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/token_pool" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens" ) func TestTokenPool(t *testing.T) { @@ -27,7 +29,7 @@ func TestTokenPool(t *testing.T) { require.NoError(t, err) ctx := tests.Context(t) - solanaGoClient := utils.DeployAllPrograms(t, utils.PathToAnchorConfig, admin) + solanaGoClient := testutils.DeployAllPrograms(t, testutils.PathToAnchorConfig, admin) getBalance := func(account solana.PublicKey) string { balanceRes, err := solanaGoClient.GetTokenAccountBalance(ctx, account, config.DefaultCommitment) require.NoError(t, err) @@ -38,7 +40,7 @@ func TestTokenPool(t *testing.T) { remoteToken := []byte{4, 5, 6} t.Run("setup:funding", func(t *testing.T) { - utils.FundAccounts(ctx, []solana.PrivateKey{admin, anotherAdmin}, solanaGoClient, t) + testutils.FundAccounts(ctx, []solana.PrivateKey{admin, anotherAdmin}, solanaGoClient, t) }) // test functionality with token & token-2022 standards @@ -55,40 +57,40 @@ func TestTokenPool(t *testing.T) { amount := uint64(1000) for _, poolType := range []token_pool.PoolType{token_pool.LockAndRelease_PoolType, token_pool.BurnAndMint_PoolType} { - p, err := NewTokenPool(v.tokenProgram) + p, err := tokens.NewTokenPool(v.tokenProgram) require.NoError(t, err) mint := p.Mint.PublicKey() t.Run("setup:token", func(t *testing.T) { // create token - instructions, err := utils.CreateToken(ctx, v.tokenProgram, mint, admin.PublicKey(), decimals, solanaGoClient, config.DefaultCommitment) + instructions, err := tokens.CreateToken(ctx, v.tokenProgram, mint, admin.PublicKey(), decimals, solanaGoClient, config.DefaultCommitment) require.NoError(t, err) // create admin associated token account - createI, tokenAccount, err := utils.CreateAssociatedTokenAccount(v.tokenProgram, mint, admin.PublicKey(), admin.PublicKey()) + createI, tokenAccount, err := tokens.CreateAssociatedTokenAccount(v.tokenProgram, mint, admin.PublicKey(), admin.PublicKey()) require.NoError(t, err) p.User[admin.PublicKey()] = tokenAccount // set admin token account // mint tokens to admin - mintToI, err := utils.MintTo(amount, v.tokenProgram, mint, tokenAccount, admin.PublicKey()) + mintToI, err := tokens.MintTo(amount, v.tokenProgram, mint, tokenAccount, admin.PublicKey()) require.NoError(t, err) instructions = append(instructions, createI, mintToI) - utils.SendAndConfirm(ctx, t, solanaGoClient, instructions, admin, config.DefaultCommitment, utils.AddSigners(p.Mint)) + testutils.SendAndConfirm(ctx, t, solanaGoClient, instructions, admin, config.DefaultCommitment, common.AddSigners(p.Mint)) // validate - outDec, outVal, err := utils.TokenBalance(ctx, solanaGoClient, p.User[admin.PublicKey()], config.DefaultCommitment) + outDec, outVal, err := tokens.TokenBalance(ctx, solanaGoClient, p.User[admin.PublicKey()], config.DefaultCommitment) require.NoError(t, err) require.Equal(t, int(amount), outVal) require.Equal(t, decimals, outDec) }) t.Run("pool:"+poolType.String(), func(t *testing.T) { - poolConfig, err := TokenPoolConfigAddress(mint) + poolConfig, err := tokens.TokenPoolConfigAddress(mint) require.NoError(t, err) - poolSigner, err := TokenPoolSignerAddress(mint) + poolSigner, err := tokens.TokenPoolSignerAddress(mint) require.NoError(t, err) - createI, poolTokenAccount, err := utils.CreateAssociatedTokenAccount(v.tokenProgram, mint, poolSigner, admin.PublicKey()) + createI, poolTokenAccount, err := tokens.CreateAssociatedTokenAccount(v.tokenProgram, mint, poolSigner, admin.PublicKey()) require.NoError(t, err) // LockAndRelease => [Lock, Release] @@ -103,7 +105,7 @@ func TestTokenPool(t *testing.T) { require.NoError(t, err) // make pool mint_authority for token (required for burn/mint) - authI, err := utils.SetTokenMintAuthority(v.tokenProgram, poolSigner, mint, admin.PublicKey()) + authI, err := tokens.SetTokenMintAuthority(v.tokenProgram, poolSigner, mint, admin.PublicKey()) require.NoError(t, err) // set pool config @@ -125,23 +127,23 @@ func TestTokenPool(t *testing.T) { }, poolConfig, p.Chain[config.EvmChainSelector], admin.PublicKey(), solana.SystemProgramID).ValidateAndBuild() require.NoError(t, err) - res := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{poolInitI, createI, authI, ixConfigure, ixRates}, admin, config.DefaultCommitment) + res := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{poolInitI, createI, authI, ixConfigure, ixRates}, admin, config.DefaultCommitment) require.NotNil(t, res) var configAccount token_pool.Config - require.NoError(t, utils.GetAccountDataBorshInto(ctx, solanaGoClient, poolConfig, config.DefaultCommitment, &configAccount)) + require.NoError(t, common.GetAccountDataBorshInto(ctx, solanaGoClient, poolConfig, config.DefaultCommitment, &configAccount)) require.Equal(t, poolTokenAccount, configAccount.PoolTokenAccount) - eventConfigured := EventChainConfigured{} - require.NoError(t, utils.ParseEvent(res.Meta.LogMessages, "RemoteChainConfigured", &eventConfigured, config.PrintEvents)) + eventConfigured := tokens.EventChainConfigured{} + require.NoError(t, common.ParseEvent(res.Meta.LogMessages, "RemoteChainConfigured", &eventConfigured, config.PrintEvents)) require.Equal(t, config.EvmChainSelector, eventConfigured.ChainSelector) require.Equal(t, remotePool, eventConfigured.PoolAddress) require.Equal(t, 0, len(eventConfigured.PreviousPoolAddress)) require.Equal(t, remoteToken, eventConfigured.Token) require.Equal(t, 0, len(eventConfigured.PreviousToken)) - eventRateLimit := EventRateLimitConfigured{} - require.NoError(t, utils.ParseEvent(res.Meta.LogMessages, "RateLimitConfigured", &eventRateLimit, config.PrintEvents)) + eventRateLimit := tokens.EventRateLimitConfigured{} + require.NoError(t, common.ParseEvent(res.Meta.LogMessages, "RateLimitConfigured", &eventRateLimit, config.PrintEvents)) require.Equal(t, config.EvmChainSelector, eventRateLimit.ChainSelector) require.Equal(t, true, eventRateLimit.InboundRateLimit.Enabled) require.Equal(t, amount, eventRateLimit.InboundRateLimit.Capacity) @@ -159,7 +161,7 @@ func TestTokenPool(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) // Successfully accept ownership @@ -169,14 +171,14 @@ func TestTokenPool(t *testing.T) { anotherAdmin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) + result = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) require.NotNil(t, result) }) t.Run(lockOrBurn, func(t *testing.T) { require.Equal(t, fmt.Sprintf("%d", amount), getBalance(p.User[admin.PublicKey()])) - transferI, err := utils.TokenTransferChecked(amount, decimals, v.tokenProgram, p.User[admin.PublicKey()], mint, poolTokenAccount, admin.PublicKey(), solana.PublicKeySlice{}) + transferI, err := tokens.TokenTransferChecked(amount, decimals, v.tokenProgram, p.User[admin.PublicKey()], mint, poolTokenAccount, admin.PublicKey(), solana.PublicKeySlice{}) require.NoError(t, err) lbI, err := token_pool.NewLockOrBurnTokensInstruction(token_pool.LockOrBurnInV1{ @@ -186,11 +188,11 @@ func TestTokenPool(t *testing.T) { }, admin.PublicKey(), poolConfig, v.tokenProgram, mint, poolSigner, poolTokenAccount, p.Chain[config.EvmChainSelector]).ValidateAndBuild() require.NoError(t, err) - res := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{transferI, lbI}, admin, config.DefaultCommitment) + res := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{transferI, lbI}, admin, config.DefaultCommitment) require.NotNil(t, res) - event := EventBurnLock{} - require.NoError(t, utils.ParseEvent(res.Meta.LogMessages, MethodToEvent(lockOrBurn), &event)) + event := tokens.EventBurnLock{} + require.NoError(t, common.ParseEvent(res.Meta.LogMessages, tokens.MethodToEvent(lockOrBurn), &event)) require.Equal(t, admin.PublicKey(), event.Sender) require.Equal(t, amount, event.Amount) @@ -209,17 +211,17 @@ func TestTokenPool(t *testing.T) { rmI, err := token_pool.NewReleaseOrMintTokensInstruction(token_pool.ReleaseOrMintInV1{ LocalToken: mint, SourcePoolAddress: remotePool, - Amount: utils.ToLittleEndianU256(amount * 1e9), // scale to proper decimals + Amount: tokens.ToLittleEndianU256(amount * 1e9), // scale to proper decimals Receiver: admin.PublicKey(), RemoteChainSelector: config.EvmChainSelector, }, admin.PublicKey(), poolConfig, v.tokenProgram, mint, poolSigner, poolTokenAccount, p.Chain[config.EvmChainSelector], p.User[admin.PublicKey()]).ValidateAndBuild() require.NoError(t, err) - res := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{rmI}, admin, config.DefaultCommitment) + res := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{rmI}, admin, config.DefaultCommitment) require.NotNil(t, res) - event := EventMintRelease{} - require.NoError(t, utils.ParseEvent(res.Meta.LogMessages, MethodToEvent(releaseOrMint), &event)) + event := tokens.EventMintRelease{} + require.NoError(t, common.ParseEvent(res.Meta.LogMessages, tokens.MethodToEvent(releaseOrMint), &event)) require.Equal(t, admin.PublicKey(), event.Recipient) require.Equal(t, poolSigner, event.Sender) require.Equal(t, amount, event.Amount) @@ -283,7 +285,7 @@ func TestTokenPool(t *testing.T) { ixRates, err := token_pool.NewSetChainRateLimitInstruction(config.EvmChainSelector, p.Mint.PublicKey(), cfg.c, token_pool.RateLimitConfig{}, poolConfig, p.Chain[config.EvmChainSelector], anotherAdmin.PublicKey(), solana.SystemProgramID).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ixRates}, anotherAdmin, config.DefaultCommitment, []string{cfg.errStr}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ixRates}, anotherAdmin, config.DefaultCommitment, []string{cfg.errStr}) }) } }) @@ -295,27 +297,27 @@ func TestTokenPool(t *testing.T) { rmI, err := token_pool.NewReleaseOrMintTokensInstruction(token_pool.ReleaseOrMintInV1{ LocalToken: mint, SourcePoolAddress: remotePool, - Amount: utils.ToLittleEndianU256(amount * amount * 1e9), + Amount: tokens.ToLittleEndianU256(amount * amount * 1e9), Receiver: admin.PublicKey(), RemoteChainSelector: config.EvmChainSelector, }, admin.PublicKey(), poolConfig, v.tokenProgram, mint, poolSigner, poolTokenAccount, p.Chain[config.EvmChainSelector], p.User[admin.PublicKey()]).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{rmI}, admin, config.DefaultCommitment, []string{"max capacity exceeded"}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{rmI}, admin, config.DefaultCommitment, []string{"max capacity exceeded"}) // exceed rate limit of transfer // request two release/mint of max capacity // if first does not exceed limit, the second one should - transferI, err := utils.TokenTransferChecked(amount, decimals, v.tokenProgram, p.User[admin.PublicKey()], mint, poolTokenAccount, admin.PublicKey(), solana.PublicKeySlice{}) // ensure pool is funded + transferI, err := tokens.TokenTransferChecked(amount, decimals, v.tokenProgram, p.User[admin.PublicKey()], mint, poolTokenAccount, admin.PublicKey(), solana.PublicKeySlice{}) // ensure pool is funded require.NoError(t, err) rmI, err = token_pool.NewReleaseOrMintTokensInstruction(token_pool.ReleaseOrMintInV1{ LocalToken: mint, SourcePoolAddress: remotePool, - Amount: utils.ToLittleEndianU256(amount * 1e9), + Amount: tokens.ToLittleEndianU256(amount * 1e9), Receiver: admin.PublicKey(), RemoteChainSelector: config.EvmChainSelector, }, admin.PublicKey(), poolConfig, v.tokenProgram, mint, poolSigner, poolTokenAccount, p.Chain[config.EvmChainSelector], p.User[admin.PublicKey()]).ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{transferI, rmI, rmI}, admin, config.DefaultCommitment, []string{"rate limit reached"}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{transferI, rmI, rmI}, admin, config.DefaultCommitment, []string{"rate limit reached"}) // pool should refill automatically, but slowly // small amount should pass @@ -323,12 +325,12 @@ func TestTokenPool(t *testing.T) { rmI, err = token_pool.NewReleaseOrMintTokensInstruction(token_pool.ReleaseOrMintInV1{ LocalToken: mint, SourcePoolAddress: remotePool, - Amount: utils.ToLittleEndianU256(1e9), + Amount: tokens.ToLittleEndianU256(1e9), Receiver: admin.PublicKey(), RemoteChainSelector: config.EvmChainSelector, }, admin.PublicKey(), poolConfig, v.tokenProgram, mint, poolSigner, poolTokenAccount, p.Chain[config.EvmChainSelector], p.User[admin.PublicKey()]).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{transferI, rmI}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{transferI, rmI}, admin, config.DefaultCommitment) }) }) @@ -339,15 +341,15 @@ func TestTokenPool(t *testing.T) { ixRouterChange, err := token_pool.NewSetRampAuthorityInstruction(config.ExternalTokenPoolsSignerPDA, poolConfig, anotherAdmin.PublicKey()).ValidateAndBuild() require.NoError(t, err) - res := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixDelete, ixRouterChange}, anotherAdmin, config.DefaultCommitment) + res := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ixDelete, ixRouterChange}, anotherAdmin, config.DefaultCommitment) require.NotNil(t, res) - eventDelete := EventChainRemoved{} - require.NoError(t, utils.ParseEvent(res.Meta.LogMessages, "RemoteChainRemoved", &eventDelete, config.PrintEvents)) + eventDelete := tokens.EventChainRemoved{} + require.NoError(t, common.ParseEvent(res.Meta.LogMessages, "RemoteChainRemoved", &eventDelete, config.PrintEvents)) require.Equal(t, config.EvmChainSelector, eventDelete.ChainSelector) - eventRouter := EventRouterUpdated{} - require.NoError(t, utils.ParseEvent(res.Meta.LogMessages, "RouterUpdated", &eventRouter, config.PrintEvents)) + eventRouter := tokens.EventRouterUpdated{} + require.NoError(t, common.ParseEvent(res.Meta.LogMessages, "RouterUpdated", &eventRouter, config.PrintEvents)) require.Equal(t, config.ExternalTokenPoolsSignerPDA, eventRouter.NewAuthority) require.Equal(t, admin.PublicKey(), eventRouter.OldAuthority) }) @@ -359,19 +361,19 @@ func TestTokenPool(t *testing.T) { // test functionality with arbitrary wrapped program t.Run("Wrapped", func(t *testing.T) { t.Parallel() - p, err := NewTokenPool(solana.TokenProgramID) + p, err := tokens.NewTokenPool(solana.TokenProgramID) require.NoError(t, err) mint := p.Mint.PublicKey() t.Run("setup:pool", func(t *testing.T) { var err error - p.PoolConfig, err = TokenPoolConfigAddress(mint) + p.PoolConfig, err = tokens.TokenPoolConfigAddress(mint) require.NoError(t, err) - p.PoolSigner, err = TokenPoolSignerAddress(mint) + p.PoolSigner, err = tokens.TokenPoolSignerAddress(mint) require.NoError(t, err) // create token - instructions, err := utils.CreateToken(ctx, solana.TokenProgramID, mint, admin.PublicKey(), 0, solanaGoClient, config.DefaultCommitment) + instructions, err := tokens.CreateToken(ctx, solana.TokenProgramID, mint, admin.PublicKey(), 0, solanaGoClient, config.DefaultCommitment) require.NoError(t, err) // create pool @@ -380,7 +382,7 @@ func TestTokenPool(t *testing.T) { // create pool token account var createI solana.Instruction - createI, p.PoolTokenAccount, err = utils.CreateAssociatedTokenAccount(solana.TokenProgramID, mint, p.PoolSigner, admin.PublicKey()) + createI, p.PoolTokenAccount, err = tokens.CreateAssociatedTokenAccount(solana.TokenProgramID, mint, p.PoolSigner, admin.PublicKey()) require.NoError(t, err) // set pool config @@ -390,7 +392,7 @@ func TestTokenPool(t *testing.T) { }, p.PoolConfig, p.Chain[config.EvmChainSelector], admin.PublicKey(), solana.SystemProgramID).ValidateAndBuild() require.NoError(t, err) - res := utils.SendAndConfirm(ctx, t, solanaGoClient, append(instructions, poolInitI, createI, configureI), admin, config.DefaultCommitment, utils.AddSigners(p.Mint)) + res := testutils.SendAndConfirm(ctx, t, solanaGoClient, append(instructions, poolInitI, createI, configureI), admin, config.DefaultCommitment, common.AddSigners(p.Mint)) require.NotNil(t, res) }) @@ -400,7 +402,7 @@ func TestTokenPool(t *testing.T) { lbI, err := raw.ValidateAndBuild() require.NoError(t, err) - res := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{lbI}, admin, config.DefaultCommitment) + res := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{lbI}, admin, config.DefaultCommitment) require.NotNil(t, res) require.Contains(t, strings.Join(res.Meta.LogMessages, "\n"), "Called `ccip_token_lock_burn`") }) @@ -411,13 +413,13 @@ func TestTokenPool(t *testing.T) { SourcePoolAddress: remotePool, Receiver: p.PoolSigner, RemoteChainSelector: config.EvmChainSelector, - Amount: utils.ToLittleEndianU256(1), + Amount: tokens.ToLittleEndianU256(1), }, admin.PublicKey(), p.PoolConfig, solana.TokenProgramID, mint, p.PoolSigner, p.PoolTokenAccount, p.Chain[config.EvmChainSelector], p.PoolTokenAccount) raw.AccountMetaSlice = append(raw.AccountMetaSlice, solana.NewAccountMeta(config.CcipReceiverProgram, false, false)) rmI, err := raw.ValidateAndBuild() require.NoError(t, err) - res := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{rmI}, admin, config.DefaultCommitment) + res := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{rmI}, admin, config.DefaultCommitment) require.NotNil(t, res) require.Contains(t, strings.Join(res.Meta.LogMessages, "\n"), "Called `ccip_token_release_mint`") }) diff --git a/chains/solana/contracts/tests/config/access_controller_config.go b/chains/solana/contracts/tests/config/accesscontroller_config.go similarity index 100% rename from chains/solana/contracts/tests/config/access_controller_config.go rename to chains/solana/contracts/tests/config/accesscontroller_config.go diff --git a/chains/solana/contracts/tests/config/ccip_config.go b/chains/solana/contracts/tests/config/ccip_config.go index e8c564f49..7eb6f7d01 100644 --- a/chains/solana/contracts/tests/config/ccip_config.go +++ b/chains/solana/contracts/tests/config/ccip_config.go @@ -6,7 +6,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" ) var ( @@ -33,7 +33,7 @@ var ( SolanaChainSelector uint64 = 15 EvmChainSelector uint64 = 21 - EvmChainLE = utils.Uint64ToLE(EvmChainSelector) + EvmChainLE = common.Uint64ToLE(EvmChainSelector) SolanaSourceChainStatePDA, _, _ = solana.FindProgramAddress([][]byte{[]byte("source_chain_state"), binary.LittleEndian.AppendUint64([]byte{}, SolanaChainSelector)}, CcipRouterProgram) SolanaDestChainStatePDA, _, _ = solana.FindProgramAddress([][]byte{[]byte("dest_chain_state"), binary.LittleEndian.AppendUint64([]byte{}, SolanaChainSelector)}, CcipRouterProgram) @@ -45,7 +45,7 @@ var ( MaxOracles = 16 OcrF uint8 = 5 - ConfigDigest = utils.MakeRandom32ByteArray() + ConfigDigest = common.MakeRandom32ByteArray() Empty24Byte = [24]byte{} MaxSignersAndTransmitters = 16 ) diff --git a/chains/solana/contracts/tests/config/config.go b/chains/solana/contracts/tests/config/config.go deleted file mode 100644 index 1b96b1f77..000000000 --- a/chains/solana/contracts/tests/config/config.go +++ /dev/null @@ -1,41 +0,0 @@ -package config - -type Config struct { - ChainName string - ChainID string - RPCUrls []string - WSUrls []string - ProgramAddresses *ProgramAddresses - PrivateKey string -} - -type ProgramAddresses struct { - OCR2 string - AccessController string - Store string -} - -func DevnetConfig() *Config { - return &Config{ - ChainName: "solana", - ChainID: "devnet", - // Will be overridden if set in toml - RPCUrls: []string{"https://api.devnet.solana.com"}, - WSUrls: []string{"wss://api.devnet.solana.com/"}, - } -} - -func LocalNetConfig() *Config { - return &Config{ - ChainName: "solana", - ChainID: "localnet", - // Will be overridden if set in toml - RPCUrls: []string{"http://sol:8899"}, - WSUrls: []string{"ws://sol:8900"}, - ProgramAddresses: &ProgramAddresses{ - OCR2: "E3j24rx12SyVsG6quKuZPbQqZPkhAUCh8Uek4XrKYD2x", - AccessController: "2ckhep7Mvy1dExenBqpcdevhRu7CLuuctMcx7G9mWEvo", - Store: "9kRNTZmoZSiTBuXC62dzK9E7gC7huYgcmRRhYv3i4osC", - }, - } -} diff --git a/chains/solana/contracts/tests/lookuptable_test.go b/chains/solana/contracts/tests/lookuptable_test.go index f58408a39..3de9f3cb4 100644 --- a/chains/solana/contracts/tests/lookuptable_test.go +++ b/chains/solana/contracts/tests/lookuptable_test.go @@ -13,19 +13,20 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" ) func TestSolanaLookupTables(t *testing.T) { t.Parallel() ctx := tests.Context(t) - url := utils.SetupLocalSolNode(t) + url := testutils.SetupLocalSolNode(t) c := rpc.New(url) sender, err := solana.NewRandomPrivateKey() require.NoError(t, err) - utils.FundAccounts(ctx, []solana.PrivateKey{sender}, c, t) + testutils.FundAccounts(ctx, []solana.PrivateKey{sender}, c, t) // transfer instructions pubkeys := solana.PublicKeySlice{} @@ -41,17 +42,17 @@ func TestSolanaLookupTables(t *testing.T) { // create lookup table slot, serr := c.GetSlot(ctx, rpc.CommitmentFinalized) require.NoError(t, serr) - table, instruction, ierr := utils.NewCreateLookupTableInstruction( + table, instruction, ierr := common.NewCreateLookupTableInstruction( sender.PublicKey(), sender.PublicKey(), slot, ) require.NoError(t, ierr) - utils.SendAndConfirm(ctx, t, c, []solana.Instruction{instruction}, sender, rpc.CommitmentConfirmed) + testutils.SendAndConfirm(ctx, t, c, []solana.Instruction{instruction}, sender, rpc.CommitmentConfirmed) // add entries to lookup table - utils.SendAndConfirm(ctx, t, c, []solana.Instruction{ - utils.NewExtendLookupTableInstruction( + testutils.SendAndConfirm(ctx, t, c, []solana.Instruction{ + common.NewExtendLookupTableInstruction( table, sender.PublicKey(), sender.PublicKey(), k, ), diff --git a/chains/solana/contracts/tests/mcms/mcm_multiple_multisigs_test.go b/chains/solana/contracts/tests/mcms/mcm_multiple_multisigs_test.go index 8e6094179..c7271c0d0 100644 --- a/chains/solana/contracts/tests/mcms/mcm_multiple_multisigs_test.go +++ b/chains/solana/contracts/tests/mcms/mcm_multiple_multisigs_test.go @@ -12,10 +12,11 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/eth" - mcmsUtils "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/mcms" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/mcm" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/eth" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/mcms" ) func TestMcmMultipleMultisigs(t *testing.T) { @@ -27,26 +28,26 @@ func TestMcmMultipleMultisigs(t *testing.T) { admin, err := solana.NewRandomPrivateKey() require.NoError(t, err) - solanaGoClient := utils.DeployAllPrograms(t, utils.PathToAnchorConfig, admin) + solanaGoClient := testutils.DeployAllPrograms(t, testutils.PathToAnchorConfig, admin) // mcm multisig 1 - testMsigName1, err := mcmsUtils.PadString32("test_mcm_instance_1") + testMsigName1, err := mcms.PadString32("test_mcm_instance_1") require.NoError(t, err) - multisigConfigPDA1 := McmConfigAddress(testMsigName1) - rootMetadataPDA1 := RootMetadataAddress(testMsigName1) - expiringRootAndOpCountPDA1 := ExpiringRootAndOpCountAddress(testMsigName1) - configSignersPDA1 := McmConfigSignersAddress(testMsigName1) + multisigConfigPDA1 := mcms.McmConfigAddress(testMsigName1) + rootMetadataPDA1 := mcms.RootMetadataAddress(testMsigName1) + expiringRootAndOpCountPDA1 := mcms.ExpiringRootAndOpCountAddress(testMsigName1) + configSignersPDA1 := mcms.McmConfigSignersAddress(testMsigName1) // mcm multisig 2 - testMsigName2, err := mcmsUtils.PadString32("test_mcm_instance_2") + testMsigName2, err := mcms.PadString32("test_mcm_instance_2") require.NoError(t, err) - multisigConfigPDA2 := McmConfigAddress(testMsigName2) - rootMetadataPDA2 := RootMetadataAddress(testMsigName2) - expiringRootAndOpCountPDA2 := ExpiringRootAndOpCountAddress(testMsigName2) - configSignersPDA2 := McmConfigSignersAddress(testMsigName2) + multisigConfigPDA2 := mcms.McmConfigAddress(testMsigName2) + rootMetadataPDA2 := mcms.RootMetadataAddress(testMsigName2) + expiringRootAndOpCountPDA2 := mcms.ExpiringRootAndOpCountAddress(testMsigName2) + configSignersPDA2 := mcms.McmConfigSignersAddress(testMsigName2) t.Run("setup:funding", func(t *testing.T) { - utils.FundAccounts(ctx, []solana.PrivateKey{admin}, solanaGoClient, t) + testutils.FundAccounts(ctx, []solana.PrivateKey{admin}, solanaGoClient, t) }) t.Run("setup:test_mcm_instance_1", func(t *testing.T) { @@ -76,11 +77,11 @@ func TestMcmMultipleMultisigs(t *testing.T) { expiringRootAndOpCountPDA1, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) // get config and validate var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA1, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA1, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -101,7 +102,7 @@ func TestMcmMultipleMultisigs(t *testing.T) { groupQuorums := []uint8{1, 1, 1, 1, 1, 1, 1, 1, 1, 1} groupParents := []uint8{0, 0, 0, 2, 0, 0, 0, 0, 0, 0} - mcmConfig, err := mcmsUtils.NewValidMcmConfig( + mcmConfig, err := mcms.NewValidMcmConfig( testMsigName1, signerPrivateKeys, signerGroups, @@ -116,7 +117,7 @@ func TestMcmMultipleMultisigs(t *testing.T) { t.Run("mcm:set_config: preload signers on PDA", func(t *testing.T) { ixs := make([]solana.Instruction, 0) - parsedTotalSigners, err := mcmsUtils.SafeToUint8(len(signerAddresses)) + parsedTotalSigners, err := mcms.SafeToUint8(len(signerAddresses)) require.NoError(t, err) initSignersIx, err := mcm.NewInitSignersInstruction( @@ -131,7 +132,7 @@ func TestMcmMultipleMultisigs(t *testing.T) { require.NoError(t, err) ixs = append(ixs, initSignersIx) - appendSignersIxs, err := AppendSignersIxs(signerAddresses, testMsigName1, multisigConfigPDA1, configSignersPDA1, admin.PublicKey(), config.MaxAppendSignerBatchSize) + appendSignersIxs, err := mcms.AppendSignersIxs(signerAddresses, testMsigName1, multisigConfigPDA1, configSignersPDA1, admin.PublicKey(), config.MaxAppendSignerBatchSize) require.NoError(t, err) ixs = append(ixs, appendSignersIxs...) @@ -145,11 +146,11 @@ func TestMcmMultipleMultisigs(t *testing.T) { ixs = append(ixs, finalizeSignersIx) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } var cfgSignersAccount mcm.ConfigSigners - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA1, config.DefaultCommitment, &cfgSignersAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA1, config.DefaultCommitment, &cfgSignersAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, true, cfgSignersAccount.IsFinalized) @@ -175,12 +176,12 @@ func TestMcmMultipleMultisigs(t *testing.T) { require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) require.NotNil(t, result) // get config and validate var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA1, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA1, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -195,7 +196,7 @@ func TestMcmMultipleMultisigs(t *testing.T) { } // pda closed after set_config - utils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA1, config.DefaultCommitment) + testutils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA1, config.DefaultCommitment) }) }) }) @@ -225,11 +226,11 @@ func TestMcmMultipleMultisigs(t *testing.T) { expiringRootAndOpCountPDA2, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) // get config and validate var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA2, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA2, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -249,7 +250,7 @@ func TestMcmMultipleMultisigs(t *testing.T) { groupQuorums := []uint8{1, 1, 1, 1, 1, 1, 1, 1, 1, 1} groupParents := []uint8{0, 0, 0, 2, 0, 0, 0, 0, 0, 0} - mcmConfig, err := mcmsUtils.NewValidMcmConfig( + mcmConfig, err := mcms.NewValidMcmConfig( testMsigName2, signerPrivateKeys, signerGroups, @@ -264,7 +265,7 @@ func TestMcmMultipleMultisigs(t *testing.T) { t.Run("mcm:set_config: preload signers on PDA", func(t *testing.T) { ixs := make([]solana.Instruction, 0) - parsedTotalSigners, err := mcmsUtils.SafeToUint8(len(signerAddresses)) + parsedTotalSigners, err := mcms.SafeToUint8(len(signerAddresses)) require.NoError(t, err) initSignersIx, err := mcm.NewInitSignersInstruction( @@ -279,7 +280,7 @@ func TestMcmMultipleMultisigs(t *testing.T) { require.NoError(t, err) ixs = append(ixs, initSignersIx) - appendSignersIxs, err := AppendSignersIxs(signerAddresses, testMsigName2, multisigConfigPDA2, configSignersPDA2, admin.PublicKey(), config.MaxAppendSignerBatchSize) + appendSignersIxs, err := mcms.AppendSignersIxs(signerAddresses, testMsigName2, multisigConfigPDA2, configSignersPDA2, admin.PublicKey(), config.MaxAppendSignerBatchSize) require.NoError(t, err) ixs = append(ixs, appendSignersIxs...) @@ -293,11 +294,11 @@ func TestMcmMultipleMultisigs(t *testing.T) { ixs = append(ixs, finalizeSignersIx) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } var cfgSignersAccount mcm.ConfigSigners - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA2, config.DefaultCommitment, &cfgSignersAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA2, config.DefaultCommitment, &cfgSignersAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, true, cfgSignersAccount.IsFinalized) @@ -323,7 +324,7 @@ func TestMcmMultipleMultisigs(t *testing.T) { require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment, []string{"Error Code: " + "ConstraintSeeds"}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment, []string{"Error Code: " + "ConstraintSeeds"}) require.NotNil(t, result) }) @@ -342,12 +343,12 @@ func TestMcmMultipleMultisigs(t *testing.T) { require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) require.NotNil(t, result) // get config and validate var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA2, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA2, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -362,7 +363,7 @@ func TestMcmMultipleMultisigs(t *testing.T) { } // pda closed after set_config - utils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA2, config.DefaultCommitment) + testutils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA2, config.DefaultCommitment) }) }) }) diff --git a/chains/solana/contracts/tests/mcms/mcm_set_config_test.go b/chains/solana/contracts/tests/mcms/mcm_set_config_test.go index 2237f94cc..d6acf3f1b 100644 --- a/chains/solana/contracts/tests/mcms/mcm_set_config_test.go +++ b/chains/solana/contracts/tests/mcms/mcm_set_config_test.go @@ -14,10 +14,11 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/eth" - mcmsUtils "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/mcms" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/mcm" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/eth" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/mcms" ) func TestMcmSetConfig(t *testing.T) { @@ -35,19 +36,19 @@ func TestMcmSetConfig(t *testing.T) { user, err := solana.NewRandomPrivateKey() require.NoError(t, err) - solanaGoClient := utils.DeployAllPrograms(t, utils.PathToAnchorConfig, admin) + solanaGoClient := testutils.DeployAllPrograms(t, testutils.PathToAnchorConfig, admin) // mcm name testMsigName := config.TestMsigNamePaddedBuffer // test mcm pdas - multisigConfigPDA := McmConfigAddress(testMsigName) - rootMetadataPDA := RootMetadataAddress(testMsigName) - expiringRootAndOpCountPDA := ExpiringRootAndOpCountAddress(testMsigName) - configSignersPDA := McmConfigSignersAddress(testMsigName) + multisigConfigPDA := mcms.McmConfigAddress(testMsigName) + rootMetadataPDA := mcms.RootMetadataAddress(testMsigName) + expiringRootAndOpCountPDA := mcms.ExpiringRootAndOpCountAddress(testMsigName) + configSignersPDA := mcms.McmConfigSignersAddress(testMsigName) t.Run("setup:funding", func(t *testing.T) { - utils.FundAccounts(ctx, []solana.PrivateKey{admin, anotherAdmin, user}, solanaGoClient, t) + testutils.FundAccounts(ctx, []solana.PrivateKey{admin, anotherAdmin, user}, solanaGoClient, t) }) t.Run("setup:mcm", func(t *testing.T) { @@ -76,11 +77,11 @@ func TestMcmSetConfig(t *testing.T) { expiringRootAndOpCountPDA, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) // get config and validate var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -96,7 +97,7 @@ func TestMcmSetConfig(t *testing.T) { user.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + UnauthorizedMcmError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + mcms.UnauthorizedMcmError.String()}) require.NotNil(t, result) // successfully transfer ownership @@ -107,7 +108,7 @@ func TestMcmSetConfig(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) // Fail to accept ownership when not proposed_owner @@ -117,7 +118,7 @@ func TestMcmSetConfig(t *testing.T) { user.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result = utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + UnauthorizedMcmError.String()}) + result = testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + mcms.UnauthorizedMcmError.String()}) require.NotNil(t, result) // Successfully accept ownership @@ -128,7 +129,7 @@ func TestMcmSetConfig(t *testing.T) { anotherAdmin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) + result = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) require.NotNil(t, result) // Current owner cannot propose self @@ -139,12 +140,12 @@ func TestMcmSetConfig(t *testing.T) { anotherAdmin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result = utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + mcm.InvalidInputs_McmError.String()}) + result = testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + mcm.InvalidInputs_McmError.String()}) require.NotNil(t, result) // Validate proposed set to 0-address after accepting ownership var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -159,7 +160,7 @@ func TestMcmSetConfig(t *testing.T) { anotherAdmin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) + result = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) require.NotNil(t, result) instruction, err = mcm.NewAcceptOwnershipInstruction( @@ -168,10 +169,10 @@ func TestMcmSetConfig(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - result = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -194,7 +195,7 @@ func TestMcmSetConfig(t *testing.T) { groupQuorums := []uint8{1, 1, 1, 1, 1, 1, 1, 1, 1, 1} groupParents := []uint8{0, 0, 0, 2, 0, 0, 0, 0, 0, 0} - mcmConfig, err := mcmsUtils.NewValidMcmConfig( + mcmConfig, err := mcms.NewValidMcmConfig( testMsigName, signerPrivateKeys, signerGroups, @@ -208,7 +209,7 @@ func TestMcmSetConfig(t *testing.T) { t.Run("mcm:set_config: preload signers on PDA", func(t *testing.T) { ixs := make([]solana.Instruction, 0) - parsedTotalSigners, err := mcmsUtils.SafeToUint8(len(signerAddresses)) + parsedTotalSigners, err := mcms.SafeToUint8(len(signerAddresses)) require.NoError(t, err) initSignersIx, err := mcm.NewInitSignersInstruction( @@ -223,7 +224,7 @@ func TestMcmSetConfig(t *testing.T) { require.NoError(t, err) ixs = append(ixs, initSignersIx) - appendSignersIxs, err := AppendSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) + appendSignersIxs, err := mcms.AppendSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) require.NoError(t, err) ixs = append(ixs, appendSignersIxs...) @@ -237,11 +238,11 @@ func TestMcmSetConfig(t *testing.T) { ixs = append(ixs, finalizeSignersIx) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } var cfgSignersAccount mcm.ConfigSigners - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA, config.DefaultCommitment, &cfgSignersAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA, config.DefaultCommitment, &cfgSignersAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, true, cfgSignersAccount.IsFinalized) @@ -267,7 +268,7 @@ func TestMcmSetConfig(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, user, config.DefaultCommitment, []string{"Error Code: " + UnauthorizedMcmError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, user, config.DefaultCommitment, []string{"Error Code: " + mcms.UnauthorizedMcmError.String()}) require.NotNil(t, result) }) @@ -287,23 +288,23 @@ func TestMcmSetConfig(t *testing.T) { require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[ConfigSet]("ConfigSet"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.ConfigSet]("ConfigSet"), }, ) - event := parsedLogs[0].EventData[0].Data.(*ConfigSet) + event := parsedLogs[0].EventData[0].Data.(*mcms.ConfigSet) require.Equal(t, mcmConfig.GroupParents, event.GroupParents) require.Equal(t, mcmConfig.GroupQuorums, event.GroupQuorums) require.Equal(t, mcmConfig.ClearRoot, event.IsRootCleared) // get config and validate var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -318,7 +319,7 @@ func TestMcmSetConfig(t *testing.T) { } // pda closed after set_config - utils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA, config.DefaultCommitment) + testutils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA, config.DefaultCommitment) }) }) }) @@ -337,7 +338,7 @@ func TestMcmSetConfig(t *testing.T) { groupQuorums := []uint8{1, 1, 1, 1, 1, 1, 1, 1, 1, 1} groupParents := []uint8{0, 0, 0, 2, 0, 0, 0, 0, 0, 0} - mcmConfig, err := mcmsUtils.NewValidMcmConfig( + mcmConfig, err := mcms.NewValidMcmConfig( testMsigName, signerPrivateKeys, signerGroups, @@ -351,9 +352,9 @@ func TestMcmSetConfig(t *testing.T) { t.Run("mcm:set_config: preload signers on PDA", func(t *testing.T) { // ConfigSignersPDA should be closed before reinitializing - utils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA, config.DefaultCommitment) + testutils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA, config.DefaultCommitment) - parsedTotalSigners, err := mcmsUtils.SafeToUint8(len(signerAddresses)) + parsedTotalSigners, err := mcms.SafeToUint8(len(signerAddresses)) require.NoError(t, err) initSignersIx, err := mcm.NewInitSignersInstruction( @@ -366,14 +367,14 @@ func TestMcmSetConfig(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSignersIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSignersIx}, admin, config.DefaultCommitment) - appendSignersIxs, err := AppendSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) + appendSignersIxs, err := mcms.AppendSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) require.NoError(t, err) // partially register signers for _, ix := range appendSignersIxs[:3] { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } // clear signers(this closes the account) @@ -385,8 +386,8 @@ func TestMcmSetConfig(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{clearIx}, admin, config.DefaultCommitment) - utils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{clearIx}, admin, config.DefaultCommitment) + testutils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA, config.DefaultCommitment) reInitSignersIx, err := mcm.NewInitSignersInstruction( testMsigName, @@ -398,10 +399,10 @@ func TestMcmSetConfig(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{reInitSignersIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{reInitSignersIx}, admin, config.DefaultCommitment) // register all signers for _, ix := range appendSignersIxs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } // finalize registration @@ -413,10 +414,10 @@ func TestMcmSetConfig(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSignersIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSignersIx}, admin, config.DefaultCommitment) var cfgSignersAccount mcm.ConfigSigners - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA, config.DefaultCommitment, &cfgSignersAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA, config.DefaultCommitment, &cfgSignersAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, true, cfgSignersAccount.IsFinalized) @@ -442,23 +443,23 @@ func TestMcmSetConfig(t *testing.T) { require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[ConfigSet]("ConfigSet"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.ConfigSet]("ConfigSet"), }, ) - event := parsedLogs[0].EventData[0].Data.(*ConfigSet) + event := parsedLogs[0].EventData[0].Data.(*mcms.ConfigSet) require.Equal(t, mcmConfig.GroupParents, event.GroupParents) require.Equal(t, mcmConfig.GroupQuorums, event.GroupQuorums) require.Equal(t, mcmConfig.ClearRoot, event.IsRootCleared) // get config and validate var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -473,7 +474,7 @@ func TestMcmSetConfig(t *testing.T) { } // pda closed after set_config - utils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA, config.DefaultCommitment) + testutils.AssertClosedAccount(ctx, t, solanaGoClient, configSignersPDA, config.DefaultCommitment) }) }) @@ -481,61 +482,61 @@ func TestMcmSetConfig(t *testing.T) { tests := []struct { name string errorMsg string - modifyConfig func(*mcmsUtils.McmConfigArgs) + modifyConfig func(*mcms.McmConfigArgs) skipPreloadSigners bool skipFinalizeSigners bool }{ { name: "should not be able to call set_config without preloading config_signers", errorMsg: "Error Code: " + "AccountNotInitialized.", - modifyConfig: func(c *mcmsUtils.McmConfigArgs) {}, + modifyConfig: func(c *mcms.McmConfigArgs) {}, skipPreloadSigners: true, }, { name: "should not be able to call set_config without finalized config_signers", errorMsg: "Error Code: " + mcm.SignersNotFinalized_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) {}, + modifyConfig: func(c *mcms.McmConfigArgs) {}, skipFinalizeSigners: true, }, { name: "length of signer addresses and signer groups length should be equal", errorMsg: "Error Code: " + mcm.MismatchedInputSignerVectorsLength_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) { + modifyConfig: func(c *mcms.McmConfigArgs) { c.SignerGroups = append(c.SignerGroups, 1) }, }, { name: "every group index in signer group should be less than NUM_GROUPS", errorMsg: "Error Code: " + mcm.MismatchedInputGroupArraysLength_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) { + modifyConfig: func(c *mcms.McmConfigArgs) { (c.SignerGroups)[0] = 32 }, }, { name: "the parent of root has to be 0", errorMsg: "Error Code: " + mcm.GroupTreeNotWellFormed_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) { + modifyConfig: func(c *mcms.McmConfigArgs) { (c.GroupParents)[0] = 1 }, }, { name: "the parent group should be at a higher index than the child group", errorMsg: "Error Code: " + mcm.GroupTreeNotWellFormed_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) { + modifyConfig: func(c *mcms.McmConfigArgs) { (c.GroupParents)[1] = 2 }, }, { name: "disabled group(with 0 quorum) should not have a signer", errorMsg: "Error Code: " + mcm.SignerInDisabledGroup_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) { + modifyConfig: func(c *mcms.McmConfigArgs) { (c.GroupQuorums)[3] = 0 // set quorum of group 3 to 0, but we still have signers in group 3 }, }, { name: "the group quorum should be able to be met(i.e. have more signers than the quorum)", errorMsg: "Error Code: " + mcm.OutOfBoundsGroupQuorum_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) { + modifyConfig: func(c *mcms.McmConfigArgs) { (c.GroupQuorums)[3] = 3 // set quorum of group 3 to 3, but we have two signers in group 3 }, }, @@ -546,14 +547,14 @@ func TestMcmSetConfig(t *testing.T) { t.Parallel() // use different msig accounts per test - failTestMsigName, err := mcmsUtils.PadString32(fmt.Sprintf("fail_test_%d", i)) + failTestMsigName, err := mcms.PadString32(fmt.Sprintf("fail_test_%d", i)) require.NoError(t, err) // test scope mcm pdas - failMultisigConfigPDA := McmConfigAddress(failTestMsigName) - failRootMetadataPDA := RootMetadataAddress(failTestMsigName) - failExpiringRootAndOpCountPDA := ExpiringRootAndOpCountAddress(failTestMsigName) - failConfigSignersPDA := McmConfigSignersAddress(failTestMsigName) + failMultisigConfigPDA := mcms.McmConfigAddress(failTestMsigName) + failRootMetadataPDA := mcms.RootMetadataAddress(failTestMsigName) + failExpiringRootAndOpCountPDA := mcms.ExpiringRootAndOpCountAddress(failTestMsigName) + failConfigSignersPDA := mcms.McmConfigSignersAddress(failTestMsigName) t.Run(fmt.Sprintf("msig initialization:%s", tt.name), func(t *testing.T) { // get program data account @@ -582,10 +583,10 @@ func TestMcmSetConfig(t *testing.T) { failExpiringRootAndOpCountPDA, ).ValidateAndBuild() require.NoError(t, initIxErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) }) - cfg, err := mcmsUtils.NewValidMcmConfig( + cfg, err := mcms.NewValidMcmConfig( failTestMsigName, config.SignerPrivateKeys, config.SignerGroups, @@ -599,7 +600,7 @@ func TestMcmSetConfig(t *testing.T) { if tt.skipPreloadSigners { return } - parsedTotalSigners, parsingErr := mcmsUtils.SafeToUint8(len(cfg.SignerAddresses)) + parsedTotalSigners, parsingErr := mcms.SafeToUint8(len(cfg.SignerAddresses)) require.NoError(t, parsingErr) initSignersIx, initSignersErr := mcm.NewInitSignersInstruction( @@ -612,12 +613,12 @@ func TestMcmSetConfig(t *testing.T) { ).ValidateAndBuild() require.NoError(t, initSignersErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSignersIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSignersIx}, admin, config.DefaultCommitment) - appendSignersIxs, appendSignersIxsErr := AppendSignersIxs(cfg.SignerAddresses, failTestMsigName, failMultisigConfigPDA, failConfigSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) + appendSignersIxs, appendSignersIxsErr := mcms.AppendSignersIxs(cfg.SignerAddresses, failTestMsigName, failMultisigConfigPDA, failConfigSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) require.NoError(t, appendSignersIxsErr) for _, ix := range appendSignersIxs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } if !tt.skipFinalizeSigners { @@ -629,10 +630,10 @@ func TestMcmSetConfig(t *testing.T) { ).ValidateAndBuild() require.NoError(t, finSignersIxErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSignersIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSignersIx}, admin, config.DefaultCommitment) var cfgSignersAccount mcm.ConfigSigners - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, failConfigSignersPDA, config.DefaultCommitment, &cfgSignersAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, failConfigSignersPDA, config.DefaultCommitment, &cfgSignersAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, true, cfgSignersAccount.IsFinalized) @@ -660,7 +661,7 @@ func TestMcmSetConfig(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, rpc.CommitmentConfirmed, []string{tt.errorMsg}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, rpc.CommitmentConfirmed, []string{tt.errorMsg}) require.NotNil(t, result) }) } @@ -683,7 +684,7 @@ func TestMcmSetConfig(t *testing.T) { tests := []struct { name string errorMsg string - modifyConfig func(*mcmsUtils.McmConfigArgs) + modifyConfig func(*mcms.McmConfigArgs) failureStage TestStage skipInitSigners bool totalSignersOffset int @@ -691,7 +692,7 @@ func TestMcmSetConfig(t *testing.T) { { name: "should not be able to initialize config_signers with empty", errorMsg: "Error Code: " + mcm.OutOfBoundsNumOfSigners_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) { + modifyConfig: func(c *mcms.McmConfigArgs) { // empty cfg.SignerAddresses c.SignerAddresses = make([][20]byte, 0) }, @@ -700,7 +701,7 @@ func TestMcmSetConfig(t *testing.T) { { name: "should not be able to initialize config_signers with more than MAX_NUM_SIGNERS", errorMsg: "Error Code: " + mcm.OutOfBoundsNumOfSigners_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) { + modifyConfig: func(c *mcms.McmConfigArgs) { // replace cfg.SignerAddresses with more than MAX_NUM_SIGNERS(200) privateKeys, err := eth.GenerateEthPrivateKeys(201) require.NoError(t, err) @@ -717,14 +718,14 @@ func TestMcmSetConfig(t *testing.T) { { name: "should not be able to append signers without initializing", errorMsg: "Error Code: " + "AccountNotInitialized.", - modifyConfig: func(c *mcmsUtils.McmConfigArgs) {}, + modifyConfig: func(c *mcms.McmConfigArgs) {}, failureStage: AppendStage, skipInitSigners: true, }, { name: "should not be able to append unsorted signer", errorMsg: "Error Code: " + mcm.SignersAddressesMustBeStrictlyIncreasing_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) { + modifyConfig: func(c *mcms.McmConfigArgs) { slices.Reverse(c.SignerAddresses) }, failureStage: AppendStage, @@ -732,14 +733,14 @@ func TestMcmSetConfig(t *testing.T) { { name: "should not be able to append more signers than specified in total_signers", errorMsg: "Error Code: " + mcm.OutOfBoundsNumOfSigners_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) {}, + modifyConfig: func(c *mcms.McmConfigArgs) {}, failureStage: AppendStage, totalSignersOffset: -2, }, { name: "should not be able to finalize unmatched total signers", errorMsg: "Error Code: " + mcm.OutOfBoundsNumOfSigners_McmError.String(), - modifyConfig: func(c *mcmsUtils.McmConfigArgs) {}, + modifyConfig: func(c *mcms.McmConfigArgs) {}, failureStage: FinalizeStage, totalSignersOffset: 2, }, @@ -750,14 +751,14 @@ func TestMcmSetConfig(t *testing.T) { t.Parallel() // use different msig accounts per test - failTestMsigName, err := mcmsUtils.PadString32(fmt.Sprintf("fail_preupload_signer_test_%d", i)) + failTestMsigName, err := mcms.PadString32(fmt.Sprintf("fail_preupload_signer_test_%d", i)) require.NoError(t, err) // test scope mcm pdas - failMultisigConfigPDA := McmConfigAddress(failTestMsigName) - failRootMetadataPDA := RootMetadataAddress(failTestMsigName) - failExpiringRootAndOpCountPDA := ExpiringRootAndOpCountAddress(failTestMsigName) - failConfigSignersPDA := McmConfigSignersAddress(failTestMsigName) + failMultisigConfigPDA := mcms.McmConfigAddress(failTestMsigName) + failRootMetadataPDA := mcms.RootMetadataAddress(failTestMsigName) + failExpiringRootAndOpCountPDA := mcms.ExpiringRootAndOpCountAddress(failTestMsigName) + failConfigSignersPDA := mcms.McmConfigSignersAddress(failTestMsigName) t.Run(fmt.Sprintf("msig initialization:%s", tt.name), func(t *testing.T) { // get program data account @@ -786,10 +787,10 @@ func TestMcmSetConfig(t *testing.T) { failExpiringRootAndOpCountPDA, ).ValidateAndBuild() require.NoError(t, initIxErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) }) - cfg, err := mcmsUtils.NewValidMcmConfig( + cfg, err := mcms.NewValidMcmConfig( failTestMsigName, config.SignerPrivateKeys, config.SignerGroups, @@ -806,7 +807,7 @@ func TestMcmSetConfig(t *testing.T) { if !tt.skipInitSigners { actualLength := len(cfg.SignerAddresses) - totalSigners, _ := mcmsUtils.SafeToUint8(actualLength + tt.totalSignersOffset) // offset for the test + totalSigners, _ := mcms.SafeToUint8(actualLength + tt.totalSignersOffset) // offset for the test initSignersIx, _ := mcm.NewInitSignersInstruction( failTestMsigName, @@ -822,7 +823,7 @@ func TestMcmSetConfig(t *testing.T) { }) } - appendIxs, _ := AppendSignersIxs( + appendIxs, _ := mcms.AppendSignersIxs( cfg.SignerAddresses, failTestMsigName, failMultisigConfigPDA, @@ -851,7 +852,7 @@ func TestMcmSetConfig(t *testing.T) { for _, tx := range txs { if tx.Stage == tt.failureStage { // this stage should fail - result := utils.SendAndFailWith(ctx, t, solanaGoClient, + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, tx.Instructions, admin, rpc.CommitmentConfirmed, @@ -862,7 +863,7 @@ func TestMcmSetConfig(t *testing.T) { } // all other instructions should succeed - utils.SendAndConfirm(ctx, t, solanaGoClient, + testutils.SendAndConfirm(ctx, t, solanaGoClient, tx.Instructions, admin, config.DefaultCommitment, diff --git a/chains/solana/contracts/tests/mcms/mcm_set_root_execute_test.go b/chains/solana/contracts/tests/mcms/mcm_set_root_execute_test.go index 2a187352f..666875673 100644 --- a/chains/solana/contracts/tests/mcms/mcm_set_root_execute_test.go +++ b/chains/solana/contracts/tests/mcms/mcm_set_root_execute_test.go @@ -19,11 +19,12 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/eth" - mcmsUtils "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/mcms" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/external_program_cpi_stub" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/mcm" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/eth" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/mcms" ) type TestMcmOperation struct { @@ -31,7 +32,7 @@ type TestMcmOperation struct { ExpectedMethod string ExpectedLogSubstr string RemainingAccounts []*solana.AccountMeta - CheckExpectations func(instruction *utils.AnchorInstruction) error + CheckExpectations func(instruction *common.AnchorInstruction) error } func TestMcmSetRootAndExecute(t *testing.T) { @@ -47,10 +48,10 @@ func TestMcmSetRootAndExecute(t *testing.T) { user, err := solana.NewRandomPrivateKey() require.NoError(t, err) - solanaGoClient := utils.DeployAllPrograms(t, utils.PathToAnchorConfig, admin) + solanaGoClient := testutils.DeployAllPrograms(t, testutils.PathToAnchorConfig, admin) t.Run("setup:funding", func(t *testing.T) { - utils.FundAccounts(ctx, []solana.PrivateKey{admin, user}, solanaGoClient, t) + testutils.FundAccounts(ctx, []solana.PrivateKey{admin, user}, solanaGoClient, t) }) t.Run("mcm:general test cases", func(t *testing.T) { @@ -58,15 +59,15 @@ func TestMcmSetRootAndExecute(t *testing.T) { testMsigName := config.TestMsigNamePaddedBuffer // test mcm pdas - multisigConfigPDA := McmConfigAddress(testMsigName) - rootMetadataPDA := RootMetadataAddress(testMsigName) - expiringRootAndOpCountPDA := ExpiringRootAndOpCountAddress(testMsigName) - configSignersPDA := McmConfigSignersAddress(testMsigName) - msigSignerPDA := McmSignerAddress(testMsigName) + multisigConfigPDA := mcms.McmConfigAddress(testMsigName) + rootMetadataPDA := mcms.RootMetadataAddress(testMsigName) + expiringRootAndOpCountPDA := mcms.ExpiringRootAndOpCountAddress(testMsigName) + configSignersPDA := mcms.McmConfigSignersAddress(testMsigName) + msigSignerPDA := mcms.McmSignerAddress(testMsigName) // fund the signer pda fundPDAIx := system.NewTransferInstruction(1*solana.LAMPORTS_PER_SOL, admin.PublicKey(), msigSignerPDA).Build() - result := utils.SendAndConfirm(ctx, t, solanaGoClient, + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{fundPDAIx}, admin, config.DefaultCommitment) require.NotNil(t, result) @@ -93,7 +94,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { IsWritable: true, }, { - PublicKey: McmSignerAddress(config.TestMsigNamePaddedBuffer), + PublicKey: mcms.McmSignerAddress(config.TestMsigNamePaddedBuffer), IsSigner: false, IsWritable: true, }, @@ -130,7 +131,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { }, }, ExpectedLogSubstr: "Called `account_read`", - CheckExpectations: func(instruction *utils.AnchorInstruction) error { + CheckExpectations: func(instruction *common.AnchorInstruction) error { if !strings.Contains(instruction.Logs[0], "value: 1") { return fmt.Errorf("expected log to contain 'value: 1', got: %s", instruction.Logs[0]) } @@ -147,7 +148,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { IsWritable: true, }, { - PublicKey: McmSignerAddress(config.TestMsigNamePaddedBuffer), + PublicKey: mcms.McmSignerAddress(config.TestMsigNamePaddedBuffer), IsSigner: false, IsWritable: true, }, @@ -158,7 +159,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { }, }, ExpectedLogSubstr: "Called `account_mut`", - CheckExpectations: func(instruction *utils.AnchorInstruction) error { + CheckExpectations: func(instruction *common.AnchorInstruction) error { if !strings.Contains(instruction.Logs[0], "is_writable: true") { return fmt.Errorf("expected log to contain 'is_writable: true', got: %s", instruction.Logs[0]) } @@ -193,7 +194,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { expiringRootAndOpCountPDA, ).ValidateAndBuild() require.NoError(t, initErr) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, user, config.DefaultCommitment, []string{"Error Code: " + UnauthorizedMcmError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, user, config.DefaultCommitment, []string{"Error Code: " + mcms.UnauthorizedMcmError.String()}) require.NotNil(t, result) }) @@ -223,11 +224,11 @@ func TestMcmSetRootAndExecute(t *testing.T) { expiringRootAndOpCountPDA, ).ValidateAndBuild() require.NoError(t, initErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) // get config and validate var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -249,7 +250,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { groupQuorums := []uint8{1, 1, 1, 1, 1} groupParents := []uint8{0, 0, 0, 2, 0} - mcmConfig, configErr := mcmsUtils.NewValidMcmConfig( + mcmConfig, configErr := mcms.NewValidMcmConfig( testMsigName, signerPrivateKeys, signerGroups, @@ -264,7 +265,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { t.Run("mcm:preload signers", func(t *testing.T) { ixs := make([]solana.Instruction, 0) - parsedTotalSigners, pErr := mcmsUtils.SafeToUint8(len(signerAddresses)) + parsedTotalSigners, pErr := mcms.SafeToUint8(len(signerAddresses)) require.NoError(t, pErr) initSignersIx, isErr := mcm.NewInitSignersInstruction( testMsigName, @@ -278,7 +279,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { require.NoError(t, isErr) ixs = append(ixs, initSignersIx) - appendSignersIxs, asErr := AppendSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) + appendSignersIxs, asErr := mcms.AppendSignersIxs(signerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) require.NoError(t, asErr) ixs = append(ixs, appendSignersIxs...) @@ -292,11 +293,11 @@ func TestMcmSetRootAndExecute(t *testing.T) { ixs = append(ixs, finalizeSignersIx) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } var cfgSignersAccount mcm.ConfigSigners - queryErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA, config.DefaultCommitment, &cfgSignersAccount) + queryErr := common.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA, config.DefaultCommitment, &cfgSignersAccount) require.NoError(t, queryErr, "failed to get account info") require.Equal(t, true, cfgSignersAccount.IsFinalized) @@ -322,12 +323,12 @@ func TestMcmSetRootAndExecute(t *testing.T) { require.NoError(t, configErr) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) require.NotNil(t, result) // get config and validate var configAccount mcm.MultisigConfig - configErr = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) + configErr = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, configErr, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -342,11 +343,11 @@ func TestMcmSetRootAndExecute(t *testing.T) { } }) - var opNodes []mcmsUtils.McmOpNode + var opNodes []mcms.McmOpNode t.Run("mcm set_root happy path", func(t *testing.T) { for i, op := range stupProgramTestMcmOps { - node := mcmsUtils.McmOpNode{ + node := mcms.McmOpNode{ Nonce: uint64(i), Multisig: multisigConfigPDA, To: config.ExternalCpiStubProgram, @@ -357,8 +358,8 @@ func TestMcmSetRootAndExecute(t *testing.T) { } validUntil := uint32(0xffffffff) - rootValidationData, rvErr := CreateMcmRootData( - McmRootInput{ + rootValidationData, rvErr := mcms.CreateMcmRootData( + mcms.McmRootInput{ Multisig: multisigConfigPDA, Operations: opNodes, PreOpCount: 0, @@ -368,16 +369,16 @@ func TestMcmSetRootAndExecute(t *testing.T) { }, ) require.NoError(t, rvErr) - signaturesPDA := RootSignaturesAddress(testMsigName, rootValidationData.Root, validUntil) + signaturesPDA := mcms.RootSignaturesAddress(testMsigName, rootValidationData.Root, validUntil) t.Run("preload signatures", func(t *testing.T) { signers, getSignerErr := eth.GetEvmSigners(signerPrivateKeys) require.NoError(t, getSignerErr, "Failed to get signers") - signatures, sigsErr := BulkSignOnMsgHash(signers, rootValidationData.EthMsgHash) + signatures, sigsErr := mcms.BulkSignOnMsgHash(signers, rootValidationData.EthMsgHash) require.NoError(t, sigsErr) - parsedTotalSigs, pErr := mcmsUtils.SafeToUint8(len(signatures)) + parsedTotalSigs, pErr := mcms.SafeToUint8(len(signatures)) require.NoError(t, pErr) initSigsIx, isErr := mcm.NewInitSignaturesInstruction( @@ -391,14 +392,14 @@ func TestMcmSetRootAndExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, isErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSigsIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSigsIx}, admin, config.DefaultCommitment) - appendSigsIxs, asErr := AppendSignaturesIxs(signatures, testMsigName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) + appendSigsIxs, asErr := mcms.AppendSignaturesIxs(signatures, testMsigName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) require.NoError(t, asErr) // partially register signatures for _, ix := range appendSigsIxs[:3] { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } // clear uploaded signatures(this closes the account) @@ -411,8 +412,8 @@ func TestMcmSetRootAndExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, cErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{clearIx}, admin, config.DefaultCommitment) - utils.AssertClosedAccount(ctx, t, solanaGoClient, signaturesPDA, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{clearIx}, admin, config.DefaultCommitment) + testutils.AssertClosedAccount(ctx, t, solanaGoClient, signaturesPDA, config.DefaultCommitment) reInitSigsIx, rIsErr := mcm.NewInitSignaturesInstruction( testMsigName, @@ -425,11 +426,11 @@ func TestMcmSetRootAndExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, rIsErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{reInitSigsIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{reInitSigsIx}, admin, config.DefaultCommitment) // register all signatures again for _, ix := range appendSigsIxs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } finalizeSigsIx, fsErr := mcm.NewFinalizeSignaturesInstruction( @@ -441,10 +442,10 @@ func TestMcmSetRootAndExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, fsErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSigsIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSigsIx}, admin, config.DefaultCommitment) var sigAccount mcm.RootSignatures - queryErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, signaturesPDA, config.DefaultCommitment, &sigAccount) + queryErr := common.GetAccountDataBorshInto(ctx, solanaGoClient, signaturesPDA, config.DefaultCommitment, &sigAccount) require.NoError(t, queryErr, "failed to get account info") require.Equal(t, true, sigAccount.IsFinalized) @@ -464,7 +465,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { rootValidationData.MetadataProof, signaturesPDA, rootMetadataPDA, - SeenSignedHashesAddress(testMsigName, rootValidationData.Root, validUntil), + mcms.SeenSignedHashesAddress(testMsigName, rootValidationData.Root, validUntil), expiringRootAndOpCountPDA, multisigConfigPDA, admin.PublicKey(), @@ -472,15 +473,15 @@ func TestMcmSetRootAndExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, setRootIxErr) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{newIx}, admin, config.DefaultCommitment, utils.AddComputeUnitLimit(1_400_000)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{newIx}, admin, config.DefaultCommitment, common.AddComputeUnitLimit(1_400_000)) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[NewRoot]("NewRoot"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.NewRoot]("NewRoot"), }, ) - event := parsedLogs[0].EventData[0].Data.(*NewRoot) + event := parsedLogs[0].EventData[0].Data.(*mcms.NewRoot) require.Equal(t, rootValidationData.Root, event.Root) require.Equal(t, validUntil, event.ValidUntil) require.Equal(t, rootValidationData.Metadata.ChainId, event.MetadataChainID) @@ -491,7 +492,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { var newRootAndOpCount mcm.ExpiringRootAndOpCount - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, expiringRootAndOpCountPDA, config.DefaultCommitment, &newRootAndOpCount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, expiringRootAndOpCountPDA, config.DefaultCommitment, &newRootAndOpCount) require.NoError(t, err, "failed to get account info") require.Equal(t, rootValidationData.Root, newRootAndOpCount.Root) @@ -500,7 +501,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { // get config and validate var newRootMetadata mcm.RootMetadata - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, rootMetadataPDA, config.DefaultCommitment, &newRootMetadata) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, rootMetadataPDA, config.DefaultCommitment, &newRootMetadata) require.NoError(t, err, "failed to get account info") require.Equal(t, rootValidationData.Metadata.ChainId, newRootMetadata.ChainId) @@ -526,7 +527,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { rootMetadataPDA, expiringRootAndOpCountPDA, config.ExternalCpiStubProgram, - McmSignerAddress(testMsigName), + mcms.McmSignerAddress(testMsigName), admin.PublicKey(), ) // append remaining accounts @@ -535,12 +536,12 @@ func TestMcmSetRootAndExecute(t *testing.T) { vIx, vIxErr := ix.ValidateAndBuild() require.NoError(t, vIxErr) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, admin, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, admin, config.DefaultCommitment) require.NotNil(t, tx.Meta) require.Nil(t, tx.Meta.Err, fmt.Sprintf("tx failed with: %+v", tx.Meta)) - parsedInstructions := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[OpExecuted]("OpExecuted"), + parsedInstructions := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.OpExecuted]("OpExecuted"), }, ) @@ -566,7 +567,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { } var stubAccountValue external_program_cpi_stub.Value - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.StubAccountPDA, config.DefaultCommitment, &stubAccountValue) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.StubAccountPDA, config.DefaultCommitment, &stubAccountValue) require.NoError(t, err, "failed to get account info") require.Equal(t, uint8(2), stubAccountValue.Value) @@ -594,7 +595,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { errorMsg string failureStage TestStage modifyTxs func(*[]TxWithStage) - modifySigs func(*[]mcm.Signature, *McmRootData) + modifySigs func(*[]mcm.Signature, *mcms.McmRootData) }{ { name: "should not be able to initialize signatures more than one time ", @@ -657,7 +658,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { name: "signatures not in ascending order", errorMsg: "Error Code: " + mcm.SignersAddressesMustBeStrictlyIncreasing_McmError.String(), failureStage: SetRoot, - modifySigs: func(sigs *[]mcm.Signature, _ *McmRootData) { + modifySigs: func(sigs *[]mcm.Signature, _ *mcms.McmRootData) { slices.Reverse(*sigs) }, }, @@ -665,7 +666,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { name: "should fail set_root when signatures don't meet group quorum", errorMsg: "Error Code: " + mcm.InsufficientSigners_McmError.String(), failureStage: SetRoot, - modifySigs: func(sigs *[]mcm.Signature, _ *McmRootData) { + modifySigs: func(sigs *[]mcm.Signature, _ *mcms.McmRootData) { *sigs = (*sigs)[:1] // only keep first signature }, }, @@ -673,13 +674,13 @@ func TestMcmSetRootAndExecute(t *testing.T) { name: "when message hash is different from the one used to sign", errorMsg: "Error Code: " + mcm.InvalidSigner_McmError.String(), failureStage: SetRoot, - modifySigs: func(sigs *[]mcm.Signature, _ *McmRootData) { + modifySigs: func(sigs *[]mcm.Signature, _ *mcms.McmRootData) { // same signers signers, signerErr := eth.GetEvmSigners(config.SignerPrivateKeys) require.NoError(t, signerErr) // but different signatures(wrong eth hash) // secp256k1_recover_from recovers a valid but different address --> invalidSigner - signatures, sigErr := BulkSignOnMsgHash(signers, bytes.Repeat([]byte{1}, 32)) + signatures, sigErr := mcms.BulkSignOnMsgHash(signers, bytes.Repeat([]byte{1}, 32)) require.NoError(t, sigErr) *sigs = signatures }, @@ -688,7 +689,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { name: "invalid signature should fail ECDSA recovery", errorMsg: "Error Code: " + mcm.FailedEcdsaRecover_McmError.String(), failureStage: SetRoot, - modifySigs: func(sigs *[]mcm.Signature, _ *McmRootData) { + modifySigs: func(sigs *[]mcm.Signature, _ *mcms.McmRootData) { invalidSig := (*sigs)[0] // corrupt V invalidSig.V = 26 @@ -703,12 +704,12 @@ func TestMcmSetRootAndExecute(t *testing.T) { name: "signatures from unauthorized signers should fail", errorMsg: "Error Code: " + mcm.InvalidSigner_McmError.String(), failureStage: SetRoot, - modifySigs: func(sigs *[]mcm.Signature, rootData *McmRootData) { + modifySigs: func(sigs *[]mcm.Signature, rootData *mcms.McmRootData) { wrongPrivateKeys, err := eth.GenerateEthPrivateKeys(len(*sigs)) require.NoError(t, err) wrongSigners, err := eth.GetEvmSigners(wrongPrivateKeys) require.NoError(t, err) - signatures, err := BulkSignOnMsgHash(wrongSigners, rootData.EthMsgHash) + signatures, err := mcms.BulkSignOnMsgHash(wrongSigners, rootData.EthMsgHash) require.NoError(t, err) *sigs = signatures }, @@ -720,20 +721,20 @@ func TestMcmSetRootAndExecute(t *testing.T) { t.Parallel() // use different msig accounts per test - testMsigName, err := mcmsUtils.PadString32(fmt.Sprintf("fail_sig_validation_test_%d", i)) + testMsigName, err := mcms.PadString32(fmt.Sprintf("fail_sig_validation_test_%d", i)) require.NoError(t, err) // test scoped mcm pdas - multisigConfigPDA := McmConfigAddress(testMsigName) - multisigSignerPDA := McmSignerAddress(testMsigName) - rootMetadataPDA := RootMetadataAddress(testMsigName) - expiringRootAndOpCountPDA := ExpiringRootAndOpCountAddress(testMsigName) - configSignersPDA := McmConfigSignersAddress(testMsigName) + multisigConfigPDA := mcms.McmConfigAddress(testMsigName) + multisigSignerPDA := mcms.McmSignerAddress(testMsigName) + rootMetadataPDA := mcms.RootMetadataAddress(testMsigName) + expiringRootAndOpCountPDA := mcms.ExpiringRootAndOpCountAddress(testMsigName) + configSignersPDA := mcms.McmConfigSignersAddress(testMsigName) // fund the signer pda fundPDAIx, err := system.NewTransferInstruction(1*solana.LAMPORTS_PER_SOL, admin.PublicKey(), multisigSignerPDA).ValidateAndBuild() require.NoError(t, err) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{fundPDAIx}, admin, config.DefaultCommitment) require.NotNil(t, result) @@ -764,18 +765,18 @@ func TestMcmSetRootAndExecute(t *testing.T) { expiringRootAndOpCountPDA, ).ValidateAndBuild() require.NoError(t, initErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) // get config and validate var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) require.Equal(t, admin.PublicKey(), configAccount.Owner) }) - mcmConfig, configErr := mcmsUtils.NewValidMcmConfig( + mcmConfig, configErr := mcms.NewValidMcmConfig( testMsigName, config.SignerPrivateKeys, config.SignerGroups, @@ -788,7 +789,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { t.Run("setup: load signers and set_config", func(t *testing.T) { ixs := make([]solana.Instruction, 0) - parsedTotalSigners, pErr := mcmsUtils.SafeToUint8(len(mcmConfig.SignerAddresses)) + parsedTotalSigners, pErr := mcms.SafeToUint8(len(mcmConfig.SignerAddresses)) require.NoError(t, pErr) initSignersIx, isErr := mcm.NewInitSignersInstruction( testMsigName, @@ -802,7 +803,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { require.NoError(t, isErr) ixs = append(ixs, initSignersIx) - appendSignersIxs, asErr := AppendSignersIxs(mcmConfig.SignerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) + appendSignersIxs, asErr := mcms.AppendSignersIxs(mcmConfig.SignerAddresses, testMsigName, multisigConfigPDA, configSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) require.NoError(t, asErr) ixs = append(ixs, appendSignersIxs...) @@ -816,11 +817,11 @@ func TestMcmSetRootAndExecute(t *testing.T) { ixs = append(ixs, finalizeSignersIx) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } var cfgSignersAccount mcm.ConfigSigners - queryErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA, config.DefaultCommitment, &cfgSignersAccount) + queryErr := common.GetAccountDataBorshInto(ctx, solanaGoClient, configSignersPDA, config.DefaultCommitment, &cfgSignersAccount) require.NoError(t, queryErr, "failed to get account info") require.Equal(t, true, cfgSignersAccount.IsFinalized) @@ -845,11 +846,11 @@ func TestMcmSetRootAndExecute(t *testing.T) { require.NoError(t, configErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) // get config and validate var configAccount mcm.MultisigConfig - configErr = utils.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) + configErr = common.GetAccountDataBorshInto(ctx, solanaGoClient, multisigConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, configErr, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -868,18 +869,18 @@ func TestMcmSetRootAndExecute(t *testing.T) { // use simple program for testing stubProgramIx, err := external_program_cpi_stub.NewEmptyInstruction().ValidateAndBuild() - node, err := IxToMcmTestOpNode(multisigConfigPDA, multisigSignerPDA, stubProgramIx, 0) + node, err := mcms.IxToMcmTestOpNode(multisigConfigPDA, multisigSignerPDA, stubProgramIx, 0) require.NoError(t, err) validUntil := uint32(0xffffffff) // this will be used to generate proof on mcm::execute - ops := []mcmsUtils.McmOpNode{ + ops := []mcms.McmOpNode{ node, } - rootValidationData, rvErr := CreateMcmRootData( - McmRootInput{ + rootValidationData, rvErr := mcms.CreateMcmRootData( + mcms.McmRootInput{ Multisig: multisigConfigPDA, Operations: ops, PreOpCount: 0, @@ -889,18 +890,18 @@ func TestMcmSetRootAndExecute(t *testing.T) { }, ) require.NoError(t, rvErr) - signaturesPDA := RootSignaturesAddress(testMsigName, rootValidationData.Root, validUntil) + signaturesPDA := mcms.RootSignaturesAddress(testMsigName, rootValidationData.Root, validUntil) signers, getSignerErr := eth.GetEvmSigners(config.SignerPrivateKeys) require.NoError(t, getSignerErr) - signatures, err := BulkSignOnMsgHash(signers, rootValidationData.EthMsgHash) + signatures, err := mcms.BulkSignOnMsgHash(signers, rootValidationData.EthMsgHash) require.NoError(t, err) if tt.modifySigs != nil { tt.modifySigs(&signatures, &rootValidationData) } - parsedTotalSigs, err := mcmsUtils.SafeToUint8(len(signatures)) + parsedTotalSigs, err := mcms.SafeToUint8(len(signatures)) require.NoError(t, err) initSigsIx, err := mcm.NewInitSignaturesInstruction( @@ -915,7 +916,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { require.NoError(t, err) txs = append(txs, TxWithStage{Instructions: []solana.Instruction{initSigsIx}, Stage: InitSignatures}) - appendSigsIxs, asErr := AppendSignaturesIxs(signatures, testMsigName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) + appendSigsIxs, asErr := mcms.AppendSignaturesIxs(signatures, testMsigName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) require.NoError(t, asErr) // one tx is enough since we only have 5 signers @@ -940,7 +941,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { rootValidationData.MetadataProof, signaturesPDA, rootMetadataPDA, - SeenSignedHashesAddress(testMsigName, rootValidationData.Root, validUntil), + mcms.SeenSignedHashesAddress(testMsigName, rootValidationData.Root, validUntil), expiringRootAndOpCountPDA, multisigConfigPDA, admin.PublicKey(), @@ -988,7 +989,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { for _, tx := range txs { if tx.Stage == tt.failureStage { // this stage should fail - result := utils.SendAndFailWith(ctx, t, solanaGoClient, + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, tx.Instructions, admin, rpc.CommitmentConfirmed, @@ -999,7 +1000,7 @@ func TestMcmSetRootAndExecute(t *testing.T) { } // all other instructions should succeed - utils.SendAndConfirm(ctx, t, solanaGoClient, + testutils.SendAndConfirm(ctx, t, solanaGoClient, tx.Instructions, admin, config.DefaultCommitment, diff --git a/chains/solana/contracts/tests/mcms/mcm_timelock_test.go b/chains/solana/contracts/tests/mcms/mcm_timelock_test.go index 9043caeaf..bc18c726b 100644 --- a/chains/solana/contracts/tests/mcms/mcm_timelock_test.go +++ b/chains/solana/contracts/tests/mcms/mcm_timelock_test.go @@ -13,13 +13,16 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/accesscontroller" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" - mcmsUtils "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/mcms" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/access_controller" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/mcm" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/timelock" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/accesscontroller" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/mcms" + timelockutil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/timelock" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens" ) func TestMcmWithTimelock(t *testing.T) { @@ -37,19 +40,19 @@ func TestMcmWithTimelock(t *testing.T) { anyone, err := solana.NewRandomPrivateKey() require.NoError(t, err) - solanaGoClient := utils.DeployAllPrograms(t, utils.PathToAnchorConfig, admin) + solanaGoClient := testutils.DeployAllPrograms(t, testutils.PathToAnchorConfig, admin) - msigs := map[timelock.Role]RoleMultisigs{ - timelock.Proposer_Role: CreateRoleMultisigs(timelock.Proposer_Role, 1), - timelock.Canceller_Role: CreateRoleMultisigs(timelock.Canceller_Role, 1), - timelock.Executor_Role: CreateRoleMultisigs(timelock.Executor_Role, 1), - timelock.Bypasser_Role: CreateRoleMultisigs(timelock.Bypasser_Role, 1), + msigs := map[timelock.Role]timelockutil.RoleMultisigs{ + timelock.Proposer_Role: timelockutil.CreateRoleMultisigs(timelock.Proposer_Role, 1), + timelock.Canceller_Role: timelockutil.CreateRoleMultisigs(timelock.Canceller_Role, 1), + timelock.Executor_Role: timelockutil.CreateRoleMultisigs(timelock.Executor_Role, 1), + timelock.Bypasser_Role: timelockutil.CreateRoleMultisigs(timelock.Bypasser_Role, 1), } require.NoError(t, err) t.Run("setup:funding", func(t *testing.T) { - utils.FundAccounts(ctx, []solana.PrivateKey{admin, anyone}, solanaGoClient, t) + testutils.FundAccounts(ctx, []solana.PrivateKey{admin, anyone}, solanaGoClient, t) // fund msig PDA signers for _, roleMsigs := range msigs { @@ -58,13 +61,13 @@ func TestMcmWithTimelock(t *testing.T) { fundPDAIx := system.NewTransferInstruction(1*solana.LAMPORTS_PER_SOL, admin.PublicKey(), msig.SignerPDA).Build() ixs = append(ixs, fundPDAIx) } - utils.SendAndConfirm(ctx, t, solanaGoClient, + testutils.SendAndConfirm(ctx, t, solanaGoClient, ixs, admin, config.DefaultCommitment) } // fund timelock signer fundPDAIx := system.NewTransferInstruction(1*solana.LAMPORTS_PER_SOL, admin.PublicKey(), config.TimelockSignerPDA).Build() - utils.SendAndConfirm(ctx, t, solanaGoClient, + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{fundPDAIx}, admin, config.DefaultCommitment) }) @@ -72,7 +75,7 @@ func TestMcmWithTimelock(t *testing.T) { t.Run("setup: initialize mcm multisigs", func(t *testing.T) { for role, roleMsigs := range msigs { for _, msig := range roleMsigs.Multisigs { - t.Run(fmt.Sprintf("init mcm for role %s with multisig %s", role.String(), mcmsUtils.UnpadString32(msig.PaddedName)), func(t *testing.T) { + t.Run(fmt.Sprintf("init mcm for role %s with multisig %s", role.String(), mcms.UnpadString32(msig.PaddedName)), func(t *testing.T) { // get program data account data, accErr := solanaGoClient.GetAccountInfoWithOpts(ctx, config.McmProgram, &rpc.GetAccountInfoOpts{ Commitment: config.DefaultCommitment, @@ -98,11 +101,11 @@ func TestMcmWithTimelock(t *testing.T) { msig.ExpiringRootAndOpCountPDA, ).ValidateAndBuild() require.NoError(t, initIxErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) // get config and validate var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, msig.ConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, msig.ConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -116,13 +119,13 @@ func TestMcmWithTimelock(t *testing.T) { t.Run("setup: set_config for each mcm multisigs", func(t *testing.T) { for role, roleMsigs := range msigs { for _, msig := range roleMsigs.Multisigs { - t.Run(fmt.Sprintf("set_config of role %s with multisig %s", role.String(), mcmsUtils.UnpadString32(msig.PaddedName)), func(t *testing.T) { + t.Run(fmt.Sprintf("set_config of role %s with multisig %s", role.String(), mcms.UnpadString32(msig.PaddedName)), func(t *testing.T) { signerAddresses := msig.RawConfig.SignerAddresses t.Run("preload signers on PDA", func(t *testing.T) { ixs := make([]solana.Instruction, 0) - parsedTotalSigners, parseErr := mcmsUtils.SafeToUint8(len(signerAddresses)) + parsedTotalSigners, parseErr := mcms.SafeToUint8(len(signerAddresses)) require.NoError(t, parseErr) initSignersIx, initSignersIxErr := mcm.NewInitSignersInstruction( @@ -136,7 +139,7 @@ func TestMcmWithTimelock(t *testing.T) { require.NoError(t, initSignersIxErr) ixs = append(ixs, initSignersIx) - appendSignersIxs, appendSignersIxsErr := AppendSignersIxs(signerAddresses, msig.PaddedName, msig.ConfigPDA, msig.ConfigSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) + appendSignersIxs, appendSignersIxsErr := mcms.AppendSignersIxs(signerAddresses, msig.PaddedName, msig.ConfigPDA, msig.ConfigSignersPDA, admin.PublicKey(), config.MaxAppendSignerBatchSize) require.NoError(t, appendSignersIxsErr) ixs = append(ixs, appendSignersIxs...) @@ -150,11 +153,11 @@ func TestMcmWithTimelock(t *testing.T) { ixs = append(ixs, finalizeSignersIx) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } var cfgSignersAccount mcm.ConfigSigners - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, msig.ConfigSignersPDA, config.DefaultCommitment, &cfgSignersAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, msig.ConfigSignersPDA, config.DefaultCommitment, &cfgSignersAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, true, cfgSignersAccount.IsFinalized) @@ -180,23 +183,23 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, setConfigErr) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[ConfigSet]("ConfigSet"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.ConfigSet]("ConfigSet"), }, ) - event := parsedLogs[0].EventData[0].Data.(*ConfigSet) + event := parsedLogs[0].EventData[0].Data.(*mcms.ConfigSet) require.Equal(t, msig.RawConfig.GroupParents, event.GroupParents) require.Equal(t, msig.RawConfig.GroupQuorums, event.GroupQuorums) require.Equal(t, msig.RawConfig.ClearRoot, event.IsRootCleared) // get config and validate var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, msig.ConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, msig.ConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, config.TestChainID, configAccount.ChainId) @@ -211,7 +214,7 @@ func TestMcmWithTimelock(t *testing.T) { } // pda closed after set_config - utils.AssertClosedAccount(ctx, t, solanaGoClient, msig.ConfigSignersPDA, config.DefaultCommitment) + testutils.AssertClosedAccount(ctx, t, solanaGoClient, msig.ConfigSignersPDA, config.DefaultCommitment) }) }) } @@ -221,13 +224,13 @@ func TestMcmWithTimelock(t *testing.T) { t.Run("setup: timelock", func(t *testing.T) { for role, roleMsigs := range msigs { t.Run(fmt.Sprintf("init access controller for role %s", role.String()), func(t *testing.T) { - initAccIxs, initAccErr := InitAccessControllersIxs(ctx, roleMsigs.AccessController.PublicKey(), admin, solanaGoClient) + initAccIxs, initAccErr := timelockutil.InitAccessControllersIxs(ctx, roleMsigs.AccessController.PublicKey(), admin, solanaGoClient) require.NoError(t, initAccErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, initAccIxs, admin, config.DefaultCommitment, utils.AddSigners(roleMsigs.AccessController)) + testutils.SendAndConfirm(ctx, t, solanaGoClient, initAccIxs, admin, config.DefaultCommitment, common.AddSigners(roleMsigs.AccessController)) var ac access_controller.AccessController - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, roleMsigs.AccessController.PublicKey(), config.DefaultCommitment, &ac) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, roleMsigs.AccessController.PublicKey(), config.DefaultCommitment, &ac) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -263,10 +266,10 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, initTimelockErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initTimelockIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initTimelockIx}, admin, config.DefaultCommitment) var configAccount timelock.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -287,11 +290,11 @@ func TestMcmWithTimelock(t *testing.T) { for _, msig := range roleMsigs.Multisigs { addresses = append(addresses, msig.SignerPDA) } - batchAddAccessIxs, batchAddAccessErr := TimelockBatchAddAccessIxs(ctx, roleMsigs.AccessController.PublicKey(), role, addresses, admin, config.BatchAddAccessChunkSize, solanaGoClient) + batchAddAccessIxs, batchAddAccessErr := timelockutil.BatchAddAccessIxs(ctx, roleMsigs.AccessController.PublicKey(), role, addresses, admin, config.BatchAddAccessChunkSize, solanaGoClient) require.NoError(t, batchAddAccessErr) for _, ix := range batchAddAccessIxs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } for _, msig := range roleMsigs.Multisigs { @@ -307,7 +310,7 @@ func TestMcmWithTimelock(t *testing.T) { t.Run("setup: transfer ownership multisigs to timelock signer", func(t *testing.T) { for role, roleMsigs := range msigs { for _, msig := range roleMsigs.Multisigs { - t.Run(fmt.Sprintf("transfer ownership of role %s multisig %s to timelock signer", role.String(), mcmsUtils.UnpadString32(msig.PaddedName)), func(t *testing.T) { + t.Run(fmt.Sprintf("transfer ownership of role %s multisig %s to timelock signer", role.String(), mcms.UnpadString32(msig.PaddedName)), func(t *testing.T) { t.Parallel() ix, transferOwnershipErr := mcm.NewTransferOwnershipInstruction( msig.PaddedName, @@ -317,10 +320,10 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, transferOwnershipErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) var configAccount mcm.MultisigConfig - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, msig.ConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, msig.ConfigPDA, config.DefaultCommitment, &configAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -334,9 +337,9 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, acceptOwnershipixErr) - salt, sErr := mcmsUtils.SimpleSalt() + salt, sErr := mcms.SimpleSalt() require.NoError(t, sErr) - acceptOwnershipOp := TimelockOperation{ + acceptOwnershipOp := timelockutil.Operation{ Predecessor: config.TimelockEmptyOpID, Salt: salt, Delay: uint64(1), @@ -347,10 +350,10 @@ func TestMcmWithTimelock(t *testing.T) { id := acceptOwnershipOp.OperationID() operationPDA := acceptOwnershipOp.OperationPDA() - ixs, ierr := TimelockPreloadOperationIxs(ctx, acceptOwnershipOp, admin.PublicKey(), solanaGoClient) + ixs, ierr := timelockutil.PreloadOperationIxs(ctx, acceptOwnershipOp, admin.PublicKey(), solanaGoClient) require.NoError(t, ierr) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } scheduleBatchIx, scErr := timelock.NewScheduleBatchInstruction( @@ -363,22 +366,22 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, scErr) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{scheduleBatchIx}, admin, config.DefaultCommitment) - parsed := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[CallScheduled]("CallScheduled"), + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{scheduleBatchIx}, admin, config.DefaultCommitment) + parsed := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.CallScheduled]("CallScheduled"), }, ) for _, ixx := range acceptOwnershipOp.ToInstructionData() { - event := parsed[0].EventData[0].Data.(*CallScheduled) + event := parsed[0].EventData[0].Data.(*timelockutil.CallScheduled) require.Equal(t, acceptOwnershipOp.OperationID(), event.ID) require.Equal(t, acceptOwnershipOp.Salt, event.Salt) require.Equal(t, ixx.Data, event.Data) } var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, operationPDA, config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, operationPDA, config.DefaultCommitment, &opAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -408,22 +411,22 @@ func TestMcmWithTimelock(t *testing.T) { vIx, vIxErr := bypassExeIx.ValidateAndBuild() require.NoError(t, vIxErr) - acceptTx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, admin, config.DefaultCommitment) + acceptTx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, admin, config.DefaultCommitment) - parsedLogs := utils.ParseLogMessages(acceptTx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[BypasserCallExecuted]("BypasserCallExecuted"), + parsedLogs := common.ParseLogMessages(acceptTx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.BypasserCallExecuted]("BypasserCallExecuted"), }, ) for i, ixx := range acceptOwnershipOp.ToInstructionData() { - event := parsedLogs[0].EventData[i].Data.(*BypasserCallExecuted) + event := parsedLogs[0].EventData[i].Data.(*timelockutil.BypasserCallExecuted) require.Equal(t, uint64(i), event.Index) require.Equal(t, ixx.ProgramId, event.Target) - require.Equal(t, ixx.Data, utils.NormalizeData(event.Data)) + require.Equal(t, ixx.Data, common.NormalizeData(event.Data)) } - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, msig.ConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, msig.ConfigPDA, config.DefaultCommitment, &configAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -455,7 +458,7 @@ func TestMcmWithTimelock(t *testing.T) { // Use CreateToken utility to get initialization instructions // NOTE: can't create token with cpi(mint signature required) - createTokenIxs, createTokenErr := utils.CreateToken( + createTokenIxs, createTokenErr := tokens.CreateToken( ctx, v.tokenProgram, // token program mint, // mint account @@ -467,7 +470,7 @@ func TestMcmWithTimelock(t *testing.T) { require.NoError(t, createTokenErr) for _, ix := range createTokenIxs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment, utils.AddSigners(mintKeypair)) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment, common.AddSigners(mintKeypair)) } t.Run("mint ixs", func(t *testing.T) { @@ -481,26 +484,26 @@ func TestMcmWithTimelock(t *testing.T) { recipient, kerr := solana.NewRandomPrivateKey() require.NoError(t, kerr) - rIxATA, rAta, rAtaIxErr := utils.CreateAssociatedTokenAccount(v.tokenProgram, mint, recipient.PublicKey(), admin.PublicKey()) + rIxATA, rAta, rAtaIxErr := tokens.CreateAssociatedTokenAccount(v.tokenProgram, mint, recipient.PublicKey(), admin.PublicKey()) require.NoError(t, rAtaIxErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{rIxATA}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{rIxATA}, admin, config.DefaultCommitment) - _, rInitBal, bErr := utils.TokenBalance(ctx, solanaGoClient, rAta, config.DefaultCommitment) + _, rInitBal, bErr := tokens.TokenBalance(ctx, solanaGoClient, rAta, config.DefaultCommitment) require.NoError(t, bErr) require.Equal(t, 0, rInitBal) // mint authority to timelock - authIx, aErr := utils.SetTokenMintAuthority(v.tokenProgram, config.TimelockSignerPDA, mint, admin.PublicKey()) + authIx, aErr := tokens.SetTokenMintAuthority(v.tokenProgram, config.TimelockSignerPDA, mint, admin.PublicKey()) require.NoError(t, aErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{authIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{authIx}, admin, config.DefaultCommitment) numMintIxs := 18 - salt, sErr := mcmsUtils.SimpleSalt() + salt, sErr := mcms.SimpleSalt() require.NoError(t, sErr) - opToSchedule := TimelockOperation{ + opToSchedule := timelockutil.Operation{ Predecessor: config.TimelockEmptyOpID, // no predecessor Salt: salt, Delay: uint64(1), @@ -508,17 +511,17 @@ func TestMcmWithTimelock(t *testing.T) { for i := 0; i < numMintIxs; i++ { // timelock signer can mint token (transferred authority) - ix, mIxErr := utils.MintTo(1*solana.LAMPORTS_PER_SOL, v.tokenProgram, mint, rAta, config.TimelockSignerPDA) + ix, mIxErr := tokens.MintTo(1*solana.LAMPORTS_PER_SOL, v.tokenProgram, mint, rAta, config.TimelockSignerPDA) require.NoError(t, mIxErr) // add instruction to timelock operation opToSchedule.AddInstruction(ix, []solana.PublicKey{v.tokenProgram}) } - ixs, ierr := TimelockPreloadOperationIxs(ctx, opToSchedule, admin.PublicKey(), solanaGoClient) + ixs, ierr := timelockutil.PreloadOperationIxs(ctx, opToSchedule, admin.PublicKey(), solanaGoClient) require.NoError(t, ierr) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } // Schedule the operation @@ -532,12 +535,12 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, scErr) - node, cErr := IxToMcmTestOpNode(proposerMsig.ConfigPDA, proposerMsig.SignerPDA, scheduleIx, uint64(currentOpCount)) // operation nonce + node, cErr := mcms.IxToMcmTestOpNode(proposerMsig.ConfigPDA, proposerMsig.SignerPDA, scheduleIx, uint64(currentOpCount)) // operation nonce require.NoError(t, cErr) - mcmOpNodes := []mcmsUtils.McmOpNode{node} // only one mcm op node + mcmOpNodes := []mcms.McmOpNode{node} // only one mcm op node validUntil := uint32(0xffffffff) - rootValidationData, rErr := CreateMcmRootData(McmRootInput{ + rootValidationData, rErr := mcms.CreateMcmRootData(mcms.McmRootInput{ Multisig: proposerMsig.ConfigPDA, Operations: mcmOpNodes, PreOpCount: uint64(currentOpCount), @@ -549,12 +552,12 @@ func TestMcmWithTimelock(t *testing.T) { currentOpCount += len(mcmOpNodes) - signatures, bulkSignErr := BulkSignOnMsgHash(proposerMsig.Signers, rootValidationData.EthMsgHash) + signatures, bulkSignErr := mcms.BulkSignOnMsgHash(proposerMsig.Signers, rootValidationData.EthMsgHash) require.NoError(t, bulkSignErr) signaturesPDA := proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil) t.Run("mcm:preload signatures", func(t *testing.T) { - parsedTotalSigs, pErr := mcmsUtils.SafeToUint8(len(signatures)) + parsedTotalSigs, pErr := mcms.SafeToUint8(len(signatures)) require.NoError(t, pErr) ixs := make([]solana.Instruction, 0) @@ -572,7 +575,7 @@ func TestMcmWithTimelock(t *testing.T) { require.NoError(t, isErr) ixs = append(ixs, initSigsIx) - appendSigsIxs, asErr := AppendSignaturesIxs(signatures, proposerMsig.PaddedName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) + appendSigsIxs, asErr := mcms.AppendSignaturesIxs(signatures, proposerMsig.PaddedName, rootValidationData.Root, validUntil, signaturesPDA, admin.PublicKey(), config.MaxAppendSignatureBatchSize) require.NoError(t, asErr) ixs = append(ixs, appendSigsIxs...) @@ -587,11 +590,11 @@ func TestMcmWithTimelock(t *testing.T) { ixs = append(ixs, finalizeSigsIx) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } var sigAccount mcm.RootSignatures - queryErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, signaturesPDA, config.DefaultCommitment, &sigAccount) + queryErr := common.GetAccountDataBorshInto(ctx, solanaGoClient, signaturesPDA, config.DefaultCommitment, &sigAccount) require.NoError(t, queryErr, "failed to get account info") require.Equal(t, true, sigAccount.IsFinalized) @@ -612,7 +615,7 @@ func TestMcmWithTimelock(t *testing.T) { rootValidationData.MetadataProof, signaturesPDA, proposerMsig.RootMetadataPDA, - SeenSignedHashesAddress(proposerMsig.PaddedName, rootValidationData.Root, validUntil), + mcms.SeenSignedHashesAddress(proposerMsig.PaddedName, rootValidationData.Root, validUntil), proposerMsig.ExpiringRootAndOpCountPDA, proposerMsig.ConfigPDA, admin.PublicKey(), @@ -620,15 +623,15 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, setRootIxErr) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{newIx}, admin, config.DefaultCommitment, utils.AddComputeUnitLimit(1_400_000)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{newIx}, admin, config.DefaultCommitment, common.AddComputeUnitLimit(1_400_000)) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[NewRoot]("NewRoot"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.NewRoot]("NewRoot"), }, ) - event := parsedLogs[0].EventData[0].Data.(*NewRoot) + event := parsedLogs[0].EventData[0].Data.(*mcms.NewRoot) require.Equal(t, rootValidationData.Root, event.Root) require.Equal(t, validUntil, event.ValidUntil) require.Equal(t, rootValidationData.Metadata.ChainId, event.MetadataChainID) @@ -639,7 +642,7 @@ func TestMcmWithTimelock(t *testing.T) { var newRootAndOpCount mcm.ExpiringRootAndOpCount - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.ExpiringRootAndOpCountPDA, config.DefaultCommitment, &newRootAndOpCount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.ExpiringRootAndOpCountPDA, config.DefaultCommitment, &newRootAndOpCount) require.NoError(t, err, "failed to get account info") require.Equal(t, rootValidationData.Root, newRootAndOpCount.Root) @@ -648,7 +651,7 @@ func TestMcmWithTimelock(t *testing.T) { // get config and validate var newRootMetadata mcm.RootMetadata - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.RootMetadataPDA, config.DefaultCommitment, &newRootMetadata) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.RootMetadataPDA, config.DefaultCommitment, &newRootMetadata) require.NoError(t, err, "failed to get account info") require.Equal(t, rootValidationData.Metadata.ChainId, newRootMetadata.ChainId) @@ -691,40 +694,40 @@ func TestMcmWithTimelock(t *testing.T) { vIx, vIxErr := ix.ValidateAndBuild() require.NoError(t, vIxErr) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, anyone, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, anyone, config.DefaultCommitment) require.NotNil(t, tx.Meta) require.Nil(t, tx.Meta.Err, fmt.Sprintf("tx failed with: %+v", tx.Meta)) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[OpExecuted]("OpExecuted"), - utils.EventMappingFor[CallScheduled]("CallScheduled"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.OpExecuted]("OpExecuted"), + common.EventMappingFor[timelockutil.CallScheduled]("CallScheduled"), }, ) // check opExecuted event - event := parsedLogs[0].EventData[0].Data.(*OpExecuted) + event := parsedLogs[0].EventData[0].Data.(*mcms.OpExecuted) require.Equal(t, op.Nonce, event.Nonce) require.Equal(t, op.To, event.To) - require.Equal(t, op.Data, utils.NormalizeData(event.Data)) + require.Equal(t, op.Data, common.NormalizeData(event.Data)) // check inner CallScheduled events opIxData := opToSchedule.ToInstructionData() require.Equal(t, len(opIxData), len(parsedLogs[0].InnerCalls[0].EventData), "Number of actual CallScheduled events does not match expected for operation") for j, ix := range opIxData { - timelockEvent := parsedLogs[0].InnerCalls[0].EventData[j].Data.(*CallScheduled) + timelockEvent := parsedLogs[0].InnerCalls[0].EventData[j].Data.(*timelockutil.CallScheduled) require.Equal(t, opToSchedule.OperationID(), timelockEvent.ID, "ID does not match") require.Equal(t, uint64(j), timelockEvent.Index, "Index does not match") require.Equal(t, ix.ProgramId, timelockEvent.Target, "Target does not match") require.Equal(t, opToSchedule.Predecessor, timelockEvent.Predecessor, "Predecessor does not match") require.Equal(t, opToSchedule.Salt, timelockEvent.Salt, "Salt does not match") require.Equal(t, opToSchedule.Delay, timelockEvent.Delay, "Delay does not match") - require.Equal(t, ix.Data, utils.NormalizeData(timelockEvent.Data), "Data does not match") + require.Equal(t, ix.Data, common.NormalizeData(timelockEvent.Data), "Data does not match") } var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, opToSchedule.OperationPDA(), config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, opToSchedule.OperationPDA(), config.DefaultCommitment, &opAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -744,7 +747,7 @@ func TestMcmWithTimelock(t *testing.T) { }) t.Run("success: wait for operations to be ready", func(t *testing.T) { - wErr := WaitForOperationToBeReady(ctx, solanaGoClient, opToSchedule.OperationPDA(), config.DefaultCommitment) + wErr := timelockutil.WaitForOperationToBeReady(ctx, solanaGoClient, opToSchedule.OperationPDA(), config.DefaultCommitment) require.NoError(t, wErr) }) @@ -764,25 +767,25 @@ func TestMcmWithTimelock(t *testing.T) { vIx, vErr := ix.ValidateAndBuild() require.NoError(t, vErr) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, admin, config.DefaultCommitment, utils.AddComputeUnitLimit(1_400_000)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, admin, config.DefaultCommitment, common.AddComputeUnitLimit(1_400_000)) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[CallExecuted]("CallExecuted"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.CallExecuted]("CallExecuted"), }, ) for i, ixx := range opToSchedule.ToInstructionData() { - event := parsedLogs[0].EventData[i].Data.(*CallExecuted) + event := parsedLogs[0].EventData[i].Data.(*timelockutil.CallExecuted) require.Equal(t, opToSchedule.OperationID(), event.ID) require.Equal(t, uint64(i), event.Index) require.Equal(t, ixx.ProgramId, event.Target) - require.Equal(t, ixx.Data, utils.NormalizeData(event.Data)) + require.Equal(t, ixx.Data, common.NormalizeData(event.Data)) } var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, opToSchedule.OperationPDA(), config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, opToSchedule.OperationPDA(), config.DefaultCommitment, &opAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -793,7 +796,7 @@ func TestMcmWithTimelock(t *testing.T) { "Executed operation's time should be 1(DONE_TIMESTAMP)", ) - _, rInitBal, bErr := utils.TokenBalance(ctx, solanaGoClient, rAta, config.DefaultCommitment) + _, rInitBal, bErr := tokens.TokenBalance(ctx, solanaGoClient, rAta, config.DefaultCommitment) require.NoError(t, bErr) require.Equal(t, numMintIxs*int(solana.LAMPORTS_PER_SOL), rInitBal) }) @@ -802,7 +805,7 @@ func TestMcmWithTimelock(t *testing.T) { } var rootAccount mcm.ExpiringRootAndOpCount - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.ExpiringRootAndOpCountPDA, config.DefaultCommitment, &rootAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.ExpiringRootAndOpCountPDA, config.DefaultCommitment, &rootAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, uint64(currentOpCount), rootAccount.OpCount) @@ -820,7 +823,7 @@ func TestMcmWithTimelock(t *testing.T) { // Use CreateToken utility to get initialization instructions // NOTE: can't create token with cpi(mint signature required) - createTokenIxs, err := utils.CreateToken( + createTokenIxs, err := tokens.CreateToken( ctx, tokenProgram, // token program mint, // mint account @@ -831,12 +834,12 @@ func TestMcmWithTimelock(t *testing.T) { ) require.NoError(t, err) - authIx, err := utils.SetTokenMintAuthority(tokenProgram, config.TimelockSignerPDA, mint, admin.PublicKey()) + authIx, err := tokens.SetTokenMintAuthority(tokenProgram, config.TimelockSignerPDA, mint, admin.PublicKey()) require.NoError(t, err) setupIxs := append(createTokenIxs, authIx) - utils.SendAndConfirm(ctx, t, solanaGoClient, setupIxs, admin, config.DefaultCommitment, utils.AddSigners(mintKeypair)) + testutils.SendAndConfirm(ctx, t, solanaGoClient, setupIxs, admin, config.DefaultCommitment, common.AddSigners(mintKeypair)) ///////////////////////////////////////// // Timelock Operation 1 - Initial Mint // @@ -844,15 +847,15 @@ func TestMcmWithTimelock(t *testing.T) { treasury, kerr := solana.NewRandomPrivateKey() require.NoError(t, kerr) - ix1, treasuryATA, err := utils.CreateAssociatedTokenAccount(tokenProgram, mint, treasury.PublicKey(), config.TimelockSignerPDA) + ix1, treasuryATA, err := tokens.CreateAssociatedTokenAccount(tokenProgram, mint, treasury.PublicKey(), config.TimelockSignerPDA) require.NoError(t, err) - ix2, err := utils.MintTo(1000*solana.LAMPORTS_PER_SOL, tokenProgram, mint, treasuryATA, config.TimelockSignerPDA) + ix2, err := tokens.MintTo(1000*solana.LAMPORTS_PER_SOL, tokenProgram, mint, treasuryATA, config.TimelockSignerPDA) require.NoError(t, err) - salt1, err := mcmsUtils.SimpleSalt() + salt1, err := mcms.SimpleSalt() require.NoError(t, err) - op1 := TimelockOperation{ + op1 := timelockutil.Operation{ Predecessor: config.TimelockEmptyOpID, // no predecessor Salt: salt1, Delay: uint64(1), @@ -870,27 +873,27 @@ func TestMcmWithTimelock(t *testing.T) { team3, err := solana.NewRandomPrivateKey() require.NoError(t, err) - ix3, team1ATA, err := utils.CreateAssociatedTokenAccount( + ix3, team1ATA, err := tokens.CreateAssociatedTokenAccount( tokenProgram, mint, team1.PublicKey(), config.TimelockSignerPDA, ) require.NoError(t, err) - ix4, team2ATA, err := utils.CreateAssociatedTokenAccount( + ix4, team2ATA, err := tokens.CreateAssociatedTokenAccount( tokenProgram, mint, team2.PublicKey(), config.TimelockSignerPDA, ) require.NoError(t, err) - ix5, team3ATA, err := utils.CreateAssociatedTokenAccount( + ix5, team3ATA, err := tokens.CreateAssociatedTokenAccount( tokenProgram, mint, team3.PublicKey(), config.TimelockSignerPDA, ) require.NoError(t, err) - salt2, err := mcmsUtils.SimpleSalt() + salt2, err := mcms.SimpleSalt() require.NoError(t, err) - op2 := TimelockOperation{ + op2 := timelockutil.Operation{ Predecessor: op1.OperationID(), // must happen after initial mint Salt: salt2, Delay: uint64(1), @@ -902,17 +905,17 @@ func TestMcmWithTimelock(t *testing.T) { ////////////////////////////////////////////////////////////// // Timelock Operation 3 - Schedule team token distribution // ////////////////////////////////////////////////////////////// - ix6, err := utils.TokenTransferChecked(100*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team1ATA, config.TimelockSignerPDA, []solana.PublicKey{}) + ix6, err := tokens.TokenTransferChecked(100*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team1ATA, config.TimelockSignerPDA, []solana.PublicKey{}) require.NoError(t, err) - ix7, err := utils.TokenTransferChecked(200*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team2ATA, config.TimelockSignerPDA, []solana.PublicKey{}) + ix7, err := tokens.TokenTransferChecked(200*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team2ATA, config.TimelockSignerPDA, []solana.PublicKey{}) require.NoError(t, err) - ix8, err := utils.TokenTransferChecked(300*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team3ATA, config.TimelockSignerPDA, []solana.PublicKey{}) + ix8, err := tokens.TokenTransferChecked(300*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team3ATA, config.TimelockSignerPDA, []solana.PublicKey{}) require.NoError(t, err) // add all team distribution instructions - salt3, err := mcmsUtils.SimpleSalt() + salt3, err := mcms.SimpleSalt() require.NoError(t, err) - op3 := TimelockOperation{ + op3 := timelockutil.Operation{ Predecessor: op2.OperationID(), // must happen after ata creation Salt: salt3, Delay: uint64(1), @@ -928,15 +931,15 @@ func TestMcmWithTimelock(t *testing.T) { //////////////////////////////////////// // Pre-create Timelock Operation PDAs // //////////////////////////////////////// - opNodes := []mcmsUtils.McmOpNode{} - timelockOps := []TimelockOperation{op1, op2, op3} + opNodes := []mcms.McmOpNode{} + timelockOps := []timelockutil.Operation{op1, op2, op3} for i, op := range timelockOps { t.Run(fmt.Sprintf("prepare mcm op node %d with timelock::schedule_batch ix", i), func(t *testing.T) { - ixs, ierr := TimelockPreloadOperationIxs(ctx, op, admin.PublicKey(), solanaGoClient) + ixs, ierr := timelockutil.PreloadOperationIxs(ctx, op, admin.PublicKey(), solanaGoClient) require.NoError(t, ierr) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } scheduleOpIx, scErr := timelock.NewScheduleBatchInstruction( @@ -949,7 +952,7 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, scErr) - opNode, cErr := IxToMcmTestOpNode(proposerMsig.ConfigPDA, proposerMsig.SignerPDA, scheduleOpIx, uint64(currentOpCount+i)) + opNode, cErr := mcms.IxToMcmTestOpNode(proposerMsig.ConfigPDA, proposerMsig.SignerPDA, scheduleOpIx, uint64(currentOpCount+i)) require.NoError(t, cErr) // fmt.Println("opNode", opNode) opNodes = append(opNodes, opNode) @@ -961,7 +964,7 @@ func TestMcmWithTimelock(t *testing.T) { ////////////////////////////////// validUntil := uint32(0xffffffff) - rootValidationData, err := CreateMcmRootData(McmRootInput{ + rootValidationData, err := mcms.CreateMcmRootData(mcms.McmRootInput{ Multisig: proposerMsig.ConfigPDA, Operations: opNodes, PreOpCount: uint64(currentOpCount), @@ -976,13 +979,13 @@ func TestMcmWithTimelock(t *testing.T) { t.Run("offchain: bulk sign on root and upload signatures", func(t *testing.T) { // sign the root - signatures, signErr := BulkSignOnMsgHash(proposerMsig.Signers, rootValidationData.EthMsgHash) + signatures, signErr := mcms.BulkSignOnMsgHash(proposerMsig.Signers, rootValidationData.EthMsgHash) require.NoError(t, signErr) //////////////////////////////////////////////// // mcm::set_root - with preloading signatures // //////////////////////////////////////////////// - parsedTotalSigs, pErr := mcmsUtils.SafeToUint8(len(signatures)) + parsedTotalSigs, pErr := mcms.SafeToUint8(len(signatures)) require.NoError(t, pErr) ixs := make([]solana.Instruction, 0) @@ -1000,7 +1003,7 @@ func TestMcmWithTimelock(t *testing.T) { require.NoError(t, isErr) ixs = append(ixs, initSigsIx) - appendSigsIxs, asErr := AppendSignaturesIxs(signatures, proposerMsig.PaddedName, rootValidationData.Root, validUntil, proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil), admin.PublicKey(), config.MaxAppendSignatureBatchSize) + appendSigsIxs, asErr := mcms.AppendSignaturesIxs(signatures, proposerMsig.PaddedName, rootValidationData.Root, validUntil, proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil), admin.PublicKey(), config.MaxAppendSignatureBatchSize) require.NoError(t, asErr) ixs = append(ixs, appendSigsIxs...) @@ -1015,11 +1018,11 @@ func TestMcmWithTimelock(t *testing.T) { ixs = append(ixs, finalizeSigsIx) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } var sigAccount mcm.RootSignatures - queryErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil), config.DefaultCommitment, &sigAccount) + queryErr := common.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil), config.DefaultCommitment, &sigAccount) require.NoError(t, queryErr, "failed to get account info") require.Equal(t, true, sigAccount.IsFinalized) @@ -1040,7 +1043,7 @@ func TestMcmWithTimelock(t *testing.T) { rootValidationData.MetadataProof, proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil), proposerMsig.RootMetadataPDA, - SeenSignedHashesAddress(proposerMsig.PaddedName, rootValidationData.Root, validUntil), + mcms.SeenSignedHashesAddress(proposerMsig.PaddedName, rootValidationData.Root, validUntil), proposerMsig.ExpiringRootAndOpCountPDA, proposerMsig.ConfigPDA, admin.PublicKey(), @@ -1048,15 +1051,15 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, setRootIxErr) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{newIx}, admin, config.DefaultCommitment, utils.AddComputeUnitLimit(1_400_000)) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{newIx}, admin, config.DefaultCommitment, common.AddComputeUnitLimit(1_400_000)) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[NewRoot]("NewRoot"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.NewRoot]("NewRoot"), }, ) - event := parsedLogs[0].EventData[0].Data.(*NewRoot) + event := parsedLogs[0].EventData[0].Data.(*mcms.NewRoot) require.Equal(t, rootValidationData.Root, event.Root) require.Equal(t, validUntil, event.ValidUntil) require.Equal(t, rootValidationData.Metadata.ChainId, event.MetadataChainID) @@ -1067,7 +1070,7 @@ func TestMcmWithTimelock(t *testing.T) { var newRootAndOpCount mcm.ExpiringRootAndOpCount - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.ExpiringRootAndOpCountPDA, config.DefaultCommitment, &newRootAndOpCount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.ExpiringRootAndOpCountPDA, config.DefaultCommitment, &newRootAndOpCount) require.NoError(t, err, "failed to get account info") require.Equal(t, rootValidationData.Root, newRootAndOpCount.Root) @@ -1076,7 +1079,7 @@ func TestMcmWithTimelock(t *testing.T) { // get config and validate var newRootMetadata mcm.RootMetadata - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.RootMetadataPDA, config.DefaultCommitment, &newRootMetadata) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, proposerMsig.RootMetadataPDA, config.DefaultCommitment, &newRootMetadata) require.NoError(t, err, "failed to get account info") require.Equal(t, rootValidationData.Metadata.ChainId, newRootMetadata.ChainId) @@ -1110,41 +1113,41 @@ func TestMcmWithTimelock(t *testing.T) { vIx, vIxErr := ix.ValidateAndBuild() require.NoError(t, vIxErr) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, anyone, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, anyone, config.DefaultCommitment) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[OpExecuted]("OpExecuted"), - utils.EventMappingFor[CallScheduled]("CallScheduled"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.OpExecuted]("OpExecuted"), + common.EventMappingFor[timelockutil.CallScheduled]("CallScheduled"), }, ) // check opExecuted event - event := parsedLogs[0].EventData[0].Data.(*OpExecuted) + event := parsedLogs[0].EventData[0].Data.(*mcms.OpExecuted) require.Equal(t, op.Nonce, event.Nonce) require.Equal(t, op.To, event.To) - require.Equal(t, op.Data, utils.NormalizeData(event.Data)) + require.Equal(t, op.Data, common.NormalizeData(event.Data)) // check inner CallScheduled events - currentOp := timelockOps[i] // match the TimelockOperation with the current opNode + currentOp := timelockOps[i] // match the Operation with the current opNode opIxData := currentOp.ToInstructionData() require.Equal(t, len(opIxData), len(parsedLogs[0].InnerCalls[0].EventData), "Number of actual CallScheduled events does not match expected for operation %d", i) for j, ix := range opIxData { - timelockEvent := parsedLogs[0].InnerCalls[0].EventData[j].Data.(*CallScheduled) + timelockEvent := parsedLogs[0].InnerCalls[0].EventData[j].Data.(*timelockutil.CallScheduled) require.Equal(t, currentOp.OperationID(), timelockEvent.ID, "ID does not match") require.Equal(t, uint64(j), timelockEvent.Index, "Index does not match") require.Equal(t, ix.ProgramId, timelockEvent.Target, "Target does not match") require.Equal(t, currentOp.Predecessor, timelockEvent.Predecessor, "Predecessor does not match") require.Equal(t, currentOp.Salt, timelockEvent.Salt, "Salt does not match") require.Equal(t, currentOp.Delay, timelockEvent.Delay, "Delay does not match") - require.Equal(t, ix.Data, utils.NormalizeData(timelockEvent.Data), "Data does not match") + require.Equal(t, ix.Data, common.NormalizeData(timelockEvent.Data), "Data does not match") } } }) - var newOp3 TimelockOperation + var newOp3 timelockutil.Operation t.Run("cancel and reschedule token distribution with corrected amounts", func(t *testing.T) { t.Run("cancel existing distribution operation through multisig", func(t *testing.T) { @@ -1161,14 +1164,14 @@ func TestMcmWithTimelock(t *testing.T) { // create MCM operation node for the cancel instruction // NOTE: nonce is 0 since it's the first operation - node, err := IxToMcmTestOpNode(canceller.ConfigPDA, canceller.SignerPDA, cancelIx, uint64(0)) + node, err := mcms.IxToMcmTestOpNode(canceller.ConfigPDA, canceller.SignerPDA, cancelIx, uint64(0)) require.NoError(t, err) - cancleOpNodes := []mcmsUtils.McmOpNode{node} + cancleOpNodes := []mcms.McmOpNode{node} // create and validate root data for the cancel operation validUntil := uint32(0xffffffff) - rootValidationData, err := CreateMcmRootData(McmRootInput{ + rootValidationData, err := mcms.CreateMcmRootData(mcms.McmRootInput{ Multisig: canceller.ConfigPDA, Operations: cancleOpNodes, PreOpCount: uint64(0), @@ -1178,12 +1181,12 @@ func TestMcmWithTimelock(t *testing.T) { }) require.NoError(t, err) - signatures, err := BulkSignOnMsgHash(canceller.Signers, rootValidationData.EthMsgHash) + signatures, err := mcms.BulkSignOnMsgHash(canceller.Signers, rootValidationData.EthMsgHash) require.NoError(t, err) signaturesPDA := canceller.RootSignaturesPDA(rootValidationData.Root, validUntil) - parsedTotalSigs, err := mcmsUtils.SafeToUint8(len(signatures)) + parsedTotalSigs, err := mcms.SafeToUint8(len(signatures)) require.NoError(t, err) initSigsIx, err := mcm.NewInitSignaturesInstruction( @@ -1196,9 +1199,9 @@ func TestMcmWithTimelock(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSigsIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSigsIx}, admin, config.DefaultCommitment) - appendSigsIxs, err := AppendSignaturesIxs( + appendSigsIxs, err := mcms.AppendSignaturesIxs( signatures, canceller.PaddedName, rootValidationData.Root, @@ -1209,7 +1212,7 @@ func TestMcmWithTimelock(t *testing.T) { ) require.NoError(t, err) for _, ix := range appendSigsIxs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } finalizeSigsIx, err := mcm.NewFinalizeSignaturesInstruction( @@ -1220,7 +1223,7 @@ func TestMcmWithTimelock(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSigsIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSigsIx}, admin, config.DefaultCommitment) setRootIx, err := mcm.NewSetRootInstruction( canceller.PaddedName, @@ -1230,7 +1233,7 @@ func TestMcmWithTimelock(t *testing.T) { rootValidationData.MetadataProof, signaturesPDA, canceller.RootMetadataPDA, - SeenSignedHashesAddress(canceller.PaddedName, rootValidationData.Root, validUntil), + mcms.SeenSignedHashesAddress(canceller.PaddedName, rootValidationData.Root, validUntil), canceller.ExpiringRootAndOpCountPDA, canceller.ConfigPDA, admin.PublicKey(), @@ -1238,15 +1241,15 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{setRootIx}, admin, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{setRootIx}, admin, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[NewRoot]("NewRoot"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.NewRoot]("NewRoot"), }, ) - event := parsedLogs[0].EventData[0].Data.(*NewRoot) + event := parsedLogs[0].EventData[0].Data.(*mcms.NewRoot) require.Equal(t, rootValidationData.Root, event.Root) require.Equal(t, validUntil, event.ValidUntil) require.Equal(t, rootValidationData.Metadata.ChainId, event.MetadataChainID) @@ -1277,43 +1280,43 @@ func TestMcmWithTimelock(t *testing.T) { vIx, err := executeIx.ValidateAndBuild() require.NoError(t, err) - exeTx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, anyone, config.DefaultCommitment) + exeTx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, anyone, config.DefaultCommitment) require.NotNil(t, exeTx) - parsedExeLogs := utils.ParseLogMessages(exeTx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[OpExecuted]("OpExecuted"), - utils.EventMappingFor[Cancelled]("Cancelled"), + parsedExeLogs := common.ParseLogMessages(exeTx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.OpExecuted]("OpExecuted"), + common.EventMappingFor[timelockutil.Cancelled]("Cancelled"), }, ) // check opExecuted event - exeEvent := parsedExeLogs[0].EventData[0].Data.(*OpExecuted) + exeEvent := parsedExeLogs[0].EventData[0].Data.(*mcms.OpExecuted) require.Equal(t, node.Nonce, exeEvent.Nonce) require.Equal(t, node.To, exeEvent.To) - require.Equal(t, node.Data, utils.NormalizeData(exeEvent.Data)) + require.Equal(t, node.Data, common.NormalizeData(exeEvent.Data)) // check inner Cancelled event - timelockEvent := parsedExeLogs[0].InnerCalls[0].EventData[0].Data.(*Cancelled) + timelockEvent := parsedExeLogs[0].InnerCalls[0].EventData[0].Data.(*timelockutil.Cancelled) require.Equal(t, op3.OperationID(), timelockEvent.ID, "ID does not match") // check if operation pda is closed - utils.AssertClosedAccount(ctx, t, solanaGoClient, op3.OperationPDA(), config.DefaultCommitment) + testutils.AssertClosedAccount(ctx, t, solanaGoClient, op3.OperationPDA(), config.DefaultCommitment) }) t.Run("create new operation with corrected amounts", func(t *testing.T) { // Create corrected transfer instructions with new amounts - ix1, err := utils.TokenTransferChecked(150*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team1ATA, config.TimelockSignerPDA, []solana.PublicKey{}) + ix1, err := tokens.TokenTransferChecked(150*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team1ATA, config.TimelockSignerPDA, []solana.PublicKey{}) require.NoError(t, err) - ix2, err := utils.TokenTransferChecked(150*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team2ATA, config.TimelockSignerPDA, []solana.PublicKey{}) + ix2, err := tokens.TokenTransferChecked(150*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team2ATA, config.TimelockSignerPDA, []solana.PublicKey{}) require.NoError(t, err) - ix3, err := utils.TokenTransferChecked(100*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team3ATA, config.TimelockSignerPDA, []solana.PublicKey{}) + ix3, err := tokens.TokenTransferChecked(100*solana.LAMPORTS_PER_SOL, 9, tokenProgram, treasuryATA, mint, team3ATA, config.TimelockSignerPDA, []solana.PublicKey{}) require.NoError(t, err) // Create new operation - salt, err := mcmsUtils.SimpleSalt() + salt, err := mcms.SimpleSalt() require.NoError(t, err) - newOp3 = TimelockOperation{ + newOp3 = timelockutil.Operation{ Predecessor: op2.OperationID(), Salt: salt, Delay: uint64(1), @@ -1323,10 +1326,10 @@ func TestMcmWithTimelock(t *testing.T) { newOp3.AddInstruction(ix2, []solana.PublicKey{tokenProgram}) newOp3.AddInstruction(ix3, []solana.PublicKey{tokenProgram}) - ixs, err := TimelockPreloadOperationIxs(ctx, newOp3, admin.PublicKey(), solanaGoClient) + ixs, err := timelockutil.PreloadOperationIxs(ctx, newOp3, admin.PublicKey(), solanaGoClient) require.NoError(t, err) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } // Create mcm operation node for scheduling @@ -1340,14 +1343,14 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - opNode, err := IxToMcmTestOpNode(proposerMsig.ConfigPDA, proposerMsig.SignerPDA, scheduleIx, uint64(currentOpCount)) + opNode, err := mcms.IxToMcmTestOpNode(proposerMsig.ConfigPDA, proposerMsig.SignerPDA, scheduleIx, uint64(currentOpCount)) require.NoError(t, err) - newOpNodes := []mcmsUtils.McmOpNode{opNode} + newOpNodes := []mcms.McmOpNode{opNode} // Create and validate root data validUntil := uint32(0xffffffff) - rootValidationData, err := CreateMcmRootData(McmRootInput{ + rootValidationData, err := mcms.CreateMcmRootData(mcms.McmRootInput{ Multisig: proposerMsig.ConfigPDA, Operations: newOpNodes, PreOpCount: uint64(currentOpCount), @@ -1360,13 +1363,13 @@ func TestMcmWithTimelock(t *testing.T) { currentOpCount++ // Sign and set root - signatures, err := BulkSignOnMsgHash(proposerMsig.Signers, rootValidationData.EthMsgHash) + signatures, err := mcms.BulkSignOnMsgHash(proposerMsig.Signers, rootValidationData.EthMsgHash) require.NoError(t, err) signaturesPDA := proposerMsig.RootSignaturesPDA(rootValidationData.Root, validUntil) // Initialize signatures - parsedTotalSigs, err := mcmsUtils.SafeToUint8(len(signatures)) + parsedTotalSigs, err := mcms.SafeToUint8(len(signatures)) require.NoError(t, err) initSigsIx, err := mcm.NewInitSignaturesInstruction( @@ -1379,10 +1382,10 @@ func TestMcmWithTimelock(t *testing.T) { solana.SystemProgramID, ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSigsIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initSigsIx}, admin, config.DefaultCommitment) // Append signatures - appendSigsIxs, err := AppendSignaturesIxs( + appendSigsIxs, err := mcms.AppendSignaturesIxs( signatures, proposerMsig.PaddedName, rootValidationData.Root, @@ -1393,7 +1396,7 @@ func TestMcmWithTimelock(t *testing.T) { ) require.NoError(t, err) for _, ix := range appendSigsIxs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } // Finalize signatures @@ -1405,7 +1408,7 @@ func TestMcmWithTimelock(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSigsIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{finalizeSigsIx}, admin, config.DefaultCommitment) // Set root setRootIx, err := mcm.NewSetRootInstruction( @@ -1416,7 +1419,7 @@ func TestMcmWithTimelock(t *testing.T) { rootValidationData.MetadataProof, signaturesPDA, proposerMsig.RootMetadataPDA, - SeenSignedHashesAddress(proposerMsig.PaddedName, rootValidationData.Root, validUntil), + mcms.SeenSignedHashesAddress(proposerMsig.PaddedName, rootValidationData.Root, validUntil), proposerMsig.ExpiringRootAndOpCountPDA, proposerMsig.ConfigPDA, admin.PublicKey(), @@ -1424,15 +1427,15 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{setRootIx}, admin, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{setRootIx}, admin, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[NewRoot]("NewRoot"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.NewRoot]("NewRoot"), }, ) - event := parsedLogs[0].EventData[0].Data.(*NewRoot) + event := parsedLogs[0].EventData[0].Data.(*mcms.NewRoot) require.Equal(t, rootValidationData.Root, event.Root) require.Equal(t, validUntil, event.ValidUntil) require.Equal(t, rootValidationData.Metadata.ChainId, event.MetadataChainID) @@ -1463,19 +1466,19 @@ func TestMcmWithTimelock(t *testing.T) { vIx, err := executeIx.ValidateAndBuild() require.NoError(t, err) - exeTx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, anyone, config.DefaultCommitment) + exeTx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, anyone, config.DefaultCommitment) require.NotNil(t, exeTx) - parsedExeLogs := utils.ParseLogMessages(exeTx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[OpExecuted]("OpExecuted"), - utils.EventMappingFor[CallScheduled]("CallScheduled"), + parsedExeLogs := common.ParseLogMessages(exeTx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[mcms.OpExecuted]("OpExecuted"), + common.EventMappingFor[timelockutil.CallScheduled]("CallScheduled"), }, ) - exeEvent := parsedExeLogs[0].EventData[0].Data.(*OpExecuted) + exeEvent := parsedExeLogs[0].EventData[0].Data.(*mcms.OpExecuted) require.Equal(t, opNode.Nonce, exeEvent.Nonce) require.Equal(t, opNode.To, exeEvent.To) - require.Equal(t, opNode.Data, utils.NormalizeData(exeEvent.Data)) + require.Equal(t, opNode.Data, common.NormalizeData(exeEvent.Data)) // check inner CallScheduled events opIxData := newOp3.ToInstructionData() @@ -1483,24 +1486,24 @@ func TestMcmWithTimelock(t *testing.T) { require.Equal(t, len(opIxData), len(parsedExeLogs[0].InnerCalls[0].EventData), "Number of actual CallScheduled events does not match expected for operation") for j, ix := range opIxData { - timelockEvent := parsedExeLogs[0].InnerCalls[0].EventData[j].Data.(*CallScheduled) + timelockEvent := parsedExeLogs[0].InnerCalls[0].EventData[j].Data.(*timelockutil.CallScheduled) require.Equal(t, newOp3.OperationID(), timelockEvent.ID, "ID does not match") require.Equal(t, uint64(j), timelockEvent.Index, "Index does not match") require.Equal(t, ix.ProgramId, timelockEvent.Target, "Target does not match") require.Equal(t, newOp3.Predecessor, timelockEvent.Predecessor, "Predecessor does not match") require.Equal(t, newOp3.Salt, timelockEvent.Salt, "Salt does not match") require.Equal(t, newOp3.Delay, timelockEvent.Delay, "Delay does not match") - require.Equal(t, ix.Data, utils.NormalizeData(timelockEvent.Data), "Data does not match") + require.Equal(t, ix.Data, common.NormalizeData(timelockEvent.Data), "Data does not match") } }) }) t.Run("execute timelock operations", func(t *testing.T) { // Wait for operations to be ready - err := WaitForOperationToBeReady(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment) + err := timelockutil.WaitForOperationToBeReady(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment) require.NoError(t, err) - rErr := WaitForOperationToBeReady(ctx, solanaGoClient, op2.OperationPDA(), config.DefaultCommitment) + rErr := timelockutil.WaitForOperationToBeReady(ctx, solanaGoClient, op2.OperationPDA(), config.DefaultCommitment) require.NoError(t, rErr) t.Run("op2: cannot be executed before op1", func(t *testing.T) { @@ -1518,7 +1521,7 @@ func TestMcmWithTimelock(t *testing.T) { vIx, err := ix.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{vIx}, admin, config.DefaultCommitment, @@ -1541,34 +1544,34 @@ func TestMcmWithTimelock(t *testing.T) { vIx, err := ix.ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, admin, config.DefaultCommitment, - utils.AddComputeUnitLimit(1_400_000), + common.AddComputeUnitLimit(1_400_000), ) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[CallExecuted]("CallExecuted"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.CallExecuted]("CallExecuted"), }, ) for i, ix := range op1.ToInstructionData() { - event := parsedLogs[0].EventData[i].Data.(*CallExecuted) + event := parsedLogs[0].EventData[i].Data.(*timelockutil.CallExecuted) require.Equal(t, op1.OperationID(), event.ID) require.Equal(t, uint64(i), event.Index) require.Equal(t, ix.ProgramId, event.Target) - require.Equal(t, ix.Data, utils.NormalizeData(event.Data)) + require.Equal(t, ix.Data, common.NormalizeData(event.Data)) } // Verify operation status var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment, &opAccount) require.NoError(t, err) require.Equal(t, config.TimelockOpDoneTimestamp, opAccount.Timestamp, "Op1 should be marked as executed") // Verify treasury balance - _, treasuryBalance, err := utils.TokenBalance(ctx, solanaGoClient, treasuryATA, config.DefaultCommitment) + _, treasuryBalance, err := tokens.TokenBalance(ctx, solanaGoClient, treasuryATA, config.DefaultCommitment) require.NoError(t, err) require.Equal(t, 1000*int(solana.LAMPORTS_PER_SOL), treasuryBalance, "Treasury should have received 1000 tokens") @@ -1583,10 +1586,10 @@ func TestMcmWithTimelock(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{fundIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{fundIx}, admin, config.DefaultCommitment) // approve can't be deligated to timelock authority(security - CPI Guard) - approveIx, err := utils.TokenApproveChecked( + approveIx, err := tokens.TokenApproveChecked( 600*solana.LAMPORTS_PER_SOL, 9, tokenProgram, @@ -1597,7 +1600,7 @@ func TestMcmWithTimelock(t *testing.T) { nil, ) require.NoError(t, err) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{approveIx}, treasury, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{approveIx}, treasury, config.DefaultCommitment) }) t.Run("op2: should provide the correct predecessor pda address", func(t *testing.T) { @@ -1615,7 +1618,7 @@ func TestMcmWithTimelock(t *testing.T) { vIx, err := ix.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{vIx}, admin, config.DefaultCommitment, @@ -1638,29 +1641,29 @@ func TestMcmWithTimelock(t *testing.T) { vIx, err := ix.ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, admin, config.DefaultCommitment, - utils.AddComputeUnitLimit(1_400_000), + common.AddComputeUnitLimit(1_400_000), ) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[CallExecuted]("CallExecuted"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.CallExecuted]("CallExecuted"), }, ) for i, ix := range op2.ToInstructionData() { - event := parsedLogs[0].EventData[i].Data.(*CallExecuted) + event := parsedLogs[0].EventData[i].Data.(*timelockutil.CallExecuted) require.Equal(t, op2.OperationID(), event.ID) require.Equal(t, uint64(i), event.Index) require.Equal(t, ix.ProgramId, event.Target) - require.Equal(t, ix.Data, utils.NormalizeData(event.Data)) + require.Equal(t, ix.Data, common.NormalizeData(event.Data)) } // verify operation status var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, op2.OperationPDA(), config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, op2.OperationPDA(), config.DefaultCommitment, &opAccount) require.NoError(t, err) require.Equal(t, config.TimelockOpDoneTimestamp, opAccount.Timestamp, "Op2 should be marked as executed") }) @@ -1668,7 +1671,7 @@ func TestMcmWithTimelock(t *testing.T) { t.Run("op3: team token distribution", func(t *testing.T) { // Wait for delay and execute the timelock operation - werr := WaitForOperationToBeReady(ctx, solanaGoClient, newOp3.OperationPDA(), config.DefaultCommitment) + werr := timelockutil.WaitForOperationToBeReady(ctx, solanaGoClient, newOp3.OperationPDA(), config.DefaultCommitment) require.NoError(t, werr) executeTimelockIx := timelock.NewExecuteBatchInstruction( @@ -1685,38 +1688,38 @@ func TestMcmWithTimelock(t *testing.T) { vTimelockIx, err := executeTimelockIx.ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vTimelockIx}, admin, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vTimelockIx}, admin, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[CallExecuted]("CallExecuted"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.CallExecuted]("CallExecuted"), }, ) for i, ix := range newOp3.ToInstructionData() { - event := parsedLogs[0].EventData[i].Data.(*CallExecuted) + event := parsedLogs[0].EventData[i].Data.(*timelockutil.CallExecuted) require.NotEqual(t, op3.OperationID(), event.ID) require.Equal(t, newOp3.OperationID(), event.ID) require.Equal(t, uint64(i), event.Index) require.Equal(t, ix.ProgramId, event.Target) - require.Equal(t, ix.Data, utils.NormalizeData(event.Data)) + require.Equal(t, ix.Data, common.NormalizeData(event.Data)) } // Verify final balances - _, treasuryBalance, err := utils.TokenBalance(ctx, solanaGoClient, treasuryATA, config.DefaultCommitment) + _, treasuryBalance, err := tokens.TokenBalance(ctx, solanaGoClient, treasuryATA, config.DefaultCommitment) require.NoError(t, err) require.Equal(t, 600*int(solana.LAMPORTS_PER_SOL), treasuryBalance, "Treasury should have 600 tokens remaining after distributions") - _, team1Balance, err := utils.TokenBalance(ctx, solanaGoClient, team1ATA, config.DefaultCommitment) + _, team1Balance, err := tokens.TokenBalance(ctx, solanaGoClient, team1ATA, config.DefaultCommitment) require.NoError(t, err) require.Equal(t, 150*int(solana.LAMPORTS_PER_SOL), team1Balance, "Team1 should have received 150 tokens") - _, team2Balance, err := utils.TokenBalance(ctx, solanaGoClient, team2ATA, config.DefaultCommitment) + _, team2Balance, err := tokens.TokenBalance(ctx, solanaGoClient, team2ATA, config.DefaultCommitment) require.NoError(t, err) require.Equal(t, 150*int(solana.LAMPORTS_PER_SOL), team2Balance, "Team2 should have received 150 tokens") - _, team3Balance, err := utils.TokenBalance(ctx, solanaGoClient, team3ATA, config.DefaultCommitment) + _, team3Balance, err := tokens.TokenBalance(ctx, solanaGoClient, team3ATA, config.DefaultCommitment) require.NoError(t, err) require.Equal(t, 100*int(solana.LAMPORTS_PER_SOL), team3Balance, "Team3 should have received 100 tokens") diff --git a/chains/solana/contracts/tests/mcms/timelock_bypasser_execute_test.go b/chains/solana/contracts/tests/mcms/timelock_bypasser_execute_test.go index 328088af4..042b6f7ce 100644 --- a/chains/solana/contracts/tests/mcms/timelock_bypasser_execute_test.go +++ b/chains/solana/contracts/tests/mcms/timelock_bypasser_execute_test.go @@ -12,12 +12,15 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/accesscontroller" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" - mcmsUtils "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/mcms" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/access_controller" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/timelock" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/accesscontroller" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/mcms" + timelockutil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/timelock" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens" ) func TestTimelockBypasserExecute(t *testing.T) { @@ -30,8 +33,8 @@ func TestTimelockBypasserExecute(t *testing.T) { admin, err := solana.NewRandomPrivateKey() require.NoError(t, err) - roles, roleMap := mcmsUtils.TestRoleAccounts(t, config.NumAccountsPerRole) - solanaGoClient := utils.DeployAllPrograms(t, utils.PathToAnchorConfig, admin) + roles, roleMap := timelockutil.TestRoleAccounts(config.NumAccountsPerRole) + solanaGoClient := testutils.DeployAllPrograms(t, testutils.PathToAnchorConfig, admin) allowance := struct { timelockAuthority uint64 @@ -61,18 +64,18 @@ func TestTimelockBypasserExecute(t *testing.T) { for _, role := range roles { all = append(all, role.Accounts...) } - utils.FundAccounts(ctx, all, solanaGoClient, t) + testutils.FundAccounts(ctx, all, solanaGoClient, t) }) t.Run("setup:init access controllers", func(t *testing.T) { for _, data := range roleMap { - initAccIxs, initAccIxsErr := InitAccessControllersIxs(ctx, data.AccessController.PublicKey(), admin, solanaGoClient) + initAccIxs, initAccIxsErr := timelockutil.InitAccessControllersIxs(ctx, data.AccessController.PublicKey(), admin, solanaGoClient) require.NoError(t, initAccIxsErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, initAccIxs, admin, config.DefaultCommitment, utils.AddSigners(data.AccessController)) + testutils.SendAndConfirm(ctx, t, solanaGoClient, initAccIxs, admin, config.DefaultCommitment, common.AddSigners(data.AccessController)) var ac access_controller.AccessController - acAccErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, data.AccessController.PublicKey(), config.DefaultCommitment, &ac) + acAccErr := common.GetAccountDataBorshInto(ctx, solanaGoClient, data.AccessController.PublicKey(), config.DefaultCommitment, &ac) if acAccErr != nil { require.NoError(t, acAccErr, "failed to get account info") } @@ -108,10 +111,10 @@ func TestTimelockBypasserExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, initErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initTimelockIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initTimelockIx}, admin, config.DefaultCommitment) var configAccount timelock.Config - cfgErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) + cfgErr := common.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) if cfgErr != nil { require.NoError(t, cfgErr, "failed to get account info") } @@ -130,11 +133,11 @@ func TestTimelockBypasserExecute(t *testing.T) { for _, account := range data.Accounts { addresses = append(addresses, account.PublicKey()) } - batchAddAccessIxs, batchAddAccessIxsErr := TimelockBatchAddAccessIxs(ctx, data.AccessController.PublicKey(), role, addresses, admin, config.BatchAddAccessChunkSize, solanaGoClient) + batchAddAccessIxs, batchAddAccessIxsErr := timelockutil.BatchAddAccessIxs(ctx, data.AccessController.PublicKey(), role, addresses, admin, config.BatchAddAccessChunkSize, solanaGoClient) require.NoError(t, batchAddAccessIxsErr) for _, ix := range batchAddAccessIxs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } for _, account := range data.Accounts { @@ -151,7 +154,7 @@ func TestTimelockBypasserExecute(t *testing.T) { fundPDAIx := system.NewTransferInstruction(allowance.timelockAuthority, admin.PublicKey(), config.TimelockSignerPDA).Build() - createAdminATAIx, _, caErr := utils.CreateAssociatedTokenAccount(tokenProgram, wsol, admin.PublicKey(), admin.PublicKey()) + createAdminATAIx, _, caErr := tokens.CreateAssociatedTokenAccount(tokenProgram, wsol, admin.PublicKey(), admin.PublicKey()) require.NoError(t, caErr) wrapSolIx, wsErr := system.NewTransferInstruction( @@ -161,14 +164,14 @@ func TestTimelockBypasserExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, wsErr) - syncNativeIx, snErr := utils.SyncNative( + syncNativeIx, snErr := tokens.SyncNative( tokenProgram, adminATA, // token account ) require.NoError(t, snErr) // approve can't be deligated to timelock authority(CPI Guard) - approveIx, aiErr := utils.TokenApproveChecked( + approveIx, aiErr := tokens.TokenApproveChecked( requiredAmount, wsolDecimal, tokenProgram, @@ -180,7 +183,7 @@ func TestTimelockBypasserExecute(t *testing.T) { ) require.NoError(t, aiErr) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{createAdminATAIx, wrapSolIx, syncNativeIx, fundPDAIx, approveIx}, admin, config.DefaultCommitment) require.NotNil(t, result) @@ -204,15 +207,15 @@ func TestTimelockBypasserExecute(t *testing.T) { }) t.Run("success: schedule and execute batch instructions", func(t *testing.T) { - salt, err := mcmsUtils.SimpleSalt() + salt, err := mcms.SimpleSalt() require.NoError(t, err) - op := TimelockOperation{ + op := timelockutil.Operation{ Predecessor: config.TimelockEmptyOpID, Salt: salt, Delay: uint64(1000), } - cIx, _, ciErr := utils.CreateAssociatedTokenAccount( + cIx, _, ciErr := tokens.CreateAssociatedTokenAccount( tokenProgram, wsol, recipient.PublicKey(), @@ -224,7 +227,7 @@ func TestTimelockBypasserExecute(t *testing.T) { []solana.PublicKey{tokenProgram, solana.SPLAssociatedTokenAccountProgramID}, ) - tIx, tiErr := utils.TokenTransferChecked( + tIx, tiErr := tokens.TokenTransferChecked( allowance.recipient, wsolDecimal, tokenProgram, @@ -241,14 +244,14 @@ func TestTimelockBypasserExecute(t *testing.T) { operationPDA := op.OperationPDA() signer := roleMap[timelock.Proposer_Role].RandomPick() - ixs, err := TimelockPreloadOperationIxs(ctx, op, signer.PublicKey(), solanaGoClient) + ixs, err := timelockutil.PreloadOperationIxs(ctx, op, signer.PublicKey(), solanaGoClient) require.NoError(t, err) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment) } var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, operationPDA, config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, operationPDA, config.DefaultCommitment, &opAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -276,24 +279,24 @@ func TestTimelockBypasserExecute(t *testing.T) { vIx, err := ix.ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, signer, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, signer, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[BypasserCallExecuted]("BypasserCallExecuted"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.BypasserCallExecuted]("BypasserCallExecuted"), }, ) for i, ixx := range op.ToInstructionData() { - event := parsedLogs[0].EventData[i].Data.(*BypasserCallExecuted) + event := parsedLogs[0].EventData[i].Data.(*timelockutil.BypasserCallExecuted) require.Equal(t, uint64(i), event.Index) require.Equal(t, ixx.ProgramId, event.Target) - require.Equal(t, ixx.Data, utils.NormalizeData(event.Data)) + require.Equal(t, ixx.Data, common.NormalizeData(event.Data)) } var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, operationPDA, config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, operationPDA, config.DefaultCommitment, &opAccount) if err != nil { require.NoError(t, err, "failed to get account info") } diff --git a/chains/solana/contracts/tests/mcms/timelock_rbac_test.go b/chains/solana/contracts/tests/mcms/timelock_rbac_test.go index 7cd7a7434..86bc79e8c 100644 --- a/chains/solana/contracts/tests/mcms/timelock_rbac_test.go +++ b/chains/solana/contracts/tests/mcms/timelock_rbac_test.go @@ -11,12 +11,14 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/accesscontroller" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" - mcmsUtils "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/mcms" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/access_controller" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/timelock" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/accesscontroller" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/mcms" + timelockutil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/timelock" ) func TestTimelockRBAC(t *testing.T) { @@ -35,8 +37,8 @@ func TestTimelockRBAC(t *testing.T) { user, err := solana.NewRandomPrivateKey() require.NoError(t, err) - roles, roleMap := mcmsUtils.TestRoleAccounts(t, config.NumAccountsPerRole) - solanaGoClient := utils.DeployAllPrograms(t, utils.PathToAnchorConfig, admin) + roles, roleMap := timelockutil.TestRoleAccounts(config.NumAccountsPerRole) + solanaGoClient := testutils.DeployAllPrograms(t, testutils.PathToAnchorConfig, admin) t.Run("setup:funding", func(t *testing.T) { all := []solana.PrivateKey{} @@ -45,18 +47,18 @@ func TestTimelockRBAC(t *testing.T) { for _, role := range roles { all = append(all, role.Accounts...) } - utils.FundAccounts(ctx, all, solanaGoClient, t) + testutils.FundAccounts(ctx, all, solanaGoClient, t) }) t.Run("setup:init access controllers", func(t *testing.T) { for _, data := range roleMap { - initAccIxs, ierr := InitAccessControllersIxs(ctx, data.AccessController.PublicKey(), admin, solanaGoClient) + initAccIxs, ierr := timelockutil.InitAccessControllersIxs(ctx, data.AccessController.PublicKey(), admin, solanaGoClient) require.NoError(t, ierr) - utils.SendAndConfirm(ctx, t, solanaGoClient, initAccIxs, admin, config.DefaultCommitment, utils.AddSigners(data.AccessController)) + testutils.SendAndConfirm(ctx, t, solanaGoClient, initAccIxs, admin, config.DefaultCommitment, common.AddSigners(data.AccessController)) var ac access_controller.AccessController - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, data.AccessController.PublicKey(), config.DefaultCommitment, &ac) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, data.AccessController.PublicKey(), config.DefaultCommitment, &ac) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -92,7 +94,7 @@ func TestTimelockRBAC(t *testing.T) { ).ValidateAndBuild() require.NoError(t, ierr) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{initTimelockIx}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + UnauthorizedTimelockError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{initTimelockIx}, anotherAdmin, config.DefaultCommitment, []string{"Error Code: " + timelockutil.UnauthorizedError.String()}) require.NotNil(t, result) }) @@ -125,10 +127,10 @@ func TestTimelockRBAC(t *testing.T) { ).ValidateAndBuild() require.NoError(t, ierr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initTimelockIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initTimelockIx}, admin, config.DefaultCommitment) var configAccount timelock.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -149,7 +151,7 @@ func TestTimelockRBAC(t *testing.T) { user.PublicKey(), ).ValidateAndBuild() require.NoError(t, ierr) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + UnauthorizedTimelockError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + timelockutil.UnauthorizedError.String()}) require.NotNil(t, result) }) @@ -160,7 +162,7 @@ func TestTimelockRBAC(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, ierr) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment, []string{"Error Code: " + timelock.InvalidInput_TimelockError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment, []string{"Error Code: " + timelock.InvalidInput_TimelockError.String()}) require.NotNil(t, result) }) @@ -171,7 +173,7 @@ func TestTimelockRBAC(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, ierr) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, admin, config.DefaultCommitment) require.NotNil(t, result) }) @@ -181,7 +183,7 @@ func TestTimelockRBAC(t *testing.T) { user.PublicKey(), ).ValidateAndBuild() require.NoError(t, ierr) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + UnauthorizedTimelockError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{instruction}, user, config.DefaultCommitment, []string{"Error Code: " + timelockutil.UnauthorizedError.String()}) require.NotNil(t, result) }) @@ -191,12 +193,12 @@ func TestTimelockRBAC(t *testing.T) { anotherAdmin.PublicKey(), ).ValidateAndBuild() require.NoError(t, ierr) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{instruction}, anotherAdmin, config.DefaultCommitment) require.NotNil(t, result) // Validate proposed set to 0-address after accepting ownership var configAccount timelock.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -212,7 +214,7 @@ func TestTimelockRBAC(t *testing.T) { anotherAdmin.PublicKey(), ).ValidateAndBuild() require.NoError(t, ierr) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{tix}, anotherAdmin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{tix}, anotherAdmin, config.DefaultCommitment) require.NotNil(t, result) aix, aerr := timelock.NewAcceptOwnershipInstruction( @@ -220,11 +222,11 @@ func TestTimelockRBAC(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, aerr) - result = utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{aix}, admin, config.DefaultCommitment) + result = testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{aix}, admin, config.DefaultCommitment) require.NotNil(t, result) var configAccount timelock.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -240,11 +242,11 @@ func TestTimelockRBAC(t *testing.T) { for _, account := range data.Accounts { addresses = append(addresses, account.PublicKey()) } - batchAddAccessIxs, baerr := TimelockBatchAddAccessIxs(ctx, data.AccessController.PublicKey(), role, addresses, admin, config.BatchAddAccessChunkSize, solanaGoClient) + batchAddAccessIxs, baerr := timelockutil.BatchAddAccessIxs(ctx, data.AccessController.PublicKey(), role, addresses, admin, config.BatchAddAccessChunkSize, solanaGoClient) require.NoError(t, baerr) for _, ix := range batchAddAccessIxs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } for _, account := range data.Accounts { @@ -256,9 +258,9 @@ func TestTimelockRBAC(t *testing.T) { }) t.Run("rbac: schedule and cancel a timelock operation", func(t *testing.T) { - salt, serr := mcmsUtils.SimpleSalt() + salt, serr := mcms.SimpleSalt() require.NoError(t, serr) - nonExecutableOp := TimelockOperation{ + nonExecutableOp := timelockutil.Operation{ Predecessor: config.TimelockEmptyOpID, Salt: salt, Delay: uint64(1), @@ -271,10 +273,10 @@ func TestTimelockRBAC(t *testing.T) { nonProposer := roleMap[timelock.Executor_Role].RandomPick() ac := roleMap[timelock.Proposer_Role].AccessController - ixs, prierr := TimelockPreloadOperationIxs(ctx, nonExecutableOp, nonProposer.PublicKey(), solanaGoClient) + ixs, prierr := timelockutil.PreloadOperationIxs(ctx, nonExecutableOp, nonProposer.PublicKey(), solanaGoClient) require.NoError(t, prierr) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, nonProposer, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, nonProposer, config.DefaultCommitment) } ix, scerr := timelock.NewScheduleBatchInstruction( @@ -286,7 +288,7 @@ func TestTimelockRBAC(t *testing.T) { nonProposer.PublicKey(), ).ValidateAndBuild() require.NoError(t, scerr) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, nonProposer, config.DefaultCommitment, []string{"Error Code: " + UnauthorizedTimelockError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, nonProposer, config.DefaultCommitment, []string{"Error Code: " + timelockutil.UnauthorizedError.String()}) }) t.Run("rbac: Should able to schedule tx with proposer role", func(t *testing.T) { @@ -300,7 +302,7 @@ func TestTimelockRBAC(t *testing.T) { proposer.PublicKey(), // remove access of proposer ).ValidateAndBuild() require.NoError(t, raerr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{raIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{raIx}, admin, config.DefaultCommitment) found, ferr := accesscontroller.HasAccess(ctx, solanaGoClient, ac.PublicKey(), proposer.PublicKey(), config.DefaultCommitment) require.NoError(t, ferr) @@ -315,7 +317,7 @@ func TestTimelockRBAC(t *testing.T) { proposer.PublicKey(), ).ValidateAndBuild() require.NoError(t, scerr) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment, []string{"Error Code: " + UnauthorizedTimelockError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment, []string{"Error Code: " + timelockutil.UnauthorizedError.String()}) }) raIx, raerr := access_controller.NewAddAccessInstruction( @@ -324,15 +326,15 @@ func TestTimelockRBAC(t *testing.T) { proposer.PublicKey(), // add access of proposer again ).ValidateAndBuild() require.NoError(t, raerr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{raIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{raIx}, admin, config.DefaultCommitment) found, ferr := accesscontroller.HasAccess(ctx, solanaGoClient, ac.PublicKey(), proposer.PublicKey(), config.DefaultCommitment) require.NoError(t, ferr) require.True(t, found, "Account %s should be in the AccessList", proposer.PublicKey()) - salt, serr := mcmsUtils.SimpleSalt() + salt, serr := mcms.SimpleSalt() require.NoError(t, serr) - nonExecutableOp2 := TimelockOperation{ + nonExecutableOp2 := timelockutil.Operation{ Predecessor: config.TimelockEmptyOpID, Salt: salt, Delay: uint64(1), @@ -340,10 +342,10 @@ func TestTimelockRBAC(t *testing.T) { ix := system.NewTransferInstruction(1*solana.LAMPORTS_PER_SOL, admin.PublicKey(), config.TimelockSignerPDA).Build() nonExecutableOp2.AddInstruction(ix, []solana.PublicKey{}) - ixs, prerr := TimelockPreloadOperationIxs(ctx, nonExecutableOp2, proposer.PublicKey(), solanaGoClient) + ixs, prerr := timelockutil.PreloadOperationIxs(ctx, nonExecutableOp2, proposer.PublicKey(), solanaGoClient) require.NoError(t, prerr) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) } sbix, sberr := timelock.NewScheduleBatchInstruction( @@ -356,28 +358,28 @@ func TestTimelockRBAC(t *testing.T) { ).ValidateAndBuild() require.NoError(t, sberr) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{sbix}, proposer, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{sbix}, proposer, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[CallScheduled]("CallScheduled"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.CallScheduled]("CallScheduled"), }, ) for i, ixx := range nonExecutableOp2.ToInstructionData() { - event := parsedLogs[0].EventData[i].Data.(*CallScheduled) + event := parsedLogs[0].EventData[i].Data.(*timelockutil.CallScheduled) require.Equal(t, nonExecutableOp2.OperationID(), event.ID) require.Equal(t, uint64(i), event.Index) require.Equal(t, ixx.ProgramId, event.Target) require.Equal(t, nonExecutableOp2.Predecessor, event.Predecessor) require.Equal(t, nonExecutableOp2.Salt, event.Salt) require.Equal(t, nonExecutableOp2.Delay, event.Delay) - require.Equal(t, ixx.Data, utils.NormalizeData(event.Data)) + require.Equal(t, ixx.Data, common.NormalizeData(event.Data)) } var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, nonExecutableOp2.OperationPDA(), config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, nonExecutableOp2.OperationPDA(), config.DefaultCommitment, &opAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -408,7 +410,7 @@ func TestTimelockRBAC(t *testing.T) { require.NoError(t, cerr) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment, []string{"Error Code: " + "InvalidAccessController."}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment, []string{"Error Code: " + "InvalidAccessController."}) require.NotNil(t, result) }) @@ -425,7 +427,7 @@ func TestTimelockRBAC(t *testing.T) { ).ValidateAndBuild() require.NoError(t, cerr) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment, []string{"Error Code: " + UnauthorizedTimelockError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment, []string{"Error Code: " + timelockutil.UnauthorizedError.String()}) require.NotNil(t, result) }) @@ -442,21 +444,21 @@ func TestTimelockRBAC(t *testing.T) { ).ValidateAndBuild() require.NoError(t, cerr) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[Cancelled]("Cancelled"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.Cancelled]("Cancelled"), }, ) for i := range nonExecutableOp2.ToInstructionData() { - event := parsedLogs[0].EventData[i].Data.(*Cancelled) + event := parsedLogs[0].EventData[i].Data.(*timelockutil.Cancelled) require.Equal(t, nonExecutableOp2.OperationID(), event.ID) } - utils.AssertClosedAccount(ctx, t, solanaGoClient, nonExecutableOp2.OperationPDA(), config.DefaultCommitment) + testutils.AssertClosedAccount(ctx, t, solanaGoClient, nonExecutableOp2.OperationPDA(), config.DefaultCommitment) }) }) }) @@ -475,7 +477,7 @@ func TestTimelockRBAC(t *testing.T) { ).ValidateAndBuild() require.NoError(t, ierr) - result := utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment, []string{"Error Code: " + UnauthorizedTimelockError.String()}) + result := testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment, []string{"Error Code: " + timelockutil.UnauthorizedError.String()}) require.NotNil(t, result) }) @@ -483,7 +485,7 @@ func TestTimelockRBAC(t *testing.T) { signer := admin var oldConfigAccount timelock.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &oldConfigAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &oldConfigAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -495,21 +497,21 @@ func TestTimelockRBAC(t *testing.T) { ).ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, signer, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[MinDelayChange]("MinDelayChange"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.MinDelayChange]("MinDelayChange"), }, ) - event := parsedLogs[0].EventData[0].Data.(*MinDelayChange) + event := parsedLogs[0].EventData[0].Data.(*timelockutil.MinDelayChange) require.Equal(t, oldConfigAccount.MinDelay, event.OldDuration) require.Equal(t, newMinDelay, event.NewDuration) var newConfigAccount timelock.Config - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &newConfigAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &newConfigAccount) if err != nil { require.NoError(t, err, "failed to get account info") } diff --git a/chains/solana/contracts/tests/mcms/timelock_schedule_execute_test.go b/chains/solana/contracts/tests/mcms/timelock_schedule_execute_test.go index 0a2f47aab..cfd021366 100644 --- a/chains/solana/contracts/tests/mcms/timelock_schedule_execute_test.go +++ b/chains/solana/contracts/tests/mcms/timelock_schedule_execute_test.go @@ -13,13 +13,16 @@ import ( "github.com/smartcontractkit/chainlink-common/pkg/utils/tests" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/accesscontroller" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" - mcmsUtils "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/mcms" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/testutils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/access_controller" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/external_program_cpi_stub" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/timelock" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/accesscontroller" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/mcms" + timelockutil "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/timelock" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/tokens" ) func TestTimelockScheduleAndExecute(t *testing.T) { @@ -32,8 +35,8 @@ func TestTimelockScheduleAndExecute(t *testing.T) { admin, err := solana.NewRandomPrivateKey() require.NoError(t, err) - roles, roleMap := mcmsUtils.TestRoleAccounts(t, config.NumAccountsPerRole) - solanaGoClient := utils.DeployAllPrograms(t, utils.PathToAnchorConfig, admin) + roles, roleMap := timelockutil.TestRoleAccounts(config.NumAccountsPerRole) + solanaGoClient := testutils.DeployAllPrograms(t, testutils.PathToAnchorConfig, admin) allowance := struct { timelockAuthority uint64 recipient uint64 @@ -62,18 +65,18 @@ func TestTimelockScheduleAndExecute(t *testing.T) { for _, role := range roles { all = append(all, role.Accounts...) } - utils.FundAccounts(ctx, all, solanaGoClient, t) + testutils.FundAccounts(ctx, all, solanaGoClient, t) }) t.Run("setup:init access controllers", func(t *testing.T) { for _, data := range roleMap { - initAccIxs, initAccIxsErr := InitAccessControllersIxs(ctx, data.AccessController.PublicKey(), admin, solanaGoClient) + initAccIxs, initAccIxsErr := timelockutil.InitAccessControllersIxs(ctx, data.AccessController.PublicKey(), admin, solanaGoClient) require.NoError(t, initAccIxsErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, initAccIxs, admin, config.DefaultCommitment, utils.AddSigners(data.AccessController)) + testutils.SendAndConfirm(ctx, t, solanaGoClient, initAccIxs, admin, config.DefaultCommitment, common.AddSigners(data.AccessController)) var ac access_controller.AccessController - acAccErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, data.AccessController.PublicKey(), config.DefaultCommitment, &ac) + acAccErr := common.GetAccountDataBorshInto(ctx, solanaGoClient, data.AccessController.PublicKey(), config.DefaultCommitment, &ac) if acAccErr != nil { require.NoError(t, acAccErr, "failed to get account info") } @@ -108,10 +111,10 @@ func TestTimelockScheduleAndExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, initErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initTimelockIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{initTimelockIx}, admin, config.DefaultCommitment) var configAccount timelock.Config - cfgErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) + cfgErr := common.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) if cfgErr != nil { require.NoError(t, cfgErr, "failed to get account info") } @@ -130,11 +133,11 @@ func TestTimelockScheduleAndExecute(t *testing.T) { for _, account := range data.Accounts { addresses = append(addresses, account.PublicKey()) } - batchAddAccessIxs, batchAddAccessIxsErr := TimelockBatchAddAccessIxs(ctx, data.AccessController.PublicKey(), role, addresses, admin, config.BatchAddAccessChunkSize, solanaGoClient) + batchAddAccessIxs, batchAddAccessIxsErr := timelockutil.BatchAddAccessIxs(ctx, data.AccessController.PublicKey(), role, addresses, admin, config.BatchAddAccessChunkSize, solanaGoClient) require.NoError(t, batchAddAccessIxsErr) for _, ix := range batchAddAccessIxs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) } for _, account := range data.Accounts { @@ -156,11 +159,11 @@ func TestTimelockScheduleAndExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, updateDelayIxErr) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment) require.NotNil(t, result) var configAccount timelock.Config - getConfigAccountErr := utils.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) + getConfigAccountErr := common.GetAccountDataBorshInto(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment, &configAccount) require.NoError(t, getConfigAccountErr, "failed to get account info") require.Equal(t, newMinDelay, configAccount.MinDelay, "MinDelay is not updated") @@ -171,7 +174,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { fundPDAIx := system.NewTransferInstruction(allowance.timelockAuthority, admin.PublicKey(), config.TimelockSignerPDA).Build() - createAdminATAIx, _, caErr := utils.CreateAssociatedTokenAccount(tokenProgram, wsol, admin.PublicKey(), admin.PublicKey()) + createAdminATAIx, _, caErr := tokens.CreateAssociatedTokenAccount(tokenProgram, wsol, admin.PublicKey(), admin.PublicKey()) require.NoError(t, caErr) wrapSolIx := system.NewTransferInstruction( @@ -180,14 +183,14 @@ func TestTimelockScheduleAndExecute(t *testing.T) { adminATA, ).Build() - syncNativeIx, snErr := utils.SyncNative( + syncNativeIx, snErr := tokens.SyncNative( tokenProgram, adminATA, // token account ) require.NoError(t, snErr) // approve can't be deligated to timelock authority(CPI Guard) - approveIx, aiErr := utils.TokenApproveChecked( + approveIx, aiErr := tokens.TokenApproveChecked( requiredAmount*2, // double the requiredAmount for op2 + op3(op2 will be executed only) wsolDecimal, tokenProgram, @@ -199,7 +202,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { ) require.NoError(t, aiErr) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{createAdminATAIx, wrapSolIx, syncNativeIx, fundPDAIx, approveIx}, admin, config.DefaultCommitment) require.NotNil(t, result) @@ -222,14 +225,14 @@ func TestTimelockScheduleAndExecute(t *testing.T) { }) t.Run("success: schedule and execute operations", func(t *testing.T) { - salt1, err := mcmsUtils.SimpleSalt() + salt1, err := mcms.SimpleSalt() require.NoError(t, err) - op1 := TimelockOperation{ + op1 := timelockutil.Operation{ Predecessor: config.TimelockEmptyOpID, Salt: salt1, Delay: 2, } - cIx, _, ciErr := utils.CreateAssociatedTokenAccount( + cIx, _, ciErr := tokens.CreateAssociatedTokenAccount( tokenProgram, wsol, recipient.PublicKey(), @@ -238,15 +241,15 @@ func TestTimelockScheduleAndExecute(t *testing.T) { require.NoError(t, ciErr) op1.AddInstruction(cIx, []solana.PublicKey{solana.TokenProgramID, solana.SPLAssociatedTokenAccountProgramID}) - salt2, err := mcmsUtils.SimpleSalt() + salt2, err := mcms.SimpleSalt() require.NoError(t, err) - op2 := TimelockOperation{ + op2 := timelockutil.Operation{ Predecessor: op1.OperationID(), Salt: salt2, Delay: 2, } - tIx, tiErr := utils.TokenTransferChecked( + tIx, tiErr := tokens.TokenTransferChecked( allowance.recipient, wsolDecimal, tokenProgram, @@ -259,15 +262,15 @@ func TestTimelockScheduleAndExecute(t *testing.T) { require.NoError(t, tiErr) op2.AddInstruction(tIx, []solana.PublicKey{tokenProgram}) - salt3, err := mcmsUtils.SimpleSalt() + salt3, err := mcms.SimpleSalt() require.NoError(t, err) - op3 := TimelockOperation{ + op3 := timelockutil.Operation{ Predecessor: op1.OperationID(), Salt: salt3, Delay: 300, // enough delay to assert OperationNotReady error } - anotherTransferIx, atErr := utils.TokenTransferChecked( + anotherTransferIx, atErr := tokens.TokenTransferChecked( allowance.recipient, wsolDecimal, tokenProgram, @@ -288,11 +291,11 @@ func TestTimelockScheduleAndExecute(t *testing.T) { executorAccessController := roleMap[timelock.Executor_Role].AccessController.PublicKey() t.Run("success: schedule all operations", func(t *testing.T) { - for _, op := range []TimelockOperation{op1, op2, op3} { - invalidIxs, ierr := TimelockPreloadOperationIxs(ctx, op, proposer.PublicKey(), solanaGoClient) + for _, op := range []timelockutil.Operation{op1, op2, op3} { + invalidIxs, ierr := timelockutil.PreloadOperationIxs(ctx, op, proposer.PublicKey(), solanaGoClient) require.NoError(t, ierr) for _, ix := range invalidIxs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) } t.Run("clear operation", func(t *testing.T) { @@ -306,15 +309,15 @@ func TestTimelockScheduleAndExecute(t *testing.T) { require.NoError(t, ciErr) // send clear and check if it's closed - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{clearIx}, proposer, config.DefaultCommitment) - utils.AssertClosedAccount(ctx, t, solanaGoClient, op.OperationPDA(), config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{clearIx}, proposer, config.DefaultCommitment) + testutils.AssertClosedAccount(ctx, t, solanaGoClient, op.OperationPDA(), config.DefaultCommitment) }) // re-preload instructions - ixs, err := TimelockPreloadOperationIxs(ctx, op, proposer.PublicKey(), solanaGoClient) + ixs, err := timelockutil.PreloadOperationIxs(ctx, op, proposer.PublicKey(), solanaGoClient) require.NoError(t, err) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) } ix, ixVErr := timelock.NewScheduleBatchInstruction( @@ -327,11 +330,11 @@ func TestTimelockScheduleAndExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, ixVErr) - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) require.NotNil(t, result) var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, op.OperationPDA(), config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, op.OperationPDA(), config.DefaultCommitment, &opAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, @@ -350,7 +353,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { t.Run("wait for operation 1 to be ready", func(t *testing.T) { // Wait for operations to be ready - err := WaitForOperationToBeReady(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment) + err := timelockutil.WaitForOperationToBeReady(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment) require.NoError(t, err) }) @@ -364,12 +367,12 @@ func TestTimelockScheduleAndExecute(t *testing.T) { proposer.PublicKey(), ).Build() - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment, []string{"Error Code: " + timelock.OperationAlreadyScheduled_TimelockError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment, []string{"Error Code: " + timelock.OperationAlreadyScheduled_TimelockError.String()}) }) t.Run("wait for operation 2 to be ready", func(t *testing.T) { // Wait for operations to be ready - err := WaitForOperationToBeReady(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment) + err := timelockutil.WaitForOperationToBeReady(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment) require.NoError(t, err) }) @@ -388,7 +391,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { vIx, err := ix.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{vIx}, executor, config.DefaultCommitment, []string{"Error Code: " + timelock.InvalidInput_TimelockError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{vIx}, executor, config.DefaultCommitment, []string{"Error Code: " + timelock.InvalidInput_TimelockError.String()}) }) t.Run("fail: not able to execute op2 before dependency(op1) execution", func(t *testing.T) { @@ -407,7 +410,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { vIx, err := ix.ValidateAndBuild() require.NoError(t, err) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{vIx}, executor, config.DefaultCommitment, []string{"Error Code: " + timelock.MissingDependency_TimelockError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{vIx}, executor, config.DefaultCommitment, []string{"Error Code: " + timelock.MissingDependency_TimelockError.String()}) }) t.Run("success: op1 executed", func(t *testing.T) { @@ -426,25 +429,25 @@ func TestTimelockScheduleAndExecute(t *testing.T) { vIx, err := ix.ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, executor, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, executor, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[CallExecuted]("CallExecuted"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.CallExecuted]("CallExecuted"), }, ) for i, ixx := range op1.ToInstructionData() { - event := parsedLogs[0].EventData[i].Data.(*CallExecuted) + event := parsedLogs[0].EventData[i].Data.(*timelockutil.CallExecuted) require.Equal(t, op1.OperationID(), event.ID) require.Equal(t, uint64(i), event.Index) require.Equal(t, ixx.ProgramId, event.Target) - require.Equal(t, ixx.Data, utils.NormalizeData(event.Data)) + require.Equal(t, ixx.Data, common.NormalizeData(event.Data)) } var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment, &opAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -472,25 +475,25 @@ func TestTimelockScheduleAndExecute(t *testing.T) { vIx, err := ix.ValidateAndBuild() require.NoError(t, err) - tx := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, executor, config.DefaultCommitment) + tx := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{vIx}, executor, config.DefaultCommitment) require.NotNil(t, tx) - parsedLogs := utils.ParseLogMessages(tx.Meta.LogMessages, - []utils.EventMapping{ - utils.EventMappingFor[CallExecuted]("CallExecuted"), + parsedLogs := common.ParseLogMessages(tx.Meta.LogMessages, + []common.EventMapping{ + common.EventMappingFor[timelockutil.CallExecuted]("CallExecuted"), }, ) for i, ixx := range op2.ToInstructionData() { - event := parsedLogs[0].EventData[i].Data.(*CallExecuted) + event := parsedLogs[0].EventData[i].Data.(*timelockutil.CallExecuted) require.Equal(t, op2.OperationID(), event.ID) require.Equal(t, uint64(i), event.Index) require.Equal(t, ixx.ProgramId, event.Target) - require.Equal(t, ixx.Data, utils.NormalizeData(event.Data)) + require.Equal(t, ixx.Data, common.NormalizeData(event.Data)) } var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, op1.OperationPDA(), config.DefaultCommitment, &opAccount) if err != nil { require.NoError(t, err, "failed to get account info") } @@ -529,7 +532,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { vIx, vIxErr := ix.ValidateAndBuild() require.NoError(t, vIxErr) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{vIx}, executor, config.DefaultCommitment, []string{"Error Code: " + timelock.OperationNotReady_TimelockError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{vIx}, executor, config.DefaultCommitment, []string{"Error Code: " + timelock.OperationNotReady_TimelockError.String()}) }) }) }) @@ -539,10 +542,10 @@ func TestTimelockScheduleAndExecute(t *testing.T) { proposer := roleMap[timelock.Proposer_Role].RandomPick() proposerAccessController := roleMap[timelock.Proposer_Role].AccessController.PublicKey() - salt, err := mcmsUtils.SimpleSalt() + salt, err := mcms.SimpleSalt() require.NoError(t, err) - op := TimelockOperation{ + op := timelockutil.Operation{ Predecessor: config.TimelockEmptyOpID, Salt: salt, Delay: 1, @@ -564,9 +567,9 @@ func TestTimelockScheduleAndExecute(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, bIxErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{bIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{bIx}, admin, config.DefaultCommitment) - blockedSelectors, bserr := GetBlockedFunctionSelectors(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment) + blockedSelectors, bserr := timelockutil.GetBlockedFunctionSelectors(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment) require.NoError(t, bserr) require.Contains(t, blockedSelectors, external_program_cpi_stub.Instruction_Initialize.Bytes()) }) @@ -578,16 +581,16 @@ func TestTimelockScheduleAndExecute(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, bbIxErr) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{bbIx}, admin, config.DefaultCommitment, []string{"Error Code: " + timelock.AlreadyBlocked_TimelockError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{bbIx}, admin, config.DefaultCommitment, []string{"Error Code: " + timelock.AlreadyBlocked_TimelockError.String()}) }) id := op.OperationID() operationPDA := op.OperationPDA() - ixs, err := TimelockPreloadOperationIxs(ctx, op, proposer.PublicKey(), solanaGoClient) + ixs, err := timelockutil.PreloadOperationIxs(ctx, op, proposer.PublicKey(), solanaGoClient) require.NoError(t, err) for _, ix := range ixs { - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{ix}, proposer, config.DefaultCommitment) } scIx, scIxVErr := timelock.NewScheduleBatchInstruction( @@ -600,7 +603,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, scIxVErr) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{scIx}, proposer, config.DefaultCommitment, []string{"Error Code: " + timelock.BlockedSelector_TimelockError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{scIx}, proposer, config.DefaultCommitment, []string{"Error Code: " + timelock.BlockedSelector_TimelockError.String()}) t.Run("unblocks initialize function", func(t *testing.T) { bIx, bIxErr := timelock.NewUnblockFunctionSelectorInstruction( @@ -609,9 +612,9 @@ func TestTimelockScheduleAndExecute(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, bIxErr) - utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{bIx}, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{bIx}, admin, config.DefaultCommitment) - blockedSelectors, bserr := GetBlockedFunctionSelectors(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment) + blockedSelectors, bserr := timelockutil.GetBlockedFunctionSelectors(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment) require.NoError(t, bserr) require.NotContains(t, blockedSelectors, external_program_cpi_stub.Instruction_Initialize.Bytes()) }) @@ -623,15 +626,15 @@ func TestTimelockScheduleAndExecute(t *testing.T) { admin.PublicKey(), ).ValidateAndBuild() require.NoError(t, bbIxErr) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{bbIx}, admin, config.DefaultCommitment, []string{"Error Code: " + timelock.SelectorNotFound_TimelockError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{bbIx}, admin, config.DefaultCommitment, []string{"Error Code: " + timelock.SelectorNotFound_TimelockError.String()}) }) t.Run("when unblocked, able to schedule operation", func(t *testing.T) { - result := utils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{scIx}, proposer, config.DefaultCommitment) + result := testutils.SendAndConfirm(ctx, t, solanaGoClient, []solana.Instruction{scIx}, proposer, config.DefaultCommitment) require.NotNil(t, result) var opAccount timelock.Operation - err = utils.GetAccountDataBorshInto(ctx, solanaGoClient, operationPDA, config.DefaultCommitment, &opAccount) + err = common.GetAccountDataBorshInto(ctx, solanaGoClient, operationPDA, config.DefaultCommitment, &opAccount) require.NoError(t, err, "failed to get account info") require.Equal(t, op.OperationID(), opAccount.Id, "Ids don't match") @@ -644,7 +647,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { t.Run("can't register more than MAX_SELECTOR", func(t *testing.T) { // check if it's empty - oldBlockedSelectors, gberr := GetBlockedFunctionSelectors(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment) + oldBlockedSelectors, gberr := timelockutil.GetBlockedFunctionSelectors(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment) require.NoError(t, gberr) require.Empty(t, oldBlockedSelectors) @@ -668,11 +671,11 @@ func TestTimelockScheduleAndExecute(t *testing.T) { end = len(ixs) } chunk := ixs[i:end] - utils.SendAndConfirm(ctx, t, solanaGoClient, chunk, admin, config.DefaultCommitment) + testutils.SendAndConfirm(ctx, t, solanaGoClient, chunk, admin, config.DefaultCommitment) } // check if it's full - blockedSelectors, bserr := GetBlockedFunctionSelectors(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment) + blockedSelectors, bserr := timelockutil.GetBlockedFunctionSelectors(ctx, solanaGoClient, config.TimelockConfigPDA, config.DefaultCommitment) require.NoError(t, bserr) require.Equal(t, config.MaxFunctionSelectorLen, len(blockedSelectors)) @@ -684,7 +687,7 @@ func TestTimelockScheduleAndExecute(t *testing.T) { ).ValidateAndBuild() require.NoError(t, nberr) - utils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment, []string{"Error Code: " + timelock.MaxCapacityReached_TimelockError.String()}) + testutils.SendAndFailWith(ctx, t, solanaGoClient, []solana.Instruction{ix}, admin, config.DefaultCommitment, []string{"Error Code: " + timelock.MaxCapacityReached_TimelockError.String()}) }) }) } diff --git a/chains/solana/contracts/tests/utils/anchor.go b/chains/solana/contracts/tests/testutils/anchor.go similarity index 51% rename from chains/solana/contracts/tests/utils/anchor.go rename to chains/solana/contracts/tests/testutils/anchor.go index b5b78470c..7a149fbdf 100644 --- a/chains/solana/contracts/tests/utils/anchor.go +++ b/chains/solana/contracts/tests/testutils/anchor.go @@ -1,20 +1,13 @@ -package utils +package testutils import ( - "bytes" "context" - "crypto/rand" - "crypto/sha256" - "encoding/base64" - "encoding/binary" "fmt" "os" "path/filepath" - "strings" "testing" "time" - bin "github.com/gagliardetto/binary" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" @@ -25,48 +18,6 @@ import ( var PathToAnchorConfig = filepath.Join(ProjectRoot, "Anchor.toml") -var ZeroAddress = [32]byte{} - -func MakeRandom32ByteArray() [32]byte { - a := make([]byte, 32) - if _, err := rand.Read(a); err != nil { - panic(err) // should never panic but check in case - } - return [32]byte(a) -} - -func Uint64ToLE(chain uint64) []byte { - chainLE := make([]byte, 8) - binary.LittleEndian.PutUint64(chainLE, chain) - return chainLE -} - -func To28BytesLE(value uint64) [28]byte { - le := make([]byte, 28) - binary.LittleEndian.PutUint64(le, value) - return [28]byte(le) -} - -func To28BytesBE(value uint64) [28]byte { - be := make([]byte, 28) - binary.BigEndian.PutUint64(be[20:], value) - return [28]byte(be) -} - -func Map[T, V any](ts []T, fn func(T) V) []V { - result := make([]V, len(ts)) - for i, t := range ts { - result[i] = fn(t) - } - return result -} - -func Discriminator(namespace, name string) []byte { - h := sha256.New() - h.Write([]byte(fmt.Sprintf("%s:%s", namespace, name))) - return h.Sum(nil)[:8] -} - func DeployAllPrograms(t *testing.T, pathToAnchorConfig string, admin solana.PrivateKey) *rpc.Client { return rpc.New(SetupTestValidatorWithAnchorPrograms(t, pathToAnchorConfig, admin.PublicKey().String())) } @@ -124,81 +75,6 @@ func SetupTestValidatorWithAnchorPrograms(t *testing.T, pathToAnchorConfig strin return url } -func IsEvent(event string, data []byte) bool { - if len(data) < 8 { - return false - } - d := Discriminator("event", event) - return bytes.Equal(d, data[:8]) -} - -func ParseEvent(logs []string, event string, obj interface{}, shouldPrint ...bool) error { - for _, v := range logs { - if strings.Contains(v, "Program data:") { - encodedData := strings.TrimSpace(strings.TrimPrefix(v, "Program data:")) - data, err := base64.StdEncoding.DecodeString(encodedData) - if err != nil { - return err - } - if IsEvent(event, data) { - if err := bin.UnmarshalBorsh(obj, data); err != nil { - return err - } - - if len(shouldPrint) > 0 && shouldPrint[0] { - fmt.Printf("%s: %+v\n", event, obj) - } - return nil - } - } - } - return fmt.Errorf("%s: event not found", event) -} - -func ParseMultipleEvents[T any](logs []string, event string, shouldPrint bool) ([]T, error) { - var results []T - for _, v := range logs { - if strings.Contains(v, "Program data:") { - encodedData := strings.TrimSpace(strings.TrimPrefix(v, "Program data:")) - data, err := base64.StdEncoding.DecodeString(encodedData) - if err != nil { - return nil, err - } - if IsEvent(event, data) { - var obj T - if err := bin.UnmarshalBorsh(&obj, data); err != nil { - return nil, err - } - - if shouldPrint { - fmt.Printf("%s: %+v\n", event, obj) - } - - results = append(results, obj) - } - } - } - if len(results) == 0 { - return nil, fmt.Errorf("%s: event not found", event) - } - - return results, nil -} - -func GetBlockTime(ctx context.Context, client *rpc.Client, commitment rpc.CommitmentType) (*solana.UnixTimeSeconds, error) { - block, err := client.GetBlockHeight(ctx, commitment) - if err != nil { - return nil, fmt.Errorf("failed to get block height: %w", err) - } - - blockTime, err := client.GetBlockTime(ctx, block) - if err != nil { - return nil, fmt.Errorf("failed to get block time: %w", err) - } - - return blockTime, nil -} - func WaitForTheNextBlock(client *rpc.Client, timeout time.Duration, commitment rpc.CommitmentType) error { ctx, cancel := context.WithTimeout(context.Background(), timeout) defer cancel() diff --git a/chains/solana/contracts/tests/utils/localvalidator.go b/chains/solana/contracts/tests/testutils/localvalidator.go similarity index 99% rename from chains/solana/contracts/tests/utils/localvalidator.go rename to chains/solana/contracts/tests/testutils/localvalidator.go index 9acb9c5ac..164351bad 100644 --- a/chains/solana/contracts/tests/utils/localvalidator.go +++ b/chains/solana/contracts/tests/testutils/localvalidator.go @@ -1,4 +1,4 @@ -package utils +package testutils import ( "bytes" diff --git a/chains/solana/contracts/tests/utils/ocr.go b/chains/solana/contracts/tests/testutils/ocr.go similarity index 93% rename from chains/solana/contracts/tests/utils/ocr.go rename to chains/solana/contracts/tests/testutils/ocr.go index 59695a2bf..3b8432382 100644 --- a/chains/solana/contracts/tests/utils/ocr.go +++ b/chains/solana/contracts/tests/testutils/ocr.go @@ -1,4 +1,4 @@ -package utils +package testutils import ( cryptorand "crypto/rand" @@ -9,7 +9,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/eth" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/eth" ) type OcrPlugin uint8 diff --git a/chains/solana/contracts/tests/utils/project_path.go b/chains/solana/contracts/tests/testutils/project_path.go similarity index 94% rename from chains/solana/contracts/tests/utils/project_path.go rename to chains/solana/contracts/tests/testutils/project_path.go index b06d36d93..3d4eb8c8b 100644 --- a/chains/solana/contracts/tests/utils/project_path.go +++ b/chains/solana/contracts/tests/testutils/project_path.go @@ -1,4 +1,4 @@ -package utils +package testutils import ( "path/filepath" diff --git a/chains/solana/contracts/tests/testutils/wrapped.go b/chains/solana/contracts/tests/testutils/wrapped.go new file mode 100644 index 000000000..5fd54c6d0 --- /dev/null +++ b/chains/solana/contracts/tests/testutils/wrapped.go @@ -0,0 +1,83 @@ +package testutils + +import ( + "context" + "testing" + + "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/rpc" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/ccip" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" +) + +// this files includes wrapped methods to be used in testing without additional error checks +// this is used to keep a consistent interface to introduce less code churn in the tests + +func SendAndConfirm(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, signer solana.PrivateKey, commitment rpc.CommitmentType, opts ...common.TxModifier) *rpc.GetTransactionResult { + res, err := common.SendAndConfirm(ctx, rpcClient, instructions, signer, commitment, opts...) + require.NoError(t, err) + + return res +} + +func SendAndConfirmWithLookupTables(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, + signer solana.PrivateKey, commitment rpc.CommitmentType, lookupTables map[solana.PublicKey]solana.PublicKeySlice, opts ...common.TxModifier) *rpc.GetTransactionResult { + res, err := common.SendAndConfirmWithLookupTables(ctx, rpcClient, instructions, signer, commitment, lookupTables, opts...) + require.NoError(t, err) + + return res +} + +func SendAndFailWith(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, + signer solana.PrivateKey, commitment rpc.CommitmentType, expectedErrors []string, opts ...common.TxModifier) *rpc.GetTransactionResult { + res, err := common.SendAndFailWith(ctx, rpcClient, instructions, signer, commitment, expectedErrors, opts...) + require.NoError(t, err) + + return res +} + +func SendAndFailWithLookupTables(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, + signer solana.PrivateKey, commitment rpc.CommitmentType, lookupTables map[solana.PublicKey]solana.PublicKeySlice, expectedErrors []string, opts ...common.TxModifier) *rpc.GetTransactionResult { + res, err := common.SendAndFailWithLookupTables(ctx, rpcClient, instructions, signer, commitment, lookupTables, expectedErrors, opts...) + require.NoError(t, err) + + return res +} + +func SendAndFailWithRPCError(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, + signer solana.PrivateKey, commitment rpc.CommitmentType, expectedErrors []string) { + require.NoError(t, common.SendAndFailWithRPCError(ctx, rpcClient, instructions, signer, commitment, expectedErrors)) +} + +func SimulateTransaction(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, signer solana.PrivateKey) *rpc.SimulateTransactionResponse { + simRes, err := common.SimulateTransaction(ctx, rpcClient, instructions, signer) + require.NoError(t, err) + + return simRes +} + +func AssertClosedAccount(ctx context.Context, t *testing.T, solanaGoClient *rpc.Client, accountKey solana.PublicKey, commitment rpc.CommitmentType) { + isClosed := common.IsClosedAccount(ctx, solanaGoClient, accountKey, commitment) + require.True(t, isClosed) +} + +func CreateNextMessage(ctx context.Context, solanaGoClient *rpc.Client, t *testing.T) (ccip_router.Any2SolanaRampMessage, [32]byte) { + msg, hash, err := ccip.CreateNextMessage(ctx, solanaGoClient) + require.NoError(t, err) + return msg, hash +} + +func NextSequenceNumber(ctx context.Context, solanaGoClient *rpc.Client, sourceChainStatePDA solana.PublicKey, t *testing.T) uint64 { + num, err := ccip.NextSequenceNumber(ctx, solanaGoClient, sourceChainStatePDA) + require.NoError(t, err) + return num +} + +func MakeEvmToSolanaMessage(t *testing.T, ccipReceiver solana.PublicKey, evmChainSelector uint64, solanaChainSelector uint64, data []byte) (ccip_router.Any2SolanaRampMessage, [32]byte) { + msg, hash, err := ccip.MakeEvmToSolanaMessage(ccipReceiver, evmChainSelector, solanaChainSelector, data) + require.NoError(t, err) + return msg, hash +} diff --git a/chains/solana/contracts/tests/txsizing_test.go b/chains/solana/contracts/tests/txsizing_test.go index 164f74a07..34b89c0d9 100644 --- a/chains/solana/contracts/tests/txsizing_test.go +++ b/chains/solana/contracts/tests/txsizing_test.go @@ -10,8 +10,8 @@ import ( "golang.org/x/exp/maps" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" ) func mustRandomPubkey() solana.PublicKey { @@ -57,7 +57,7 @@ func TestTransactionSizing(t *testing.T) { "mint": mustRandomPubkey(), } - run := func(name string, ix solana.Instruction, tables map[solana.PublicKey]solana.PublicKeySlice, opts ...utils.TxModifier) string { + run := func(name string, ix solana.Instruction, tables map[solana.PublicKey]solana.PublicKeySlice, opts ...common.TxModifier) string { tx, err := solana.NewTransaction([]solana.Instruction{ix}, solana.Hash{1}, solana.TransactionAddressTables(tables)) require.NoError(t, err) @@ -319,9 +319,9 @@ func TestTransactionSizing(t *testing.T) { outputs = append(outputs, run(p.name+l, p.ix, tables), - run(p.name+l+" +cuLimit", p.ix, tables, utils.AddComputeUnitLimit(0)), - run(p.name+l+" +cuPrice", p.ix, tables, utils.AddComputeUnitPrice(0)), - run(p.name+l+" +cuPrice +cuLimit", p.ix, tables, utils.AddComputeUnitLimit(0), utils.AddComputeUnitPrice(0)), + run(p.name+l+" +cuLimit", p.ix, tables, common.AddComputeUnitLimit(0)), + run(p.name+l+" +cuPrice", p.ix, tables, common.AddComputeUnitPrice(0)), + run(p.name+l+" +cuPrice +cuLimit", p.ix, tables, common.AddComputeUnitLimit(0), common.AddComputeUnitPrice(0)), divider, ) } diff --git a/chains/solana/contracts/tests/utils/offchainConfig.go b/chains/solana/contracts/tests/utils/offchainConfig.go deleted file mode 100644 index 5853a013a..000000000 --- a/chains/solana/contracts/tests/utils/offchainConfig.go +++ /dev/null @@ -1,9 +0,0 @@ -package utils - -func ChunkSlice(items []byte, chunkSize int) (chunks [][]byte) { - for chunkSize < len(items) { - chunks = append(chunks, items[0:chunkSize]) - items = items[chunkSize:] - } - return append(chunks, items) -} diff --git a/chains/solana/contracts/tests/accesscontroller/access_controller.go b/chains/solana/utils/accesscontroller/access_controller.go similarity index 88% rename from chains/solana/contracts/tests/accesscontroller/access_controller.go rename to chains/solana/utils/accesscontroller/access_controller.go index 88d6bd7c3..90ca0b506 100644 --- a/chains/solana/contracts/tests/accesscontroller/access_controller.go +++ b/chains/solana/utils/accesscontroller/access_controller.go @@ -9,13 +9,13 @@ import ( "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/access_controller" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" ) func HasAccess(ctx context.Context, client *rpc.Client, accessController solana.PublicKey, address solana.PublicKey, commitment rpc.CommitmentType) (bool, error) { var ac access_controller.AccessController - err := utils.GetAccountDataBorshInto( + err := common.GetAccountDataBorshInto( ctx, client, accessController, diff --git a/chains/solana/contracts/tests/ccip/ccip_errors.go b/chains/solana/utils/ccip/ccip_errors.go similarity index 99% rename from chains/solana/contracts/tests/ccip/ccip_errors.go rename to chains/solana/utils/ccip/ccip_errors.go index 8d39e8b2e..fc521d0d5 100644 --- a/chains/solana/contracts/tests/ccip/ccip_errors.go +++ b/chains/solana/utils/ccip/ccip_errors.go @@ -1,4 +1,4 @@ -package contracts +package ccip import ( ag_binary "github.com/gagliardetto/binary" diff --git a/chains/solana/contracts/tests/ccip/ccip_events.go b/chains/solana/utils/ccip/ccip_events.go similarity index 98% rename from chains/solana/contracts/tests/ccip/ccip_events.go rename to chains/solana/utils/ccip/ccip_events.go index d586da53b..04d0edd38 100644 --- a/chains/solana/contracts/tests/ccip/ccip_events.go +++ b/chains/solana/utils/ccip/ccip_events.go @@ -1,4 +1,4 @@ -package contracts +package ccip import ( "github.com/gagliardetto/solana-go" diff --git a/chains/solana/contracts/tests/ccip/ccip_messages.go b/chains/solana/utils/ccip/ccip_messages.go similarity index 86% rename from chains/solana/contracts/tests/ccip/ccip_messages.go rename to chains/solana/utils/ccip/ccip_messages.go index e0de7d258..19eecb98f 100644 --- a/chains/solana/contracts/tests/ccip/ccip_messages.go +++ b/chains/solana/utils/ccip/ccip_messages.go @@ -1,4 +1,4 @@ -package contracts +package ccip import ( "bytes" @@ -6,16 +6,14 @@ import ( "crypto/sha256" "encoding/binary" "encoding/hex" - "testing" bin "github.com/gagliardetto/binary" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" ) func HashCommitReport(ctx [3][32]byte, report ccip_router.CommitInput) ([]byte, error) { @@ -50,7 +48,7 @@ func CreateReportContext(sequence uint64) [3][32]byte { return [3][32]byte{ config.ConfigDigest, [32]byte(binary.BigEndian.AppendUint64(config.Empty24Byte[:], sequence)), - utils.MakeRandom32ByteArray(), + common.MakeRandom32ByteArray(), } } @@ -67,20 +65,21 @@ func NextCommitReportContext() [3][32]byte { return CreateReportContext(reportSequence) } -func CreateNextMessage(ctx context.Context, solanaGoClient *rpc.Client, t *testing.T) (ccip_router.Any2SolanaRampMessage, [32]byte) { - nextSeq := NextSequenceNumber(ctx, solanaGoClient, config.EvmSourceChainStatePDA, t) +func CreateNextMessage(ctx context.Context, solanaGoClient *rpc.Client) (ccip_router.Any2SolanaRampMessage, [32]byte, error) { + nextSeq, err := NextSequenceNumber(ctx, solanaGoClient, config.EvmSourceChainStatePDA) + if err != nil { + return ccip_router.Any2SolanaRampMessage{}, [32]byte{}, err + } msg := CreateDefaultMessageWith(config.EvmChainSelector, nextSeq) hash, err := HashEvmToSolanaMessage(msg, config.OnRampAddress) - require.NoError(t, err) - return msg, [32]byte(hash) + return msg, [32]byte(hash), err } -func NextSequenceNumber(ctx context.Context, solanaGoClient *rpc.Client, sourceChainStatePDA solana.PublicKey, t *testing.T) uint64 { +func NextSequenceNumber(ctx context.Context, solanaGoClient *rpc.Client, sourceChainStatePDA solana.PublicKey) (uint64, error) { var chainStateAccount ccip_router.SourceChain - err := utils.GetAccountDataBorshInto(ctx, solanaGoClient, sourceChainStatePDA, config.DefaultCommitment, &chainStateAccount) - require.NoError(t, err) - return chainStateAccount.State.MinSeqNr + err := common.GetAccountDataBorshInto(ctx, solanaGoClient, sourceChainStatePDA, config.DefaultCommitment, &chainStateAccount) + return chainStateAccount.State.MinSeqNr, err } func CreateDefaultMessageWith(sourceChainSelector uint64, sequenceNumber uint64) ccip_router.Any2SolanaRampMessage { @@ -111,16 +110,15 @@ func CreateDefaultMessageWith(sourceChainSelector uint64, sequenceNumber uint64) return message } -func MakeEvmToSolanaMessage(t *testing.T, ccipReceiver solana.PublicKey, evmChainSelector uint64, solanaChainSelector uint64, data []byte) (ccip_router.Any2SolanaRampMessage, [32]byte) { +func MakeEvmToSolanaMessage(ccipReceiver solana.PublicKey, evmChainSelector uint64, solanaChainSelector uint64, data []byte) (ccip_router.Any2SolanaRampMessage, [32]byte, error) { msg := CreateDefaultMessageWith(evmChainSelector, 1) msg.Header.DestChainSelector = solanaChainSelector msg.Receiver = ccipReceiver msg.Data = data hash, err := HashEvmToSolanaMessage(msg, config.OnRampAddress) - require.NoError(t, err) msg.Header.MessageId = [32]byte(hash) - return msg, msg.Header.MessageId + return msg, msg.Header.MessageId, err } func HashEvmToSolanaMessage(msg ccip_router.Any2SolanaRampMessage, onRampAddress []byte) ([]byte, error) { diff --git a/chains/solana/contracts/tests/ccip/ccip_messages_test.go b/chains/solana/utils/ccip/ccip_messages_test.go similarity index 98% rename from chains/solana/contracts/tests/ccip/ccip_messages_test.go rename to chains/solana/utils/ccip/ccip_messages_test.go index 1f333053e..88b761bf5 100644 --- a/chains/solana/contracts/tests/ccip/ccip_messages_test.go +++ b/chains/solana/utils/ccip/ccip_messages_test.go @@ -1,4 +1,4 @@ -package contracts +package ccip import ( "encoding/hex" diff --git a/chains/solana/contracts/tests/ccip/ccip_transactions.go b/chains/solana/utils/ccip/ccip_transactions.go similarity index 81% rename from chains/solana/contracts/tests/ccip/ccip_transactions.go rename to chains/solana/utils/ccip/ccip_transactions.go index 600bd063d..6c7a1e45b 100644 --- a/chains/solana/contracts/tests/ccip/ccip_transactions.go +++ b/chains/solana/utils/ccip/ccip_transactions.go @@ -1,4 +1,4 @@ -package contracts +package ccip import ( "math/rand" @@ -8,9 +8,9 @@ import ( "github.com/gagliardetto/solana-go" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/eth" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/ccip_router" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/eth" ) func SignCommitReport(ctx [3][32]byte, report ccip_router.CommitInput, baseSigners []eth.Signer) (sigs [][65]byte, err error) { @@ -37,25 +37,25 @@ func SignCommitReport(ctx [3][32]byte, report ccip_router.CommitInput, baseSigne } func GetSourceChainStatePDA(chainSelector uint64) (solana.PublicKey, error) { - chainSelectorLE := utils.Uint64ToLE(chainSelector) + chainSelectorLE := common.Uint64ToLE(chainSelector) p, _, err := solana.FindProgramAddress([][]byte{[]byte("source_chain_state"), chainSelectorLE}, config.CcipRouterProgram) return p, err } func GetDestChainStatePDA(chainSelector uint64) (solana.PublicKey, error) { - chainSelectorLE := utils.Uint64ToLE(chainSelector) + chainSelectorLE := common.Uint64ToLE(chainSelector) p, _, err := solana.FindProgramAddress([][]byte{[]byte("dest_chain_state"), chainSelectorLE}, config.CcipRouterProgram) return p, err } func GetCommitReportPDA(chainSelector uint64, root [32]byte) (solana.PublicKey, error) { - chainSelectorLE := utils.Uint64ToLE(chainSelector) + chainSelectorLE := common.Uint64ToLE(chainSelector) p, _, err := solana.FindProgramAddress([][]byte{[]byte("commit_report"), chainSelectorLE, root[:]}, config.CcipRouterProgram) return p, err } -func getNoncePDA(chainSelector uint64, user solana.PublicKey) (solana.PublicKey, error) { - chainSelectorLE := utils.Uint64ToLE(chainSelector) +func GetNoncePDA(chainSelector uint64, user solana.PublicKey) (solana.PublicKey, error) { + chainSelectorLE := common.Uint64ToLE(chainSelector) p, _, err := solana.FindProgramAddress([][]byte{[]byte("nonce"), chainSelectorLE, user.Bytes()}, config.CcipRouterProgram) return p, err } diff --git a/chains/solana/utils/common/common.go b/chains/solana/utils/common/common.go new file mode 100644 index 000000000..c4a6d6d89 --- /dev/null +++ b/chains/solana/utils/common/common.go @@ -0,0 +1,68 @@ +package common + +import ( + "context" + "crypto/rand" + "crypto/sha256" + "encoding/binary" + "fmt" + + "github.com/gagliardetto/solana-go" + "github.com/gagliardetto/solana-go/rpc" +) + +var ZeroAddress = [32]byte{} + +func MakeRandom32ByteArray() [32]byte { + a := make([]byte, 32) + if _, err := rand.Read(a); err != nil { + panic(err) // should never panic but check in case + } + return [32]byte(a) +} + +func Uint64ToLE(chain uint64) []byte { + chainLE := make([]byte, 8) + binary.LittleEndian.PutUint64(chainLE, chain) + return chainLE +} + +func To28BytesLE(value uint64) [28]byte { + le := make([]byte, 28) + binary.LittleEndian.PutUint64(le, value) + return [28]byte(le) +} + +func To28BytesBE(value uint64) [28]byte { + be := make([]byte, 28) + binary.BigEndian.PutUint64(be[20:], value) + return [28]byte(be) +} + +func Map[T, V any](ts []T, fn func(T) V) []V { + result := make([]V, len(ts)) + for i, t := range ts { + result[i] = fn(t) + } + return result +} + +func Discriminator(namespace, name string) []byte { + h := sha256.New() + h.Write([]byte(fmt.Sprintf("%s:%s", namespace, name))) + return h.Sum(nil)[:8] +} + +func GetBlockTime(ctx context.Context, client *rpc.Client, commitment rpc.CommitmentType) (*solana.UnixTimeSeconds, error) { + block, err := client.GetBlockHeight(ctx, commitment) + if err != nil { + return nil, fmt.Errorf("failed to get block height: %w", err) + } + + blockTime, err := client.GetBlockTime(ctx, block) + if err != nil { + return nil, fmt.Errorf("failed to get block time: %w", err) + } + + return blockTime, nil +} diff --git a/chains/solana/utils/common/event.go b/chains/solana/utils/common/event.go new file mode 100644 index 000000000..b5cceb79b --- /dev/null +++ b/chains/solana/utils/common/event.go @@ -0,0 +1,71 @@ +package common + +import ( + "bytes" + "encoding/base64" + "fmt" + "strings" + + bin "github.com/gagliardetto/binary" +) + +func IsEvent(event string, data []byte) bool { + if len(data) < 8 { + return false + } + d := Discriminator("event", event) + return bytes.Equal(d, data[:8]) +} + +func ParseEvent(logs []string, event string, obj interface{}, shouldPrint ...bool) error { + for _, v := range logs { + if strings.Contains(v, "Program data:") { + encodedData := strings.TrimSpace(strings.TrimPrefix(v, "Program data:")) + data, err := base64.StdEncoding.DecodeString(encodedData) + if err != nil { + return err + } + if IsEvent(event, data) { + if err := bin.UnmarshalBorsh(obj, data); err != nil { + return err + } + + if len(shouldPrint) > 0 && shouldPrint[0] { + fmt.Printf("%s: %+v\n", event, obj) + } + return nil + } + } + } + return fmt.Errorf("%s: event not found", event) +} + +func ParseMultipleEvents[T any](logs []string, event string, shouldPrint bool) ([]T, error) { + var results []T + for _, v := range logs { + if strings.Contains(v, "Program data:") { + encodedData := strings.TrimSpace(strings.TrimPrefix(v, "Program data:")) + data, err := base64.StdEncoding.DecodeString(encodedData) + if err != nil { + return nil, err + } + if IsEvent(event, data) { + var obj T + if err := bin.UnmarshalBorsh(&obj, data); err != nil { + return nil, err + } + + if shouldPrint { + fmt.Printf("%s: %+v\n", event, obj) + } + + results = append(results, obj) + } + } + } + if len(results) == 0 { + return nil, fmt.Errorf("%s: event not found", event) + } + + return results, nil +} diff --git a/chains/solana/contracts/tests/utils/logparser.go b/chains/solana/utils/common/logparser.go similarity index 99% rename from chains/solana/contracts/tests/utils/logparser.go rename to chains/solana/utils/common/logparser.go index 247f8426a..8c4922c72 100644 --- a/chains/solana/contracts/tests/utils/logparser.go +++ b/chains/solana/utils/common/logparser.go @@ -1,4 +1,4 @@ -package utils +package common import ( "encoding/base64" diff --git a/chains/solana/contracts/tests/utils/logparser_test.go b/chains/solana/utils/common/logparser_test.go similarity index 99% rename from chains/solana/contracts/tests/utils/logparser_test.go rename to chains/solana/utils/common/logparser_test.go index 603acaf54..4cbfb26f3 100644 --- a/chains/solana/contracts/tests/utils/logparser_test.go +++ b/chains/solana/utils/common/logparser_test.go @@ -1,4 +1,4 @@ -package utils +package common import ( "testing" diff --git a/chains/solana/contracts/tests/utils/lookuptable.go b/chains/solana/utils/common/lookuptable.go similarity index 83% rename from chains/solana/contracts/tests/utils/lookuptable.go rename to chains/solana/utils/common/lookuptable.go index 5eae09f7c..5b57222bb 100644 --- a/chains/solana/contracts/tests/utils/lookuptable.go +++ b/chains/solana/utils/common/lookuptable.go @@ -1,9 +1,8 @@ -package utils +package common import ( "context" "encoding/binary" - "testing" "time" "github.com/gagliardetto/solana-go" @@ -77,9 +76,7 @@ func NewExtendLookupTableInstruction( ) } -// TODO remove dependency on all methods on *testing.T once SendAndConfirm no longer requires it - -func CreateLookupTable(ctx context.Context, t *testing.T, client *rpc.Client, admin solana.PrivateKey) (solana.PublicKey, error) { +func CreateLookupTable(ctx context.Context, client *rpc.Client, admin solana.PrivateKey) (solana.PublicKey, error) { slot, serr := client.GetSlot(ctx, rpc.CommitmentFinalized) if serr != nil { return solana.PublicKey{}, serr @@ -94,13 +91,12 @@ func CreateLookupTable(ctx context.Context, t *testing.T, client *rpc.Client, ad return solana.PublicKey{}, ierr } - SendAndConfirm(ctx, t, client, []solana.Instruction{instruction}, admin, rpc.CommitmentConfirmed) - - return table, nil + _, err := SendAndConfirm(ctx, client, []solana.Instruction{instruction}, admin, rpc.CommitmentConfirmed) + return table, err } -func ExtendLookupTable(ctx context.Context, t *testing.T, client *rpc.Client, table solana.PublicKey, admin solana.PrivateKey, entries []solana.PublicKey) { - SendAndConfirm(ctx, t, client, []solana.Instruction{ +func ExtendLookupTable(ctx context.Context, client *rpc.Client, table solana.PublicKey, admin solana.PrivateKey, entries []solana.PublicKey) error { + _, err := SendAndConfirm(ctx, client, []solana.Instruction{ NewExtendLookupTableInstruction( table, admin.PublicKey(), @@ -108,6 +104,7 @@ func ExtendLookupTable(ctx context.Context, t *testing.T, client *rpc.Client, ta entries, ), }, admin, rpc.CommitmentConfirmed) + return err } func AwaitSlotChange(ctx context.Context, client *rpc.Client) error { @@ -126,13 +123,16 @@ func AwaitSlotChange(ctx context.Context, client *rpc.Client) error { return nil } -func SetupLookupTable(ctx context.Context, t *testing.T, client *rpc.Client, admin solana.PrivateKey, entries []solana.PublicKey) (solana.PublicKey, error) { - table, err := CreateLookupTable(ctx, t, client, admin) +func SetupLookupTable(ctx context.Context, client *rpc.Client, admin solana.PrivateKey, entries []solana.PublicKey) (solana.PublicKey, error) { + table, err := CreateLookupTable(ctx, client, admin) if err != nil { return solana.PublicKey{}, err } - ExtendLookupTable(ctx, t, client, table, admin, entries) + err = ExtendLookupTable(ctx, client, table, admin, entries) + if err != nil { + return solana.PublicKey{}, err + } // Address lookup tables have to "warm up" for at least 1 slot before they can be used. // So, we wait for a new slot to be produced before returning the table, so it's available diff --git a/chains/solana/contracts/tests/utils/transactions.go b/chains/solana/utils/common/transactions.go similarity index 51% rename from chains/solana/contracts/tests/utils/transactions.go rename to chains/solana/utils/common/transactions.go index d76ca2b83..3d5a23e03 100644 --- a/chains/solana/contracts/tests/utils/transactions.go +++ b/chains/solana/utils/common/transactions.go @@ -1,92 +1,101 @@ -package utils +package common import ( "context" "encoding/base64" "fmt" "strings" - "testing" "time" bin "github.com/gagliardetto/binary" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" - "github.com/stretchr/testify/require" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/fees" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/fees" ) -func SendAndConfirm(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, - signer solana.PrivateKey, commitment rpc.CommitmentType, opts ...TxModifier) *rpc.GetTransactionResult { +func SendAndConfirm(ctx context.Context, rpcClient *rpc.Client, instructions []solana.Instruction, + signer solana.PrivateKey, commitment rpc.CommitmentType, opts ...TxModifier) (*rpc.GetTransactionResult, error) { emptyLookupTables := map[solana.PublicKey]solana.PublicKeySlice{} - txres := sendTransactionWithLookupTables(ctx, rpcClient, t, instructions, signer, commitment, false, emptyLookupTables, opts...) // do not skipPreflight when expected to pass, preflight can help debug - - require.NotNil(t, txres.Meta) - require.Nil(t, txres.Meta.Err, fmt.Sprintf("tx failed with: %+v", txres.Meta)) // tx should not err, print meta if it does (contains logs) - return txres + return SendAndConfirmWithLookupTables(ctx, rpcClient, instructions, signer, commitment, emptyLookupTables, opts...) } -func SendAndConfirmWithLookupTables(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, - signer solana.PrivateKey, commitment rpc.CommitmentType, lookupTables map[solana.PublicKey]solana.PublicKeySlice, opts ...TxModifier) *rpc.GetTransactionResult { - txres := sendTransactionWithLookupTables(ctx, rpcClient, t, instructions, signer, commitment, false, lookupTables, opts...) // do not skipPreflight when expected to pass, preflight can help debug +func SendAndConfirmWithLookupTables(ctx context.Context, rpcClient *rpc.Client, instructions []solana.Instruction, + signer solana.PrivateKey, commitment rpc.CommitmentType, lookupTables map[solana.PublicKey]solana.PublicKeySlice, opts ...TxModifier) (*rpc.GetTransactionResult, error) { + txres, err := sendTransactionWithLookupTables(ctx, rpcClient, instructions, signer, commitment, false, lookupTables, opts...) // do not skipPreflight when expected to pass, preflight can help debug + if err != nil { + return nil, err + } + + if txres.Meta == nil { + return nil, fmt.Errorf("txres.Meta == nil") + } - require.NotNil(t, txres.Meta) - require.Nil(t, txres.Meta.Err, fmt.Sprintf("tx failed with: %+v", txres.Meta)) // tx should not err, print meta if it does (contains logs) - return txres + if txres.Meta.Err != nil { + return nil, fmt.Errorf("tx failed with: %+v", txres.Meta) // tx should not err, print meta if it does (contains logs) + } + return txres, nil } -func SendAndFailWith(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, - signer solana.PrivateKey, commitment rpc.CommitmentType, expectedErrors []string, opts ...TxModifier) *rpc.GetTransactionResult { +func SendAndFailWith(ctx context.Context, rpcClient *rpc.Client, instructions []solana.Instruction, + signer solana.PrivateKey, commitment rpc.CommitmentType, expectedErrors []string, opts ...TxModifier) (*rpc.GetTransactionResult, error) { emptyLookupTables := map[solana.PublicKey]solana.PublicKeySlice{} - txres := sendTransactionWithLookupTables(ctx, rpcClient, t, instructions, signer, commitment, true, emptyLookupTables, opts...) // skipPreflight when expected to fail so revert captured onchain - - require.NotNil(t, txres.Meta) - require.NotNil(t, txres.Meta.Err, fmt.Sprintf("tx should have reverted with: %+v", expectedErrors)) - logs := strings.Join(txres.Meta.LogMessages, " ") - for _, expectedError := range expectedErrors { - require.Contains(t, logs, expectedError, fmt.Sprintf("The logs did not contain '%s'. The logs were: %s", expectedError, logs)) - } - return txres + return SendAndFailWithLookupTables(ctx, rpcClient, instructions, signer, commitment, emptyLookupTables, expectedErrors, opts...) } -func SendAndFailWithLookupTables(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, - signer solana.PrivateKey, commitment rpc.CommitmentType, lookupTables map[solana.PublicKey]solana.PublicKeySlice, expectedErrors []string, opts ...TxModifier) *rpc.GetTransactionResult { - txres := sendTransactionWithLookupTables(ctx, rpcClient, t, instructions, signer, commitment, true, lookupTables, opts...) // skipPreflight when expected to fail so revert captured onchain +func SendAndFailWithLookupTables(ctx context.Context, rpcClient *rpc.Client, instructions []solana.Instruction, + signer solana.PrivateKey, commitment rpc.CommitmentType, lookupTables map[solana.PublicKey]solana.PublicKeySlice, expectedErrors []string, opts ...TxModifier) (*rpc.GetTransactionResult, error) { + txres, err := sendTransactionWithLookupTables(ctx, rpcClient, instructions, signer, commitment, true, lookupTables, opts...) // skipPreflight when expected to fail so revert captured onchain + if err != nil { + return nil, err + } - require.NotNil(t, txres.Meta) - require.NotNil(t, txres.Meta.Err) + if txres.Meta == nil || txres.Meta.Err == nil { + return nil, fmt.Errorf("txres.Meta == nil || txres.Meta.Err == nil") + } logs := strings.Join(txres.Meta.LogMessages, " ") for _, expectedError := range expectedErrors { - require.Contains(t, logs, expectedError, fmt.Sprintf("The logs did not contain '%s'. The logs were: %s", expectedError, logs)) + if !strings.Contains(logs, expectedError) { + return nil, fmt.Errorf("The logs did not contain '%s'. The logs were: %s", expectedError, logs) + } } - return txres + return txres, nil } -func SendAndFailWithRPCError(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, - signer solana.PrivateKey, commitment rpc.CommitmentType, expectedErrors []string) { +func SendAndFailWithRPCError(ctx context.Context, rpcClient *rpc.Client, instructions []solana.Instruction, + signer solana.PrivateKey, commitment rpc.CommitmentType, expectedErrors []string) error { hashRes, err := rpcClient.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) - require.NoError(t, err) + if err != nil { + return err + } tx, err := solana.NewTransaction( instructions, hashRes.Value.Blockhash, solana.TransactionPayer(signer.PublicKey()), ) - require.NoError(t, err) + if err != nil { + return err + } - _, err = tx.Sign(func(_ solana.PublicKey) *solana.PrivateKey { + if _, err = tx.Sign(func(_ solana.PublicKey) *solana.PrivateKey { return &signer - }) - require.NoError(t, err) + }); err != nil { + return err + } _, err = rpcClient.SendTransactionWithOpts(ctx, tx, rpc.TransactionOpts{SkipPreflight: false, PreflightCommitment: rpc.CommitmentProcessed}) - require.NotNil(t, err) + if err == nil { + return fmt.Errorf("expected RPC error - none found") + } errStr := err.Error() - for _, expectedError := range expectedErrors { - require.Contains(t, errStr, expectedError) + if !strings.Contains(errStr, expectedError) { + return fmt.Errorf("The error did not contain '%s'. The error was: %s", expectedError, errStr) + } } + return nil } // TxModifier is a dynamic function used to flexibly add components to a transaction such as additional signers, and compute budget parameters @@ -115,10 +124,12 @@ func AddComputeUnitPrice(v fees.ComputeUnitPrice) TxModifier { } } -func sendTransactionWithLookupTables(ctx context.Context, rpcClient *rpc.Client, t *testing.T, instructions []solana.Instruction, - signerAndPayer solana.PrivateKey, commitment rpc.CommitmentType, skipPreflight bool, lookupTables map[solana.PublicKey]solana.PublicKeySlice, opts ...TxModifier) *rpc.GetTransactionResult { +func sendTransactionWithLookupTables(ctx context.Context, rpcClient *rpc.Client, instructions []solana.Instruction, + signerAndPayer solana.PrivateKey, commitment rpc.CommitmentType, skipPreflight bool, lookupTables map[solana.PublicKey]solana.PublicKeySlice, opts ...TxModifier) (*rpc.GetTransactionResult, error) { hashRes, err := rpcClient.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) - require.NoError(t, err) + if err != nil { + return nil, err + } tx, err := solana.NewTransaction( instructions, @@ -126,8 +137,9 @@ func sendTransactionWithLookupTables(ctx context.Context, rpcClient *rpc.Client, solana.TransactionAddressTables(lookupTables), solana.TransactionPayer(signerAndPayer.PublicKey()), ) - - require.NoError(t, err) + if err != nil { + return nil, err + } // build signers map signers := map[solana.PublicKey]solana.PrivateKey{} @@ -135,88 +147,91 @@ func sendTransactionWithLookupTables(ctx context.Context, rpcClient *rpc.Client, // set options before signing transaction for _, o := range opts { - require.NoError(t, o(tx, signers)) + if err = o(tx, signers); err != nil { + return nil, err + } } - _, err = tx.Sign(func(pub solana.PublicKey) *solana.PrivateKey { + if _, err = tx.Sign(func(pub solana.PublicKey) *solana.PrivateKey { priv, ok := signers[pub] - require.True(t, ok, fmt.Sprintf("Missing signer private key for %s", pub)) + if !ok { + fmt.Printf("ERROR: Missing signer private key for %s\n", pub) + } return &priv - }) - require.NoError(t, err) + }); err != nil { + return nil, err + } txsig, err := rpcClient.SendTransactionWithOpts(ctx, tx, rpc.TransactionOpts{SkipPreflight: skipPreflight, PreflightCommitment: rpc.CommitmentProcessed}) - require.NoError(t, err) + if err != nil { + return nil, err + } var txStatus rpc.ConfirmationStatusType count := 0 for txStatus != rpc.ConfirmationStatusConfirmed && txStatus != rpc.ConfirmationStatusFinalized { count++ statusRes, sigErr := rpcClient.GetSignatureStatuses(ctx, true, txsig) - require.NoError(t, sigErr) + if sigErr != nil { + return nil, sigErr + } if statusRes != nil && len(statusRes.Value) > 0 && statusRes.Value[0] != nil { txStatus = statusRes.Value[0].ConfirmationStatus } time.Sleep(50 * time.Millisecond) if count > 500 { - require.NoError(t, fmt.Errorf("unable to find transaction within timeout")) + return nil, fmt.Errorf("unable to find transaction within timeout") } } v := uint64(0) - txres, err := rpcClient.GetTransaction(ctx, txsig, &rpc.GetTransactionOpts{ + return rpcClient.GetTransaction(ctx, txsig, &rpc.GetTransactionOpts{ Commitment: commitment, MaxSupportedTransactionVersion: &v, }) - require.NoError(t, err) - return txres -} - -func SimulateTransaction(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, signer solana.PrivateKey) *rpc.SimulateTransactionResponse { - simRes, err := simulateTransaction(ctx, t, rpcClient, instructions, signer) - require.NoError(t, err) - - return simRes } -func simulateTransaction(ctx context.Context, t *testing.T, rpcClient *rpc.Client, instructions []solana.Instruction, +func SimulateTransaction(ctx context.Context, rpcClient *rpc.Client, instructions []solana.Instruction, signer solana.PrivateKey) (*rpc.SimulateTransactionResponse, error) { hashRes, err := rpcClient.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) - require.NoError(t, err) + if err != nil { + return nil, err + } tx, err := solana.NewTransaction( instructions, hashRes.Value.Blockhash, solana.TransactionPayer(signer.PublicKey()), ) - require.NoError(t, err) + if err != nil { + return nil, err + } - _, err = tx.Sign(func(_ solana.PublicKey) *solana.PrivateKey { + if _, err = tx.Sign(func(_ solana.PublicKey) *solana.PrivateKey { return &signer - }) - require.NoError(t, err) + }); err != nil { + return nil, err + } return rpcClient.SimulateTransaction(ctx, tx) } -func ExtractReturnValue(ctx context.Context, t *testing.T, logs []string, programID string) []byte { +func ExtractReturnValue(ctx context.Context, logs []string, programID string) ([]byte, error) { if logs == nil { - return []byte{} + return []byte{}, nil } for _, log := range logs { if strings.HasPrefix(log, "Program return: "+programID) { parts := strings.Split(log, " ") encoded := parts[len(parts)-1] - ret, err := base64.StdEncoding.DecodeString(encoded) - require.NoError(t, err) - return ret + return base64.StdEncoding.DecodeString(encoded) } } - return []byte{} + return []byte{}, nil } -func ExtractReturnedError(ctx context.Context, t *testing.T, logs []string, programID string) *string { +func ExtractReturnedError(ctx context.Context, logs []string, programID string) *string { if logs == nil { return nil } @@ -233,9 +248,9 @@ func ExtractReturnedError(ctx context.Context, t *testing.T, logs []string, prog return nil } -func ExtractTypedReturnValue[T any](ctx context.Context, t *testing.T, logs []string, programID string, decoderFn func([]byte) T) T { - bytes := ExtractReturnValue(ctx, t, logs, programID) - return decoderFn(bytes) +func ExtractTypedReturnValue[T any](ctx context.Context, logs []string, programID string, decoderFn func([]byte) T) (T, error) { + bytes, err := ExtractReturnValue(ctx, logs, programID) + return decoderFn(bytes), err } func GetAccountDataBorshInto(ctx context.Context, solanaGoClient *rpc.Client, account solana.PublicKey, commitment rpc.CommitmentType, data interface{}) error { @@ -253,9 +268,9 @@ func GetAccountDataBorshInto(ctx context.Context, solanaGoClient *rpc.Client, ac return bin.NewBorshDecoder(resp.Value.Data.GetBinary()).Decode(data) } -func AssertClosedAccount(ctx context.Context, t *testing.T, solanaGoClient *rpc.Client, accountKey solana.PublicKey, commitment rpc.CommitmentType) { +func IsClosedAccount(ctx context.Context, solanaGoClient *rpc.Client, accountKey solana.PublicKey, commitment rpc.CommitmentType) bool { _, err := solanaGoClient.GetAccountInfoWithOpts(ctx, accountKey, &rpc.GetAccountInfoOpts{ Commitment: commitment, }) - require.Error(t, err) + return err != nil } diff --git a/chains/solana/contracts/tests/utils/eth/signer.go b/chains/solana/utils/eth/signer.go similarity index 100% rename from chains/solana/contracts/tests/utils/eth/signer.go rename to chains/solana/utils/eth/signer.go diff --git a/chains/solana/contracts/tests/utils/fees/computebudget.go b/chains/solana/utils/fees/computebudget.go similarity index 100% rename from chains/solana/contracts/tests/utils/fees/computebudget.go rename to chains/solana/utils/fees/computebudget.go diff --git a/chains/solana/contracts/tests/utils/fees/computebudget_test.go b/chains/solana/utils/fees/computebudget_test.go similarity index 100% rename from chains/solana/contracts/tests/utils/fees/computebudget_test.go rename to chains/solana/utils/fees/computebudget_test.go diff --git a/chains/solana/contracts/tests/utils/mcms/common.go b/chains/solana/utils/mcms/common.go similarity index 97% rename from chains/solana/contracts/tests/utils/mcms/common.go rename to chains/solana/utils/mcms/common.go index 4c22c05d1..c80578b61 100644 --- a/chains/solana/contracts/tests/utils/mcms/common.go +++ b/chains/solana/utils/mcms/common.go @@ -10,7 +10,7 @@ import ( "golang.org/x/crypto/sha3" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/eth" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/eth" ) type McmConfigArgs struct { diff --git a/chains/solana/contracts/tests/utils/mcms/common_test.go b/chains/solana/utils/mcms/common_test.go similarity index 100% rename from chains/solana/contracts/tests/utils/mcms/common_test.go rename to chains/solana/utils/mcms/common_test.go diff --git a/chains/solana/contracts/tests/mcms/mcm.go b/chains/solana/utils/mcms/mcm.go similarity index 91% rename from chains/solana/contracts/tests/mcms/mcm.go rename to chains/solana/utils/mcms/mcm.go index d0b8eeec9..b0358bf0f 100644 --- a/chains/solana/contracts/tests/mcms/mcm.go +++ b/chains/solana/utils/mcms/mcm.go @@ -1,4 +1,4 @@ -package contracts +package mcms import ( "encoding/binary" @@ -8,9 +8,8 @@ import ( "github.com/gagliardetto/solana-go" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/eth" - mcmsUtils "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/mcms" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/mcm" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/eth" ) // mcm signer dataless pda @@ -81,8 +80,8 @@ func SeenSignedHashesAddress(msigName [32]byte, root [32]byte, validUntil uint32 return pda } -func NewMcmMultisig(name [32]byte) mcmsUtils.Multisig { - return mcmsUtils.Multisig{ +func NewMcmMultisig(name [32]byte) Multisig { + return Multisig{ PaddedName: name, SignerPDA: McmSignerAddress(name), ConfigPDA: McmConfigAddress(name), @@ -153,7 +152,7 @@ func AppendSignaturesIxs(signatures []mcm.Signature, msigName [32]byte, root [32 type McmRootInput struct { Multisig solana.PublicKey - Operations []mcmsUtils.McmOpNode + Operations []McmOpNode PreOpCount uint64 PostOpCount uint64 ValidUntil uint32 @@ -171,12 +170,12 @@ func CreateMcmRootData(input McmRootInput) (McmRootData, error) { numOps := len(input.Operations) // add 1 for the root metadata node - nodes := make([]mcmsUtils.MerkleNode, numOps+1) + nodes := make([]MerkleNode, numOps+1) for i := range input.Operations { nodes[i] = &input.Operations[i] } - rootMetadata := mcmsUtils.RootMetadataNode{ + rootMetadata := RootMetadataNode{ Multisig: input.Multisig, PreOpCount: input.PreOpCount, PostOpCount: input.PostOpCount, @@ -185,7 +184,7 @@ func CreateMcmRootData(input McmRootInput) (McmRootData, error) { nodes[numOps] = &rootMetadata // construct the tree - tree, err := mcmsUtils.NewOpMerkleTree(nodes) + tree, err := NewOpMerkleTree(nodes) if err != nil { return McmRootData{}, fmt.Errorf("failed to create tree: %w", err) } @@ -206,7 +205,7 @@ func CreateMcmRootData(input McmRootInput) (McmRootData, error) { return McmRootData{}, fmt.Errorf("failed to get metadata proof: %w", err) } - opTree, ok := tree.(*mcmsUtils.OpMerkleTree) + opTree, ok := tree.(*OpMerkleTree) if !ok { return McmRootData{}, fmt.Errorf("tree is not of type *OpMerkleTree") } @@ -232,10 +231,10 @@ func BulkSignOnMsgHash(signers []eth.Signer, ethMsgHash []byte) ([]mcm.Signature return signatures, nil } -func IxToMcmTestOpNode(multisig solana.PublicKey, msigSigner solana.PublicKey, ix solana.Instruction, nonce uint64) (mcmsUtils.McmOpNode, error) { +func IxToMcmTestOpNode(multisig solana.PublicKey, msigSigner solana.PublicKey, ix solana.Instruction, nonce uint64) (McmOpNode, error) { ixData, err := ix.Data() if err != nil { - return mcmsUtils.McmOpNode{}, err + return McmOpNode{}, err } // Create the accounts slice with the correct size accounts := make([]*solana.AccountMeta, 0, len(ix.Accounts())) @@ -253,7 +252,7 @@ func IxToMcmTestOpNode(multisig solana.PublicKey, msigSigner solana.PublicKey, i }) } - node := mcmsUtils.McmOpNode{ + node := McmOpNode{ Multisig: multisig, Nonce: nonce, To: ix.ProgramID(), diff --git a/chains/solana/contracts/tests/mcms/mcm_errors.go b/chains/solana/utils/mcms/mcm_errors.go similarity index 95% rename from chains/solana/contracts/tests/mcms/mcm_errors.go rename to chains/solana/utils/mcms/mcm_errors.go index f33235530..48acaf038 100644 --- a/chains/solana/contracts/tests/mcms/mcm_errors.go +++ b/chains/solana/utils/mcms/mcm_errors.go @@ -1,4 +1,4 @@ -package contracts +package mcms import ( agbinary "github.com/gagliardetto/binary" diff --git a/chains/solana/contracts/tests/mcms/mcm_events.go b/chains/solana/utils/mcms/mcm_events.go similarity index 98% rename from chains/solana/contracts/tests/mcms/mcm_events.go rename to chains/solana/utils/mcms/mcm_events.go index bedb839d5..db6310e99 100644 --- a/chains/solana/contracts/tests/mcms/mcm_events.go +++ b/chains/solana/utils/mcms/mcm_events.go @@ -1,4 +1,4 @@ -package contracts +package mcms import ( "github.com/gagliardetto/solana-go" diff --git a/chains/solana/contracts/tests/utils/mcms/merkle.go b/chains/solana/utils/mcms/merkle.go similarity index 99% rename from chains/solana/contracts/tests/utils/mcms/merkle.go rename to chains/solana/utils/mcms/merkle.go index f479a5929..0338ac462 100644 --- a/chains/solana/contracts/tests/utils/mcms/merkle.go +++ b/chains/solana/utils/mcms/merkle.go @@ -10,7 +10,7 @@ import ( "github.com/gagliardetto/solana-go" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/eth" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/eth" ) // Note: mcms tree reference can be found in https://github.com/smartcontractkit/mcms/blob/main/internal/core/merkle/tree.go diff --git a/chains/solana/contracts/tests/utils/mcms/merkle_test.go b/chains/solana/utils/mcms/merkle_test.go similarity index 100% rename from chains/solana/contracts/tests/utils/mcms/merkle_test.go rename to chains/solana/utils/mcms/merkle_test.go diff --git a/chains/solana/contracts/tests/utils/mcms/mcm.go b/chains/solana/utils/mcms/multisig.go similarity index 97% rename from chains/solana/contracts/tests/utils/mcms/mcm.go rename to chains/solana/utils/mcms/multisig.go index 7d8fdbd42..3b3cbb113 100644 --- a/chains/solana/contracts/tests/utils/mcms/mcm.go +++ b/chains/solana/utils/mcms/multisig.go @@ -6,7 +6,7 @@ import ( "github.com/gagliardetto/solana-go" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/eth" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/eth" ) type Multisig struct { diff --git a/chains/solana/contracts/tests/utils/mcms/timelock.go b/chains/solana/utils/timelock/accounts.go similarity index 61% rename from chains/solana/contracts/tests/utils/mcms/timelock.go rename to chains/solana/utils/timelock/accounts.go index d0917db94..eacdf0209 100644 --- a/chains/solana/contracts/tests/utils/mcms/timelock.go +++ b/chains/solana/utils/timelock/accounts.go @@ -1,12 +1,10 @@ -package mcms +package timelock import ( crypto_rand "crypto/rand" "math/big" - "testing" "github.com/gagliardetto/solana-go" - "github.com/stretchr/testify/require" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/timelock" ) @@ -34,27 +32,27 @@ func (r RoleAccounts) RandomPick() solana.PrivateKey { return r.Accounts[n.Int64()] } -func TestRoleAccounts(t *testing.T, numAccounts int) ([]RoleAccounts, RoleMap) { +func TestRoleAccounts(numAccounts int) ([]RoleAccounts, RoleMap) { roles := []RoleAccounts{ { Role: timelock.Proposer_Role, - Accounts: createRoleAccounts(t, numAccounts), - AccessController: getPrivateKey(t), + Accounts: mustCreateRoleAccounts(numAccounts), + AccessController: mustPrivateKey(), }, { Role: timelock.Executor_Role, - Accounts: createRoleAccounts(t, numAccounts), - AccessController: getPrivateKey(t), + Accounts: mustCreateRoleAccounts(numAccounts), + AccessController: mustPrivateKey(), }, { Role: timelock.Canceller_Role, - Accounts: createRoleAccounts(t, numAccounts), - AccessController: getPrivateKey(t), + Accounts: mustCreateRoleAccounts(numAccounts), + AccessController: mustPrivateKey(), }, { Role: timelock.Bypasser_Role, - Accounts: createRoleAccounts(t, numAccounts), - AccessController: getPrivateKey(t), + Accounts: mustCreateRoleAccounts(numAccounts), + AccessController: mustPrivateKey(), }, } @@ -65,21 +63,21 @@ func TestRoleAccounts(t *testing.T, numAccounts int) ([]RoleAccounts, RoleMap) { return roles, roleMap } -func createRoleAccounts(t *testing.T, num int) []solana.PrivateKey { +func mustCreateRoleAccounts(num int) []solana.PrivateKey { if num < 1 || num > 64 { panic("num should be between 1 and 64") } accounts := make([]solana.PrivateKey, num) for i := 0; i < num; i++ { - account, err := solana.NewRandomPrivateKey() - require.NoError(t, err) - accounts[i] = account + accounts[i] = mustPrivateKey() } return accounts } -func getPrivateKey(t *testing.T) solana.PrivateKey { +func mustPrivateKey() solana.PrivateKey { key, err := solana.NewRandomPrivateKey() - require.NoError(t, err) + if err != nil { + panic(err) + } return key } diff --git a/chains/solana/contracts/tests/mcms/timelock.go b/chains/solana/utils/timelock/timelock.go similarity index 84% rename from chains/solana/contracts/tests/mcms/timelock.go rename to chains/solana/utils/timelock/timelock.go index 764e722b4..aae1c1626 100644 --- a/chains/solana/contracts/tests/mcms/timelock.go +++ b/chains/solana/utils/timelock/timelock.go @@ -1,4 +1,4 @@ -package contracts +package timelock import ( "context" @@ -12,11 +12,11 @@ import ( "github.com/gagliardetto/solana-go/rpc" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/eth" - mcmsUtils "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/mcms" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/access_controller" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/timelock" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/eth" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/mcms" ) // instruction builder for initializing access controllers @@ -57,9 +57,9 @@ func InitAccessControllersIxs(ctx context.Context, roleAcAccount solana.PublicKe } // instructions builder for adding access to a role -func TimelockBatchAddAccessIxs(ctx context.Context, roleAcAccount solana.PublicKey, role timelock.Role, addresses []solana.PublicKey, authority solana.PrivateKey, chunkSize int, client *rpc.Client) ([]solana.Instruction, error) { +func BatchAddAccessIxs(ctx context.Context, roleAcAccount solana.PublicKey, role timelock.Role, addresses []solana.PublicKey, authority solana.PrivateKey, chunkSize int, client *rpc.Client) ([]solana.Instruction, error) { var ac access_controller.AccessController - err := utils.GetAccountDataBorshInto(ctx, client, roleAcAccount, config.DefaultCommitment, &ac) + err := common.GetAccountDataBorshInto(ctx, client, roleAcAccount, config.DefaultCommitment, &ac) if err != nil { return nil, fmt.Errorf("access controller for role %s is not initialized: %w", role, err) } @@ -90,7 +90,7 @@ func TimelockBatchAddAccessIxs(ctx context.Context, roleAcAccount solana.PublicK } // instructions builder for preloading instructions to timelock operation -func TimelockPreloadOperationIxs(ctx context.Context, op TimelockOperation, authority solana.PublicKey, client *rpc.Client) ([]solana.Instruction, error) { +func PreloadOperationIxs(ctx context.Context, op Operation, authority solana.PublicKey, client *rpc.Client) ([]solana.Instruction, error) { ixs := []solana.Instruction{} initOpIx, ioErr := timelock.NewInitializeOperationInstruction( op.OperationID(), @@ -138,11 +138,11 @@ func TimelockPreloadOperationIxs(ctx context.Context, op TimelockOperation, auth // mcm + timelock test helpers type RoleMultisigs struct { - Multisigs []mcmsUtils.Multisig + Multisigs []mcms.Multisig AccessController solana.PrivateKey } -func (r RoleMultisigs) GetAnyMultisig() mcmsUtils.Multisig { +func (r RoleMultisigs) GetAnyMultisig() mcms.Multisig { if len(r.Multisigs) == 0 { panic("no multisigs to pick from") } @@ -155,10 +155,10 @@ func (r RoleMultisigs) GetAnyMultisig() mcmsUtils.Multisig { } func CreateRoleMultisigs(role timelock.Role, numMsigs int) RoleMultisigs { - msigs := make([]mcmsUtils.Multisig, numMsigs) + msigs := make([]mcms.Multisig, numMsigs) for i := 0; i < numMsigs; i++ { - name, _ := mcmsUtils.PadString32(fmt.Sprintf("%s_%d", role.String(), i)) - msig := NewMcmMultisig(name) + name, _ := mcms.PadString32(fmt.Sprintf("%s_%d", role.String(), i)) + msig := mcms.NewMcmMultisig(name) // Create and set the config for each msig // ┌──────┐ // │2-of-2│ root @@ -178,7 +178,7 @@ func CreateRoleMultisigs(role timelock.Role, numMsigs int) RoleMultisigs { groupQuorums := []uint8{2, 2, 1} // root: 2-of-2, group1: 2-of-3, group2: 1-of-2 groupParents := []uint8{0, 0, 0} // both groups under root - mcmConfig, _ := mcmsUtils.NewValidMcmConfig( + mcmConfig, _ := mcms.NewValidMcmConfig( name, signerPrivateKeys, signerGroups, @@ -207,7 +207,7 @@ func WaitForOperationToBeReady(ctx context.Context, client *rpc.Client, opPDA so const timeBuffer = 2 * time.Second var opAccount timelock.Operation - err := utils.GetAccountDataBorshInto(ctx, client, opPDA, commitment, &opAccount) + err := common.GetAccountDataBorshInto(ctx, client, opPDA, commitment, &opAccount) if err != nil { return fmt.Errorf("failed to get account info: %w", err) } @@ -223,7 +223,7 @@ func WaitForOperationToBeReady(ctx context.Context, client *rpc.Client, opPDA so scheduledTimeWithBuffer := scheduledTime.Add(timeBuffer) for attempts := 0; attempts < maxAttempts; attempts++ { - currentTime, err := utils.GetBlockTime(ctx, client, commitment) + currentTime, err := common.GetBlockTime(ctx, client, commitment) if err != nil { return fmt.Errorf("failed to get current block time: %w", err) } @@ -246,7 +246,7 @@ func GetBlockedFunctionSelectors( commitment rpc.CommitmentType, ) ([][]byte, error) { var config timelock.Config - err := utils.GetAccountDataBorshInto(ctx, client, configPubKey, commitment, &config) + err := common.GetAccountDataBorshInto(ctx, client, configPubKey, commitment, &config) if err != nil { return nil, fmt.Errorf("failed to fetch config account data: %w", err) } diff --git a/chains/solana/contracts/tests/mcms/timelock_errors.go b/chains/solana/utils/timelock/timelock_errors.go similarity index 56% rename from chains/solana/contracts/tests/mcms/timelock_errors.go rename to chains/solana/utils/timelock/timelock_errors.go index f7e581424..f8002754a 100644 --- a/chains/solana/contracts/tests/mcms/timelock_errors.go +++ b/chains/solana/utils/timelock/timelock_errors.go @@ -1,19 +1,19 @@ -package contracts +package timelock import ( agbinary "github.com/gagliardetto/binary" ) // This Errors should be automatically generated by Anchor-Go but they only support one error per program -type TimelockError agbinary.BorshEnum +type Error agbinary.BorshEnum const ( - UnauthorizedTimelockError TimelockError = iota + UnauthorizedError Error = iota ) -func (value TimelockError) String() string { +func (value Error) String() string { switch value { - case UnauthorizedTimelockError: + case UnauthorizedError: return "Unauthorized" default: return "" diff --git a/chains/solana/contracts/tests/mcms/timelock_events.go b/chains/solana/utils/timelock/timelock_events.go similarity index 98% rename from chains/solana/contracts/tests/mcms/timelock_events.go rename to chains/solana/utils/timelock/timelock_events.go index 52ae930e1..e83ffb7e0 100644 --- a/chains/solana/contracts/tests/mcms/timelock_events.go +++ b/chains/solana/utils/timelock/timelock_events.go @@ -1,4 +1,4 @@ -package contracts +package timelock import ( "github.com/gagliardetto/solana-go" diff --git a/chains/solana/contracts/tests/mcms/timelock_operations.go b/chains/solana/utils/timelock/timelock_operations.go similarity index 77% rename from chains/solana/contracts/tests/mcms/timelock_operations.go rename to chains/solana/utils/timelock/timelock_operations.go index ed34f4c08..dae9866c4 100644 --- a/chains/solana/contracts/tests/mcms/timelock_operations.go +++ b/chains/solana/utils/timelock/timelock_operations.go @@ -1,4 +1,4 @@ -package contracts +package timelock import ( "bytes" @@ -8,25 +8,25 @@ import ( "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/timelock" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - mcmsUtils "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils/mcms" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/mcms" ) // represents a single instruction with its required accounts -type TimelockInstruction struct { +type Instruction struct { Ix solana.Instruction // instruction to be scheduled / executed Accounts []solana.AccountMeta // required accounts for the instruction(should be provided in execute stage) } // represents a batch of instructions that having atomicy to be scheduled and executed via timelock -type TimelockOperation struct { - Predecessor [32]byte // hashed id of the previous operation - Salt [32]byte // random salt for the operation - Delay uint64 // delay in seconds - instructions []TimelockInstruction // instruction data slice, use Add method to add instructions and accounts +type Operation struct { + Predecessor [32]byte // hashed id of the previous operation + Salt [32]byte // random salt for the operation + Delay uint64 // delay in seconds + instructions []Instruction // instruction data slice, use Add method to add instructions and accounts } // add instruction and required accounts to operation -func (op *TimelockOperation) AddInstruction(ix solana.Instruction, additionalPrograms []solana.PublicKey) { +func (op *Operation) AddInstruction(ix solana.Instruction, additionalPrograms []solana.PublicKey) { accounts := make([]solana.AccountMeta, len(ix.Accounts())) // anchor ix builder doesn't include program for _, program := range additionalPrograms { @@ -40,14 +40,14 @@ func (op *TimelockOperation) AddInstruction(ix solana.Instruction, additionalPro // i, acc.PublicKey, acc.IsSigner, acc.IsWritable) } - op.instructions = append(op.instructions, TimelockInstruction{ + op.instructions = append(op.instructions, Instruction{ Ix: ix, Accounts: accounts, }) } -func (op *TimelockOperation) IxsCountU32() uint32 { - ixsCount, err := mcmsUtils.SafeToUint32(len(op.instructions)) +func (op *Operation) IxsCountU32() uint32 { + ixsCount, err := mcms.SafeToUint32(len(op.instructions)) if err != nil { panic(err) } @@ -55,7 +55,7 @@ func (op *TimelockOperation) IxsCountU32() uint32 { } // convert operation to timelock instruction data slice -func (op *TimelockOperation) ToInstructionData() []timelock.InstructionData { +func (op *Operation) ToInstructionData() []timelock.InstructionData { ixs := make([]timelock.InstructionData, len(op.instructions)) for i, ix := range op.instructions { ixData, err := convertToInstructionData(ix.Ix) @@ -69,7 +69,7 @@ func (op *TimelockOperation) ToInstructionData() []timelock.InstructionData { // get required accounts for the operation // it merges the required accounts of all instructions and removes duplicates -func (op *TimelockOperation) RemainingAccounts() []*solana.AccountMeta { +func (op *Operation) RemainingAccounts() []*solana.AccountMeta { accountMap := make(map[string]*solana.AccountMeta) for _, instr := range op.instructions { for _, acc := range instr.Accounts { @@ -93,11 +93,11 @@ func (op *TimelockOperation) RemainingAccounts() []*solana.AccountMeta { } // hash the operation and return operation id -func (op *TimelockOperation) OperationID() [32]byte { +func (op *Operation) OperationID() [32]byte { return hashOperation(op.ToInstructionData(), op.Predecessor, op.Salt) } -func (op *TimelockOperation) OperationPDA() solana.PublicKey { +func (op *Operation) OperationPDA() solana.PublicKey { id := op.OperationID() return config.TimelockOperationPDA(id) } @@ -150,7 +150,7 @@ func hashOperation(instructions []timelock.InstructionData, predecessor [32]byte encodedData.Write(predecessor[:]) encodedData.Write(salt[:]) - result := mcmsUtils.Keccak256(encodedData.Bytes()) + result := mcms.Keccak256(encodedData.Bytes()) var hash [32]byte copy(hash[:], result) diff --git a/chains/solana/contracts/tests/utils/token.go b/chains/solana/utils/tokens/token.go similarity index 99% rename from chains/solana/contracts/tests/utils/token.go rename to chains/solana/utils/tokens/token.go index c3288c63e..6886c306f 100644 --- a/chains/solana/contracts/tests/utils/token.go +++ b/chains/solana/utils/tokens/token.go @@ -1,4 +1,4 @@ -package utils +package tokens import ( "context" diff --git a/chains/solana/contracts/tests/ccip/tokenpool.go b/chains/solana/utils/tokens/tokenpool.go similarity index 91% rename from chains/solana/contracts/tests/ccip/tokenpool.go rename to chains/solana/utils/tokens/tokenpool.go index 7a269b703..3930ffe6a 100644 --- a/chains/solana/contracts/tests/ccip/tokenpool.go +++ b/chains/solana/utils/tokens/tokenpool.go @@ -1,17 +1,16 @@ -package contracts +package tokens import ( "context" "encoding/binary" "strings" - "testing" "github.com/gagliardetto/solana-go" "github.com/gagliardetto/solana-go/rpc" "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/config" - "github.com/smartcontractkit/chainlink-ccip/chains/solana/contracts/tests/utils" "github.com/smartcontractkit/chainlink-ccip/chains/solana/gobindings/token_pool" + "github.com/smartcontractkit/chainlink-ccip/chains/solana/utils/common" ) type TokenPool struct { @@ -87,14 +86,16 @@ func NewTokenPool(program solana.PublicKey) (TokenPool, error) { return p, nil } -func (tp *TokenPool) SetupLookupTable(ctx context.Context, t *testing.T, client *rpc.Client, admin solana.PrivateKey) error { - table, err := utils.CreateLookupTable(ctx, t, client, admin) +func (tp *TokenPool) SetupLookupTable(ctx context.Context, client *rpc.Client, admin solana.PrivateKey) error { + table, err := common.CreateLookupTable(ctx, client, admin) if err != nil { return err } tp.PoolLookupTable = table // the LUT entries will include this, so set it before adding the addresses to the LUT - utils.ExtendLookupTable(ctx, t, client, table, admin, tp.ToTokenPoolEntries()) - return utils.AwaitSlotChange(ctx, client) + if err = common.ExtendLookupTable(ctx, client, table, admin, tp.ToTokenPoolEntries()); err != nil { + return err + } + return common.AwaitSlotChange(ctx, client) } func TokenPoolConfigAddress(token solana.PublicKey) (solana.PublicKey, error) { @@ -161,7 +162,7 @@ func ParseTokenLookupTable(ctx context.Context, client *rpc.Client, token TokenP tokenBillingConfig := token.Billing[config.EvmChainSelector] poolChainConfig := token.Chain[config.EvmChainSelector] - lookupTableEntries, err := utils.GetAddressLookupTable(ctx, client, token.PoolLookupTable) + lookupTableEntries, err := common.GetAddressLookupTable(ctx, client, token.PoolLookupTable) if err != nil { return nil, nil, err } From d015a111f66955b3438e9cf6315b4287756815b3 Mon Sep 17 00:00:00 2001 From: Balamurali Gopalswami <167726375+b-gopalswami@users.noreply.github.com> Date: Fri, 20 Dec 2024 14:05:51 -0500 Subject: [PATCH 08/10] CCIP-4710: Adding nil and length check on input value to Ed25519 verify call (#400) * CCIP-4710: Adding nil and length check on input value to Ed25519 verify call * fix test --------- Co-authored-by: Will Winder --- commit/merkleroot/rmn/controller_test.go | 16 ++++++++++------ commit/merkleroot/rmn/crypto.go | 6 ++++++ 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/commit/merkleroot/rmn/controller_test.go b/commit/merkleroot/rmn/controller_test.go index c9fd9c5a1..e1f3a25f9 100644 --- a/commit/merkleroot/rmn/controller_test.go +++ b/commit/merkleroot/rmn/controller_test.go @@ -415,15 +415,11 @@ func TestClient_ComputeReportSignatures(t *testing.T) { const numNodes = 8 rmnNodes := make([]rmntypes.HomeNodeInfo, numNodes) for i := 0; i < numNodes; i++ { - // deterministically create a public key by seeding with a 32char string. - publicKey, _, err := ed25519.GenerateKey( - strings.NewReader(strconv.Itoa(i) + strings.Repeat("x", 31))) - require.NoError(t, err) rmnNodes[i] = rmntypes.HomeNodeInfo{ ID: rmntypes.NodeID(i + 1), PeerID: [32]byte{1, 2, 3}, SupportedSourceChains: mapset.NewSet(chainS1, chainS2), - OffchainPublicKey: &publicKey, + OffchainPublicKey: getDeterministicPubKey(t), } } @@ -648,7 +644,7 @@ func Test_controller_validateSignedObservationResponse(t *testing.T) { { ID: 20, SupportedSourceChains: mapset.NewSet[cciptypes.ChainSelector](cciptypes.ChainSelector(2)), - OffchainPublicKey: &ed25519.PublicKey{}, + OffchainPublicKey: getDeterministicPubKey(t), }, }, }, @@ -743,6 +739,14 @@ func Test_newRequestID(t *testing.T) { } } +func getDeterministicPubKey(t *testing.T) *ed25519.PublicKey { + // deterministically create a public key by seeding with a 32char string. + publicKey, _, err := ed25519.GenerateKey( + strings.NewReader(strconv.Itoa(1) + strings.Repeat("x", 31))) + require.NoError(t, err) + return &publicKey +} + func (ts *testSetup) waitForObservationRequestsToBeSent( rmnClient *mockPeerClient, homeF int, diff --git a/commit/merkleroot/rmn/crypto.go b/commit/merkleroot/rmn/crypto.go index 00d7be2c7..e43321d6d 100644 --- a/commit/merkleroot/rmn/crypto.go +++ b/commit/merkleroot/rmn/crypto.go @@ -47,6 +47,12 @@ func verifyObservationSignature( msg := append([]byte(signedObservationPrefix), observationBytesSha256[:]...) msgSha256 := sha256.Sum256(msg) + if rmnNode.OffchainPublicKey == nil { + return fmt.Errorf("node %d has no offchain public key", rmnNode.ID) + } + if len(*rmnNode.OffchainPublicKey) != ed25519.PublicKeySize { + return fmt.Errorf("node %d has an invalid offchain public key", rmnNode.ID) + } isValid := verifier.Verify(*rmnNode.OffchainPublicKey, msgSha256[:], signedObs.Signature) if !isValid { return fmt.Errorf("observation signature does not match node %d public key", rmnNode.ID) From 4e2de677b059fb6f1824b108930e75a0aa9ab65c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tob=C3=ADas=20Lichtig?= Date: Fri, 20 Dec 2024 16:22:50 -0300 Subject: [PATCH 09/10] When paying with SOL, check that PDAs with data will not get garbage collected (#390) --- .../solana/contracts/programs/ccip-router/src/fee_quoter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chains/solana/contracts/programs/ccip-router/src/fee_quoter.rs b/chains/solana/contracts/programs/ccip-router/src/fee_quoter.rs index 33a048caf..14e2104b6 100644 --- a/chains/solana/contracts/programs/ccip-router/src/fee_quoter.rs +++ b/chains/solana/contracts/programs/ccip-router/src/fee_quoter.rs @@ -91,8 +91,8 @@ pub fn wrap_native_sol<'info>( signer_bump: u8, ) -> Result<()> { require!( - // guarantee that if caller is a PDA it won't get garbage-collected - *from.owner == System::id() || from.get_lamports() > amount, + // guarantee that if caller is a PDA with data it won't get garbage-collected + from.data_is_empty() || from.get_lamports() > amount, CcipRouterError::InsufficientLamports ); From 609f7b0c9734b3cfc1d1a1aea0fd1ddf4c90bd0c Mon Sep 17 00:00:00 2001 From: Balamurali Gopalswami <167726375+b-gopalswami@users.noreply.github.com> Date: Fri, 20 Dec 2024 16:22:37 -0500 Subject: [PATCH 10/10] CCIP-4711: Fix incorrect terminology use of message hash (#401) --- execute/tokendata/usdc/attestation.go | 24 ++++++++++++------------ execute/tokendata/usdc/usdc.go | 12 ++++++------ pkg/reader/usdc_reader.go | 10 +++++----- pkg/reader/usdc_reader_test.go | 8 ++++---- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/execute/tokendata/usdc/attestation.go b/execute/tokendata/usdc/attestation.go index 3450c0a0b..21cce663e 100644 --- a/execute/tokendata/usdc/attestation.go +++ b/execute/tokendata/usdc/attestation.go @@ -88,22 +88,22 @@ func NewSequentialAttestationClient( func (s *sequentialAttestationClient) Attestations( ctx context.Context, - msgs map[cciptypes.ChainSelector]map[reader.MessageTokenID]cciptypes.Bytes, + messagesByChain map[cciptypes.ChainSelector]map[reader.MessageTokenID]cciptypes.Bytes, ) (map[cciptypes.ChainSelector]map[reader.MessageTokenID]AttestationStatus, error) { outcome := make(map[cciptypes.ChainSelector]map[reader.MessageTokenID]AttestationStatus) - for chainSelector, hashes := range msgs { + for chainSelector, messagesByTokenID := range messagesByChain { outcome[chainSelector] = make(map[reader.MessageTokenID]AttestationStatus) - for tokenID, messageHash := range hashes { + for tokenID, message := range messagesByTokenID { s.lggr.Debugw( "Fetching attestation from the API", "chainSelector", chainSelector, - "messageHash", messageHash, + "message", message, "messageTokenID", tokenID, ) // TODO sequential processing - outcome[chainSelector][tokenID] = s.fetchSingleMessage(ctx, messageHash) + outcome[chainSelector][tokenID] = s.fetchSingleMessage(ctx, message) } } return outcome, nil @@ -111,14 +111,14 @@ func (s *sequentialAttestationClient) Attestations( func (s *sequentialAttestationClient) fetchSingleMessage( ctx context.Context, - messageHash cciptypes.Bytes, + message cciptypes.Bytes, ) AttestationStatus { - response, _, err := s.client.Get(ctx, s.hasher.Hash(messageHash)) + response, _, err := s.client.Get(ctx, s.hasher.Hash(message)) if err != nil { return ErrorAttestationStatus(err) } - return SuccessAttestationStatus(messageHash, response) + return SuccessAttestationStatus(message, response) } type FakeAttestationClient struct { @@ -127,15 +127,15 @@ type FakeAttestationClient struct { func (f FakeAttestationClient) Attestations( _ context.Context, - msgs map[cciptypes.ChainSelector]map[reader.MessageTokenID]cciptypes.Bytes, + messagesByChain map[cciptypes.ChainSelector]map[reader.MessageTokenID]cciptypes.Bytes, ) (map[cciptypes.ChainSelector]map[reader.MessageTokenID]AttestationStatus, error) { outcome := make(map[cciptypes.ChainSelector]map[reader.MessageTokenID]AttestationStatus) - for chainSelector, hashes := range msgs { + for chainSelector, messagesByTokenID := range messagesByChain { outcome[chainSelector] = make(map[reader.MessageTokenID]AttestationStatus) - for tokenID, messageHash := range hashes { - outcome[chainSelector][tokenID] = f.Data[string(messageHash)] + for tokenID, message := range messagesByTokenID { + outcome[chainSelector][tokenID] = f.Data[string(message)] } } return outcome, nil diff --git a/execute/tokendata/usdc/usdc.go b/execute/tokendata/usdc/usdc.go index 156df20ac..c60a3d92a 100644 --- a/execute/tokendata/usdc/usdc.go +++ b/execute/tokendata/usdc/usdc.go @@ -60,14 +60,14 @@ func (u *TokenDataObserver) Observe( // 1. Pick only messages that contain USDC tokens usdcMessages := u.pickOnlyUSDCMessages(messages) - // 2. Fetch USDC messages hashes based on the `MessageSent (bytes message)` event - usdcMessageHashes, err := u.fetchUSDCMessageHashes(ctx, usdcMessages) + // 2. Fetch USDC messages by token id based on the `MessageSent (bytes message)` event + usdcMessagesByTokenID, err := u.fetchUSDCEventMessages(ctx, usdcMessages) if err != nil { return nil, err } // 3. Fetch attestations for USDC messages - attestations, err := u.fetchAttestations(ctx, usdcMessageHashes) + attestations, err := u.fetchAttestations(ctx, usdcMessagesByTokenID) if err != nil { return nil, err } @@ -116,7 +116,7 @@ func (u *TokenDataObserver) pickOnlyUSDCMessages( return usdcMessages } -func (u *TokenDataObserver) fetchUSDCMessageHashes( +func (u *TokenDataObserver) fetchUSDCEventMessages( ctx context.Context, usdcMessages map[cciptypes.ChainSelector]map[reader.MessageTokenID]cciptypes.RampTokenAmount, ) (map[cciptypes.ChainSelector]map[reader.MessageTokenID]cciptypes.Bytes, error) { @@ -128,7 +128,7 @@ func (u *TokenDataObserver) fetchUSDCMessageHashes( } // TODO Sequential reading USDC messages from the source chain - usdcHashes, err := u.usdcMessageReader.MessageHashes(ctx, chainSelector, u.destChainSelector, messages) + msgByTokenID, err := u.usdcMessageReader.MessagesByTokenID(ctx, chainSelector, u.destChainSelector, messages) if err != nil { u.lggr.Errorw( "Failed fetching USDC events from the source chain", @@ -139,7 +139,7 @@ func (u *TokenDataObserver) fetchUSDCMessageHashes( ) return nil, err } - output[chainSelector] = usdcHashes + output[chainSelector] = msgByTokenID } return output, nil } diff --git a/pkg/reader/usdc_reader.go b/pkg/reader/usdc_reader.go index b2600a5c8..e811811b1 100644 --- a/pkg/reader/usdc_reader.go +++ b/pkg/reader/usdc_reader.go @@ -21,7 +21,7 @@ import ( ) type USDCMessageReader interface { - MessageHashes(ctx context.Context, + MessagesByTokenID(ctx context.Context, source, dest cciptypes.ChainSelector, tokens map[MessageTokenID]cciptypes.RampTokenAmount, ) (map[MessageTokenID]cciptypes.Bytes, error) @@ -133,7 +133,7 @@ func AllAvailableDomains() map[uint64]uint32 { return destDomains } -func (u usdcMessageReader) MessageHashes( +func (u usdcMessageReader) MessagesByTokenID( ctx context.Context, source, dest cciptypes.ChainSelector, tokens map[MessageTokenID]cciptypes.RampTokenAmount, @@ -207,7 +207,7 @@ func (u usdcMessageReader) MessageHashes( // 3. Remapping database events to the proper MessageTokenID out := make(map[MessageTokenID]cciptypes.Bytes) for tokenID, messageID := range eventIDs { - messageHash, ok1 := messageSentEvents[messageID] + message, ok1 := messageSentEvents[messageID] if !ok1 { // Token not available in the source chain, it should never happen at this stage u.lggr.Warnw("Message not found in the source chain", @@ -217,7 +217,7 @@ func (u usdcMessageReader) MessageHashes( ) continue } - out[tokenID] = messageHash + out[tokenID] = message } return out, nil @@ -323,7 +323,7 @@ func NewFakeUSDCMessageReader(messages map[MessageTokenID]cciptypes.Bytes) FakeU return FakeUSDCMessageReader{Messages: messages} } -func (f FakeUSDCMessageReader) MessageHashes( +func (f FakeUSDCMessageReader) MessagesByTokenID( _ context.Context, _, _ cciptypes.ChainSelector, tokens map[MessageTokenID]cciptypes.RampTokenAmount, diff --git a/pkg/reader/usdc_reader_test.go b/pkg/reader/usdc_reader_test.go index fe339a023..0b2b94e0c 100644 --- a/pkg/reader/usdc_reader_test.go +++ b/pkg/reader/usdc_reader_test.go @@ -114,7 +114,7 @@ func Test_USDCMessageReader_New(t *testing.T) { } } -func Test_USDCMessageReader_MessageHashes(t *testing.T) { +func Test_USDCMessageReader_MessagesByTokenID(t *testing.T) { ctx := tests.Context(t) emptyChain := cciptypes.ChainSelector(sel.ETHEREUM_MAINNET.Selector) emptyReader := reader.NewMockContractReaderFacade(t) @@ -227,7 +227,7 @@ func Test_USDCMessageReader_MessageHashes(t *testing.T) { for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - hashes, err1 := usdcReader.MessageHashes( + messages, err1 := usdcReader.MessagesByTokenID( tests.Context(t), tc.sourceSelector, tc.destSelector, @@ -239,8 +239,8 @@ func Test_USDCMessageReader_MessageHashes(t *testing.T) { require.ErrorContains(t, err1, tc.errorMessage) } else { require.NoError(t, err) - require.NotNil(t, hashes) - require.Equal(t, tc.expectedMsgIDs, maps.Keys(hashes)) + require.NotNil(t, messages) + require.Equal(t, tc.expectedMsgIDs, maps.Keys(messages)) } }) }