quorum_sdk 0.1.0 → 0.3.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 +4 -4
- data/Gemfile.lock +21 -23
- data/README.md +40 -17
- data/lib/proto/chain.proto +169 -234
- data/lib/proto/chain_pb.rb +123 -193
- data/lib/quorum_sdk/account.rb +0 -2
- data/lib/quorum_sdk/chain/chain.rb +18 -0
- data/lib/quorum_sdk/chain/group.rb +69 -0
- data/lib/quorum_sdk/chain/management.rb +143 -0
- data/lib/quorum_sdk/chain/node.rb +18 -0
- data/lib/quorum_sdk/chain.rb +26 -0
- data/lib/quorum_sdk/client.rb +23 -10
- data/lib/quorum_sdk/node/announce.rb +52 -0
- data/lib/quorum_sdk/node/app_config.rb +25 -0
- data/lib/quorum_sdk/node/auth.rb +32 -0
- data/lib/quorum_sdk/node/group.rb +32 -0
- data/lib/quorum_sdk/node/trx.rb +78 -0
- data/lib/quorum_sdk/node.rb +44 -0
- data/lib/quorum_sdk/utils.rb +58 -28
- data/lib/quorum_sdk/version.rb +1 -1
- data/lib/quorum_sdk.rb +2 -2
- metadata +14 -23
- data/activity_stream_pb.rb +0 -146
- data/lib/proto/activity_stream.proto +0 -134
- data/lib/proto/activity_stream_pb.rb +0 -146
- data/lib/quorum_sdk/api/chain.rb +0 -13
- data/lib/quorum_sdk/api/light_node.rb +0 -88
- data/lib/quorum_sdk/api.rb +0 -32
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QuorumSdk
|
4
|
+
class Chain
|
5
|
+
# Wrapper for HTTP APIs for chain
|
6
|
+
module Management
|
7
|
+
def allow_list(group_id: nil)
|
8
|
+
group_id ||= @group_id
|
9
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
10
|
+
|
11
|
+
path = "api/v1/group/#{group_id}/trx/allowlist"
|
12
|
+
client.get(path).body
|
13
|
+
end
|
14
|
+
|
15
|
+
def deny_list(group_id: nil)
|
16
|
+
group_id ||= @group_id
|
17
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
18
|
+
|
19
|
+
path = "api/v1/group/#{group_id}/trx/denylist"
|
20
|
+
client.get(path).body
|
21
|
+
end
|
22
|
+
|
23
|
+
def auth_type(trx_type, group_id: nil)
|
24
|
+
group_id ||= @group_id
|
25
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
26
|
+
|
27
|
+
path = "api/v1/group/#{group_id}/trx/auth/#{trx_type}"
|
28
|
+
client.get(path).body
|
29
|
+
end
|
30
|
+
|
31
|
+
ARGUMENTS_FOR_UPDATE_CHAIN_CONFIG = %i[config type].freeze
|
32
|
+
def update_chain_config(**kwargs)
|
33
|
+
raise ArgumentError, "Keyword arguments #{ARGUMENTS_FOR_UPDATE_CHAIN_CONFIG} must be provided" unless ARGUMENTS_FOR_UPDATE_CHAIN_CONFIG.all?(&->(arg) { arg.in? kwargs.keys })
|
34
|
+
|
35
|
+
type = kwargs[:type]
|
36
|
+
raise ArgumentError, 'type must be one of set_trx_auth_mode/upd_alw_list/upd_dny_list' unless type.in? %w[set_trx_auth_mode upd_alw_list upd_dny_list]
|
37
|
+
|
38
|
+
config = kwargs[:config]
|
39
|
+
config = config.to_json if config.is_a?(Hash)
|
40
|
+
|
41
|
+
group_id = kwargs[:group_id] || @group_id
|
42
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
43
|
+
|
44
|
+
path = 'api/v1/group/chainconfig'
|
45
|
+
payload = {
|
46
|
+
type:,
|
47
|
+
config:,
|
48
|
+
group_id:,
|
49
|
+
memo: kwargs[:memo]
|
50
|
+
}.compact
|
51
|
+
client.post(path, **payload).body
|
52
|
+
end
|
53
|
+
|
54
|
+
ARGUMENTS_FOR_UPDATE_AUTH_MODE = %i[trx_auth_mode].freeze
|
55
|
+
def update_trx_auth_mode(**kwargs)
|
56
|
+
raise ArgumentError, "Keyword arguments #{ARGUMENTS_FOR_UPDATE_AUTH_MODE} must be provided" unless ARGUMENTS_FOR_UPDATE_AUTH_MODE.all?(&->(arg) { arg.in? kwargs.keys })
|
57
|
+
|
58
|
+
trx_type = kwargs[:trx_type] || 'POST'
|
59
|
+
raise ArgumentError, 'trx_type must be one of POST/ANNOUNCE/REQ_BLOCK_FORWARD/REQ_BLOCK_FORWARD' unless trx_type.in? %w[POST ANNOUNCE REQ_BLOCK_RESP REQ_BLOCK_BACKWARD]
|
60
|
+
|
61
|
+
trx_auth_mode = kwargs[:trx_auth_mode]
|
62
|
+
raise ArgumentError, 'trx_auth_mode must be one of follow_alw_list/follow_dny_list' unless trx_auth_mode.in? %w[follow_alw_list follow_dny_list]
|
63
|
+
|
64
|
+
group_id = kwargs[:group_id] || @group_id
|
65
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
66
|
+
|
67
|
+
update_chain_config(
|
68
|
+
group_id:,
|
69
|
+
type: 'set_trx_auth_mode',
|
70
|
+
config: {
|
71
|
+
trx_type:,
|
72
|
+
trx_auth_mode:
|
73
|
+
},
|
74
|
+
memo: kwargs[:memo]
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
ARGUMENTS_FOR_UPDATE_ALLOW_LIST = %i[action pubkey].freeze
|
79
|
+
def update_allow_list(**kwargs)
|
80
|
+
raise ArgumentError, "Keyword arguments #{ARGUMENTS_FOR_UPDATE_ALLOW_LIST} must be provided" unless ARGUMENTS_FOR_UPDATE_ALLOW_LIST.all?(&->(arg) { arg.in? kwargs.keys })
|
81
|
+
|
82
|
+
trx_type = kwargs[:trx_type] || ['POST']
|
83
|
+
trx_type = [trx_type] if trx_type.is_a?(String)
|
84
|
+
raise ArgumentError, 'trx type must be one of POST/ANNOUNCE/REQ_BLOCK_FORWARD/REQ_BLOCK_FORWARD' unless trx_type.all?(&->(op) { op.in? %w[POST ANNOUNCE REQ_BLOCK_RESP REQ_BLOCK_BACKWARD] })
|
85
|
+
|
86
|
+
action = kwargs[:action]
|
87
|
+
raise ArgumentError, 'trx_auth_mode must be one of add/remove' unless action.in? %w[add remove]
|
88
|
+
|
89
|
+
pubkey = kwargs[:pubkey]
|
90
|
+
if Eth::Util.hex?(pubkey)
|
91
|
+
pubkey = Secp256k1::PublicKey.from_data([pubkey].pack('H*')).compressed
|
92
|
+
pubkey = Base64.urlsafe_encode64(pubkey)
|
93
|
+
end
|
94
|
+
|
95
|
+
group_id = kwargs[:group_id] || @group_id
|
96
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
97
|
+
|
98
|
+
update_chain_config(
|
99
|
+
group_id:,
|
100
|
+
type: 'upd_alw_list',
|
101
|
+
config: {
|
102
|
+
trx_type:,
|
103
|
+
action:,
|
104
|
+
pubkey:
|
105
|
+
},
|
106
|
+
memo: kwargs[:memo]
|
107
|
+
)
|
108
|
+
end
|
109
|
+
|
110
|
+
ARGUMENTS_FOR_UPDATE_DENY_LIST = %i[action pubkey].freeze
|
111
|
+
def update_deny_list(**kwargs)
|
112
|
+
raise ArgumentError, "Keyword arguments #{ARGUMENTS_FOR_UPDATE_DENY_LIST} must be provided" unless ARGUMENTS_FOR_UPDATE_DENY_LIST.all?(&->(arg) { arg.in? kwargs.keys })
|
113
|
+
|
114
|
+
trx_type = kwargs[:trx_type] || ['POST']
|
115
|
+
trx_type = [trx_type] if trx_type.is_a?(String)
|
116
|
+
raise ArgumentError, 'trx type must be one of POST/ANNOUNCE/REQ_BLOCK_FORWARD/REQ_BLOCK_FORWARD' unless trx_type.all?(&->(op) { op.in? %w[POST ANNOUNCE REQ_BLOCK_RESP REQ_BLOCK_BACKWARD] })
|
117
|
+
|
118
|
+
action = kwargs[:action]
|
119
|
+
raise ArgumentError, 'trx_auth_mode must be one of add/remove' unless action.in? %w[add remove]
|
120
|
+
|
121
|
+
pubkey = kwargs[:pubkey]
|
122
|
+
if Eth::Util.hex?(pubkey)
|
123
|
+
pubkey = Secp256k1::PublicKey.from_data([pubkey].pack('H*')).compressed
|
124
|
+
pubkey = Base64.urlsafe_encode64(pubkey)
|
125
|
+
end
|
126
|
+
|
127
|
+
group_id = kwargs[:group_id] || @group_id
|
128
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
129
|
+
|
130
|
+
update_chain_config(
|
131
|
+
group_id:,
|
132
|
+
type: 'upd_dny_list',
|
133
|
+
config: {
|
134
|
+
trx_type:,
|
135
|
+
action:,
|
136
|
+
pubkey:
|
137
|
+
},
|
138
|
+
memo: kwargs[:memo]
|
139
|
+
)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QuorumSdk
|
4
|
+
class Chain
|
5
|
+
# Wrapper for HTTP APIs for chain
|
6
|
+
module Node
|
7
|
+
def network
|
8
|
+
path = 'api/v1/network'
|
9
|
+
client.get(path).body
|
10
|
+
end
|
11
|
+
|
12
|
+
def node
|
13
|
+
path = 'api/v1/node'
|
14
|
+
client.get(path).body
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'chain/chain'
|
4
|
+
require_relative 'chain/group'
|
5
|
+
require_relative 'chain/management'
|
6
|
+
require_relative 'chain/node'
|
7
|
+
require_relative 'client'
|
8
|
+
|
9
|
+
module QuorumSdk
|
10
|
+
# Wrapper for HTTP APIs as chain admin
|
11
|
+
class Chain
|
12
|
+
attr_reader :chain_url, :jwt, :client
|
13
|
+
|
14
|
+
def initialize(**kwargs)
|
15
|
+
@chain_url = kwargs[:chain_url]
|
16
|
+
@jwt = kwargs[:jwt]
|
17
|
+
|
18
|
+
@client = QuorumSdk::Client.new(@chain_url, @jwt)
|
19
|
+
end
|
20
|
+
|
21
|
+
include QuorumSdk::Chain::Chain
|
22
|
+
include QuorumSdk::Chain::Group
|
23
|
+
include QuorumSdk::Chain::Management
|
24
|
+
include QuorumSdk::Chain::Node
|
25
|
+
end
|
26
|
+
end
|
data/lib/quorum_sdk/client.rb
CHANGED
@@ -1,19 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative 'api/light_node'
|
4
|
-
|
5
3
|
module QuorumSdk
|
6
4
|
# HTTP Client wrapper
|
7
5
|
class Client
|
8
6
|
attr_reader :domains
|
9
7
|
|
10
|
-
def initialize(
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
8
|
+
def initialize(chain_url, jwt = nil)
|
9
|
+
chain_url =
|
10
|
+
case chain_url
|
11
|
+
when Array
|
12
|
+
chain_url.first
|
13
|
+
when String
|
14
|
+
chain_url
|
15
|
+
end
|
16
|
+
|
17
|
+
uri = Addressable::URI.parse chain_url
|
18
|
+
url = Addressable::URI.new(scheme: uri.scheme, host: uri.host, port: uri.port).to_s
|
19
|
+
jwt ||= uri.query_values['jwt']
|
20
|
+
|
21
|
+
@conn =
|
22
|
+
Faraday.new(
|
23
|
+
url:,
|
24
|
+
headers: { Authorization: "Bearer #{jwt}" }
|
25
|
+
) do |f|
|
26
|
+
f.request :json
|
27
|
+
f.response :json
|
28
|
+
f.response :logger
|
29
|
+
end
|
17
30
|
end
|
18
31
|
|
19
32
|
def post(path, **body)
|
@@ -21,7 +34,7 @@ module QuorumSdk
|
|
21
34
|
end
|
22
35
|
|
23
36
|
def get(path, **params)
|
24
|
-
@conn.
|
37
|
+
@conn.get path, **params
|
25
38
|
end
|
26
39
|
end
|
27
40
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QuorumSdk
|
4
|
+
class Node
|
5
|
+
# Announce module
|
6
|
+
module Announce
|
7
|
+
def announce(**kwargs)
|
8
|
+
group_id = kwargs[:group_id] || @group_id
|
9
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
10
|
+
|
11
|
+
payload = {
|
12
|
+
data: {
|
13
|
+
Action: kwargs[:action],
|
14
|
+
AnnouncerSignature: kwargs[:announce_signature],
|
15
|
+
EncryptPubkey: kwargs[:encrypt_pubkey],
|
16
|
+
GroupId: group_id,
|
17
|
+
Memo: kwargs[:memo],
|
18
|
+
OwnerPubkey: kwargs[:owner_pubkey],
|
19
|
+
OwnerSignature: kwargs[:owner_signature],
|
20
|
+
Result: kwargs[:result],
|
21
|
+
SignPubkey: kwargs[:sign_pubkey],
|
22
|
+
TimeStamp: kwargs[:timestamp],
|
23
|
+
Type: kwargs[:type]
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
path = "/api/v1/node/#{group_id}/announce"
|
28
|
+
client.post(path, **payload).body
|
29
|
+
end
|
30
|
+
|
31
|
+
def announced_producers(group_id: nil)
|
32
|
+
group_id ||= @group_id
|
33
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
34
|
+
|
35
|
+
path = "/api/v1/node/#{group_id}/announced/producer"
|
36
|
+
client.get(path).body
|
37
|
+
end
|
38
|
+
|
39
|
+
def announced_users(group_id: nil, pubkey: nil)
|
40
|
+
group_id ||= @group_id
|
41
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
42
|
+
|
43
|
+
params = {
|
44
|
+
pubkey:
|
45
|
+
}.compact
|
46
|
+
|
47
|
+
path = "/api/v1/node/#{group_id}/announced/user"
|
48
|
+
client.get(path, **params).body
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QuorumSdk
|
4
|
+
class Node
|
5
|
+
# Appconfig module
|
6
|
+
module AppConfig
|
7
|
+
def app_config(key, group_id: nil)
|
8
|
+
group_id ||= @group_id
|
9
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
10
|
+
raise ArgumentError, 'key must be provided' if key.blank?
|
11
|
+
|
12
|
+
path = "api/v1/node/#{group_id}/appconfig/by/#{key}"
|
13
|
+
client.get(path).body
|
14
|
+
end
|
15
|
+
|
16
|
+
def app_config_keys(group_id: nil)
|
17
|
+
group_id ||= @group_id
|
18
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
19
|
+
|
20
|
+
path = "api/v1/node/#{group_id}/appconfig/keylist"
|
21
|
+
client.get(path).body
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QuorumSdk
|
4
|
+
class Node
|
5
|
+
# Auth module
|
6
|
+
module Auth
|
7
|
+
def allow_list(group_id: nil)
|
8
|
+
group_id ||= @group_id
|
9
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
10
|
+
|
11
|
+
path = "api/v1/node/#{group_id}/auth/alwlist"
|
12
|
+
client.get(path).body
|
13
|
+
end
|
14
|
+
|
15
|
+
def deny_list(group_id: nil)
|
16
|
+
group_id ||= @group_id
|
17
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
18
|
+
|
19
|
+
path = "api/v1/node/#{group_id}/auth/denylist"
|
20
|
+
client.get(path).body
|
21
|
+
end
|
22
|
+
|
23
|
+
def auth_type(group_id: nil, trx_type: 'POST')
|
24
|
+
group_id ||= @group_id
|
25
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
26
|
+
|
27
|
+
path = "api/v1/node/#{group_id}/auth/by/#{trx_type}"
|
28
|
+
client.get(path).body
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QuorumSdk
|
4
|
+
class Node
|
5
|
+
# group module
|
6
|
+
module Group
|
7
|
+
def info(group_id: nil)
|
8
|
+
group_id ||= @group_id
|
9
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
10
|
+
|
11
|
+
path = "api/v1/node/#{group_id}/info"
|
12
|
+
client.get(path).body
|
13
|
+
end
|
14
|
+
|
15
|
+
def producers(group_id: nil)
|
16
|
+
group_id ||= @group_id
|
17
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
18
|
+
|
19
|
+
path = "api/v1/node/#{group_id}/producers"
|
20
|
+
client.get(path).body
|
21
|
+
end
|
22
|
+
|
23
|
+
def user_encrypt_pubkeys(group_id: nil)
|
24
|
+
group_id ||= @group_id
|
25
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
26
|
+
|
27
|
+
path = "api/v1/node/#{group_id}/userencryptpubkeys"
|
28
|
+
client.get(path).body
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module QuorumSdk
|
4
|
+
class Node
|
5
|
+
# Wrapper for HTTP APIs as light node client
|
6
|
+
module Trx
|
7
|
+
ARGUMENTS_FOR_BUILD_TRX = %i[private_key data].freeze
|
8
|
+
def build_trx(**kwargs)
|
9
|
+
group_id = kwargs[:group_id] || @group_id
|
10
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
11
|
+
|
12
|
+
cipher_key = kwargs[:cipher_key] || @cipher_key
|
13
|
+
raise ArgumentError, 'cipher_key must be provided' if cipher_key.blank?
|
14
|
+
|
15
|
+
raise ArgumentError, "Keyword arguments #{ARGUMENTS_FOR_BUILD_TRX} must be provided" unless ARGUMENTS_FOR_BUILD_TRX.all?(&->(arg) { arg.in? kwargs.keys })
|
16
|
+
|
17
|
+
kwargs = kwargs.merge(
|
18
|
+
group_id:,
|
19
|
+
cipher_key:
|
20
|
+
)
|
21
|
+
QuorumSdk::Utils.build_trx(**kwargs)
|
22
|
+
end
|
23
|
+
|
24
|
+
ARGUMENTS_FOR_SEND_TRX = %i[data sender_pubkey sender_sign trx_id version].freeze
|
25
|
+
def send_trx(trx = nil, **kwargs)
|
26
|
+
trx ||= kwargs
|
27
|
+
raise ArgumentError, "Keyword arguments #{ARGUMENTS_FOR_SEND_TRX} must be provided" unless ARGUMENTS_FOR_SEND_TRX.all?(&->(arg) { arg.in? trx.keys })
|
28
|
+
|
29
|
+
group_id = trx[:group_id] || @group_id
|
30
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
31
|
+
|
32
|
+
payload = {
|
33
|
+
data: trx[:data],
|
34
|
+
sender_pubkey: trx[:sender_pubkey],
|
35
|
+
sender_sign: trx[:sender_sign],
|
36
|
+
timestamp: trx[:time_stamp] || trx[:timestamp],
|
37
|
+
trx_id: trx[:trx_id],
|
38
|
+
version: trx[:version]
|
39
|
+
}.compact
|
40
|
+
payload.deep_transform_values!(&->(v) { v.is_a?(String) ? v : v.to_s })
|
41
|
+
|
42
|
+
path = "api/v1/node/#{group_id}/trx"
|
43
|
+
client.post(path, **payload).body
|
44
|
+
end
|
45
|
+
|
46
|
+
def list_trx(**kwargs)
|
47
|
+
group_id = kwargs[:group_id] || @group_id
|
48
|
+
raise ArgumentError, 'group_id must be provided' if group_id.blank?
|
49
|
+
|
50
|
+
cipher_key = kwargs[:cipher_key] || @cipher_key
|
51
|
+
|
52
|
+
params = {
|
53
|
+
group_id:,
|
54
|
+
start_trx: kwargs[:start_trx],
|
55
|
+
num: kwargs[:num] || 100,
|
56
|
+
senders: kwargs[:senders],
|
57
|
+
reverse: kwargs[:reverse],
|
58
|
+
include_start_trx: kwargs[:include_start_trx]
|
59
|
+
}.compact
|
60
|
+
|
61
|
+
path = "api/v1/node/#{group_id}/groupctn"
|
62
|
+
list = client.get(path, **params).body
|
63
|
+
|
64
|
+
return list unless list.is_a?(Array)
|
65
|
+
return list if cipher_key.blank?
|
66
|
+
|
67
|
+
list.each do |trx|
|
68
|
+
next if trx['Data'].blank?
|
69
|
+
|
70
|
+
data = Base64.strict_decode64 trx['Data']
|
71
|
+
trx['DecryptedData'] = QuorumSdk::Utils.decrypt_trx_data data, key: cipher_key
|
72
|
+
end
|
73
|
+
|
74
|
+
list
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'client'
|
4
|
+
require_relative 'node/announce'
|
5
|
+
require_relative 'node/app_config'
|
6
|
+
require_relative 'node/auth'
|
7
|
+
require_relative 'node/group'
|
8
|
+
require_relative 'node/trx'
|
9
|
+
|
10
|
+
module QuorumSdk
|
11
|
+
# Wrapper for HTTP APIs as light node client
|
12
|
+
class Node
|
13
|
+
attr_reader :group_id, :group_name, :consensus_type, :encryption_type, :app_key, :owner_pubkey, :signature,
|
14
|
+
:cipher_key, :chain_url, :jwt, :client
|
15
|
+
|
16
|
+
def initialize(**kwargs)
|
17
|
+
config =
|
18
|
+
if kwargs[:seed].present?
|
19
|
+
QuorumSdk::Utils.parse_seed kwargs[:seed]
|
20
|
+
else
|
21
|
+
kwargs
|
22
|
+
end
|
23
|
+
|
24
|
+
@group_id = config[:group_id]
|
25
|
+
@group_name = config[:group_name]
|
26
|
+
@consensus_type = config[:consensus_type]
|
27
|
+
@encryption_type = config[:encryption_type]
|
28
|
+
@app_key = config[:app_key]
|
29
|
+
@owner_pubkey = config[:owner_pubkey]
|
30
|
+
@signature = config[:signature]
|
31
|
+
@cipher_key = config[:cipher_key]
|
32
|
+
@chain_url = config[:chain_url]
|
33
|
+
@jwt = config[:jwt]
|
34
|
+
|
35
|
+
@client = QuorumSdk::Client.new(@chain_url, @jwt)
|
36
|
+
end
|
37
|
+
|
38
|
+
include QuorumSdk::Node::Announce
|
39
|
+
include QuorumSdk::Node::AppConfig
|
40
|
+
include QuorumSdk::Node::Auth
|
41
|
+
include QuorumSdk::Node::Group
|
42
|
+
include QuorumSdk::Node::Trx
|
43
|
+
end
|
44
|
+
end
|
data/lib/quorum_sdk/utils.rb
CHANGED
@@ -3,8 +3,10 @@
|
|
3
3
|
module QuorumSdk
|
4
4
|
# Wrapper for some useful methods
|
5
5
|
module Utils
|
6
|
+
TRX_VERSION = '2.0.0'
|
7
|
+
|
6
8
|
class << self
|
7
|
-
def
|
9
|
+
def parse_seed(url)
|
8
10
|
url = Addressable::URI.parse(url.gsub(/\\u([a-f0-9]{4})/i) { [::Regexp.last_match(1).hex].pack('U') })
|
9
11
|
query_values = url.query_values
|
10
12
|
|
@@ -17,7 +19,7 @@ module QuorumSdk
|
|
17
19
|
app_key = query_values['y']
|
18
20
|
consensus_type = query_values['n'].to_s == '1' ? 'pos' : 'poa'
|
19
21
|
encryption_type = query_values['e'].to_s == '0' ? 'public' : 'private'
|
20
|
-
|
22
|
+
chain_url = query_values['u'].split('|')
|
21
23
|
|
22
24
|
{
|
23
25
|
group_id:,
|
@@ -29,11 +31,13 @@ module QuorumSdk
|
|
29
31
|
app_key:,
|
30
32
|
consensus_type:,
|
31
33
|
encryption_type:,
|
32
|
-
|
34
|
+
chain_url:
|
33
35
|
}
|
34
36
|
end
|
35
37
|
|
36
38
|
def uuid_from_base64(str)
|
39
|
+
return if str.blank?
|
40
|
+
|
37
41
|
hex = Base64.urlsafe_decode64(str).unpack1('H*')
|
38
42
|
format(
|
39
43
|
'%<first>s-%<second>s-%<third>s-%<forth>s-%<fifth>s',
|
@@ -46,38 +50,62 @@ module QuorumSdk
|
|
46
50
|
end
|
47
51
|
|
48
52
|
ARGUMENTS_FOR_ENCRYPT_TRX = %i[private_key group_id cipher_key data].freeze
|
49
|
-
def
|
53
|
+
def build_trx(**kwargs)
|
50
54
|
raise ArgumentError, "Keyword arguments #{ARGUMENTS_FOR_ENCRYPT_TRX} must be provided" unless ARGUMENTS_FOR_ENCRYPT_TRX.all?(&->(arg) { arg.in? kwargs.keys })
|
51
|
-
raise ArgumentError, 'data should be instance of Google::Protobuf::MessageExts' unless kwargs[:data].is_a?(Google::Protobuf::MessageExts)
|
52
55
|
|
53
|
-
data =
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
data =
|
57
|
+
case kwargs[:data]
|
58
|
+
when Hash
|
59
|
+
kwargs[:data].to_json
|
60
|
+
else
|
61
|
+
kwargs[:data].to_s
|
62
|
+
end
|
63
|
+
encrypted_data = aes_encrypt(data, key: kwargs[:cipher_key])
|
58
64
|
|
59
65
|
account = QuorumSdk::Account.new priv: kwargs[:private_key]
|
60
66
|
|
61
|
-
|
67
|
+
trx =
|
62
68
|
Quorum::Pb::Trx.new(
|
63
69
|
TrxId: (kwargs[:trx_id] || SecureRandom.uuid),
|
64
70
|
GroupId: kwargs[:group_id],
|
65
71
|
Data: encrypted_data,
|
66
|
-
TimeStamp: (kwargs[:timestamp].
|
67
|
-
Version: (kwargs[:version] ||
|
68
|
-
Expired: (kwargs[:expired].to_i || (30.seconds.from_now.to_f * 1e9).to_i),
|
69
|
-
Nonce: (kwargs[:nonce] || 1),
|
72
|
+
TimeStamp: (kwargs[:timestamp].presence || (Time.now.to_f * 1e9)).to_i,
|
73
|
+
Version: (kwargs[:version] || TRX_VERSION),
|
70
74
|
SenderPubkey: Base64.urlsafe_encode64(account.public_bytes_compressed, padding: false)
|
71
75
|
)
|
72
76
|
|
73
|
-
hash = Digest::SHA256.hexdigest
|
77
|
+
hash = Digest::SHA256.hexdigest trx.to_proto
|
74
78
|
signature = account.sign [hash].pack('H*')
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
}
|
79
|
-
|
80
|
-
|
79
|
+
trx.SenderSign = [signature].pack('H*')
|
80
|
+
|
81
|
+
json = Quorum::Pb::Trx.encode_json trx
|
82
|
+
JSON.parse(json).deep_transform_keys! { |key| key.to_s.underscore.to_sym }
|
83
|
+
end
|
84
|
+
|
85
|
+
def verify_trx(trx = nil, **kwargs)
|
86
|
+
trx ||= kwargs
|
87
|
+
trx.deep_transform_keys! { |key| key.to_s.camelize.to_sym }
|
88
|
+
trx = trx.with_indifferent_access
|
89
|
+
trx['TimeStamp'] = trx['TimeStamp'].to_i if trx['TimeStamp'].present? && trx['TimeStamp'].is_a?(String)
|
90
|
+
trx['Expired'] = trx['Expired'].to_i if trx['Expired'].present? && trx['Expired'].is_a?(String)
|
91
|
+
|
92
|
+
signature = Base64.urlsafe_decode64(trx['SenderSign']).unpack1('H*')
|
93
|
+
|
94
|
+
trx = Quorum::Pb::Trx.new(
|
95
|
+
TrxId: trx['TrxId'],
|
96
|
+
GroupId: trx['GroupId'],
|
97
|
+
Data: Base64.decode64(trx['Data']),
|
98
|
+
TimeStamp: trx['TimeStamp'],
|
99
|
+
Expired: trx['Expired'],
|
100
|
+
Version: trx['Version'],
|
101
|
+
SenderPubkey: trx['SenderPubkey']
|
102
|
+
)
|
103
|
+
|
104
|
+
hash = Digest::SHA256.hexdigest trx.to_proto
|
105
|
+
public_key = Secp256k1::PublicKey.from_data(Base64.urlsafe_decode64(trx.SenderPubkey)).uncompressed.unpack1('H*')
|
106
|
+
recover_key = Eth::Signature.recover [hash].pack('H*'), signature
|
107
|
+
|
108
|
+
public_key == recover_key
|
81
109
|
end
|
82
110
|
|
83
111
|
def decrypt_trx(cipher, key:)
|
@@ -91,9 +119,9 @@ module QuorumSdk
|
|
91
119
|
hash = Digest::SHA256.hexdigest Quorum::Pb::Trx.encode(trx_without_sig)
|
92
120
|
|
93
121
|
signature = trx.SenderSign.unpack1('H*')
|
94
|
-
|
95
|
-
|
96
|
-
raise QuorumSdk::Error,
|
122
|
+
public_key = Secp256k1::PublicKey.from_data(Base64.urlsafe_decode64(trx.SenderPubkey)).uncompressed.unpack1('H*')
|
123
|
+
recover_key = Eth::Signature.recover [hash].pack('H*'), signature
|
124
|
+
raise QuorumSdk::Error, "Signature not verified: #{public_key} != #{recover_key}" unless public_key == recover_key
|
97
125
|
|
98
126
|
data = decrypt_trx_data(trx.Data, key:)
|
99
127
|
|
@@ -104,9 +132,11 @@ module QuorumSdk
|
|
104
132
|
|
105
133
|
def decrypt_trx_data(cipher, key:)
|
106
134
|
decrypted_data = aes_decrypt(cipher, key:)
|
107
|
-
|
108
|
-
|
109
|
-
JSON
|
135
|
+
begin
|
136
|
+
JSON.parse(decrypted_data).with_indifferent_access
|
137
|
+
rescue JSON::ParserError
|
138
|
+
decrypted_data
|
139
|
+
end
|
110
140
|
end
|
111
141
|
|
112
142
|
def aes_encrypt(data, key:)
|
data/lib/quorum_sdk/version.rb
CHANGED
data/lib/quorum_sdk.rb
CHANGED
@@ -9,10 +9,10 @@ require 'faraday'
|
|
9
9
|
require 'faraday/retry'
|
10
10
|
require 'google/protobuf/well_known_types'
|
11
11
|
|
12
|
-
require_relative 'proto/activity_stream_pb'
|
13
12
|
require_relative 'proto/chain_pb'
|
14
13
|
require_relative 'quorum_sdk/account'
|
15
|
-
require_relative 'quorum_sdk/
|
14
|
+
require_relative 'quorum_sdk/chain'
|
15
|
+
require_relative 'quorum_sdk/node'
|
16
16
|
require_relative 'quorum_sdk/utils'
|
17
17
|
require_relative 'quorum_sdk/version'
|
18
18
|
|