liability-proof 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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: []