tierion 0.1.0 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c55375f17cd7d97c88eb2121cae9b144d061a48d
4
- data.tar.gz: bb6d9e026c6efb40e5d41c801cc8d9a9bbafff81
3
+ metadata.gz: 7a6c61dbd6130591ba0ae23a7fa8845a8b2a4c8f
4
+ data.tar.gz: d7023dcf234d38ae0b383eb51ab5480d69427c41
5
5
  SHA512:
6
- metadata.gz: 938e14d5a7fb6e19fbc2edeeab3933dc85ea7c67b1718263134fd3767085ae8e221fccceed4cb39ad62cb40dca85ff8e702b97e3cb3f61a28c4f0876440406e1
7
- data.tar.gz: 36fd4e48acc7e5efa0cfec1535380ba93e82fc052b391940ac9ea6cad90a8b9a81cfd50a7a02b6114119cf67c0ec605f36ba37545f88fcf7d5a08b03a774e738
6
+ metadata.gz: 39443a1eca9208226af6a56587ba863df0428a94f647c6d82dac1cdd122109c8a192fc7fa1034e6fb11f3c72780b8d0e9bc5a0e58774ffd8447516fa3b7d3b29
7
+ data.tar.gz: 1060b935d65af04add356b0679f327cf6ce1175fc6d61cf18f93d4b3c073d4c63fbbd5e97811a81f8e366e97e6c63df8fe418fcc380dfb2bfae5bc28c02a2e30
Binary file
data.tar.gz.sig CHANGED
Binary file
@@ -1,5 +1,11 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## v1.0.0 (8/5/2016)
4
+
5
+ - Refactor Ruby API to better match the Client, HashItem, Receipt, Confirmation hierarchy.
6
+ - Support newly release Chainpoint 2.0 Blockchain receipts
7
+ - Drop support for Chainpoint 1.0
8
+
3
9
  ## v0.1.0 (8/1/2016)
4
10
 
5
11
  This is the initial ALPHA quality release.
data/README.md CHANGED
@@ -25,19 +25,21 @@ Shell commands start with a `$`, Ruby console commands start with `>`.
25
25
  Instantiate a new API client
26
26
 
27
27
  ```
28
- > t = Tierion::Hashitem.new('me@example.com', 'mypassword')
28
+ > t = Tierion::HashApi::Client.new('me@example.com', 'mypassword')
29
29
  ```
30
30
 
31
31
  You can also set the username and password in environment
32
- variables.
32
+ variables...
33
33
 
34
34
  ```
35
35
  $ export TIERION_USERNAME=me@example.com
36
36
  $ export TIERION_PASSWORD=my_pass
37
37
  ```
38
38
 
39
+ ... and call it without hardcoding your credentials.
40
+
39
41
  ```
40
- > t = Tierion::Hashitem.new
42
+ > t = Tierion::HashApi::Client.new
41
43
  ```
42
44
 
43
45
  Create the hash you want to record on the blockchain
@@ -48,93 +50,159 @@ and send it.
48
50
  => "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae">
49
51
 
50
52
  > t.send(my_hash)
51
- => Tierion::HashitemReceipt ...
53
+ => Tierion::HashApi::HashItem ...
52
54
  ```
53
55
 
54
- Examine the array of `HashitemReceipt` objects stored in the
55
- client. This is the only place to find the hash item
56
- ID's. You probably want to store these somewhere in your DB
57
- since this client is ephemeral and there is no way to
58
- retrieve these ID's later.
56
+ Now you can take a look at the Array of `HashItem`s.
59
57
 
60
58
  ```
61
- > t.receipts
62
- => [Tierion::HashitemReceipt..., Tierion::HashitemReceipt ...]
59
+ > t.hash_items
60
+ => [Tierion::HashApi::HashItem ..., Tierion::HashApi::HashItem ...]
63
61
  ```
64
62
 
65
- Get one HashitemReceipt to work on
63
+ Within the `Tierion::HashApi::HashItem` objects is the only place you
64
+ can find the hash item ID's for hashes you've sent. You will probably want
65
+ to store these somewhere in your DB since this client instance is ephemeral
66
+ and there is no way to retrieve these ID's later.
67
+
68
+ Let's grab a single `Tierion::HashApi::HashItem` to work on:
66
69
 
67
70
  ```
68
- > h = t.receipts.first
71
+ > h = t.hash_items.first
69
72
  ```
70
73
 
71
- For convenience you can use `Hashitem#time` to get
72
- the timestamp as a `Time` object (UTC time)
74
+ By default the `HashItem#timestamp` value is an Integer representing
75
+ the seconds since the UNIX epoch. For convenience you can use `HashItem#time` to get the `timestamp` as a Ruby `Time` object (UTC time).
73
76
 
74
77
  ```
78
+ > h.timestamp
79
+ => 1470448435
75
80
  > h.time
76
- => 2016-08-01 17:56:11 UTC
81
+ => 2016-08-06 01:53:55 UTC
77
82
  ```
78
83
 
79
- You can retrieve an individual `BlockchainReceipt` by
80
- passing a `HashitemReceipt` as the arg to `Hashitem#blockchain_receipt`
84
+ You can retrieve an individual `Tierion::HashApi::Receipt`
85
+ for this `HashItem` by passing the `Hashitem` instance as
86
+ an arg to `Client#receipt`
81
87
 
82
88
  ```
83
- > t.blockchain_receipt(h)
84
- => Tierion::BlockchainReceipt ...
89
+ > t.receipt(h)
90
+ => Tierion::HashApi::Receipt ...
85
91
  ```
86
92
 
87
- Or, call `Hashitem#blockchain_receipts` to loop through
88
- each `HashitemReceipt` submitted in this session
89
- and collect `BlockchainReceipts` for each from the
90
- API.
93
+ Or, call `Client#receipts` to loop through each `Hashitem`
94
+ submitted in this session and collect and cache `Receipts`
95
+ for each from the API.
91
96
 
92
- Remember that these are not available until
93
- processed and sent to the blockchain so you may need to
94
- call this again later to populate this Array fully.
97
+ Remember that `Receipt`s are not available until
98
+ processed and sent to the blockchain so you may need
99
+ to call this again to get the `Reciept` for every `HashItem`.
95
100
 
96
101
  ```
97
- > t.blockchain_receipts
98
- => [Tierion::BlockchainReceipt..., Tierion::BlockchainReceipt ...]
102
+ > t.receipts
103
+ => [Tierion::HashApi::Receipt ..., Tierion::HashApi::Receipt ...]
99
104
  ```
100
105
 
101
- Get one `BlockchainReceipt` to work on
106
+ Get one `Tierion::HashApi::Receipt` to work on
102
107
 
103
108
  ```
104
- > b = t.blockchain_receipts.first
105
- => Tierion::BlockchainReceipt ...
109
+ > r = t.receipts.first
110
+ => Tierion::HashApi::Receipt ...
106
111
  ```
107
112
 
108
- A `BlockchainReceipt` object has a number of properties
109
- which are populated from the API.
113
+ A `Tierion::HashApi::Receipt` object has a number of properties
114
+ which are populated from the API. Here is an example:
110
115
 
111
116
  ```
112
- > b.header
113
- > b.target
114
- > b.extra
115
-
116
- # The URL's that are generated for confirming this txn on the blockchain.
117
- > b.confirmation_url
118
- > b.confirmation_url_json
117
+ > r = t.receipts.first
118
+ => {
119
+ "@context"=>"https://w3id.org/chainpoint/v2",
120
+ "type"=>"ChainpointSHA256v2",
121
+ "targetHash"=>"2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
122
+ "merkleRoot"=>"326c0c924a162c8637b8fa392add7c8c98f64f5194f3d2591caf88d7b0b956bd",
123
+ "proof"=>[
124
+ {
125
+ "left"=>"c108bfba805d899faa0ef53b0c064fad650249f6a648fec2bc79fd563106b1f8"
126
+ }
127
+ ],
128
+ "anchors"=>[
129
+ {
130
+ "type"=>"BTCOpReturn",
131
+ "sourceId"=>"579dce214fe0242e3397c9a988622f35b5ee91cef5068b0895a0bbe2c7797b9a"
132
+ }
133
+ ]
134
+ }
119
135
  ```
120
136
 
121
- You can also query whether the transaction associated
122
- with a `BlockchainReceipt` has actually been confirmed on the
123
- blockchain. Once confirmed a data structure from the
124
- third-party `blockchain.info` API will also be populated
125
- with the full transaction data.
137
+ The `Receipt` returns `anchors` which represent one or more trust
138
+ anchors where your hash has been stored. Currently, the only `anchor`
139
+ returned is `BTCOpReturn` which also gives you a `sourceId`
140
+ attribute. This represents the Bitcoin blockchain's OP_RETURN anchor,
141
+ with the `sourceId` representing the BTC transaction ID.
126
142
 
127
- `BlockchainReceipt`s can take quite a while to be confirmed.
143
+ You can also query whether the transaction associated with a `Receipt`
144
+ has actually been confirmed on a trust anchor with a call to
145
+ `Receipt#confirmations`. This will query each supported trust anchor
146
+ to determine whether or not the expected hash can be found. This depends
147
+ on an API call to a different third-party site for each anchor. This call
148
+ will return a hash with the supported trust anchors as the key and a `Boolean` to indicate if the confirmation was successful for that anchor.
128
149
 
150
+ `Receipt`s can take quite a while to be confirmed. Possibly several hours.
151
+ So, be patient.
152
+
153
+ ```
154
+ > r.confirmations
155
+ => {"BTCOpReturn"=>true}
129
156
  ```
130
- > b.confirmed?
131
- => true
132
157
 
133
- # A hash of data from the blockchain.info API
134
- > b.blockchain_info_confirmation
135
- => { ... }
158
+ You can of course also manually confirm that a hash is
159
+ visible at the Transaction ID (`sourceId`) appropriate
160
+ for your trust anchor. For example, for Bitcoin you can
161
+ search for the `sourceId` at a URL like:
162
+
163
+ [https://blockchain.info/tx/579dce214fe0242e3397c9a988622f35b5ee91cef5068b0895a0bbe2c7797b9a](https://blockchain.info/tx/579dce214fe0242e3397c9a988622f35b5ee91cef5068b0895a0bbe2c7797b9a)
164
+
165
+ Or just paste the transaction ID into the search field at [https://blockchain.info](https://blockchain.info).
166
+
167
+ Once you are looking at the transaction info page, you want to
168
+ look for the `OP_RETURN` part of the page, and your hash should be
169
+ there, with a `326c` prefix followed by the 32 byte (64 hex characters)
170
+ hash value you originally submitted. The prefix represents the hex values
171
+ `0x32` and `0x6c` which are the OP_RETURN special code, and the byte length in hex of the OP_RETURN value (your hash).
172
+
173
+ You can get a pretty JSON representation of the `Receipt` by calling
174
+ the `Receipt#to_pretty_json` method.
175
+
176
+ ```
177
+ > r.to_pretty_json
178
+ {
179
+ "@context": "https://w3id.org/chainpoint/v2",
180
+ "type": "ChainpointSHA256v2",
181
+ "targetHash": "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae",
182
+ "merkleRoot": "326c0c924a162c8637b8fa392add7c8c98f64f5194f3d2591caf88d7b0b956bd",
183
+ "proof": [
184
+ {
185
+ "left": "c108bfba805d899faa0ef53b0c064fad650249f6a648fec2bc79fd563106b1f8"
186
+ }
187
+ ],
188
+ "anchors": [
189
+ {
190
+ "type": "BTCOpReturn",
191
+ "sourceId": "579dce214fe0242e3397c9a988622f35b5ee91cef5068b0895a0bbe2c7797b9a"
192
+ }
193
+ ]
194
+ }
136
195
  ```
137
196
 
197
+ You can validate this JSON representation of the receipt by
198
+ submitting it to the [Tierion validation](https://tierion.com/validate)
199
+ web page.
200
+
201
+ ## TODO
202
+
203
+ - Add blockchain receipt subscription functionality.
204
+
205
+
138
206
  ## Development
139
207
 
140
208
  After checking out the repo, run `bin/setup` to install dependencies. Then,
data/Rakefile CHANGED
@@ -5,6 +5,8 @@ Rake::TestTask.new(:test) do |t|
5
5
  t.libs << 'test'
6
6
  t.libs << 'lib'
7
7
  t.test_files = FileList['test/**/*_test.rb']
8
+ t.verbose = false
9
+ t.warning = false
8
10
  end
9
11
 
10
12
  task default: :test
@@ -7,8 +7,4 @@ require 'httparty'
7
7
  require 'hashie'
8
8
 
9
9
  require 'tierion/version'
10
- require 'tierion/hashitem'
11
- require 'tierion/hashitem_receipt'
12
- require 'tierion/blockchain_receipt'
13
- require 'tierion/blockchain_receipt_header'
14
- require 'tierion/blockchain_receipt_target'
10
+ require 'tierion/hash_api'
@@ -0,0 +1,3 @@
1
+ require 'tierion/hash_api/client'
2
+ require 'tierion/hash_api/hash_item'
3
+ require 'tierion/hash_api/receipt'
@@ -0,0 +1,145 @@
1
+ module Tierion
2
+ module HashApi
3
+ class Client
4
+ include ::HTTParty
5
+ base_uri 'https://hashapi.tierion.com/v1'
6
+
7
+ default_timeout 5
8
+ open_timeout 5
9
+ # debug_output $stdout
10
+
11
+ attr_accessor :hash_items
12
+
13
+ def initialize(uname = ENV['TIERION_USERNAME'], pwd = ENV['TIERION_PASSWORD'])
14
+ @auth = { username: uname, password: pwd }
15
+ @access_token = nil
16
+ @expires_at = Time.now.utc - 1
17
+ @refresh_token = nil
18
+ @hash_items = []
19
+ auth
20
+ end
21
+
22
+ def auth
23
+ options = { body: @auth }
24
+ response = self.class.post('/auth/token', options)
25
+
26
+ if response.success?
27
+ extract_auth_tokens(response)
28
+ else
29
+ raise_error(response)
30
+ end
31
+ end
32
+
33
+ def auth_refresh
34
+ if expired_auth?
35
+ options = { body: { 'refreshToken' => @refresh_token } }
36
+ response = self.class.post('/auth/refresh', options)
37
+
38
+ if response.success?
39
+ extract_auth_tokens(response)
40
+ else
41
+ raise_error(response)
42
+ end
43
+ else
44
+ auth
45
+ end
46
+ end
47
+
48
+ def send(hash)
49
+ unless hash =~ /^[a-f0-9]{64}$/
50
+ raise ArgumentError, 'is not a valid SHA256 hex hash string'
51
+ end
52
+
53
+ auth_refresh unless logged_in?
54
+ options = {
55
+ body: { 'hash' => hash },
56
+ headers: { 'Authorization' => "Bearer #{@access_token}" }
57
+ }
58
+ response = self.class.post('/hashitems', options)
59
+
60
+ if response.success?
61
+ parsed = response.parsed_response
62
+ Hashie.symbolize_keys!(parsed)
63
+ parsed.merge!({hash: hash})
64
+ h = Tierion::HashApi::HashItem.new(parsed)
65
+ @hash_items << h
66
+ return h
67
+ else
68
+ raise_error(response)
69
+ end
70
+ end
71
+
72
+ # Get a Receipt for each HashItem that doesn't have one
73
+ # and return the collection of Receipts.
74
+ def receipts
75
+ @hash_items.each do |h|
76
+ next if h.receipt.present?
77
+ h.receipt = receipt(h)
78
+ end
79
+
80
+ @hash_items.collect(&:receipt).compact
81
+ end
82
+
83
+ # Retrieve the receipt for a specific HashItem
84
+ def receipt(h)
85
+ unless h.is_a?(Tierion::HashApi::HashItem)
86
+ raise ArgumentError, 'is not a Tierion::HashApi::HashItem object'
87
+ end
88
+
89
+ auth_refresh unless logged_in?
90
+ options = { headers: { 'Authorization' => "Bearer #{@access_token}" } }
91
+ response = self.class.get("/receipts/#{h.id}", options)
92
+
93
+ if response.success? && response.parsed_response['receipt'].present?
94
+ receipt = JSON.parse(response.parsed_response['receipt'])
95
+ Hashie.symbolize_keys!(receipt)
96
+
97
+ if receipt.key?(:type) || receipt.key?('@type')
98
+ Tierion::HashApi::Receipt.new(receipt)
99
+ else
100
+ raise 'Invalid Receipt found'
101
+ end
102
+ else
103
+ return nil
104
+ end
105
+ end
106
+
107
+ def logged_in?
108
+ @access_token.present? &&
109
+ @refresh_token.present? &&
110
+ @expires_at >= Time.now.utc
111
+ end
112
+
113
+ private
114
+
115
+ def raise_error(response)
116
+ if response['error'].present?
117
+ raise response['error']
118
+ else
119
+ raise 'Unknown Fatal Error'
120
+ end
121
+ end
122
+
123
+
124
+ def expired_auth?
125
+ @access_token.present? &&
126
+ @refresh_token.present? &&
127
+ @expires_at < Time.now.utc
128
+ end
129
+
130
+ def extract_auth_tokens(resp)
131
+ if resp &&
132
+ resp.parsed_response &&
133
+ resp.parsed_response.is_a?(Hash) &&
134
+ resp.parsed_response.key?('access_token') &&
135
+ resp.parsed_response.key?('refresh_token') &&
136
+ resp.parsed_response.key?('expires_in')
137
+ @access_token = resp.parsed_response['access_token']
138
+ @refresh_token = resp.parsed_response['refresh_token']
139
+ @expires_at = Time.now.utc + resp.parsed_response['expires_in']
140
+ return true
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,16 @@
1
+ module Tierion
2
+ module HashApi
3
+ class HashItem < Hashie::Dash
4
+ include Hashie::Extensions::Dash::PropertyTranslation
5
+
6
+ property :hash, required: true
7
+ property :id, from: :receiptId, required: true
8
+ property :timestamp, required: true
9
+ property :receipt, required: false, default: nil
10
+
11
+ def time
12
+ timestamp.is_a?(Integer) ? Time.at(timestamp).utc : nil
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,62 @@
1
+ module Tierion
2
+ module HashApi
3
+ class Receipt < Hash
4
+ include Hashie::Extensions::MergeInitializer
5
+ include Hashie::Extensions::MethodAccess
6
+ include Hashie::Extensions::IndifferentAccess
7
+
8
+ def to_pretty_json
9
+ puts JSON.pretty_generate(self)
10
+ end
11
+
12
+ def confirmations
13
+ get_confirmations
14
+ @confs
15
+ end
16
+
17
+ private
18
+
19
+ def get_confirmations
20
+ @confs = {} if @confs.blank?
21
+
22
+ return {} if anchors.blank?
23
+
24
+ anchors.each do |a|
25
+ # allready confirmed this anchor
26
+ next if @confs[a['type']].is_a?(TrueClass)
27
+
28
+ case a['type']
29
+ when 'BTCOpReturn'
30
+ # txn_id
31
+ if a['sourceId'].present?
32
+ @confs[a['type']] = btc_op_return_confirmed?(a['sourceId'])
33
+ end
34
+ end
35
+ end
36
+
37
+ @confs
38
+ end
39
+
40
+ # Confirm Bitcoin OP_RETURN anchor
41
+ def btc_op_return_confirmed?(source_id)
42
+ url = "https://blockchain.info/tx-index/#{source_id}?format=json"
43
+
44
+ # op_return values begin with 0x6a (op_return code) &
45
+ # 0x20 (length in hex : 32 bytes)
46
+ op_return = ['6a20', merkleRoot].join('')
47
+
48
+ response = HTTParty.get(url)
49
+
50
+ if response.success? && response['out'].present?
51
+ has_op_return = response['out'].any? do |o|
52
+ o['script'].present? && o['script'] == op_return
53
+ end
54
+
55
+ return has_op_return
56
+ else
57
+ false
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -1,3 +1,3 @@
1
1
  module Tierion
2
- VERSION = '0.1.0'
2
+ VERSION = '1.0.0'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tierion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Glenn Rempe
@@ -30,7 +30,7 @@ cert_chain:
30
30
  zieXiXZSAojfFx9g91fKdIrlPbInHU/BaCxXSLBwvOM0drE+c2ue9X8gB55XAhzX
31
31
  37oBiw==
32
32
  -----END CERTIFICATE-----
33
- date: 2016-08-02 00:00:00.000000000 Z
33
+ date: 2016-08-06 00:00:00.000000000 Z
34
34
  dependencies:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: httparty
@@ -154,11 +154,10 @@ files:
154
154
  - certs/gem-public_cert_grempe.pem
155
155
  - exe/tierion
156
156
  - lib/tierion.rb
157
- - lib/tierion/blockchain_receipt.rb
158
- - lib/tierion/blockchain_receipt_header.rb
159
- - lib/tierion/blockchain_receipt_target.rb
160
- - lib/tierion/hashitem.rb
161
- - lib/tierion/hashitem_receipt.rb
157
+ - lib/tierion/hash_api.rb
158
+ - lib/tierion/hash_api/client.rb
159
+ - lib/tierion/hash_api/hash_item.rb
160
+ - lib/tierion/hash_api/receipt.rb
162
161
  - lib/tierion/version.rb
163
162
  - tierion.gemspec
164
163
  homepage: https://github.com/grempe/tierion
metadata.gz.sig CHANGED
Binary file
@@ -1,62 +0,0 @@
1
- module Tierion
2
- class BlockchainReceipt < Hashie::Dash
3
- include Hashie::Extensions::Dash::PropertyTranslation
4
-
5
- property :header, required: true, transform_with: ->(v) {
6
- BlockchainReceiptHeader.new(v)
7
- }
8
-
9
- property :target, required: true, transform_with: ->(v) {
10
- BlockchainReceiptTarget.new(v)
11
- }
12
-
13
- property :extra, required: false, default: []
14
-
15
- property :blockchain_info_confirmation, required: false, default: nil
16
-
17
- # Output clean JSON in a format that works with the Blockchain
18
- # Receipt validator at https://tierion.com/validate
19
- def to_pretty_json
20
- puts JSON.pretty_generate(self)
21
- end
22
-
23
- # Recalculate the merkle tree to ensure the receipt is valid
24
- def valid?
25
- # TODO
26
- end
27
-
28
- # Make an API call to check if the tx_id is a confirmed Transaction
29
- # on the Blockchain and contains the expected OP_RETURN value with
30
- # the merkle_root from this receipt.
31
- def confirmed?
32
- return false if header.blank? || header.tx_id.blank?
33
- return false if header.merkle_root.blank?
34
- response = HTTParty.get(confirmation_url_json)
35
-
36
- if response.success? && response['out'].present?
37
- # op_return values begin with 0x6a (op_return code) &
38
- # 0x20 (hex length in bytes of string)
39
- expected_op_return_value = ['6a20', header.merkle_root].join('')
40
- confirmed = response['out'].any? do |o|
41
- o['script'].present? && o['script'] == expected_op_return_value
42
- end
43
-
44
- # store the parsed output from blockchain.info
45
- self.blockchain_info_confirmation = response.parsed_response if confirmed
46
- return confirmed
47
- else
48
- false
49
- end
50
- end
51
-
52
- def confirmation_url
53
- return nil if header.blank? || header.tx_id.blank?
54
- "https://blockchain.info/tx-index/#{header.tx_id}"
55
- end
56
-
57
- def confirmation_url_json
58
- return nil if header.blank? || header.tx_id.blank?
59
- "#{confirmation_url}?format=json"
60
- end
61
- end
62
- end
@@ -1,11 +0,0 @@
1
- module Tierion
2
- class BlockchainReceiptHeader < Hashie::Dash
3
- include Hashie::Extensions::Dash::PropertyTranslation
4
-
5
- property :chainpoint_version, required: true
6
- property :hash_type, required: true
7
- property :merkle_root, required: true
8
- property :tx_id, required: true
9
- property :timestamp, required: true
10
- end
11
- end
@@ -1,9 +0,0 @@
1
- module Tierion
2
- class BlockchainReceiptTarget < Hashie::Dash
3
- include Hashie::Extensions::Dash::PropertyTranslation
4
-
5
- property :target_hash, required: true
6
- property :target_proof, required: true
7
- property :target_uri, required: false
8
- end
9
- end
@@ -1,138 +0,0 @@
1
- module Tierion
2
- class Hashitem
3
- include ::HTTParty
4
- base_uri 'https://hashapi.tierion.com/v1'
5
-
6
- default_timeout 5
7
- open_timeout 5
8
- # debug_output $stdout
9
-
10
- attr_reader :receipts
11
- attr_accessor :debug
12
-
13
- def initialize(uname = ENV['TIERION_USERNAME'], pwd = ENV['TIERION_PASSWORD'])
14
- @auth = { username: uname, password: pwd }
15
- @access_token = nil
16
- @expires_at = Time.now.utc - 1
17
- @refresh_token = nil
18
- @receipts = []
19
- auth
20
- end
21
-
22
- def auth
23
- options = { body: @auth }
24
- response = self.class.post('/auth/token', options)
25
-
26
- if response.success?
27
- extract_auth_tokens(response)
28
- else
29
- raise_error(response)
30
- end
31
- end
32
-
33
- def auth_refresh
34
- if expired_auth?
35
- options = { body: { 'refreshToken' => @refresh_token } }
36
- response = self.class.post('/auth/refresh', options)
37
-
38
- if response.success?
39
- extract_auth_tokens(response)
40
- else
41
- raise_error(response)
42
- end
43
- else
44
- auth
45
- end
46
- end
47
-
48
- def send(hash)
49
- unless hash =~ /^[a-f0-9]{64}$/
50
- raise ArgumentError, 'is not a valid SHA256 hex hash string'
51
- end
52
-
53
- auth_refresh unless logged_in?
54
- options = {
55
- body: { 'hash' => hash },
56
- headers: { 'Authorization' => "Bearer #{@access_token}" }
57
- }
58
- response = self.class.post('/hashitems', options)
59
-
60
- if response.success?
61
- parsed = response.parsed_response
62
- Hashie.symbolize_keys!(parsed)
63
- hir = HashitemReceipt.new(parsed)
64
- @receipts << hir
65
- return hir
66
- else
67
- raise_error(response)
68
- end
69
- end
70
-
71
- # Retrieve and store the BlockchainReceipt from the API for each
72
- # HashitemReceipt that does not have one.
73
- def blockchain_receipts
74
- @receipts.each do |hir|
75
- next if hir.blockchain_receipt.is_a?(Tierion::BlockchainReceipt)
76
- bcr = blockchain_receipt(hir)
77
- hir.blockchain_receipt = bcr
78
- end
79
-
80
- @receipts.collect(&:blockchain_receipt).compact
81
- end
82
-
83
- # Retrieve the blockchain receipt for a specific HashitemReceipt ID
84
- def blockchain_receipt(hir)
85
- unless hir.is_a?(Tierion::HashitemReceipt)
86
- raise ArgumentError, 'is not a HashitemReceipt object'
87
- end
88
-
89
- auth_refresh unless logged_in?
90
- options = { headers: { 'Authorization' => "Bearer #{@access_token}" } }
91
- response = self.class.get("/receipts/#{hir.id}", options)
92
-
93
- if response.success? && response.parsed_response['receipt'].present?
94
- receipt = JSON.parse(response.parsed_response['receipt'])
95
- Hashie.symbolize_keys!(receipt)
96
- BlockchainReceipt.new(receipt)
97
- else
98
- return nil
99
- end
100
- end
101
-
102
- private
103
-
104
- def raise_error(response)
105
- if response['error'].present?
106
- raise response['error']
107
- else
108
- raise 'Unknown Fatal Error'
109
- end
110
- end
111
-
112
- def logged_in?
113
- @access_token.present? &&
114
- @refresh_token.present? &&
115
- @expires_at >= Time.now.utc
116
- end
117
-
118
- def expired_auth?
119
- @access_token.present? &&
120
- @refresh_token.present? &&
121
- @expires_at < Time.now.utc
122
- end
123
-
124
- def extract_auth_tokens(resp)
125
- if resp &&
126
- resp.parsed_response &&
127
- resp.parsed_response.is_a?(Hash) &&
128
- resp.parsed_response.key?('access_token') &&
129
- resp.parsed_response.key?('refresh_token') &&
130
- resp.parsed_response.key?('expires_in')
131
- @access_token = resp.parsed_response['access_token']
132
- @refresh_token = resp.parsed_response['refresh_token']
133
- @expires_at = Time.now.utc + resp.parsed_response['expires_in']
134
- return true
135
- end
136
- end
137
- end
138
- end
@@ -1,13 +0,0 @@
1
- module Tierion
2
- class HashitemReceipt < Hashie::Dash
3
- include Hashie::Extensions::Dash::PropertyTranslation
4
-
5
- property :id, from: :receiptId, required: true
6
- property :timestamp, required: true
7
- property :blockchain_receipt, required: false, default: nil
8
-
9
- def time
10
- timestamp.is_a?(Integer) ? Time.at(timestamp).utc : nil
11
- end
12
- end
13
- end