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.
@@ -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
@@ -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(domains)
11
- @domains = domains || []
12
- @conn = Faraday.new(url: @domains.first) do |f|
13
- f.request :json
14
- f.response :json
15
- f.response :logger
16
- end
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.post path, **params
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
@@ -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 parse_seed_url(url)
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
- chain_urls = query_values['u'].split('|')
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
- chain_urls:
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 encrypt_trx(**kwargs)
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 = Google::Protobuf::Any.new(
54
- type_url: "type.googleapis.com/#{kwargs[:data].class.descriptor.name}",
55
- value: kwargs[:data].to_proto
56
- ).to_proto
57
- encrypted_data = aes_encrypt data, key: kwargs[:cipher_key]
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
- msg =
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].to_i || (Time.now.to_f * 1e9).to_i),
67
- Version: (kwargs[:version] || '1.0.0'),
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 msg.to_proto
77
+ hash = Digest::SHA256.hexdigest trx.to_proto
74
78
  signature = account.sign [hash].pack('H*')
75
- msg.SenderSign = [signature].pack('H*')
76
- trx_json = {
77
- TrxBytes: Base64.strict_encode64(msg.to_proto)
78
- }
79
- encrypted = aes_encrypt trx_json.to_json, key: kwargs[:cipher_key]
80
- Base64.strict_encode64 encrypted
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
- public_key_compressed = Base64.urlsafe_decode64(trx.SenderPubkey).unpack1('H*')
95
- public_key_uncompressed = Eth::Signature.recover [hash].pack('H*'), signature
96
- raise QuorumSdk::Error, 'Signature not verified' if public_key_uncompressed[2...66] != public_key_compressed[2...]
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
- obj = Google::Protobuf::Any.decode decrypted_data
108
- msgclass = ::Google::Protobuf::DescriptorPool.generated_pool.lookup(obj.type_url.split('/').last).msgclass
109
- JSON.parse msgclass.encode_json(msgclass.decode(obj.value))
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:)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QuorumSdk
4
- VERSION = '0.1.0'
4
+ VERSION = '0.3.0'
5
5
  end
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/api'
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