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.
- checksums.yaml +5 -5
- data/{HISTORY.md → CHANGELOG.md} +3 -3
- data/Manifest.txt +3 -3
- data/README.md +310 -197
- data/Rakefile +30 -29
- data/lib/blockchain-lite.rb +11 -26
- data/lib/blockchain-lite/base.rb +28 -0
- data/lib/blockchain-lite/basic/block.rb +34 -12
- data/lib/blockchain-lite/blockchain.rb +30 -27
- data/lib/blockchain-lite/proof_of_work/block.rb +53 -20
- data/lib/blockchain-lite/version.rb +23 -23
- data/test/helper.rb +28 -10
- data/test/test_block.rb +93 -35
- data/test/test_block_basic.rb +9 -7
- data/test/test_block_proof_of_work.rb +9 -7
- data/test/test_blockchain.rb +18 -18
- data/test/test_version.rb +21 -0
- metadata +35 -17
- data/LICENSE.md +0 -116
- data/lib/blockchain-lite/block.rb +0 -5
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 =
|
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 = '
|
19
|
-
|
20
|
-
self.extra_deps = [
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
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
|
data/lib/blockchain-lite.rb
CHANGED
@@ -1,26 +1,11 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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 :
|
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,
|
16
|
-
@index
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@
|
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( @
|
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(
|
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,
|
49
|
+
Block.new( 0, transactions, '0' )
|
34
50
|
end
|
35
51
|
|
36
|
-
def self.next( previous,
|
37
|
-
|
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=
|
11
|
-
|
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
|
-
|
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?
|
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
|
40
|
-
|
41
|
-
|
42
|
-
|
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 :
|
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
|
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,
|
17
|
-
@index
|
18
|
-
|
19
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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,
|
37
|
-
|
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 =
|
8
|
-
PATCH =
|
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
|
-
|
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
|
data/test/helper.rb
CHANGED
@@ -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
|