son-consensus-communication-and-transaction-signing-on-chain-lld

1.Purpose

The purpose of this document is to provide low-level design for the communication mechanism between SONs and transaction signing.

2. Scope

The design requirement listed in this document will be limited to the communication, consensus and transaction signing of SONs on-chain.

The document also proposes a design to create a way to know the health of an active SON.

The document takes only the Bitcoin sidechain into consideration as of this writing but can be extended to others in the future.

The document doesn’t deal with sidechain deposit handling or withdrawal.

The assumption is that we use a single PW design without creating deposit addresses.

Note1: For the simplicity of the examples, let's assume we have 3 SONs.

Note2: Code snippets provided in the document are pseudo-codes, they just illustrate basic logic and may not be syntactically correct.

2.1 Glossary

Consensus-SON: Top 2/3rd active SONs who signs transactions on behalf of the 15 active SONs takes part in the consensus and the operations. They are termed as Consensus-SONs

SON-Quorum: The availability of SONs with 2/3rd accumulated stakes via votes is a SON-Quorum.

3. Background

The Bitcoin Sidechain functionality has been implemented in the Peerplays blockchain but it doesn't take into account, the change of witnesses.

As per the current implementation of Sidechain, a multi-sig bitcoin address will be created on the bitcoin blockchain to hold the bitcoins that have been deposited into the pBTC accounts of the Peerplays users.

Every Peerplays witnesses will have a bitcoin transaction signing key for this multi-sig bitcoin address and will be required to sign any withdrawal transaction.

When a witness changes, the transaction signing key of the outgoing witness needs to be removed from the multi-sig bitcoin address and the key of the incoming witness needs to be added.

The suggested proposal is to make the Sidechain code available as a plugin and assign the responsibility for running the sidechain code to separate nodes called the Sidechain Operating Nodes (SONs).

The SON functionality will be independent of the witness functionality and SONs don't need to be changed much often.

4. Basic Idea

One of the biggest challenges of the SON project is to achieve consensus among the SONs on the blockchain.

The consensus is required among SONs to perform the following tasks,

  1. Transferring BTC to User address after withdrawal.

  2. Transferring BTC from old PW to new PW.

  3. To issue pBTC.

  4. To report on another active SON for being inactive.

Also, the challenge is that SONs have to make sure they do all the work with respect to the sidechain and relieve the witnesses of the extra load of listening to the sidechain and handling deposits and withdrawals.

But the records/statistics of the sidechain transactions, issuance of pBTC, SON health status, etc have to be kept on-chain.

The Statics of the sidechain transactions, issuance of pBTC, SON health, availability of SONs etc have to be kept on the Peerplays blockchain.

This provides a robust mechanism for anyone to get the historical performance of the SONs, near real time view of the chain fund movements.

The basic idea of how to achieve consensus is as follows,

  1. Create a peerplays account for a sidechain SON-195 - Getting issue details... STATUS , let's name it ‘son-btc-account’.

  2. Owners for the account are SONs.

  3. Weights of their signatures are proportional to the votes they receive.

  4. The weight threshold of the account is 2/3 of the total weight.

  5. Any operation related to the SON tasks has the paying account as the son account that's created.

  6. All such operations are encapsulated in proposals.

  7. So for any SON proposal to be successfully executed it needs to have 2/3 of the total authority of SONs.

Eg. SON1 weight = 7 SON2 weight = 5 SON3 weight = 3 Total weight = 15 Weight Threshold = 2/3 * 15 = 10

So signatures of one of the SON combinations (1,2), (1,3), (1,2,3) are required to execute a successful transaction/proposal.

Psuedo-code is as follows,

const auto& son_btc_account = create<account_object>( [&]( account_object& obj ) {

obj.name = "son-btc-account";

obj.statistics = create<account_statistics_object>([&]( account_statistics_object& acc_stat ){ acc_stat.owner = obj.id; }).id;

obj.owner.weight_threshold = 10;

obj.active.weight_threshold = 10;

obj.membership_expiration_date = time_point_sec::maximum();

obj.network_fee_percentage = GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;

obj.lifetime_referrer_fee_percentage = GRAPHENE_100_PERCENT - GRAPHENE_DEFAULT_NETWORK_PERCENT_OF_FEE;

// Add Authority Weights

obj.owner.add_authority( son1, 7 );

obj.active.add_authority( son1, 7 );

obj.owner.add_authority( son2, 5 );

obj.active.add_authority( son2, 5 );

obj.owner.add_authority( son3, 3 );

obj.active.add_authority( son3, 3 );

});

5. How to get SON Public Keys for Multi-Sig PW creation

We need SON public keys to create a multi-sig Bitcoin address for PW.

We get this from son_object::signing_key stored in the database.

The key is assigned during the registration of SON through CLI (refer to create_son).

We can also extend the create_son API to include a sidechain public key that is different from peerplays public key. SON-190 - Getting issue details... STATUS

6. Creation and Signing of BTC Transaction

As discussed above, we need to egress BTC transactions in case of transferring BTC to a user after withdrawal and transferring BTC from old PW to new PW.

Following steps will accomplish this,

  1. A Proposal is created with the fee-paying account as the individual SON account who is proposing.

  2. Create the BTC raw transaction.

  3. Create a BTC Transaction Send Operation with fee payer as the peerplays bitcoin sidechain account son-btc-account(weight threshold as 2/3 of SONs total weight).

  4. Encapsulate the send operation in the proposal created.

  5. Ensure the proposal lifetime is sufficient ( N*(number of SONs)*(block_creation_interval) where N is configurable i.e. 1,2,3,4 etc).

  6. Publish the proposal on to the peerplays blockchain.

  7. Each SON after receiving the proposal validated the data against the sidechain BTC database stored in the plugin locally.

  8. If appropriate, each SON signs the BTC transaction.

  9. Create a BTC Transaction Sign Operation with fee payer as the individual SON account who is signing (i.e. SON1 account, SON2 account, etc).

  10. Sign the BTC transaction and store the signature in the sign operation.

  11. Encapsulate the operation in a peerplays transaction, sign it and send it to peerplays blockchain.

  12. The sign operation is evaluated by checking the SON signature on BTC Transaction with the SON public key registered on peerplays blockchain.

  13. If evaluation succeeds, during the do_apply of sign operation the verified signatures are stored in the send operation encapsulated in the proposal created initially.

  14. After the required signatures are available or lifetime of the proposal is expired and if enough signatures to reach the weight threshold of the son-btc-account are present, the proposal is executed, signatures are sorted and BTC transaction is sent out.

As the weights in the bitcoin script and weights of the son-btc-account are the same, if proposal is executed successfully the bitcoin transaction will also be successful.

Creating send-BTC transaction proposal pseudo-code,

// Create BTC Transaction with the vins and vouts stored in SON Database

btc_tx = create_btc_transaction();

// Create BTC Transaction Send Operation

bitcoin_transaction_send_operation btc_send_op;

btc_send_op.payer = son-btc-account;

// Get the BTC vector of inputs

btc_send_op.vins = btc_tx.get_valid_vins();

// Fill the BTC vector of outputs

btc_send_op.vout = fill_vouts();

// Store BTC Transaction data i.e. hash, amount, fee etc.

btc_send_op.transaction = btc_tx;

// Create the proposal and encapsulate the send operation in it.

proposal_create_operation proposal_op;

proposal_op.fee_paying_account = son_account;

proposal_op.proposed_ops.push_back( op_wrapper( btc_send_op ) );

uint32_t lifetime = ( get_global_properties().parameters.block_interval * get_global_properties().active_sons.size() ) * 4;

proposal_op.expiration_time = time_point_sec( head_block_time().sec_since_epoch() + lifetime );

Each SON signing the send-BTC transaction proposal pseudo-code,

bitcoin_transaction_sign_operation sign_operation;

sign_operation.payer = current_son.son_account;

// Proposal ID of the send-BTC Transaction Operation

sign_operation.proposal_id = proposal_id;

// Assign SON ID

sign_operation.son_id = my_son_id;

// Get the vins from the proposal

auto vins = btc_send_op.vins;

// Sign the transaction

sign_operation.signatures = sign_transaction(btc_send_op.transaction, vins);

BTC Sign Operation Evaluator pseudo-code,

void_result bitcoin_transaction_sign_evaluator::do_evaluate( const bitcoin_transaction_sign_operation& op ) {

const auto& proposal_itr = proposal_idx.find(op.proposal_id);

FC_ASSERT(proposal_idx.end() != proposal_itr, "proposal not found");

FC_ASSERT(is_signed_by_active_son(op.son_id), "Not signed by active SON.");

const auto& son_object = get_son_object(op.son_id);

bytes public_key( public_key_data_to_bytes( son_object.signing_key.key_data ) );

auto btc_send_op = proposal_itr->proposed_transaction.operations[0].get<bitcoin_transaction_send_operation>();

// Get the vector of input txs from send-operation from proposal and verify sigs with SON Public key

auto vins = btc_send_op.vins;

FC_ASSERT( check_sigs( public_key, op.signatures, vins, btc_send_op.transaction ) );

}

void_result bitcoin_transaction_sign_evaluator::do_apply( const bitcoin_transaction_sign_operation& op ) {

database& d = db();

// Get the send-BTC Transaction Operation Proposal created

const auto& proposal = op.proposal_id( d );

// Modify the operation by adding the validated signature

d.modify( proposal, [&]( proposal_object& po ) {

auto bitcoin_transaction_send_op = po.proposed_transaction.operations[0].get<bitcoin_transaction_send_operation>();

for( size_t i = 0; i < op.signatures.size(); i++ ) {

bitcoin_transaction_send_op.transaction.vin[i].scriptWitness.push_back( op.signatures[i] );

}

po.proposed_transaction.operations[0] = bitcoin_transaction_send_op;

});

// Create proposal_update_operation and add the SON's approval to the proposal.

proposal_update_operation update_op;

update_op.fee_paying_account = op.payer;

update_op.proposal = op.proposal_id;

update_op.active_approvals_to_add = { op.payer };

d.apply_operation(*trx_state, update_op);

return void_result();

}

BTC Send Transaction Operation Evaluator Pseudo-code,

void_result bitcoin_transaction_send_evaluator::do_evaluate( const bitcoin_transaction_send_operation& op ) {

// Check if the payer of the operation is the multi signatory SON BTC peerplays account.

FC_ASSERT( son-btc-account == op.payer );

return void_result();

}

object_id_type bitcoin_transaction_send_evaluator::do_apply( const bitcoin_transaction_send_operation& op ) {

bitcoin_transaction_send_operation& mutable_op = const_cast<bitcoin_transaction_send_operation&>( op );

database& d = db();

// Make the transaction ready by sorting sigs etc

finalize_btc_tx(mutable_op);

auto new_vins = create_transaction_vins( mutable_op );

// Create the bitcoin_transaction_object to be stored on the chain.

const bitcoin_transaction_object& btc_tx = d.create< bitcoin_transaction_object >( [&]( bitcoin_transaction_object& obj )

{

obj.pw_vin = mutable_op.pw_vin.identifier;

obj.vins = new_vins;

obj.vouts = mutable_op.vouts;

obj.transaction = mutable_op.transaction;

obj.transaction_id = mutable_op.transaction.get_txid();

obj.fee_for_size = mutable_op.fee_for_size;

});

// Send out the transaction.

send_bitcoin_transaction( btc_tx );

return btc_tx.id;

}

7. SON Heart Beats

Heartbeat transactions are sent by active SONs to notify the blockchain that they are active.

The frequency of the heartbeat can be configured through chain parameter extensions.

We need these in order to mark active SONs as in_maintenance if heartbeats are missed.

Evaluation and application of the heartbeat operation pseudo-code,

void_result son_evaluator::do_evaluate(const son_heartbeat_operation& op) {

// Get SON Object from son id through son_index

son_object son = get_son_by_id(op.son_id);

// Payer for hearbeat operations are individual SON accounts

FC_ASSERT(op.payer == son.son_account);

return void_result();

}

void_result son_evaluator::do_apply(const son_heartbeat_operation& op) {

// Get SON Object from son id through son_index

son_object son = get_son_by_id(op.son_id);

// Get SON Statistics Object from son_stats_index

son_statistics_object son_stats = get_son_stats(son.statistics);

// Update the last active timestamp

db.modify( son_stats, [&]( son_statistics_object& _s)

{

_s.last_active_ts = op.ts;

});

}

8. Reporting SON Down

If any SON misses N number of heartbeats then one of the other active SONs can notify the peerplays blockchain about it.

N can be configurable with chain parameter extensions.

One of the rest of the active SON can raise a son_deactivate_operation to report on the SON which is down.

The fee-paying account for this operation should be the peerplays bitcoin sidechain account son-btc-account.

A proposal is created and the deactivate operation is encapsulated inside the proposal. (Fee-paying account for the proposal is individual SON account)

The lifetime of the proposal can be N*(number of SONs)*(block_creation_interval) where N is configurable i.e. 1,2,3,4 etc.

The rest of the SONs on receiving the proposal can validate and add their approvals to the proposal.

After the required signatures are available or once the proposal is expired, if it succeeds it is executed and the down SON status is set to in_maintenance on the blockchain.

If the down SON becomes active and sends a heartbeat while the deactivate proposal is active, we can leave the SON status as active but add up the downtime in statistics object.

Evaluation and application of the son deactivate operation pseudo-code,

void_result son_evaluator::do_evaluate(const son_deactivate_operation& op) {

// Get SON Object from son id through son_index

son_object son = get_son_by_id(op.son_id);

// Payer for hearbeat operations are individual SON accounts

FC_ASSERT(op.payer == son.son_account);

return void_result();

}

void_result son_evaluator::do_apply(const son_deactivate_operation& op) {

// Get SON Object from son id through son_index

son_object son = get_son_by_id(op.son_id);

// Get SON Statistics Object from son_stats_index

son_statistics_object son_stats = get_son_stats(son.statistics);

// Update the last active timestamp

db.modify( son, [&]( son_object& _son_obj)

{

_son_obj.status = son_status::in_maintenance;

});

db.modify( son_stats, [&]( son_statistics_object& _s)

{

_s.last_down_ts = op.ts;

});

}

Adding approvals by rest of the active SONs pseudo-code,

// Create proposal_update_operation and add the SON's approval to the proposal.

proposal_update_operation update_op;

update_op.fee_paying_account = op.payer;

update_op.proposal = op.proposal_id;

update_op.active_approvals_to_add = { op.payer };

d.apply_operation(*trx_state, update_op);

9. SON Issue pBTC

Once a required number of confirmations are reached for a transaction sending BTC from user bitcoin address to PW address, SON pBTC issue operation has to be raised to user’s peerplays account.

As discussed above similar proposal process is followed.

  1. A proposal is created by one active SON and the fee-paying account is set to the individual account of the SON. (lifetime same as discussed above)

  2. A bitcoin_issue_operation is created for which the fee-paying account is set to son-btc-account.

  3. The issue operation is encapsulated in the proposal and sent to the peerplays blockchain.

  4. Rest of the active SONs validate their plugin BTC databases and add approvals upon receiving the proposal.

  5. Once the required number of approvals (2/3 of SONs total weight) are added, proposal is executed and the pBTC is issued.

SON Issue pBTC pseudo-code,

void_result son_evaluator::do_evaluate(const bitcoin_issue_operation& op) {

// Check if the payer of the operation is the multi signatory SON BTC peerplays account.

FC_ASSERT( son-btc-account == op.payer );

// Check if the vin tx input address is user registered address on peerplays blockchain.

FC_ASSERT( is_address_registered(op.vin_addres))

return void_result();

}

void_result son_evaluator::do_apply(const bitcoin_issue_operation& op) {

// Get the peerplays account to issue pBTC

const auto& account_to_issue = get_account_to_issue(op.vin_address);

// Get the amount to issue i.e. equal to BTC amount transferred

const auto& amount_to_issue = get_amount_to_issue(op.amount);

// Follow the normal UIA issue procedure.

add_issue(account_to_issue, amount_to_issue);

}

Adding approvals by rest of the active SONs pseudo-code,

// Create proposal_update_operation and add the SON's approval to the proposal.

proposal_update_operation update_op;

update_op.fee_paying_account = op.payer;

update_op.proposal = op.proposal_id;

update_op.active_approvals_to_add = { op.payer };

d.apply_operation(*trx_state, update_op);

10. Choosing the SON who creates the next proposal

In all the above operations, any one of the SON has to raise the initial proposal.

If multiple proposals are raised for the same BTC transaction, only one of them is valid and the rest of them should be rejected.

To achieve this we can use one of the existing witnesses shuffled or scheduled algorithms.

With these algorithms in place, all the SONs know when is their slot to publish a proposal for the next event.

The detailed design for this is out of the scope of this document and should be part of SON plugin handling.

For the existing witness scheduling algorithm please refer to this link.

11. Data Structures

11.1 bitcoin_transaction_send_operation

We can retain most of the implementation from the sidechain branch.

It can be found here.

11.2 bitcoin_transaction_sign_operation

We can retain most of the implementation from the sidechain branch.

It can be found here.

11.3 son_heartbeat_operation

11.4 son_deactivate_operation

12. SON changes during Maintenance

One of the many things we have to do during maintenance is updating the PW and the multi signatory SON account (son-btc-account).

As discussed above the weights of signatures is the same for both PW and the multi signatory SON account, we have to change both accordingly after the SON weight changes or SON changes.

The change of PW is discussed here.

Eg. Weights of the SONs have changed from the previous example above and a new SON is active. SON1 weight = 10 SON2 weight = 5 SON4 weight = 6 Total weight = 21 Weight Threshold = 2/3 * 21 = 14

So signatures of one of the SON combinations (1,2), (1,4), (1,2,4) are required to execute a successful transaction/proposal.

For changing the weights of peerplays SON account following is the pseudo-code,

modify( sidechain_account, [&]( account_object& obj ) {

// Add new weight threshold

obj.owner.weight_threshold = 14;

obj.active.weight_threshold = 14;

// Add new authority for SON1

obj.owner.add_authority( son1, 10 );

obj.active.add_authority( son1, 10 );

// Add new authority for SON2

obj.owner.add_authority( son2, 5 );

obj.active.add_authority( son2, 5 );

// Remove the SON3 Authority

obj.owner.add_authority( son3, 0 );

obj.active.add_authority( son3, 0 );

// Add new authority for SON4

obj.owner.add_authority( son4, 6 );

obj.active.add_authority( son4, 6 );

});

13. Conclusion

This approach can greatly relieve the peerplays blockchain of any type of sidechain listening, deposit, and withdrawal operations.

Only the transactions which are needed to be logged are processed by the nodes other than SONs.

All the operations which need multiple signatures can directly be accepted by everyone on the blockchain as the proposals ensure all signatures present before execution.

The most important aspect of this design is we can re-use most of the existing bitcoin-related sidechain code which can reduce our development effort.