blockchain

package module
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 24, 2021 License: MIT Imports: 3 Imported by: 0

README

go-blockchain

GoDoc Go Report Card Build Status codecov

The library that implements models and algorithms of blockchain.

Features

  • models:
    • block:
      • storing:
        • timestamp;
        • custom data;
        • hash;
        • previous hash;
      • operations:
        • creation (using a proofer);
        • getting merged data;
        • self-validation (using a proofer);
    • genesis block:
      • based on a usual block without a previous hash;
    • block group:
      • storing:
        • group of blocks;
      • operations:
        • self-validation (using a proofer):
          • modes:
            • as a full blockchain;
            • as a blockchain chunk;
          • takes into account a prepended chunk;
          • allows empty block groups;
        • validation of the last block (using a proofer):
          • modes:
            • as a full blockchain;
            • as a blockchain chunk;
    • block group loaders:
      • loading block groups via the external interface;
      • automatically saving the loaded block groups to a storage;
      • wrappers:
        • chunk validating loader:
          • automatically validates the loaded block group as a blockchain chunk;
        • last block validating loader:
          • automatically validates the last block from the loaded block group;
          • automatically preloads the next block group to perform the above validation;
        • memoizing loader:
          • remembers loaded block groups;
          • restricts the quantity of the remembered block groups:
            • stores the loaded block groups in the LRU cache;
      • kinds:
        • memory loader:
          • loading blocks from the block group;
    • blockchain:
      • storing:
        • storage;
        • last block;
      • operations:
        • creation:
          • loading the last block from the storage;
          • when the storage is empty:
            • creation a genesis block using a proofer;
            • storing the genesis block to the storage;
        • adding a block:
          • creation a block using a proofer;
          • storing the block to the storage;
  • proofers:
    • operations:
      • block hashing;
      • block validation;
    • kinds:
      • simple:
        • based on once hashing by the SHA-256 algorithm;
      • proof of work:
        • based on the Hashcash algorithm;
        • additional storing in a block (in a hash actually):
          • nonce;
          • target bit;
  • storages:
    • operations:
      • loading the last block;
      • storing a block;
      • storing a block group (optional);
    • wrappers:
      • wrapper that adds support for saving a block group to those storages that cannot do this;
    • kinds:
      • memory storage:
        • storing blocks in memory;
        • additional operations:
          • getting the stored blocks.

Installation

Prepare the directory:

$ mkdir --parents "$(go env GOPATH)/src/github.com/thewizardplusplus/"
$ cd "$(go env GOPATH)/src/github.com/thewizardplusplus/"

Clone this repository:

$ git clone https://github.com/thewizardplusplus/go-blockchain.git
$ cd go-blockchain

Install dependencies with the dep tool:

$ dep ensure -vendor-only

Examples

blockchain.Blockchain:

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/thewizardplusplus/go-blockchain"
	"github.com/thewizardplusplus/go-blockchain/proofers"
	"github.com/thewizardplusplus/go-blockchain/storing/storages"
)

type StringData string

func (data StringData) String() string {
	return string(data)
}

func main() {
	timestamp := time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)
	blockDependencies := blockchain.BlockDependencies{
		// use the custom clock function to get the same blocks
		Clock: func() time.Time {
			timestamp = timestamp.Add(time.Hour)
			return timestamp
		},
		Proofer: proofers.ProofOfWork{TargetBit: 248},
	}

	var storage storages.MemoryStorage
	blockchain, err := blockchain.NewBlockchain(
		StringData("genesis block"),
		blockchain.Dependencies{
			BlockDependencies: blockDependencies,
			Storage:           &storage,
		},
	)
	if err != nil {
		log.Fatalf("unable to create the blockchain: %v", err)
	}

	for i := 0; i < 5; i++ {
		if err := blockchain.AddBlock(
			StringData(fmt.Sprintf("block #%d", i)),
		); err != nil {
			log.Fatalf("unable to add the block: %v", err)
		}
	}

	blocksBytes, _ := json.MarshalIndent(storage.Blocks(), "", "  ")
	fmt.Println(string(blocksBytes))

	// Output:
	// [
	//   {
	//     "Timestamp": "2006-01-02T16:04:05Z",
	//     "Data": "genesis block",
	//     "Hash": "248:225:00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36",
	//     "PrevHash": ""
	//   },
	//   {
	//     "Timestamp": "2006-01-02T17:04:05Z",
	//     "Data": "block #0",
	//     "Hash": "248:198:0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf",
	//     "PrevHash": "248:225:00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T18:04:05Z",
	//     "Data": "block #1",
	//     "Hash": "248:15:002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa",
	//     "PrevHash": "248:198:0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T19:04:05Z",
	//     "Data": "block #2",
	//     "Hash": "248:136:003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be",
	//     "PrevHash": "248:15:002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T20:04:05Z",
	//     "Data": "block #3",
	//     "Hash": "248:65:00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa",
	//     "PrevHash": "248:136:003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T21:04:05Z",
	//     "Data": "block #4",
	//     "Hash": "248:173:00b6863763acd6ec77ca3521589d8e68c118efe855657d702783e8e6aee169a9",
	//     "PrevHash": "248:65:00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa"
	//   }
	// ]
}

blockchain.Block:

package main

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/thewizardplusplus/go-blockchain"
	"github.com/thewizardplusplus/go-blockchain/proofers"
)

type StringData string

func (data StringData) String() string {
	return string(data)
}

func main() {
	timestamp := time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)
	blockDependencies := blockchain.BlockDependencies{
		// use the custom clock function to get the same blocks
		Clock: func() time.Time {
			timestamp = timestamp.Add(time.Hour)
			return timestamp
		},
		Proofer: proofers.ProofOfWork{TargetBit: 248},
	}

	blocks := []blockchain.Block{
		blockchain.NewGenesisBlock(StringData("genesis block"), blockDependencies),
	}
	for i := 0; i < 5; i++ {
		blocks = append(blocks, blockchain.NewBlock(
			StringData(fmt.Sprintf("block #%d", i)),
			blocks[len(blocks)-1],
			blockDependencies,
		))
	}

	blocksBytes, _ := json.MarshalIndent(blocks, "", "  ")
	fmt.Println(string(blocksBytes))

	// Output:
	// [
	//   {
	//     "Timestamp": "2006-01-02T16:04:05Z",
	//     "Data": "genesis block",
	//     "Hash": "248:225:00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36",
	//     "PrevHash": ""
	//   },
	//   {
	//     "Timestamp": "2006-01-02T17:04:05Z",
	//     "Data": "block #0",
	//     "Hash": "248:198:0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf",
	//     "PrevHash": "248:225:00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T18:04:05Z",
	//     "Data": "block #1",
	//     "Hash": "248:15:002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa",
	//     "PrevHash": "248:198:0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T19:04:05Z",
	//     "Data": "block #2",
	//     "Hash": "248:136:003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be",
	//     "PrevHash": "248:15:002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T20:04:05Z",
	//     "Data": "block #3",
	//     "Hash": "248:65:00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa",
	//     "PrevHash": "248:136:003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T21:04:05Z",
	//     "Data": "block #4",
	//     "Hash": "248:173:00b6863763acd6ec77ca3521589d8e68c118efe855657d702783e8e6aee169a9",
	//     "PrevHash": "248:65:00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa"
	//   }
	// ]
}

blockchain.BlockGroup:

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/thewizardplusplus/go-blockchain"
	"github.com/thewizardplusplus/go-blockchain/proofers"
)

type StringData string

func (data StringData) String() string {
	return string(data)
}

func main() {
	timestamp := time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)
	blockChunks := []blockchain.BlockGroup{
		// chunk #0
		{
			{
				Timestamp: timestamp.Add(6 * time.Hour),
				Data:      StringData("block #4"),
				Hash: "248:" +
					"173:" +
					"00b6863763acd6ec77ca3521589d8e68c118efe855657d702783e8e6aee169a9",
				PrevHash: "248:" +
					"65:" +
					"00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa",
			},
			{
				Timestamp: timestamp.Add(5 * time.Hour),
				Data:      StringData("block #3"),
				Hash: "248:" +
					"65:" +
					"00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa",
				PrevHash: "248:" +
					"136:" +
					"003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be",
			},
		},

		// chunk #1
		{
			{
				Timestamp: timestamp.Add(4 * time.Hour),
				Data:      StringData("block #2"),
				Hash: "248:" +
					"136:" +
					"003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be",
				PrevHash: "248:" +
					"15:" +
					"002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa",
			},
			{
				Timestamp: timestamp.Add(3 * time.Hour),
				Data:      StringData("block #1"),
				Hash: "248:" +
					"15:" +
					"002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa",
				PrevHash: "248:" +
					"198:" +
					"0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf",
			},
		},

		// chunk #2
		{
			{
				Timestamp: timestamp.Add(2 * time.Hour),
				Data:      StringData("block #0"),
				Hash: "248:" +
					"198:" +
					"0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf",
				PrevHash: "248:" +
					"225:" +
					"00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36",
			},
			{
				Timestamp: timestamp.Add(time.Hour),
				Data:      StringData("genesis block"),
				Hash: "248:" +
					"225:" +
					"00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36",
				PrevHash: "",
			},
		},
	}

	var prependedChunk blockchain.BlockGroup
	for index, blockChunk := range blockChunks {
		validationMode := blockchain.AsBlockchainChunk
		if index == len(blockChunks)-1 {
			validationMode = blockchain.AsFullBlockchain
		}

		err := blockChunk.IsValid(
			prependedChunk,
			validationMode,
			blockchain.BlockDependencies{
				Proofer: proofers.ProofOfWork{TargetBit: 248},
			},
		)
		if err != nil {
			log.Fatalf("chunk #%d is incorrect: %v", index, err)
		}

		prependedChunk = blockChunk
	}

	fmt.Println("all chunks are correct")

	// Output:
	// all chunks are correct
}

loading.LoadStorage:

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"time"

	"github.com/thewizardplusplus/go-blockchain"
	"github.com/thewizardplusplus/go-blockchain/loading"
	"github.com/thewizardplusplus/go-blockchain/loading/loaders"
	"github.com/thewizardplusplus/go-blockchain/proofers"
	"github.com/thewizardplusplus/go-blockchain/storing"
	"github.com/thewizardplusplus/go-blockchain/storing/storages"
)

type StringData string

func (data StringData) String() string {
	return string(data)
}

type LoggingLoader struct {
	Loader blockchain.Loader
}

func (loader LoggingLoader) LoadBlocks(cursor interface{}, count int) (
	blocks blockchain.BlockGroup,
	nextCursor interface{},
	err error,
) {
	fmt.Printf("[DEBUG] load the blocks corresponding to cursor %v\n", cursor)

	return loader.Loader.LoadBlocks(cursor, count)
}

func main() {
	timestamp := time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)
	blocks := blockchain.BlockGroup{
		{
			Timestamp: timestamp.Add(6 * time.Hour),
			Data:      StringData("block #4"),
			Hash: "248:" +
				"173:" +
				"00b6863763acd6ec77ca3521589d8e68c118efe855657d702783e8e6aee169a9",
			PrevHash: "248:" +
				"65:" +
				"00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa",
		},
		{
			Timestamp: timestamp.Add(5 * time.Hour),
			Data:      StringData("block #3"),
			Hash: "248:" +
				"65:" +
				"00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa",
			PrevHash: "248:" +
				"136:" +
				"003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be",
		},
		{
			Timestamp: timestamp.Add(4 * time.Hour),
			Data:      StringData("block #2"),
			Hash: "248:" +
				"136:" +
				"003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be",
			PrevHash: "248:" +
				"15:" +
				"002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa",
		},
		{
			Timestamp: timestamp.Add(3 * time.Hour),
			Data:      StringData("block #1"),
			Hash: "248:" +
				"15:" +
				"002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa",
			PrevHash: "248:" +
				"198:" +
				"0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf",
		},
		{
			Timestamp: timestamp.Add(2 * time.Hour),
			Data:      StringData("block #0"),
			Hash: "248:" +
				"198:" +
				"0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf",
			PrevHash: "248:" +
				"225:" +
				"00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36",
		},
		{
			Timestamp: timestamp.Add(time.Hour),
			Data:      StringData("genesis block"),
			Hash: "248:" +
				"225:" +
				"00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36",
			PrevHash: "",
		},
	}

	blockDependencies := blockchain.BlockDependencies{
		Proofer: proofers.ProofOfWork{TargetBit: 248},
	}

	var storage storages.MemoryStorage
	if _, err := loading.LoadStorage(
		storing.NewGroupStorage(&storage),
		loading.LastBlockValidatingLoader{
			Loader: loading.NewMemoizingLoader(1, loading.ChunkValidatingLoader{
				Loader: LoggingLoader{
					Loader: loaders.MemoryLoader(blocks),
				},
				Dependencies: blockDependencies,
			}),
			Dependencies: blockDependencies,
		},
		nil,
		2,
	); err != nil {
		log.Fatalf("unable to load the blocks: %v", err)
	}

	blocksBytes, _ := json.MarshalIndent(storage.Blocks(), "", "  ")
	fmt.Println(string(blocksBytes))

	// Output:
	// [DEBUG] load the blocks corresponding to cursor <nil>
	// [DEBUG] load the blocks corresponding to cursor 2
	// [DEBUG] load the blocks corresponding to cursor 4
	// [DEBUG] load the blocks corresponding to cursor 6
	// [
	//   {
	//     "Timestamp": "2006-01-02T16:04:05Z",
	//     "Data": "genesis block",
	//     "Hash": "248:225:00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36",
	//     "PrevHash": ""
	//   },
	//   {
	//     "Timestamp": "2006-01-02T17:04:05Z",
	//     "Data": "block #0",
	//     "Hash": "248:198:0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf",
	//     "PrevHash": "248:225:00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T18:04:05Z",
	//     "Data": "block #1",
	//     "Hash": "248:15:002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa",
	//     "PrevHash": "248:198:0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T19:04:05Z",
	//     "Data": "block #2",
	//     "Hash": "248:136:003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be",
	//     "PrevHash": "248:15:002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T20:04:05Z",
	//     "Data": "block #3",
	//     "Hash": "248:65:00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa",
	//     "PrevHash": "248:136:003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be"
	//   },
	//   {
	//     "Timestamp": "2006-01-02T21:04:05Z",
	//     "Data": "block #4",
	//     "Hash": "248:173:00b6863763acd6ec77ca3521589d8e68c118efe855657d702783e8e6aee169a9",
	//     "PrevHash": "248:65:00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa"
	//   }
	// ]
}

License

The MIT License (MIT)

Copyright © 2021 thewizardplusplus

Documentation

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrEmptyStorage = errors.New("empty storage")

ErrEmptyStorage ...

Functions

This section is empty.

Types

type Block

type Block struct {
	Timestamp time.Time
	Data      fmt.Stringer
	Hash      string
	PrevHash  string
}

Block ...

Example
timestamp := time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)
dependencies := blockchain.BlockDependencies{
	// use the custom clock function to get the same blocks
	Clock: func() time.Time {
		timestamp = timestamp.Add(time.Hour)
		return timestamp
	},
	Proofer: proofers.ProofOfWork{TargetBit: 248},
}

blocks := []blockchain.Block{
	blockchain.NewGenesisBlock(StringData("genesis block"), dependencies),
}
for i := 0; i < 5; i++ {
	blocks = append(blocks, blockchain.NewBlock(
		StringData(fmt.Sprintf("block #%d", i)),
		blocks[len(blocks)-1],
		dependencies,
	))
}

blocksBytes, _ := json.MarshalIndent(blocks, "", "  ")
fmt.Println(string(blocksBytes))
Output:

[
  {
    "Timestamp": "2006-01-02T16:04:05Z",
    "Data": "genesis block",
    "Hash": "248:225:00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36",
    "PrevHash": ""
  },
  {
    "Timestamp": "2006-01-02T17:04:05Z",
    "Data": "block #0",
    "Hash": "248:198:0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf",
    "PrevHash": "248:225:00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36"
  },
  {
    "Timestamp": "2006-01-02T18:04:05Z",
    "Data": "block #1",
    "Hash": "248:15:002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa",
    "PrevHash": "248:198:0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf"
  },
  {
    "Timestamp": "2006-01-02T19:04:05Z",
    "Data": "block #2",
    "Hash": "248:136:003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be",
    "PrevHash": "248:15:002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa"
  },
  {
    "Timestamp": "2006-01-02T20:04:05Z",
    "Data": "block #3",
    "Hash": "248:65:00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa",
    "PrevHash": "248:136:003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be"
  },
  {
    "Timestamp": "2006-01-02T21:04:05Z",
    "Data": "block #4",
    "Hash": "248:173:00b6863763acd6ec77ca3521589d8e68c118efe855657d702783e8e6aee169a9",
    "PrevHash": "248:65:00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa"
  }
]

func NewBlock

func NewBlock(
	data fmt.Stringer,
	prevBlock Block,
	dependencies BlockDependencies,
) Block

NewBlock ...

func NewGenesisBlock

func NewGenesisBlock(data fmt.Stringer, dependencies BlockDependencies) Block

NewGenesisBlock ...

func (Block) IsValid

func (block Block) IsValid(
	prevBlock *Block,
	dependencies BlockDependencies,
) error

IsValid ...

func (Block) IsValidGenesisBlock

func (block Block) IsValidGenesisBlock(dependencies BlockDependencies) error

IsValidGenesisBlock ...

func (Block) MergedData

func (block Block) MergedData() string

MergedData ...

type BlockDependencies

type BlockDependencies struct {
	Clock   Clock
	Proofer Proofer
}

BlockDependencies ...

type BlockGroup

type BlockGroup []Block

BlockGroup ...

Example
timestamp := time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)
blockChunks := []blockchain.BlockGroup{
	// chunk #0
	{
		{
			Timestamp: timestamp.Add(6 * time.Hour),
			Data:      StringData("block #4"),
			Hash: "248:" +
				"173:" +
				"00b6863763acd6ec77ca3521589d8e68c118efe855657d702783e8e6aee169a9",
			PrevHash: "248:" +
				"65:" +
				"00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa",
		},
		{
			Timestamp: timestamp.Add(5 * time.Hour),
			Data:      StringData("block #3"),
			Hash: "248:" +
				"65:" +
				"00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa",
			PrevHash: "248:" +
				"136:" +
				"003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be",
		},
	},

	// chunk #1
	{
		{
			Timestamp: timestamp.Add(4 * time.Hour),
			Data:      StringData("block #2"),
			Hash: "248:" +
				"136:" +
				"003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be",
			PrevHash: "248:" +
				"15:" +
				"002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa",
		},
		{
			Timestamp: timestamp.Add(3 * time.Hour),
			Data:      StringData("block #1"),
			Hash: "248:" +
				"15:" +
				"002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa",
			PrevHash: "248:" +
				"198:" +
				"0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf",
		},
	},

	// chunk #2
	{
		{
			Timestamp: timestamp.Add(2 * time.Hour),
			Data:      StringData("block #0"),
			Hash: "248:" +
				"198:" +
				"0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf",
			PrevHash: "248:" +
				"225:" +
				"00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36",
		},
		{
			Timestamp: timestamp.Add(time.Hour),
			Data:      StringData("genesis block"),
			Hash: "248:" +
				"225:" +
				"00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36",
			PrevHash: "",
		},
	},
}

var prependedChunk blockchain.BlockGroup
for index, blockChunk := range blockChunks {
	validationMode := blockchain.AsBlockchainChunk
	if index == len(blockChunks)-1 {
		validationMode = blockchain.AsFullBlockchain
	}

	err := blockChunk.IsValid(
		prependedChunk,
		validationMode,
		blockchain.BlockDependencies{
			Proofer: proofers.ProofOfWork{TargetBit: 248},
		},
	)
	if err != nil {
		log.Fatalf("chunk #%d is incorrect: %v", index, err)
	}

	prependedChunk = blockChunk
}

fmt.Println("all chunks are correct")
Output:

all chunks are correct

func (BlockGroup) IsLastBlockValid

func (blocks BlockGroup) IsLastBlockValid(
	prevBlock *Block,
	validationMode ValidationMode,
	dependencies BlockDependencies,
) error

IsLastBlockValid ...

func (BlockGroup) IsValid

func (blocks BlockGroup) IsValid(
	prependedChunk BlockGroup,
	validationMode ValidationMode,
	dependencies BlockDependencies,
) error

IsValid ...

type Blockchain

type Blockchain struct {
	// contains filtered or unexported fields
}

Blockchain ...

Example
timestamp := time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)
blockDependencies := blockchain.BlockDependencies{
	// use the custom clock function to get the same blocks
	Clock: func() time.Time {
		timestamp = timestamp.Add(time.Hour)
		return timestamp
	},
	Proofer: proofers.ProofOfWork{TargetBit: 248},
}

var storage storages.MemoryStorage
blockchain, err := blockchain.NewBlockchain(
	StringData("genesis block"),
	blockchain.Dependencies{
		BlockDependencies: blockDependencies,
		Storage:           &storage,
	},
)
if err != nil {
	log.Fatalf("unable to create the blockchain: %v", err)
}

for i := 0; i < 5; i++ {
	if err := blockchain.AddBlock(
		StringData(fmt.Sprintf("block #%d", i)),
	); err != nil {
		log.Fatalf("unable to add the block: %v", err)
	}
}

blocksBytes, _ := json.MarshalIndent(storage.Blocks(), "", "  ")
fmt.Println(string(blocksBytes))
Output:

[
  {
    "Timestamp": "2006-01-02T16:04:05Z",
    "Data": "genesis block",
    "Hash": "248:225:00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36",
    "PrevHash": ""
  },
  {
    "Timestamp": "2006-01-02T17:04:05Z",
    "Data": "block #0",
    "Hash": "248:198:0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf",
    "PrevHash": "248:225:00e26abd9974fcdea4b32eca43c9dc5c67fffa8efd53cebffa9b049fd6c2bb36"
  },
  {
    "Timestamp": "2006-01-02T18:04:05Z",
    "Data": "block #1",
    "Hash": "248:15:002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa",
    "PrevHash": "248:198:0058f5dae6ca3451801a276c94862c7cce085e6f9371e50d80ddbb87c1438faf"
  },
  {
    "Timestamp": "2006-01-02T19:04:05Z",
    "Data": "block #2",
    "Hash": "248:136:003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be",
    "PrevHash": "248:15:002fc891ad012c4a89f7b267a2ec1767415c627ff69b88b90a93be938b026efa"
  },
  {
    "Timestamp": "2006-01-02T20:04:05Z",
    "Data": "block #3",
    "Hash": "248:65:00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa",
    "PrevHash": "248:136:003c7def3d467a759fad481c03cadbd62e62b2c5dbc10e4bbb6e1944c158a8be"
  },
  {
    "Timestamp": "2006-01-02T21:04:05Z",
    "Data": "block #4",
    "Hash": "248:173:00b6863763acd6ec77ca3521589d8e68c118efe855657d702783e8e6aee169a9",
    "PrevHash": "248:65:00d5800e119abe44d89469c2161be7f9645d7237697c6d14b4a72717893582fa"
  }
]

func NewBlockchain

func NewBlockchain(
	genesisBlockData fmt.Stringer,
	dependencies Dependencies,
) (*Blockchain, error)

NewBlockchain ...

func (*Blockchain) AddBlock

func (blockchain *Blockchain) AddBlock(data fmt.Stringer) error

AddBlock ...

type Clock

type Clock func() time.Time

Clock ...

type Dependencies

type Dependencies struct {
	BlockDependencies

	Storage Storage
}

Dependencies ...

type GroupStorage

type GroupStorage interface {
	Storage

	StoreBlockGroup(blocks BlockGroup) error
}

GroupStorage ...

type Loader

type Loader interface {
	LoadBlocks(cursor interface{}, count int) (
		blocks BlockGroup,
		nextCursor interface{},
		err error,
	)
}

Loader ...

type Proofer

type Proofer interface {
	Hash(block Block) string
	Validate(block Block) error
}

Proofer ...

type Storage

type Storage interface {
	LoadLastBlock() (Block, error)
	StoreBlock(block Block) error
}

Storage ...

type Stringer

type Stringer interface {
	fmt.Stringer
}

Stringer ...

It's used only for mock generating.

type ValidationMode

type ValidationMode int

ValidationMode ...

const (
	AsFullBlockchain ValidationMode = iota
	AsBlockchainChunk
)

...

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL