Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: integrate new nodes contract with xmtpd #550

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/cli/cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func TestRegisterNodeArgParse(t *testing.T) {
require.Equal(
t,
err.Error(),
"Could not parse options: the required flags `--admin-private-key', `--http-address', `--node-owner-address' and `--node-signing-key-pub' were not specified",
"could not parse options: the required flags `--admin-private-key', `--http-address', `--node-owner-address' and `--node-signing-key-pub' were not specified",
)
}

Expand Down
194 changes: 152 additions & 42 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ var Version string = "unknown"

type CLI struct {
config.GlobalOptions
Command string
GetPubKey config.GetPubKeyOptions
GenerateKey config.GenerateKeyOptions
RegisterNode config.RegisterNodeOptions
GetAllNodes config.GetAllNodesOptions
UpdateHealth config.UpdateHealthOptions
UpdateAddress config.UpdateAddressOptions
Command string
GetPubKey config.GetPubKeyOptions
GenerateKey config.GenerateKeyOptions
RegisterNode config.RegisterNodeOptions
UpdateActive config.UpdateActiveOptions
GetAllNodes config.GetAllNodesOptions
UpdateHealth config.UpdateHealthOptions
UpdateAddress config.UpdateAddressOptions
UpdateApiEnabled config.UpdateApiEnabledOptions
UpdateReplicationEnabled config.UpdateReplicationEnabledOptions
}

/*
Expand All @@ -42,42 +45,54 @@ func parseOptions(args []string) (*CLI, error) {
var options config.GlobalOptions
var generateKeyOptions config.GenerateKeyOptions
var registerNodeOptions config.RegisterNodeOptions
var updateActiveOptions config.UpdateActiveOptions
var getPubKeyOptions config.GetPubKeyOptions
var getAllNodesOptions config.GetAllNodesOptions
var updateHealthOptions config.UpdateHealthOptions
var updateAddressOptions config.UpdateAddressOptions
var updateApiEnabledOptions config.UpdateApiEnabledOptions
var updateReplicationEnabledOptions config.UpdateReplicationEnabledOptions

parser := flags.NewParser(&options, flags.Default)
if _, err := parser.AddCommand("generate-key", "Generate a public/private keypair", "", &generateKeyOptions); err != nil {
return nil, fmt.Errorf("Could not add generate-key command: %s", err)
return nil, fmt.Errorf("could not add generate-key command: %s", err)
}
if _, err := parser.AddCommand("register-node", "Register a node", "", &registerNodeOptions); err != nil {
return nil, fmt.Errorf("Could not add register-node command: %s", err)
return nil, fmt.Errorf("could not add register-node command: %s", err)
}
if _, err := parser.AddCommand("update-active", "Update the active status of a node", "", &updateActiveOptions); err != nil {
return nil, fmt.Errorf("could not add update-active command: %s", err)
}
if _, err := parser.AddCommand("update-api-enabled", "Update the API enabled status of a node", "", &updateApiEnabledOptions); err != nil {
return nil, fmt.Errorf("could not add update-api-enabled command: %s", err)
}
if _, err := parser.AddCommand("update-replication-enabled", "Update the replication enabled status of a node", "", &updateReplicationEnabledOptions); err != nil {
return nil, fmt.Errorf("could not add update-replication-enabled command: %s", err)
}
if _, err := parser.AddCommand("get-pub-key", "Get the public key for a private key", "", &getPubKeyOptions); err != nil {
return nil, fmt.Errorf("Could not add get-pub-key command: %s", err)
return nil, fmt.Errorf("could not add get-pub-key command: %s", err)
}
if _, err := parser.AddCommand("get-all-nodes", "Get all nodes from the registry", "", &getAllNodesOptions); err != nil {
return nil, fmt.Errorf("Could not add get-all-nodes command: %s", err)
return nil, fmt.Errorf("could not add get-all-nodes command: %s", err)
}
if _, err := parser.AddCommand("mark-healthy", "Mark a node as healthy in the registry", "", &updateHealthOptions); err != nil {
return nil, fmt.Errorf("Could not add mark-healthy command: %s", err)
return nil, fmt.Errorf("could not add mark-healthy command: %s", err)
}
if _, err := parser.AddCommand("mark-unhealthy", "Mark a node as unhealthy in the registry", "", &updateHealthOptions); err != nil {
return nil, fmt.Errorf("Could not add mark-unhealthy command: %s", err)
return nil, fmt.Errorf("could not add mark-unhealthy command: %s", err)
}
if _, err := parser.AddCommand("update-address", "Update HTTP address of a node", "", &updateAddressOptions); err != nil {
return nil, fmt.Errorf("Could not add update-address command: %s", err)
return nil, fmt.Errorf("could not add update-address command: %s", err)
}
if _, err := parser.ParseArgs(args); err != nil {
if err, ok := err.(*flags.Error); !ok || err.Type != flags.ErrHelp {
return nil, fmt.Errorf("Could not parse options: %s", err)
return nil, fmt.Errorf("could not parse options: %s", err)
}
return nil, nil
}

if parser.Active == nil {
return nil, errors.New("No command provided")
return nil, errors.New("no command provided")
}

return &CLI{
Expand All @@ -86,9 +101,12 @@ func parseOptions(args []string) (*CLI, error) {
getPubKeyOptions,
generateKeyOptions,
registerNodeOptions,
updateActiveOptions,
getAllNodesOptions,
updateHealthOptions,
updateAddressOptions,
updateApiEnabledOptions,
updateReplicationEnabledOptions,
}, nil
}

Expand All @@ -104,6 +122,7 @@ func getPubKey(logger *zap.Logger, options *CLI) {
)
privKey.Public()
}

func registerNode(logger *zap.Logger, options *CLI) {
ctx := context.Background()
chainClient, err := blockchain.NewClient(ctx, options.Contracts.RpcUrl)
Expand Down Expand Up @@ -135,7 +154,7 @@ func registerNode(logger *zap.Logger, options *CLI) {
logger.Fatal("could not decompress public key", zap.Error(err))
}

err = registryAdmin.AddNode(
nodeId, err := registryAdmin.AddNode(
ctx,
options.RegisterNode.OwnerAddress,
signingKeyPub,
Expand All @@ -149,59 +168,98 @@ func registerNode(logger *zap.Logger, options *CLI) {
zap.String("node-owner-address", options.RegisterNode.OwnerAddress),
zap.String("node-http-address", options.RegisterNode.HttpAddress),
zap.String("node-signing-key-pub", utils.EcdsaPublicKeyToString(signingKeyPub)),
zap.Uint32("node-id", nodeId),
)
}

func generateKey(logger *zap.Logger) {
privKey, err := utils.GenerateEcdsaPrivateKey()
func updateActive(logger *zap.Logger, options *CLI) {
ctx := context.Background()
chainClient, err := blockchain.NewClient(ctx, options.Contracts.RpcUrl)
if err != nil {
logger.Fatal("could not generate private key", zap.Error(err))
logger.Fatal("could not create chain client", zap.Error(err))
}

signer, err := blockchain.NewPrivateKeySigner(
options.UpdateActive.AdminPrivateKey,
options.Contracts.ChainID,
)

if err != nil {
logger.Fatal("could not create signer", zap.Error(err))
}

registryAdmin, err := blockchain.NewNodeRegistryAdmin(
logger,
chainClient,
signer,
options.Contracts,
)
if err != nil {
logger.Fatal("could not create registry admin", zap.Error(err))
}

err = registryAdmin.UpdateActive(
ctx,
uint32(options.UpdateActive.NodeId),
options.UpdateActive.IsActive,
)
if err != nil {
logger.Fatal("could not update node active", zap.Error(err))
}
logger.Info(
"generated private key",
zap.String("private-key", utils.EcdsaPrivateKeyToString(privKey)),
zap.String("public-key", utils.EcdsaPublicKeyToString(privKey.Public().(*ecdsa.PublicKey))),
zap.String("address", utils.EcdsaPublicKeyToAddress(privKey.Public().(*ecdsa.PublicKey))),
"successfully updated node active",
zap.Uint32("node-id", uint32(options.UpdateActive.NodeId)),
)
}

func getAllNodes(logger *zap.Logger, options *CLI) {
func updateApiEnabled(logger *zap.Logger, options *CLI) {
ctx := context.Background()
chainClient, err := blockchain.NewClient(ctx, options.Contracts.RpcUrl)
if err != nil {
logger.Fatal("could not create chain client", zap.Error(err))
}

caller, err := blockchain.NewNodeRegistryCaller(
signer, err := blockchain.NewPrivateKeySigner(
options.UpdateApiEnabled.OperatorPrivateKey,
options.Contracts.ChainID,
)

if err != nil {
logger.Fatal("could not create signer", zap.Error(err))
}

registryAdmin, err := blockchain.NewNodeRegistryAdmin(
logger,
chainClient,
signer,
options.Contracts,
)
if err != nil {
logger.Fatal("could not create registry admin", zap.Error(err))
}

nodes, err := caller.GetAllNodes(ctx)
err = registryAdmin.UpdateIsApiEnabled(
ctx,
uint32(options.UpdateApiEnabled.NodeId),
)
if err != nil {
logger.Fatal("could not retrieve nodes from registry", zap.Error(err))
logger.Fatal("could not update node api enabled", zap.Error(err))
}

logger.Info(
"got nodes",
zap.Int("size", len(nodes)),
zap.Any("nodes", nodes),
"successfully updated node api enabled",
zap.Uint32("node-id", uint32(options.UpdateApiEnabled.NodeId)),
)
}

func updateHealth(logger *zap.Logger, options *CLI, health bool) {
func updateReplicationEnabled(logger *zap.Logger, options *CLI) {
ctx := context.Background()
chainClient, err := blockchain.NewClient(ctx, options.Contracts.RpcUrl)
if err != nil {
logger.Fatal("could not create chain client", zap.Error(err))
}

signer, err := blockchain.NewPrivateKeySigner(
options.UpdateHealth.AdminPrivateKey,
options.UpdateReplicationEnabled.AdminPrivateKey,
options.Contracts.ChainID,
)

Expand All @@ -219,10 +277,59 @@ func updateHealth(logger *zap.Logger, options *CLI, health bool) {
logger.Fatal("could not create registry admin", zap.Error(err))
}

err = registryAdmin.UpdateHealth(ctx, options.UpdateHealth.NodeId, health)
err = registryAdmin.UpdateIsReplicationEnabled(
ctx,
uint32(options.UpdateReplicationEnabled.NodeId),
options.UpdateReplicationEnabled.IsReplicationEnabled,
)
if err != nil {
logger.Fatal("could not update node health in registry", zap.Error(err))
logger.Fatal("could not update node replication enabled", zap.Error(err))
}
logger.Info(
"successfully updated node replication enabled",
zap.Uint32("node-id", uint32(options.UpdateReplicationEnabled.NodeId)),
)
}

func generateKey(logger *zap.Logger) {
privKey, err := utils.GenerateEcdsaPrivateKey()
if err != nil {
logger.Fatal("could not generate private key", zap.Error(err))
}
logger.Info(
"generated private key",
zap.String("private-key", utils.EcdsaPrivateKeyToString(privKey)),
zap.String("public-key", utils.EcdsaPublicKeyToString(privKey.Public().(*ecdsa.PublicKey))),
zap.String("address", utils.EcdsaPublicKeyToAddress(privKey.Public().(*ecdsa.PublicKey))),
)
}

func getAllNodes(logger *zap.Logger, options *CLI) {
ctx := context.Background()
chainClient, err := blockchain.NewClient(ctx, options.Contracts.RpcUrl)
if err != nil {
logger.Fatal("could not create chain client", zap.Error(err))
}

caller, err := blockchain.NewNodeRegistryCaller(
logger,
chainClient,
options.Contracts,
)
if err != nil {
logger.Fatal("could not create registry admin", zap.Error(err))
}

nodes, err := caller.GetAllNodes(ctx)
if err != nil {
logger.Fatal("could not retrieve nodes from registry", zap.Error(err))
}

logger.Info(
"got nodes",
zap.Int("size", len(nodes)),
zap.Any("nodes", nodes),
)
}

func updateAddress(logger *zap.Logger, options *CLI) {
Expand Down Expand Up @@ -291,14 +398,17 @@ func main() {
case "register-node":
registerNode(logger, options)
return
case "get-all-nodes":
getAllNodes(logger, options)
case "update-active":
updateActive(logger, options)
return
case "mark-healthy":
updateHealth(logger, options, true)
case "update-api-enabled":
updateApiEnabled(logger, options)
return
case "mark-unhealthy":
updateHealth(logger, options, false)
case "update-replication-enabled":
updateReplicationEnabled(logger, options)
return
case "get-all-nodes":
getAllNodes(logger, options)
return
case "update-address":
updateAddress(logger, options)
Expand Down
2 changes: 1 addition & 1 deletion contracts/dev/deploy-local
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ forge_build_contracts
forge_test_contracts
forge_deploy_script group_messages
forge_deploy_script identity_updates
forge_deploy_script nodes src/Nodes.sol Nodes
forge_deploy_script nodes
2 changes: 1 addition & 1 deletion contracts/dev/generate
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function main() {
# Define contracts (pass as arguments or use a default list)
local contracts=("$@")
if [ "${#contracts[@]}" -eq 0 ]; then
contracts=("Nodes" "GroupMessages" "IdentityUpdates" "NodesV2")
contracts=("Nodes" "GroupMessages" "IdentityUpdates")
fi

# Generate bindings for each contract
Expand Down
11 changes: 7 additions & 4 deletions contracts/dev/lib/common
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,15 @@ function forge_deploy_script() {
;;

nodes)
# TODO: Migrate to forge script
echo "⧖ Deploying Nodes to chainId ${chain_id} using RPC ${RPC_URL}"
forge create --broadcast --legacy --json --rpc-url $RPC_URL --private-key $PRIVATE_KEY "$2:$3" > config/anvil_localnet/$3.json
echo -e "\033[32m✔\033[0m Nodes deployed. Deployment details in contracts/config/anvil_localnet/$3.json\n"
;;
forge script --quiet --rpc-url "${RPC_URL}" --broadcast script/DeployNodeRegistry.s.sol || BUILD_FAILED=true
if [[ -n "${BUILD_FAILED:-}" ]]; then
echo "Failed to deploy nodes contract"
exit 1
fi

echo -e "\033[32m✔\033[0m Nodes deployed. Deployment details in contracts/config/anvil_localnet/XMTPNodeRegistry.json\n"
;;
*)
echo "Invalid option. Use 'group_messages', 'identity_updates' or 'nodes'."
exit 1
Expand Down
Loading
Loading