Validator Registry Transaction Family Specification


The validator registry transaction family provides a way to add new validators to the network.

The validator’s information is used by poet_consensus to verify that when a validator tries to claim a block it is following the block claiming policies of PoET consensus. For example, the Z policy will refuse blocks from a validator if it has already claimed a disproportionate amount of blocks compared to the other validators on the network. The C policy says that a validator may not claim blocks, after being added to the registry or updating their information, until a certain number of blocks are claimed by other participants. The number of blocks claimed since signup can be found by checking the current block number against the block number that is stored in the validator information when the validator registry information has either been added or updated. And finally the K policy requires new signup information to be sent to the validator registry after it has claimed a maximum number of block. If the validator is not found in the validator registry the block will be rejected since there is no way to validate.

Currently the number of blocks claimed by a specific validator is stored within the poet_consensus, not within the validator registry.


This section describes in detail how validator information, including identification and signup data, is stored and addressed using the validator registry transaction family.

The following protocol buffers definition defines the validator info:

message ValidatorInfo {
  // Used to check if the validator's signup has been revoked.
  string registered = 1;

  // The name of the endpoint. It should be a human readable name.
  string name = 2;

  // The validator's public key (currently using signer_pubkey as this is
  // stored in the transaction header)
  string id = 3;

  // This is the protocol buffer described below.
  SignUpInfo signup_info = 4;

  // The block number that this ValidatorInfo was set on.
  // This is used as a form of time to help with the enforcement of the C test.
  int32 block_num = 5;

The above ValidatorInfo includes the following protocol buffer:

message SignUpInfo {
  // Encoded public key corresponding to private key used by PoET to sign
  // wait certificates.
  string poet_public_key = 1;

  // Information that can be used internally to verify the validity of
  // the signup information stored as an opaque buffer.
  string proof_data = 2;

  // A string corresponding to the anti-Sybil ID for the enclave that
  // generated the signup information.
  string anti_sybil_id = 3;

Currently the poet_enclave needs a way to check how many validators are currently registered within the validator registry. It also needs a way to find the validator_id associated with an anti-sybil_id. A ValidatorMap, where the anti_sybil_id is the key and the validator_id is the value solves both of these problems.

message ValidatorMap {
    // Contains a validator entry where the key is an  anti_sybil_id,
    // and the value is a validator_id
    message Entry {
        string key = 1;
        string value = 2;

    // List of validator entries
    repeated Entry entries = 1;

This can be used to check the number of validators registered. This information is used to decide what number of blocks a validator has to wait for before it can start claiming blocks after it adds new signup information to the validator registry. This check is necessary because if the number of blocks that must be waited on is greater than the number of validators minus one, it is possible for the network to get into a state where nobody can publish blocks because the validators are all waiting for more blocks to be committed or their signup information to be added to a block.

Validator registry transaction would not be able to be done at the same time as any other transaction as an update to the ValidatorMap is necessary. However all other transaction that need to access the state set by the validator registry, can be done in parallel since it will only be a read and the statics for each validator is stored in the poet_enclave. If this was changed so that the stats were stored in the validator registry this would require a write to state every time a block is published and would reduce the ability for parallelism.


When a validator’s signup info is registered or updated it should be accessed using the following algorithm:

Addresses for the validator registry transaction family are set by adding sha256 hash of the validator’s id to the validator registry namespace. The namespace for the validator registry will be the first 6 characters of the sha256 hash of the string “validator_registry”, which is “6a4372” For example, the validator signup info of a validator with the id “validator_id” could be set like this:

>>>“6a4372” + hashlib.sha256('validator_id'.encode("utf-8")).hexdigest()
The map of the current registered validator_id should be stored at the
following address:
>>>“6a4372” + hashlib.sha256('validator_map'.encode()).hexdigest()

Transaction Payload

Validator registry transaction family payloads are defined by the following protocol buffers code:

message ValidatorRegistryPayload {
  // The action that the transaction processor will take. Currently this
  // is only “register”, but could include other actions in the futures
  // such as “revoke”
  string verb = 1;

  // The name of the endpoint. It should be a human readable name.
  string name = 2;

  // Validator's public key (currently using signer_pubkey as this is
  // stored in the transaction header)
  string id = 3;

  // This is the protocol buffer described above.
  SignUpInfo signup_info = 4;

  // The header signature for the ValidatorRegistryPayload transaction. This
  // will be used to look up the block the ValidatorInfo was committed on.
  string transaction_id = 5;


Transaction Header

Inputs and Outputs

The inputs for validator registry family transactions must include:

  • the address of validator_id
  • the address of validator_map

The outputs for validator registry family transactions must include:

  • the address of validator_id
  • the address of validator_map




  • family_name: “sawtooth_validator”
  • family_version: “1.0”


The encoding field must be set to “application/protobuf”.


Untrusted python code, that is a part of the transaction processor, will verify the attestation verification report for the signup information. It is important to note, that the IAS report public key will need to be on the block chain and it will need to be set on configuration. This will allow both the simulator logic and real SGX logic to be the same.

The validator_registry transaction will need to be injected into a block by poet_consensus. At this time, the block number for the block that poet_consensus is checking will be set into the transaction.

The journal will need a new method to retrieve this information.

If the validator_name does not match syntactic requirements, the transaction is invalid. The current requirement is that the validator_name is 64 characters or less.

If the validator_id does not match the transaction signer, the transaction is invalid. The validator_id should be the same as the signer_pubkey stored in the transaction header.

The signup info needs to be verified next. The signup info, public key hash, and the most recent wait_certificate_id are passed to verify the signup data.

If any of the signup info checks fail verification, the validator_registry transaction is rejected and a invalid transaction response is returned.

If the transaction is deemed to be valid, the validator_id is used to find the address where the validator_info should be stored. Store the serialized ValidatorInfo protocol buffer in state at the address as mentioned above. If this validator is new (not updating its SignUpInfo), the validator’s id needs to be added to the validator_map.

For any previous entries with the anti_sybil_id, the public key is revoked before resetting the new information.