Skip to content
This repository was archived by the owner on Jul 4, 2022. It is now read-only.

Doughnut

Hoani Bryson edited this page Apr 28, 2020 · 20 revisions

Doughnuts are Proofs of Delegation between two or more cryptographic keypairs. Doughnuts let us prove that one address delegates something to another address.

For example, if Alice wants to let Bob use her account, but only to send tokens to Charlie. This could be achieved using a doughnut by doing the following:

  • Alice creates a doughnut which sets:
    • Alice as the issuer
    • Bob as the holder
    • Sending funds to Charlie as a rule in the permission domain
  • Alice signs the doughnut and gives it to Bob
  • When Bob wants to send funds to Charlie with Alice's account, Bob should attach the doughnut when signing and sending an extrinsic.

This guide covers:

  1. How to create a doughnut in your D'App using the Javascript SDK
  2. How to send an encoded doughnut from your D'App to a Pl^g blockchain
  3. How to write a DispatchVerifier hook on your blockchain to interpret a doughnut

Using Javascript SDK to create a doughnut

The following instructions will help you use the Javascript SDK to play with doughnut.

1. Install plug-doughnut

Run npm or yarn command to install the plug-doughnut package.

npm install plug-doughnut or yarn add plug-doughunut

2. Create doughnut

Use the Doughnut builder pattern to create a doughnut with necessary fields. (Note: the issuer and holder should be set to the account public keys)

const Doughnut = require('plug-doughnut').Doughnut;
const testingPairs = require('@polkadot/keyring/testingPairs');

const keyring = testingPairs.default({ type: 'ed25519'});
 
const issuer = keyring.alice.publicKey;
const issuer_private_key = keyring.alice.sign()
const holder = keyring.bob.publicKey;
const expiry = 100;
const not_before = 1;
 
const doughnut = Doughnut
    .new(issuer, holder, expiry, not_before)
    .add_payload_version(1)
    .add_domain('awesome', [1, 2, 3])
    .sign(issuer_private_key)
};

For doughnuts to be valid, they must be signed by the issuer.

You can access the fields in the doughnut with getter functions:

const issuer = doughnut.issuer;
const holder = doughnut.holder;
const expiry = doughnut.expiry;

3. Encode the doughnut

To send a doughnut, it must be encoded as a binary object. Doughnuts are encoded as u8 arrays by using doughnut.encode().

Encoded doughnut binaries can be decoded into Doughnut objects using Doughnut.decode([u8]).

const encoded_doughnut = doughnut.encode();
const doughnut = Doughnut.decode(encoded_doughnut);

An encoded doughnut is a Uint8Array, for example:

[
 64, 24, 64, 22, 126, 150, 15, 176, 190, 210, 156, 179, 149, 142, 84, 153, 4, 203, 61, 62,
  185, 76, 45, 162, 220, 254, 188, 163, 187, 63, 39, 186, 113, 126, 12, 60, 121, 179, 67,
  105, 121, 244, 39, 137, 174, 55, 85, 167, 73, 111, 50, 249, 10, 145, 141, 125, 105, 138,
  38, 93, 144, 45, 224, 70, 206, 246, 116, 196, 94, 16, 0, 115, 111, 109, 101, 116, 104, 105,
  110, 103, 0, 0, 0, 0, 0, 0, 0, 128, 0, 115, 111, 109, 101, 116, 104, 105, 110, 103, 69,
  108, 115, 101, 0, 0, 0, 128, 0, 0, 0, 8, 185, 184, 138, 72, 86, 187, 125, 166, 109, 176,
  31, 104, 162, 235, 78, 157, 166, 8, 137, 191, 33, 202, 128, 138, 165, 73, 244, 67, 247, 37,
  13, 218, 44, 244, 54, 137, 179, 56, 110, 152, 170, 180, 218, 107, 177, 170, 58, 91, 62, 24,
  240, 248, 244, 13, 51, 235, 3, 21, 63, 79, 192, 137, 6
]

Send doughnut to blockchain

1. Create a signed and encoded doughnut

const Doughnut = require('plug-doughnut').Doughnut;
const domain = 'awesome';

const doughnut = Doughnut
    .new(issuer, holder, expiry, not_before)
    .add_domain(domain, 0x00)
    .sign(issuer_private_key)
    .encode();

2. Add the encoded doughnut to an extrinsic

We will do a balances.transfer and add the encoded doughnut in the option parameter.

// Create the API and wait until ready
const provider = new WsProvider("ws://localhost:9944");
const types = PlugRuntimeTypes.default;
const api = await ApiPromise.create({ provider, types });

// Send transfer extrinsic with doughnut
const options = { doughnut: doughnut.encode() };
const txHash = await api.tx.balances
  .transfer(keyring.charlie.address, "1_500_000_000")
  .signAndSend(keyring.bob, options);

Get domain through dispatch verifier

In this part we are trying to show an example how to get the domains in the verify_dispatch function which is implemented in the plug-blockchain.

In the node-template we define DelegatedDispatchVerifier as DummyDispatchVerifier.

type DelegatedDispatchVerifier = DummyDispatchVerifier<Self::Doughnut, Self::AccountId>;

So we can modify the verify_dispatch function for DummyDispatchVerifier to print out the domains:

use sp_runtime::{Doughnut, DoughnutV0};
use log::{trace};

fn verify_dispatch(doughnut: &Self::Doughnut, module: &str, method: &str) -> Result<(), &'static str> {
    let doughnut = DoughnutV0::try_from(doughnut.clone()).unwrap();
    let domain = doughnut.get_domain("awesome_node").unwrap()[0];
    trace!("awesome_node domain's value: {}", domain);

    Ok(());
}

After send the transfer extrinsic to the local node. We can see the output in the console log.

Config permission domain

We can set a permission domain specific for our awesome_node in the DelegatedDispatchVerifier implementation for DummyDispatchVerifier. Only doughnut contains awesome_node domain will be verified.

impl<D: PlugDoughnutApi, A: Parameter> DelegatedDispatchVerifier for DummyDispatchVerifier<D, A> {
	type Doughnut = D;
	type AccountId = A;

        // The doughnut permission domain it verifies
	const DOMAIN: &'static str = "awesome_node";

	fn verify_dispatch(doughnut: &Self::Doughnut, module: &str, method: &str) -> Result<(), &'static str> {
            let doughnut = DoughnutV0::try_from(doughnut.clone()).unwrap();
            let domain = doughnut.get_domain(Self::DOMAIN).ok_or("Doughnut does not grant permission for awesome_node domain")?;
            trace!("Permission domain for awesome_node is verified, domain value: {}", domain);

            Ok(());
        }
}

We can try to send same kind of transactions included doughnut with different domains. Only doughnut contains permission domain: domain: "awesome_node" would be verified and the transaction could be processed successfully

PL^G


Getting Started


PL^G Component Guides


Advanced Topics


External Links

Clone this wiki locally