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.
- 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
|