Skip to content

Commit

Permalink
Add registerNodeTx builder and service methods, tx now checks/updates…
Browse files Browse the repository at this point in the history
… AddressStateRegisteredNodeBit
  • Loading branch information
evlekht authored and peak3d committed Dec 30, 2022
1 parent 4cb4b08 commit 35401d5
Show file tree
Hide file tree
Showing 11 changed files with 287 additions and 81 deletions.
77 changes: 77 additions & 0 deletions vms/platformvm/camino_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/ava-labs/avalanchego/utils/crypto"
"github.com/ava-labs/avalanchego/utils/formatting"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/utils/wrappers"
"github.com/ava-labs/avalanchego/vms/components/avax"
"github.com/ava-labs/avalanchego/vms/components/keystore"
"github.com/ava-labs/avalanchego/vms/platformvm/locked"
Expand Down Expand Up @@ -348,6 +349,82 @@ func (s *CaminoService) Spend(_ *http.Request, args *SpendArgs, response *SpendR
return nil
}

type RegisterNodeArgs struct {
api.JSONSpendHeader

OldNodeID ids.NodeID `json:"oldNodeID"`
NewNodeID ids.NodeID `json:"newNodeID"`
ConsortiumMemberAddress string `json:"consortiumMemberAddress"`
}

// RegisterNode issues an RegisterNodeTx
func (s *CaminoService) RegisterNode(_ *http.Request, args *RegisterNodeArgs, reply *api.JSONTxIDChangeAddr) error {
s.vm.ctx.Log.Debug("Platform: RegisterNode called")

// Parse the from addresses
fromAddrs, err := avax.ParseServiceAddresses(s.addrManager, args.From)
if err != nil {
return err
}

user, err := keystore.NewUserFromKeystore(s.vm.ctx.Keystore, args.Username, args.Password)
if err != nil {
return err
}

// Get the user's keys
privKeys, err := keystore.GetKeychain(user, fromAddrs)
if err != nil {
return fmt.Errorf("couldn't get addresses controlled by the user: %w", err)
}

if err := user.Close(); err != nil {
return err
}

if len(privKeys.Keys) == 0 {
return errNoKeys
}

// Parse the change address.
changeAddr := ids.ShortEmpty
if len(args.ChangeAddr) > 0 {
var err error
if changeAddr, err = avax.ParseServiceAddress(s.addrManager, args.ChangeAddr); err != nil {
return fmt.Errorf(errInvalidChangeAddr, err)
}
}

// Parse the consortium member address.
consortiumMemberAddress, err := avax.ParseServiceAddress(s.addrManager, args.ConsortiumMemberAddress)
if err != nil {
return fmt.Errorf("couldn't parse consortiumMemberAddress: %w", err)
}

// Create the transaction
tx, err := s.vm.txBuilder.NewRegisterNodeTx(
args.OldNodeID,
args.NewNodeID,
consortiumMemberAddress,
privKeys.Keys,
changeAddr,
)
if err != nil {
return fmt.Errorf("couldn't create tx: %w", err)
}

reply.TxID = tx.ID()
reply.ChangeAddr, err = s.addrManager.FormatLocalAddress(changeAddr)

errs := wrappers.Errs{}
errs.Add(
err,
s.vm.Builder.AddUnverifiedTx(tx),
)

return errs.Err
}

func (s *Service) getKeystoreKeys(args *api.JSONSpendHeader) (*secp256k1fx.Keychain, error) {
// Parse the from addresses
fromAddrs, err := avax.ParseServiceAddresses(s.addrManager, args.From)
Expand Down
3 changes: 3 additions & 0 deletions vms/platformvm/metrics/camino_tx_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ func newCaminoTxMetrics(
txMetrics: *txm,
// Camino specific tx metrics
numAddAddressStateTxs: newTxMetric(namespace, "add_address_state", registerer, &errs),
numDepositTxs: newTxMetric(namespace, "deposit", registerer, &errs),
numUnlockDepositTxs: newTxMetric(namespace, "unlock_deposit", registerer, &errs),
numRegisterNodeTx: newTxMetric(namespace, "register_node", registerer, &errs),
}
return m, errs.Err
}
Expand Down
27 changes: 27 additions & 0 deletions vms/platformvm/msig/msig.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (C) 2022, Chain4Travel AG. All rights reserved.
// See the file LICENSE for licensing terms.

package msig

import (
"github.com/ava-labs/avalanchego/database"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/vms/platformvm/state"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"
)

func GetOwner(state state.Chain, addr ids.ShortID) (*secp256k1fx.OutputOwners, error) {
msigOwner, err := state.GetMultisigOwner(addr)
if err != nil && err != database.ErrNotFound {
return nil, err
}

if msigOwner != nil {
return &msigOwner.Owners, nil
}

return &secp256k1fx.OutputOwners{
Threshold: 1,
Addrs: []ids.ShortID{addr},
}, nil
}
8 changes: 8 additions & 0 deletions vms/platformvm/state/camino.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,14 @@ func (cs *caminoState) SyncGenesis(s *state, g *genesis.State) error {

for _, consortiumMemberNode := range g.Camino.ConsortiumMembersNodeIDs {
cs.SetNodeConsortiumMember(consortiumMemberNode.NodeID, &consortiumMemberNode.ConsortiumMemberAddress)
addrState, err := cs.GetAddressStates(consortiumMemberNode.ConsortiumMemberAddress)
if err != nil {
return err
}
cs.SetAddressStates(
consortiumMemberNode.ConsortiumMemberAddress,
addrState|txs.AddressStateRegisteredNodeBit,
)
}

// adding deposit offers
Expand Down
4 changes: 4 additions & 0 deletions vms/platformvm/state/camino_diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,8 @@ func (d *diff) ApplyCaminoState(baseState State) {
for _, v := range d.caminoDiff.modifiedMultisigOwners {
baseState.SetMultisigOwner(v)
}

for nodeID, addr := range d.caminoDiff.modifiedConsortiumMemberNodes {
baseState.SetNodeConsortiumMember(nodeID, addr)
}
}
1 change: 0 additions & 1 deletion vms/platformvm/state/mock_chain.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

100 changes: 89 additions & 11 deletions vms/platformvm/txs/builder/camino_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/ava-labs/avalanchego/vms/platformvm/config"
"github.com/ava-labs/avalanchego/vms/platformvm/fx"
"github.com/ava-labs/avalanchego/vms/platformvm/locked"
"github.com/ava-labs/avalanchego/vms/platformvm/msig"
"github.com/ava-labs/avalanchego/vms/platformvm/state"
"github.com/ava-labs/avalanchego/vms/platformvm/txs"
"github.com/ava-labs/avalanchego/vms/platformvm/utxo"
Expand Down Expand Up @@ -58,6 +59,14 @@ type CaminoTxBuilder interface {
keys []*crypto.PrivateKeySECP256K1R,
changeAddr ids.ShortID,
) (*txs.Tx, error)

NewRegisterNodeTx(
OldNodeID ids.NodeID,
NewNodeID ids.NodeID,
ConsortiumMemberAddress ids.ShortID,
keys []*crypto.PrivateKeySECP256K1R,
changeAddr ids.ShortID,
) (*txs.Tx, error)
}

func NewCamino(
Expand Down Expand Up @@ -176,7 +185,7 @@ func (b *caminoBuilder) NewAddSubnetValidatorTx(
return tx, nil
}

nodeSigners, err := getSigners(keys, ids.ShortID(nodeID))
nodeSigners, err := getSigner(keys, ids.ShortID(nodeID))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -327,19 +336,88 @@ func (b *caminoBuilder) NewUnlockDepositTx(
return tx, tx.SyntacticVerify(b.ctx)
}

func getSigners(
func (b *caminoBuilder) NewRegisterNodeTx(
oldNodeID ids.NodeID,
newNodeID ids.NodeID,
consortiumMemberAddress ids.ShortID,
keys []*crypto.PrivateKeySECP256K1R,
address ids.ShortID,
) ([]*crypto.PrivateKeySECP256K1R, error) {
signer, found := secp256k1fx.NewKeychain(keys...).Get(address)
if !found {
return nil, fmt.Errorf("%w %s", errKeyMissing, address.String())
changeAddr ids.ShortID,
) (*txs.Tx, error) {
ins, outs, signers, err := b.Lock(keys, 0, b.cfg.TxFee, locked.StateUnlocked, changeAddr)
if err != nil {
return nil, fmt.Errorf("couldn't generate tx inputs/outputs: %w", err)
}

nodeSigners := []*crypto.PrivateKeySECP256K1R{}
if newNodeID != ids.EmptyNodeID {
nodeSigners, err = getSigner(keys, ids.ShortID(newNodeID))
if err != nil {
return nil, err
}
}
signers = append(signers, nodeSigners)

consortiumMemberOwner, err := msig.GetOwner(b.state, consortiumMemberAddress)
if err != nil {
return nil, err
}

consortiumSigners, err := getSigners(keys, consortiumMemberOwner.Addrs)
if err != nil {
return nil, err
}
signers = append(signers, consortiumSigners)

kc := secp256k1fx.NewKeychain(consortiumSigners...)
sigIndices, _, able := kc.Match(consortiumMemberOwner, b.clk.Unix())
if !able {
return nil, fmt.Errorf("failed to get consortium member auth: %w", err)
}

key, ok := signer.(*crypto.PrivateKeySECP256K1R)
if !ok {
return nil, errWrongNodeKeyType
utx := &txs.RegisterNodeTx{
BaseTx: txs.BaseTx{BaseTx: avax.BaseTx{
NetworkID: b.ctx.NetworkID,
BlockchainID: b.ctx.ChainID,
Ins: ins,
Outs: outs,
}},
OldNodeID: oldNodeID,
NewNodeID: newNodeID,
ConsortiumMemberAuth: &secp256k1fx.Input{SigIndices: sigIndices},
ConsortiumMemberAddress: consortiumMemberAddress,
}

return []*crypto.PrivateKeySECP256K1R{key}, nil
tx, err := txs.NewSigned(utx, txs.Codec, signers)
if err != nil {
return nil, err
}
return tx, tx.SyntacticVerify(b.ctx)
}

func getSigner(
keys []*crypto.PrivateKeySECP256K1R,
address ids.ShortID,
) ([]*crypto.PrivateKeySECP256K1R, error) {
return getSigners(keys, []ids.ShortID{address})
}

func getSigners(
keys []*crypto.PrivateKeySECP256K1R,
addresses []ids.ShortID,
) ([]*crypto.PrivateKeySECP256K1R, error) {
signers := make([]*crypto.PrivateKeySECP256K1R, len(addresses))
for i, addr := range addresses {
signer, found := secp256k1fx.NewKeychain(keys...).Get(addr)
if !found {
return nil, fmt.Errorf("%w %s", errKeyMissing, addr.String())
}

key, ok := signer.(*crypto.PrivateKeySECP256K1R)
if !ok {
return nil, errWrongNodeKeyType
}

signers[i] = key
}
return signers, nil
}
4 changes: 2 additions & 2 deletions vms/platformvm/txs/camino_address_state_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ const (
AddressStateConsortiumBit = uint64(0b10000000000000000000000000000000000)
AddressStateKycBits = uint64(0b11100000000000000000000000000000000)

AddressStateValidator = uint8(38)
AddressStateValidatorBits = uint64(0b100000000000000000000000000000000000000)
AddressStateRegisteredNode = uint8(38)
AddressStateRegisteredNodeBit = uint64(0b100000000000000000000000000000000000000)

AddressStateMax = uint8(63)
AddressStateValidBits = AddressStateRoleBits | AddressStateKycBits
Expand Down
9 changes: 8 additions & 1 deletion vms/platformvm/txs/camino_register_node_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import (
var (
_ UnsignedTx = (*RegisterNodeTx)(nil)

errNoNodeID = errors.New("no nodeID specified")
errNoNodeID = errors.New("no nodeID specified")
errConsortiumMemberAddrEmpty = errors.New("consortium member address is empty")
)

// RegisterNodeTx is an unsigned registerNodeTx
Expand Down Expand Up @@ -49,12 +50,18 @@ func (tx *RegisterNodeTx) SyntacticVerify(ctx *snow.Context) error {
return nil
case tx.NewNodeID == ids.EmptyNodeID && tx.OldNodeID == ids.EmptyNodeID:
return errNoNodeID
case tx.ConsortiumMemberAddress == ids.ShortEmpty:
return errConsortiumMemberAddrEmpty
}

if err := tx.BaseTx.SyntacticVerify(ctx); err != nil {
return fmt.Errorf("failed to verify BaseTx: %w", err)
}

if err := tx.ConsortiumMemberAuth.Verify(); err != nil {
return fmt.Errorf("failed to verify consortium member auth: %w", err)
}

// cache that this is valid
tx.SyntacticallyVerified = true
return nil
Expand Down
Loading

0 comments on commit 35401d5

Please sign in to comment.