1.0.3 • Published 2 years ago

@dingocoin-js/accumulator v1.0.3

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

@dingocoin-js/accumulator

JavaScript library to help implement accumulator-style programs over Dingocoin blocks.

What is an accumulator-style program?

Accumulator-style programs derive its name from the concept of accumulators in functional programming.

An accumulator-style program scans through each block in the Dingocoin blockchain sequentially, executing the same function for each block. The callback allows the user to modify state variables in their script, which are persisted onto the callback for the next block. Thus, such a program is said to accumulate changes to the program state, through calling the callback sequentially across the blocks.

Installation

npm install @dingocoin-js/accumulator

Usage

// Import accumulator package.
const accumulator = require('@dingocoin-js/accumulator');

// Import RPC package to interact with Dingocoin daemon.
// This is installed automatically with @dingocoin-js/accumulator.
const nodeRpc = require('@dingocoin-js/node-rpc');

// Create RPC client using default cookie path (~/.dingocoin/.cookie).
const rpcClient = nodeRpc.fromCookie();

/*
 * Example 1: Accumulator program to print every block.
 *
 * This example does not maintain any program state.
 */
const acc1 = new Accumulator(
    rpcClient, // Needed to read blocks from the Dingocoin blockchain.
    1, // (Inclusive) block height to start scanning from. Must be >= 1.
    3, // Use 3 confirmations before processing a block.
    async (height, block) => { // Program callback function.
      console.log(height, block);
    },
    async (height) => { // Program rollback function.
      throw new Error("Unexpected rollback");
    });
acc1.start(); // Run accumulator program.


/*
 * Example 2: Accumulator program to count how user transactions exist.
 *
 * A user transaction is defined as one that does not correspond to
 * a block reward payout. Block reward payouts are identified by the presence
 * of a vin with type === 'coinbase'.
 *
 * This example maintains a program state, namely the variable `count` which
 * tracks the number of user transactons.
 */
let count = 0;
const acc2 = new Accumulator(rpcClient, 1, 0,
    async (height, block) => {
      for (const tx of block.txs) {
        if (!tx.vins.some((x) => x.type === 'coinbase')) {
          count += 1;
        }
      }
    },
    async (height) => {
      throw new Error("Unexpected rollback");
    });
acc2.start(); // Run accumulator program.

Block structure

The structure of each block is as follows:

// Each block is a list of transactions.
[
  // Each transaction contains a vins list and vouts list.
  {
    // List of vins in the transaction.
    vins:
    [
      // This structure is used for P2PKH, P2SH vins.
      {
        type: "pubkeyhash" <OR> "pubkey" <OR> "scripthash",
        value: "....",
        vout: ...,
        address: "..."
      },
      // This structure is used for block reward payouts.
      {
        type: "coinbase"
      },
      ...
    ],
    // List of vouts in the transaction.
    vouts:
    [
      // This structure is used for P2PKH, P2SH vouts.
      {
        type: "pubkeyhash" <OR> "pubkey" <OR> "scripthash",
        value: "....",
        vout: ...,
        address: "..."
      },
      // This structure is used for OP_RETURN vouts.
      {
        type: "nulldata",
        data: "....",
      },
      ...
    ]
  },
  ...
]

Accumulator rollback

It is common for the blockchain to have small re-orgs, causing the latest few blocks to switch to another string completely. This often happens when latency in the network causes forks to grow separately. A node that was on one chain will switch to another chain, if the amount of work done for the other chain is larger. This is precisely the Proof-of-Work (PoW) consensus mechanism.

When writing an accumulator-style program, we recommend using a large enough confirmation window. This reduces the possibility that blocks are invalidated due to re-orgs, after they have been processed by the accumulator's callback. Otherwise, a re-org will cause the program state to de-sync.

If such re-orgs are inevitable, the user can handle them cleanly by passing a rollback function (height) => { ... } to the Accumulator constructor. The rollback function would contain program-specific logic to revert the program state back to some height. It then returns the height of the next block, which informs the Accumulator where to resume scanning the blocks from. The height argument is the height of the would-be block if the re-org did not occur.

More examples

Below is a list of realistic use cases of accumulator-style programs:

  • Dingocoin staking program - blocks are scanned for transactions which deposit exact multiples of 100,000 coins into addresses. These are counted as a stake for the address. Deployed live.

Testing (mocha)

npm test

Contributing

Please create a PR or drop a message in our community.

Community