一、Architecture

1.1 Storage

  • rocksdb: kv store

1.2 Runtime

  • Wasm

1.3 Peer-to-peer network

1.4 Consensus

  • POW: ethash
  • POS: AureBABE

1.5 RPC

  • jsonRPC

1.6 Telemetry

1.7 State

  • Tire(Patricia Merkle Tree) structure

二、Key Concept

2.1 Runtime

2.1.1 API

  • BlockBuilder: Provides the functionality required for building a block.
  • TaggedTransactionQueue: Handles validating transactions in the transaction queue.
  • OffchainWorkerApi: Handles off-chain capabilities.
  • AuraApi: Handles block authorship with Aura consensus.
  • SessionKeys: Generates and decodes session keys.
  • GrandpaApi: Integrates the GRANDPA finality gadget into the runtime.
  • AccountNonceApi: Handles querying transaction indices.
  • TransactionPaymentApi: Handles querying information about transactions.
  • Benchmark: Provides a way to benchmark a FRAME runtime.

2.1.2 Core primitives

  • Hash: A type which encodes a cryptographic digest of some data. Typically just a 256-bit quantity.
  • DigestItem: A type which must be able to encode one of a number of "hard-wired" alternatives relevant to consensus and change-tracking as well as any number of "soft-coded" variants, relevant to specific modules within the runtime.
  • Digest: A series of DigestItems. This encodes all information that is relevant for a light-client to have on hand within the block.
  • Extrinsic: A type to represent a single piece of data external to the blockchain that is recognized by the blockchain. This typically involves one or more signatures, and some sort of encoded instructions (e.g. for transferring ownership of funds or calling into a smart contract).
  • Header: A type which is representative (cryptographically or otherwise) of all information relevant to a block. It includes the parent hash, the storage root and the extrinsics trie root, the digest and a block number.
  • Block: Essentially just a combination of Header and a series of Extrinsics, together with a specification of the hashing algorithm to be used.
  • BlockNumber: A type which encodes the total number of ancestors any valid block has. Typically a 32-bit quantity.

2.1.3 FRAME primitives

frame-runtime

additional set of primitives

  • Call: The dispatch type that can be called via an extrinsic.
  • Origin: Represents where a call came from. For example, a signed message (a transaction), an unsigned message (an inherent extrinsic), or a call from the runtime itself (a root call).
  • Index: An account index (aka nonce) type. This stores the number of previous transactions associated with a sender account.
  • Hashing: The hashing algorithm being used in the runtime (e.g. Blake2).
  • AccountId: The type used to identify user accounts in the runtime.
  • Event: The type used for events emitted by the runtime.
  • Version: A type which represents the version of the runtime.

2.2 Extrinsic

2.2.1 Block structure

A block in Substrate is composed of a header and an array of extrinsics.

  • The header contains a block height, parent hash, extrinsics root, state root, and digest. This section will only focus on the extrinsics root.
  • Extrinsics are bundled together into a block as a series to be executed as each is defined in the runtime. The extrinsics root is a cryptographic digest of this series. This serves two purposes.
    • First, it prevents any alterations to the series of extrinsics after the header has been built and distributed.
    • Second, it provides a means of allowing light clients to succinctly verify that any given extrinsic did indeed exist in a block given only knowledge of the header.

2.2.2 Inherents

2.2.3 Signed transactions

2.2.4 Unsigned transactions

2.3 Account Abstractions

Substrate uses multiple sets of public/private key pairs to represent participants of the network, including validators, nominators and normal users.

  • Stash Key: a Stash account is meant to hold large amounts of funds. Its private key should be as secure as possible in a cold wallet.
  • Controller Key: a Controller account signals choices on behalf of the Stash account, like payout preferences, but should only hold a minimal amount of funds to pay transaction fees. Its private key should be secure as it can affect validator settings, but will be used somewhat regularly for validator maintenance.
  • Session Keys: these are "hot" keys kept in the validator client and used for signing certain validator operations. They should never hold funds.

2.4 Transaction

The transaction pool contains all transactions (signed and unsigned) broadcasted to the network that have been received and validated by the local node.

2.4.1 Validity

The transaction pool checks for transaction validity. Example validity checks are:

  • Checking if the Transaction Index (nonce) is correct.
  • Checking if the account has enough funds to pay for the associated fees.
  • Checking if the signature is valid.

2.4.2 Sorting

If the transaction is valid, the transaction queue sorts transactions into two groups:

  • Ready Queue Contains transactions that can be included in a new pending block. For runtimes built with FRAME, the transactions must follow the exact order in the ready queue.
  • Future Queue Contains transactions that may become valid in the future. For example, a transaction may have a nonce that is too high for its account. This transaction will wait in the future queue until the preceding transactions are included in the chain.

2.4.3 Transaction dependencies

The ValidTransaction struct defines the requires and provides parameters to build a dependency graph of transactions. Together with priority (discussed below), this dependency graph allows the pool to produce a valid linear ordering of transactions.

At minimum, FRAME transactions have a provides tag of encode(sender ++ nonce) and a requires tag of encode(sender ++ (nonce -1)) if nonce > 0. Transactions do not require anything if nonce=0. As a result, all transactions coming from a single sender will form a sequence in which they should be included.

2.4.4