vchain_client 1.0.0

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: 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: []