btcruby 1.0.4 → 1.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -0
- data/README.md +12 -0
- data/RELEASE_NOTES.md +5 -0
- data/lib/btcruby/address.rb +1 -1
- data/lib/btcruby/block_header.rb +0 -2
- data/lib/btcruby/merkle_tree.rb +91 -0
- data/lib/btcruby/open_assets/asset_address.rb +2 -2
- data/lib/btcruby/open_assets/issuance_id.rb +0 -2
- data/lib/btcruby/version.rb +1 -1
- data/lib/btcruby.rb +1 -0
- data/spec/merkle_tree_spec.rb +30 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7296b8cbca8113b4795a800e581c220afdbe9b12
|
4
|
+
data.tar.gz: a318bf76d773d9f4bc4955e03bd70f0006bbaf1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef71f62233c862f300abe91608cdc9f100a2ccdcc91f011436f543805fdceb0d8ff795240973a898edb1939c2ceceac43c00329430a0e78914bb97fe6ca5ae94
|
7
|
+
data.tar.gz: de2350b6b17e8c3de1f9e563916bdea830ed23fb0758e49ae1ea30de9d6ae4615fe786015860c1bf707f7a18e6c290eb52695ea77dc6bc63cf34ea546052fd3c
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -52,6 +52,18 @@ $ bundle install
|
|
52
52
|
$ rake
|
53
53
|
```
|
54
54
|
|
55
|
+
## How to publish a gem
|
56
|
+
|
57
|
+
1. Edit version.rb to bump the version.
|
58
|
+
2. Update `RELEASE_NOTES.md`.
|
59
|
+
3. Commit changes and tag it with new version.
|
60
|
+
4. Generate and publish the gem:
|
61
|
+
|
62
|
+
```
|
63
|
+
$ gem build btcruby
|
64
|
+
$ gem push btcruby-<version>.gem
|
65
|
+
```
|
66
|
+
|
55
67
|
## Authors
|
56
68
|
|
57
69
|
* [Oleg Andreev](http://oleganza.com/)
|
data/RELEASE_NOTES.md
CHANGED
data/lib/btcruby/address.rb
CHANGED
data/lib/btcruby/block_header.rb
CHANGED
@@ -0,0 +1,91 @@
|
|
1
|
+
module BTC
|
2
|
+
class MerkleTree
|
3
|
+
|
4
|
+
attr_reader :merkle_root
|
5
|
+
|
6
|
+
def initialize(hashes: nil, transactions: nil, items: nil)
|
7
|
+
raise ArgumentError, "None of the arguments are used" if !transactions && !hashes && !items
|
8
|
+
if transactions
|
9
|
+
hashes = transactions.map{|tx| tx.transaction_hash}
|
10
|
+
elsif items
|
11
|
+
hashes = items.map{|item| BTC.hash256(item) }
|
12
|
+
end
|
13
|
+
raise ArgumentError, "Empty list is not allowed" if hashes.size == 0
|
14
|
+
@hashes = hashes
|
15
|
+
end
|
16
|
+
|
17
|
+
def merkle_root
|
18
|
+
@merkle_root ||= compute_merkle_root
|
19
|
+
end
|
20
|
+
|
21
|
+
def tail_duplicates?
|
22
|
+
if !@merkle_root
|
23
|
+
@merkle_root = compute_merkle_root
|
24
|
+
end
|
25
|
+
@tail_duplicates
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def compute_merkle_root
|
31
|
+
# Based on original Satoshi implementation + vulnerability detection API:
|
32
|
+
# WARNING! If you're reading this because you're learning about crypto
|
33
|
+
# and/or designing a new system that will use merkle trees, keep in mind
|
34
|
+
# that the following merkle tree algorithm has a serious flaw related to
|
35
|
+
# duplicate txids, resulting in a vulnerability (CVE-2012-2459).
|
36
|
+
#
|
37
|
+
# The reason is that if the number of hashes in the list at a given time
|
38
|
+
# is odd, the last one is duplicated before computing the next level (which
|
39
|
+
# is unusual in Merkle trees). This results in certain sequences of
|
40
|
+
# transactions leading to the same merkle root. For example, these two
|
41
|
+
# trees:
|
42
|
+
#
|
43
|
+
# A A
|
44
|
+
# / \ / \
|
45
|
+
# B C B C
|
46
|
+
# / \ | / \ / \
|
47
|
+
# D E F D E F F
|
48
|
+
# / \ / \ / \ / \ / \ / \ / \
|
49
|
+
# 1 2 3 4 5 6 1 2 3 4 5 6 5 6
|
50
|
+
#
|
51
|
+
# for transaction lists [1,2,3,4,5,6] and [1,2,3,4,5,6,5,6] (where 5 and
|
52
|
+
# 6 are repeated) result in the same root hash A (because the hash of both
|
53
|
+
# of (F) and (F,F) is C).
|
54
|
+
#
|
55
|
+
# The vulnerability results from being able to send a block with such a
|
56
|
+
# transaction list, with the same merkle root, and the same block hash as
|
57
|
+
# the original without duplication, resulting in failed validation. If the
|
58
|
+
# receiving node proceeds to mark that block as permanently invalid
|
59
|
+
# however, it will fail to accept further unmodified (and thus potentially
|
60
|
+
# valid) versions of the same block. We defend against this by detecting
|
61
|
+
# the case where we would hash two identical hashes at the end of the list
|
62
|
+
# together, and treating that identically to the block having an invalid
|
63
|
+
# merkle root. Assuming no double-SHA256 collisions, this will detect all
|
64
|
+
# known ways of changing the transactions without affecting the merkle
|
65
|
+
# root.
|
66
|
+
|
67
|
+
@tail_duplicates = false
|
68
|
+
|
69
|
+
tree = @hashes.dup
|
70
|
+
j = 0
|
71
|
+
size = tree.size
|
72
|
+
while size > 1
|
73
|
+
i = 0
|
74
|
+
while i < size
|
75
|
+
i2 = [i + 1, size - 1].min
|
76
|
+
if i2 == i + 1 && i2 + 1 == size && tree[j+i] == tree[j+i2]
|
77
|
+
# Two identical hashes at the end of the list at a particular level.
|
78
|
+
@tail_duplicates = true
|
79
|
+
end
|
80
|
+
hash = BTC.hash256(tree[j+i] + tree[j+i2])
|
81
|
+
tree << hash
|
82
|
+
i += 2
|
83
|
+
end
|
84
|
+
j += size
|
85
|
+
size = (size + 1) / 2
|
86
|
+
end
|
87
|
+
tree.last
|
88
|
+
end
|
89
|
+
|
90
|
+
end # MerkleTree
|
91
|
+
end # BTC
|
@@ -20,7 +20,7 @@ module BTC
|
|
20
20
|
if string
|
21
21
|
_raw_data ||= Base58.data_from_base58check(string)
|
22
22
|
raise FormatError, "Too short AssetAddress" if _raw_data.bytesize < 2
|
23
|
-
raise FormatError, "Invalid namespace for AssetAddress" if _raw_data.bytes[0] !=
|
23
|
+
raise FormatError, "Invalid namespace for AssetAddress" if _raw_data.bytes[0] != self.class.mainnet_version
|
24
24
|
@bitcoin_address = BTC::Address.mux_parse_raw_data(_raw_data[1..-1])
|
25
25
|
@base58check_string = string
|
26
26
|
elsif bitcoin_address
|
@@ -46,7 +46,7 @@ module BTC
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def data_for_base58check_encoding
|
49
|
-
BTC::Data.data_from_bytes([
|
49
|
+
BTC::Data.data_from_bytes([self.class.mainnet_version]) + @bitcoin_address.data_for_base58check_encoding
|
50
50
|
end
|
51
51
|
end
|
52
52
|
end
|
@@ -13,8 +13,6 @@ module BTC
|
|
13
13
|
125 # 's' prefix
|
14
14
|
end
|
15
15
|
|
16
|
-
# Instantiates AssetID with output, output script or raw hash.
|
17
|
-
# To compute an Asset ID for the Asset Definition file, use `trim_script_prefix: true`.
|
18
16
|
def initialize(string: nil, hash: nil, network: nil, outpoint: nil, amount: nil, _raw_data: nil)
|
19
17
|
if outpoint || amount
|
20
18
|
raise ArgumentError, "Outpoint is missing" if !outpoint
|
data/lib/btcruby/version.rb
CHANGED
data/lib/btcruby.rb
CHANGED
@@ -34,6 +34,7 @@ require_relative 'btcruby/transaction_builder.rb'
|
|
34
34
|
require_relative 'btcruby/proof_of_work.rb'
|
35
35
|
require_relative 'btcruby/block_header.rb'
|
36
36
|
require_relative 'btcruby/block.rb'
|
37
|
+
require_relative 'btcruby/merkle_tree.rb'
|
37
38
|
require_relative 'btcruby/open_assets.rb'
|
38
39
|
|
39
40
|
# TODO:
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
describe BTC::MerkleTree do
|
3
|
+
|
4
|
+
it "should compute root of one hash equal to that hash" do
|
5
|
+
hash = "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456"
|
6
|
+
MerkleTree.new(hashes: [hash.from_hex]).merkle_root.to_hex.must_equal hash
|
7
|
+
end
|
8
|
+
|
9
|
+
it "merkle root of 2 hashes must equal hash(a+b)" do
|
10
|
+
a = "9c2e4d8fe97d881430de4e754b4205b9c27ce96715231cffc4337340cb110280".from_hex
|
11
|
+
b = "0c08173828583fc6ecd6ecdbcca7b6939c49c242ad5107e39deb7b0a5996b903".from_hex
|
12
|
+
r = "7de236613dd3d9fa1d86054a84952f1e0df2f130546b394a4d4dd7b76997f607".from_hex
|
13
|
+
r.to_hex.must_equal BTC.hash256(a+b).to_hex
|
14
|
+
mt = MerkleTree.new(hashes: [a,b])
|
15
|
+
mt.tail_duplicates?.must_equal false
|
16
|
+
mt.merkle_root.to_hex.must_equal r.to_hex
|
17
|
+
end
|
18
|
+
|
19
|
+
it "merkle root of 3 hashes must equal Hash(Hash(a+b)+Hash(c+c))" do
|
20
|
+
a = "9c2e4d8fe97d881430de4e754b4205b9c27ce96715231cffc4337340cb110280".from_hex
|
21
|
+
b = "0c08173828583fc6ecd6ecdbcca7b6939c49c242ad5107e39deb7b0a5996b903".from_hex
|
22
|
+
c = "80903da4e6bbdf96e8ff6fc3966b0cfd355c7e860bdd1caa8e4722d9230e40ac".from_hex
|
23
|
+
r = "5b7534123197114fa7e7459075f39d89ffab74b5c3f31fad48a025b931ff5a01".from_hex
|
24
|
+
r.to_hex.must_equal BTC.hash256(BTC.hash256(a+b)+BTC.hash256(c+c)).to_hex
|
25
|
+
mt = MerkleTree.new(hashes: [a,b,c])
|
26
|
+
mt.tail_duplicates?.must_equal false
|
27
|
+
mt.merkle_root.to_hex.must_equal r.to_hex
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: btcruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleg Andreev
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-07-
|
12
|
+
date: 2015-07-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ffi
|
@@ -92,6 +92,7 @@ files:
|
|
92
92
|
- lib/btcruby/hash_id.rb
|
93
93
|
- lib/btcruby/key.rb
|
94
94
|
- lib/btcruby/keychain.rb
|
95
|
+
- lib/btcruby/merkle_tree.rb
|
95
96
|
- lib/btcruby/network.rb
|
96
97
|
- lib/btcruby/opcode.rb
|
97
98
|
- lib/btcruby/open_assets.rb
|
@@ -140,6 +141,7 @@ files:
|
|
140
141
|
- spec/diagnostics_spec.rb
|
141
142
|
- spec/key_spec.rb
|
142
143
|
- spec/keychain_spec.rb
|
144
|
+
- spec/merkle_tree_spec.rb
|
143
145
|
- spec/network_spec.rb
|
144
146
|
- spec/open_assets/asset_address_spec.rb
|
145
147
|
- spec/open_assets/asset_id_spec.rb
|