liability-proof 0.0.3

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 977147c8250bb363215bad2fd155bc13aec6269f
4
+ data.tar.gz: c84794a53390071cb9800bf692eff4c0e2bf2b0d
5
+ SHA512:
6
+ metadata.gz: 662738943559c4e770f91d11de77a39af291418c775b8a0b7a774aa710bf5d51e3f5a60e26a887c4486676c815df9eb1615314391ce65e2d82861eac84dbae89
7
+ data.tar.gz: e5e98a75e127860b41ed67cc102a8bc370c0ffdea3b6960daf59b9599ea346e7e5fec3311307d8ec2a7d6a9683949f6ea64f9d0a3104bb75f1cfd19035e2f395
data/README.markdown ADDED
@@ -0,0 +1,44 @@
1
+ Liability Proof
2
+ ===============
3
+
4
+ If you're not familiar with *liability proof* or *the Merkle approach*, check this page: [Proving Your Bitcoin Reserves](https://iwilcox.me.uk/2014/proving-bitcoin-reserves). Basically, every mordern exchanges should prove they really hold the bitcoins/money they claim to.
5
+
6
+ ### Requirements ###
7
+
8
+ * ruby 2.0.0 or higher (if you want to run 'rake test' in this gem you'll need ruby 2.1.0 or higher)
9
+ * openssl
10
+
11
+ ### Install ###
12
+
13
+ gem install liability-proof
14
+
15
+ ### Usage ###
16
+
17
+ As command line tool:
18
+
19
+ # Generate root.json and partial tree json for each account in accounts.json.
20
+ # The generated file format conforms with the standard in progress:
21
+ # https://github.com/olalonde/blind-liability-proof#serialized-data-formats-work-in-progress--draft
22
+ lproof generate -f accounts.json
23
+
24
+ # verify specified partial tree is valid, i.e. the root node calculated from
25
+ # from the partial tree matches the root node in root.json
26
+ lproof verify -r root.json -f partial_trees/jan.json
27
+
28
+ As library: check `LiabilityProof::Generator` and `LiabilityProof::Verifier` for example.
29
+
30
+ ### License ###
31
+
32
+ LiabilityProof is a ruby gem released under MIT license. See [http://peatio.mit-license.org](http://peatio.mit-license.org) for more information.
33
+
34
+ ### How To Contribute ###
35
+
36
+ Just create an issue or open a pull request :)
37
+
38
+ ### Other implementations ###
39
+
40
+ * clojure: https://github.com/zw/PoLtree/blob/master/src/uk/me/iwilcox/poltree.clj
41
+ * node.js: http://olalonde.github.io/blind-liability-proof
42
+ * c++: https://github.com/bifubao/proof_of_reserves
43
+ * python: https://github.com/ConceptPending/proveit
44
+
data/bin/lproof ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'liability-proof'
4
+ require 'optparse'
5
+
6
+ options = {}
7
+
8
+ opt_parser = OptionParser.new do |opt|
9
+ opt.banner = "Usage: #{$0} COMMAND [OPTIONS]"
10
+ opt.separator ""
11
+ opt.separator "Commands"
12
+ opt.separator " generate: generate json of root node and partial trees"
13
+ opt.separator " verify: verify specified partial tree is valid (matches root node)"
14
+ opt.separator ""
15
+ opt.separator "Options"
16
+
17
+ opt.on("-f", "--file source.json", "Specify source file. For generate command, it should contain accounts data; for verify command, it should contain the partial tree.") do |f|
18
+ options[:file] = f
19
+ end
20
+
21
+ opt.on("-r", "--root root.json", "Specify root node data file. For generate command it's the destination to write data; for verify command it's the source to read data.") do |f|
22
+ options[:root] = f
23
+ end
24
+
25
+ options[:partial_trees_dir] = 'partial_trees'
26
+ opt.on("--partial-trees-dir DIR", "Specify the directory to store generated partial tree json files, default to 'partial_trees'.") do |d|
27
+ options[:partial_trees_dir] = d
28
+ end
29
+ end
30
+
31
+ opt_parser.parse!
32
+
33
+ case ARGV[0]
34
+ when 'generate'
35
+ LiabilityProof::Generator.new(options).write!
36
+ when 'verify'
37
+ LiabilityProof::Verifier.new(options).verify!
38
+ else
39
+ puts opt_parser
40
+ end
@@ -0,0 +1,13 @@
1
+ module LiabilityProof
2
+
3
+ autoload :Tree, 'liability-proof/tree'
4
+ autoload :Generator, 'liability-proof/generator'
5
+ autoload :Verifier, 'liability-proof/verifier'
6
+
7
+ module_function
8
+
9
+ def sha256_base64(message)
10
+ Base64.encode64(OpenSSL::Digest::SHA256.new.digest(message)).strip
11
+ end
12
+
13
+ end
@@ -0,0 +1,48 @@
1
+ require 'json'
2
+ require 'fileutils'
3
+
4
+ module LiabilityProof
5
+ class Generator
6
+
7
+ def initialize(options)
8
+ @accounts_path = options.delete(:file) || 'accounts.json'
9
+ @root_path = options.delete(:root) || 'root.json'
10
+
11
+ @partial_trees_dir = options.delete(:partial_trees_dir) || 'partial_trees'
12
+ FileUtils.mkdir @partial_trees_dir unless File.exists?(@partial_trees_dir)
13
+
14
+ accounts = JSON.parse File.read(@accounts_path)
15
+ @tree = LiabilityProof::Tree.new accounts
16
+ end
17
+
18
+ def root_json
19
+ { 'root' => {
20
+ 'hash' => @tree.root.hash,
21
+ 'value' => @tree.root.value_string }}
22
+ end
23
+
24
+ def partial_tree_json(user)
25
+ { 'partial_tree' => @tree.partial(user) }
26
+ end
27
+
28
+ def write!
29
+ write_root_json
30
+ @tree.indices.keys.each {|user| write_partial_tree(user) }
31
+ end
32
+
33
+ private
34
+
35
+ def write_root_json
36
+ File.open(@root_path, 'w') do |f|
37
+ f.write JSON.dump(root_json)
38
+ end
39
+ end
40
+
41
+ def write_partial_tree(user)
42
+ File.open(File.join(@partial_trees_dir, "#{user}.json"), 'w') do |f|
43
+ f.write JSON.dump(partial_tree_json(user))
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,82 @@
1
+ require 'base64'
2
+ require 'bigdecimal'
3
+ require 'openssl'
4
+
5
+ module LiabilityProof
6
+ class Tree
7
+
8
+ autoload :Node, 'liability-proof/tree/node'
9
+ autoload :LeafNode, 'liability-proof/tree/leaf_node'
10
+ autoload :InteriorNode, 'liability-proof/tree/interior_node'
11
+
12
+ attr :root, :indices
13
+
14
+ def initialize(accounts)
15
+ raise ArgumentError, 'accounts is empty' unless accounts && accounts.size > 0
16
+
17
+ @accounts = accounts
18
+ @root = generate
19
+ @indices = Hash[index_leaves(@root)]
20
+ end
21
+
22
+ def partial(user)
23
+ h = { 'data' => nil }
24
+ _partial user, @root, @indices[user].dup, h
25
+ h
26
+ end
27
+
28
+ private
29
+
30
+ def _partial(user, node, index, acc)
31
+ if node.is_a?(LeafNode)
32
+ acc['data'] = node.as_json
33
+
34
+ if node.user == user
35
+ acc['data'].merge!({
36
+ 'user' => user,
37
+ 'nonce' => node.nonce
38
+ })
39
+ end
40
+ else
41
+ follow_direction = index.shift
42
+ other_direction = follow_direction == :left ? :right : :left
43
+ follow_child = node.send follow_direction
44
+ other_child = node.send other_direction
45
+
46
+ acc[other_direction.to_s] = { 'data' => other_child.as_json }
47
+ acc[follow_direction.to_s] = { 'data' => nil }
48
+ _partial user, follow_child, index, acc[follow_direction.to_s]
49
+ end
50
+ end
51
+
52
+ def generate
53
+ leaves = @accounts.map {|a| LeafNode.new(a) }
54
+ combine leaves
55
+ end
56
+
57
+ def combine(nodes)
58
+ return nodes.first if nodes.size <= 1
59
+
60
+ parents = nodes.each_slice(2).map do |(left, right)|
61
+ # if right is not nil, return combined interior node;
62
+ # otherwise keep the left leaf node
63
+ right ? InteriorNode.new(left, right) : left
64
+ end
65
+
66
+ combine parents
67
+ end
68
+
69
+ # Walk the tree and produce indices, each index include the destination
70
+ # leaf and the path from given node to it.
71
+ #
72
+ # The path is expressed as an array of directions, e.g. :left, :right
73
+ def index_leaves(node, index=[])
74
+ if node.is_a?(LeafNode)
75
+ [[node.user, index]]
76
+ else
77
+ index_leaves(node.left, index+[:left]) + index_leaves(node.right, index+[:right])
78
+ end
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,23 @@
1
+ module LiabilityProof
2
+ class Tree
3
+
4
+ class InteriorNode < Struct.new(:left, :right, :value, :hash)
5
+ include ::LiabilityProof::Tree::Node
6
+
7
+ def initialize(left, right)
8
+ super(left, right)
9
+
10
+ self.value = left.value + right.value
11
+ self.hash = generate_hash
12
+ end
13
+
14
+ private
15
+
16
+ def generate_hash
17
+ LiabilityProof.sha256_base64 "#{value_string}#{left.hash}#{right.hash}"
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ module LiabilityProof
2
+ class Tree
3
+
4
+ class LeafNode < Struct.new(:user, :value, :nonce, :hash)
5
+ include ::LiabilityProof::Tree::Node
6
+
7
+ def initialize(account)
8
+ value = BigDecimal.new(account['value'] || account['balance'])
9
+ super(account['user'], value)
10
+
11
+ self.nonce = account['nonce'] || generate_nonce
12
+ self.hash = account['hash'] || generate_hash
13
+
14
+ if account['user'] && account['hash'] && account['nonce']
15
+ raise ArgumentError, "Hash doesn't match" if generate_hash != account['hash']
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ # a 16 bytes random string encoded in 32 hex digits
22
+ def generate_nonce
23
+ OpenSSL::Random.random_bytes(16).unpack("H*").first
24
+ end
25
+
26
+ # a sha256 hash encoded in 64 hex digits
27
+ def generate_hash
28
+ LiabilityProof.sha256_base64 "#{user}|#{value_string}|#{nonce}"
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,17 @@
1
+ module LiabilityProof
2
+ class Tree
3
+
4
+ module Node
5
+
6
+ def as_json
7
+ { 'value' => value_string, 'hash' => hash }
8
+ end
9
+
10
+ def value_string
11
+ value.to_s('F')
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,56 @@
1
+ require 'json'
2
+
3
+ module LiabilityProof
4
+ class Verifier
5
+
6
+ def initialize(options)
7
+ @root_path = options.delete(:root) || 'root.json'
8
+ @partial_tree_path = options.delete(:file)
9
+ end
10
+
11
+ def root_json
12
+ @root_json ||= JSON.parse File.read(@root_path)
13
+ end
14
+
15
+ def partial_tree_json
16
+ @partial_tree_json ||= JSON.parse File.read(@partial_tree_path)
17
+ end
18
+
19
+ def expect_root
20
+ root_json['root']
21
+ end
22
+
23
+ def match?
24
+ partial_tree = partial_tree_json['partial_tree']
25
+ reduce(partial_tree).as_json == expect_root
26
+ end
27
+
28
+ def verify!
29
+ if match?
30
+ puts "Partial tree verified successfully!\n\n"
31
+ puts "User: #{@user_node.user}"
32
+ puts "Balance: #{@user_node.value_string}"
33
+ else
34
+ raise "Mismatch! Expected root: #{expect_root.inspect}"
35
+ end
36
+ rescue
37
+ puts "INVALID partial tree!"
38
+ puts "ERROR: #{$!}"
39
+ end
40
+
41
+ private
42
+
43
+ def reduce(node)
44
+ if node['data']
45
+ leaf = Tree::LeafNode.new node['data']
46
+ @user_node = leaf if leaf.user
47
+ leaf
48
+ else
49
+ left = reduce node['left']
50
+ right = reduce node['right']
51
+ Tree::InteriorNode.new(left, right)
52
+ end
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ module LiabilityProof
2
+ VERSION = '0.0.3'
3
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: liability-proof
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Peatio Opensource
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-03-17 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Check https://iwilcox.me.uk/2014/proving-bitcoin-reserves for more details.
14
+ email:
15
+ - community@peatio.com
16
+ executables:
17
+ - lproof
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - README.markdown
22
+ - bin/lproof
23
+ - lib/liability-proof.rb
24
+ - lib/liability-proof/generator.rb
25
+ - lib/liability-proof/tree.rb
26
+ - lib/liability-proof/tree/interior_node.rb
27
+ - lib/liability-proof/tree/leaf_node.rb
28
+ - lib/liability-proof/tree/node.rb
29
+ - lib/liability-proof/verifier.rb
30
+ - lib/liability-proof/version.rb
31
+ homepage: https://github.com/peatio/liability-proof
32
+ licenses:
33
+ - MIT
34
+ metadata: {}
35
+ post_install_message:
36
+ rdoc_options: []
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ requirements: []
50
+ rubyforge_project:
51
+ rubygems_version: 2.2.0
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: An implementation of Greg Maxwell's Merkle approach to prove Bitcoin liabilities.
55
+ test_files: []