chain-sdk 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +33 -0
- data/lib/chain/access_token.rb +66 -0
- data/lib/chain/account.rb +101 -0
- data/lib/chain/asset.rb +103 -0
- data/lib/chain/balance.rb +36 -0
- data/lib/chain/batch_response.rb +21 -0
- data/lib/chain/client.rb +75 -0
- data/lib/chain/client_module.rb +11 -0
- data/lib/chain/config.rb +121 -0
- data/lib/chain/connection.rb +187 -0
- data/lib/chain/constants.rb +4 -0
- data/lib/chain/control_program.rb +10 -0
- data/lib/chain/errors.rb +80 -0
- data/lib/chain/hsm_signer.rb +91 -0
- data/lib/chain/mock_hsm.rb +65 -0
- data/lib/chain/query.rb +50 -0
- data/lib/chain/response_object.rb +81 -0
- data/lib/chain/transaction.rb +472 -0
- data/lib/chain/transaction_feed.rb +100 -0
- data/lib/chain/unspent_output.rb +90 -0
- data/lib/chain/version.rb +3 -0
- data/lib/chain.rb +3 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f0aa5bb32b142eae9a8ccad181ddfa907e75215b
|
4
|
+
data.tar.gz: dd2f336ad6c531de0cee5bf0ceb525b4b4c3f750
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7a888f2ff7604a2edb0d5ec4713e07e6086a774cad048e529073be20630e0339450b73d3fb238cea744b8cd3c2540282cc8d10e4851880c05668009de3108527
|
7
|
+
data.tar.gz: 7f8c2c45ae8ed6fc39914df1c34f803ab128eea95b36d1c4008497aa8eb43341171a3d28e07d5cc120424248291402a81e523c92850e569fb2e2180caa06620c
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Chain Ruby SDK
|
2
|
+
|
3
|
+
## Usage
|
4
|
+
|
5
|
+
### Installing the library
|
6
|
+
|
7
|
+
#### Via Rubygems
|
8
|
+
|
9
|
+
TBA
|
10
|
+
|
11
|
+
#### Via downloaded .gem
|
12
|
+
|
13
|
+
Install the gem into your gem library:
|
14
|
+
|
15
|
+
```
|
16
|
+
gem install --local chain-sdk-<VERSION>.gem
|
17
|
+
```
|
18
|
+
|
19
|
+
### In your code
|
20
|
+
|
21
|
+
```
|
22
|
+
require 'chain'
|
23
|
+
|
24
|
+
chain = Chain::Client.new
|
25
|
+
```
|
26
|
+
|
27
|
+
## Testing
|
28
|
+
|
29
|
+
To run integration tests, run a configured, empty Chain Core on http://localhost:1999. Then run:
|
30
|
+
|
31
|
+
```
|
32
|
+
bundle exec rspec
|
33
|
+
```
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require_relative './client_module'
|
2
|
+
require_relative './query'
|
3
|
+
require_relative './response_object'
|
4
|
+
|
5
|
+
module Chain
|
6
|
+
class AccessToken < ResponseObject
|
7
|
+
|
8
|
+
# @!attribute [r] id
|
9
|
+
# User specified, unique identifier.
|
10
|
+
# @return [String]
|
11
|
+
attrib :id
|
12
|
+
|
13
|
+
# @!attribute [r] token
|
14
|
+
# Only returned in the response from {ClientModule.create}.
|
15
|
+
# @return [String]
|
16
|
+
attrib :token
|
17
|
+
|
18
|
+
# @!attribute [r] type
|
19
|
+
# Either 'client' or 'network'.
|
20
|
+
# @return [String]
|
21
|
+
attrib :type
|
22
|
+
|
23
|
+
# @!attribute [r] created_at
|
24
|
+
# Timestamp of token creation.
|
25
|
+
# @return [Time]
|
26
|
+
attrib(:created_at) { |raw| Time.parse(raw) }
|
27
|
+
|
28
|
+
class ClientModule < Chain::ClientModule
|
29
|
+
|
30
|
+
# @return [AccessToken]
|
31
|
+
def create(type:, id:)
|
32
|
+
AccessToken.new(client.conn.request(
|
33
|
+
'create-access-token',
|
34
|
+
{type: type, id: id}
|
35
|
+
))
|
36
|
+
end
|
37
|
+
|
38
|
+
# @param [Hash] opts
|
39
|
+
# @return [Query]
|
40
|
+
def query(opts = {})
|
41
|
+
Query.new(client, opts)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Delete the access token specified.
|
45
|
+
# @param [String] id access token ID
|
46
|
+
# @raise [APIError]
|
47
|
+
# @return [void]
|
48
|
+
def delete(id)
|
49
|
+
client.conn.request('delete-access-token', {id: id})
|
50
|
+
return
|
51
|
+
end
|
52
|
+
|
53
|
+
class Query < Chain::Query
|
54
|
+
def fetch(query)
|
55
|
+
client.conn.request('list-access-tokens', query)
|
56
|
+
end
|
57
|
+
|
58
|
+
def translate(raw)
|
59
|
+
AccessToken.new(raw)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require_relative './client_module'
|
2
|
+
require_relative './control_program'
|
3
|
+
require_relative './errors'
|
4
|
+
require_relative './query'
|
5
|
+
require_relative './response_object'
|
6
|
+
|
7
|
+
module Chain
|
8
|
+
class Account < ResponseObject
|
9
|
+
|
10
|
+
# @!attribute [r] id
|
11
|
+
# Unique account identifier.
|
12
|
+
# @return [String]
|
13
|
+
attrib :id
|
14
|
+
|
15
|
+
# @!attribute [r] alias
|
16
|
+
# User specified, unique identifier.
|
17
|
+
# @return [String]
|
18
|
+
attrib :alias
|
19
|
+
|
20
|
+
# @!attribute [r] keys
|
21
|
+
# The list of keys used to create control programs under the account.
|
22
|
+
# Signatures from these keys are required for spending funds held in the account.
|
23
|
+
# @return [Array<Key>]
|
24
|
+
attrib(:keys) { |raw| raw.map { |v| Key.new(v) } }
|
25
|
+
|
26
|
+
# @!attribute [r] quorum
|
27
|
+
# The number of keys required to sign transactions for the account.
|
28
|
+
# @return [Integer]
|
29
|
+
attrib :quorum
|
30
|
+
|
31
|
+
# @!attribute [r] tags
|
32
|
+
# User-specified tag structure for the account.
|
33
|
+
# @return [Hash]
|
34
|
+
attrib :tags
|
35
|
+
|
36
|
+
class ClientModule < Chain::ClientModule
|
37
|
+
# @param [Hash] opts
|
38
|
+
# @return [Account]
|
39
|
+
def create(opts)
|
40
|
+
opts = {client_token: SecureRandom.uuid}.merge(opts)
|
41
|
+
client.conn.singleton_batch_request('create-account', [opts]) { |item| Account.new(item) }
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [Array<Hash>] opts
|
45
|
+
# @return [Array<Account>]
|
46
|
+
def create_batch(opts)
|
47
|
+
opts = opts.map { |i| {client_token: SecureRandom.uuid}.merge(i) }
|
48
|
+
client.conn.batch_request('create-account', opts) { |item| Account.new(item) }
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param [Hash] opts
|
52
|
+
# @return [ControlProgram]
|
53
|
+
def create_control_program(opts = {})
|
54
|
+
# We don't use keyword params here because 'alias' is a Ruby reserverd
|
55
|
+
# word.
|
56
|
+
params = {}
|
57
|
+
params[:account_alias] = opts[:alias] if opts.key?(:alias)
|
58
|
+
params[:account_id] = opts[:id] if opts.key?(:id)
|
59
|
+
|
60
|
+
client.conn.singleton_batch_request(
|
61
|
+
'create-control-program',
|
62
|
+
[{type: :account, params: params}]
|
63
|
+
) { |item| ControlProgram.new(item) }
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param [Hash] query
|
67
|
+
# @return [Query]
|
68
|
+
def query(query = {})
|
69
|
+
Query.new(client, query)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Query < Chain::Query
|
74
|
+
def fetch(query)
|
75
|
+
client.conn.request('list-accounts', query)
|
76
|
+
end
|
77
|
+
|
78
|
+
def translate(raw)
|
79
|
+
Account.new(raw)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
class Key < ResponseObject
|
84
|
+
# @!attribute [r] root_xpub
|
85
|
+
# Hex-encoded representation of the root extended public key.
|
86
|
+
# @return [String]
|
87
|
+
attrib :root_xpub
|
88
|
+
|
89
|
+
# @!attribute [r] account_xpub
|
90
|
+
# The extended public key used to create control programs for the account.
|
91
|
+
# @return [String]
|
92
|
+
attrib :account_xpub
|
93
|
+
|
94
|
+
# @!attribute [r] account_derivation_path
|
95
|
+
# The derivation path of the extended key.
|
96
|
+
# @return [String]
|
97
|
+
attrib :account_derivation_path
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
data/lib/chain/asset.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
require_relative './client_module'
|
4
|
+
require_relative './errors'
|
5
|
+
require_relative './query'
|
6
|
+
require_relative './response_object'
|
7
|
+
|
8
|
+
module Chain
|
9
|
+
class Asset < ResponseObject
|
10
|
+
|
11
|
+
# @!attribute [r] id
|
12
|
+
# Globally unique identifier of the asset.
|
13
|
+
# Asset version 1 specifies the asset id as the hash of:
|
14
|
+
# - the asset version
|
15
|
+
# - the asset's issuance program
|
16
|
+
# - the core's VM version
|
17
|
+
# - the hash of the network's initial block
|
18
|
+
# @return [String]
|
19
|
+
attrib :id
|
20
|
+
|
21
|
+
# @!attribute [r] alias
|
22
|
+
# User specified, unique identifier.
|
23
|
+
# @return [String]
|
24
|
+
attrib :alias
|
25
|
+
|
26
|
+
# @!attribute [r] issuance_program
|
27
|
+
# @return [String]
|
28
|
+
attrib :issuance_program
|
29
|
+
|
30
|
+
# @!attribute [r] keys
|
31
|
+
# @return [Array<Key>]
|
32
|
+
attrib(:keys) { |raw| raw.map { |v| Key.new(v) } }
|
33
|
+
|
34
|
+
# @!attribute [r] quorum
|
35
|
+
# @return [Integer]
|
36
|
+
attrib :quorum
|
37
|
+
|
38
|
+
# @!attribute [r] definition
|
39
|
+
# User-specified, arbitrary/unstructured data visible across
|
40
|
+
# blockchain networks. Version 1 assets specify the definition in their
|
41
|
+
# issuance programs, rendering the definition immutable.
|
42
|
+
# @return [Hash]
|
43
|
+
attrib :definition
|
44
|
+
|
45
|
+
# @!attribute [r] tags
|
46
|
+
# @return [Hash]
|
47
|
+
attrib :tags
|
48
|
+
|
49
|
+
# @!attribute [r] is_local
|
50
|
+
# @return [Boolean]
|
51
|
+
attrib :is_local
|
52
|
+
|
53
|
+
class ClientModule < Chain::ClientModule
|
54
|
+
# @param [Hash] opts
|
55
|
+
# @return [Asset]
|
56
|
+
def create(opts)
|
57
|
+
opts = {client_token: SecureRandom.uuid}.merge(opts)
|
58
|
+
client.conn.singleton_batch_request('create-asset', [opts]) { |item| Asset.new(item) }
|
59
|
+
end
|
60
|
+
|
61
|
+
# @param [Hash] opts
|
62
|
+
# @return [Array<Asset>]
|
63
|
+
def create_batch(opts)
|
64
|
+
opts = opts.map { |i| {client_token: SecureRandom.uuid}.merge(i) }
|
65
|
+
client.conn.batch_request('create-asset', opts) { |item| Asset.new(item) }
|
66
|
+
end
|
67
|
+
|
68
|
+
# @param [Hash] query
|
69
|
+
# @return [Query]
|
70
|
+
def query(query = {})
|
71
|
+
Query.new(client, query)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class Query < Chain::Query
|
76
|
+
def fetch(query)
|
77
|
+
client.conn.request('list-assets', query)
|
78
|
+
end
|
79
|
+
|
80
|
+
def translate(raw)
|
81
|
+
Asset.new(raw)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Key < ResponseObject
|
86
|
+
# @!attribute [r] root_xpub
|
87
|
+
# Hex-encoded representation of the root extended public key.
|
88
|
+
# @return [String]
|
89
|
+
attrib :root_xpub
|
90
|
+
|
91
|
+
# @!attribute [r] asset_pubkey
|
92
|
+
# The derived public key, used in the asset's issuance program.
|
93
|
+
# @return [String]
|
94
|
+
attrib :asset_pubkey
|
95
|
+
|
96
|
+
# @!attribute [r] asset_derivation_path
|
97
|
+
# The derivation path of the extended key.
|
98
|
+
# @return [String]
|
99
|
+
attrib :asset_derivation_path
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require_relative './client_module'
|
2
|
+
require_relative './response_object'
|
3
|
+
require_relative './query'
|
4
|
+
|
5
|
+
module Chain
|
6
|
+
class Balance < ResponseObject
|
7
|
+
|
8
|
+
# @!attribute [r] amount
|
9
|
+
# Sum of the unspent outputs.
|
10
|
+
# @return [Integer]
|
11
|
+
attrib :amount
|
12
|
+
|
13
|
+
# @!attribute [r] sum_by
|
14
|
+
# List of parameters on which to sum unspent outputs.
|
15
|
+
# @return [Hash<String => String>]
|
16
|
+
attrib :sum_by
|
17
|
+
|
18
|
+
class ClientModule < Chain::ClientModule
|
19
|
+
# @param [Hash] query
|
20
|
+
# @return [Query]
|
21
|
+
def query(query = {})
|
22
|
+
Query.new(client, query)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Query < Chain::Query
|
27
|
+
def fetch(query)
|
28
|
+
client.conn.request('list-balances', query)
|
29
|
+
end
|
30
|
+
|
31
|
+
def translate(raw)
|
32
|
+
Balance.new(raw)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
def ensure_key_sorting(h)
|
2
|
+
sorted = h.keys.sort
|
3
|
+
return h if sorted == h.keys
|
4
|
+
sorted.reduce({}) { |memo, k| memo[k] = h[k]; memo }
|
5
|
+
end
|
6
|
+
|
7
|
+
module Chain
|
8
|
+
class BatchResponse
|
9
|
+
def initialize(successes: {}, errors: {}, response: nil)
|
10
|
+
@successes = ensure_key_sorting(successes)
|
11
|
+
@errors = ensure_key_sorting(errors)
|
12
|
+
@response = response
|
13
|
+
end
|
14
|
+
|
15
|
+
def size
|
16
|
+
successes.size + errors.size
|
17
|
+
end
|
18
|
+
|
19
|
+
attr_reader :successes, :errors, :response
|
20
|
+
end
|
21
|
+
end
|
data/lib/chain/client.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require_relative './access_token'
|
2
|
+
require_relative './account'
|
3
|
+
require_relative './asset'
|
4
|
+
require_relative './balance'
|
5
|
+
require_relative './config'
|
6
|
+
require_relative './constants'
|
7
|
+
require_relative './hsm_signer'
|
8
|
+
require_relative './mock_hsm'
|
9
|
+
require_relative './transaction'
|
10
|
+
require_relative './transaction_feed'
|
11
|
+
require_relative './unspent_output'
|
12
|
+
|
13
|
+
module Chain
|
14
|
+
class Client
|
15
|
+
|
16
|
+
def initialize(opts = {})
|
17
|
+
@opts = {url: DEFAULT_API_HOST}.merge(opts)
|
18
|
+
end
|
19
|
+
|
20
|
+
def opts
|
21
|
+
@opts.dup
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Connection]
|
25
|
+
def conn
|
26
|
+
@conn ||= Connection.new(@opts)
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [AccessToken::ClientModule]
|
30
|
+
def access_tokens
|
31
|
+
@access_tokens ||= AccessToken::ClientModule.new(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Account::ClientModule]
|
35
|
+
def accounts
|
36
|
+
@accounts ||= Account::ClientModule.new(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Asset::ClientModule]
|
40
|
+
def assets
|
41
|
+
@assets ||= Asset::ClientModule.new(self)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @return [Balance::ClientModule]
|
45
|
+
def balances
|
46
|
+
@balances ||= Balance::ClientModule.new(self)
|
47
|
+
end
|
48
|
+
|
49
|
+
# @return [Config::ClientModule]
|
50
|
+
def config
|
51
|
+
@config ||= Config::ClientModule.new(self)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @return [MockHSM::ClientModule]
|
55
|
+
def mock_hsm
|
56
|
+
@mock_hsm ||= MockHSM::ClientModule.new(self)
|
57
|
+
end
|
58
|
+
|
59
|
+
# @return [Transaction::ClientModule]
|
60
|
+
def transactions
|
61
|
+
@transactions ||= Transaction::ClientModule.new(self)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [TransactionFeed::ClientModule]
|
65
|
+
def transaction_feeds
|
66
|
+
@transaction_feeds ||= TransactionFeed::ClientModule.new(self)
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [UnspentOutput::ClientModule]
|
70
|
+
def unspent_outputs
|
71
|
+
@unspent_outputs ||= UnspentOutput::ClientModule.new(self)
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
end
|
data/lib/chain/config.rb
ADDED
@@ -0,0 +1,121 @@
|
|
1
|
+
require_relative './client_module'
|
2
|
+
require_relative './response_object'
|
3
|
+
|
4
|
+
module Chain
|
5
|
+
module Config
|
6
|
+
|
7
|
+
class Info < ResponseObject
|
8
|
+
class Snapshot < ResponseObject
|
9
|
+
# @!attribute [r] attempt
|
10
|
+
# @return [Integer]
|
11
|
+
attrib :attempt
|
12
|
+
|
13
|
+
# @!attribute [r] height
|
14
|
+
# @return [Integer]
|
15
|
+
attrib :height
|
16
|
+
|
17
|
+
# @!attribute [r] size
|
18
|
+
# @return [Integer]
|
19
|
+
attrib :size
|
20
|
+
|
21
|
+
# @!attribute [r] downloaded
|
22
|
+
# @return [Integer]
|
23
|
+
attrib :downloaded
|
24
|
+
|
25
|
+
# @!attribute [r] in_progress
|
26
|
+
# @return [Boolean]
|
27
|
+
attrib :in_progress
|
28
|
+
end
|
29
|
+
|
30
|
+
# @!attribute [r] is_configured
|
31
|
+
# @return [Boolean]
|
32
|
+
attrib :is_configured
|
33
|
+
|
34
|
+
# @!attribute [r] configured_at
|
35
|
+
# @return [Time]
|
36
|
+
attrib(:configured_at) { |raw| Time.parse(raw) }
|
37
|
+
|
38
|
+
# @!attribute [r] is_signer
|
39
|
+
# @return [Boolean]
|
40
|
+
attrib :is_signer
|
41
|
+
|
42
|
+
# @!attribute [r] is_generator
|
43
|
+
# @return [Boolean]
|
44
|
+
attrib :is_generator
|
45
|
+
|
46
|
+
# @!attribute [r] is_generator
|
47
|
+
# @return [String]
|
48
|
+
attrib :generator_url
|
49
|
+
|
50
|
+
# @!attribute [r] generator_access_token
|
51
|
+
# @return [String]
|
52
|
+
attrib :generator_access_token
|
53
|
+
|
54
|
+
# @!attribute [r] blockchain_id
|
55
|
+
# @return [String]
|
56
|
+
attrib :blockchain_id
|
57
|
+
|
58
|
+
# @!attribute [r] block_height
|
59
|
+
# @return [Integer]
|
60
|
+
attrib :block_height
|
61
|
+
|
62
|
+
# @!attribute [r] generator_block_height
|
63
|
+
# @return [Integer]
|
64
|
+
attrib :generator_block_height
|
65
|
+
|
66
|
+
# @!attribute [r] generator_block_height_fetched_at
|
67
|
+
# @return [Time]
|
68
|
+
attrib(:generator_block_height_fetched_at) { |raw| Time.parse(raw) }
|
69
|
+
|
70
|
+
# @!attribute [r] is_production
|
71
|
+
# @return [Boolean]
|
72
|
+
attrib :is_production
|
73
|
+
|
74
|
+
# @!attribute [r] network_rpc_version
|
75
|
+
# @return [Integer]
|
76
|
+
attrib :network_rpc_version
|
77
|
+
|
78
|
+
# @!attribute [r] core_id
|
79
|
+
# @return [String]
|
80
|
+
attrib :core_id
|
81
|
+
|
82
|
+
# @!attribute [r] build_commit
|
83
|
+
# @return [String]
|
84
|
+
attrib :build_commit
|
85
|
+
|
86
|
+
# @!attribute [r] build_date
|
87
|
+
# Date when the core binary was compiled.
|
88
|
+
#
|
89
|
+
# The API may not return this field as an RFC3399 timestamp,
|
90
|
+
# so it is not converted into a Time object.
|
91
|
+
# @return [String]
|
92
|
+
attrib :build_date
|
93
|
+
|
94
|
+
# @!attribute [r] health
|
95
|
+
# @return [Hash]
|
96
|
+
attrib :health
|
97
|
+
|
98
|
+
# @!attribute [r] snapshot
|
99
|
+
# @return [Snapshot]
|
100
|
+
attrib(:snapshot) { |raw| Snapshot.new(raw) }
|
101
|
+
end
|
102
|
+
|
103
|
+
class ClientModule < Chain::ClientModule
|
104
|
+
# @return [void]
|
105
|
+
def reset(everything: false)
|
106
|
+
client.conn.request('reset', {everything: everything})
|
107
|
+
end
|
108
|
+
|
109
|
+
# @return [void]
|
110
|
+
def configure(opts)
|
111
|
+
client.conn.request('configure', opts)
|
112
|
+
end
|
113
|
+
|
114
|
+
# @return [Info]
|
115
|
+
def info
|
116
|
+
Info.new(client.conn.request('info'))
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|