quorum_sdk 0.1.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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