blockchain-lite 1.1.0 → 1.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e7543889414d4d92f12f6f5bfe9d708ba12c0093
4
- data.tar.gz: 96017ba92283640a77ed3be91d52721f1e245791
3
+ metadata.gz: 7d943d2cc505e7e01dbaf6fb364ca317d55d9563
4
+ data.tar.gz: 4ab4b023a57d9ef9794e6024035c6cf6f718afaa
5
5
  SHA512:
6
- metadata.gz: 9648bcb18e39173799709aef395c346f63ff4fe6fdff824199cc0934b3b2ade710e5ef6e11e1a802d7d435cdb2ee3d905bc4bf28efbcafe64e94c3b154198d10
7
- data.tar.gz: 9b0ca66ac0656b65bc3179387ba7b9818313f94ec7e6d7f25b6ac2cb54ed57a492be7a2a37a13a813a7c6ca9550b7e50c22e3bb0268d8a4c2284d62847079b0d
6
+ metadata.gz: 4d10c42c537839b325d0ab9ebc59bd86238e985f9c549f5119b1b055bd5bf9d70a949ed51dd704082f07974408d40eb887fad1302148819d4fc6f9d63621de76
7
+ data.tar.gz: 612428a641e7e40ffab34c1c33f30a5b0e224e441479c68b551da1581c8e845f31d29624967a9c7915afce4ed6ce6f0aa421ae71f8a67924d37ea23349d55f9b
data/README.md CHANGED
@@ -10,10 +10,10 @@ blockchain-lite library / gem - build your own blockchain with crypto hashes - r
10
10
 
11
11
  ## What's a Blockchain?
12
12
 
13
- > A blockchain is a distributed database -
13
+ > A blockchain is a distributed database with
14
14
  > a list (that is, chain) of records (that is, blocks)
15
15
  > linked and secured by digital fingerprints
16
- > (that is, hashes also known as (one-way) crypto(graphic) hash digest checksums).
16
+ > (that is, crypto hashes).
17
17
 
18
18
  See the [Awesome Blockchains](https://github.com/openblockchains/awesome-blockchains) page for more.
19
19
 
@@ -28,45 +28,51 @@ require 'blockchain-lite'
28
28
 
29
29
  b0 = Block.first( 'Genesis' )
30
30
  b1 = Block.next( b0, 'Transaction Data...' )
31
- b2 = Block.next( b1, 'Transaction Data......' )
32
- b3 = Block.next( b2, 'More Transaction Data...' )
31
+ b2 = Block.next( b1, 'Transaction Data...' )
32
+ b3 = Block.next( b2, 'Transaction Data...' )
33
33
 
34
34
  blockchain = [b0, b1, b2, b3]
35
35
 
36
36
  pp blockchain
37
+ ```
38
+
39
+ will pretty print (pp) something like:
37
40
 
38
- ######
39
- # will pretty print (pp) something like:
40
- #
41
- # [#<Block:0x1eed2a0
42
- # @index = 0,
43
- # @timestamp = 2017-09-15 20:52:38,
44
- # @data = "Genesis",
45
- # @previous_hash = "0",
46
- # @hash ="edbd4e11e69bc399a9ccd8faaea44fb27410fe8e3023bb9462450a0a9c4caa1b">,
47
- # #<Block:0x1eec9a0
48
- # @index = 1,
49
- # @timestamp = 2017-09-15 20:52:38,
50
- # @data = "Transaction Data...",
51
- # @hash = "eb8ecbf6d5870763ae246e37539d82e37052cb32f88bb8c59971f9978e437743",
52
- # @previous_hash = "edbd4e11e69bc399a9ccd8faaea44fb27410fe8e3023bb9462450a0a9c4caa1b">,
53
- # #<Block:0x1eec838
54
- # @index = 2,
55
- # @timestamp = 2017-09-15 20:52:38,
56
- # @data = "Transaction Data......",
57
- # @hash = "be50017ee4bbcb33844b3dc2b7c4e476d46569b5df5762d14ceba9355f0a85f4",
58
- # @previous_hash = "eb8ecbf6d5870763ae246e37539d82e37052cb32f88bb8c59971f9978e437743">,
59
- # #<Block:0x1eec6d0
60
- # @index = 3,
61
- # @timestamp = 2017-09-15 20:52:38
62
- # @data = "More Transaction Data...",
63
- # @hash = "5ee2981606328abfe0c3b1171440f0df746c1e1f8b3b56c351727f7da7ae5d8d",
64
- # @previous_hash = "be50017ee4bbcb33844b3dc2b7c4e476d46569b5df5762d14ceba9355f0a85f4">]
41
+ ```
42
+ [#<Block:0x1eed2a0
43
+ @index = 0,
44
+ @timestamp = 2017-09-15 20:52:38,
45
+ @transactions_count = 1,
46
+ @transactions = ["Genesis"],
47
+ @previous_hash = "0",
48
+ @hash = "edbd4e11e69bc399a9ccd8faaea44fb27410fe8e3023bb9462450a0a9c4caa1b">,
49
+ #<Block:0x1eec9a0
50
+ @index = 1,
51
+ @timestamp = 2017-09-15 20:52:38,
52
+ @transactions_count = 1,
53
+ @transactions = ["Transaction Data..."],
54
+ @hash = "eb8ecbf6d5870763ae246e37539d82e37052cb32f88bb8c59971f9978e437743",
55
+ @previous_hash = "edbd4e11e69bc399a9ccd8faaea44fb27410fe8e3023bb9462450a0a9c4caa1b">,
56
+ #<Block:0x1eec838
57
+ @index = 2,
58
+ @timestamp = 2017-09-15 20:52:38,
59
+ @transactions_count = 1,
60
+ @transactions = ["Transaction Data..."],
61
+ @hash = "be50017ee4bbcb33844b3dc2b7c4e476d46569b5df5762d14ceba9355f0a85f4",
62
+ @previous_hash = "eb8ecbf6d5870763ae246e37539d82e37052cb32f88bb8c59971f9978e437743">,
63
+ #<Block:0x1eec6d0
64
+ @index = 3,
65
+ @timestamp = 2017-09-15 20:52:38
66
+ @transactions_count = 1,
67
+ @transactions = ["Transaction Data..."],
68
+ @hash = "5ee2981606328abfe0c3b1171440f0df746c1e1f8b3b56c351727f7da7ae5d8d",
69
+ @previous_hash = "be50017ee4bbcb33844b3dc2b7c4e476d46569b5df5762d14ceba9355f0a85f4">]
65
70
  ```
66
71
 
72
+
67
73
  ### Blocks
68
74
 
69
- [Basic](#basic)
75
+ [Basic](#basic)
70
76
  [Proof-of-Work](#proof-of-work)
71
77
 
72
78
  Supported block types / classes for now include:
@@ -78,21 +84,27 @@ class Block
78
84
 
79
85
  attr_reader :index
80
86
  attr_reader :timestamp
81
- attr_reader :data
87
+ attr_reader :transactions_count
88
+ attr_reader :transactions
82
89
  attr_reader :previous_hash
83
90
  attr_reader :hash
84
91
 
85
- def initialize(index, data, previous_hash)
86
- @index = index
87
- @timestamp = Time.now.utc ## note: use coordinated universal time (utc)
88
- @data = data
89
- @previous_hash = previous_hash
90
- @hash = calc_hash
92
+ def initialize(index, transactions, previous_hash)
93
+ @index = index
94
+ @timestamp = Time.now.utc ## note: use coordinated universal time (utc)
95
+ @transactions = transactions
96
+ @transactions_count = transactions.size
97
+ @previous_hash = previous_hash
98
+ @hash = calc_hash
91
99
  end
92
100
 
93
101
  def calc_hash
94
102
  sha = Digest::SHA256.new
95
- sha.update( @index.to_s + @timestamp.to_s + @data + @previous_hash )
103
+ sha.update( @index.to_s +
104
+ @timestamp.to_s +
105
+ @transactions.to_s +
106
+ @transactions_count.to_s +
107
+ @previous_hash )
96
108
  sha.hexdigest
97
109
  end
98
110
  ...
@@ -109,22 +121,29 @@ class Block
109
121
 
110
122
  attr_reader :index
111
123
  attr_reader :timestamp
112
- attr_reader :data
124
+ attr_reader :transactions_count
125
+ attr_reader :transactions
113
126
  attr_reader :previous_hash
114
127
  attr_reader :nonce ## proof of work if hash starts with leading zeros (00)
115
128
  attr_reader :hash
116
129
 
117
- def initialize(index, data, previous_hash)
118
- @index = index
119
- @timestamp = Time.now.utc ## note: use coordinated universal time (utc)
120
- @data = data
121
- @previous_hash = previous_hash
122
- @nonce, @hash = compute_hash_with_proof_of_work
130
+ def initialize(index, transactions, previous_hash)
131
+ @index = index
132
+ @timestamp = Time.now.utc ## note: use coordinated universal time (utc)
133
+ @transactions = transactions
134
+ @transactions_count = transactions.size
135
+ @previous_hash = previous_hash
136
+ @nonce, @hash = compute_hash_with_proof_of_work
123
137
  end
124
138
 
125
139
  def calc_hash
126
140
  sha = Digest::SHA256.new
127
- sha.update( @nonce.to_s + @index.to_s + @timestamp.to_s + @data + @previous_hash )
141
+ sha.update( @nonce.to_s +
142
+ @index.to_s +
143
+ @timestamp.to_s +
144
+ @transactions.to_s +
145
+ @transactions_count.to_s +
146
+ @previous_hash )
128
147
  sha.hexdigest
129
148
  end
130
149
  ...
@@ -144,8 +163,8 @@ for building and checking blockchains. Example:
144
163
  b = Blockchain.new # note: will (auto-) add the first (genesis) block
145
164
 
146
165
  b << 'Transaction Data...'
147
- b << 'Transaction Data......'
148
- b << 'More Transaction Data...'
166
+ b << 'Transaction Data...'
167
+ b << 'Transaction Data...'
149
168
 
150
169
  pp b
151
170
  ```
@@ -163,8 +182,8 @@ or use the `Blockchain` class as a wrapper (pass in the blockchain array):
163
182
  ``` ruby
164
183
  b0 = Block.first( 'Genesis' )
165
184
  b1 = Block.next( b0, 'Transaction Data...' )
166
- b2 = Block.next( b1, 'Transaction Data......' )
167
- b3 = Block.next( b2, 'More Transaction Data...' )
185
+ b2 = Block.next( b1, 'Transaction Data...' )
186
+ b3 = Block.next( b2, 'Transaction Data...' )
168
187
 
169
188
  blockchain = [b0, b1, b2, b3]
170
189
 
@@ -178,6 +197,90 @@ b.broken?
178
197
  and so on.
179
198
 
180
199
 
200
+ ### Transactions
201
+
202
+ Let's put the transactions from the (hyper) ledger book from [Tulips on the Blockchain!](https://github.com/openblockchains/tulips)
203
+ on the blockchain:
204
+
205
+
206
+ | From | To | What | Qty |
207
+ |---------------------|--------------|---------------------------|----:|
208
+ | Dutchgrown (†) | Vincent | Tulip Bloemendaal Sunset | 10 |
209
+ | Keukenhof (†) | Anne | Tulip Semper Augustus | 7 |
210
+ | | | | |
211
+ | Flowers (†) | Ruben | Tulip Admiral van Eijck | 5 |
212
+ | Vicent | Anne | Tulip Bloemendaal Sunset | 3 |
213
+ | Anne | Julia | Tulip Semper Augustus | 1 |
214
+ | Julia | Luuk | Tulip Semper Augustus | 1 |
215
+ | | | | |
216
+ | Bloom & Blossom (†) | Daisy | Tulip Admiral of Admirals | 8 |
217
+ | Vincent | Max | Tulip Bloemendaal Sunset | 2 |
218
+ | Anne | Martijn | Tulip Semper Augustus | 2 |
219
+ | Ruben | Julia | Tulip Admiral van Eijck | 2 |
220
+ | | | | |
221
+ | Teleflora (†) | Max | Tulip Red Impression | 11 |
222
+ | Anne | Naomi | Tulip Bloemendaal Sunset | 1 |
223
+ | Daisy | Vincent | Tulip Admiral of Admirals | 3 |
224
+ | Julia | Mina | Tulip Admiral van Eijck | 1 |
225
+
226
+ (†): Grower Transaction - New Tulips on the Market!
227
+
228
+
229
+ ```ruby
230
+ b0 = Block.first(
231
+ { from: "Dutchgrown", to: "Vincent", what: "Tulip Bloemendaal Sunset", qty: 10 },
232
+ { from: "Keukenhof", to: "Anne", what: "Tulip Semper Augustus", qty: 7 } )
233
+
234
+ b1 = Block.next( b0,
235
+ { from: "Flowers", to: "Ruben", what: "Tulip Admiral van Eijck", qty: 5 },
236
+ { from: "Vicent", to: "Anne", what: "Tulip Bloemendaal Sunset", qty: 3 },
237
+ { from: "Anne", to: "Julia", what: "Tulip Semper Augustus", qty: 1 },
238
+ { from: "Julia", to: "Luuk", what: "Tulip Semper Augustus", qty: 1 } )
239
+
240
+ b2 = Block.next( b1,
241
+ { from: "Bloom & Blossom", to: "Daisy", what: "Tulip Admiral of Admirals", qty: 8 },
242
+ { from: "Vincent", to: "Max", what: "Tulip Bloemendaal Sunset", qty: 2 },
243
+ { from: "Anne", to: "Martijn", what: "Tulip Semper Augustus", qty: 2 },
244
+ { from: "Ruben", to: "Julia", what: "Tulip Admiral van Eijck", qty: 2 } )
245
+ ...
246
+ ```
247
+
248
+ resulting in:
249
+
250
+ ```
251
+ [#<Block:0x2da3da0
252
+ @index = 0,
253
+ @timestamp = 1637-09-24 11:40:15,
254
+ @previous_hash = "0",
255
+ @hash = "32bd169baebba0b70491b748329ab631c85175be15e1672f924ca174f628cb66",
256
+ @transactions_count = 2,
257
+ @transactions =
258
+ [{:from=>"Dutchgrown", :to=>"Vincent", :what=>"Tulip Bloemendaal Sunset", :qty=>10},
259
+ {:from=>"Keukenhof", :to=>"Anne", :what=>"Tulip Semper Augustus", :qty=>7}]>,
260
+ #<Block:0x2da2ff0
261
+ @index = 1,
262
+ @timestamp = 1637-09-24 11:50:15,
263
+ @previous_hash = "32bd169baebba0b70491b748329ab631c85175be15e1672f924ca174f628cb66",
264
+ @hash = "57b519a8903e45348ac8a739c788815e2bd90423663957f87e276307f77f1028",
265
+ @transactions_count = 4,
266
+ @transactions =
267
+ [{:from=>"Flowers", :to=>"Ruben", :what=>"Tulip Admiral van Eijck", :qty=>5},
268
+ {:from=>"Vicent", :to=>"Anne", :what=>"Tulip Bloemendaal Sunset", :qty=>3},
269
+ {:from=>"Anne", :to=>"Julia", :what=>"Tulip Semper Augustus", :qty=>1},
270
+ {:from=>"Julia", :to=>"Luuk", :what=>"Tulip Semper Augustus", :qty=>1}]>,
271
+ #<Block:0x2da2720
272
+ @index = 2,
273
+ @timestamp = 1637-09-24 12:00:15,
274
+ @previous_hash = "57b519a8903e45348ac8a739c788815e2bd90423663957f87e276307f77f1028",
275
+ @hash = "ec7dd5ea86ab966d4d4db182abb7aa93c7e5f63857476e6301e7e38cebf36568",
276
+ @transactions_count = 4,
277
+ @transactions =
278
+ [{:from=>"Bloom & Blossom", :to=>"Daisy", :what=>"Tulip Admiral of Admirals", :qty=>8},
279
+ {:from=>"Vincent", :to=>"Max", :what=>"Tulip Bloemendaal Sunset", :qty=>2},
280
+ {:from=>"Anne", :to=>"Martijn", :what=>"Tulip Semper Augustus", :qty=>2},
281
+ {:from=>"Ruben", :to=>"Julia", :what=>"Tulip Admiral van Eijck", :qty=>2}]>,
282
+ ...
283
+ ```
181
284
 
182
285
 
183
286
  ## Install
@@ -8,33 +8,57 @@ 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( @index.to_s +
34
+ @timestamp.to_s +
35
+ @transactions.to_s +
36
+ @transactions_count.to_s +
37
+ @previous_hash )
26
38
  sha.hexdigest
27
39
  end
28
40
 
29
41
 
30
42
 
31
- def self.first( data='Genesis' ) # create genesis (big bang! first) block
43
+ def self.first( *transactions ) # create genesis (big bang! first) block
44
+ ## note: allow/support splat-* for now for convenience (auto-wraps args into array)
45
+ if transactions.size == 1 && transactions[0].is_a?( Array )
46
+ t = transactions[0] ## "unwrap" array in array
47
+ else
48
+ t = transactions ## use "auto-wrapped" splat array
49
+ end
32
50
  ## uses index zero (0) and arbitrary previous_hash ('0')
33
- Block.new( 0, data, '0' )
51
+ Block.new( 0, t, '0' )
34
52
  end
35
53
 
36
- def self.next( previous, data='Transaction Data...' )
37
- Block.new( previous.index+1, data, previous.hash )
54
+ def self.next( previous, *transactions )
55
+ ## note: allow/support splat-* for now for convenience (auto-wraps args into array)
56
+ if transactions.size == 1 && transactions[0].is_a?( Array )
57
+ t = transactions[0] ## "unwrap" array in array
58
+ else
59
+ t = transactions ## use "auto-wrapped" splat array
60
+ end
61
+ Block.new( previous.index+1, t, previous.hash )
38
62
  end
39
63
 
40
64
  end # class Block
@@ -9,35 +9,51 @@ class Blockchain
9
9
 
10
10
  def initialize( chain=nil, block_class: nil )
11
11
  if chain.nil?
12
- @block_class = block_class || BlockchainLite::ProofOfWork::Block
12
+ @block_class = if block_class
13
+ block_class
14
+ else
15
+ ## check if Block is defined
16
+ ## if yes, use it othwerwise fallback for ProofOfWork::Block
17
+ defined?( Block ) ? Block : BlockchainLite::ProofOfWork::Block
18
+ end
19
+
13
20
  b0 = @block_class.first( 'Genesis' )
14
21
  @chain = [b0]
15
22
  else
16
23
  @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
+ @block_class = if block_class
25
+ block_class
26
+ else
27
+ ### no block class configured; use class of first block
28
+ if @chain.first
29
+ @chain.first.class
30
+ else
31
+ ## todo/fix: throw except if chain is empty (no class configured) - why? why not??
32
+ ## throw exception on add block if not a block - why? why not??
33
+ end
34
+ end
24
35
  end
25
36
  end
26
37
 
27
38
 
39
+
28
40
  def last() @chain.last; end ## return last block in chain
29
41
 
30
42
 
43
+ ###
44
+ ## make method-<< abstract/virtual - why? why not?
45
+ ## must be added by to make sure proper block_class is always used - why? why not??
46
+
31
47
  def <<( arg )
32
- if arg.is_a? String ## assume its (just) data
48
+ if arg.is_a? Array ## assume its (just) data
33
49
  data = arg
34
50
  bl = @chain.last
35
51
  b = @block_class.next( bl, data )
36
52
  elsif arg.class.respond_to?( :first ) && ## check if respond_to? Block.first? and Block.next? - assume it's a block
37
53
  arg.class.respond_to?( :next ) ## check/todo: use is_a? @block_class why? why not?
38
54
  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??
55
+ else ## fallback; assume single transaction record; wrap in array - allow fallback - why? why not??
56
+ data = [arg]
41
57
  bl = @chain.last
42
58
  b = @block_class.next( bl, data )
43
59
  end
@@ -8,36 +8,73 @@ 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
- attr_reader :nonce ## proof of work if hash starts with leading zeros (00)
14
+ attr_reader :nonce # ("lucky" number used once) - proof of work if hash starts with leading zeros (00)
14
15
  attr_reader :hash
15
16
 
16
- def initialize(index, data, previous_hash)
17
- @index = index
18
- @timestamp = Time.now.utc ## note: use coordinated universal time (utc)
19
- @data = data
17
+ def initialize(index, transactions, previous_hash, timestamp: nil, nonce: nil)
18
+ @index = index
19
+
20
+ ## note: assumes / expects an array for transactions
21
+ @transactions = transactions
22
+ @transactions_count = transactions.size
23
+
20
24
  @previous_hash = previous_hash
21
- @nonce, @hash = compute_hash_with_proof_of_work
25
+
26
+ ## note: use coordinated universal time (utc)
27
+ @timestamp = timestamp ? timestamp : Time.now.utc
28
+
29
+ if nonce ## restore pre-computed/mined block (from disk/cache/db/etc.)
30
+ ## todo: check timestamp MUST NOT be nil
31
+ @nonce = nonce
32
+ @hash = calc_hash
33
+ else ## new block (mine! e.g. find nonce - "lucky" number used once)
34
+ @nonce, @hash = compute_hash_with_proof_of_work
35
+ end
22
36
  end
23
37
 
24
38
  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
39
+ calc_hash_with_nonce( @nonce )
28
40
  end
29
41
 
30
42
 
31
- def self.first( data='Genesis' ) # create genesis (big bang! first) block
43
+
44
+ def self.first( *transactions, **opts ) # create genesis (big bang! first) block
45
+ ## note: allow/support splat-* for now for convenience (auto-wraps args into array)
46
+ if transactions.size == 1 && transactions[0].is_a?( Array )
47
+ t = transactions[0] ## "unwrap" array in array
48
+ else
49
+ t = transactions ## use "auto-wrapped" splat array
50
+ end
32
51
  ## uses index zero (0) and arbitrary previous_hash ('0')
33
- Block.new( 0, data, '0' )
52
+ ## note: pass along (optional) custom timestamp (e.g. used for 1637 etc.)
53
+ Block.new( 0, t, '0', timestamp: opts[:timestamp] )
34
54
  end
35
55
 
36
- def self.next( previous, data='Transaction Data...' )
37
- Block.new( previous.index+1, data, previous.hash )
56
+ def self.next( previous, *transactions, **opts )
57
+ ## note: allow/support splat-* for now for convenience (auto-wraps args into array)
58
+ if transactions.size == 1 && transactions[0].is_a?( Array )
59
+ t = transactions[0] ## "unwrap" array in array
60
+ else
61
+ t = transactions ## use "auto-wrapped" splat array
62
+ end
63
+ Block.new( previous.index+1, t, previous.hash, timestamp: opts[:timestamp] )
38
64
  end
39
65
 
66
+
40
67
  private
68
+ def calc_hash_with_nonce( nonce=0 )
69
+ sha = Digest::SHA256.new
70
+ sha.update( nonce.to_s +
71
+ @index.to_s +
72
+ @timestamp.to_s +
73
+ @transactions.to_s +
74
+ @transactions_count.to_s +
75
+ @previous_hash )
76
+ sha.hexdigest
77
+ end
41
78
 
42
79
  def compute_hash_with_proof_of_work( difficulty='00' )
43
80
  nonce = 0
@@ -51,12 +88,6 @@ private
51
88
  end
52
89
  end
53
90
 
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
91
  end # class Block
61
92
 
62
93
 
@@ -4,7 +4,7 @@
4
4
  module BlockchainLite
5
5
 
6
6
  MAJOR = 1
7
- MINOR = 1
7
+ MINOR = 2
8
8
  PATCH = 0
9
9
  VERSION = [MAJOR,MINOR,PATCH].join('.')
10
10
 
data/test/test_block.rb CHANGED
@@ -18,14 +18,78 @@ def test_version
18
18
  assert true ## (for now) everything ok if we get here
19
19
  end
20
20
 
21
+
21
22
  def test_example
22
23
 
23
24
  b0 = Block.first( 'Genesis' )
24
25
  b1 = Block.next( b0, 'Transaction Data...' )
25
- b2 = Block.next( b1, 'Transaction Data......' )
26
- b3 = Block.next( b2, 'More Transaction Data...' )
26
+ b2 = Block.next( b1, 'Transaction Data...', 'Transaction Data...' )
27
+ b3 = Block.next( b2 ) ## no transaction data
28
+ b4 = Block.next( b3, ['Transaction Data...', 'Transaction Data...'] )
29
+
30
+ blockchain = [b0, b1, b2, b3, b4]
31
+
32
+ pp blockchain
33
+
34
+ assert true ## (for now) everything ok if we get here
35
+ end
36
+
37
+ def test_tulips_example
38
+ b0 = Block.first(
39
+ { from: "Dutchgrown", to: "Vincent", what: "Tulip Bloemendaal Sunset", qty: 10 },
40
+ { from: "Keukenhof", to: "Anne", what: "Tulip Semper Augustus", qty: 7 } )
41
+
42
+ b1 = Block.next( b0,
43
+ { from: "Flowers", to: "Ruben", what: "Tulip Admiral van Eijck", qty: 5 },
44
+ { from: "Vicent", to: "Anne", what: "Tulip Bloemendaal Sunset", qty: 3 },
45
+ { from: "Anne", to: "Julia", what: "Tulip Semper Augustus", qty: 1 },
46
+ { from: "Julia", to: "Luuk", what: "Tulip Semper Augustus", qty: 1 } )
47
+
48
+ b2 = Block.next( b1,
49
+ { from: "Bloom & Blossom", to: "Daisy", what: "Tulip Admiral of Admirals", qty: 8 },
50
+ { from: "Vincent", to: "Max", what: "Tulip Bloemendaal Sunset", qty: 2 },
51
+ { from: "Anne", to: "Martijn", what: "Tulip Semper Augustus", qty: 2 },
52
+ { from: "Ruben", to: "Julia", what: "Tulip Admiral van Eijck", qty: 2 } )
53
+
54
+ blockchain = [b0, b1, b2]
55
+
56
+ pp blockchain
57
+
58
+ assert true ## (for now) everything ok if we get here
59
+ end
60
+
61
+
62
+ def timestamp1637
63
+ ## change year to 1637 :-)
64
+ ## note: time (uses signed integer e.g. epoch/unix time starting in 1970 with 0)
65
+ ## todo: add nano/mili-seconds - why? why not? possible?
66
+ now = Time.now.utc.to_datetime
67
+ past = DateTime.new( 1637, now.month, now.mday, now.hour, now.min, now.sec, now.zone )
68
+ past
69
+ end
70
+
71
+ def test_tulips_1637_example
72
+
73
+ b0 = Block.first(
74
+ { from: "Dutchgrown", to: "Vincent", what: "Tulip Bloemendaal Sunset", qty: 10 },
75
+ { from: "Keukenhof", to: "Anne", what: "Tulip Semper Augustus", qty: 7 },
76
+ timestamp: timestamp1637 )
77
+
78
+ b1 = Block.next( b0,
79
+ { from: "Flowers", to: "Ruben", what: "Tulip Admiral van Eijck", qty: 5 },
80
+ { from: "Vicent", to: "Anne", what: "Tulip Bloemendaal Sunset", qty: 3 },
81
+ { from: "Anne", to: "Julia", what: "Tulip Semper Augustus", qty: 1 },
82
+ { from: "Julia", to: "Luuk", what: "Tulip Semper Augustus", qty: 1 },
83
+ timestamp: timestamp1637 )
84
+
85
+ b2 = Block.next( b1,
86
+ { from: "Bloom & Blossom", to: "Daisy", what: "Tulip Admiral of Admirals", qty: 8 },
87
+ { from: "Vincent", to: "Max", what: "Tulip Bloemendaal Sunset", qty: 2 },
88
+ { from: "Anne", to: "Martijn", what: "Tulip Semper Augustus", qty: 2 },
89
+ { from: "Ruben", to: "Julia", what: "Tulip Admiral van Eijck", qty: 2 },
90
+ timestamp: timestamp1637 )
27
91
 
28
- blockchain = [b0, b1, b2, b3]
92
+ blockchain = [b0, b1, b2]
29
93
 
30
94
  pp blockchain
31
95
 
@@ -16,10 +16,11 @@ def test_example
16
16
 
17
17
  b0 = block_class.first( 'Genesis' )
18
18
  b1 = block_class.next( b0, 'Transaction Data...' )
19
- b2 = block_class.next( b1, 'Transaction Data......' )
20
- b3 = block_class.next( b2, 'More Transaction Data...' )
19
+ b2 = block_class.next( b1, 'Transaction Data...', 'Transaction Data...' )
20
+ b3 = block_class.next( b2 ) ## no transaction data
21
+ b4 = block_class.next( b3, ['Transaction Data...', 'Transaction Data...'] )
21
22
 
22
- blockchain = [b0, b1, b2, b3]
23
+ blockchain = [b0, b1, b2, b3, b4]
23
24
 
24
25
  pp blockchain
25
26
 
@@ -16,10 +16,11 @@ def test_example
16
16
 
17
17
  b0 = block_class.first( 'Genesis' )
18
18
  b1 = block_class.next( b0, 'Transaction Data...' )
19
- b2 = block_class.next( b1, 'Transaction Data......' )
20
- b3 = block_class.next( b2, 'More Transaction Data...' )
19
+ b2 = block_class.next( b1, 'Transaction Data...', 'Transaction Data...' )
20
+ b3 = block_class.next( b2 ) ## no transaction data
21
+ b4 = block_class.next( b3, ['Transaction Data...', 'Transaction Data...'] )
21
22
 
22
- blockchain = [b0, b1, b2, b3]
23
+ blockchain = [b0, b1, b2, b3, b4]
23
24
 
24
25
  pp blockchain
25
26
 
@@ -14,11 +14,14 @@ def test_new
14
14
  b = Blockchain.new
15
15
 
16
16
  b << 'Transaction Data...'
17
+ b << ['Transaction Data...']
18
+ b << ['Transaction Data...', 'Transaction Data...']
19
+ b << [] ## empty block (no transactions)
17
20
 
18
21
  ## add do-it-yourself built block
19
- b << Block.next( b.last, 'Transaction Data......' )
22
+ b << Block.next( b.last, 'Transaction Data...' )
20
23
 
21
- b << 'More Transaction Data...'
24
+ b << 'Transaction Data...'
22
25
 
23
26
  pp b
24
27
 
@@ -31,8 +34,8 @@ def test_with_block_class
31
34
  b = Blockchain.new( block_class: BlockchainLite::Basic::Block )
32
35
 
33
36
  b << 'Transaction Data...'
34
- b << 'Transaction Data......'
35
- b << 'More Transaction Data...'
37
+ b << 'Transaction Data...'
38
+ b << 'Transaction Data...'
36
39
 
37
40
  pp b
38
41
 
@@ -44,8 +47,8 @@ def test_wrap
44
47
 
45
48
  b0 = Block.first( 'Genesis' )
46
49
  b1 = Block.next( b0, 'Transaction Data...' )
47
- b2 = Block.next( b1, 'Transaction Data......' )
48
- b3 = Block.next( b2, 'More Transaction Data...' )
50
+ b2 = Block.next( b1, 'Transaction Data...' )
51
+ b3 = Block.next( b2, 'Transaction Data...' )
49
52
  blockchain = [b0, b1, b2, b3]
50
53
 
51
54
  b = Blockchain.new( blockchain )
@@ -58,7 +61,7 @@ def test_wrap
58
61
  assert_equal true, b.valid?
59
62
 
60
63
  ## corrupt data in block in chain
61
- b2.instance_eval %{ @data='XXXXXXX' }
64
+ b2.instance_eval %{ @transactions=['XXXXXXX'] }
62
65
 
63
66
  assert_equal true, b.broken?
64
67
  assert_equal false, b.valid?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blockchain-lite
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gerald Bauer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-09-23 00:00:00.000000000 Z
11
+ date: 2017-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc