blockchain-lite 1.1.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Rakefile CHANGED
@@ -1,29 +1,30 @@
1
- require 'hoe'
2
- require './lib/blockchain-lite/version.rb'
3
-
4
- Hoe.spec 'blockchain-lite' do
5
-
6
- self.version = BlockchainLite::VERSION
7
-
8
- self.summary = "blockchain-lite - build your own blockchain with crypto hashes - revolutionize the world with blockchains, blockchains, blockchains one block at a time"
9
- self.description = summary
10
-
11
- self.urls = ['https://github.com/openblockchains/blockchain.lite.rb']
12
-
13
- self.author = 'Gerald Bauer'
14
- self.email = 'wwwmake@googlegroups.com'
15
-
16
- # switch extension to .markdown for gihub formatting
17
- self.readme_file = 'README.md'
18
- self.history_file = 'HISTORY.md'
19
-
20
- self.extra_deps = [
21
- ]
22
-
23
- self.licenses = ['Public Domain']
24
-
25
- self.spec_extras = {
26
- required_ruby_version: '>= 2.3'
27
- }
28
-
29
- end
1
+ require 'hoe'
2
+ require './lib/blockchain-lite/version.rb'
3
+
4
+ Hoe.spec 'blockchain-lite' do
5
+
6
+ self.version = BlockchainLite::VERSION
7
+
8
+ self.summary = "blockchain-lite - build your own blockchain with crypto hashes - revolutionize the world with blockchains, blockchains, blockchains one block at a time"
9
+ self.description = summary
10
+
11
+ self.urls = { home: 'https://github.com/rubycoco/blockchain' }
12
+
13
+ self.author = 'Gerald Bauer'
14
+ self.email = 'wwwmake@googlegroups.com'
15
+
16
+ # switch extension to .markdown for gihub formatting
17
+ self.readme_file = 'README.md'
18
+ self.history_file = 'CHANGELOG.md'
19
+
20
+ self.extra_deps = [
21
+ ['merkletree'],
22
+ ]
23
+
24
+ self.licenses = ['Public Domain']
25
+
26
+ self.spec_extras = {
27
+ required_ruby_version: '>= 2.3'
28
+ }
29
+
30
+ end
@@ -1,26 +1,11 @@
1
- # encoding: utf-8
2
-
3
- require 'digest' # for hash checksum digest function SHA256
4
- require 'pp' # for pp => pretty printer
5
-
6
- require 'date'
7
- require 'time'
8
- require 'json'
9
- require 'uri'
10
-
11
-
12
-
13
- ## our own code
14
- require 'blockchain-lite/version' # note: let version always go first
15
-
16
- require 'blockchain-lite/basic/block'
17
- require 'blockchain-lite/proof_of_work/block'
18
-
19
- require 'blockchain-lite/blockchain'
20
-
21
- require 'blockchain-lite/block' ## configure "standard" default block (e.g. basic, proof-of-work, etc.)
22
-
23
-
24
-
25
- # say hello
26
- puts BlockchainLite.banner if defined?($RUBYLIBS_DEBUG) && $RUBYLIBS_DEBUG
1
+ # encoding: utf-8
2
+
3
+
4
+ ## our own code (without "top-level" shortcuts e.g. "modular version")
5
+ require 'blockchain-lite/base'
6
+
7
+ ###
8
+ # add convenience top-level shortcut / alias
9
+ # "standard" default block for now block with proof of work
10
+ Block = BlockchainLite::ProofOfWork::Block
11
+ Blockchain = BlockchainLite::Blockchain
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ require 'digest' # for hash checksum digest function SHA256
4
+ require 'pp' # for pp => pretty printer
5
+
6
+ require 'forwardable'
7
+ require 'date'
8
+ require 'time'
9
+ require 'json'
10
+ require 'uri'
11
+
12
+
13
+ ## 3rd party libs
14
+ require 'merkletree'
15
+
16
+
17
+ ## our own code
18
+ require 'blockchain-lite/version' # note: let version always go first
19
+
20
+ require 'blockchain-lite/basic/block'
21
+ require 'blockchain-lite/proof_of_work/block'
22
+
23
+ require 'blockchain-lite/blockchain'
24
+
25
+
26
+
27
+ # say hello
28
+ puts BlockchainLite.banner ## if defined?($RUBYLIBS_DEBUG) && $RUBYLIBS_DEBUG
@@ -8,33 +8,55 @@ class Block
8
8
 
9
9
  attr_reader :index
10
10
  attr_reader :timestamp
11
- attr_reader :data
11
+ attr_reader :transactions_count # use alias - txn_count - why? why not?
12
+ attr_reader :transactions # use alias - txn - why? why not?
12
13
  attr_reader :previous_hash
13
14
  attr_reader :hash
14
15
 
15
- def initialize(index, data, previous_hash)
16
- @index = index
17
- @timestamp = Time.now.utc ## note: use coordinated universal time (utc)
18
- @data = data
19
- @previous_hash = previous_hash
20
- @hash = calc_hash
16
+ def initialize(index, transactions, previous_hash, timestamp: nil)
17
+ @index = index
18
+
19
+ ## note: use coordinated universal time (utc)
20
+ ## auto-add timestamp for new blocks (e.g. timestamp is nil)
21
+ @timestamp = timestamp ? timestamp : Time.now.utc
22
+
23
+ ## note: assumes / expects an array for transactions
24
+ @transactions = transactions
25
+ @transactions_count = transactions.size
26
+
27
+ @previous_hash = previous_hash
28
+ @hash = calc_hash
21
29
  end
22
30
 
23
31
  def calc_hash
24
32
  sha = Digest::SHA256.new
25
- sha.update( @index.to_s + @timestamp.to_s + @data + @previous_hash )
33
+ sha.update( @timestamp.to_s +
34
+ @transactions.to_s +
35
+ @previous_hash )
26
36
  sha.hexdigest
27
37
  end
28
38
 
29
39
 
30
40
 
31
- def self.first( data='Genesis' ) # create genesis (big bang! first) block
41
+ def self.first( *args ) # create genesis (big bang! first) block
42
+ ## note: allow/support splat-* for now for convenience (auto-wraps args into array)
43
+ if args.size == 1 && args[0].is_a?( Array )
44
+ transactions = args[0] ## "unwrap" array in array
45
+ else
46
+ transactions = args ## use "auto-wrapped" splat array
47
+ end
32
48
  ## uses index zero (0) and arbitrary previous_hash ('0')
33
- Block.new( 0, data, '0' )
49
+ Block.new( 0, transactions, '0' )
34
50
  end
35
51
 
36
- def self.next( previous, data='Transaction Data...' )
37
- Block.new( previous.index+1, data, previous.hash )
52
+ def self.next( previous, *args )
53
+ ## note: allow/support splat-* for now for convenience (auto-wraps args into array)
54
+ if args.size == 1 && args[0].is_a?( Array )
55
+ transactions = args[0] ## "unwrap" array in array
56
+ else
57
+ transactions = args ## use "auto-wrapped" splat array
58
+ end
59
+ Block.new( previous.index+1, transactions, previous.hash )
38
60
  end
39
61
 
40
62
  end # class Block
@@ -4,48 +4,49 @@
4
4
  ##
5
5
  # convenience wrapper for array holding blocks (that is, a blockchain)
6
6
 
7
+ module BlockchainLite
8
+
7
9
 
8
10
  class Blockchain
11
+ extend Forwardable
9
12
 
10
- def initialize( chain=nil, block_class: nil )
11
- if chain.nil?
12
- @block_class = block_class || BlockchainLite::ProofOfWork::Block
13
- b0 = @block_class.first( 'Genesis' )
14
- @chain = [b0]
15
- else
16
- @chain = chain # "wrap" passed in blockchain (in array)
17
- if block_class # configure block class ("factory")
18
- @block_class = block_class
19
- else
20
- ### no block class configured; use class of first block
21
- ## todo/fix: throw except if chain is empty (no class configured) - why? why not??
22
- @block_class = @chain.first.class if @chain.first
23
- end
24
- end
13
+ def initialize( chain=[] )
14
+ @chain = chain # "wrap" passed in blockchain (in array)
25
15
  end
26
16
 
17
+ ##
18
+ # todo/check: can we make it work without "virtual" block_class method
19
+ ## e.g. use constant lookup with singleton class or something - possible?
20
+ def block_class() Block; end # if not configured; fallback to "top level" Block
21
+
27
22
 
28
- def last() @chain.last; end ## return last block in chain
23
+
24
+ ## delegate some methods (and operators) to chain array (for easier/shortcut access)
25
+ def_delegators :@chain, :[], :size, :each, :last, :empty?, :any?
29
26
 
30
27
 
31
28
  def <<( arg )
32
- if arg.is_a? String ## assume its (just) data
33
- data = arg
34
- bl = @chain.last
35
- b = @block_class.next( bl, data )
36
- elsif arg.class.respond_to?( :first ) && ## check if respond_to? Block.first? and Block.next? - assume it's a block
37
- arg.class.respond_to?( :next ) ## check/todo: use is_a? @block_class why? why not?
29
+ if arg.is_a?( block_class )
38
30
  b = arg
39
- else ## fallback; assume its (just) data (not a block)
40
- data = arg.to_s ## note: always convert arg to string!! - why? why not??
41
- bl = @chain.last
42
- b = @block_class.next( bl, data )
31
+ else
32
+ if arg.is_a?( Array ) ## assume its (just) data
33
+ data = arg
34
+ else ## fallback; assume single transaction record; wrap in array - allow fallback - why? why not??
35
+ data = [arg]
36
+ end
37
+
38
+ if @chain.empty?
39
+ b = block_class.first( data )
40
+ else
41
+ bl = @chain.last
42
+ b = block_class.next( bl, data )
43
+ end
43
44
  end
45
+
44
46
  @chain << b ## add/append (new) block to chain
45
47
  end
46
48
 
47
49
 
48
-
49
50
  def broken?
50
51
  ## check for validation conventions
51
52
  ## - start with first block?
@@ -94,3 +95,5 @@ class Blockchain
94
95
  def valid?() !broken?; end
95
96
 
96
97
  end ## class Blockchain
98
+
99
+ end ## module BlockchainLite
@@ -8,36 +8,75 @@ class Block
8
8
 
9
9
  attr_reader :index
10
10
  attr_reader :timestamp
11
- attr_reader :data
11
+ attr_reader :transactions_count # use alias - txn_count - why? why not?
12
+ attr_reader :transactions # use alias - txn - why? why not?
13
+ attr_reader :transactions_hash # use alias - merkle_root - why? why not?
12
14
  attr_reader :previous_hash
13
- attr_reader :nonce ## proof of work if hash starts with leading zeros (00)
15
+ attr_reader :nonce # ("lucky" number used once) - proof of work if hash starts with leading zeros (00)
14
16
  attr_reader :hash
15
17
 
16
- def initialize(index, data, previous_hash)
17
- @index = index
18
- @timestamp = Time.now.utc ## note: use coordinated universal time (utc)
19
- @data = data
18
+ def initialize(index, transactions, previous_hash, timestamp: nil, nonce: nil)
19
+ @index = index
20
+
21
+ ## note: assumes / expects an array for transactions
22
+ @transactions = transactions
23
+ @transactions_count = transactions.size
24
+
25
+ ## todo: add empty array check to merkletree.compute why? why not?
26
+ @transactions_hash = transactions.empty? ? '0' : MerkleTree.compute_root_for( transactions )
27
+
20
28
  @previous_hash = previous_hash
21
- @nonce, @hash = compute_hash_with_proof_of_work
29
+
30
+ ## note: use coordinated universal time (utc)
31
+ @timestamp = timestamp ? timestamp : Time.now.utc
32
+
33
+ if nonce ## restore pre-computed/mined block (from disk/cache/db/etc.)
34
+ ## todo: check timestamp MUST NOT be nil
35
+ @nonce = nonce
36
+ @hash = calc_hash
37
+ else ## new block (mine! e.g. find nonce - "lucky" number used once)
38
+ @nonce, @hash = compute_hash_with_proof_of_work
39
+ end
22
40
  end
23
41
 
24
42
  def calc_hash
25
- sha = Digest::SHA256.new
26
- sha.update( @nonce.to_s + @index.to_s + @timestamp.to_s + @data + @previous_hash )
27
- sha.hexdigest
43
+ calc_hash_with_nonce( @nonce )
28
44
  end
29
45
 
30
46
 
31
- def self.first( data='Genesis' ) # create genesis (big bang! first) block
47
+
48
+ def self.first( *args, **opts ) # create genesis (big bang! first) block
49
+ ## note: allow/support splat-* for now for convenience (auto-wraps args into array)
50
+ if args.size == 1 && args[0].is_a?( Array )
51
+ transactions = args[0] ## "unwrap" array in array
52
+ else
53
+ transactions = args ## use "auto-wrapped" splat array
54
+ end
32
55
  ## uses index zero (0) and arbitrary previous_hash ('0')
33
- Block.new( 0, data, '0' )
56
+ ## note: pass along (optional) custom timestamp (e.g. used for 1637 etc.)
57
+ Block.new( 0, transactions, '0', timestamp: opts[:timestamp] )
34
58
  end
35
59
 
36
- def self.next( previous, data='Transaction Data...' )
37
- Block.new( previous.index+1, data, previous.hash )
60
+ def self.next( previous, *args, **opts )
61
+ ## note: allow/support splat-* for now for convenience (auto-wraps args into array)
62
+ if args.size == 1 && args[0].is_a?( Array )
63
+ transactions = args[0] ## "unwrap" array in array
64
+ else
65
+ transactions = args ## use "auto-wrapped" splat array
66
+ end
67
+ Block.new( previous.index+1, transactions, previous.hash, timestamp: opts[:timestamp] )
38
68
  end
39
69
 
70
+
40
71
  private
72
+ def calc_hash_with_nonce( nonce=0 )
73
+ sha = Digest::SHA256.new
74
+ sha.update( nonce.to_s +
75
+ @timestamp.to_s +
76
+ @transactions_hash +
77
+ @previous_hash )
78
+ sha.hexdigest
79
+ end
41
80
 
42
81
  def compute_hash_with_proof_of_work( difficulty='00' )
43
82
  nonce = 0
@@ -51,12 +90,6 @@ private
51
90
  end
52
91
  end
53
92
 
54
- def calc_hash_with_nonce( nonce=0 )
55
- sha = Digest::SHA256.new
56
- sha.update( nonce.to_s + @index.to_s + @timestamp.to_s + @data + @previous_hash )
57
- sha.hexdigest
58
- end
59
-
60
93
  end # class Block
61
94
 
62
95
 
@@ -1,23 +1,23 @@
1
- # encoding: utf-8
2
-
3
-
4
- module BlockchainLite
5
-
6
- MAJOR = 1
7
- MINOR = 1
8
- PATCH = 0
9
- VERSION = [MAJOR,MINOR,PATCH].join('.')
10
-
11
- def self.version
12
- VERSION
13
- end
14
-
15
- def self.banner
16
- "blockchain-lite/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
17
- end
18
-
19
- def self.root
20
- "#{File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )}"
21
- end
22
-
23
- end # module BlockchainLite
1
+ # encoding: utf-8
2
+
3
+
4
+ module BlockchainLite
5
+
6
+ MAJOR = 1
7
+ MINOR = 4
8
+ PATCH = 1
9
+ VERSION = [MAJOR,MINOR,PATCH].join('.')
10
+
11
+ def self.version
12
+ VERSION
13
+ end
14
+
15
+ def self.banner
16
+ "blockchain-lite/#{VERSION} on Ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}] in (#{root})"
17
+ end
18
+
19
+ def self.root
20
+ File.expand_path( File.dirname(File.dirname(File.dirname(__FILE__))) )
21
+ end
22
+
23
+ end # module BlockchainLite
@@ -1,10 +1,28 @@
1
- ## $:.unshift(File.dirname(__FILE__))
2
-
3
- ## minitest setup
4
-
5
- require 'minitest/autorun'
6
-
7
-
8
- ## our own code
9
-
10
- require 'blockchain-lite'
1
+ ## $:.unshift(File.dirname(__FILE__))
2
+
3
+ ## minitest setup
4
+
5
+ require 'minitest/autorun'
6
+
7
+
8
+ ## our own code
9
+
10
+ require 'blockchain-lite/base' ## note: use "modular" version without "top-level" Block constant
11
+
12
+
13
+ module Basic
14
+ Block = BlockchainLite::Basic::Block ## convenience shortcut
15
+
16
+ class Blockchain < BlockchainLite::Blockchain
17
+ def block_class() Block; end
18
+ end
19
+ end # module Basic
20
+
21
+
22
+ module ProofOfWork
23
+ Block = BlockchainLite::ProofOfWork::Block ## convenience shortcut
24
+
25
+ class Blockchain < BlockchainLite::Blockchain
26
+ def block_class() Block; end
27
+ end
28
+ end # module ProofOfWork