Skip to content

Commit

Permalink
Fix CLI commands to handle blockchain operations (#217)
Browse files Browse the repository at this point in the history
- add 3 new commands, mark healthy, mark unhealthy and update address
- print address in get-pub-key for debugging purposes
- change local keys to funded anvil key pairs
- get-nodes no longer needs a key since its accessing public info
  • Loading branch information
mkysel authored Oct 11, 2024
1 parent a29e144 commit cc8dbbe
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 26 deletions.
109 changes: 97 additions & 12 deletions cmd/cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ type globalOptions struct {

type CLI struct {
globalOptions
Command string
GetPubKey config.GetPubKeyOptions
GenerateKey config.GenerateKeyOptions
RegisterNode config.RegisterNodeOptions
GetAllNodes config.GetAllNodesOptions
Command string
GetPubKey config.GetPubKeyOptions
GenerateKey config.GenerateKeyOptions
RegisterNode config.RegisterNodeOptions
GetAllNodes config.GetAllNodesOptions
UpdateHealth config.UpdateHealthOptions
UpdateAddress config.UpdateAddressOptions
}

/*
Expand All @@ -44,6 +46,8 @@ func parseOptions(args []string) (*CLI, error) {
var registerNodeOptions config.RegisterNodeOptions
var getPubKeyOptions config.GetPubKeyOptions
var getAllNodesOptions config.GetAllNodesOptions
var updateHealthOptions config.UpdateHealthOptions
var updateAddressOptions config.UpdateAddressOptions

parser := flags.NewParser(&options, flags.Default)
if _, err := parser.AddCommand("generate-key", "Generate a public/private keypair", "", &generateKeyOptions); err != nil {
Expand All @@ -58,6 +62,15 @@ func parseOptions(args []string) (*CLI, error) {
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)
}
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)
}
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)
}
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)
}
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)
Expand All @@ -76,6 +89,8 @@ func parseOptions(args []string) (*CLI, error) {
generateKeyOptions,
registerNodeOptions,
getAllNodesOptions,
updateHealthOptions,
updateAddressOptions,
}, nil
}

Expand All @@ -87,7 +102,9 @@ func getPubKey(logger *zap.Logger, options *CLI) {
logger.Info(
"parsed private key",
zap.String("pub-key", utils.EcdsaPublicKeyToString(privKey.Public().(*ecdsa.PublicKey))),
zap.String("address", utils.EcdsaPublicKeyToAddress(privKey.Public().(*ecdsa.PublicKey))),
)
privKey.Public()
}
func registerNode(logger *zap.Logger, options *CLI) {
ctx := context.Background()
Expand Down Expand Up @@ -156,8 +173,36 @@ func getAllNodes(logger *zap.Logger, options *CLI) {
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 updateHealth(logger *zap.Logger, options *CLI, health bool) {
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.GetAllNodes.AdminPrivateKey,
options.UpdateHealth.AdminPrivateKey,
options.Contracts.ChainID,
)

Expand All @@ -175,15 +220,46 @@ func getAllNodes(logger *zap.Logger, options *CLI) {
logger.Fatal("could not create registry admin", zap.Error(err))
}

nodes, err := registryAdmin.GetAllNodes(ctx)
err = registryAdmin.UpdateHealth(ctx, options.UpdateHealth.NodeId, health)
if err != nil {
logger.Fatal("could not retrieve nodes from registry", zap.Error(err))
logger.Fatal("could not update node health in registry", zap.Error(err))
}
logger.Info(
"got nodes",
zap.Int("size", len(nodes)),
zap.Any("nodes", nodes),
}

func updateAddress(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.UpdateAddress.PrivateKey,
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.UpdateHttpAddress(
ctx,
options.UpdateAddress.NodeId,
options.UpdateAddress.Address,
)
if err != nil {
logger.Fatal("could not update node address in registry", zap.Error(err))
}
}

func main() {
Expand Down Expand Up @@ -212,6 +288,15 @@ func main() {
case "get-all-nodes":
getAllNodes(logger, options)
return
case "mark-healthy":
updateHealth(logger, options, true)
return
case "mark-unhealthy":
updateHealth(logger, options, false)
return
case "update-address":
updateAddress(logger, options)
return
}

}
1 change: 1 addition & 0 deletions cmd/replication/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ func main() {
if err != nil {
fatal("Could not build logger: %s", err)
}
logger = logger.Named("replication")

logger.Info(fmt.Sprintf("Version: %s", Commit))
if options.Tracing.Enable {
Expand Down
14 changes: 11 additions & 3 deletions dev/local.env
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,18 @@ export XMTPD_CONTRACTS_MESSAGES_ADDRESS
XMTPD_CONTRACTS_IDENTITY_UPDATES_ADDRESS="$(jq -r '.deployedTo' build/IdentityUpdates.json)" # Built by contracts/deploy-local
export XMTPD_CONTRACTS_IDENTITY_UPDATES_ADDRESS

export ANVIL_ACC_1_PRIVATE_KEY="0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d"
export ANVIL_ACC_1_PUBLIC_KEY="0x02ba5734d8f7091719471e7f7ed6b9df170dc70cc661ca05e688601ad984f068b0"
export ANVIL_ACC_1_ADDRESS="0x70997970C51812dc3A010C7d01b50e0d17dc79C8"

export ANVIL_ACC_2_PRIVATE_KEY="0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a"
export ANVIL_ACC_2_PUBLIC_KEY="0x039d9031e97dd78ff8c15aa86939de9b1e791066a0224e331bc962a2099a7b1f04"
export ANVIL_ACC_2_ADDRESS="0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC"


# Top Level Options Default Node
# {"private-key": "0x5742e57f960fbe62d0823b678df466b9e7d6c0c52888eacf3c31c132c645fd34", "public-key": "0x0202fd5e60ea5b9324431ec567a6e2655c4dc7160bba2ee5f8cc07ffc86a28342b"}
export XMTPD_SIGNER_PRIVATE_KEY="0x5742e57f960fbe62d0823b678df466b9e7d6c0c52888eacf3c31c132c645fd34" # From contracts/.env
export XMTPD_SIGNER_PRIVATE_KEY=$ANVIL_ACC_1_PRIVATE_KEY
export XMTPD_PAYER_PRIVATE_KEY=$XMTPD_SIGNER_PRIVATE_KEY
export XMTPD_MLS_VALIDATION_GRPC_ADDRESS="localhost:60051"

export XMTPD_SIGNER_PUBLIC_KEY="0x0202fd5e60ea5b9324431ec567a6e2655c4dc7160bba2ee5f8cc07ffc86a28342b"
export XMTPD_SIGNER_PUBLIC_KEY=$ANVIL_ACC_1_PUBLIC_KEY
4 changes: 3 additions & 1 deletion dev/register-local-node
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ set -eu

. dev/local.env

export NODE_ADDRESS=$ANVIL_ACC_1_ADDRESS

dev/cli register-node \
--http-address=http://localhost:5050 \
--owner-address=0xf0490b45884803924Ca84C2051ef435991D7350D \
--owner-address=$NODE_ADDRESS \
--admin-private-key=$PRIVATE_KEY \
--signing-key-pub=$XMTPD_SIGNER_PUBLIC_KEY
7 changes: 5 additions & 2 deletions dev/register-local-node-2
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@ set -eu

. dev/local.env

export XMTPD_SIGNER_PUBLIC_KEY=$ANVIL_ACC_2_PUBLIC_KEY
export NODE_ADDRESS=$ANVIL_ACC_2_ADDRESS

dev/cli register-node \
--http-address=http://localhost:5051 \
--owner-address=0xf0490b45884803924Ca84C2051ef435991D7350D \
--owner-address=$NODE_ADDRESS \
--admin-private-key=$PRIVATE_KEY \
--signing-key-pub=0x03da7f733d870237f6dfd0074aea27edaf7b840d68e88641fb2a687de16bbe6a2b
--signing-key-pub=$XMTPD_SIGNER_PUBLIC_KEY
4 changes: 1 addition & 3 deletions dev/run-2
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ set -eu

. dev/local.env

# second node keys
# {"private-key": "0x3c3040ee266ccd22f14c555b71093ea586e46226f1ed8d37cf9fb239d2b4ad79", "public-key": "0x03da7f733d870237f6dfd0074aea27edaf7b840d68e88641fb2a687de16bbe6a2b"}
export XMTPD_SIGNER_PRIVATE_KEY="0x3c3040ee266ccd22f14c555b71093ea586e46226f1ed8d37cf9fb239d2b4ad79"
export XMTPD_SIGNER_PRIVATE_KEY=$ANVIL_ACC_2_PRIVATE_KEY
export XMTPD_PAYER_PRIVATE_KEY=$XMTPD_SIGNER_PRIVATE_KEY
export XMTPD_DB_WRITER_CONNECTION_STRING="postgres://postgres:xmtp@localhost:8766/postgres?sslmode=disable"

Expand Down
92 changes: 91 additions & 1 deletion pkg/blockchain/registryAdmin.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"time"

"github.com/ethereum/go-ethereum/accounts/abi/bind"
Expand Down Expand Up @@ -62,6 +63,9 @@ func (n *NodeRegistryAdmin) AddNode(
ownerAddress := common.HexToAddress(owner)
signingKey := crypto.FromECDSAPub(signingKeyPub)

if n.signer == nil {
return fmt.Errorf("No signer provided")
}
tx, err := n.contract.AddNode(&bind.TransactOpts{
Context: ctx,
From: n.signer.FromAddress(),
Expand All @@ -81,11 +85,97 @@ func (n *NodeRegistryAdmin) AddNode(
tx.Hash(),
)
}
func (n *NodeRegistryAdmin) GetAllNodes(

/*
*
A NodeRegistryCaller is a struct responsible for calling public functions on the node registry
*
*/
type NodeRegistryCaller struct {
client *ethclient.Client
contract *abis.NodesCaller
logger *zap.Logger
}

func NewNodeRegistryCaller(
logger *zap.Logger,
client *ethclient.Client,
contractsOptions config.ContractsOptions,
) (*NodeRegistryCaller, error) {
contract, err := abis.NewNodesCaller(
common.HexToAddress(contractsOptions.NodesContractAddress),
client,
)
if err != nil {
return nil, err
}

return &NodeRegistryCaller{
client: client,
logger: logger.Named("NodeRegistryAdmin"),
contract: contract,
}, nil
}

func (n *NodeRegistryCaller) GetAllNodes(
ctx context.Context,
) ([]abis.NodesNodeWithId, error) {

return n.contract.AllNodes(&bind.CallOpts{
Context: ctx,
})
}

func (n *NodeRegistryAdmin) UpdateHealth(
ctx context.Context, nodeId int64, health bool,
) error {
tx, err := n.contract.UpdateHealth(
&bind.TransactOpts{
Context: ctx,
From: n.signer.FromAddress(),
Signer: n.signer.SignerFunc(),
},
big.NewInt(nodeId),
health,
)

if err != nil {
return err
}

return WaitForTransaction(
ctx,
n.logger,
n.client,
2*time.Second,
250*time.Millisecond,
tx.Hash(),
)
}

func (n *NodeRegistryAdmin) UpdateHttpAddress(
ctx context.Context, nodeId int64, address string,
) error {
tx, err := n.contract.UpdateHttpAddress(
&bind.TransactOpts{
Context: ctx,
From: n.signer.FromAddress(),
Signer: n.signer.SignerFunc(),
},
big.NewInt(nodeId),
address,
)

if err != nil {
return err
}

return WaitForTransaction(
ctx,
n.logger,
n.client,
2*time.Second,
250*time.Millisecond,
tx.Hash(),
)
}
13 changes: 11 additions & 2 deletions pkg/config/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,17 @@ type SignerOptions struct {

type GenerateKeyOptions struct{}

type GetAllNodesOptions struct {
AdminPrivateKey string `long:"admin-private-key" description:"Private key of the admin to register the node"`
type GetAllNodesOptions struct{}

type UpdateHealthOptions struct {
AdminPrivateKey string `long:"admin-private-key" description:"Private key of the admin to administer the node"`
NodeId int64 `long:"node-id" description:"NodeId to update"`
}

type UpdateAddressOptions struct {
PrivateKey string `long:"private-key" description:"Private key of node to be updated"`
NodeId int64 `long:"node-id" description:"NodeId to update"`
Address string `long:"address" description:"New HTTP address"`
}

type GetPubKeyOptions struct {
Expand Down
4 changes: 4 additions & 0 deletions pkg/utils/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ func ParseEcdsaPublicKey(key string) (*ecdsa.PublicKey, error) {
func EcdsaPublicKeyToString(key *ecdsa.PublicKey) string {
return "0x" + common.Bytes2Hex(crypto.CompressPubkey(key))
}

func EcdsaPublicKeyToAddress(key *ecdsa.PublicKey) string {
return "0x" + common.Bytes2Hex(crypto.PubkeyToAddress(*key).Bytes())
}
2 changes: 0 additions & 2 deletions pkg/utils/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,5 @@ func BuildLogger(options config.LogOptions) (*zap.Logger, *zap.Config, error) {
return nil, nil, err
}

logger = logger.Named("replication")

return logger, &cfg, nil
}

0 comments on commit cc8dbbe

Please sign in to comment.