From a18422e3875a0bac6e7a12787a089d6f088aaad3 Mon Sep 17 00:00:00 2001 From: Ian Shim Date: Tue, 23 Apr 2024 17:48:01 -0700 Subject: [PATCH] validate onchain operator ID against local operator ID --- core/eth/tx.go | 6 ++ core/mock/tx.go | 6 ++ core/tx.go | 3 + node/grpc/server_test.go | 9 ++- node/node.go | 23 +++++-- node/node_test.go | 141 +++++++++++++++++++++++++++++++++++++++ test/integration_test.go | 1 + 7 files changed, 180 insertions(+), 9 deletions(-) create mode 100644 node/node_test.go diff --git a/core/eth/tx.go b/core/eth/tx.go index 5207473022..05f281187e 100644 --- a/core/eth/tx.go +++ b/core/eth/tx.go @@ -550,6 +550,12 @@ func (t *Transactor) OperatorIDToAddress(ctx context.Context, operatorId core.Op }, operatorId) } +func (t *Transactor) OperatorAddressToID(ctx context.Context, address gethcommon.Address) (core.OperatorID, error) { + return t.Bindings.BLSApkRegistry.GetOperatorId(&bind.CallOpts{ + Context: ctx, + }, address) +} + func (t *Transactor) BatchOperatorIDToAddress(ctx context.Context, operatorIds []core.OperatorID) ([]gethcommon.Address, error) { type AddressOrError struct { address gethcommon.Address diff --git a/core/mock/tx.go b/core/mock/tx.go index 920bef251a..6651d7b53d 100644 --- a/core/mock/tx.go +++ b/core/mock/tx.go @@ -110,6 +110,12 @@ func (t *MockTransactor) OperatorIDToAddress(ctx context.Context, operatorId cor return result.(gethcommon.Address), args.Error(1) } +func (t *MockTransactor) OperatorAddressToID(ctx context.Context, address gethcommon.Address) (core.OperatorID, error) { + args := t.Called() + result := args.Get(0) + return result.(core.OperatorID), args.Error(1) +} + func (t *MockTransactor) BatchOperatorIDToAddress(ctx context.Context, operatorIds []core.OperatorID) ([]gethcommon.Address, error) { args := t.Called() result := args.Get(0) diff --git a/core/tx.go b/core/tx.go index 2242aceb39..1a540e9a13 100644 --- a/core/tx.go +++ b/core/tx.go @@ -97,6 +97,9 @@ type Transactor interface { // OperatorIDToAddress returns the address of the operator from the operator id. OperatorIDToAddress(ctx context.Context, operatorId OperatorID) (gethcommon.Address, error) + // OperatorAddressToID returns the operator id from the operator address. + OperatorAddressToID(ctx context.Context, operatorAddress gethcommon.Address) (OperatorID, error) + // BatchOperatorIDToAddress returns the addresses of the operators from the operator id. BatchOperatorIDToAddress(ctx context.Context, operatorIds []OperatorID) ([]gethcommon.Address, error) diff --git a/node/grpc/server_test.go b/node/grpc/server_test.go index 56c2f00749..5bc8b5e27f 100644 --- a/node/grpc/server_test.go +++ b/node/grpc/server_test.go @@ -15,7 +15,6 @@ import ( "github.com/Layr-Labs/eigenda/common" commonmock "github.com/Layr-Labs/eigenda/common/mock" "github.com/Layr-Labs/eigenda/core" - core_mock "github.com/Layr-Labs/eigenda/core/mock" coremock "github.com/Layr-Labs/eigenda/core/mock" "github.com/Layr-Labs/eigenda/encoding" "github.com/Layr-Labs/eigenda/encoding/kzg" @@ -36,12 +35,12 @@ import ( var ( encodedChunk = []byte{42, 255, 129, 3, 1, 1, 5, 67, 104, 117, 110, 107, 1, 255, 130, 0, 1, 2, 1, 6, 67, 111, 101, 102, 102, 115, 1, 255, 134, 0, 1, 5, 80, 114, 111, 111, 102, 1, 255, 136, 0, 0, 0, 25, 255, 133, 2, 1, 1, 10, 91, 93, 98, 110, 50, 53, 52, 46, 70, 114, 1, 255, 134, 0, 1, 255, 132, 0, 0, 18, 255, 131, 1, 1, 1, 2, 70, 114, 1, 255, 132, 0, 1, 6, 1, 8, 0, 0, 35, 255, 135, 3, 1, 1, 7, 71, 49, 80, 111, 105, 110, 116, 1, 255, 136, 0, 1, 2, 1, 1, 88, 1, 255, 138, 0, 1, 1, 89, 1, 255, 138, 0, 0, 0, 23, 255, 137, 1, 1, 1, 7, 69, 108, 101, 109, 101, 110, 116, 1, 255, 138, 0, 1, 6, 1, 8, 0, 0, 254, 4, 243, 255, 130, 1, 32, 4, 248, 186, 196, 96, 34, 212, 35, 97, 83, 248, 121, 9, 252, 220, 181, 118, 97, 134, 248, 186, 26, 225, 204, 191, 144, 133, 234, 248, 7, 223, 191, 156, 83, 115, 21, 36, 4, 248, 43, 196, 225, 43, 61, 88, 43, 49, 248, 28, 200, 121, 122, 178, 119, 200, 17, 248, 29, 172, 61, 194, 130, 114, 50, 171, 248, 33, 141, 185, 47, 11, 129, 128, 116, 4, 248, 246, 236, 255, 207, 43, 92, 176, 63, 248, 103, 179, 139, 80, 75, 57, 128, 89, 248, 107, 170, 70, 254, 95, 17, 101, 158, 248, 8, 106, 82, 82, 25, 78, 95, 104, 4, 248, 28, 125, 21, 116, 243, 255, 206, 10, 248, 153, 249, 156, 88, 61, 254, 171, 171, 248, 103, 66, 131, 8, 12, 165, 173, 173, 248, 36, 227, 189, 242, 180, 18, 171, 208, 4, 248, 19, 159, 205, 146, 86, 81, 57, 28, 248, 161, 130, 249, 92, 236, 82, 103, 4, 248, 84, 44, 63, 43, 249, 88, 187, 12, 248, 42, 121, 83, 118, 55, 127, 180, 134, 4, 248, 193, 39, 155, 110, 195, 113, 118, 46, 248, 47, 92, 162, 69, 188, 120, 94, 161, 248, 101, 214, 253, 103, 243, 8, 246, 176, 248, 41, 1, 238, 37, 43, 132, 228, 244, 4, 248, 70, 34, 194, 33, 68, 87, 108, 180, 248, 203, 230, 97, 137, 162, 177, 142, 23, 248, 101, 25, 216, 255, 137, 96, 240, 73, 248, 40, 50, 167, 154, 63, 108, 55, 240, 4, 248, 78, 40, 51, 224, 193, 131, 8, 90, 248, 162, 203, 245, 119, 83, 125, 219, 33, 248, 85, 109, 106, 231, 162, 152, 229, 110, 248, 38, 189, 66, 40, 176, 177, 114, 84, 4, 248, 193, 67, 43, 158, 218, 245, 83, 116, 248, 100, 165, 217, 161, 166, 209, 98, 172, 248, 231, 23, 45, 28, 225, 102, 143, 157, 248, 20, 12, 146, 122, 104, 126, 51, 235, 4, 248, 19, 118, 59, 144, 83, 246, 144, 229, 248, 203, 168, 161, 194, 137, 34, 191, 157, 248, 252, 196, 212, 78, 99, 166, 6, 225, 248, 29, 41, 54, 112, 125, 128, 240, 209, 4, 248, 24, 175, 53, 2, 113, 155, 113, 233, 248, 162, 189, 238, 198, 233, 31, 199, 239, 248, 205, 162, 128, 190, 163, 250, 181, 226, 248, 40, 205, 5, 117, 16, 49, 205, 45, 4, 248, 78, 49, 135, 21, 90, 93, 196, 50, 248, 115, 105, 77, 122, 222, 27, 224, 166, 248, 44, 0, 255, 63, 67, 184, 234, 235, 248, 45, 88, 39, 211, 138, 80, 43, 243, 4, 248, 244, 239, 154, 119, 68, 204, 215, 5, 248, 53, 82, 219, 150, 72, 243, 20, 147, 248, 141, 131, 101, 73, 11, 218, 234, 89, 248, 25, 246, 203, 17, 86, 91, 107, 199, 4, 248, 111, 106, 155, 101, 22, 163, 231, 214, 248, 86, 123, 235, 222, 87, 192, 80, 167, 248, 107, 38, 156, 175, 73, 123, 184, 189, 248, 23, 12, 154, 39, 153, 2, 158, 213, 4, 248, 40, 166, 62, 99, 6, 145, 128, 237, 248, 77, 160, 235, 64, 123, 181, 120, 66, 248, 116, 0, 126, 221, 26, 18, 100, 74, 248, 46, 92, 161, 252, 177, 177, 191, 127, 4, 248, 227, 144, 223, 154, 232, 249, 22, 233, 248, 53, 82, 148, 149, 84, 76, 107, 93, 248, 71, 251, 7, 58, 156, 200, 102, 4, 248, 3, 147, 75, 172, 199, 222, 109, 87, 4, 248, 169, 207, 109, 252, 37, 85, 158, 78, 248, 237, 12, 207, 255, 117, 62, 171, 3, 248, 43, 93, 155, 238, 136, 102, 150, 139, 248, 40, 174, 6, 46, 62, 50, 174, 104, 4, 248, 156, 217, 228, 156, 76, 202, 37, 121, 248, 80, 44, 200, 177, 237, 112, 103, 44, 248, 211, 172, 202, 164, 34, 242, 190, 204, 248, 15, 241, 94, 33, 88, 13, 34, 66, 4, 248, 198, 229, 9, 111, 155, 117, 84, 125, 248, 69, 115, 47, 6, 35, 132, 39, 86, 248, 243, 113, 79, 216, 240, 35, 72, 75, 248, 7, 29, 38, 85, 134, 106, 213, 236, 4, 248, 8, 8, 251, 11, 97, 66, 8, 55, 248, 159, 67, 100, 214, 31, 167, 88, 221, 248, 151, 110, 49, 190, 136, 249, 55, 217, 248, 47, 94, 78, 30, 0, 220, 176, 125, 4, 248, 246, 81, 132, 144, 151, 161, 113, 102, 248, 229, 8, 10, 180, 28, 223, 222, 8, 248, 158, 88, 212, 24, 77, 31, 96, 232, 248, 41, 65, 45, 216, 25, 224, 221, 4, 4, 248, 11, 189, 86, 122, 64, 254, 107, 253, 248, 242, 174, 32, 144, 43, 116, 187, 77, 248, 16, 163, 127, 128, 4, 233, 82, 168, 248, 4, 90, 126, 233, 232, 220, 81, 74, 4, 248, 54, 17, 20, 36, 220, 10, 168, 78, 248, 77, 61, 41, 4, 95, 154, 130, 70, 248, 37, 180, 163, 188, 242, 88, 81, 28, 248, 37, 195, 179, 103, 195, 0, 252, 30, 4, 248, 148, 154, 198, 22, 110, 201, 164, 240, 248, 242, 100, 163, 103, 30, 185, 139, 205, 248, 198, 168, 87, 116, 135, 219, 11, 230, 248, 43, 163, 196, 37, 51, 32, 130, 241, 4, 248, 160, 22, 80, 69, 111, 126, 3, 23, 248, 76, 89, 182, 79, 244, 245, 155, 42, 248, 144, 203, 89, 203, 85, 216, 109, 139, 248, 36, 125, 246, 94, 210, 7, 236, 50, 4, 248, 244, 42, 154, 219, 137, 78, 64, 167, 248, 73, 57, 191, 50, 122, 120, 124, 249, 248, 192, 102, 139, 159, 135, 150, 18, 35, 248, 40, 167, 252, 247, 112, 215, 52, 61, 4, 248, 151, 181, 121, 81, 121, 147, 227, 13, 248, 236, 181, 178, 176, 243, 4, 136, 195, 248, 62, 97, 145, 239, 166, 114, 175, 107, 248, 23, 91, 75, 217, 198, 192, 155, 92, 4, 248, 182, 191, 150, 70, 229, 96, 122, 14, 248, 134, 0, 111, 72, 36, 162, 244, 220, 248, 168, 72, 14, 253, 239, 166, 139, 197, 248, 44, 139, 158, 151, 191, 127, 27, 222, 4, 248, 74, 171, 39, 27, 36, 31, 102, 30, 248, 41, 77, 140, 191, 229, 182, 30, 16, 248, 219, 194, 193, 143, 239, 141, 47, 73, 248, 23, 1, 236, 49, 51, 57, 155, 228, 4, 248, 128, 145, 254, 105, 104, 55, 224, 206, 248, 195, 70, 112, 120, 42, 171, 202, 23, 248, 242, 232, 247, 249, 215, 77, 208, 121, 248, 29, 0, 45, 26, 151, 224, 199, 214, 4, 248, 235, 253, 108, 246, 112, 139, 56, 187, 248, 214, 211, 157, 43, 210, 247, 57, 203, 248, 150, 28, 35, 231, 169, 220, 146, 139, 248, 48, 54, 207, 130, 116, 140, 125, 197, 4, 248, 23, 120, 154, 57, 66, 85, 149, 5, 248, 170, 172, 192, 127, 230, 130, 224, 17, 248, 117, 98, 19, 140, 134, 78, 47, 98, 248, 40, 206, 62, 254, 165, 238, 160, 130, 1, 1, 4, 248, 164, 40, 240, 180, 149, 114, 87, 82, 248, 195, 115, 109, 187, 95, 132, 65, 10, 248, 176, 59, 100, 197, 207, 37, 161, 253, 248, 10, 19, 137, 98, 39, 77, 128, 20, 1, 4, 248, 213, 212, 69, 58, 138, 39, 69, 249, 248, 99, 187, 162, 108, 114, 239, 78, 157, 248, 62, 166, 165, 148, 83, 202, 37, 169, 248, 47, 253, 18, 76, 216, 168, 22, 21, 0, 0} - chainState *core_mock.ChainDataMock + chainState *coremock.ChainDataMock opID [32]byte ) func TestMain(m *testing.M) { - chainState, _ = core_mock.MakeChainDataMock(map[uint8]int{ + chainState, _ = coremock.MakeChainDataMock(map[uint8]int{ 0: 4, 1: 4, 2: 4, @@ -109,7 +108,7 @@ func newTestServer(t *testing.T, mockValidator bool) *grpc.Server { var val core.ShardValidator if mockValidator { - mockVal := core_mock.NewMockShardValidator() + mockVal := coremock.NewMockShardValidator() mockVal.On("ValidateBlob", mock.Anything, mock.Anything).Return(nil) mockVal.On("ValidateBatch", mock.Anything, mock.Anything, mock.Anything).Return(nil) val = mockVal @@ -122,7 +121,7 @@ func newTestServer(t *testing.T, mockValidator bool) *grpc.Server { asn := &core.StdAssignmentCoordinator{} - cst, err := core_mock.MakeChainDataMock(map[uint8]int{ + cst, err := coremock.MakeChainDataMock(map[uint8]int{ 0: 10, 1: 10, 2: 10, diff --git a/node/node.go b/node/node.go index 1356e7d41e..010b76cf6d 100644 --- a/node/node.go +++ b/node/node.go @@ -118,8 +118,6 @@ func NewNode(config *Config, pubIPProvider pubip.Provider, logger logging.Logger asgn := &core.StdAssignmentCoordinator{} validator := core.NewShardValidator(v, asgn, cst, config.ID) - // Create new store - // Resolve the BLOCK_STALE_MEASURE and STORE_DURATION_BLOCKS. var blockStaleMeasure, storeDurationBlocks uint32 if config.EnableTestMode && config.OverrideBlockStaleMeasure > 0 { @@ -140,6 +138,7 @@ func NewNode(config *Config, pubIPProvider pubip.Provider, logger logging.Logger } storeDurationBlocks = storeDuration } + // Create new store store, err := NewLevelDBStore(config.DbPath+"/chunk", logger, metrics, blockStaleMeasure, storeDurationBlocks) if err != nil { return nil, fmt.Errorf("failed to create new store: %w", err) @@ -150,10 +149,15 @@ func NewNode(config *Config, pubIPProvider pubip.Provider, logger logging.Logger if err != nil { return nil, fmt.Errorf("failed to create new operator sockets filterer: %w", err) } + nodeLogger := logger.With("component", "Node") + nodeLogger.Info("Creating node", "chainID", chainID.String(), "operatorID", config.ID.Hex(), + "dispersalPort", config.DispersalPort, "retrievalPort", config.RetrievalPort, "churnerUrl", config.ChurnerUrl, + "quorumIDs", fmt.Sprint(config.QuorumIDList), "registerNodeAtStart", config.RegisterNodeAtStart, "pubIPCheckInterval", config.PubIPCheckInterval, + "eigenDAServiceManagerAddr", config.EigenDAServiceManagerAddr, "blockStaleMeasure", blockStaleMeasure, "storeDurationBlocks", storeDurationBlocks) return &Node{ Config: config, - Logger: logger.With("component", "Node"), + Logger: nodeLogger, KeyPair: keyPair, Metrics: metrics, NodeApi: nodeApi, @@ -183,6 +187,7 @@ func (n *Node) Start(ctx context.Context) error { // Build the socket based on the hostname/IP provided in the CLI socket := string(core.MakeOperatorSocket(n.Config.Hostname, n.Config.DispersalPort, n.Config.RetrievalPort)) + var operator *Operator if n.Config.RegisterNodeAtStart { n.Logger.Info("Registering node on chain with the following parameters:", "operatorId", n.Config.ID.Hex(), "hostname", n.Config.Hostname, "dispersalPort", n.Config.DispersalPort, @@ -192,7 +197,7 @@ func (n *Node) Start(ctx context.Context) error { if err != nil { return fmt.Errorf("NewClient: cannot parse private key: %w", err) } - operator := &Operator{ + operator = &Operator{ Address: crypto.PubkeyToAddress(privateKey.PublicKey).Hex(), Socket: socket, Timeout: 10 * time.Second, @@ -216,6 +221,16 @@ func (n *Node) Start(ctx context.Context) error { } } + if operator != nil && operator.Address != "" { + operatorID, err := n.Transactor.OperatorAddressToID(ctx, gethcommon.HexToAddress(operator.Address)) + if err != nil { + return fmt.Errorf("failed to get operator ID: %w", err) + } + if operatorID != operator.OperatorId { + return fmt.Errorf("operator ID mismatch: expected %s, got %s", operator.OperatorId.Hex(), operatorID.Hex()) + } + } + n.CurrentSocket = socket // Start the Node IP updater only if the PUBLIC_IP_PROVIDER is greater than 0. if n.Config.PubIPCheckInterval > 0 { diff --git a/node/node_test.go b/node/node_test.go new file mode 100644 index 0000000000..ed732445c5 --- /dev/null +++ b/node/node_test.go @@ -0,0 +1,141 @@ +package node_test + +import ( + "context" + "fmt" + "os" + "runtime" + "testing" + "time" + + "github.com/Layr-Labs/eigenda/common" + "github.com/Layr-Labs/eigenda/common/geth" + "github.com/Layr-Labs/eigenda/core" + coremock "github.com/Layr-Labs/eigenda/core/mock" + "github.com/Layr-Labs/eigenda/node" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +var privateKey = "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" +var opID = [32]byte{} + +type components struct { + node *node.Node + tx *coremock.MockTransactor +} + +func newComponents(t *testing.T) *components { + dbPath := t.TempDir() + keyPair, err := core.GenRandomBlsKeys() + if err != nil { + panic("failed to create a BLS Key") + } + copy(opID[:], []byte(fmt.Sprintf("%d", 3))) + config := &node.Config{ + Timeout: 10 * time.Second, + ExpirationPollIntervalSec: 1, + QuorumIDList: []core.QuorumID{0}, + DbPath: dbPath, + ID: opID, + NumBatchValidators: runtime.GOMAXPROCS(0), + EnableNodeApi: false, + EnableMetrics: false, + RegisterNodeAtStart: false, + } + loggerConfig := common.DefaultLoggerConfig() + logger, err := common.NewLogger(loggerConfig) + if err != nil { + panic("failed to create a logger") + } + + err = os.MkdirAll(config.DbPath, os.ModePerm) + if err != nil { + panic("failed to create a directory for db") + } + tx := &coremock.MockTransactor{} + + mockVal := coremock.NewMockShardValidator() + mockVal.On("ValidateBlob", mock.Anything, mock.Anything).Return(nil) + mockVal.On("ValidateBatch", mock.Anything, mock.Anything, mock.Anything).Return(nil) + + chainState, _ := coremock.MakeChainDataMock(map[uint8]int{ + 0: 4, + 1: 4, + 2: 4, + }) + + store, err := node.NewLevelDBStore(dbPath, logger, nil, 1e9, 1e9) + if err != nil { + panic("failed to create a new levelDB store") + } + defer os.Remove(dbPath) + + return &components{ + node: &node.Node{ + Config: config, + Logger: logger, + KeyPair: keyPair, + Metrics: nil, + Store: store, + ChainState: chainState, + Validator: mockVal, + Transactor: tx, + }, + tx: tx, + } +} + +func TestNodeStartNoAddress(t *testing.T) { + c := newComponents(t) + c.node.Config.RegisterNodeAtStart = false + + err := c.node.Start(context.Background()) + assert.NoError(t, err) +} + +func TestNodeStartOperatorIDMatch(t *testing.T) { + c := newComponents(t) + c.node.Config.RegisterNodeAtStart = true + c.node.Config.EthClientConfig = geth.EthClientConfig{ + RPCURLs: []string{"http://localhost:8545"}, + PrivateKeyString: privateKey, + NumConfirmations: 1, + } + c.tx.On("GetRegisteredQuorumIdsForOperator", mock.Anything).Return([]core.QuorumID{}, nil) + c.tx.On("GetOperatorSetParams", mock.Anything, mock.Anything).Return(&core.OperatorSetParam{ + MaxOperatorCount: uint32(4), + ChurnBIPsOfOperatorStake: uint16(1000), + ChurnBIPsOfTotalStake: uint16(10), + }, nil) + c.tx.On("GetNumberOfRegisteredOperatorForQuorum", mock.Anything, mock.Anything).Return(uint32(0), nil) + c.tx.On("RegisterOperator", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + + c.tx.On("OperatorAddressToID", mock.Anything).Return(core.OperatorID(opID), nil) + + err := c.node.Start(context.Background()) + assert.NoError(t, err) +} + +func TestNodeStartOperatorIDDoesNotMatch(t *testing.T) { + c := newComponents(t) + c.node.Config.RegisterNodeAtStart = true + c.node.Config.EthClientConfig = geth.EthClientConfig{ + RPCURLs: []string{"http://localhost:8545"}, + PrivateKeyString: privateKey, + NumConfirmations: 1, + } + c.tx.On("GetRegisteredQuorumIdsForOperator", mock.Anything).Return([]core.QuorumID{}, nil) + c.tx.On("GetOperatorSetParams", mock.Anything, mock.Anything).Return(&core.OperatorSetParam{ + MaxOperatorCount: uint32(4), + ChurnBIPsOfOperatorStake: uint16(1000), + ChurnBIPsOfTotalStake: uint16(10), + }, nil) + c.tx.On("GetNumberOfRegisteredOperatorForQuorum", mock.Anything, mock.Anything).Return(uint32(0), nil) + c.tx.On("RegisterOperator", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + + c.tx.On("OperatorAddressToID", mock.Anything).Return(core.OperatorID{1}, nil) + + err := c.node.Start(context.Background()) + assert.ErrorContains(t, err, "operator ID mismatch") +} diff --git a/test/integration_test.go b/test/integration_test.go index ecda8fbe85..56c7c7d92b 100644 --- a/test/integration_test.go +++ b/test/integration_test.go @@ -273,6 +273,7 @@ func mustMakeOperators(t *testing.T, cst *coremock.ChainDataMock, logger logging tx.On("UpdateOperatorSocket").Return(nil) tx.On("GetBlockStaleMeasure").Return(nil) tx.On("GetStoreDurationBlocks").Return(nil) + tx.On("OperatorIDToAddress").Return(gethcommon.Address{1}, nil) noopMetrics := metrics.NewNoopMetrics() reg := prometheus.NewRegistry()