Skip to content

Commit

Permalink
add evm multisig feature
Browse files Browse the repository at this point in the history
  • Loading branch information
felipemadero committed Mar 1, 2025
1 parent 6df6a16 commit 6904684
Show file tree
Hide file tree
Showing 15 changed files with 188 additions and 91 deletions.
99 changes: 65 additions & 34 deletions cmd/blockchaincmd/add_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package blockchaincmd

import (
"encoding/hex"
"errors"
"fmt"
"strings"
Expand Down Expand Up @@ -31,31 +32,31 @@ import (
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/utils/units"
warpMessage "github.com/ava-labs/avalanchego/vms/platformvm/warp/message"

"github.com/ethereum/go-ethereum/common"
"github.com/spf13/cobra"
)

var (
nodeIDStr string
nodeEndpoint string
balanceAVAX float64
weight uint64
startTimeStr string
duration time.Duration
defaultValidatorParams bool
useDefaultStartTime bool
useDefaultDuration bool
useDefaultWeight bool
waitForTxAcceptance bool
publicKey string
pop string
remainingBalanceOwnerAddr string
disableOwnerAddr string
rpcURL string
aggregatorLogLevel string
aggregatorLogToStdout bool
delegationFee uint16

nodeIDStr string
nodeEndpoint string
balanceAVAX float64
weight uint64
startTimeStr string
duration time.Duration
defaultValidatorParams bool
useDefaultStartTime bool
useDefaultDuration bool
useDefaultWeight bool
waitForTxAcceptance bool
publicKey string
pop string
remainingBalanceOwnerAddr string
disableOwnerAddr string
rpcURL string
aggregatorLogLevel string
aggregatorLogToStdout bool
delegationFee uint16
errNoSubnetID = errors.New("failed to find the subnet ID for this subnet, has it been deployed/created on this network?")
errMutuallyExclusiveDurationOptions = errors.New("--use-default-duration/--use-default-validator-params and --staking-period are mutually exclusive")
errMutuallyExclusiveStartOptions = errors.New("--use-default-start-time/--use-default-validator-params and --start-time are mutually exclusive")
Expand All @@ -65,6 +66,7 @@ var (
aggregatorAllowPrivatePeers bool
clusterNameFlagValue string
createLocalValidator bool
multisigValidatorManagerOwner bool
)

const (
Expand Down Expand Up @@ -124,6 +126,7 @@ Testnet or Mainnet.`,
cmd.Flags().StringVar(&subnetIDstr, "subnet-id", "", "subnet ID (only if blockchain name is not provided)")
cmd.Flags().StringVar(&validatorManagerOwnerAddress, "validator-manager-owner", "", "validator manager owner address (only if blockchain name is not provided)")
cmd.Flags().Uint64Var(&weight, validatorWeightFlag, uint64(constants.DefaultStakeWeight), "set the weight of the validator")
cmd.Flags().BoolVar(&multisigValidatorManagerOwner, "multisig-validator-manager-ower", false, "validator manager owner is multisig, make hex dump of ech evm transactions, so they can be signed in a separate flow")

return cmd
}
Expand Down Expand Up @@ -404,17 +407,22 @@ func CallAddValidator(
return fmt.Errorf("unable to find Validator Manager address")
}
validatorManagerAddress = sc.Networks[network.Name()].ValidatorManagerAddress
ownerPrivateKeyFound, _, _, ownerPrivateKey, err := contract.SearchForManagedKey(
app,
network,
common.HexToAddress(sc.ValidatorManagerOwner),
true,
)
if err != nil {
return err
}
if !ownerPrivateKeyFound {
return fmt.Errorf("private key for Validator manager owner %s is not found", sc.ValidatorManagerOwner)

var ownerPrivateKey string
if !multisigValidatorManagerOwner {
var ownerPrivateKeyFound bool
ownerPrivateKeyFound, _, _, ownerPrivateKey, err = contract.SearchForManagedKey(
app,
network,
common.HexToAddress(sc.ValidatorManagerOwner),
true,
)
if err != nil {
return err
}
if !ownerPrivateKeyFound {
return fmt.Errorf("private key for Validator manager owner %s is not found", sc.ValidatorManagerOwner)
}
}

pos := sc.PoS()
Expand Down Expand Up @@ -523,12 +531,14 @@ func CallAddValidator(
}
aggregatorCtx, aggregatorCancel := sdkutils.GetTimedContext(constants.SignatureAggregatorTimeout)
defer aggregatorCancel()
signedMessage, validationID, err := validatormanager.InitValidatorRegistration(
signedMessage, validationID, rawTx, err := validatormanager.InitValidatorRegistration(
aggregatorCtx,
app,
network,
rpcURL,
chainSpec,
multisigValidatorManagerOwner,
sc.ValidatorManagerOwner,
ownerPrivateKey,
nodeID,
blsInfo.PublicKey[:],
Expand All @@ -547,6 +557,15 @@ func CallAddValidator(
if err != nil {
return err
}
if rawTx != nil {
bs, err := rawTx.MarshalBinary()
if err != nil {
return fmt.Errorf("failure marshalling raw evm tx: %w", err)
}
ux.Logger.PrintToUser("Raw Tx Dump For Initializing Validator Registration. Please sign and commit it.")
ux.Logger.PrintToUser("0x%s", hex.EncodeToString(bs))
return nil
}
ux.Logger.PrintToUser("ValidationID: %s", validationID)

txID, _, err := deployer.RegisterL1Validator(balance, blsInfo, signedMessage)
Expand All @@ -566,21 +585,33 @@ func CallAddValidator(

aggregatorCtx, aggregatorCancel = sdkutils.GetTimedContext(constants.SignatureAggregatorTimeout)
defer aggregatorCancel()
if err := validatormanager.FinishValidatorRegistration(
rawTx, err = validatormanager.FinishValidatorRegistration(
aggregatorCtx,
app,
network,
rpcURL,
chainSpec,
multisigValidatorManagerOwner,
sc.ValidatorManagerOwner,
ownerPrivateKey,
validationID,
extraAggregatorPeers,
aggregatorAllowPrivatePeers,
aggregatorLogger,
validatorManagerAddress,
); err != nil {
)
if err != nil {
return err
}
if rawTx != nil {
bs, err := rawTx.MarshalBinary()
if err != nil {
return fmt.Errorf("failure marshalling raw evm tx: %w", err)
}
ux.Logger.PrintToUser("Raw Tx Dump For Finish Validator Registration. Please sign and commit it.")
ux.Logger.PrintToUser("0x%s", hex.EncodeToString(bs))
return nil
}

ux.Logger.PrintToUser(" NodeID: %s", nodeID)
ux.Logger.PrintToUser(" Network: %s", network.Name())
Expand Down
8 changes: 6 additions & 2 deletions cmd/nodecmd/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -541,12 +541,14 @@ func addAsValidator(network models.Network,

aggregatorCtx, aggregatorCancel := sdkutils.GetTimedContext(constants.SignatureAggregatorTimeout)
defer aggregatorCancel()
signedMessage, validationID, err := validatormanager.InitValidatorRegistration(
signedMessage, validationID, _, err := validatormanager.InitValidatorRegistration(
aggregatorCtx,
app,
network,
rpcURL,
chainSpec,
false,
"",
payerPrivateKey,
nodeID,
blsInfo.PublicKey[:],
Expand Down Expand Up @@ -585,12 +587,14 @@ func addAsValidator(network models.Network,

aggregatorCtx, aggregatorCancel = sdkutils.GetTimedContext(constants.SignatureAggregatorTimeout)
defer aggregatorCancel()
if err := validatormanager.FinishValidatorRegistration(
if _, err := validatormanager.FinishValidatorRegistration(
aggregatorCtx,
app,
network,
rpcURL,
chainSpec,
false,
"",
payerPrivateKey,
validationID,
extraAggregatorPeers,
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module github.com/ava-labs/avalanche-cli

go 1.22.10

replace github.com/ava-labs/subnet-evm => ../subnet-evm

require (
github.com/ava-labs/apm v1.0.0
github.com/ava-labs/avalanche-network-runner v1.8.4-0.20250219173912-dbb9afce58c1
Expand Down
60 changes: 31 additions & 29 deletions pkg/contract/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,25 +289,11 @@ func ParseSpec(
return name, string(abiBytes), nil
}

// Signer that just copy the raw Tx and returns error
// To be used on multisig flows where the user is going
// to sign and commit in separate flow
//
// Using this signer hack because it is the only setting that can be
// configured to get access to the raw tx before beign signed (subnet-evm
// transact function)
//
// Not multithread safe
var (
rawTxCopy *types.Transaction
errRawTxCopy = errors.New("raw tx has been copied, not signed")
)
func rawTxCopySigner(
func idempotentSigner(
_ common.Address,
tx *types.Transaction,
) (*types.Transaction, error) {
rawTxCopy = tx
return nil, errRawTxCopy
return tx, nil
}

// get method name and types from [methodsSpec], then call it
Expand All @@ -316,6 +302,7 @@ func rawTxCopySigner(
func TxToMethod(
rpcURL string,
generateRawTxOnly bool,
from common.Address,
privateKey string,
contractAddress common.Address,
payment *big.Int,
Expand All @@ -341,21 +328,25 @@ func TxToMethod(
}
defer client.Close()
contract := bind.NewBoundContract(contractAddress, *abi, client, client, client)
txOpts, err := evm.GetTxOptsWithSigner(client, privateKey)
if err != nil {
return nil, nil, err
}
txOpts.Value = payment
var txOpts *bind.TransactOpts
if generateRawTxOnly {
txOpts.Signer = rawTxCopySigner
txOpts = &bind.TransactOpts{
From: from,
Signer: idempotentSigner,
NoSend: true,
}
} else {
txOpts, err = evm.GetTxOptsWithSigner(client, privateKey)
if err != nil {
return nil, nil, err
}
}
txOpts.Value = payment
tx, err := contract.Transact(txOpts, methodName, params...)
if err != nil {
if err == errRawTxCopy {
return rawTxCopy, nil, nil
}
trace, traceCallErr := DebugTraceCall(
rpcURL,
from,
privateKey,
contractAddress,
payment,
Expand All @@ -379,6 +370,9 @@ func TxToMethod(
}
return tx, nil, err
}
if generateRawTxOnly {
return tx, nil, nil
}
receipt, success, err := evm.WaitForTransaction(client, tx)
if err != nil {
return tx, nil, err
Expand All @@ -402,6 +396,7 @@ func TxToMethod(
func TxToMethodWithWarpMessage(
rpcURL string,
generateRawTxOnly bool,
from common.Address,
privateKey string,
contractAddress common.Address,
warpMessage *avalancheWarp.Message,
Expand Down Expand Up @@ -434,6 +429,7 @@ func TxToMethodWithWarpMessage(
tx, err := evm.GetTxToMethodWithWarpMessage(
client,
generateRawTxOnly,
from,
privateKey,
warpMessage,
contractAddress,
Expand All @@ -443,6 +439,9 @@ func TxToMethodWithWarpMessage(
if err != nil {
return nil, nil, err
}
if generateRawTxOnly {
return tx, nil, nil
}
if err := evm.SendTransaction(client, tx); err != nil {
return tx, nil, err
}
Expand Down Expand Up @@ -519,6 +518,7 @@ func DebugTraceTransaction(

func DebugTraceCall(
rpcURL string,
from common.Address,
privateKey string,
contractAddress common.Address,
payment *big.Int,
Expand All @@ -545,11 +545,13 @@ func DebugTraceCall(
return nil, err
}
defer client.Close()
pk, err := crypto.HexToECDSA(privateKey)
if err != nil {
return nil, err
if from == (common.Address{}) {
pk, err := crypto.HexToECDSA(privateKey)
if err != nil {
return nil, err
}
from = crypto.PubkeyToAddress(pk.PublicKey)
}
from := crypto.PubkeyToAddress(pk.PublicKey)
data := map[string]string{
"from": from.Hex(),
"to": contractAddress.Hex(),
Expand Down
21 changes: 15 additions & 6 deletions pkg/evm/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,19 +267,28 @@ func FundAddress(
func GetTxToMethodWithWarpMessage(
client ethclient.Client,
generateRawTxOnly bool,
from common.Address,
privateKeyStr string,
warpMessage *avalancheWarp.Message,
contract common.Address,
callData []byte,
value *big.Int,
) (*types.Transaction, error) {
const defaultGasLimit = 2_000_000
privateKey, err := crypto.HexToECDSA(privateKeyStr)
if err != nil {
return nil, err
var (
privateKey *ecdsa.PrivateKey
err error
)
if privateKeyStr != "" {
privateKey, err = crypto.HexToECDSA(privateKeyStr)
if err != nil {
return nil, err
}
}
if from == (common.Address{}) {
from = crypto.PubkeyToAddress(privateKey.PublicKey)
}
address := crypto.PubkeyToAddress(privateKey.PublicKey)
gasFeeCap, gasTipCap, nonce, err := CalculateTxParams(client, address.Hex())
gasFeeCap, gasTipCap, nonce, err := CalculateTxParams(client, from.Hex())
if err != nil {
return nil, err
}
Expand All @@ -294,7 +303,7 @@ func GetTxToMethodWithWarpMessage(
},
}
msg := interfaces.CallMsg{
From: address,
From: from,
To: &contract,
GasPrice: nil,
GasTipCap: gasTipCap,
Expand Down
Loading

0 comments on commit 6904684

Please sign in to comment.