vchain_client 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5331089e365d13a224dc8b586d015a5b38de6929
4
+ data.tar.gz: a6883aff699c11c312a38f66cdf4a7142f606853
5
+ SHA512:
6
+ metadata.gz: d393052fc49844ce44434dfda1a277cf516fca9a6455c907acd73f5c4982069d5f1582f71422b4f66b06637d40f4fb639ce368f8d2212cb3dc23d0d0e95687d9
7
+ data.tar.gz: 0d1dafe6329b2a54c9622ce358b1578488e7b5a11820200a2ccc4a1ef0456df65f98a13f9e910645717c54f7addf0de79940b421f6a8679bd75be6cf7aafb7b9
@@ -0,0 +1,135 @@
1
+ module VChainClient
2
+
3
+ class BitcoindBlockchainAdapter < VChainClient::BlockchainAdapter
4
+
5
+ @server = nil
6
+ @port = nil
7
+ @rpc_username = nil
8
+ @rpc_password = nil
9
+
10
+ def initialize(server, port, rpc_username, rpc_password)
11
+ @server = server
12
+ @port = port
13
+ @rpc_username = rpc_username
14
+ @rpc_password = rpc_password
15
+ end
16
+
17
+ def getTx(txid)
18
+ raw_tx = self.getRawTx(txid)
19
+
20
+ if raw_tx != nil
21
+ if raw_tx.key?("confirmations")
22
+ if raw_tx["confirmations"] > 5
23
+ if raw_tx.key?("blockhash")
24
+ if raw_tx.key?("blocktime")
25
+
26
+ op_return = self.getOpReturn(raw_tx)
27
+
28
+ if op_return != nil
29
+ prefix = op_return[0..2]
30
+ if prefix == "VCH"
31
+ op_return = op_return[3..op_return.length]
32
+
33
+ return {
34
+ "size" => raw_tx["size"],
35
+ "block_hash" => raw_tx["blockhash"],
36
+ "block_timestamp" => raw_tx["blocktime"],
37
+ "op_return" => op_return
38
+ }
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ return nil
49
+ end
50
+
51
+ def getRawTx(txid)
52
+ request = {
53
+ "id" => Random.rand(0...999999),
54
+ "method" => "getrawtransaction",
55
+ "params" => [txid, 1]
56
+ }
57
+
58
+ url = "http://"+ @rpc_username +":"+ @rpc_password +"@"+ @server +":"+ @port +"/"
59
+
60
+ begin
61
+
62
+ req = RestClient.post(url, request.to_json)
63
+
64
+ if req.code != 200
65
+ return nil
66
+ end
67
+
68
+ raw_tx = JSON.parse req.body
69
+
70
+ return raw_tx["result"]
71
+
72
+ rescue => e
73
+ raise e
74
+ end
75
+
76
+ return nil
77
+ end
78
+
79
+ def getOpReturn(raw_tx)
80
+ if raw_tx != nil
81
+ tx_unpacked = {}
82
+ tx_unpacked["vout"] = []
83
+
84
+ if raw_tx.key?("vout")
85
+ if raw_tx["vout"].length > 0
86
+ raw_tx["vout"].each_with_index { |vout,index|
87
+ vout_t = {}
88
+
89
+ vout_t["value"] = 0
90
+
91
+ if vout.key?("value")
92
+ vout_t["value"] = vout["value"]
93
+ end
94
+
95
+ vout_t["scriptPubKey"] = ""
96
+ if vout.key?("scriptPubKey")
97
+ if vout["scriptPubKey"].key?("hex")
98
+ vout_t["scriptPubKey"] = vout["scriptPubKey"]["hex"]
99
+ end
100
+ end
101
+
102
+ tx_unpacked["vout"][index] = vout_t;
103
+ }
104
+
105
+ tx_unpacked["vout"].each { |output|
106
+
107
+ scriptPubKeyArr = output["scriptPubKey"].split()
108
+ scriptPubKeyBinary = scriptPubKeyArr.pack("H*")
109
+
110
+ if scriptPubKeyBinary[0] == "\x6a"
111
+ first_ord = scriptPubKeyBinary[1].ord()
112
+
113
+ if first_ord <= 75
114
+ return scriptPubKeyBinary[2..first_ord+1]
115
+
116
+ elsif first_ord == 0x4c
117
+ return scriptPubKeyBinary[3..scriptPubKeyBinary[2].ord()+1]
118
+
119
+ elsif first_ord == 0x4d
120
+ return scriptPubKeyBinary[4..scriptPubKeyBinary[2].ord()+1+256*scriptPubKeyBinary[3].ord()+1]
121
+ end
122
+ end
123
+
124
+ }
125
+
126
+ end
127
+ end
128
+ end
129
+
130
+ return nil
131
+ end
132
+
133
+ end
134
+
135
+ end
@@ -0,0 +1,11 @@
1
+ module VChainClient
2
+
3
+ class BlockchainAdapter
4
+
5
+ def getTx(txid)
6
+ return nil
7
+ end
8
+
9
+ end
10
+
11
+ end
@@ -0,0 +1,18 @@
1
+ module VChainClient
2
+
3
+ class BlockchainAdapterFactory
4
+
5
+ def self.getAdapter(type, config)
6
+ if type == "bitcoind"
7
+ return VChainClient::BitcoindBlockchainAdapter.new(config["server"], config["port"], config["rpc_username"], config["rpc_password"])
8
+
9
+ elsif type == "blockcypher"
10
+ return VChainClient::BlockcypherBlockchainAdapter.new(config["api_token"])
11
+ end
12
+
13
+ return nil
14
+ end
15
+
16
+ end
17
+
18
+ end
@@ -0,0 +1,85 @@
1
+ module VChainClient
2
+
3
+ class BlockchainConnection
4
+
5
+ @config = nil
6
+
7
+ def initialize(config)
8
+ @config = config
9
+ end
10
+
11
+ def getTx(txid)
12
+
13
+ adapters = []
14
+
15
+ config_adapters = @config["blockchain_adapters"]
16
+
17
+ config_adapters.each { |adapter_config|
18
+ adapter = VChainClient::BlockchainAdapterFactory.getAdapter(adapter_config["type"], adapter_config)
19
+
20
+ if adapter != nil
21
+ adapters.push(adapter)
22
+ end
23
+ }
24
+
25
+ if adapters.length > 0
26
+
27
+ prev_size = nil
28
+ prev_block_hash = nil
29
+ prev_block_timestamp = nil
30
+ prev_op_return = nil
31
+
32
+ adapters.each { |adapter|
33
+
34
+ tx = adapter.getTx(txid)
35
+
36
+ if tx == nil
37
+ return nil
38
+ end
39
+ if !tx.key?("block_hash")
40
+ return nil
41
+ end
42
+ if !tx.key?("block_timestamp")
43
+ return nil
44
+ end
45
+ if tx["block_hash"] == nil || tx["block_hash"] == ""
46
+ return nil
47
+ end
48
+
49
+ size = tx["size"]
50
+ block_hash = tx["block_hash"]
51
+ block_timestamp = tx["block_timestamp"]
52
+ op_return = tx["op_return"]
53
+
54
+ if prev_size != nil && prev_size != size
55
+ return nil
56
+ end
57
+ if prev_block_hash != nil && prev_block_hash != block_hash
58
+ return nil
59
+ end
60
+ if prev_block_timestamp != nil && prev_block_timestamp != block_timestamp
61
+ return nil
62
+ end
63
+ if prev_op_return != nil && prev_op_return != op_return
64
+ return nil
65
+ end
66
+
67
+ prev_size = size
68
+ prev_block_hash = block_hash
69
+ prev_block_timestamp = block_timestamp
70
+ prev_op_return = op_return
71
+ }
72
+
73
+ return {
74
+ "block_hash" => prev_block_hash,
75
+ "block_timestamp" => prev_block_timestamp,
76
+ "op_return" => prev_op_return
77
+ }
78
+ end
79
+
80
+ return nil
81
+ end
82
+
83
+ end
84
+
85
+ end
@@ -0,0 +1,79 @@
1
+ module VChainClient
2
+
3
+ class BlockcypherBlockchainAdapter < VChainClient::BlockchainAdapter
4
+
5
+ @api_token = nil
6
+
7
+ def initialize(api_token)
8
+ @api_token = api_token
9
+ end
10
+
11
+ def getTx(txid)
12
+ url = "https://api.blockcypher.com/v1/btc/main/txs/"
13
+ url += txid
14
+ url += "?token="+ @api_token
15
+ url += "&rand="+ Random.rand(0...999999).to_s
16
+
17
+ req = RestClient.get(url)
18
+
19
+ if req.code != 200
20
+ return nil
21
+ end
22
+
23
+ res = JSON.parse req.body
24
+
25
+ if res != nil
26
+ if res.key?("confirmations")
27
+ if res["confirmations"] > 5
28
+ if res.key?("block_hash")
29
+ if res.key?("confirmed")
30
+ block_timestamp = DateTime.parse(res["confirmed"]).to_i
31
+
32
+ op_return = self.getOpReturn(res)
33
+
34
+ if op_return != nil
35
+ prefix = op_return[0..2]
36
+
37
+ if prefix == "VCH"
38
+ op_return = op_return[3..op_return.length]
39
+
40
+ return {
41
+ "size" => res["size"],
42
+ "block_hash" => res["block_hash"],
43
+ "block_timestamp" => block_timestamp,
44
+ "op_return" => op_return
45
+ }
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ return nil
55
+ end
56
+
57
+ def getOpReturn(raw_tx)
58
+ if raw_tx != nil
59
+ if raw_tx.key?("outputs")
60
+ if raw_tx["outputs"].length > 0
61
+ raw_tx["outputs"].each { |vout|
62
+ if vout.key?("script_type")
63
+ if vout["script_type"] == "null-data"
64
+ if vout.key?("data_string")
65
+ return vout["data_string"]
66
+ end
67
+ end
68
+ end
69
+ }
70
+ end
71
+ end
72
+ end
73
+
74
+ return nil
75
+ end
76
+
77
+ end
78
+
79
+ end
@@ -0,0 +1,155 @@
1
+ module VChainClient
2
+
3
+ class BlockstackClient
4
+
5
+ MASTER_PUBLIC_KEY = "MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEXGOuvJT5hb/bR5y/JADHxJEEaQzuJUzw
6
+ Xet0UYuBrILjHB9HcxFc+WwuCflIRWtRDsNfaY6Ra7j/cRYLeiocYA=="
7
+
8
+ @config = nil
9
+
10
+ @@recs_cache = {}
11
+ @@keys_cache = {}
12
+
13
+ def initialize(config)
14
+ @config = config
15
+ end
16
+
17
+ def checkFederativeServer(federative_server_id)
18
+ return self.checkBlockstackRecord(federative_server_id, 'federative_server')
19
+ end
20
+
21
+ def checkVerificator(verificator_id)
22
+ return self.checkBlockstackRecord(verificator_id, 'verificator')
23
+ end
24
+
25
+ def checkValidator(validator_id)
26
+ return self.checkBlockstackRecord(validator_id, 'validator')
27
+ end
28
+
29
+ def checkBlockstackRecord(blockstack_id, type)
30
+
31
+ record = self.getBlockstackRecord(blockstack_id)
32
+
33
+ signaturesHelper = VChainClient::Signatures.new(@config)
34
+
35
+ if record != nil
36
+ if record.key?("pubkey")
37
+ if record.key?("vchain_role")
38
+
39
+ if record["vchain_role"] != type
40
+ return nil
41
+ end
42
+
43
+ validator_blockstack_id = record["validator_blockstack_id"]
44
+ if validator_blockstack_id != nil
45
+
46
+ if record["vchain_role"] != 'validator'
47
+ if !self.checkValidator(validator_blockstack_id)
48
+ return nil
49
+ end
50
+ end
51
+
52
+ validator_pub_key = nil
53
+ if record["vchain_role"] != 'validator'
54
+ validator_pub_key = self.getPublicKey(validator_blockstack_id)
55
+ else
56
+ validator_pub_key = MASTER_PUBLIC_KEY;
57
+ end
58
+
59
+ # check client's sig
60
+
61
+ client_sig_to_check = record["vchain_id"] + record["vchain_role"] + blockstack_id + record["pubkey"] + record["sig_version"];
62
+
63
+ if signaturesHelper.verifySignature(client_sig_to_check, record["client_sig"], record["pubkey"])
64
+ # check validator's sig
65
+ validator_sig_to_check = record["vchain_id"] + record["vchain_role"] + blockstack_id + record["pubkey"] + record["sig_version"] + record["validator_vchain_id"] + validator_blockstack_id
66
+
67
+ if signaturesHelper.verifySignature(validator_sig_to_check, record["validator_sig"], validator_pub_key)
68
+ return true;
69
+
70
+ else
71
+ return nil
72
+ end
73
+
74
+ else
75
+ return nil
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+
82
+ return nil
83
+ end
84
+
85
+ def getPublicKey(blockstack_id)
86
+ if @@keys_cache.key?(blockstack_id)
87
+ return @@keys_cache[blockstack_id]
88
+ end
89
+
90
+ record = self.getBlockstackRecord(blockstack_id);
91
+ if record != nil
92
+ if record.key?("pubkey")
93
+
94
+ @@keys_cache[blockstack_id] = record["pubkey"]
95
+
96
+ return record["pubkey"]
97
+ end
98
+ end
99
+
100
+ return nil
101
+ end
102
+
103
+ def getBlockstackRecord(blockstack_id)
104
+ if @@recs_cache.key?(blockstack_id)
105
+ return @@recs_cache[blockstack_id]
106
+ end
107
+
108
+ blockstack_path = @config["blockstack"]["path"]
109
+
110
+ cmd = blockstack_path +" get_name_zonefile "+ blockstack_id
111
+
112
+ ret = `#{cmd}`
113
+ if ret == nil || ret == "Name has no user record hash defined"
114
+ return nil
115
+ end
116
+ ret = JSON.parse ret
117
+
118
+ fz = {}
119
+ if ret != nil
120
+ if ret.key?("txt")
121
+ ret["txt"].each { |rec|
122
+ if rec.key?("name") && rec.key?("txt")
123
+ fz[rec["name"]] = rec["txt"]
124
+ end
125
+ }
126
+ end
127
+ end
128
+
129
+ if fz.key?("A1")
130
+ pubkey_aligned = fz["A1"]
131
+ pubkey = pubkey_aligned[0..63] +"\n"+ pubkey_aligned[64..pubkey_aligned.length]
132
+
133
+ output = {
134
+ "pubkey" => pubkey,
135
+ "vchain_id" => fz["A2"],
136
+ "validator_sig" => Base64.decode64(fz["A3"]),
137
+ "validator_vchain_id" => fz["A4"],
138
+ "validator_blockstack_id" => fz["A5"],
139
+ "vchain_role" => fz["A6"],
140
+ "sig_version" => fz["A7"],
141
+ "client_sig" => Base64.decode64(fz["A8"])
142
+ }
143
+
144
+ @@recs_cache[blockstack_id] = output
145
+ @@keys_cache[blockstack_id] = pubkey
146
+
147
+ return output
148
+ end
149
+
150
+ return nil
151
+ end
152
+
153
+ end
154
+
155
+ end
@@ -0,0 +1,103 @@
1
+ module VChainClient
2
+
3
+ class Signatures
4
+
5
+ @config = nil
6
+
7
+ def initialize(config)
8
+ @config = config
9
+ end
10
+
11
+ def signRequest(document, timestamp)
12
+ priv_key_path = @config["private_key_location"]
13
+
14
+ priv_key = File.read(priv_key_path)
15
+
16
+ whole_sign = document.to_json + timestamp.to_s
17
+
18
+ ec = OpenSSL::PKey::EC.new(priv_key)
19
+
20
+ digest = OpenSSL::Digest::SHA256.new
21
+
22
+ whole_signature = ec.sign(digest, whole_sign)
23
+
24
+ return Base64.encode64(whole_signature).gsub(/\n/, "")
25
+ end
26
+
27
+ def verifySignature(what_to_check, signature, public_key)
28
+ pub_key = "-----BEGIN PUBLIC KEY-----\n"
29
+ pub_key += public_key
30
+ pub_key += "\n-----END PUBLIC KEY-----"
31
+
32
+ ec = OpenSSL::PKey::EC.new(pub_key)
33
+
34
+ digest = OpenSSL::Digest::SHA256.new
35
+
36
+ return ec.verify(digest, signature, what_to_check)
37
+ end
38
+
39
+ def signVerification(verification_type, data, timestamp)
40
+
41
+ this_client_id = @config["blockstack"]["client_id"]
42
+ priv_key_path = @config["private_key_location"]
43
+
44
+ priv_key = File.read(priv_key_path)
45
+
46
+ output = {}
47
+
48
+ data.each{ |rec|
49
+ field = rec[0]
50
+ value = rec[1]
51
+
52
+ if field != 'type' && field != 'client_id'
53
+
54
+ field_hash = Digest::SHA512.hexdigest(field)
55
+
56
+ value_hash = Digest::SHA512.hexdigest(value)
57
+
58
+ what_to_sign = field_hash
59
+ what_to_sign += value_hash
60
+ what_to_sign += verification_type
61
+ what_to_sign += timestamp.to_s
62
+ what_to_sign += this_client_id
63
+
64
+ ec = OpenSSL::PKey::EC.new(priv_key)
65
+
66
+ digest = OpenSSL::Digest::SHA256.new
67
+
68
+ signature = ec.sign(digest, what_to_sign)
69
+
70
+ output[field] = Base64.encode64(signature).gsub(/\n/, "")
71
+
72
+ end
73
+ }
74
+
75
+ return output
76
+ end
77
+
78
+ def checkTreeSignature(tree_root_hash, blockchain_txid, blockchain_block_hash, blockchain_timestamp, blockstack_client_id, sig_version, signature, pubkey)
79
+
80
+ what_to_check = tree_root_hash
81
+ what_to_check += blockchain_txid
82
+ what_to_check += blockchain_block_hash
83
+ what_to_check += blockchain_timestamp.to_s
84
+ what_to_check += blockstack_client_id
85
+ what_to_check += sig_version
86
+
87
+ return self.verifySignature(what_to_check, signature, pubkey)
88
+ end
89
+
90
+ def checkVerificationSignature(field_hash, data_hash, verification_type, timestamp, blockstack_client_id, pubkey, signature)
91
+
92
+ what_to_check = field_hash
93
+ what_to_check += data_hash
94
+ what_to_check += verification_type
95
+ what_to_check += timestamp.to_s
96
+ what_to_check += blockstack_client_id
97
+
98
+ return self.verifySignature(what_to_check, signature, pubkey)
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -0,0 +1,302 @@
1
+ module VChainClient
2
+
3
+ class Client
4
+
5
+ require 'rest-client'
6
+ require 'base64'
7
+ gem "openssl"
8
+ require 'openssl'
9
+
10
+ require 'vchain_client/blockchain_adapter'
11
+ require 'vchain_client/bitcoind_blockchain_adapter'
12
+ require 'vchain_client/blockcypher_blockchain_adapter'
13
+ require 'vchain_client/blockchain_adapter_factory'
14
+ require 'vchain_client/blockchain_connection'
15
+ require 'vchain_client/signatures'
16
+ require 'vchain_client/blockstack_client'
17
+
18
+ FIELD_TYPE_HASHED = "fbb6889f44061c2a91e17a411cf168f9457981257a5e0a31fb706cd5cd1e64c263780a42a1fd858ee69429869ab2e2c53b9d94c4a26946f2b0c12f8ce2812d6b"
19
+
20
+ @config = nil
21
+
22
+ def initialize(config)
23
+ @config = config
24
+ end
25
+
26
+ def hash(arr)
27
+ arr.each { |k, v|
28
+ arr[k] = Digest::SHA512.hexdigest(v.downcase)
29
+ }
30
+ end
31
+
32
+ def get_credentials_hash(document)
33
+ if document["type"] == FIELD_TYPE_HASHED
34
+ what_to_hash = document["type"] + document["citizenship"] + document["number"] + document["first_name"] + document["last_name"] + document["birthdate"]
35
+
36
+ return Digest::SHA512.hexdigest(what_to_hash)
37
+ end
38
+
39
+ return nil
40
+ end
41
+
42
+ def build_merkle_tree(tree, timestamp)
43
+ previous_parent = nil
44
+
45
+ tree.each { |tree_level|
46
+ if previous_parent != nil
47
+ if previous_parent != tree_level["left"] && previous_parent != tree_level["right"]
48
+ return nil
49
+ end
50
+ end
51
+
52
+ what_to_hash = tree_level["left"]
53
+ if tree_level["right"] != nil
54
+ what_to_hash += tree_level["right"]
55
+ else
56
+ what_to_hash += tree_level["left"]
57
+ end
58
+ what_to_hash += timestamp.to_s
59
+
60
+ level_hash = Digest::SHA256.hexdigest(what_to_hash);
61
+
62
+ if level_hash != tree_level["parent"]
63
+ return nil
64
+ end
65
+
66
+ previous_parent = level_hash
67
+ }
68
+
69
+ return previous_parent
70
+ end
71
+
72
+ def do_use(input, ignore_possible_matches = false)
73
+
74
+ client_id = @config["client_id"]
75
+ api_url = @config["api"]["url"] + "v0.1/use/";
76
+
77
+ document = input
78
+ document = self.hash(document)
79
+
80
+ document["client_id"] = client_id
81
+ document["ignore_possible_matches"] = ignore_possible_matches
82
+
83
+ req = RestClient.post(api_url,
84
+ document.to_json,
85
+ {'Content-Type' => 'application/json'})
86
+
87
+ if req.code != 200
88
+ return false
89
+ end
90
+
91
+ return true
92
+ end
93
+
94
+ def do_verify(verification_type, input)
95
+
96
+ client_id = @config["client_id"]
97
+ api_url = @config["api"]["url"] + "v0.1/verify/";
98
+
99
+ time = Time.now.getutc
100
+ timestamp = time.to_i
101
+
102
+ document = input
103
+ document = self.hash(document)
104
+
105
+ signaturesHelper = VChainClient::Signatures.new(@config)
106
+
107
+ verifications_data = signaturesHelper.signVerification(verification_type, document, timestamp)
108
+
109
+ document = Hash[document.sort]
110
+
111
+ whole_signature = signaturesHelper.signRequest(document, timestamp)
112
+
113
+ send_data = {}
114
+ send_data["client_id"] = client_id
115
+ send_data["data"] = document
116
+ send_data["timestamp"] = timestamp.to_s
117
+ send_data["verification_type"] = verification_type;
118
+ send_data["verifications_signatures"] = verifications_data
119
+ send_data["signature"] = whole_signature
120
+
121
+ req = RestClient.post(api_url,
122
+ send_data.to_json,
123
+ {'Content-Type' => 'application/json'})
124
+
125
+ if req.code != 200
126
+ return false
127
+ end
128
+
129
+ return true
130
+ end
131
+
132
+ def do_check(input, is_already_hashed=false)
133
+
134
+ client_id = @config["client_id"]
135
+ api_url = @config["api"]["url"] + "v0.1/check/";
136
+
137
+ document = input
138
+
139
+ if (!is_already_hashed)
140
+ document = self.hash(document)
141
+ end
142
+
143
+ document["client_id"] = client_id
144
+
145
+ req = RestClient.post(api_url,
146
+ document.to_json,
147
+ {'Content-Type' => 'application/json'})
148
+
149
+ if req.code != 200
150
+ return false
151
+ end
152
+
153
+ res = JSON.parse req.body
154
+
155
+ if res["status"] == "ERROR" || res["status"] == "error"
156
+
157
+ raise res["error_reason_code"]
158
+
159
+ else
160
+ res_document = res["document"]
161
+
162
+ validated_verifications = {}
163
+
164
+ credentials_hash = self.get_credentials_hash(document)
165
+
166
+ blockchainConnection = VChainClient::BlockchainConnection.new(@config)
167
+
168
+ blockstackClient = VChainClient::BlockstackClient.new(@config)
169
+
170
+ signaturesHelper = VChainClient::Signatures.new(@config)
171
+
172
+ res_document.each { |field, v|
173
+ if v["verifications"].length > 0 && field != "type"
174
+ v["verifications"].each { |verification|
175
+
176
+ next if !verification.key?("blockchain_reciepts")
177
+
178
+ next if verification["blockchain_reciepts"].length <= 0
179
+
180
+ # 1a. check credentials_hash
181
+ next if credentials_hash != verification["credentials_hash"]
182
+
183
+ # 1b. check field_hash
184
+ field_hash = Digest::SHA512.hexdigest(field)
185
+ next if field_hash != verification["field_hash"]
186
+
187
+ # 1c. check data_hash
188
+ data_hash = Digest::SHA512.hexdigest(document[field])
189
+ next if data_hash != verification["data_hash"]
190
+
191
+ # 1d. check checksum
192
+ checksum_to_hash = credentials_hash + field_hash + data_hash
193
+ checksum = Digest::SHA512.hexdigest(checksum_to_hash)
194
+ next if checksum != verification["checksum"]
195
+
196
+ verification_hash = verification["verification_hash"]
197
+
198
+ # 2. check verification_hash to be in target_proof's
199
+ # first element
200
+ reciepts_validated = 0
201
+ verification["blockchain_reciepts"].each { |reciept|
202
+
203
+ break if !reciept.key?("target_proof")
204
+
205
+ break if reciept["target_proof"].length <= 0
206
+
207
+ break if reciept["target_proof"][0]["left"] != verification_hash && reciept["target_proof"][0]["right"] != verification_hash
208
+
209
+ # verification_hash is in tree
210
+ # and in right position
211
+ # now,
212
+
213
+ # 3. check tree convergence to root hash
214
+ # and compare this computed root hash
215
+ # with merkle_tree_root_hash from response
216
+ computed_tree_root_hash = self.build_merkle_tree(reciept["target_proof"], reciept["timestamp"])
217
+
218
+ break if computed_tree_root_hash == nil
219
+ break if computed_tree_root_hash != reciept["merkle_tree_root_hash"]
220
+
221
+ last_proof_index = reciept["target_proof"].length - 1
222
+ reciept_stored_last_parent = reciept["target_proof"][last_proof_index]["parent"]
223
+ break if reciept_stored_last_parent != computed_tree_root_hash
224
+
225
+ # 4. check OP_RETURN in Bitcoin's tx,
226
+ # compare it to computed root hash of a tree
227
+ # retrieve some info from tx to verify signature
228
+ tx = blockchainConnection.getTx(reciept["blockchain_txid"])
229
+
230
+ break if tx == nil
231
+ break if tx["block_hash"] != reciept["blockchain_block_hash"]
232
+ break if tx["block_timestamp"] != reciept["blockchain_timestamp"]
233
+ break if tx["op_return"] != computed_tree_root_hash
234
+
235
+ blockchain_txid = reciept["blockchain_txid"];
236
+ blockchain_block_hash = tx["block_hash"];
237
+ blockchain_timestamp = tx["block_timestamp"];
238
+
239
+ # 5. check tree signature:
240
+ # a) federative server record in Blockstack (recursive)
241
+ # b) tree_signature
242
+
243
+ # a) federative server record in Blockstack (recursive)
244
+ break if !blockstackClient.checkFederativeServer(reciept["federative_server_id"])
245
+
246
+ # b) check tree signature
247
+ federative_server_pubkey = blockstackClient.getPublicKey(reciept["federative_server_id"])
248
+
249
+ break if !signaturesHelper.checkTreeSignature(computed_tree_root_hash, blockchain_txid, blockchain_block_hash, blockchain_timestamp, reciept["federative_server_id"], reciept["federative_server_version"], Base64.decode64(reciept["tree_signature"]), federative_server_pubkey)
250
+
251
+ reciepts_validated += 1
252
+ }
253
+ next if reciepts_validated != verification["blockchain_reciepts"].length
254
+
255
+ # 6. check verification signatures:
256
+ # a) check verificator record in Blockstack (recursive)
257
+ # b) check validator record in Blockstack (recursive)
258
+ # c) verificator_sig
259
+ # d) validtor_sig
260
+
261
+ # a) check verificator record in Blockstack (recursive)
262
+ next if !blockstackClient.checkVerificator(verification["verificator_id"])
263
+
264
+ # b) check validator record in Blockstack (recursive)
265
+ next if !blockstackClient.checkValidator(verification["validator_id"])
266
+
267
+ # c) check verificator's signature
268
+ verificator_pubkey = blockstackClient.getPublicKey(verification["verificator_id"])
269
+
270
+ next if !signaturesHelper.checkVerificationSignature(field_hash, data_hash, verification["type"], verification["timestamp"], verification["verificator_id"], verificator_pubkey, Base64.decode64(verification["verificator_sig"]))
271
+
272
+ # d) check validator's signature
273
+ validator_pubkey = blockstackClient.getPublicKey(verification["validator_id"])
274
+
275
+ next if !signaturesHelper.checkVerificationSignature(field_hash, data_hash, verification["type"], verification["timestamp"], verification["validator_id"], validator_pubkey, Base64.decode64(verification["validator_sig"]))
276
+
277
+ # 7. timestamps checking
278
+ # TODO
279
+ if !validated_verifications.key?(field)
280
+ validated_verifications[field] = 0
281
+ end
282
+ validated_verifications[field] = validated_verifications[field] + 1
283
+ }
284
+ end
285
+ }
286
+
287
+ # check input fields
288
+ input.each_with_index { |field,index|
289
+ if field[0] != 'type' && field[0] != 'client_id'
290
+ if !validated_verifications.key?(field[0])
291
+ validated_verifications[field[0]] = 0
292
+ end
293
+ end
294
+ }
295
+
296
+ return validated_verifications
297
+ end
298
+ end
299
+
300
+ end
301
+
302
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vchain_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Aleksandr Gorelik
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-09-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.8'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.8.3
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.8'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.8.3
33
+ - !ruby/object:Gem::Dependency
34
+ name: rest-client
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 2.0.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '2.0'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 2.0.0
53
+ description: Fully functional client for VChain Platform written on Ruby
54
+ email: alexander@vchain.tech
55
+ executables: []
56
+ extensions: []
57
+ extra_rdoc_files: []
58
+ files:
59
+ - lib/vchain_client.rb
60
+ - lib/vchain_client/bitcoind_blockchain_adapter.rb
61
+ - lib/vchain_client/blockchain_adapter.rb
62
+ - lib/vchain_client/blockchain_adapter_factory.rb
63
+ - lib/vchain_client/blockchain_connection.rb
64
+ - lib/vchain_client/blockcypher_blockchain_adapter.rb
65
+ - lib/vchain_client/blockstack_client.rb
66
+ - lib/vchain_client/signatures.rb
67
+ homepage: http://rubygems.org/gems/vchain_client
68
+ licenses:
69
+ - MIT
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 2.6.6
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: VChain Platform client written on Ruby
91
+ test_files: []