blockchain-lite 1.1.0 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
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