Hyperledger Sawtooth Transaction Families: Settings

0
2681

This article is a continuation of our articles in the series on Hyperledger Sawtooth. It is primarily aimed at open source enthusiasts with an interest in blockchain technologies and a working knowledge of the Hyperledger Sawtooth project.

The Hyperledger Sawtooth Settings transaction family provides a method for storing on-chain configuration settings. The settings are stored in state, and as a result of this transaction family, play a critical role in the operation of a validator. For example, the consensus module uses these settings; in the case of PoET, one cross-network setting is the target wait time (which must be the same across validators), and this setting is stored as sawtooth.poet.target_wait_time. Other parts of the system use these settings similarly; for example, the list of enabled transaction families is used by the transaction processing platform. In addition, pluggable components such as transaction family implementations can use the settings during their execution. This design supports two authorisation options:

  • A single authorised key which can make changes.
  • Multiple authorised keys. In the case of multiple keys, a percentage of votes signed by the keys is required to make a change.

States in settings

This section describes how settings are stored and addressed using the Settings transaction family. The settings data consists of settings/value pairs. A setting is the name for the item of the configuration data. The value is the data in the form of a string. Settings are name spaced using dots. For example:

sawtooth.poet.target_wait_time

sawtooth.validator.max_transactions_per_block

The Settings transaction family also uses settings for its own configuration. For example:

sawtooth.settings.vote.authorized_keys

sawtooth.settings.vote.approval_threshold

sawtooth.settings.vote.proposals

Definition of setting entries

The following protocol buffers define setting entries:

// Setting Container for the resulting state

message Setting {

// Contains a setting entry (or entries, in the case of collisions).

message Entry {

string key = 1;

string value = 2;

}

// List of setting entries - more than one implies a state key collision

repeated Entry entries = 1;

}

sawtooth.settings.vote.proposals is a base64 encoded string of the protobuf message SettingCandidates. This setting cannot be modified by a proposal or a vote. The setting is stored as defined by the following protocol buffers definition. The value returned by this setting is a base64 encoded SettingCandidates message:

// Contains the vote counts for a given proposal.

message SettingCandidate {

// An individual vote record

message VoteRecord {

// The public key of the voter

string public_key = 1;

// The voter’s actual vote

SettingVote.Vote vote = 2;

}

// The proposal id, a hash of the original proposal

string proposal_id = 1;

// The active proposal

SettingProposal proposal = 2;

// list of votes

repeated VoteRecord votes = 3;

}

// Contains all the setting candidates up for vote.

message SettingCandidates {

repeated SettingCandidate candidates = 1;

}

Addressing

When a setting is read or changed, it is accessed by addressing it using the following algorithm:

  • Setting keys are broken into four parts, based on the dots in the string. For example, the address for the key a.b.c is computed based on a, b, c and the empty string.
  • A longer key, for example a.b.c.d.e, is still broken into four parts, but the remaining pieces are in the last part: a, b, c and d.e.
  • Each of these pieces has a short hash computed (the first 16 characters of its SHA256 hash in hex) and is joined into a single address, with the settings name space (000000) added at the beginning.

For example, the setting sawtooth.settings.vote.proposals could be set like this:

>>> ‘000000’ + hashlib.sha256(‘sawtooth’.encode()).hexdigest()[:16] + \

hashlib.sha256(‘config’.encode()).hexdigest()[:16] + \

hashlib.sha256(‘vote’.encode()).hexdigest()[:16] + \

hashlib.sha256(‘proposals’.encode()).hexdigest()[:16]

‘000000a87cb5eafdcca6a 8b79606fb3afea5bdab27447 4a6aa82c1c0cbf0fbcaf64c0b’

Transaction header

Inputs and outputs: The inputs for the config family transactions must include:

  • The address of sawtooth.settings.vote.proposals
  • The address of sawtooth.settings.vote.authorized_keys
  • The address of sawtooth.settings.vote.approval_threshold
  • The address of the setting being changed
  • The outputs for the config family transactions must include:
  • The address of sawtooth.settings.vote.proposals
  • The address of the setting being changed

Dependencies: None.

Family:

  • family_name: sawtooth_settings
  • family_version: 1.0

Transaction payload

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

”// Setting Payload

// - Contains either a proposal or a vote.

message SettingPayload {

// The action indicates data is contained within this payload

enum Action {

// A proposal action - data will be a SettingProposal

PROPOSE = 0;

// A vote action - data will be a SettingVote

VOTE = 1;

}

// The action of this payload

Action action = 1;




// The content of this payload

bytes data = 2;

}

// Setting Proposal

// This message proposes a change in a setting value.

message SettingProposal {

// The setting key. E.g. sawtooth.consensus.module

string setting = 1;

// The setting value. E.g. ‘poet’

string value = 2;

// allow duplicate proposals with different hashes

// randomly created by the client

string nonce = 3;

}

// Setting Vote

// In ballot mode, a proposal must be voted on. This message indicates an

// acceptance or rejection of a proposal, where the proposal is identified

// by its id.

message SettingVote {

enum Vote {

ACCEPT = 0;

REJECT = 1;

}

// The id of the proposal, as found in the

// sawtooth.settings.vote.proposals setting field

string proposal_id = 1;

Vote vote = 2;

}

Settings transaction family in action

Initially, the transaction processor gets the current values of sawtooth.settings.vote.authorized_keys from the state. The public key of the transaction signer is checked against the values in the list of authorised keys. If it is empty, no settings can be proposed, save for the authorised keys.

A Propose action is validated. If it fails, it is considered an invalid transaction. A proposal_id is calculated by taking the sha256 hash of the raw SettingProposal bytes as they exist in the payload. Duplicate proposal_ids causes an invalid transaction. The proposal will be recorded in the SettingProposals stored in sawtooth.settings.vote.proposals, with one ‘accept’ vote counted. The transaction processor outputs a DEBUG-level logging message similar to the following:

“Adding proposal {}: {}”.format(proposal_id, repr(proposal_data)

A Vote action is validated, checking to see if proposal_id exists, and the public key of the transaction has not already voted. The value of sawtooth.settings.vote.approval_threshold is read from the state.

  • If the ‘accept’ vote count is equal to or above the approval threshold, the proposal is applied to the state. This results in the above INFO message being logged. The proposal is deleted from the SettingProposals record.
  • If the ‘reject’ vote count is equal to or above the approval threshold, then it is deleted from sawtooth.settings.vote.proposals and an appropriate debug logging message is logged.

Otherwise, the vote is recorded in the list of sawtooth.settings.vote.proposals by the public key and vote pair.

Validation of configuration settings is as follows:

  • sawtooth.settings.vote.approval_threshold must be a positive integer; it must be between 1 (the default) and the number of authorised keys, and include both.
  • sawtooth.settings.vote.proposals may not be set by a proposal.

LEAVE A REPLY

Please enter your comment!
Please enter your name here