Skip to content

Commit d4735fe

Browse files
authored
feat: integrate ECDSA registration for KeyRegistrar (#269)
<!-- 🚨 ATTENTION! 🚨 This PR template is REQUIRED. PRs not following this format will be closed without review. Requirements: - PR title must follow commit conventions: https://www.conventionalcommits.org/en/v1.0.0/ - Label your PR with the correct type (e.g., 🐛 Bug, ✨ Enhancement, 🧪 Test, etc.) - Provide clear and specific details in each section --> **Motivation:** <!-- Explain here the context, and why you're making that change. What is the problem you're trying to solve. --> As a devkit user, I want to be able to register either BN254 or ECDSA keys for my Operator to the KeyRegsitrar **Modifications:** <!-- Describe the modifications you've done from a high level. What are the key technical decisions and why were they made? --> - Allows either curveType to be registered to the KeyRegistrar **Result:** <!-- *After your change, what will change. --> - Can setup and register ECDSA or BN254 keys in the `RegisterKeyInKeyRegistrarAction` deploy step
1 parent e880d08 commit d4735fe

File tree

3 files changed

+148
-61
lines changed

3 files changed

+148
-61
lines changed

pkg/commands/deploy_actions.go

Lines changed: 109 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@ import (
1414
"strings"
1515
"time"
1616

17-
"github.com/Layr-Labs/crypto-libs/pkg/bn254"
1817
"github.com/Layr-Labs/crypto-libs/pkg/keystore"
1918
"github.com/Layr-Labs/devkit-cli/config/configs"
2019
"github.com/Layr-Labs/devkit-cli/config/contexts"
2120
"github.com/Layr-Labs/devkit-cli/pkg/common"
2221
"github.com/Layr-Labs/devkit-cli/pkg/common/iface"
2322
allocationmanager "github.com/Layr-Labs/eigenlayer-contracts/pkg/bindings/AllocationManager"
23+
keyregistrar "github.com/Layr-Labs/eigenlayer-contracts/pkg/bindings/KeyRegistrar"
2424
ethcommon "github.com/ethereum/go-ethereum/common"
25+
"github.com/ethereum/go-ethereum/crypto"
2526
"github.com/ethereum/go-ethereum/ethclient"
2627
"github.com/urfave/cli/v2"
2728
"golang.org/x/text/cases"
@@ -1146,86 +1147,149 @@ func RegisterKeyInKeyRegistrarAction(cCtx *cli.Context, logger iface.Logger) err
11461147
return fmt.Errorf("failed to connect to L1 RPC: %w", err)
11471148
}
11481149

1150+
// Bind to keyRegistrar
11491151
avsAddress := ethcommon.HexToAddress(envCtx.Avs.Address)
1150-
_, _, _, keyRegistrarAddr, _, _, _, _ := common.GetEigenLayerAddresses(contextName, cfg)
1152+
_, _, _, keyRegistrarAddrHex, _, _, _, _ := common.GetEigenLayerAddresses(contextName, cfg)
1153+
krAddr := ethcommon.HexToAddress(keyRegistrarAddrHex)
1154+
kr, err := keyregistrar.NewKeyRegistrar(krAddr, client)
1155+
if err != nil {
1156+
return fmt.Errorf("failed to bind KeyRegistrar at %s: %w", krAddr.Hex(), err)
1157+
}
11511158

11521159
for _, op := range envCtx.OperatorRegistrations {
1153-
11541160
for _, operator := range envCtx.Operators {
1161+
if op.Address != operator.Address {
1162+
continue
1163+
}
1164+
1165+
// Tx signer (EOA): hex string private key
1166+
operatorPrivHex, err := loadOperatorECDSAKey(operator)
1167+
if err != nil {
1168+
return fmt.Errorf("failed to load ECDSA key for operator %s: %w", operator.Address, err)
1169+
}
1170+
operatorAddress := ethcommon.HexToAddress(op.Address)
1171+
1172+
// Build the caller
1173+
contractCaller, err := common.NewContractCaller(
1174+
operatorPrivHex,
1175+
big.NewInt(int64(l1Cfg.ChainID)),
1176+
client,
1177+
ethcommon.HexToAddress(""),
1178+
ethcommon.HexToAddress(""),
1179+
ethcommon.HexToAddress(""),
1180+
krAddr, // key registrar
1181+
ethcommon.HexToAddress(""),
1182+
ethcommon.HexToAddress(""),
1183+
ethcommon.HexToAddress(""),
1184+
logger,
1185+
)
1186+
if err != nil {
1187+
return fmt.Errorf("failed to create contract caller: %w", err)
1188+
}
11551189

1156-
if op.Address == operator.Address {
1157-
operatorPrivateKey, err := loadOperatorECDSAKey(operator)
1190+
// We need the parsed *ecdsa.PrivateKey for ECDSA signing and address derivation
1191+
operatorECDSA, err := crypto.HexToECDSA(strings.TrimPrefix(operatorPrivHex, "0x"))
1192+
if err != nil {
1193+
return fmt.Errorf("invalid operator ECDSA key hex: %w", err)
1194+
}
1195+
1196+
// Discover curve type
1197+
operatorSet := keyregistrar.OperatorSet{Avs: avsAddress, Id: uint32(op.OperatorSetID)}
1198+
curveType, err := kr.GetOperatorSetCurveType(nil, operatorSet)
1199+
if err != nil {
1200+
return fmt.Errorf("failed to get operator set curve type: %w", err)
1201+
}
1202+
1203+
switch curveType {
1204+
case common.CURVE_TYPE_KEY_REGISTRAR_ECDSA:
1205+
// keyData = 20-byte address
1206+
keyAddr := crypto.PubkeyToAddress(operatorECDSA.PublicKey)
1207+
keyData := keyAddr.Bytes()
1208+
1209+
// EIP-712 digest from contract
1210+
msgHash, err := kr.GetECDSAKeyRegistrationMessageHash(nil, operatorAddress, operatorSet, keyAddr)
11581211
if err != nil {
1159-
return fmt.Errorf("failed to load ECDSA key for operator %s: %w", operator.Address, err)
1212+
return fmt.Errorf("failed to get ECDSA hash: %w", err)
11601213
}
1161-
operatorAddress := ethcommon.HexToAddress(op.Address)
1162-
contractCaller, err := common.NewContractCaller(
1163-
operatorPrivateKey,
1164-
big.NewInt(int64(l1Cfg.ChainID)),
1165-
client,
1166-
ethcommon.HexToAddress(""),
1167-
ethcommon.HexToAddress(""),
1168-
ethcommon.HexToAddress(""),
1169-
ethcommon.HexToAddress(keyRegistrarAddr),
1170-
ethcommon.HexToAddress(""),
1171-
ethcommon.HexToAddress(""),
1172-
ethcommon.HexToAddress(""),
1173-
logger,
1174-
)
1214+
1215+
// 65-byte r||s||v with v in {27,28}
1216+
sig, err := crypto.Sign(msgHash[:], operatorECDSA)
11751217
if err != nil {
1176-
return fmt.Errorf("failed to create contract caller: %w", err)
1218+
return fmt.Errorf("failed to sign ECDSA: %w", err)
1219+
}
1220+
if sig[64] < 27 {
1221+
sig[64] += 27
11771222
}
11781223

1179-
var blskeystorePath, blskeystorePassword string
1224+
if err := contractCaller.RegisterKeyInKeyRegistrar(
1225+
cCtx.Context, operatorAddress, avsAddress, uint32(op.OperatorSetID), keyData, sig,
1226+
); err != nil {
1227+
return fmt.Errorf("failed to register ECDSA key: %w", err)
1228+
}
1229+
logger.Info("Registered ECDSA key for operator %s", operator.Address)
1230+
1231+
case common.CURVE_TYPE_KEY_REGISTRAR_BN254:
1232+
// Load BN254 key for this set
1233+
var blsKeystorePath, blsKeystorePassword string
11801234
for _, ks := range operator.Keystores {
11811235
if ks.OperatorSet == op.OperatorSetID {
1182-
blskeystorePath = ks.BlsKeystorePath
1183-
blskeystorePassword = ks.BlsKeystorePassword
1236+
blsKeystorePath = ks.BlsKeystorePath
1237+
blsKeystorePassword = ks.BlsKeystorePassword
11841238
break
11851239
}
11861240
}
1187-
1188-
if blskeystorePath == "" {
1189-
return fmt.Errorf("no bls keystore found for OperatorSet %d", op.OperatorSetID)
1241+
if blsKeystorePath == "" {
1242+
return fmt.Errorf("no BLS keystore found for OperatorSet %d", op.OperatorSetID)
11901243
}
11911244

1192-
keystoreData, err := keystore.LoadKeystoreFile(blskeystorePath)
1245+
ksData, err := keystore.LoadKeystoreFile(blsKeystorePath)
11931246
if err != nil {
1194-
return fmt.Errorf("failed to load the keystore file from given path %s error %w", blskeystorePath, err)
1247+
return fmt.Errorf("failed to load the keystore file from given path %s error %w", blsKeystorePath, err)
11951248
}
1196-
1197-
privateKey, err := keystoreData.GetBN254PrivateKey(blskeystorePassword)
1249+
blsPriv, err := ksData.GetBN254PrivateKey(blsKeystorePassword)
11981250
if err != nil {
1199-
return fmt.Errorf("failed to extract the private key from the keystore file")
1200-
1251+
return fmt.Errorf("failed to extract BN254 private key from keystore: %w", err)
12011252
}
12021253

1203-
keyData, err := contractCaller.EncodeBN254KeyData(privateKey.Public())
1254+
keyData, err := contractCaller.EncodeBN254KeyData(blsPriv.Public())
12041255
if err != nil {
1205-
return fmt.Errorf("failed to encode key data: %w", err)
1256+
return fmt.Errorf("failed to encode BN254 key data: %w", err)
12061257
}
12071258

1208-
messageHash, err := contractCaller.GetOperatorRegistrationMessageHash(cCtx.Context, operatorAddress, avsAddress, uint32(op.OperatorSetID), keyData)
1259+
// EIP-712 digest from contract
1260+
msgHash, err := kr.GetBN254KeyRegistrationMessageHash(nil, operatorAddress, operatorSet, keyData)
12091261
if err != nil {
12101262
return fmt.Errorf("failed to get operator registration message hash: %w", err)
12111263
}
12121264

1213-
signature, err := privateKey.SignSolidityCompatible(messageHash)
1265+
// Ensure [32]byte for the BLS signer
1266+
var digest [32]byte
1267+
copy(digest[:], msgHash[:])
1268+
1269+
blsSig, err := blsPriv.SignSolidityCompatible(digest)
12141270
if err != nil {
1215-
return fmt.Errorf("failed to sign message hash: %w", err)
1271+
return fmt.Errorf("failed to sign BN254 message hash: %w", err)
12161272
}
1217-
1218-
bn254Signature := bn254.Signature(*signature)
1219-
1220-
err = contractCaller.RegisterKeyInKeyRegistrar(cCtx.Context, operatorAddress, avsAddress, uint32(op.OperatorSetID), keyData, bn254Signature)
1273+
x := blsSig.GetG1Point().X.BigInt(new(big.Int))
1274+
y := blsSig.GetG1Point().Y.BigInt(new(big.Int))
1275+
sigBytes, err := contractCaller.PackUint256Pair(x, y)
12211276
if err != nil {
1222-
return fmt.Errorf("failed to register key in key registrar: %w", err)
1277+
return fmt.Errorf("failed to pack BN254 signature: %w", err)
12231278
}
1224-
logger.Info("Successfully registered key in key registrar for operator %s", operator.Address)
1279+
1280+
if err := contractCaller.RegisterKeyInKeyRegistrar(
1281+
cCtx.Context, operatorAddress, avsAddress, uint32(op.OperatorSetID), keyData, sigBytes,
1282+
); err != nil {
1283+
return fmt.Errorf("failed to register BN254 key: %w", err)
1284+
}
1285+
logger.Info("Registered BN254 key for operator %s", operator.Address)
1286+
1287+
default:
1288+
return fmt.Errorf("unsupported curve type %d for operatorSet %d", curveType, op.OperatorSetID)
12251289
}
12261290
}
1227-
12281291
}
1292+
12291293
logger.Info("Successfully registered keys in key registrar")
12301294
return nil
12311295
}

pkg/commands/transporter.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -405,21 +405,33 @@ func Transport(cCtx *cli.Context, initialRun bool) error {
405405
return fmt.Errorf("registration hash: %w", err)
406406
}
407407

408-
// Sign the message hash with BLS key
409-
sig, err := blsPriv.SignSolidityCompatible(msgHash)
408+
// Ensure [32]byte input to the signer
409+
var digest [32]byte
410+
copy(digest[:], msgHash[:])
411+
412+
// Sign with BN254 key
413+
sig, err := blsPriv.SignSolidityCompatible(digest)
410414
if err != nil {
411415
return fmt.Errorf("BLS sign: %w", err)
412416
}
413-
bn254Signature := bn254.Signature(*sig)
414417

415-
// Register in KeyRegistrar
418+
// Convert G1 point to abi.encode(uint256 x, uint256 y)
419+
x := sig.GetG1Point().X.BigInt(new(big.Int))
420+
y := sig.GetG1Point().Y.BigInt(new(big.Int))
421+
422+
sigBytes, err := contractCaller.PackUint256Pair(x, y)
423+
if err != nil {
424+
return fmt.Errorf("pack BN254 signature: %w", err)
425+
}
426+
427+
// Register BLS key in KeyRegistrar with raw bytes
416428
if err := contractCaller.RegisterKeyInKeyRegistrar(
417429
cCtx.Context,
418430
operatorAddress,
419431
gen.Avs,
420432
uint32(gen.Id),
421433
keyData,
422-
bn254Signature,
434+
sigBytes,
423435
); err != nil {
424436
return fmt.Errorf("register key in key registrar: %w", err)
425437
}

pkg/common/contract_caller.go

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"math/big"
1111
"strings"
1212

13-
"github.com/Layr-Labs/crypto-libs/pkg/bn254"
1413
"github.com/Layr-Labs/devkit-cli/pkg/common/contracts"
1514
"github.com/Layr-Labs/devkit-cli/pkg/common/iface"
1615
allocationmanager "github.com/Layr-Labs/eigenlayer-contracts/pkg/bindings/AllocationManager"
@@ -736,7 +735,14 @@ func (cc *ContractCaller) WhitelistChainIdInCrossRegistry(ctx context.Context, o
736735
return nil
737736
}
738737

739-
func (cc *ContractCaller) RegisterKeyInKeyRegistrar(ctx context.Context, operatorAddress common.Address, avsAddress common.Address, opSetId uint32, keyData []byte, signature bn254.Signature) error {
738+
func (cc *ContractCaller) RegisterKeyInKeyRegistrar(
739+
ctx context.Context,
740+
operatorAddress common.Address,
741+
avsAddress common.Address,
742+
opSetId uint32,
743+
keyData []byte,
744+
signature []byte,
745+
) error {
740746
opts, err := cc.buildTxOpts()
741747
if err != nil {
742748
return fmt.Errorf("failed to build transaction options: %w", err)
@@ -746,13 +752,6 @@ func (cc *ContractCaller) RegisterKeyInKeyRegistrar(ctx context.Context, operato
746752
if err != nil {
747753
return fmt.Errorf("failed to get KeyRegistrar: %w", err)
748754
}
749-
g1Point := &bn254.G1Point{
750-
G1Affine: signature.GetG1Point(),
751-
}
752-
g1Bytes, err := g1Point.ToPrecompileFormat()
753-
if err != nil {
754-
return fmt.Errorf("signature not in correct subgroup: %w", err)
755-
}
756755

757756
operatorSet := keyregistrar.OperatorSet{Avs: avsAddress, Id: opSetId}
758757

@@ -767,8 +766,7 @@ func (cc *ContractCaller) RegisterKeyInKeyRegistrar(ctx context.Context, operato
767766
}
768767

769768
err = cc.SendAndWaitForTransaction(ctx, "RegisterKeyInKeyRegistrar", func() (*types.Transaction, error) {
770-
tx, err := keyRegistrar.RegisterKey(opts, operatorAddress, operatorSet, keyData, g1Bytes)
771-
return tx, err
769+
return keyRegistrar.RegisterKey(opts, operatorAddress, operatorSet, keyData, signature)
772770
})
773771
return err
774772
}
@@ -844,3 +842,16 @@ func (cc *ContractCaller) SetReleaseMetadata(
844842
return tx, err
845843
})
846844
}
845+
846+
// abi.encode(uint256 x, uint256 y)
847+
func (cc *ContractCaller) PackUint256Pair(x, y *big.Int) ([]byte, error) {
848+
t, err := abi.NewType("uint256", "", nil)
849+
if err != nil {
850+
return nil, err
851+
}
852+
args := abi.Arguments{
853+
{Type: t},
854+
{Type: t},
855+
}
856+
return args.Pack(x, y)
857+
}

0 commit comments

Comments
 (0)