Activating delegated harvesting

Share your account’s importance securely with a node.

Background

Delegated harvesting enables accounts to receive rewards from creating new blocks without running a node. The following guide will show you how to delegate your account importance without compromising the account’s funds.

In this process, you will delegate your main account (M) importance to a proxy public key (R). Then,you will request a node to add your remote account (R) as a delegated harvester with the announcer account (A).

sequenceDiagram participant Main Account participant Announcer Account participant Network participant Node Main Account ->> Network: AccountKeyLinkTransaction(remotePublicKey) activate Network Network -->> Main Account: Confirms transaction deactivate Network Announcer Account ->> Network: TransferTransaction(nodePublicKey, encryptedRemotePrivateKey) activate Network Network -->> Announcer Account: Confirms the transaction deactivate Network Network -->> Node: Sends notification opt eligible remote account Node ->> Node: Adds delegated harvester Node ->> Node: Saves remote private key on disk end

Delegated harvesting activation diagram

Prerequisites

Before you can activate delegated harvesting, you need to have three accounts:

  • Main account (M) with at least 10,000 harvesting mosaic units.
  • Announcer account (A) with enough symbol.xym units to announce a transaction.
  • Remote account (R) that did not send or receive any transactions.

Method #01: Using the SDK

  1. Define your main account (M), announcer account(A) and the remote account (R) using their private keys.
// replace with network type
const networkType = NetworkType.TEST_NET;
// replace with private key
const mainAccountPrivateKey = '0000000000000000000000000000000000000000000000000000000000000000';
const mainAccount = Account.createFromPrivateKey(mainAccountPrivateKey, networkType);
const remoteAccount = Account.generateNewAccount(networkType);
// replace with private key
const announcerAccountPrivateKey = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
const announcerAccount = Account.createFromPrivateKey(announcerAccountPrivateKey, networkType);
// replace with network type
const networkType = symbol_sdk_1.NetworkType.TEST_NET;
// replace with private key
const mainAccountPrivateKey = '0000000000000000000000000000000000000000000000000000000000000000';
const mainAccount = symbol_sdk_1.Account.createFromPrivateKey(mainAccountPrivateKey, networkType);
const remoteAccount = symbol_sdk_1.Account.generateNewAccount(networkType);
// replace with private key
const announcerAccountPrivateKey = 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF';
const announcerAccount = symbol_sdk_1.Account.createFromPrivateKey(announcerAccountPrivateKey, networkType);
  1. Create an AccountKeyLinkTransaction to delegate M’s importance to R using its public key.
const accountLinkTransaction = AccountKeyLinkTransaction.create(
    Deadline.create(),
    remoteAccount.publicKey,
    LinkAction.Link,
    networkType,
    UInt64.fromUint(2000000));
const accountLinkTransaction = symbol_sdk_1.AccountKeyLinkTransaction.create(symbol_sdk_1.Deadline.create(), remoteAccount.publicKey, symbol_sdk_1.LinkAction.Link, networkType, symbol_sdk_1.UInt64.fromUint(2000000));
  1. Sign the AccountKeyLinkTransaction with M and announce it to the network.
// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.096x.symboldev.network:3000';
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash = '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
const signedTransaction = mainAccount.sign(accountLinkTransaction, networkGenerationHash);
transactionHttp
    .announce(signedTransaction)
    .subscribe((x) => console.log(x), (err) => console.error(err));
// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.096x.symboldev.network:3000';
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash = '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
const signedTransaction = mainAccount.sign(accountLinkTransaction, networkGenerationHash);
transactionHttp
    .announce(signedTransaction)
    .subscribe((x) => console.log(x), (err) => console.error(err));

Once the transaction is confirmed, the next step is to share R’s private key with the node you wish to connect for delegated harvesting.

4. Create a PersistentDelegationRequestTransaction. Add the node’s public key as the transaction recipient and share the R’s private key by creating a special encrypted message as follows:

Note

Get the node’s public key by querying http://<node-url>:3000/node/info.

// replace with node publicKey (nodeUrl + '/node/info')
const nodePublicKey = '3A537D5A1AF51158C42F80A199BB58351DBF3253C4A6A1B7BD1014682FB595EA';
const nodePublicAccount = PublicAccount.createFromPublicKey(nodePublicKey, networkType);

const persistentDelegationRequestTransaction = PersistentDelegationRequestTransaction
    .createPersistentDelegationRequestTransaction(
        Deadline.create(),
        remoteAccount.privateKey,
        nodePublicAccount.publicKey,
        networkType,
        UInt64.fromUint(2000000));
// replace with node publicKey (nodeUrl + '/node/info')
const nodePublicKey = '3A537D5A1AF51158C42F80A199BB58351DBF3253C4A6A1B7BD1014682FB595EA';
const nodePublicAccount = symbol_sdk_1.PublicAccount.createFromPublicKey(nodePublicKey, networkType);
const persistentDelegationRequestTransaction = symbol_sdk_1.PersistentDelegationRequestTransaction
    .createPersistentDelegationRequestTransaction(symbol_sdk_1.Deadline.create(), remoteAccount.privateKey, nodePublicAccount.publicKey, networkType, symbol_sdk_1.UInt64.fromUint(2000000));

The special encrypted message ensures that R’s proxy private key is securely shared, only readable by the node owner. Moreover, the remote account does not possess any mosaics. The valuable assets remain safely in the main account which the node owner remains without access.

  1. Sign the special TransferTransaction with A and announce it to the network.
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash = '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = announcerAccount.sign(persistentDelegationRequestTransaction, networkGenerationHash);

// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.096x.symboldev.network:3000';
const repositoryFactory = new RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp
    .announce(signedTransaction)
    .subscribe((x) => console.log(x), (err) => console.error(err));
// replace with meta.networkGenerationHash (nodeUrl + '/node/info')
const networkGenerationHash = '1DFB2FAA9E7F054168B0C5FCB84F4DEB62CC2B4D317D861F3168D161F54EA78B';
const signedTransaction = announcerAccount.sign(persistentDelegationRequestTransaction, networkGenerationHash);
// replace with node endpoint
const nodeUrl = 'http://api-01.us-east-1.096x.symboldev.network:3000';
const repositoryFactory = new symbol_sdk_1.RepositoryFactoryHttp(nodeUrl);
const transactionHttp = repositoryFactory.createTransactionRepository();
transactionHttp
    .announce(signedTransaction)
    .subscribe((x) => console.log(x), (err) => console.error(err));

Note

You could announce the transaction with M, but it is recommended to use a third account to keep the information about transfer of importance confidential.

If everything is successful, the node will receive an encrypted message using WebSockets. Once the node decrypts the private key of the potential delegated harvester, the node owner may add R as a delegated harvester if the following requirements are met:

  • The node permits delegated harvesting.
  • The node has harvesting slots available.
  • The remote account has not sent or received transactions.

Note

Announcing a valid PersistentDelegationRequestTransaction does not guarantee being added as a delegated harvester. Currently, the only way to verify that an account has successfully activated delegated harvesting is to become the signer of a new block.

Method #02: Using the CLI

  1. Load your main account (M)—the one with more than 10.000 symbol.xym— as a CLI profile.
symbol-cli profile import --private-key 0000000000000000000000000000000000000000000000000000000000000000 --network TEST_NET --url http://api-01.us-east-1.096x.symboldev.network:3000 --profile main
  1. Create a brand new account. This account will be your remote account (R).
symbol-cli account generate --network-type TEST_NET
  1. Delegate M’s importance to R.
symbol-cli transaction accountlink --public-key 98DB319D390F8B52E29A5FB2B4109FD709218CA505BB38D4BC54F51E1102FCDA --action Link --profile main
  1. Load the announcer account (A) as a CLI profile. This account should have at least some symbol.xym to be able to announce the transaction to the network.
symbol-cli profile import --private-key FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF --network TEST_NET --url http://api-01.us-east-1.096x.symboldev.network:3000 --profile announcer
  1. Send a request to the node you want to be added as a delegated harvester with A. Replace <recipient-public-key> with the node’s public key and <remote-private-key> with R’s private key.

Note

Get the node’s public key by querying http://<node-url>:3000/node/info.

symbol-cli transaction persistentharvestdelegation --recipient-public-key <recipientPublicKey> --remote-private-key <remotePrivateKey> --profile announcer

Once the node decrypts the private key of the potential delegated harvester, the node owner may add you as a delegated harvester.