ts-hashgraph

0.1.7 • Public • Published

🌳 ts-hashgraph

npm version downloads coverage pipeline

Demo | Video Tutorial | Paper

ts-hashgraph is an implementation of the Swirlds Hashgraph Consensus Algorithm (view the whitepaper) written in TypeScript.

This project is a work-in-progress and does not completely work as suggested by this document. Parts of the system work as suggested, other parts are still being worked on.

About

Hashgraph is an algorithm and data structure that solves the problem of distributed consensus. The algorithm achieves this using 2 novel methods termed "Gossip about Gossip" and "Virtual Voting" coined by the inventor of the Hashgraph, Dr. Leemon Baird.

Gossip about Gossip refers to using a gossip protocol amongst connected peers in a network. Instead of gossiping the transactions alone, each peer gossips additional information about how the peers are gossiping to one another.

Virtual Voting allows peers to calculate the order in which transactions reached the majority, at least ⅔, of the community. By using the additional information about how peers gossip, this voting mechanism avoids the heavy bandwidth requirements peers would otherwise need to determine the consensus order of transactions.

Benefits

  • 🏎️ Fast: Near-perfect efficiency in bandwidth usage and consequently can process hundreds of thousands of transactions per second in a single shard
  • 🤝 Fair: The actual order transactions are received by the community will be reflected in the consensus order. ie. Fair access and ordering
  • 🔐 Secure: Achieves the gold standard for security in the field of distributed consensus: asynchronous Byzantine Fault Tolerance (aBFT)

Features

  • 🌎 Sharding: Distribute consensus state across multiple shared-worlds/shards
  • 🔎 State Proofs: Uniquely identify shards to avoid state forks/splits (WIP)
  • 🕸️ Network Agnostic: Gossip events through any network protocol (ie. WebRTC, WebSocket, HTTP, etc.)
  • 🚀 Multi-threaded: Employs web workers to free the main thread from the heavy computations associated with Virtual Voting (WIP)
  • 👾 GPU-accelerated: Uses WebGL or WebGPU for hardware-accelerated cryptogprahy (WIP)
  • ⚛️ Post-Quantum Security: Cryptographic resistance to quantum attacks using SHA-284 hashing (WIP)
  • ⚖️ Proof-of-Stake Support: Optionally, weight the votes of members to resist sybil attacks (WIP)
  • 🔄 Auto-updates: Synchronously update transaction code (WIP)
  • 📊 Visualizer: Optionally, visualize the real-time gossip traffic on a deployed hashgraph network

Limitations

  • 🧪 Experimental: This project is a work-in-progress
  • 🕵️ Less than ⅓ dishonest: Distributed consensus requires at least ⅔ of shard participants to be honest (ie. untampered code and working hardware)

Additional Notes

Progress Report

See the to-do list for a progress report on remaining tasks.

Browser Compatibility

ts-hashgraph supports all browsers that are ES5-compliant (IE8 and below are not supported).

Screenshots

Explore the public demo or visit the test/visual directory to run the visualizer locally.

Example w/ 3 Members Example w/ 4 Members

Installation

Install the library

npm install ts-hashgraph --save

Import the library

import * as TsHashgraph from 'ts-hashgraph'

Or import the browser script

<script src="https://unpkg.com/ts-hashgraph@latest/dist/library.js"></script>

Example Usecases

For working demos that use this library, please visit the examples directory.

To run the following examples, take these steps:

  1. Copy and paste the code in 2 code environments (for example, 2 codepen browser tabs)
  2. Replace the peerID from 'alice' to 'bob' in the second code environment

🏓 A Distributed Game of Pong

Keyboard controls: Press W to move ↑ and S to move ↓

Expand code details
<html>
<head>
  <title>Hashgraph Example - A Distributed Game of Pong</title>
  <script src="https://unpkg.com/ts-hashgraph@latest/dist/library.js"></script> 
</head>
<body>
  <script>
    const peerID = 'alice'
    const shard = new TsHashgraph.SharedWorld()
 
    shard.onSendGossip(handleGossip)
    shard.onTransactionOrder(handleTransactions)
 
    main()
 
    async function main () {
      const myPublicKey = await shard.addMember(null, true)
    }
 
    function handleGossip (targetPeerID, syncEventList) {
      // send syncEventList to target peer
    }
 
    function handleTransactions (previousState, transactionList, isConsensusOrder) {
      // handle move paddle transactions
      // handle move ball transactions
    }
  </script> 
</body>
</html>

🛍 ️A Distributed Shopping Mall

Keyboard controls: Press A to add inventory and P to make a purchase

Expand code details
<html>
<head>
  <title>Hashgraph Example - A Distributed Shopping Mall</title>
  <script src="https://unpkg.com/ts-hashgraph@latest/dist/library.js"></script> 
</head>
<body>
  <script>
    const peerID = 'alice'
    const shard = new TsHashgraph.SharedWorld()
 
    shard.onSendGossip(handleGossip)
    shard.onTransactionOrder(handleTransactions)
 
    main()
 
    async function main () {
      const myPublicKey = await shard.addMember(null, true)
    }
 
    function handleGossip (targetPeerID, syncEventList) {
      // send syncEventList to target peer
    }
 
    function handleTransactions (previousState, transactionList, isConsensusOrder) {
      // handle add inventory logic transactions
      // handle purchase logic transactions
    }
  </script> 
</body>
</html>

API Reference

new TsHashgraph.SharedWorld

new TsHashgraph.SharedWorld([shardID])

const shard = new TsHashgraph.SharedWorld()
  • shardID String. The ID of the hashgraph shard. Defaults to 'hashgraph'.

Create a new hashgraph shard.

shard.startAutoSync

shard.startAutoSync()

shard.startAutoSync()
  • Returns void.

Start automatically syncing with peers at 1 sync per second. Use setSyncTickRate to change the frequency of syncs.

shard.startAutoSync

shard.stopAutoSync()

shard.stopAutoSync()
  • Returns void.

Stop automatically syncing with peers.

shard.setSyncTickRate

shard.setSyncTickRate(tickRate)

shard.setSyncTickRate(1000) // 1000 milliseconds per auto-sync
  • tickRate Number. The number of milliseconds between automatic syncs (gossip + virtual voting). Defaults to 1000 milliseconds (ie. 1 sync per second).
  • Returns void.

Set the frequency of automatically synchronizing with connected peers.

shard.manualSync

shard.manualSync([targetPeerID])

await shard.manualSync()
  • targetPeerID String. PeerID or public key of the target to send gossip to.
  • Returns Promise<void>.

Manually sync with peers. Syncing involves (1) gossiping events and (2) calculating the events' order. Use startAutoSync() to automatically sync with peers.

shard.onSendGossip

shard.onSendGossip(callback)

shard.onSendGossip((targetPeerID, syncEventList) => {
  if (targetPeerID === 'alice' || targetPeerID === alicePublicKey) {
    // Send syncEventList to 'alice' through your preferred transport protocol
    //
    // For example:
    //
    // datachannel_with_alice.send({ eventList: syncEventList })
  }
})
  • callback Function. A function that sends data to a connected peer.
    • targetPeerID String. The ID of the peer to send the events to. Defaults to the public key of the peer if peerID isn't specified.
    • syncEventList HashgraphEvent[]. A list of hashgraph events that the target peer/member may not know about yet.
    • Returns Promise<void>|void.
  • Returns void.

Send the syncEventList to the member with targetPeerID.

Note: Use your preferred transport protocol (ie. HTTP, WebSocket, WebRTC, etc.) to communicate with peers.

shard.sendGossip

shard.sendGossip([targetPeerID])

await shard.sendGossip()
  • targetPeerID String. PeerID or public key of the target peer to send gossip to.
  • Returns Promise<void>.

Manually gossip to connected peers. Use startAutoSync() to automatically send gossip.

shard.receiveGossip

shard.receiveGossip(syncEventList)

await shard.receiveGossip(syncEventList)
  • syncEventList HashgraphEvent[]. A list of hashgraph events that the current peer may not know about yet.
  • Returns Promise<void>.

Receive the other parent event and sync event list.

shard.setElapsedTimeToDeleteEventHistory

shard.setElapsedTimeToDeleteEventHistory(elapsedTime)

shard.setElapsedTimeToDeleteEventHistory(60) // 60 second delay before deleting event history
  • elapsedTime Number. The time to wait before deleting event history. Measured in seconds. Defaults to 20.
  • Returns void.

Set the time to wait before deleting event history.

shard.onAddMember

shard.onAddMember(callback)

shard.onAddMember((candidateMember) => true) // Vote to allow anyone to join the shard
  • callback Function. A function used to vote on whether to add a candidate member to the shard.
    • candidateMember Object.
      • publicKey: String. The cryptographic public key used internally to validate transactions by members.
      • peerID: String. The optional peerID of the candidate member to add.
      • peerConnectionMap: Object<String, Boolean>. A map from the public keys of the existing shard members to whether the candidate will establish a connection to the respective member upon being accepted to join the shard.
      • details: Any. Optional additional data that can be used for determining a vote.
    • Returns Promise<boolean>|boolean. Returns the vote to allow or disallow the candidate into the shard.
  • Returns Promise<void>|void.

Vote to add a candidate to the shard.

shard.onRemoveMember

shard.onRemoveMember(callback)

shard.onRemoveMember((candidateMember) => {
  const isArtist = candidateMember.details.isArtist
  const isScientist = candidateMember.details.isScientist
  const isMathematician = candidateMember.details.isMathematician
  const isEngineer = candidateMember.details.isEngineer
  const isHumanitarian = candidateMember.details.isHumanitarian
  return !(isArtist || isScientist || isMathematician || isEngineer || isHumanitarian)
}) // Vote to remove non-interesting members from the shard
  • callback Function. A function used to vote on whether to remove a candidate member from the shard.
    • candidateMember Object.
    • Returns Promise<boolean>|boolean. Returns the vote to allow or disallow the candidate from the shard.
  • Returns void.

Vote to remove a candidate from the shard.

shard.addMember

shard.addMember([peerID, isSelf, isConnectedPeer, candidateMember, skipVoting])

await shard.addMember('alice', true) // On Alice's computer
  • peerID String. The ID of the peer.
  • isSelf Boolean. Set to true to initialize the self peer. Defaults to false.
  • isConnectedPeer Boolean. Set to true if the self peer communicates directly to the peerID peer through the preferred protocol. Defaults to false.
  • candidateMember Object.
    • publicKey: String (Required). The cryptographic public key used internally to validate transactions by members.
    • peerID: String. The optional peerID of the candidate member to add.
    • peerConnectionMap: Object<String, Boolean>. A map from the public keys of the existing shard members to whether the candidate will establish a connection to the respective member upon being accepted to join the shard.
    • details: Any. Optional data for sharing with current members to use when voting for whether to add the candidate to the shard.
  • skipVoting Boolean. A flag used to skip the transaction-based voting to add the candidate member to the shard. Defaults to false.
  • Returns Promise<String>. Returns the public key of the member that's been added.

Add a member to the hashgraph shard.

shard.removeMember

shard.removeMember(candidateMember [, skipVoting])

shard.removeMember({ publicKey: shard.getMemberPublicKey('carol') })
  • candidateMember Object.
  • skipVoting Boolean. A flag used to skip the transaction-based voting to remove the candidate member from the shard. Defaults to false.
  • Returns void.

Remove a member from the hashgraph shard.

shard.setMinimumVotePercentToAddMember

shard.setMinimumVotePercentToAddMember(callback)

shard.setMinimumVotePercentToAddMember((candidateMember) => 100) // Require 100 percent (unanimous) approval by current members to add a member
  • callback Function. A function used to set the percent of yes votes required by current members to add a new member.
    • candidateMember Object.
    • Returns Promise<number>|number. Returns the percent.
  • Returns void.

Set the percent of yes votes required by the current shard members to add a new member to the shard.

shard.setMinimumVotePercentToRemoveMember

shard.setMinimumVotePercentToRemoveMember(callback)

shard.setMinimumVotePercentToRemoveMember((candidateMember) => 80) // Require 80 percent approval by current members to remove a member
  • callback Function. A function used to set the percent of yes votes required by current members to remove an existing member.
    • candidateMember Object.
    • Returns Promise<number>|number. Returns the percent.
  • Returns void.

Set the percent of yes votes required by the current shard members to remove an existing member from the shard.

shard.onMemberUpdate

shard.onMemberUpdate(callback)

shard.onMemberUpdate((member, updateType) => {
  if (updateType === 'added') { console.log('Member joined: ', member) }
  if (updateType === 'removed') { console.log('Member departed: ', member) }
})
  • callback Function. A function used to be notified of member updates (ie. added or removed members).
    • member Object.
    • updateType String. A description of the update; either added or removed.
    • Returns void.
  • Returns void.

Set a callback function to be notified of when a member is added or removed from the hashgraph shard.

shard.getMemberPublicKey

shard.getMemberPublicKey(peerID)

await shard.addMember('bob', false, true) // On Bob's computer
 
const bobPublicKey = shard.getMemberPublicKey('bob')
 
// Send bob's public key to alice, then..
await shard.addMember('bob', true, false, bobPublicKey) // On Alice's computer
  • peerID String. The ID of the peer.
  • Returns String.

Retrieve the public key of a member.

Note: Send the public key of the current peer to participating peers in the hashgraph shard.

shard.getMemberList

shard.getMemberList()

const memberList = shard.getMemberList()
  • Returns Array<{ publicKey: String, peerID?: String }>.

Retrieve the public key and peerIDs of all members of the shard.

Note: Send the list to newly added members of the shard.

shard.onTransactionOrder

shard.onTransactionOrder(callback)

shard.onTransactionOrder((previousState, transactionList, isConsensusOrder) => {
  if (!isConsensusOrder) { return } // if strict consensus ordering is desired, return early
 
  for (const transaction of transactionList) {
    if (transaction.payload.name === 'Bob') { continue } // Validate transactions
    previousState.chatHistory = (previousState.chatHistory || []).concat([transaction.payload]) // Apply transactions to the previous state
  }
})
  • callback Function. A callback function used to apply a list of transactions to a state object.
    • previousState Object. A copy of the previous consensus state.
    • transactionList Array<{ timestamp: Date, payload: Any }>. A list of messages received from the shard.
    • isConsensusOrder Boolean. A flag determining whether the transactionList is in the consensus order. If you want to act on transactions as soon as they arrive, you don't need to check this value.
    • Returns Promise<void>|void.
  • Returns void.

Receive transaction list.

shard.sendTransaction

shard.sendTransaction(transaction)

shard.sendTransaction({ name: 'Alice', message: 'Hello World!' })
  • transaction Any. A message or payload to be sent to the hashgraph shard.
  • Returns void.

Send a transaction.

shard.determineTransactionOrder

shard.determineTransactionOrder()

await shard.determineTransactionOrder()
  • Returns Promise<void>.

Manually determine the order of transactions (ie. perform virtual voting on recently added events). Use startAutoSync() to automatically determine transaction order.

shard.getState

shard.getState()

const state = shard.getState()
  • Returns Object<String, Any>.

Retrieve the consensus state from the hashgraph shard.

Documentation

To check out project-related documentation, please visit docs. See metrics for performance-related documentation.

Contributing

Everyone can contribute. All are welcome. Please see contributing guide.

Acknowledgements

In general, thanks to the many people who contribute to the open ecosystem. In particular, thanks to Dr. Leemon Baird for being a genius and sharing his discovery.

Software Dependencies

Tooling Encryption Hashing
TypeScript elliptic object-hash
  tombstone (WIP)  

Contemporary Visionaries

These are a few individuals that we believe are inspiring sources for anyone looking to apply technical solutions to improving the human condition for everyone on earth.

Name Profession Life work
Jacque Fresco Sociocyberneering Venus Project, Resource Based Economy
Ted Nelson Software Design Xanadu, ZigZag
Christopher Alexander Living Architecture The Nature of Order, A Pattern Language

Learn More

🕹️ Play with the visualizer
🎬 Watch a video on how to use the visualizer
🎬 Watch a video on how to use this library
🎬 Watch a video on how this library is made
🎬 Watch a video on how hashgraph works
📜 Read the hashgraph whitepaper

Related Work

Some well-known distributed consensus data structures include: Blockchain, Tangle, Holochain, Hashgraph

Some well-known distributed consensus algorithms include: Leader-based (Raft, Paxos, Hyperledger, EOS, IOTA), Proof-of-Work (Ethereum, Bitcoin), Economy-based/Proof-of-Stake (Casper, IOTA, EOS, Ouroboros), Voting-based (None in practice), Virtual-Voting (Hashgraph)

License

MIT

Package Sidebar

Install

npm i ts-hashgraph

Weekly Downloads

2

Version

0.1.7

License

MIT

Unpacked Size

4.68 MB

Total Files

199

Last publish

Collaborators

  • piusnyakoojo