fabric-gateway 0.1.0 → 0.2.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,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fabric
4
+ #
5
+ # Gateway Client represents the connection to a Hyperledger Fabric Gateway.
6
+ #
7
+ class Client
8
+ attr_reader :grpc_client, :default_call_options
9
+
10
+ #
11
+ # Initializes a client
12
+ #
13
+ # @see https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:initialize
14
+ #
15
+ # @param [Gateway::Gateway::Stub] grpc_client grpc gateway client stub
16
+ # @param [string] host hostname and port of the gateway
17
+ # @param [GRPC::Core::ChannelCredentials|GRPC::Core::XdsChannelCredentials|Symbol] creds channel credentials
18
+ # (usually the CA certificate)
19
+ # @param [Hash] default_call_options call options to use by default for different operations
20
+ # @option default_call_options [Hash] :endorse_options default options for endorse call; @see keyword arguments in https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
21
+ # @option default_call_options [Hash] :evaluate_options default options for evaluate call; @see keyword arguments in
22
+ # https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
23
+ # @option default_call_options [Hash] :submit_options default options for submit call; @see keyword arguments in https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
24
+ # @option default_call_options [Hash] :commit_status_options default options for commit_status call;
25
+ # @see keyword arguments in https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
26
+ # @option default_call_options [Hash] :chaincode_events_options default options for chaincode_events call;
27
+ # @see keyword arguments in https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
28
+ # @param [Hash] **client_opts client initialization options; @see keyword arguments at https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:initialize
29
+ #
30
+ def initialize(grpc_client: nil, host: nil, creds: nil, default_call_options: {}, **client_opts)
31
+ if grpc_client
32
+ init_stub grpc_client
33
+ elsif host && creds
34
+ init_grpc_args(host, creds, **client_opts)
35
+ else
36
+ raise InvalidArgument, 'Must pass a Gateway::Gateway::Stub or <host>, <creds>, <client_opts>'
37
+ end
38
+ init_call_options(default_call_options)
39
+ end
40
+
41
+ #
42
+ # Submits an evaluate_request to the gateway to be evaluted.
43
+ #
44
+ # @param [Gateway::EvaluateRequest] evaluate_request
45
+ # @param [Hash] options gRPC call options (merged with default options) @see https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
46
+ #
47
+ # @return [Gateway::EvaluateResponse] evaluate_response
48
+ #
49
+ def evaluate(evaluate_request, options = {})
50
+ @grpc_client.evaluate(evaluate_request, @default_call_options[:evaluate_options].merge(options))
51
+ end
52
+
53
+ #
54
+ # Submits an endorse_request to the gateway to be evaluted.
55
+ #
56
+ # @param [Gateway::EndorseRequest] endorse_request
57
+ # @param [Hash] options gRPC call options (merged with default options) @see https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
58
+ #
59
+ # @return [Gateway::EndorseResponse] endorse_response
60
+ #
61
+ def endorse(endorse_request, options = {})
62
+ @grpc_client.endorse(endorse_request, @default_call_options[:endorse_options].merge(options))
63
+ end
64
+
65
+ #
66
+ # Submits an submit_request to the gateway to be evaluted.
67
+ #
68
+ # @param [Gateway::SubmitRequest] submit_request
69
+ # @param [Hash] options gRPC call options (merged with default options) @see https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
70
+ #
71
+ # @return [Gateway::SubmitResponse] submit_response
72
+ #
73
+ def submit(submit_request, options = {})
74
+ @grpc_client.submit(submit_request, @default_call_options[:submit_options].merge(options))
75
+ end
76
+
77
+ #
78
+ # Submits an commit_status_request to the gateway to be evaluted.
79
+ #
80
+ # @param [Gateway::CommitStatusRequest] commit_status_request
81
+ # @param [Hash] options gRPC call options (merged with default options) @see https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
82
+ #
83
+ # @return [Gateway::CommitStatusResponse] commit_status_response
84
+ #
85
+ def commit_status(commit_status_request, options = {})
86
+ @grpc_client.commit_status(commit_status_request, @default_call_options[:commit_status_options].merge(options))
87
+ end
88
+
89
+ #
90
+ # Subscribe to chaincode events
91
+ #
92
+ # @NOTE: This function has never been utilized or tested. This function is probably wrong, missing a block.
93
+ # @TODO: add testing!
94
+ #
95
+ # @param [Gateway::ChaincodeEventsRequest] chaincode_events_request
96
+ # @param [Hash] options gRPC call options (merged with default options) @see https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:server_streamer
97
+ #
98
+ # @return [Gateway::ChaincodeEventsResponse] commit_status_response (probably wrong, this is a stream.)
99
+ #
100
+ def chaincode_events(chaincode_events_request, options = {}, &block)
101
+ @grpc_client.chaincode_events(chaincode_events_request,
102
+ @default_call_options[:chaincode_events_options].merge(options), &block)
103
+ end
104
+
105
+ private
106
+
107
+ def init_stub(stub)
108
+ unless stub.is_a? ::Gateway::Gateway::Stub
109
+ raise InvalidArgument, 'Must pass a Gateway::Gateway::Stub or <host>, <creds>, <client_opts>'
110
+ end
111
+
112
+ @grpc_client = stub
113
+ end
114
+
115
+ def init_grpc_args(host, creds, **client_opts)
116
+ unless creds.is_a?(GRPC::Core::ChannelCredentials) ||
117
+ creds.is_a?(GRPC::Core::XdsChannelCredentials) ||
118
+ creds.is_a?(Symbol)
119
+ raise InvalidArgument, 'creds is not a ChannelCredentials, XdsChannelCredentials, or Symbol'
120
+ end
121
+
122
+ @grpc_client = ::Gateway::Gateway::Stub.new(host, creds, **client_opts)
123
+ end
124
+
125
+ def init_call_options(call_options)
126
+ @default_call_options = call_options
127
+ @default_call_options[:endorse_options] ||= {}
128
+ @default_call_options[:evaluate_options] ||= {}
129
+ @default_call_options[:submit_options] ||= {}
130
+ @default_call_options[:commit_status_options] ||= {}
131
+ @default_call_options[:chaincode_events_options] ||= {}
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fabric
4
+ module Constants
5
+ ## Variables
6
+ CHANNEL_HEADER_VERSION = 1
7
+ end
8
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fabric
4
+ #
5
+ # Contract represents a smart contract, and allows applications to:
6
+ #
7
+ # - Evaluate transactions that query state from the ledger using the EvaluateTransaction() method.
8
+ #
9
+ # - Submit transactions that store state to the ledger using the SubmitTransaction() method.
10
+ #
11
+ # For more complex transaction invocations, such as including transient data, transactions can be evaluated or
12
+ # submitted using the Evaluate() or Submit() methods respectively. The result of a submitted transaction can be
13
+ # accessed prior to its commit to the ledger using SubmitAsync().
14
+ #
15
+ # By default, proposal, transaction and commit status messages will be signed using the signing implementation
16
+ # specified when connecting the Gateway. In cases where an external client holds the signing credentials, a signing
17
+ # implementation can be omitted when connecting the Gateway and off-line signing can be carried out by:
18
+ #
19
+ # 1. Returning the serialized proposal, transaction or commit status along with its digest to the client for
20
+ # them to generate a signature.
21
+ #
22
+ # 2. With the serialized message and signature received from the client to create a signed proposal, transaction or
23
+ # commit using the Gateway's NewSignedProposal(), NewSignedTransaction() or NewSignedCommit() methods respectively.
24
+ #
25
+ class Contract
26
+ attr_reader :network, :chaincode_name, :contract_name
27
+
28
+ def initialize(network, chaincode_name, contract_name = '')
29
+ @network = network
30
+ @chaincode_name = chaincode_name
31
+ @contract_name = contract_name
32
+ end
33
+
34
+ def client
35
+ network.client
36
+ end
37
+
38
+ def signer
39
+ network.signer
40
+ end
41
+
42
+ def gateway
43
+ network.gateway
44
+ end
45
+
46
+ def network_name
47
+ network.name
48
+ end
49
+
50
+ #
51
+ # Evaluate a transaction function and return its results. A transaction proposal will be evaluated on endorsing
52
+ # peers but the transaction will not be sent to the ordering service and so will not be committed to the ledger.
53
+ # This can be used for querying the world state.
54
+ #
55
+ # @param [String] transaction_name
56
+ # @param [Array] arguments array of arguments to pass to the transaction
57
+ #
58
+ # @return [String] raw payload of the transaction response
59
+ #
60
+ def evaluate_transaction(transaction_name, arguments = [])
61
+ evaluate(transaction_name, { arguments: arguments })
62
+ end
63
+
64
+ #
65
+ # Submit a transaction to the ledger and return its result only after it is committed to the ledger. The
66
+ # transaction function will be evaluated on endorsing peers and then submitted to the ordering service to be
67
+ # committed to the ledger.
68
+ #
69
+ # @TODO: Not yet complete
70
+ #
71
+ # @param [String] transaction_name
72
+ # @param [Array] arguments array of arguments to pass to the transaction
73
+ #
74
+ # @return [String] raw payload of the transaction response
75
+ #
76
+ def submit_transaction(transaction_name, arguments = [])
77
+ submit(transaction_name, { arguments: arguments })
78
+ end
79
+
80
+ #
81
+ # Evaluate a transaction function and return its result. This method provides greater control over the transaction
82
+ # proposal content and the endorsing peers on which it is evaluated. This allows transaction functions to be
83
+ # evaluated where the proposal must include transient data, or that will access ledger data with key-based
84
+ # endorsement policies.
85
+ #
86
+ # @param [String] transaction_name
87
+ # @param [Hash] proposal_options
88
+ # @option proposal_options [Array] :arguments array of arguments to pass to the transaction
89
+ # @option proposal_options [Hash] :transient_data Private data passed to the transaction function but not recorded
90
+ # on the ledger.
91
+ # @option proposal_options [Array] :endorsing_organizations Specifies the set of organizations that will attempt to
92
+ # endorse the proposal.
93
+ #
94
+ # @return [String] Raw evaluation response payload
95
+ #
96
+ def evaluate(transaction_name, proposal_options = {})
97
+ new_proposal(transaction_name, **proposal_options).evaluate
98
+ end
99
+
100
+ #
101
+ # Submit a transaction to the ledger and return its result only after it is committed to the ledger. The
102
+ # transaction function will be evaluated on endorsing peers and then submitted to the ordering service to be
103
+ # committed to the ledger.
104
+ #
105
+ # @TODO: Implement Me! - LEFT OFF HERE!
106
+ #
107
+ # @param [String] transaction_name
108
+ # @param [Hash] proposal_options
109
+ # @option proposal_options [Array] :arguments array of arguments to pass to the transaction
110
+ # @option proposal_options [Hash] :transient_data Private data passed to the transaction function but not recorded
111
+ # on the ledger.
112
+ # @option proposal_options [Array] :endorsing_organizations Specifies the set of organizations that will attempt to
113
+ # endorse the proposal.
114
+ #
115
+ # @return [String] Raw evaluation response payload
116
+ #
117
+ def submit(transaction_name, proposal_options = {})
118
+ transaction = new_proposal(transaction_name, **proposal_options).endorse
119
+ submitted = transaction.submit
120
+
121
+ status = submitted.get_status
122
+
123
+ raise CommitError, status unless status.get_status == ::GRPC::Core::StatusCodes::OK
124
+ end
125
+
126
+ #
127
+ # @TODO: unimplemented, not sure if this can be implemented because
128
+ # the official grpc ruby client does not support non-blocking async
129
+ # calls (https://github.com/grpc/grpc/issues/10973)
130
+ #
131
+ # not 100% sure if grpc support is necessary for this.
132
+ #
133
+ def submit_async
134
+ raise NotYetImplemented
135
+ end
136
+
137
+ #
138
+ # Creates a transaction proposal that can be evaluated or endorsed. Supports off-line signing flow.
139
+ #
140
+ # @param [String] transaction_name transaction name (first argument unshifted into the argument array)
141
+ # @param [Array<String>] arguments array of arguments to pass to the transaction
142
+ # @param [Hash] transient_data Private data passed to the transaction function but not recorded on the ledger.
143
+ # @param [Array] endorsing_organizations Specifies the set of organizations that will attempt to endorse the
144
+ # proposal.
145
+ #
146
+ # @return [Fabric::Proposal] signed unexecuted proposal
147
+ #
148
+ def new_proposal(transaction_name, arguments: [], transient_data: {}, endorsing_organizations: [])
149
+ proposed_transaction = ProposedTransaction.new(
150
+ self,
151
+ qualified_transaction_name(transaction_name),
152
+ arguments: arguments,
153
+ transient_data: transient_data,
154
+ endorsing_organizations: endorsing_organizations
155
+ )
156
+ Proposal.new(proposed_transaction)
157
+ end
158
+
159
+ def qualified_transaction_name(transaction_name)
160
+ contract_name.nil? || contract_name.empty? ? transaction_name : "#{contract_name}:#{transaction_name}"
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,199 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ # Adapted from:
6
+ # https://github.com/kirshin/hyperledger-fabric-sdk/blob/95a5a1a37001852312df25946e960a9ff149207e/lib/fabric/crypto_suite.rb
7
+ module Fabric
8
+ #
9
+ # Elliptic-curve Crypto Suite using OpenSSL
10
+ #
11
+ # @TODO missing tests
12
+ class ECCryptoSuite # rubocop:disable Metrics/ClassLength
13
+ DEFAULT_KEY_SIZE = 256
14
+ DEFAULT_DIGEST_ALGORITHM = 'SHA256'
15
+ DEFAULT_AES_KEY_SIZE = 128
16
+
17
+ EC_CURVES = { 256 => 'prime256v1', 384 => 'secp384r1' }.freeze
18
+
19
+ CIPHER = 'aes-256-cbc'
20
+
21
+ attr_reader :key_size, :digest_algorithm, :digest_instance, :curve, :cipher
22
+
23
+ def initialize(opts = {})
24
+ @key_size = opts[:key_size] || DEFAULT_KEY_SIZE
25
+ @digest_algorithm = opts[:digest_algorithm] || DEFAULT_DIGEST_ALGORITHM
26
+ @digest_instance = OpenSSL::Digest.new digest_algorithm
27
+ @curve = EC_CURVES[key_size]
28
+ @cipher = opts[:cipher] || CIPHER
29
+ end
30
+
31
+ def sign(private_key, message)
32
+ digest = digest message
33
+ key = pkey_from_private_key private_key
34
+ signature = key.dsa_sign_asn1 digest
35
+ sequence = OpenSSL::ASN1.decode signature
36
+ sequence = prevent_malleability sequence, key.group.order
37
+
38
+ sequence.to_der
39
+ end
40
+
41
+ def verify(public_key, message, signature)
42
+ digest = digest message
43
+ openssl_pkey = openssl_pkey_from_public_key public_key
44
+ sequence = OpenSSL::ASN1.decode signature
45
+ return false unless check_malleability sequence, openssl_pkey.group.order
46
+
47
+ openssl_pkey.dsa_verify_asn1(digest, signature)
48
+ end
49
+
50
+ def generate_private_key
51
+ key = OpenSSL::PKey::EC.new curve
52
+ key.generate_key!
53
+
54
+ key.private_key.to_s(16).downcase
55
+ end
56
+
57
+ def generate_csr(private_key, attrs = [])
58
+ key = pkey_from_private_key private_key
59
+
60
+ req = OpenSSL::X509::Request.new
61
+ req.public_key = key
62
+ req.subject = OpenSSL::X509::Name.new attrs
63
+ req.sign key, @digest_instance
64
+
65
+ req
66
+ end
67
+
68
+ def generate_nonce(length = 24)
69
+ OpenSSL::Random.random_bytes length
70
+ end
71
+
72
+ def hexdigest(message)
73
+ @digest_instance.hexdigest message
74
+ end
75
+
76
+ def digest(message)
77
+ @digest_instance.digest message
78
+ end
79
+
80
+ def encode_hex(bytes)
81
+ bytes.unpack1('H*')
82
+ end
83
+
84
+ def decode_hex(string)
85
+ [string].pack('H*')
86
+ end
87
+
88
+ def restore_public_key(private_key)
89
+ private_bn = OpenSSL::BN.new private_key, 16
90
+ group = OpenSSL::PKey::EC::Group.new curve
91
+ public_bn = group.generator.mul(private_bn).to_bn
92
+ public_bn = OpenSSL::PKey::EC::Point.new(group, public_bn).to_bn
93
+
94
+ public_bn.to_s(16).downcase
95
+ end
96
+
97
+ def address_from_public_key(public_key)
98
+ bytes = decode_hex public_key
99
+ address_bytes = digest(bytes[1..])[-20..]
100
+
101
+ encode_hex address_bytes
102
+ end
103
+
104
+ def build_shared_key(private_key, public_key)
105
+ pkey = pkey_from_private_key private_key
106
+ public_bn = OpenSSL::BN.new public_key, 16
107
+ group = OpenSSL::PKey::EC::Group.new curve
108
+ public_point = OpenSSL::PKey::EC::Point.new group, public_bn
109
+
110
+ encode_hex pkey.dh_compute_key(public_point)
111
+ end
112
+
113
+ def encrypt(secret, data)
114
+ aes = OpenSSL::Cipher.new cipher
115
+ aes.encrypt
116
+ aes.key = decode_hex(secret)
117
+ iv = aes.random_iv
118
+ aes.iv = iv
119
+
120
+ Base64.strict_encode64(iv + aes.update(data) + aes.final)
121
+ end
122
+
123
+ def decrypt(secret, data)
124
+ return unless data
125
+
126
+ encrypted_data = Base64.strict_decode64 data
127
+ aes = OpenSSL::Cipher.new cipher
128
+ aes.decrypt
129
+ aes.key = decode_hex(secret)
130
+ aes.iv = encrypted_data[0..15]
131
+ encrypted_data = encrypted_data[16..]
132
+
133
+ aes.update(encrypted_data) + aes.final
134
+ end
135
+
136
+ def pkey_pem_from_private_key(private_key)
137
+ public_key = restore_public_key private_key
138
+ key = OpenSSL::PKey::EC.new curve
139
+ key.private_key = OpenSSL::BN.new private_key, 16
140
+ key.public_key = OpenSSL::PKey::EC::Point.new key.group,
141
+ OpenSSL::BN.new(public_key, 16)
142
+
143
+ pkey = OpenSSL::PKey::EC.new(key.public_key.group)
144
+ pkey.public_key = key.public_key
145
+
146
+ pkey.to_pem
147
+ end
148
+
149
+ def key_from_pem(pem)
150
+ key = OpenSSL::PKey::EC.new(pem)
151
+ key.private_key.to_s(16).downcase
152
+ end
153
+
154
+ def pkey_from_x509_certificate(certificate)
155
+ cert = OpenSSL::X509::Certificate.new(certificate)
156
+ cert.public_key.public_key.to_bn.to_s(16).downcase
157
+ end
158
+
159
+ def openssl_pkey_from_public_key(public_key)
160
+ pkey = OpenSSL::PKey::EC.new curve
161
+ pkey.public_key = OpenSSL::PKey::EC::Point.new(pkey.group, OpenSSL::BN.new(public_key, 16))
162
+
163
+ pkey
164
+ end
165
+
166
+ private
167
+
168
+ def pkey_from_private_key(private_key)
169
+ public_key = restore_public_key private_key
170
+ key = OpenSSL::PKey::EC.new curve
171
+ key.private_key = OpenSSL::BN.new private_key, 16
172
+ key.public_key = OpenSSL::PKey::EC::Point.new key.group,
173
+ OpenSSL::BN.new(public_key, 16)
174
+
175
+ key
176
+ end
177
+
178
+ # barely understand this code - this link provides a good explanation:
179
+ # http://coders-errand.com/malleability-ecdsa-signatures/
180
+ def prevent_malleability(sequence, order)
181
+ half_order = order >> 1
182
+
183
+ if (half_key = sequence.value[1].value) > half_order
184
+ sequence.value[1].value = order - half_key
185
+ end
186
+
187
+ sequence
188
+ end
189
+
190
+ # ported from python code, understanding extremely limited.
191
+ # from what I gather, sequence.value[0] and sequence.value[1]
192
+ # are the r and s values from the python implementation
193
+ # https://github.com/hyperledger/fabric-sdk-py/blob/25209f61518873da68d28313582607c29b5bae7d/hfc/util/crypto/crypto.py#L259
194
+ def check_malleability(sequence, order)
195
+ half_order = order >> 1
196
+ sequence.value[1].value <= half_order
197
+ end
198
+ end
199
+ end
@@ -1,21 +1,35 @@
1
- require "fabric/gateway/client"
2
- require "fabric/gateway/constants"
3
- require 'fabric/gateway/ec_crypto_suite'
4
- require 'fabric/gateway/identity'
5
- require "fabric/gateway/proposal"
6
- require "fabric/gateway/version"
7
-
8
-
9
- require "gateway/gateway_pb"
10
- require "gateway/gateway_services_pb"
1
+ # frozen_string_literal: true
11
2
 
12
3
  module Fabric
13
- module Gateway
14
- class Error < StandardError; end
4
+ #
5
+ # Gateway represents the connection of a specific client identity to a Fabric Gateway.
6
+ #
7
+ class Gateway
8
+ attr_reader :signer, :client
15
9
 
10
+ #
11
+ # Initialize a new Gateway
12
+ #
13
+ # @param [Fabric::Identity] signer identity utilized to sign transactions
14
+ # @param [Fabric::Client] client Gateway Client
15
+ #
16
+ def initialize(signer, client)
17
+ raise InvalidArgument, 'signer must be Fabric::Identity' unless signer.is_a? Fabric::Identity
18
+ raise InvalidArgument, 'client must be Fabric::Client' unless client.is_a? Fabric::Client
19
+
20
+ @signer = signer
21
+ @client = client
22
+ end
16
23
 
17
- def self.crypto_suite(opts = {})
18
- @crypto_suite ||= Fabric::Gateway::ECCryptoSuite.new opts
24
+ #
25
+ # Initialize new network from the Gateway
26
+ #
27
+ # @param [string] name channel name
28
+ #
29
+ # @return [Fabric::Network] returns a new network
30
+ #
31
+ def new_network(name)
32
+ Network.new(self, name)
19
33
  end
20
34
  end
21
35
  end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'msp/identities_pb'
4
+ require 'base64'
5
+
6
+ # Adapted from:
7
+ # https://github.com/kirshin/hyperledger-fabric-sdk/blob/95a5a1a37001852312df25946e960a9ff149207e/lib/fabric/identity.rb
8
+
9
+ module Fabric
10
+ #
11
+ # @attr_reader [String] private_key raw private key in hex format
12
+ # @attr_reader [String] public_key raw public key in hex format
13
+ # @attr_reader [String] certificate raw certificate in pem format
14
+ # @attr_reader [String] msp_id MSP (Membership Service Provider) Identifier
15
+ #
16
+ class Identity
17
+ attr_reader :private_key,
18
+ :public_key,
19
+ :address, # TODO: possibly unnecessary
20
+ :crypto_suite
21
+
22
+ attr_accessor :certificate, :msp_id
23
+
24
+ def initialize(private_key: nil, public_key: nil, certificate: nil, msp_id: nil, crypto_suite: nil)
25
+ @crypto_suite = crypto_suite || Fabric.crypto_suite
26
+
27
+ @private_key = private_key || @crypto_suite.generate_private_key
28
+ @public_key = public_key || @crypto_suite.restore_public_key(@private_key)
29
+ @certificate = certificate
30
+ @msp_id = msp_id
31
+
32
+ @address = @crypto_suite.address_from_public_key @public_key
33
+
34
+ return unless @certificate
35
+
36
+ raise Fabric::Error, 'Key mismatch (public_key or certificate) for identity' unless validate_key_integrity
37
+ end
38
+
39
+ #
40
+ # Validates that the private_key, public_key, and certificate are valid and match
41
+ #
42
+ # @return [boolean] true if valid, false otherwise
43
+ #
44
+ def validate_key_integrity
45
+ cert_pubkey = @crypto_suite.pkey_from_x509_certificate(certificate)
46
+ priv_pubkey = @crypto_suite.restore_public_key(@private_key)
47
+
48
+ @public_key == cert_pubkey && @public_key == priv_pubkey
49
+ end
50
+
51
+ def generate_csr(attrs = [])
52
+ @crypto_suite.generate_csr private_key, attrs
53
+ end
54
+
55
+ def sign(message)
56
+ @crypto_suite.sign(private_key, message)
57
+ end
58
+
59
+ def digest(message)
60
+ @crypto_suite.digest message
61
+ end
62
+
63
+ # TODO: Do we need this?
64
+ def shared_secret_by(public_key)
65
+ @crypto_suite.build_shared_key private_key, public_key
66
+ end
67
+
68
+ def as_proto
69
+ @as_proto ||= Msp::SerializedIdentity.new(mspid: msp_id, id_bytes: certificate)
70
+ end
71
+
72
+ def to_proto
73
+ @to_proto ||= Msp::SerializedIdentity.new(mspid: msp_id, id_bytes: certificate).to_proto
74
+ end
75
+
76
+ #
77
+ # Creates a new gateway passing in the current identity
78
+ #
79
+ # @param [Gateway::Gateway::Stub] connection <description>
80
+ #
81
+ # @return [Fabric::Gateway] <description>
82
+ #
83
+ def new_gateway(client)
84
+ Fabric::Gateway.new(self, client)
85
+ end
86
+ end
87
+ end