quorum_sdk 0.2.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.
@@ -1,23 +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
- uri = Addressable::URI.parse @domains.first
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
13
18
  url = Addressable::URI.new(scheme: uri.scheme, host: uri.host, port: uri.port).to_s
14
- jwt = uri.query_values['jwt']
19
+ jwt ||= uri.query_values['jwt']
15
20
 
16
- @conn = Faraday.new(url:, headers: { Authorization: "Bearer #{jwt}" }) do |f|
17
- f.request :json
18
- f.response :json
19
- f.response :logger
20
- end
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
21
30
  end
22
31
 
23
32
  def post(path, **body)
@@ -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,7 +31,7 @@ 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
 
@@ -48,7 +50,7 @@ module QuorumSdk
48
50
  end
49
51
 
50
52
  ARGUMENTS_FOR_ENCRYPT_TRX = %i[private_key group_id cipher_key data].freeze
51
- def encrypt_trx(**kwargs)
53
+ def build_trx(**kwargs)
52
54
  raise ArgumentError, "Keyword arguments #{ARGUMENTS_FOR_ENCRYPT_TRX} must be provided" unless ARGUMENTS_FOR_ENCRYPT_TRX.all?(&->(arg) { arg.in? kwargs.keys })
53
55
 
54
56
  data =
@@ -58,29 +60,52 @@ module QuorumSdk
58
60
  else
59
61
  kwargs[:data].to_s
60
62
  end
61
- encrypted_data = aes_encrypt data, key: kwargs[:cipher_key]
63
+ encrypted_data = aes_encrypt(data, key: kwargs[:cipher_key])
62
64
 
63
65
  account = QuorumSdk::Account.new priv: kwargs[:private_key]
64
66
 
65
- msg =
67
+ trx =
66
68
  Quorum::Pb::Trx.new(
67
69
  TrxId: (kwargs[:trx_id] || SecureRandom.uuid),
68
70
  GroupId: kwargs[:group_id],
69
71
  Data: encrypted_data,
70
- TimeStamp: (kwargs[:timestamp].to_i || (Time.now.to_f * 1e9).to_i),
72
+ TimeStamp: (kwargs[:timestamp].presence || (Time.now.to_f * 1e9)).to_i,
71
73
  Version: (kwargs[:version] || TRX_VERSION),
72
- Expired: (kwargs[:expired].to_i || (30.seconds.from_now.to_f * 1e9).to_i),
73
74
  SenderPubkey: Base64.urlsafe_encode64(account.public_bytes_compressed, padding: false)
74
75
  )
75
76
 
76
- hash = Digest::SHA256.hexdigest msg.to_proto
77
+ hash = Digest::SHA256.hexdigest trx.to_proto
77
78
  signature = account.sign [hash].pack('H*')
78
- msg.SenderSign = [signature].pack('H*')
79
- trx_json = {
80
- TrxBytes: Base64.strict_encode64(msg.to_proto)
81
- }
82
- encrypted = aes_encrypt trx_json.to_json, key: kwargs[:cipher_key]
83
- 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
84
109
  end
85
110
 
86
111
  def decrypt_trx(cipher, key:)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module QuorumSdk
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/quorum_sdk.rb CHANGED
@@ -9,16 +9,14 @@ 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
 
19
19
  module QuorumSdk
20
- TRX_VERSION = '2.0.0'
21
-
22
20
  class Error < StandardError; end
23
21
  class ArgumentError < Error; end
24
22
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quorum_sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - an-lee
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-24 00:00:00.000000000 Z
11
+ date: 2023-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -123,18 +123,22 @@ files:
123
123
  - LICENSE.txt
124
124
  - README.md
125
125
  - Rakefile
126
- - lib/proto/activity_stream.proto
127
- - lib/proto/activity_stream_pb.rb
128
126
  - lib/proto/chain.proto
129
127
  - lib/proto/chain_pb.rb
130
128
  - lib/quorum_sdk.rb
131
129
  - lib/quorum_sdk/account.rb
132
- - lib/quorum_sdk/api.rb
133
- - lib/quorum_sdk/api/chain.rb
134
- - lib/quorum_sdk/api/group.rb
135
- - lib/quorum_sdk/api/light_node.rb
136
- - lib/quorum_sdk/api/management.rb
130
+ - lib/quorum_sdk/chain.rb
131
+ - lib/quorum_sdk/chain/chain.rb
132
+ - lib/quorum_sdk/chain/group.rb
133
+ - lib/quorum_sdk/chain/management.rb
134
+ - lib/quorum_sdk/chain/node.rb
137
135
  - lib/quorum_sdk/client.rb
136
+ - lib/quorum_sdk/node.rb
137
+ - lib/quorum_sdk/node/announce.rb
138
+ - lib/quorum_sdk/node/app_config.rb
139
+ - lib/quorum_sdk/node/auth.rb
140
+ - lib/quorum_sdk/node/group.rb
141
+ - lib/quorum_sdk/node/trx.rb
138
142
  - lib/quorum_sdk/utils.rb
139
143
  - lib/quorum_sdk/version.rb
140
144
  - sig/quorum_sdk.rbs
@@ -1,134 +0,0 @@
1
- /* https://github.com/rumsystem/quorum/blob/main/pkg/pb/activity_stream.proto */
2
- syntax = "proto3";
3
- package quorum.pb;
4
- option go_package = "github.com/rumsystem/quorum/pkg/pb";
5
-
6
- import "google/protobuf/timestamp.proto";
7
- import "google/protobuf/any.proto";
8
-
9
- message AnyObj {
10
- google.protobuf.Any any=1;
11
- }
12
-
13
- message Object {
14
- string id = 1;
15
- string type = 2;
16
- repeated Object attachments = 3;
17
- repeated Object attributedTo = 4;
18
- Object audience = 5;
19
- string content = 6;
20
- Object context = 7;
21
- string name = 8;
22
- google.protobuf.Timestamp endtime = 9;
23
- Object generator= 10;
24
- repeated Object icon = 11;
25
- repeated Image image = 12;
26
- Reply inreplyto = 13;
27
- Object location = 14;
28
- Object preview = 15;
29
- google.protobuf.Timestamp published = 16;
30
- Object replies = 17;
31
- google.protobuf.Timestamp startTime = 18;
32
- string summary = 19;
33
- repeated Object tag = 20;
34
- google.protobuf.Timestamp updated = 21;
35
- repeated Link url = 22;
36
- repeated Object to = 23;
37
- repeated Object bto = 24;
38
- repeated Object cc = 25;
39
- repeated Object bcc = 26;
40
- string mediaType= 27;
41
- string duration = 28;
42
- File file = 29;
43
- }
44
-
45
- message Link {
46
- string href = 1;
47
- repeated string rel = 2;
48
- string mediaType= 3;
49
- string name = 4;
50
- string hreflang = 5;
51
- uint32 height = 6;
52
- uint32 width = 7;
53
- Object preview =8;
54
- }
55
-
56
- message Reply {
57
- string trxid = 1;
58
- string groupid = 2;
59
- }
60
-
61
- message Image {
62
- string id = 1;
63
- string name = 2;
64
- string mediaType= 3;
65
- bytes content = 4;
66
- string url = 5;
67
- }
68
-
69
- message File {
70
- string id = 1;
71
- string name = 2;
72
- string mediaType= 3;
73
- enum Compression {
74
- none = 0;
75
- gz = 1;
76
- zip = 2;
77
- zstd = 3;
78
- }
79
- Compression compression = 4;
80
- bytes content = 5;
81
- string url = 6;
82
- }
83
-
84
- message Person {
85
- string id = 1;
86
- string name = 2;
87
- Image image = 3;
88
- repeated Payment wallet = 11;
89
- }
90
-
91
- message Payment {
92
- string id = 1;
93
- string type = 2;
94
- string name = 3;
95
- }
96
-
97
- message Activity {
98
- string id = 1;
99
- string type = 2;
100
- repeated Object attachments = 3;
101
- repeated Object attributedTo = 4;
102
- Object audience = 5;
103
- string content = 6;
104
- Object context = 7;
105
- string name = 8;
106
- google.protobuf.Timestamp endtime = 9;
107
- Object generator= 10;
108
- repeated Object icon = 11;
109
- repeated Object image = 12;
110
- Object inReplyTo = 13;
111
- Object location = 14;
112
- Object preview = 15;
113
- google.protobuf.Timestamp published = 16;
114
- Object replies = 17;
115
- google.protobuf.Timestamp startTime = 18;
116
- string summary = 19;
117
- repeated Object tag = 20;
118
- google.protobuf.Timestamp updated = 21;
119
- repeated Link url = 22;
120
- repeated Object to = 23;
121
- repeated Object bto = 24;
122
- repeated Object cc = 25;
123
- repeated Object bcc = 26;
124
- string mediaType = 27;
125
- string duration = 28;
126
-
127
- Object actor = 29;
128
- Object object = 30;
129
- Object target = 31;
130
- Object result = 32;
131
- Object origin = 33;
132
- Object instrument = 34;
133
- Person person = 35;
134
- }