3. Key Provisioning in vFactory

This process is gas intensive and a Merkle Tree based solution is currently being worked on that would only require an update + approval of the tree root, drastically reducing the gas cost to add keys.

Now that we have the exact address of the vPool and its vWithdrawalRecipient, we can start creating validation keys for its withdrawal credentials and upload them to the vFactory.

Withdrawal Channels

The vFactory stores keys in separated Withdrawal Channels. Only two types of Withdrawal Channels exist:

  • Withdrawal Channel != 0: The withdrawal channel value is the actual withdrawal credential of the keys inside the channel. In the case of the vPool, keys are added on a withdrawal channel that is equal to the withdrawal credential of the vWithdrawalRecipient

  • Withdrawal Channel == 0: This special withdrawal channel holds keys with each its own withdrawal recipient, handled by the vFactory. This means that the generation of these keys is a bit different as the address of the withdrawal recipient is deterministic and based on the validator public. We won't use this channel for the vPool.

Retrieving the withdrawal credential of the vPool

TheGraph: Retrieve the withdrawal credential of the vPool
{
  vFactories(where:{address:"YOUR_VFACTORY_ADDRESS"}) {
    pools {
      address
      withdrawalRecipient {
        address
        withdrawalCredentials
      }
    }
  }
}
{
  "data": {
    "vFactory": {
      "pools": [
        {
          "address": "YOUR_NEW_VPOOL_ADDRESS",
          "withdrawalRecipient": {
            "address": "WITHDRAWAL_RECIPIENT_ADDRESS",
            "withdrawalCredentials": "THE_WITHDRAWAL_CREDENTIALS"
          }
        }
      ]
    }
  }
}

We will need both the withdrawal credential and the vWithdrawalRecipient address for the next step

Generating 10 keys

For our example, we'll use the official deposit cli to generate 10 keys for the pool.


deposit new-mnemonic \
    --eth1_withdrawal_address $WITHDRAWAL_RECIPIENT_ADDRESS \
    --num_validators 10

And once done, you can check the generated deposit_data file to make sure that all keys:

  • have the same withdrawal credential

  • the value is the proper one

Verifying our 10 keys before submission

We can now verify our deposit data to make sure it's valid. The tool we're going to use ensures that the key has no duplicates inside the vFactory and ensures that the signature associated with the public key is valid.

You should retrieve the vsuite-utils repository, and have golang installed for the next step.

Clone vsuite-utils and run:


go run main.go factory verify-deposit-data \
    --deposit-data-file $DEPOSIT_DATA_FILE_PATH \
    --eth-rpc-url $ETH_RPC_URL \
    --factory-address $FACTORY_ADDRESS \
    --network prater \
    --withdrawal-channel $WITHDRAWAL_CHANNEL

It should directly tell you if there is any error in the deposit_data, otherwise we're clear to advance to the next step: submitting the keys

Adding 10 keys to the vFactory

We can now convert the generated deposit_data into calldata for the submit transaction.

vsuite-utils: submit keys by using cast

from=ADMIN or from=OPERATOR


go run main.go factory populate-add-validators-transaction \
    --deposit-data-file $DEPOSIT_DATA_FILE_PATH \
    --factory-address $FACTORY_ADDRESS \
    --withdrawal-channel $WITHDRAWAL_CHANNEL \
    --cast | ./pipe-cast-send --rpc-url $ETH_PC_URL
  • You can add the -i flag to interactively provide the private key for the transaction

  • You can add --ledger --from YOUR_ADDRESS to use a Ledger device to perform the transaction

vsuite-utils: only generate calldata for the submit transaction

go run main.go factory populate-add-validators-transaction \
    --deposit-data-file $DEPOSIT_DATA_FILE_PATH \
    --factory-address $FACTORY_ADDRESS \
    --withdrawal-channel $WITHDRAWAL_CHANNEL
{
  "to": "YOUR_FACTORY_ADDRESS",
  "data": "0x469804480000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000010100000000000000000000000b226a56495872ad23a43130eae0bad248cc7aa1000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000b408839211755c328851abf9a1d42246ed38323d297069bcc22f907a0cb6c34b95a740bdee32354baac796ed467bfe293830c884f8d2ec14f662caad193cdb61c5fe81907728f3fd777d9779d584a04ff4c7a8a3aca502e1869c3126040874d6589808775c9ae7553b75c570d0d024d44d13b59974e1a4ec7ba191adf2e6cb9fbcdf4a032bd0942b3f1c3cc12b9731975969698f761095778e4d4ca59bf44c8d59b1fb14223db81dad2c602bfb12067119f3b4a63f62425156f8e25e4bed3649d1f07050155a2f82987597171d072fe022b4b941a2898f96bb5a72657424d96ac538ccf445afa3978af6ea50024f6c204b6b6b2a0c99591d1d041f92b575ef3d3b24503489b4d8369984d1942ec15f72356189c1a674753fbef761bedb8934521fd815fd1aa29c317b95a07585ccaa8e155cfb1ecb9678a53219aaa6d08b6f043901425261436d11fe7553af368154cb68b092926aa5ea1c3eb317ffd26d24073b0b3f930ac1f13701587c9ff5ff45ecf56161dec813194d2f0972e8dd95a90efe3a90b1f780f913f13be8d98dfed19fc5423e401d9103c58e8d66c89eb6dac363f3469e03cdea6a00e0f5ca3e3239456e39552ee28b111a577697c7f065045729c9b60eac25f7f36985bdf6672936678862e81bd52e3e4c00f55b69a1c3191a65511811c31eda934cfca51791ecc65648509fe87c5b750fe2e19b50b176d9474a195249573bfb2cf93cc68ba7b87665709952ad00d1bc04bc03628c9b43da999ffa2eabc5526c1d11f53cfe3497ba05a541b085c89c0720c5f4871eb0e055aff00a67b83e7b689a89369665332417929d7fd9443503a1bb585aec525293f2b5ab50e1177940393602aa50dc41e123ebb320bd31046135bdb2514f34230721afb03a26f167f022798e7366899cd1f12f64f3425159eefb391ff421414b7dc7f7d92ab5a8c724af0e096bfcd5375b6597fe0d523ff7705c08f6b6d7e59ce2f0a76a20418e6120ad67e84042a41453fee13bc88087121eee8331ede12b04a3f798cdf78be417caf4b8184cdf13661e5b4b1f7575b283297cc4b7b9635ffc733b56a0f1146b44c6f6761cdf112de94949a6879bad255a17c27061d6255b27ef5da36466d127a152b74dbc911dfbf28a7b31eb588a3e3965445ae7750c8b47356974f16d305d9ce57f5e58c1d5ae9217fd233d2dcba775651931eb04d21b2c915c5be66b6be672553fd5442bc3f0ae4c0653e27eeab0408259280d000144196f63ec8a0d66ede0cab66e6090b0c9a22e467f8c815a7a83184799755a36bf63839cab05d6bf3543110ee686c0b668cadb2eae55206b0ae2fb62aa66f030d3ebc473a4464937a8c47bd160839ea063169a11c1de54865c75d9d3d81c7aced40775b5767b5289a93c3c3274828cb5d4ecca72958e0a49dab893f802aa8eef62812d5e956e82e2cff54d011f6353d1c959e53156c78410216f1bcc70261c667f8a90eb324f203ff3de91556f53bfa4b2304462081bfd5ccf64e7da5a5c1de9179d5388fd2d8653b747d6dcc3bcfdebe16ef086e1837864b0f23f2500299fabcf89bcd1b7de12ad6ab6e326c5c0520139ec5cef1b3f6da73eb929b4f264aa455ed3fb2892f4cae694642aa0f410473def3f89f54dcda73d5ae96571cba8bc097153ed2bb2302c8be0feaa27113618be799225294dcc308a4abb0329ee0679005156bf08557ff23282d666c9906401fce795834f0b6b2008a8f30e4a600b179824a3d137b8df3b7b06fd611fcc563ef512ca7370774383c9cbd306ab8ae9e66de1bb45c34b8138f7586dcc0e871f781638f1aa71411659056d18a0a59c520d09ef922c78cd5725b437049ab483d9291a6d5599e6191c1b3d12955723394f143d45d3cc30ea55107f6298730991bb5badf1efcbcfe1211e98424b4c01d10d6153dc71cefb3554d2b1ebe2178833bf12e4e09f31900403299a0b2f9161e1d861cb17565bbf975d23a610c4cfa20f97dac58ee284ce19e5b54204550f86138bed381d05e97136c20af1be4f2942f7768881fe69f93dc50c346fa89c29992b91f24914412c7ab99520de195b7bbe39014a117961d99d43240093ac3b4e01b721153c08baee8faca3ecc857b680a2f55cddbf1e4d2ce81590703d57eac2f8ad3711ec7b2a85911b3c4a22b5d6fe3cd8e08055c88186ceb596bcb9b825c17294e852e1fee4ce316a82fcd25116280a6a6fd144d1a719750b6d686deb87768eddfd065dbbb4ce09afa6ef192f48a5231b052817fd3d73aa473aa6d974be27373b69cf48500909b2a3caf0d68f9c8740de1093c18e1207a9b0abb9f17b7e3c13bb51bbc3af2ecdc714a2b571c1b0174ab7f8386b86e8665751621a17f74c1fd6c21c73136cacdbb7e4c45620ef23ef4e55a09b508b352d4b8a75792f35bc87f991d2245a25342d0e6a5469057ea8bbdbfb0b245b1288c394af432800bb90549b4b01b118d26890fd1b9ad449727303dbd6a5ef791776962cb935719e74f85550f9575dd7fafb01ea2d72371e7a3db81aa3747d1097843cd029eb75e79a39670f3a9fa3e916915358ff1e8b3de156f7ca0805ff81afe66e4b5c23f93515689e99146692ab9d572ea3f73cc0bdf737bf6561d0cf7f480499ce36dff8bcef08902ea828aab9a433d07368a1ee1b6bb78611faf34ef2782750f7c7e030a54b6f99b3f29b1498b778583f366b8197d4375c50cd826166f384528cf6cda896934b8611d3b4000f27ce72c95aa246c207972e19f16f723330d7a560e18448facd5c57193e09769c6a1a12814d5aa6e3a62a556d164a680ded4437b480486218a4a2bb7d8303a53296c6d199b7bc0b7a157e8054ef22ec15a4d845308a4b117ec4a7f122aa9ead719486ad005d10c2b0400223d8979efb46d2af7ceb5f1930fbf17340b000c8047229bc3ad0ff1a5d502511ae4bedab815c7d58fea560c4c8ba54256000c0a767dabfba0ebfd680d8020ee58ecc695f9dcd2ec6c8c04bd62a27cfe178a1c4acee0b03453746c989d101cfbb45b41a1148797f7782c9ed42da9e73101fce2fab029b497ac33ea62fa52b213ad62adb1fb9c5c758c80f51642aeed75df67c46cc9e76293cda127c39d0cb7ac9c2b3ea58cedf6eacb1248aa1dbec9ecac12c62f510a86f55e063a2a89a298b937583da72f521e9fe18c995f92a8f679bcc52f8ff0c5e9070393d32fa4db62e014d3afcc6b7cccaa7197fb5efd98a670c44ea840fec1e8056bf25e437baf9bcb1d96dfa393bc7a06a6353816a2842c534306e51a372a5211b3e3fbf468314a8f3991fef2e8e62e7c52d4a761c51674029c82c55a9edc6546beafc0bea2e71523b812cf8eb83b91719d07423774a5c461123af4c7aa7d8fa7b806182645a74117fe3271dbc4d4150a2f235410ddc560d907cce3da97040f100e9456ea9322c3eca0fd8d51111624488a5ec646a98b2a0a8cae2c6de21804977c081f8bae4ec03bea9e0b3ed45f07191e04f869bcd608628cfabd1d59f8cb4e240083c6ea0c8d1943d63a8b1b460a1b92bb59feb3587f8f2381921c4cd0aa7d7126d4e1cfd78500f0e980ad784b63656b6d3e364ca35805f164b2ee8a9e03dc6e797ad879503918da338a489c17b5b0f1a8814c0393dbeefbc7d0e9a08a97c7e1c411b40db70a9a62a9693fecc37c6f6673f6bd9ee1c2222d3f607587fe0b17ca2930ecf181add5b1f93c027aa92f4755297d8dbf01e77a2216c85f452bd208be21278a25ef6980e06ef073590b18dba644b126338988e6b1d62fccaa3c7b13c40c49e25ab877388fbce2037c4e2c9364e6235d57f0e9e59269c4a4ec57e3618abdc17f546136b5757dce7943b6160be886a4bc6197fea32df9c54c22ccb6cfd0cb6febe433f257885fa36821b486afa8d9038f5eb79a17f5fa8f1354f205369b2a3e48db13fc5ab01b92a5d4efe3e821ebd674ecdf64d1658b45add2a39175f7f9ecaf1f1da2b0e9b762880aa821dfad55637d7386a00dd1baf5e6499cfe2316a45d6ad08e11654c35d7b15928355581e23fa56da35ba93ebe7fc2102098efcf6df3d453"
}
TheGraph: Retrieve the list of keys on a withdrawal channel
{
  vFactories(where:{address:"YOUR_FACTORY_ADDRESS"}) {
    withdrawalChannels(where:{withdrawalChannel:"WITHDRAWAL_CHANNEL"}) {
      keys {
        publicKey
      }
    }
  }
}
{
  "data": {
    "vFactories": [
      {
        "withdrawalChannels": [
          {
            "keys": [
              {
                "publicKey": "0x98542f9ae1aefd3ad0b5cb94c4d434bcca5ebdeb21b7646c0745f992387771f70dd3c09a66e4a1b8244992a2f4ac0718"
              },
              {
                "publicKey": "0x8c834878d7ca57ad32494be897f34791f9fced3fd242de0935570649395ae52eb73be15c040663c4218af946c12db1d7"
              },
              {
                "publicKey": "0x8e3f6d114092621f36498722089d26091c5ef4217e2038158a377d4121f25e7f41c7bbce91165ab1c6e40e63275d4eea"
              },
              {
                "publicKey": "0xadd2ec37d0203d1af0457e6bd0907851745baf1b3d2367978ba2f00f428ee6f0fdb7fb8d8ec3304cabbfcc69904d0688"
              },
              {
                "publicKey": "0xb44ea21440ea3078329d0412cb40d679814ec9e6a9f72fa47401c7b459295078cce882bd5098643f31cc9dbdddb8cc83"
              },
              {
                "publicKey": "0xb3765a0872c85bd640f4065e8ea21c6f2361d7e253818fec6a839c2799f8db89f3b5b66806ea05bed24c9eca4a523a26"
              },
              {
                "publicKey": "0x84f895a8993e5aaec47f2993b5b4d530e3e50e6c215ea74509fffe76622db0e3bda8fed3846143fd299429244ec98d24"
              },
              {
                "publicKey": "0x85b2e15746db3b072151d8a89d17a81a57cfb59ec65fa7a04e7b48d31731a1a4ef0502d72bb423045a2445774d1faea2"
              },
              {
                "publicKey": "0x95632b6d8a1b471582dd42cc5cf09df37bb396f7edc30e581ae6b3baab35f97cd6170532ebf434c074b29d8185209a87"
              },
              {
                "publicKey": "0xaf7691cc35be49bfb33c0e321105b8df05a5a6bd4326ae42d9a238acfedf2b4587319009e0d308e924749bf06eaf2984"
              }
            ]
          }
        ]
      }
    ]
  }
}js

Approving the newly added keys

When keys are approved, authorized depositors on the withdrawal channel will be able to fund them. This means that it is a very critical action, that only the ADMIN of the vFactory can perform. We suggest using a multisig where a quorum of members will be able to run the verification script on their end to ensure that the current keys of the channel are valid.

    function approve(bytes32[] calldata withdrawalChannels, uint256[] calldata limits, uint256[] calldata snapshots) external;

The approve method of the vFactory is able to change the staking limit of several withdrawal channels at once. The parameters are the following ones:

  • withdrawalChannels: The list of withdrawal channels that we will approve

  • limits: The staking limits of the withdrawal channels, at the same indexes

  • snapshots: This parameter will ensure that the approval only passes if there has been no modification to the channel since the provided snapshot block number. When perform the off-chain verifications, quorum members should use the same snapshot block number to perform their verifications.

vsuite-utils: Verifying all the keys on a channel before an approve call

go run main.go factory verify-withdrawal-channel \
    --eth-rpc-url $ETH_RPC_URL \
    --block-tag finalized \
    --factory-address $FACTORY_ADDRESS \
    --withdrawal-channel $WITHDRAWAL_CHANNEL \
    --network prater \
    --beacon-rpc-url $BEACON_RPC_URL
Starting analysis of withdrawal channel WITHDRAWAL_CHANNEL
Starting withdrawal channel analysis at finalized block 9244029
Loaded withdrawal channel details: total=10, limit=0, funded=0
All keys are ok
  • In the case where you have a quorum that needs to vote on an approval, the proposal creator can run this script with --block-tag finalized to craft a finalized report. Then, the block number at which the report was made can be used as the snapshots value for the verified withdrawal channel

  • Quorum members can run the same command, but should replace --block-tag finalized by --block-tag <snapshot block number>

Once we know all the keys are valid, we can approve them ! In the following example we use cast but only as illustration for testnet, we strongly recommend using a strong multisig on mainnet for this purpose.

cast: Approving the keys on a withdrawal channel

from=ADMIN


cast send $FACTORY_ADDRESS \
    "approve(bytes32[],uint256[],uint256[])" \
    "[$WITHDRAWAL_CHANNEL]" "[10]" "[$SNAPSHOT_BLOCK]" \
    --rpc-url $ETH_RPC_URL
  • You can add the -i flag to interactively provide the private key for the transaction

  • You can add --ledger --from YOUR_ADDRESS to use a Ledger device to perform the transaction

The vFactory is now ready for the vPool to purchase up to 10 validators !

Last updated