diff --git a/README.md b/README.md index 4f931da..d6ab314 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,10 @@ Gems: - [blockchain-lite](blockchain-lite) - build your own blockchain with crypto hashes - revolutionize the world with blockchains, blockchains, blockchains one block at a time - [merkletree](merkletree) - build your own crypto hash trees; named after Ralph Merkle who patented hash trees in 1979 (the patent expired in 2002); grow your own money on trees +- [ledger-lite](ledger-lite) - hyper ledger book for the distributed blockchain internet era; add your transactions one block at a time; transfer crypto(currencie)s and balance the accounts + + + diff --git a/ledger-lite/.gitignore b/ledger-lite/.gitignore new file mode 100644 index 0000000..5e1422c --- /dev/null +++ b/ledger-lite/.gitignore @@ -0,0 +1,50 @@ +*.gem +*.rbc +/.config +/coverage/ +/InstalledFiles +/pkg/ +/spec/reports/ +/spec/examples.txt +/test/tmp/ +/test/version_tmp/ +/tmp/ + +# Used by dotenv library to load environment variables. +# .env + +## Specific to RubyMotion: +.dat* +.repl_history +build/ +*.bridgesupport +build-iPhoneOS/ +build-iPhoneSimulator/ + +## Specific to RubyMotion (use of CocoaPods): +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# vendor/Pods/ + +## Documentation cache and generated files: +/.yardoc/ +/_yardoc/ +/doc/ +/rdoc/ + +## Environment normalization: +/.bundle/ +/vendor/bundle +/lib/bundler/man/ + +# for a library or gem, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# Gemfile.lock +# .ruby-version +# .ruby-gemset + +# unless supporting rvm < 1.11.0 or doing something fancy, ignore this: +.rvmrc diff --git a/ledger-lite/HISTORY.md b/ledger-lite/HISTORY.md new file mode 100644 index 0000000..7a77906 --- /dev/null +++ b/ledger-lite/HISTORY.md @@ -0,0 +1,3 @@ +### 0.0.1 / 2018-01-02 + +* Everything is new. First release diff --git a/ledger-lite/Manifest.txt b/ledger-lite/Manifest.txt new file mode 100644 index 0000000..7fb1e4e --- /dev/null +++ b/ledger-lite/Manifest.txt @@ -0,0 +1,13 @@ +HISTORY.md +LICENSE.md +Manifest.txt +README.md +Rakefile +lib/ledger-lite.rb +lib/ledger-lite/base.rb +lib/ledger-lite/version.rb +test/helper.rb +test/test_blocks.rb +test/test_transactions.rb +test/test_transactions_v2.rb +test/test_version.rb diff --git a/ledger-lite/NOTES.md b/ledger-lite/NOTES.md new file mode 100644 index 0000000..7c73776 --- /dev/null +++ b/ledger-lite/NOTES.md @@ -0,0 +1,4 @@ +# Notes + + +## Todos diff --git a/ledger-lite/README.md b/ledger-lite/README.md new file mode 100644 index 0000000..05f65c2 --- /dev/null +++ b/ledger-lite/README.md @@ -0,0 +1,310 @@ +# Ledger Lite + +ledger-lite library / gem - hyper ledger book for the distributed blockchain internet era; add your transactions one block at a time; transfer crypto(currencie)s and balance the accounts + + +* home :: [github.com/openblockchains/ledger.lite.rb](https://github.com/openblockchains/ledger.lite.rb) +* bugs :: [github.com/openblockchains/ledger.lite.rb/issues](https://github.com/openblockchains/ledger.lite.rb/issues) +* gem :: [rubygems.org/gems/ledger-lite](https://rubygems.org/gems/ledger-lite) +* rdoc :: [rubydoc.info/gems/ledger-lite](http://rubydoc.info/gems/ledger-lite) + + + +## Usage + +Let's add some transactions to the (hyper) ledger book: + +| From | To | $ | +|---------------------------|--------------|----:| +| Central Bank (†) | Vincent | 11 | +| Vincent | Anne | 3 | +| Anne | Julia | 2 | +| Julia | Luuk | 1 | +| | | | +| De Nederlandsche Bank (†) | Ruben | 11 | +| Vincent | Max | 3 | +| Ruben | Julia | 2 | +| Anne | Martijn | 1 | + +(†): Mining Transaction - New Dutch Gulden ($) on the Market! + + + +### Use `send` (send payment/transfer money) + +```ruby + +ledger = Ledger.new + +ledger.send( "Central Bank†", "Vincent", 11 ) +ledger.send( "Vincent", "Anne", 3 ) +ledger.send( "Anne", "Julia", 2 ) +ledger.send( "Julia", "Luuk", 1 ) + +ledger.send( "De Nederlandsche Bank†", "Ruben", 11 ) +ledger.send( "Vincent", "Max", 3 ) +ledger.send( "Ruben", "Julia", 2 ) +ledger.send( "Anne", "Martijn", 1 ) + +pp ledger ## pp = pretty print + +``` + +resulting in + +``` +# 5, + "Anne" => 0, + "Julia" => 3, + "Luuk" => 1, + "Ruben" => 9, + "Max" => 3, + "Martijn" => 1}> +``` + +that is, the addr hash holds all addresses (addr) with the account balances +telling you who owns how much: + +| Addr(ess) | Balance $ | +|---------------------|----------:| +| Vincent | 5 | +| Anne | 0 | +| Julia | 3 | +| Luuk | 1 | +| Ruben | 9 | +| Max | 3 | +| Martijn | 1 | + + +### Use `write` to write / add transactions + + +Or use transaction hashes: + +``` ruby +ledger = Ledger.new + +ledger.write( from: "Central Bank†", to: "Vincent", amount: 11 ) +ledger.write( from: "Vincent", to: "Anne", amount: 3 ) +ledger.write( from: "Anne", to: "Julia", amount: 2 ) +ledger.write( from: "Julia", to: "Luuk", amount: 1 ) + +ledger.write( { from: "De Nederlandsche Bank†", to: "Ruben", amount: 11 }, + { from: "Vincent", to: "Max", amount: 3 }, + { from: "Ruben", to: "Julia", amount: 2 }, + { from: "Anne", to: "Martijn", amount: 1 } ) + +pp ledger +``` + +Or use transaction (tx) classes/structs: + +``` ruby +ledger = Ledger.new + +ledger.write( Tx.new( "Central Bank†", "Vincent", 11 )) +ledger.write( Tx.new( "Vincent", "Anne", 3 )) +ledger.write( Tx.new( "Anne", "Julia", 2 )) +ledger.write( Tx.new( "Julia", "Luuk", 1 )) + +ledger.write( Tx.new( "De Nederlandsche Bank†", "Ruben", 11 ), + Tx.new( "Vincent", "Max", 3 ), + Tx.new( "Ruben", "Julia", 2 ), + Tx.new( "Anne", "Martijn", 1 )) + +pp ledger +``` + +Or use the operator `<<` alias for `write`: + +```ruby +ledger = Ledger.new + +ledger << Tx.new( "Central Bank†", "Vincent", 11 ) +ledger << Tx.new( "Vincent", "Anne", 3 ) +ledger << Tx.new( "Anne", "Julia", 2 ) +ledger << Tx.new( "Julia", "Luuk", 1 ) + +ledger << [Tx.new( "De Nederlandsche Bank†", "Ruben", 11 ), + Tx.new( "Vincent", "Max", 3 ), + Tx.new( "Ruben", "Julia", 2 ), + Tx.new( "Anne", "Martijn", 1 )] + +pp ledger +``` + + +Or use blocks of transaction hashes: + +``` ruby + +ledger = Ledger.new + +ledger.write( Block.new( { from: "Central Bank†", to: "Vincent", amount: 11 }, + { from: "Vincent", to: "Anne", amount: 3 }, + { from: "Anne", to: "Julia", amount: 2 }, + { from: "Julia", to: "Luuk", amount: 1 } ), + Block.new( { from: "De Nederlandsche Bank†", to: "Ruben", amount: 11 }, + { from: "Vincent", to: "Max", amount: 3 }, + { from: "Ruben", to: "Julia", amount: 2 }, + { from: "Anne", to: "Martijn", amount: 1 } )) + +pp ledger +``` + + +Or use blocks of transaction classes/structs: + +``` ruby +ledger = Ledger.new + +ledger.write( Block.new( Tx.new( "Central Bank†", "Vincent", 11 ), + Tx.new( "Vincent", "Anne", 3 ), + Tx.new( "Anne", "Julia", 2 ), + Tx.new( "Julia", "Luuk", 1 )), + Block.new( Tx.new( "De Nederlandsche Bank†", "Ruben", 11 ), + Tx.new( "Vincent", "Max", 3 ), + Tx.new( "Ruben", "Julia", 2 ), + Tx.new( "Anne", "Martijn", 1 ))) + +pp ledger +``` + +Or use blocks of transaction classes/structs (with keyword arguments): + +```ruby +ledger = Ledger.new + +ledger.write( Block.new( Tx.new( from: "Central Bank†", to: "Vincent", amount: 11 ), + Tx.new( from: "Vincent", to: "Anne", amount: 3 ), + Tx.new( from: "Anne", to: "Julia", amount: 2 ), + Tx.new( from: "Julia", to: "Luuk", amount: 1 )), + Block.new( Tx.new( from: "De Nederlandsche Bank†", to: "Ruben", amount: 11 ), + Tx.new( from: "Vincent", to: "Max", amount: 3 ), + Tx.new( from: "Ruben", to: "Julia", amount: 2 ), + Tx.new( from: "Anne", to: "Martijn", amount: 1 ))) + +pp ledger +``` + +# Bonus: Track Commodities, Collectibles or Assets + +Ledger Lite lets you design / create your own transactions. For example, let's use +`from`, `to`, `qty` (quantity) and `name` (of commodity, collectible or asset). +Override the `Ledger#unpack` method for "unpacking" arguments from transactions +and the `Ledger#send` method for "committing" transactions: + + +``` ruby +def unpack( tx ) + ## "unpack" from, to, qty, name values + if tx.is_a?( Hash ) ## support hashes + from = tx[:from] + to = tx[:to] + qty = tx[:qty] + name = tx[:name] + else ## assume it's a transaction (tx) struct/class + from = tx.from + to = tx.to + qty = tx.qty + name = tx.name + end + [from,to,qty,name] +end +``` + +and + +``` ruby +def send( from, to, qty, name ) + if sufficient?( from, qty, name ) + if self.class.config.coinbase?( from ) + # note: coinbase has unlimited supply!! magic happens here + else + @addr[ from ][ name ] -= qty + end + @addr[ to ] ||= {} ## make sure addr exists (e.g. init with empty hash {}) + @addr[ to ][ name ] ||= 0 + @addr[ to ][ name ] += qty + end +end +``` + +Now use the ledger with the new transaction format like: + + +``` ruby +ledger = Ledger.new + +ledger.send( "Keukenhof†", "Vincent", 11, "Tulip Admiral van Eijck" ) +ledger.send( "Vincent", "Anne", 3, "Tulip Admiral van Eijck" ) +ledger.send( "Anne", "Julia", 2, "Tulip Admiral van Eijck" ) +ledger.send( "Julia", "Luuk", 1, "Tulip Admiral van Eijck" ) + +ledger.send( "Dutchgrown†", "Ruben", 11, "Tulip Semper Augustus" ) +ledger.send( "Vincent", "Max", 3, "Tulip Admiral van Eijck" ) +ledger.send( "Ruben", "Julia", 2, "Tulip Semper Augustus" ) +ledger.send( "Anne", "Martijn", 1, "Tulip Admiral van Eijck" ) + +pp ledger ## pp == pretty print +``` + +resulting in: + +``` +# {"Tulip Admiral van Eijck" => 5}, + "Anne" => {"Tulip Admiral van Eijck" => 0}, + "Julia" => {"Tulip Admiral van Eijck" => 1, "Tulip Semper Augustus" => 2}, + "Luuk" => {"Tulip Admiral van Eijck" => 1}, + "Ruben" => {"Tulip Semper Augustus" => 9}, + "Max" => {"Tulip Admiral van Eijck" => 3}, + "Martijn" => {"Tulip Admiral van Eijck" => 1}}> +``` + +Or use blocks of transaction classes/structs (with keyword arguments): + + +``` ruby +ledger = Ledger.new + +ledger.write( Block.new( Tx.new( from: "Keukenhof†", to: "Vincent", qty: 11, name: "Tulip Admiral van Eijck" ), + Tx.new( from: "Vincent", to: "Anne", qty: 3, name: "Tulip Admiral van Eijck" ), + Tx.new( from: "Anne", to: "Julia", qty: 2, name: "Tulip Admiral van Eijck" ), + Tx.new( from: "Julia", to: "Luuk", qty: 1, name: "Tulip Admiral van Eijck" )), + Block.new( Tx.new( from: "Dutchgrown†", to: "Ruben", qty: 11, name: "Tulip Semper Augustus" ), + Tx.new( from: "Vincent", to: "Max", qty: 3, name: "Tulip Admiral van Eijck" ), + Tx.new( from: "Ruben", to: "Julia", qty: 2, name: "Tulip Semper Augustus" ), + Tx.new( from: "Anne", to: "Martijn", qty: 1, name: "Tulip Admiral van Eijck" ))) + +pp ledger +``` + +And so on and on. + + + + +## Ledger Lite in the Real World + +- [**centralbank**](https://github.com/openblockchains/centralbank) - command line tool (and core library) - print your own money / cryptocurrency; run your own federated central bank nodes on the blockchain peer-to-peer over HTTP; revolutionize the world one block at a time +- [**tulipmania**](https://github.com/openblockchains/tulipmania) - command line tool (and core library) - tulips on the blockchain; learn by example from the real world (anno 1637) - buy! sell! hodl! enjoy the beauty of admiral of admirals, semper augustus, and more; run your own hyper ledger tulip exchange nodes on the blockchain peer-to-peer over HTTP; revolutionize the world one block at a time +- [**shilling**](https://github.com/bitshilling/bitshilling.tools) - command line tool (and core library) - shilling (or schilling) on the blockchain! rock-solid alpine dollar from austria; print (mine) your own shillings; run your own federated shilling central bank nodes w/ public distributed (hyper) ledger book on the blockchain peer-to-peer over HTTP; revolutionize the world one block at a time +- You? Add your tool / service + + + +## References + +[**Programming Cryptocurrencies and Blockchains (in Ruby)**](http://yukimotopress.github.io/blockchains) by Gerald Bauer et al, 2018, Yuki & Moto Press + + +## License + +![](https://publicdomainworks.github.io/buttons/zero88x31.png) + +The `ledger-lite` scripts are dedicated to the public domain. +Use it as you please with no restrictions whatsoever. diff --git a/ledger-lite/Rakefile b/ledger-lite/Rakefile new file mode 100644 index 0000000..15b851c --- /dev/null +++ b/ledger-lite/Rakefile @@ -0,0 +1,29 @@ +require 'hoe' +require './lib/ledger-lite/version.rb' + +Hoe.spec 'ledger-lite' do + + self.version = LedgerLite::VERSION + + self.summary = "ledger-lite - hyper ledger book for the distributed blockchain internet era; add your transactions one block at a time; transfer crypto(currencie)s and balance the accounts" + self.description = summary + + self.urls = ['https://github.com/openblockchains/ledger.lite.rb'] + + self.author = 'Gerald Bauer' + self.email = 'wwwmake@googlegroups.com' + + # switch extension to .markdown for gihub formatting + self.readme_file = 'README.md' + self.history_file = 'HISTORY.md' + + self.extra_deps = [ + ] + + self.licenses = ['Public Domain'] + + self.spec_extras = { + required_ruby_version: '>= 2.3' + } + +end diff --git a/ledger-lite/lib/ledger-lite.rb b/ledger-lite/lib/ledger-lite.rb new file mode 100644 index 0000000..37c7b23 --- /dev/null +++ b/ledger-lite/lib/ledger-lite.rb @@ -0,0 +1,9 @@ +# encoding: utf-8 + + +## our own code (without "top-level" shortcuts e.g. "modular version") +require 'ledger-lite/base' + +### +# add convenience top-level shortcut / alias +Ledger = LedgerLite::Ledger diff --git a/ledger-lite/lib/ledger-lite/base.rb b/ledger-lite/lib/ledger-lite/base.rb new file mode 100644 index 0000000..5e93af3 --- /dev/null +++ b/ledger-lite/lib/ledger-lite/base.rb @@ -0,0 +1,256 @@ +# encoding: utf-8 + +require 'pp' # for pp => pretty printer +require 'forwardable' + + + + +## our own code +require 'ledger-lite/version' # note: let version always go first + + + +module LedgerLite + + + +class Configuration + + ## system settings + + ## use a different name e.g. + ## - mint (like royal mint or federal coin mint?) or + ## - base + ## - magic (for creating coins out-of-nothing?) or + ## - network or ??) - why? why not? + ## + attr_accessor :coinbase + + ## note: for now is an array (allow/ support multiple coinbases) + ## note: add a (†) coinbase marker + COINBASE = ['Coinbase†'] + + def initialize + @coinbase = COINBASE + end + + def coinbase?( addr ) + @coinbase.include?( addr ) + end +end # class Configuration + + + + +class Base + extend Forwardable + + + ## lets you use + ## Ledger.configure do |config| + ## config.coinbase = ['Keukenhof†'] + ## end + + def self.configure + yield( config ) + end + + def self.config + @config ||= Configuration.new + end + + + + attr_reader :addr ## make addr private e.g. remove - why? e.g. use hash forwards/delegates - why not? + + + ## delegate some methods (and operators) to addr hash (for easier/shortcut access) + def_delegators :@addr, :[], :size, :each, :empty?, :any? + + + def initialize( *args ) + @addr = {} + + ## add all transactions passed in on startup; if no args - do nothing + unless args.empty? + ## note: MUST unsplat (*) args + ## otherwise args get "double" wrapped in array + write( *args ) + end + end # method initialize + + + + def write( *args ) + puts "write:" + pp args + + ## note: allow/support splat-* for now for convenience (auto-wraps args into array) + if args.size == 1 && args[0].is_a?( Array ) + puts " unwrap array in array" + blks_or_txns = args[0] ## "unwrap" array in array + elsif args.size == 1 && defined?( Blockchain ) && args[0].is_a?( Blockchain ) + ## support passing in of "top-level" defined blockchain class if defined + ## pass along all blocks ("unwrapped" from blockchain) + blks_or_txns = [] + args[0].each { |b| blks_or_txns << b } + else + blks_or_txns = args ## use "auto-wrapped" splat array + end + + ## "unpack" transactions from possible (optional) blocks + ## and return "flattend" **single** array of transactions + transactions = unpack_transactions( blks_or_txns ) + + ## unpack & unsplat array (to pass in args to send) => from, to, amount + transactions.each { |tx| send( *unpack(tx) ) } + end + + ## note: add and the << operator is an alias for write + alias :add :write + alias :<< :write + + + def unpack_transactions( blocks ) + ## "unpack" transactions from possible (optional) blocks + ## and return "flattend" **single** array of transactions + + blocks.reduce( [] ) do |acc, block| + if block.respond_to?( :transactions ) ## bingo! assume it's block if it has transactions method + acc + block.transactions + else ## note: otherwise assumes it's just a "plain" **single** transaction + tx = block + acc + [tx] ## wrap in array (use acc << tx - with side effects/mutate in place - why? why not?) + end + end + end +end ## class Base + + + +class Ledger < Base + + def unpack( tx ) + ## "unpack" from, to, amount values + + puts "unpack:" + pp tx + + if tx.is_a?( Hash ) ## support hashes + from = tx[:from] + to = tx[:to] + amount = tx[:amount] + else ## assume it's a transaction (tx) struct/class + from = tx.from + to = tx.to + amount = tx.amount + end + [from,to,amount] + end + + + ## + # find a better name - why? why not? + ## e.g. use can? funds? sufficient? has_funds? + def sufficient?( addr, amount ) + return true if self.class.config.coinbase?( addr ) ## note: coinbase has unlimited funds!!! + + @addr.has_key?( addr ) && + @addr[addr] - amount >= 0 + end + + ## note: sufficient_funds? is an alias for sufficient? + alias :sufficient_funds? :sufficient? + + + ## apply/do single transaction - send payment - do transfer + ## - find a different name - why? why not? + def send( from, to, amount ) + + if sufficient?( from, amount ) + if self.class.config.coinbase?( from ) + # note: coinbase has unlimited funds!! ("virtual" built-in money printing address) + else + @addr[ from ] -= amount + end + @addr[ to ] ||= 0 + @addr[ to ] += amount + end + end # method send + + ## note: transfer is an alias for send (payment) + alias :transfer :send + +end # class Ledger + + + + +module V2 + ## ledger for commodities (e.g.tulips) / assets / etc. + + class Ledger < Base + + def unpack( tx ) + ## "unpack" from, to, amount values + + puts "unpack:" + pp tx + + if tx.is_a?( Hash ) ## support hashes + from = tx[:from] + to = tx[:to] + qty = tx[:qty] ## check: use different name e.g. amount, num, etc. why? why not? + name = tx[:name] ## check: use different name e.g. title, what, etc. why? why not? + else ## assume it's a transaction (tx) struct/class + from = tx.from + to = tx.to + qty = tx.qty + name = tx.name + end + [from,to,qty,name] + end + + + ## + # find a better name - why? why not? + def sufficient?( addr, qty, name ) + return true if self.class.config.coinbase?( addr ) ## note: coinbase has unlimited funds!!! + + @addr.has_key?( addr ) && + @addr[addr].has_key?( name ) && + @addr[addr][name] - qty >= 0 + end + + + ## apply/do single transaction - send commodity/assets - do transfer + ## - find a different name - why? why not? + def send( from, to, qty, name ) + + if sufficient?( from, qty, name ) + if self.class.config.coinbase?( from ) + # note: coinbase has unlimited funds!! ("virtual" built-in money printing address) + else + @addr[ from ][ name ] -= qty + end + @addr[ to ] ||= {} ## make sure addr exists (e.g. init with empty hash {}) + @addr[ to ][ name ] ||= 0 + @addr[ to ][ name ] += qty + end + end # method send + + ## note: transfer is an alias for send (payment) + alias :transfer :send + + end # class Ledger + +end # module V2 + + + +end # module LedgerLite + + +# say hello +puts LedgerLite.banner if defined?($RUBYLIBS_DEBUG) && $RUBYLIBS_DEBUG diff --git a/ledger-lite/lib/ledger-lite/version.rb b/ledger-lite/lib/ledger-lite/version.rb new file mode 100644 index 0000000..25f8e79 --- /dev/null +++ b/ledger-lite/lib/ledger-lite/version.rb @@ -0,0 +1,23 @@ +# encoding: utf-8 + + +module LedgerLite + + MAJOR = 1 + MINOR = 1 + PATCH = 1 + VERSION = [MAJOR,MINOR,PATCH].join('.') + + def self.version + VERSION + end + + def self.banner + "ledger-lite/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]" + end + + def self.root + "#{File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )}" + end + +end # module LedgerLite diff --git a/ledger-lite/test/helper.rb b/ledger-lite/test/helper.rb new file mode 100644 index 0000000..9dc6926 --- /dev/null +++ b/ledger-lite/test/helper.rb @@ -0,0 +1,87 @@ +## $:.unshift(File.dirname(__FILE__)) + +## minitest setup + +require 'minitest/autorun' + + +## our own code + +require 'ledger-lite/base' ## note: use "modular" version without "top-level" Ledger constant + + + +## some helper classes for testing + +module V1 +class Transaction + + attr_reader :from, :to, :amount + + ##### + # flexible for testing - allows: + # Tx.new( 'Alice', 'Bob', 12 ) + # Tx.new( from: 'Alice', to: 'Bob', amount: 12 ) + + def initialize( *args, **kwargs ) + if args.empty? # try keyword args + @from = kwargs[:from] + @to = kwargs[:to] + @amount = kwargs[:amount] + else # try positional args + @from = args[0] + @to = args[1] + @amount = args[2] + end + end + +end # class Transaction + + Tx = Transaction ## convenience shortcut + Ledger = LedgerLite::Ledger +end # module V1 + + + +module V2 +class Transaction + + attr_reader :from, :to, :qty, :name + + ##### + # flexible for testing - allows: + # Tx.new( 'Alice', 'Bob', 12, 'Tulip Semper Augustus' ) + # Tx.new( from: 'Alice', to: 'Bob', qty: 12, name: 'Semper Augustus' ) + + def initialize( *args, **kwargs ) + if args.empty? # try keyword args + @from = kwargs[:from] + @to = kwargs[:to] + @qty = kwargs[:qty] + @name = kwargs[:name] + else # try positional args + @from = args[0] + @to = args[1] + @qty = args[2] + @name = args[3] + end + end + +end # class Transaction + + Tx = Transaction ## convenience shortcut + Ledger = LedgerLite::V2::Ledger +end # module V2 + + + + +class Block + def initialize( *transactions ) + @transactions = transactions + end + + def transactions + @transactions + end +end # class Block diff --git a/ledger-lite/test/test_blocks.rb b/ledger-lite/test/test_blocks.rb new file mode 100644 index 0000000..336193b --- /dev/null +++ b/ledger-lite/test/test_blocks.rb @@ -0,0 +1,109 @@ +# encoding: utf-8 + +### +# to run use +# ruby -I ./lib -I ./test test/test_blocks.rb + + +require 'helper' + + +class TestBlocks < MiniTest::Test + +include V1 # includes Ledger, Tx, etc. + +def setup + Ledger.configure do |config| + config.coinbase = ["Keukenhof†", "Dutchgrown†"] + end +end + +def test_blocks_with_hash + + b0 = Block.new( { from: "Keukenhof†", to: "Vincent", amount: 11 }, + { from: "Vincent", to: "Anne", amount: 3 }, + { from: "Anne", to: "Julia", amount: 2 }, + { from: "Julia", to: "Luuk", amount: 1 } ) + + b1 = Block.new( { from: "Dutchgrown†", to: "Ruben", amount: 11 }, + { from: "Vincent", to: "Max", amount: 3 }, + { from: "Ruben", to: "Julia", amount: 2 }, + { from: "Anne", to: "Martijn", amount: 1 } ) + + blockchain = [b0,b1] + + ledger = Ledger.new( blockchain ) + + pp ledger + + balances = {"Vincent"=>5, + "Anne"=>0, + "Julia"=>3, + "Luuk"=>1, + "Ruben"=>9, + "Max"=>3, + "Martijn"=>1} + assert_equal balances, ledger.addr +end + + +def test_blocks_with_tx_v1 + + b0 = Block.new( Tx.new( from: "Keukenhof†", to: "Vincent", amount: 11 ), + Tx.new( from: "Vincent", to: "Anne", amount: 3 ), + Tx.new( from: "Anne", to: "Julia", amount: 2 ), + Tx.new( from: "Julia", to: "Luuk", amount: 1 )) + + b1 = Block.new( Tx.new( from: "Dutchgrown†", to: "Ruben", amount: 11 ), + Tx.new( from: "Vincent", to: "Max", amount: 3 ), + Tx.new( from: "Ruben", to: "Julia", amount: 2 ), + Tx.new( from: "Anne", to: "Martijn", amount: 1 )) + + blockchain = [b0,b1] + + ledger = Ledger.new( blockchain ) + + pp ledger + + balances = {"Vincent"=>5, + "Anne"=>0, + "Julia"=>3, + "Luuk"=>1, + "Ruben"=>9, + "Max"=>3, + "Martijn"=>1} + assert_equal balances, ledger.addr +end + + + +def test_blocks_with_tx_v2 + + b0 = Block.new( Tx.new( "Keukenhof†", "Vincent", 11 ), + Tx.new( "Vincent", "Anne", 3 ), + Tx.new( "Anne", "Julia", 2 ), + Tx.new( "Julia", "Luuk", 1 )) + + b1 = Block.new( Tx.new( "Dutchgrown†", "Ruben", 11 ), + Tx.new( "Vincent", "Max", 3 ), + Tx.new( "Ruben", "Julia", 2 ), + Tx.new( "Anne", "Martijn", 1 )) + + blockchain = [b0,b1] + + ledger = Ledger.new( blockchain ) + + pp ledger + + balances = {"Vincent"=>5, + "Anne"=>0, + "Julia"=>3, + "Luuk"=>1, + "Ruben"=>9, + "Max"=>3, + "Martijn"=>1} + assert_equal balances, ledger.addr +end + + +end # class TestBlocks diff --git a/ledger-lite/test/test_transactions.rb b/ledger-lite/test/test_transactions.rb new file mode 100644 index 0000000..6e8aa4d --- /dev/null +++ b/ledger-lite/test/test_transactions.rb @@ -0,0 +1,158 @@ +# encoding: utf-8 + +### +# to run use +# ruby -I ./lib -I ./test test/test_transactions.rb + + +require 'helper' + + +class TestTransactions < MiniTest::Test + +include V1 # includes Ledger, Tx, etc. + +def setup + Ledger.configure do |config| + config.coinbase = ["Keukenhof†", "Dutchgrown†"] + end +end + + +def test_send + + ledger = Ledger.new + + ledger.send( "Keukenhof†", "Vincent", 11 ) + ledger.send( "Vincent", "Anne", 3 ) + ledger.send( "Anne", "Julia", 2 ) + ledger.send( "Julia", "Luuk", 1 ) + + ledger.send( "Dutchgrown†", "Ruben", 11 ) + ledger.send( "Vincent", "Max", 3 ) + ledger.send( "Ruben", "Julia", 2 ) + ledger.send( "Anne", "Martijn", 1 ) + + pp ledger + + balances = {"Vincent"=>5, + "Anne"=>0, + "Julia"=>3, + "Luuk"=>1, + "Ruben"=>9, + "Max"=>3, + "Martijn"=>1} + assert_equal balances, ledger.addr +end + + +def test_with_tx_v1 + + ledger = Ledger.new + + ledger.write( Tx.new( "Keukenhof†", "Vincent", 11 )) + ledger.write( Tx.new( "Vincent", "Anne", 3 )) + ledger.write( Tx.new( "Anne", "Julia", 2 )) + ledger.write( Tx.new( "Julia", "Luuk", 1 )) + + ledger.write( Tx.new( "Dutchgrown†", "Ruben", 11 )) + ledger.write( Tx.new( "Vincent", "Max", 3 )) + ledger.write( Tx.new( "Ruben", "Julia", 2 )) + ledger.write( Tx.new( "Anne", "Martijn", 1 )) + + pp ledger + + balances = {"Vincent"=>5, + "Anne"=>0, + "Julia"=>3, + "Luuk"=>1, + "Ruben"=>9, + "Max"=>3, + "Martijn"=>1} + assert_equal balances, ledger.addr +end + + +def test_with_tx_v2 + + ledger = Ledger.new + + ledger.write( Tx.new( "Keukenhof†", "Vincent", 11 ), + Tx.new( "Vincent", "Anne", 3 ), + Tx.new( "Anne", "Julia", 2 ), + Tx.new( "Julia", "Luuk", 1 )) + + ledger.write( Tx.new( "Dutchgrown†", "Ruben", 11 ), + Tx.new( "Vincent", "Max", 3 ), + Tx.new( "Ruben", "Julia", 2 ), + Tx.new( "Anne", "Martijn", 1 )) + + pp ledger + + balances = {"Vincent"=>5, + "Anne"=>0, + "Julia"=>3, + "Luuk"=>1, + "Ruben"=>9, + "Max"=>3, + "Martijn"=>1} + assert_equal balances, ledger.addr +end + + +def test_with_tx_v3 + + ledger = Ledger.new + + ledger << Tx.new( "Keukenhof†", "Vincent", 11 ) + ledger << Tx.new( "Vincent", "Anne", 3 ) + ledger << Tx.new( "Anne", "Julia", 2 ) + ledger << Tx.new( "Julia", "Luuk", 1 ) + + ledger << [Tx.new( "Dutchgrown†", "Ruben", 11 ), + Tx.new( "Vincent", "Max", 3 ), + Tx.new( "Ruben", "Julia", 2 ), + Tx.new( "Anne", "Martijn", 1 )] + + pp ledger + + balances = {"Vincent"=>5, + "Anne"=>0, + "Julia"=>3, + "Luuk"=>1, + "Ruben"=>9, + "Max"=>3, + "Martijn"=>1} + assert_equal balances, ledger.addr +end + + + +def test_with_hash + + ledger = Ledger.new + + ledger.write( { from: "Keukenhof†", to: "Vincent", amount: 11 }, + { from: "Vincent", to: "Anne", amount: 3 }, + { from: "Anne", to: "Julia", amount: 2 }, + { from: "Julia", to: "Luuk", amount: 1 }) + + ledger.write( { from: "Dutchgrown†", to: "Ruben", amount: 11 }, + { from: "Vincent", to: "Max", amount: 3 }, + { from: "Ruben", to: "Julia", amount: 2 }, + { from: "Anne", to: "Martijn", amount: 1 }) + + pp ledger + + balances = {"Vincent"=>5, + "Anne"=>0, + "Julia"=>3, + "Luuk"=>1, + "Ruben"=>9, + "Max"=>3, + "Martijn"=>1} + assert_equal balances, ledger.addr +end + + +end # class TestTransactions diff --git a/ledger-lite/test/test_transactions_v2.rb b/ledger-lite/test/test_transactions_v2.rb new file mode 100644 index 0000000..f500b23 --- /dev/null +++ b/ledger-lite/test/test_transactions_v2.rb @@ -0,0 +1,109 @@ +# encoding: utf-8 + +### +# to run use +# ruby -I ./lib -I ./test test/test_transactions_v2.rb + + +require 'helper' + + +class TestTransactionsV2 < MiniTest::Test + +include V2 # includes Ledger, Tx, etc. + +def setup + Ledger.configure do |config| + ## note: for testing use different coinbase than "classic / standard" V1 version + config.coinbase = ["Flowers†", "Bloom & Blossom†"] + end +end + + + +def test_send + + ledger = Ledger.new + + ledger.send( "Flowers†", "Vincent", 11, "Tulip Admiral van Eijck" ) + ledger.send( "Vincent", "Anne", 3, "Tulip Admiral van Eijck" ) + ledger.send( "Anne", "Julia", 2, "Tulip Admiral van Eijck" ) + ledger.send( "Julia", "Luuk", 1, "Tulip Admiral van Eijck" ) + + ledger.send( "Bloom & Blossom†", "Ruben", 11, "Tulip Semper Augustus" ) + ledger.send( "Vincent", "Max", 3, "Tulip Admiral van Eijck" ) + ledger.send( "Ruben", "Julia", 2, "Tulip Semper Augustus" ) + ledger.send( "Anne", "Martijn", 1, "Tulip Admiral van Eijck" ) + + pp ledger + + balances = {"Vincent"=>{"Tulip Admiral van Eijck"=>5}, + "Anne"=>{"Tulip Admiral van Eijck"=>0}, + "Julia"=>{"Tulip Admiral van Eijck"=>1, "Tulip Semper Augustus"=>2}, + "Luuk"=>{"Tulip Admiral van Eijck"=>1}, + "Ruben"=>{"Tulip Semper Augustus"=>9}, + "Max"=>{"Tulip Admiral van Eijck"=>3}, + "Martijn"=>{"Tulip Admiral van Eijck"=>1}} + + assert_equal balances, ledger.addr +end + + +def test_with_tx + + ledger = Ledger.new + + ledger.write( Tx.new( "Flowers†", "Vincent", 11, "Tulip Admiral van Eijck" )) + ledger.write( Tx.new( "Vincent", "Anne", 3, "Tulip Admiral van Eijck" )) + + ledger << Tx.new( "Anne", "Julia", 2, "Tulip Admiral van Eijck" ) + ledger << Tx.new( "Julia", "Luuk", 1, "Tulip Admiral van Eijck" ) + + + ledger.write( Tx.new( "Bloom & Blossom†", "Ruben", 11, "Tulip Semper Augustus" ), + Tx.new( "Vincent", "Max", 3, "Tulip Admiral van Eijck" ), + Tx.new( "Ruben", "Julia", 2, "Tulip Semper Augustus" ), + Tx.new( "Anne", "Martijn", 1, "Tulip Admiral van Eijck" )) + + pp ledger + + balances = {"Vincent"=>{"Tulip Admiral van Eijck"=>5}, + "Anne"=>{"Tulip Admiral van Eijck"=>0}, + "Julia"=>{"Tulip Admiral van Eijck"=>1, "Tulip Semper Augustus"=>2}, + "Luuk"=>{"Tulip Admiral van Eijck"=>1}, + "Ruben"=>{"Tulip Semper Augustus"=>9}, + "Max"=>{"Tulip Admiral van Eijck"=>3}, + "Martijn"=>{"Tulip Admiral van Eijck"=>1}} + + assert_equal balances, ledger.addr +end + + +def test_with_hash + + ledger = Ledger.new + + ledger.write( { from: "Flowers†", to: "Vincent", qty: 11, name: "Tulip Admiral van Eijck" }, + { from: "Vincent", to: "Anne", qty: 3, name: "Tulip Admiral van Eijck" }, + { from: "Anne", to: "Julia", qty: 2, name: "Tulip Admiral van Eijck" }, + { from: "Julia", to: "Luuk", qty: 1, name: "Tulip Admiral van Eijck" }) + + ledger.write( { from: "Bloom & Blossom†", to: "Ruben", qty: 11, name: "Tulip Semper Augustus" }, + { from: "Vincent", to: "Max", qty: 3, name: "Tulip Admiral van Eijck" }, + { from: "Ruben", to: "Julia", qty: 2, name: "Tulip Semper Augustus" }, + { from: "Anne", to: "Martijn", qty: 1, name: "Tulip Admiral van Eijck" }) + + pp ledger + + balances = {"Vincent"=>{"Tulip Admiral van Eijck"=>5}, + "Anne"=>{"Tulip Admiral van Eijck"=>0}, + "Julia"=>{"Tulip Admiral van Eijck"=>1, "Tulip Semper Augustus"=>2}, + "Luuk"=>{"Tulip Admiral van Eijck"=>1}, + "Ruben"=>{"Tulip Semper Augustus"=>9}, + "Max"=>{"Tulip Admiral van Eijck"=>3}, + "Martijn"=>{"Tulip Admiral van Eijck"=>1}} + + assert_equal balances, ledger.addr +end + +end # class TestTransactionsV2 diff --git a/ledger-lite/test/test_version.rb b/ledger-lite/test/test_version.rb new file mode 100644 index 0000000..ebc24aa --- /dev/null +++ b/ledger-lite/test/test_version.rb @@ -0,0 +1,22 @@ +# encoding: utf-8 + +### +# to run use +# ruby -I ./lib -I ./test test/test_version.rb + + +require 'helper' + + +class TestVersion < MiniTest::Test + +def test_version + pp LedgerLite.version + pp LedgerLite.banner + pp LedgerLite.root + + assert true ## (for now) everything ok if we get here +end + + +end # class TestVersion diff --git a/merkletree/README.md b/merkletree/README.md index 208b134..44e9d8a 100644 --- a/merkletree/README.md +++ b/merkletree/README.md @@ -1,4 +1,4 @@ -# Merkle Tree (Ruby Edition) +# Merkle Tree merkletree library / gem - build your own crypto hash trees; named after Ralph Merkle who patented hash trees in 1979 (the patent expired in 2002); grow your own money on trees