Skip to content

Commit

Permalink
Host: Monitor and serve important contract addresses (#1645)
Browse files Browse the repository at this point in the history
  • Loading branch information
BedrockSquirrel authored Nov 22, 2023
1 parent 29cae5d commit 1248086
Show file tree
Hide file tree
Showing 14 changed files with 429 additions and 51 deletions.
11 changes: 7 additions & 4 deletions go/common/host/services.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,8 @@ type L1Publisher interface {
InitializeSecret(attestation *common.AttestationReport, encSecret common.EncryptedSharedEnclaveSecret) error
// RequestSecret will send a management contract transaction to request a secret from the enclave, returning the L1 head at time of sending
RequestSecret(report *common.AttestationReport) (gethcommon.Hash, error)
// ExtractSecretResponses will return all secret response tx from an L1 block
ExtractSecretResponses(block *types.Block) []*ethadapter.L1RespondSecretTx
// ExtractRollupTxs will return all rollup txs from an L1 block
ExtractRollupTxs(block *types.Block) []*ethadapter.L1RollupTx
// ExtractObscuroRelevantTransactions will return all Obscuro relevant tx from an L1 block
ExtractObscuroRelevantTransactions(block *types.Block) ([]*ethadapter.L1RespondSecretTx, []*ethadapter.L1RollupTx, []*ethadapter.L1SetImportantContractsTx)
// PublishRollup will create and publish a rollup tx to the management contract - fire and forget we don't wait for receipt
// todo (#1624) - With a single sequencer, it is problematic if rollup publication fails; handle this case better
PublishRollup(producedRollup *common.ExtRollup)
Expand All @@ -114,6 +112,11 @@ type L1Publisher interface {
FetchLatestPeersList() ([]string, error)

FetchLatestSeqNo() (*big.Int, error)

// GetImportantContracts returns a (cached) record of addresses of the important network contracts
GetImportantContracts() map[string]gethcommon.Address
// ResyncImportantContracts will fetch the latest important contracts from the management contract, update the cache
ResyncImportantContracts() error
}

// L2BatchRepository provides an interface for the host to request L2 batch data (live-streaming and historical)
Expand Down
1 change: 1 addition & 0 deletions go/common/query_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,5 @@ type ObscuroNetworkInfo struct {
L1StartHash common.Hash
SequencerID common.Address
MessageBusAddress common.Address
ImportantContracts map[string]common.Address // map of contract name to address
}
5 changes: 5 additions & 0 deletions go/ethadapter/l1_transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ type L1RespondSecretTx struct {
HostAddress string
}

type L1SetImportantContractsTx struct {
Key string
NewAddress gethcommon.Address
}

// Sign signs the payload with a given private key
func (l *L1RespondSecretTx) Sign(privateKey *ecdsa.PrivateKey) *L1RespondSecretTx {
var data []byte
Expand Down
13 changes: 8 additions & 5 deletions go/ethadapter/mgmtcontractlib/mgmt_contract_ABI.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package mgmtcontractlib
import "github.com/ten-protocol/go-ten/contracts/generated/ManagementContract"

const (
AddRollupMethod = "AddRollup"
RespondSecretMethod = "RespondNetworkSecret"
RequestSecretMethod = "RequestNetworkSecret"
InitializeSecretMethod = "InitializeNetworkSecret" //#nosec
GetHostAddressesMethod = "GetHostAddresses"
AddRollupMethod = "AddRollup"
RespondSecretMethod = "RespondNetworkSecret"
RequestSecretMethod = "RequestNetworkSecret"
InitializeSecretMethod = "InitializeNetworkSecret" //#nosec
GetHostAddressesMethod = "GetHostAddresses"
GetImportantContractKeysMethod = "GetImportantContractKeys"
SetImportantContractsMethod = "SetImportantContractAddress"
GetImportantAddressMethod = "importantContractAddresses"
)

var MgmtContractABI = ManagementContract.ManagementContractMetaData.ABI
161 changes: 147 additions & 14 deletions go/ethadapter/mgmtcontractlib/mgmt_contract_lib.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,23 @@ type MgmtContractLib interface {
CreateRequestSecret(tx *ethadapter.L1RequestSecretTx) types.TxData
CreateRespondSecret(tx *ethadapter.L1RespondSecretTx, verifyAttester bool) types.TxData
CreateInitializeSecret(tx *ethadapter.L1InitializeSecretTx) types.TxData
GetHostAddresses() (ethereum.CallMsg, error)

// DecodeTx receives a *types.Transaction and converts it to an common.L1Transaction
DecodeTx(tx *types.Transaction) ethadapter.L1Transaction
// DecodeCallResponse unpacks a call response into a slice of strings.
DecodeCallResponse(callResponse []byte) ([][]string, error)
GetContractAddr() *gethcommon.Address

// The methods below are used to create call messages for mgmt contract data and unpack the responses

GetHostAddressesMsg() (ethereum.CallMsg, error)
DecodeHostAddressesResponse(callResponse []byte) ([]string, error)

SetImportantContractMsg(key string, address gethcommon.Address) (ethereum.CallMsg, error)

GetImportantContractKeysMsg() (ethereum.CallMsg, error)
DecodeImportantContractKeysResponse(callResponse []byte) ([]string, error)

GetImportantAddressCallMsg(key string) (ethereum.CallMsg, error)
DecodeImportantAddressResponse(callResponse []byte) (gethcommon.Address, error)
}

type contractLibImpl struct {
Expand Down Expand Up @@ -93,6 +103,14 @@ func (c *contractLibImpl) DecodeTx(tx *types.Transaction) ethadapter.L1Transacti

case InitializeSecretMethod:
return c.unpackInitSecretTx(tx, method, contractCallData)

case SetImportantContractsMethod:
tx, err := c.unpackSetImportantContractsTx(tx, method, contractCallData)
if err != nil {
c.logger.Warn("could not unpack set important contracts tx", log.ErrKey, err)
return nil
}
return tx
}

return nil
Expand Down Expand Up @@ -180,31 +198,116 @@ func (c *contractLibImpl) CreateInitializeSecret(tx *ethadapter.L1InitializeSecr
}
}

func (c *contractLibImpl) GetHostAddresses() (ethereum.CallMsg, error) {
func (c *contractLibImpl) GetHostAddressesMsg() (ethereum.CallMsg, error) {
data, err := c.contractABI.Pack(GetHostAddressesMethod)
if err != nil {
return ethereum.CallMsg{}, fmt.Errorf("could not pack the call data. Cause: %w", err)
}
return ethereum.CallMsg{To: c.addr, Data: data}, nil
}

func (c *contractLibImpl) DecodeCallResponse(callResponse []byte) ([][]string, error) {
func (c *contractLibImpl) DecodeHostAddressesResponse(callResponse []byte) ([]string, error) {
unpackedResponse, err := c.contractABI.Unpack(GetHostAddressesMethod, callResponse)
if err != nil {
return nil, fmt.Errorf("could not unpack call response. Cause: %w", err)
}

// We convert the returned interfaces to strings.
unpackedResponseStrings := make([][]string, 0, len(unpackedResponse))
for _, obj := range unpackedResponse {
str, ok := obj.([]string)
if !ok {
return nil, fmt.Errorf("could not convert interface in call response to string")
}
unpackedResponseStrings = append(unpackedResponseStrings, str)
// We expect the response to be a list containing one element, that element is a list of address strings
if len(unpackedResponse) != 1 {
return nil, fmt.Errorf("unexpected number of results (%d) returned from call, response: %s", len(unpackedResponse), unpackedResponse)
}
addresses, ok := unpackedResponse[0].([]string)
if !ok {
return nil, fmt.Errorf("could not convert element in call response to list of strings")
}

return addresses, nil
}

func (c *contractLibImpl) GetContractNamesMsg() (ethereum.CallMsg, error) {
data, err := c.contractABI.Pack(GetImportantContractKeysMethod)
if err != nil {
return ethereum.CallMsg{}, fmt.Errorf("could not pack the call data. Cause: %w", err)
}
return ethereum.CallMsg{To: c.addr, Data: data}, nil
}

func (c *contractLibImpl) DecodeContractNamesResponse(callResponse []byte) ([]string, error) {
unpackedResponse, err := c.contractABI.Unpack(GetImportantContractKeysMethod, callResponse)
if err != nil {
return nil, fmt.Errorf("could not unpack call response. Cause: %w", err)
}

// We expect the response to be a list containing one element, that element is a list of address strings
if len(unpackedResponse) != 1 {
return nil, fmt.Errorf("unexpected number of results (%d) returned from call, response: %s", len(unpackedResponse), unpackedResponse)
}
contractNames, ok := unpackedResponse[0].([]string)
if !ok {
return nil, fmt.Errorf("could not convert element in call response to list of strings")
}

return contractNames, nil
}

func (c *contractLibImpl) SetImportantContractMsg(key string, address gethcommon.Address) (ethereum.CallMsg, error) {
data, err := c.contractABI.Pack(SetImportantContractsMethod, key, address)
if err != nil {
return ethereum.CallMsg{}, fmt.Errorf("could not pack the call data. Cause: %w", err)
}
return ethereum.CallMsg{To: c.addr, Data: data}, nil
}

return unpackedResponseStrings, nil
func (c *contractLibImpl) GetImportantContractKeysMsg() (ethereum.CallMsg, error) {
data, err := c.contractABI.Pack(GetImportantContractKeysMethod)
if err != nil {
return ethereum.CallMsg{}, fmt.Errorf("could not pack the call data. Cause: %w", err)
}
return ethereum.CallMsg{To: c.addr, Data: data}, nil
}

func (c *contractLibImpl) DecodeImportantContractKeysResponse(callResponse []byte) ([]string, error) {
unpackedResponse, err := c.contractABI.Unpack(GetImportantContractKeysMethod, callResponse)
if err != nil {
return nil, fmt.Errorf("could not unpack call response. Cause: %w", err)
}

// We expect the response to be a list containing one element, that element is a list of address strings
if len(unpackedResponse) != 1 {
return nil, fmt.Errorf("unexpected number of results (%d) returned from call, response: %s", len(unpackedResponse), unpackedResponse)
}
contractNames, ok := unpackedResponse[0].([]string)
if !ok {
return nil, fmt.Errorf("could not convert element in call response to list of strings")
}

return contractNames, nil
}

func (c *contractLibImpl) GetImportantAddressCallMsg(key string) (ethereum.CallMsg, error) {
data, err := c.contractABI.Pack(GetImportantAddressMethod, key)
if err != nil {
return ethereum.CallMsg{}, fmt.Errorf("could not pack the call data. Cause: %w", err)
}
return ethereum.CallMsg{To: c.addr, Data: data}, nil
}

func (c *contractLibImpl) DecodeImportantAddressResponse(callResponse []byte) (gethcommon.Address, error) {
unpackedResponse, err := c.contractABI.Unpack(GetImportantAddressMethod, callResponse)
if err != nil {
return gethcommon.Address{}, fmt.Errorf("could not unpack call response. Cause: %w", err)
}

// We expect the response to be a list containing one element, that element is a list of address strings
if len(unpackedResponse) != 1 {
return gethcommon.Address{}, fmt.Errorf("unexpected number of results (%d) returned from call, response: %s", len(unpackedResponse), unpackedResponse)
}
address, ok := unpackedResponse[0].(gethcommon.Address)
if !ok {
return gethcommon.Address{}, fmt.Errorf("could not convert element in call response to list of strings")
}

return address, nil
}

func (c *contractLibImpl) unpackInitSecretTx(tx *types.Transaction, method *abi.Method, contractCallData map[string]interface{}) *ethadapter.L1InitializeSecretTx {
Expand Down Expand Up @@ -297,6 +400,36 @@ func (c *contractLibImpl) unpackRespondSecretTx(tx *types.Transaction, method *a
}
}

func (c *contractLibImpl) unpackSetImportantContractsTx(tx *types.Transaction, method *abi.Method, contractCallData map[string]interface{}) (*ethadapter.L1SetImportantContractsTx, error) {
err := method.Inputs.UnpackIntoMap(contractCallData, tx.Data()[methodBytesLen:])
if err != nil {
return nil, fmt.Errorf("could not unpack transaction. Cause: %w", err)
}

keyData, found := contractCallData["key"]
if !found {
return nil, fmt.Errorf("call data not found for key")
}
keyString, ok := keyData.(string)
if !ok {
return nil, fmt.Errorf("could not decode key data")
}

contractAddressData, found := contractCallData["newAddress"]
if !found {
return nil, fmt.Errorf("call data not found for newAddress")
}
contractAddress, ok := contractAddressData.(gethcommon.Address)
if !ok {
return nil, fmt.Errorf("could not decode newAddress data")
}

return &ethadapter.L1SetImportantContractsTx{
Key: keyString,
NewAddress: contractAddress,
}, nil
}

// base64EncodeToString encodes a byte array to a string
func base64EncodeToString(bytes []byte) string {
return base64.StdEncoding.EncodeToString(bytes)
Expand Down
19 changes: 12 additions & 7 deletions go/host/enclave/guardian.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,7 @@ func (g *Guardian) provideSecret() error {
if err != nil {
return fmt.Errorf("next block after block=%s not found - %w", awaitFromBlock, err)
}
secretRespTxs := g.sl.L1Publisher().ExtractSecretResponses(nextBlock)
if err != nil {
return fmt.Errorf("could not extract secret responses from block=%s - %w", nextBlock.Hash(), err)
}
secretRespTxs, _, _ := g.sl.L1Publisher().ExtractObscuroRelevantTransactions(nextBlock)
for _, scrt := range secretRespTxs {
if scrt.RequesterID.Hex() == g.hostData.ID.Hex() {
err = g.enclaveClient.InitEnclave(scrt.Secret)
Expand Down Expand Up @@ -435,13 +432,12 @@ func (g *Guardian) submitL1Block(block *common.L1Block, isLatest bool) (bool, er

func (g *Guardian) processL1BlockTransactions(block *common.L1Block) {
// if there are any secret responses in the block we should refresh our P2P list to re-sync with the network
respTxs := g.sl.L1Publisher().ExtractSecretResponses(block)
if len(respTxs) > 0 {
secretRespTxs, rollupTxs, contractAddressTxs := g.sl.L1Publisher().ExtractObscuroRelevantTransactions(block)
if len(secretRespTxs) > 0 {
// new peers may have been granted access to the network, notify p2p service to refresh its peer list
go g.sl.P2P().RefreshPeerList()
}

rollupTxs := g.sl.L1Publisher().ExtractRollupTxs(block)
for _, rollup := range rollupTxs {
r, err := common.DecodeRollup(rollup.Rollup)
if err != nil {
Expand All @@ -456,6 +452,15 @@ func (g *Guardian) processL1BlockTransactions(block *common.L1Block) {
}
}
}

if len(contractAddressTxs) > 0 {
go func() {
err := g.sl.L1Publisher().ResyncImportantContracts()
if err != nil {
g.logger.Error("Could not resync important contracts", log.ErrKey, err)
}
}()
}
}

func (g *Guardian) publishSharedSecretResponses(scrtResponses []*common.ProducedSecretResponse) error {
Expand Down
5 changes: 3 additions & 2 deletions go/host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,8 +204,9 @@ func (h *host) ObscuroConfig() (*common.ObscuroNetworkInfo, error) {
ManagementContractAddress: h.config.ManagementContractAddress,
L1StartHash: h.config.L1StartHash,

SequencerID: h.config.SequencerID,
MessageBusAddress: h.config.MessageBusAddress,
SequencerID: h.config.SequencerID,
MessageBusAddress: h.config.MessageBusAddress,
ImportantContracts: h.services.L1Publisher().GetImportantContracts(),
}, nil
}

Expand Down
Loading

0 comments on commit 1248086

Please sign in to comment.