Educational implementation of core blockchain primitives in Ruby: blocks, transactions, proof-of-work difficulty, chain validation, and service objects.
- Create the genesis block and initialize the chain via
Case::Blockchain::CreateandCase::Block::CreateGenesis. - Add new blocks with transaction data through
Case::Blockchain::Add. - Validate chain integrity and difficulty with
Case::Blockchain::Validate. - Typed entities and value objects on dry-struct/dry-types with Zeitwerk autoloading.
- Ruby 3.4.7
- dry-rb (
dry-struct,dry-types,dry-initializer) resol(service layer)zeitwerk(autoload)rspec,simplecov/simplecov-lcov,rubocop,pry
app/
case/ # service objects
entity/ # block, transaction, blockchain entities
value/ # transaction input/output value objects
config.rb # dependencies and Zeitwerk setup
spec/ # RSpec tests
- Install Ruby 3.4.7 (rbenv/rvm) and Bundler.
- Install dependencies:
bundle install
- Run tests:
bundle exec rspec
Create a chain, add a block, and validate:
require_relative "config"
input = Value::Transaction::Input.new(transaction_id: "0" * 64, output_index: 0)
output = Value::Transaction::Output.new(address: "demo_wallet", amount: BigDecimal("1.0"))
transaction = Entity::Transaction.build([input], [output])
blockchain = Case::Blockchain::Create.call!
Case::Blockchain::Add.call!(blockchain, [transaction])
valid = Case::Blockchain::Validate.call!(blockchain)
puts "Chain valid? #{valid}"Create a chain with custom difficulty and multiple transactions:
require_relative "config"
input1 = Value::Transaction::Input.new(transaction_id: "a" * 64, output_index: 0)
output1 = Value::Transaction::Output.new(address: "alice", amount: BigDecimal("0.5"))
input2 = Value::Transaction::Input.new(transaction_id: "b" * 64, output_index: 1)
output2 = Value::Transaction::Output.new(address: "bob", amount: BigDecimal("0.25"))
transactions = [
Entity::Transaction.build([input1], [output1]),
Entity::Transaction.build([input2], [output2]),
]
blockchain = Case::Blockchain::Create.call!(difficulty: 2)
Case::Blockchain::Add.call!(blockchain, transactions)
puts "Blocks in chain: #{blockchain.blocks.size}"- Run tests:
bundle exec rspec - Coverage report (HTML and LCOV):
COVER=1 bundle exec rspec(outputs tocoverage/) - Lint:
bundle exec rubocop
- Default mining difficulty is set in
SimpleBlockchain.default_difficulty(seeconfig.rb). - SimpleCov is enabled when
COVER=1orCOVER=trueis set in the environment.