-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
- Loading branch information
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
package registry | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"sync" | ||
"time" | ||
|
||
"github.com/ethereum/go-ethereum/accounts/abi/bind" | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/xmtp/xmtpd/pkg/abis" | ||
"github.com/xmtp/xmtpd/pkg/config" | ||
) | ||
|
||
const ( | ||
CONTRACT_CALL_TIMEOUT = 10 * time.Second | ||
) | ||
|
||
type SmartContractRegistry struct { | ||
contract *abis.NodesCaller | ||
rawNodes map[uint16]abis.NodesNode | ||
rawNodesMutex sync.Mutex | ||
ctx context.Context | ||
} | ||
|
||
func NewSmartContractRegistry(ethclient bind.ContractCaller, options config.ContractsOptions) (*SmartContractRegistry, error) { | ||
contract, err := abis.NewNodesCaller(common.HexToAddress(options.NodesContractAddress), ethclient) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return &SmartContractRegistry{contract: contract}, nil | ||
} | ||
|
||
// Start the registry with the given context. | ||
// To stop the registry, cancel the context | ||
func (s *SmartContractRegistry) Start(ctx context.Context) error { | ||
s.ctx = ctx | ||
// If we can't load the data at least once, fail to start the service | ||
if err := s.refreshData(); err != nil { | ||
return err | ||
} | ||
|
||
go s.refreshLoop() | ||
|
||
return nil | ||
} | ||
|
||
func (s *SmartContractRegistry) refreshLoop() { | ||
ticker := time.NewTicker(1 * time.Minute) | ||
for { | ||
select { | ||
case <-s.ctx.Done(): | ||
return | ||
case <-ticker.C: | ||
s.refreshData() | ||
} | ||
} | ||
} | ||
|
||
func (s *SmartContractRegistry) refreshData() error { | ||
rawNodes, err := s.loadFromContract() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// Lock the mutex to protect against concurrent writes | ||
s.rawNodesMutex.Lock() | ||
defer s.rawNodesMutex.Unlock() | ||
|
||
var hasChanged bool | ||
Check failure on line 71 in pkg/registry/contractRegistry.go GitHub Actions / Lint
|
||
newNodes := []Node{} | ||
changedNodes := []Node{} | ||
for _, rawNodeWithId := range rawNodes { | ||
existingValue, ok := s.rawNodes[rawNodeWithId.NodeId] | ||
if !ok { | ||
// New node found | ||
newNodes = append(newNodes, convertNode(rawNodeWithId)) | ||
} else if !equalRawNodes(existingValue, rawNodeWithId.Node) { | ||
changedNodes = append(changedNodes, convertNode(rawNodeWithId)) | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (s *SmartContractRegistry) loadFromContract() ([]abis.NodesNodeWithId, error) { | ||
ctx, cancel := context.WithTimeout(s.ctx, CONTRACT_CALL_TIMEOUT) | ||
defer cancel() | ||
nodes, err := s.contract.AllNodes(&bind.CallOpts{Context: ctx}) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return nodes, nil | ||
} | ||
|
||
func convertNode(rawNode abis.NodesNodeWithId) Node { | ||
return Node{ | ||
NodeId: rawNode.NodeId, | ||
SigningKey: rawNode.Node.SigningKeyPub, | ||
HttpAddress: rawNode.Node.HttpAddress, | ||
IsHealthy: rawNode.Node.IsHealthy, | ||
} | ||
} | ||
|
||
func equalRawNodes(a abis.NodesNode, b abis.NodesNode) bool { | ||
return bytes.Equal(a.SigningKeyPub, b.SigningKeyPub) && a.HttpAddress == b.HttpAddress && a.IsHealthy == b.IsHealthy && bytes.Equal(a.MtlsCert, b.MtlsCert) | ||
Check failure on line 108 in pkg/registry/contractRegistry.go GitHub Actions / Lint
Check failure on line 108 in pkg/registry/contractRegistry.go GitHub Actions / Lint
Check failure on line 108 in pkg/registry/contractRegistry.go GitHub Actions / Test (Node)
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package registry | ||
|
||
type Node struct { | ||
Check failure on line 3 in pkg/registry/interface.go GitHub Actions / Lint
|
||
NodeId uint16 | ||
SigningKey []byte | ||
HttpAddress string | ||
MtlsCert []byte | ||
IsHealthy bool | ||
} | ||
|
||
type NodeRegistry interface { | ||
Check failure on line 11 in pkg/registry/interface.go GitHub Actions / Lint
|
||
GetNodes() ([]Node, error) | ||
} |