Skip to content

Commit

Permalink
Add enclave guardian service, refactor logic out of host.go (#1409)
Browse files Browse the repository at this point in the history
  • Loading branch information
BedrockSquirrel authored Aug 4, 2023
1 parent b4e4182 commit 06e63f1
Show file tree
Hide file tree
Showing 29 changed files with 997 additions and 770 deletions.
2 changes: 0 additions & 2 deletions go/common/host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,6 @@ type Host interface {

// HealthCheck returns the health status of the host + enclave + db
HealthCheck() (*HealthCheck, error)

P2PTxHandler
}

type BlockStream struct {
Expand Down
16 changes: 12 additions & 4 deletions go/common/host/identity.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
package host

import gethcommon "github.com/ethereum/go-ethereum/common"
import (
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/obscuronet/go-obscuro/go/common"
"github.com/obscuronet/go-obscuro/go/config"
)

type Identity struct {
ID gethcommon.Address
P2PPublicAddress string
IsGenesis bool
IsSequencer bool
}

func NewIdentity(id gethcommon.Address, p2pPublicAddress string) Identity {
func NewIdentity(cfg *config.HostConfig) Identity {
return Identity{
ID: id,
P2PPublicAddress: p2pPublicAddress,
ID: cfg.ID,
P2PPublicAddress: cfg.P2PPublicAddress,
IsGenesis: cfg.IsGenesis,
IsSequencer: cfg.NodeType == common.Sequencer,
}
}
37 changes: 30 additions & 7 deletions go/common/host/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ package host
import (
"math/big"

"github.com/ethereum/go-ethereum/rpc"
"github.com/obscuronet/go-obscuro/go/responses"

gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/obscuronet/go-obscuro/go/common"
Expand All @@ -11,17 +14,21 @@ import (

// service names - these are the keys used to register known services with the host
const (
P2PName = "p2p"
L1BlockRepositoryName = "l1-block-repo"
L1PublisherName = "l1-publisher"
L2BatchRepositoryName = "l2-batch-repo"
P2PName = "p2p"
L1BlockRepositoryName = "l1-block-repo"
L1PublisherName = "l1-publisher"
L2BatchRepositoryName = "l2-batch-repo"
EnclaveServiceName = "enclaves"
LogSubscriptionServiceName = "log-subs"
)

// The host has a number of services that encapsulate the various responsibilities of the host.
// This file contains service-level abstractions and utilities, as well as all the interfaces for these services, code
// should depend on these interfaces rather than the concrete implementations.

// Service interface allows the host to manage all services in a generic way
// Note: Services may depend on other services but they shouldn't use them during construction, only when 'Start()' is called.
// They should be resilient to services availability, because the construction ordering is not guaranteed.
type Service interface {
Start() error
Stop() error
Expand All @@ -48,8 +55,8 @@ type P2P interface {
// todo (@matt) feels a bit weird to have this in this interface since it relates to serving data rather than receiving
SubscribeForBatchRequests(handler P2PBatchRequestHandler) func()

// UpdatePeerList allows the host to notify the p2p service of a change in the peer list
UpdatePeerList([]string)
// RefreshPeerList notifies the P2P service that its peer list might be out-of-date and it should resync
RefreshPeerList()
}

// P2PBatchHandler is an interface for receiving new batches from the P2P network as they arrive
Expand Down Expand Up @@ -77,7 +84,7 @@ type L1BlockRepository interface {
FetchBlockByHeight(height *big.Int) (*types.Block, error)
// FetchNextBlock returns the next canonical block after a given block hash
// It returns the new block, a bool which is true if the block is the current L1 head and a bool if the block is on a different fork to prevBlock
FetchNextBlock(prevBlock gethcommon.Hash) (*types.Block, bool, bool, error)
FetchNextBlock(prevBlock gethcommon.Hash) (*types.Block, bool, error)
// FetchReceipts returns the receipts for a given L1 block
FetchReceipts(block *common.L1Block) types.Receipts
}
Expand Down Expand Up @@ -133,4 +140,20 @@ type EnclaveService interface {
// LookupBatchBySeqNo is used to fetch batch data from the enclave - it is only used as a fallback for the sequencer
// host if it's missing a batch (other host services should use L2Repo to fetch batch data)
LookupBatchBySeqNo(seqNo *big.Int) (*common.ExtBatch, error)

// GetEnclaveClient returns an enclave client // todo (@matt) we probably don't want to expose this
GetEnclaveClient() common.Enclave

// SubmitAndBroadcastTx submits an encrypted transaction to the enclave, and broadcasts it to other hosts on the network (in particular, to the sequencer)
SubmitAndBroadcastTx(encryptedParams common.EncryptedParamsSendRawTx) (*responses.RawTx, error)

Subscribe(id rpc.ID, encryptedLogSubscription common.EncryptedParamsLogSubscription) error
Unsubscribe(id rpc.ID) error
}

// LogSubscriptionManager provides an interface for the host to manage log subscriptions
type LogSubscriptionManager interface {
Subscribe(id rpc.ID, encryptedLogSubscription common.EncryptedParamsLogSubscription, matchedLogsCh chan []byte) error
Unsubscribe(id rpc.ID)
SendLogsToSubscribers(result *common.EncryptedSubscriptionLogs)
}
1 change: 1 addition & 0 deletions go/common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ type (
const (
L2GenesisHeight = uint64(0)
L1GenesisHeight = uint64(0)
L2GenesisSeqNo = uint64(1)
// HeightCommittedBlocks is the number of blocks deep a transaction must be to be considered safe from reorganisations.
HeightCommittedBlocks = 15
)
Expand Down
5 changes: 3 additions & 2 deletions go/enclave/components/batch_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,8 @@ func (executor *batchExecutor) ComputeBatch(context *BatchExecutionContext) (*Co
}

var messages common.CrossChainMessages
if context.SequencerNo.Int64() > 1 {
// Cross chain data is not accessible until one after the genesis batch
if context.SequencerNo.Int64() > int64(common.L2GenesisSeqNo+1) {
messages = executor.crossChainProcessors.Local.RetrieveInboundMessages(parentBlock, block, stateDB)
}
crossChainTransactions := executor.crossChainProcessors.Local.CreateSyntheticTransactions(messages, stateDB)
Expand Down Expand Up @@ -186,7 +187,7 @@ func (executor *batchExecutor) CreateGenesisState(blkHash common.L1BlockHash, ti
Root: *preFundGenesisState,
TxHash: types.EmptyRootHash,
Number: big.NewInt(int64(0)),
SequencerOrderNo: big.NewInt(int64(0)),
SequencerOrderNo: big.NewInt(int64(common.L2GenesisSeqNo)), // genesis batch has seq number 1
ReceiptHash: types.EmptyRootHash,
Time: timeNow,
},
Expand Down
2 changes: 1 addition & 1 deletion go/enclave/components/rollup_producer.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func NewRollupProducer(sequencerID gethcommon.Address, transactionBlobCrypto cry
func (re *rollupProducerImpl) CreateRollup(fromBatchNo uint64, limiter limiters.RollupLimiter) (*core.Rollup, error) {
batches, err := re.batchRegistry.BatchesAfter(fromBatchNo, limiter)
if err != nil {
return nil, err
return nil, fmt.Errorf("could not fetch 'from' batch (seqNo=%d) for rollup: %w", fromBatchNo, err)
}

hasBatches := len(batches) != 0
Expand Down
9 changes: 6 additions & 3 deletions go/enclave/enclave.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ import (
gethrpc "github.com/ethereum/go-ethereum/rpc"
)

var _noHeadBatch = big.NewInt(0)

type enclaveImpl struct {
config *config.EnclaveConfig
storage storage.Storage
Expand Down Expand Up @@ -305,20 +307,21 @@ func (e *enclaveImpl) Status() (common.Status, common.SystemError) {
_, err := e.storage.FetchSecret()
if err != nil {
if errors.Is(err, errutil.ErrNotFound) {
return common.Status{StatusCode: common.AwaitingSecret}, nil
return common.Status{StatusCode: common.AwaitingSecret, L2Head: _noHeadBatch}, nil
}
return common.Status{StatusCode: common.Unavailable}, responses.ToInternalError(err)
}
l1Head, err := e.storage.FetchHeadBlock()
var l1HeadHash gethcommon.Hash
l1Head, err := e.storage.FetchHeadBlock()
if err != nil {
// this might be normal while enclave is starting up, just send empty hash
e.logger.Debug("failed to fetch L1 head block for status response", log.ErrKey, err)
} else {
l1HeadHash = l1Head.Hash()
}
// we use zero when there's no head batch yet, the first seq number is 1
l2HeadSeqNo := _noHeadBatch
l2Head, err := e.storage.FetchHeadBatch()
var l2HeadSeqNo *big.Int
if err != nil {
// this might be normal while enclave is starting up, just send empty hash
e.logger.Debug("failed to fetch L2 head batch for status response", log.ErrKey, err)
Expand Down
2 changes: 1 addition & 1 deletion go/enclave/enclave_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -471,7 +471,7 @@ func dummyBatch(blkHash gethcommon.Hash, height uint64, state *state.StateDB) *c
L1Proof: blkHash,
Root: state.IntermediateRoot(true),
Number: big.NewInt(int64(height)),
SequencerOrderNo: big.NewInt(int64(height)),
SequencerOrderNo: big.NewInt(int64(height + 1)), // seq number starts at 1 so need to offset
ReceiptHash: types.EmptyRootHash,
Time: uint64(time.Now().Unix()),
}
Expand Down
2 changes: 1 addition & 1 deletion go/enclave/nodetype/sequencer.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ func (s *sequencer) CreateRollup(lastBatchNo uint64) (*common.ExtRollup, error)
}

if err := s.signRollup(rollup); err != nil {
return nil, err
return nil, fmt.Errorf("failed to sign created rollup: %w", err)
}

s.logger.Info("Created new head rollup", log.RollupHashKey, rollup.Hash(), "numBatches", len(rollup.Batches))
Expand Down
2 changes: 1 addition & 1 deletion go/enclave/rpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func (s *RPCServer) HealthCheck(_ context.Context, _ *generated.EmptyArgs) (*gen

func (s *RPCServer) CreateRollup(_ context.Context, req *generated.CreateRollupRequest) (*generated.CreateRollupResponse, error) {
var fromSeqNo uint64 = 1
if req.FromSequenceNumber != nil {
if req.FromSequenceNumber != nil && *req.FromSequenceNumber > common.L2GenesisSeqNo {
fromSeqNo = *req.FromSequenceNumber
}

Expand Down
20 changes: 5 additions & 15 deletions go/host/container/host_container.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,32 +127,22 @@ func NewHostContainerFromConfig(parsedConfig *config.HostInputConfig, logger get
cfg.ID = ethWallet.Address()

fmt.Println("Connecting to the enclave...")
services := host.NewServicesRegistry(logger)
enclaveClient := enclaverpc.NewClient(cfg, logger)
p2pLogger := logger.New(log.CmpKey, log.P2PCmp)
metricsService := metrics.New(cfg.MetricsEnabled, cfg.MetricsHTTPPort, logger)
aggP2P := p2p.NewSocketP2PLayer(cfg, p2pLogger, metricsService.Registry())
aggP2P := p2p.NewSocketP2PLayer(cfg, services, p2pLogger, metricsService.Registry())
rpcServer := clientrpc.NewServer(cfg, logger)

mgmtContractLib := mgmtcontractlib.NewMgmtContractLib(&cfg.ManagementContractAddress, logger)

return NewHostContainer(cfg, aggP2P, l1Client, enclaveClient, mgmtContractLib, ethWallet, rpcServer, logger, metricsService)
return NewHostContainer(cfg, services, aggP2P, l1Client, enclaveClient, mgmtContractLib, ethWallet, rpcServer, logger, metricsService)
}

// NewHostContainer builds a host container with dependency injection rather than from config.
// Useful for testing etc. (want to be able to pass in logger, and also have option to mock out dependencies)
func NewHostContainer(
cfg *config.HostConfig, // provides various parameters that the host needs to function
// todo (@matt) sort out all this wiring, we should depend on interfaces not concrete types, we should make it obvious and consistent how services are instantiated
p2p host.P2PHostService, // provides the inbound and outbound p2p communication layer
l1Client ethadapter.EthClient, // provides inbound and outbound L1 connectivity
enclaveClient common.Enclave, // provides RPC connection to this host's Enclave
contractLib mgmtcontractlib.MgmtContractLib, // provides the management contract lib injection
hostWallet wallet.Wallet, // provides an L1 wallet for the host's transactions
rpcServer clientrpc.Server, // For communication with Obscuro client applications
logger gethlog.Logger, // provides logging with context
metricsService *metrics.Service, // provides the metrics service for other packages to use
) *HostContainer {
h := host.NewHost(cfg, p2p, l1Client, enclaveClient, hostWallet, contractLib, logger, metricsService.Registry())
func NewHostContainer(cfg *config.HostConfig, services *host.ServicesRegistry, p2p host.P2PHostService, l1Client ethadapter.EthClient, enclaveClient common.Enclave, contractLib mgmtcontractlib.MgmtContractLib, hostWallet wallet.Wallet, rpcServer clientrpc.Server, logger gethlog.Logger, metricsService *metrics.Service) *HostContainer {
h := host.NewHost(cfg, services, p2p, l1Client, enclaveClient, hostWallet, contractLib, logger, metricsService.Registry())

hostContainer := &HostContainer{
host: h,
Expand Down
2 changes: 1 addition & 1 deletion go/host/db/batches.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (db *DB) GetBatchBySequenceNumber(sequenceNumber *big.Int) (*common.ExtBatc
db.batchReads.Inc(1)
batchHash, err := db.readBatchHashBySequenceNumber(sequenceNumber)
if err != nil {
return nil, errors.Wrapf(err, "could not retrieve batch hash for seqNo=%d", sequenceNumber)
return nil, fmt.Errorf("could not retrieve batch hash for seqNo=%d: %w", sequenceNumber, err)
}
return db.GetBatch(*batchHash)
}
Expand Down
Loading

0 comments on commit 06e63f1

Please sign in to comment.