fabric-gateway 0.0.2 → 0.4.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/.editorconfig +3 -0
- data/.github/workflows/codeql-analysis.yml +71 -0
- data/.github/workflows/rspec.yml +37 -0
- data/.github/workflows/rubocop.yml +28 -0
- data/.github/workflows/todo.yml +10 -0
- data/.github/workflows/yardoc.yml +28 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +23 -0
- data/.ruby-version +1 -0
- data/.vscode/settings.json +7 -0
- data/.yardopts +8 -0
- data/CODE_OF_CONDUCT.md +105 -46
- data/Gemfile +5 -3
- data/LICENSE.txt +1 -1
- data/README.md +123 -12
- data/Rakefile +6 -3
- data/bin/console +4 -3
- data/bin/regenerate +1 -0
- data/bin/release +5 -0
- data/fabric-gateway.gemspec +31 -17
- data/lib/fabric/accessors/contract.rb +51 -0
- data/lib/fabric/accessors/gateway.rb +33 -0
- data/lib/fabric/accessors/network.rb +40 -0
- data/lib/fabric/client.rb +199 -0
- data/lib/fabric/constants.rb +8 -0
- data/lib/fabric/contract.rb +178 -0
- data/lib/fabric/ec_crypto_suite.rb +199 -0
- data/lib/fabric/entities/chaincode_events_requests.rb +166 -0
- data/lib/fabric/entities/envelope.rb +158 -0
- data/lib/fabric/entities/identity.rb +87 -0
- data/lib/fabric/entities/proposal.rb +189 -0
- data/lib/fabric/entities/proposed_transaction.rb +163 -0
- data/lib/fabric/entities/status.rb +32 -0
- data/lib/fabric/entities/transaction.rb +247 -0
- data/lib/fabric/gateway.rb +31 -6
- data/lib/fabric/network.rb +70 -0
- data/lib/fabric/version.rb +5 -0
- data/lib/fabric.rb +59 -0
- data/lib/msp/identities_pb.rb +25 -0
- metadata +162 -13
- data/Gemfile.lock +0 -42
- data/lib/fabric/gateway/version.rb +0 -5
@@ -0,0 +1,189 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fabric
|
4
|
+
#
|
5
|
+
# Proposal represents a transaction proposal that can be sent to peers for endorsement or evaluated as a query.
|
6
|
+
#
|
7
|
+
# Combined ProposalBuilder with Proposal. Utilizing instance variables and functions in proposal seem adaquate enough
|
8
|
+
# to fully create the proposal. ProposalBuilder did not seem like a native ruby design pattern.
|
9
|
+
class Proposal
|
10
|
+
attr_reader :proposed_transaction
|
11
|
+
|
12
|
+
#
|
13
|
+
# Instantiates a new Proposal
|
14
|
+
#
|
15
|
+
# @param [Fabric::ProposedTransaction] proposed_transaction ProposedTransaction container class
|
16
|
+
#
|
17
|
+
def initialize(proposed_transaction)
|
18
|
+
@proposed_transaction = proposed_transaction
|
19
|
+
end
|
20
|
+
|
21
|
+
def contract
|
22
|
+
@proposed_transaction.contract
|
23
|
+
end
|
24
|
+
|
25
|
+
include Fabric::Accessors::Contract
|
26
|
+
|
27
|
+
def transaction_id
|
28
|
+
proposed_transaction.transaction_id
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Returns the proposal message as a protobuf Message object.
|
33
|
+
#
|
34
|
+
# @return [Protos::Proposal|nil] Proposal message
|
35
|
+
#
|
36
|
+
def proposal
|
37
|
+
proposed_transaction.proposal
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Returns the signed proposal
|
42
|
+
#
|
43
|
+
# <rant>
|
44
|
+
# Fabric message naming scheme is a mess:
|
45
|
+
# ProposedTransaction has a Proposal which is a SignedProposal
|
46
|
+
# which has a Proposal which is a Proposal
|
47
|
+
# so.... which proposal do you want to access? Adding this function for clarity
|
48
|
+
# </rant>
|
49
|
+
#
|
50
|
+
# @return [Protos::SignedProposal|nil] SignedProposal message
|
51
|
+
#
|
52
|
+
def signed_proposal
|
53
|
+
proposed_transaction.proposed_transaction.proposal
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Serialized bytes of the proposal message in proto3 format.
|
58
|
+
#
|
59
|
+
# @return [String] Binary representation of the proposal message.
|
60
|
+
#
|
61
|
+
def to_proto
|
62
|
+
proposed_transaction.to_proto
|
63
|
+
end
|
64
|
+
|
65
|
+
#
|
66
|
+
# Proposal digest which can be utilized for offline signing.
|
67
|
+
# If signing offline, call signature= to set signature once
|
68
|
+
# computed.
|
69
|
+
#
|
70
|
+
# @return [String] raw binary digest of the proposal message.
|
71
|
+
#
|
72
|
+
def digest
|
73
|
+
Fabric.crypto_suite.digest(proposal.to_proto)
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Sets the signature of the signed proposal in the proposed transaction
|
78
|
+
#
|
79
|
+
# @param [String] signature raw byte string signature of the proposal message
|
80
|
+
# (should be the signature of the proposed message digest)
|
81
|
+
#
|
82
|
+
def signature=(signature)
|
83
|
+
proposed_transaction.signed_proposal.signature = signature
|
84
|
+
end
|
85
|
+
|
86
|
+
#
|
87
|
+
# Returns the signed proposal signature
|
88
|
+
#
|
89
|
+
# @return [String] Raw byte string signature
|
90
|
+
#
|
91
|
+
def signature
|
92
|
+
proposed_transaction.signed_proposal.signature
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# Returns true if the signed proposal has a signature
|
97
|
+
#
|
98
|
+
# @return [Boolean] true|false
|
99
|
+
#
|
100
|
+
def signed?
|
101
|
+
# signature cannot be nil because google protobuf won't let it
|
102
|
+
!proposed_transaction.signed_proposal.signature.empty?
|
103
|
+
end
|
104
|
+
|
105
|
+
#
|
106
|
+
# Utilizes the signer to sign the proposal message if it has not been signed yet.
|
107
|
+
#
|
108
|
+
def sign
|
109
|
+
return if signed?
|
110
|
+
|
111
|
+
self.signature = signer.sign proposal.to_proto
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# Evaluate the transaction proposal and obtain its result, without updating the ledger. This runs the transaction
|
116
|
+
# on a peer to obtain a transaction result, but does not submit the endorsed transaction to the orderer to be
|
117
|
+
# committed to the ledger.
|
118
|
+
#
|
119
|
+
# @param [Hash] options gRPC call options @see https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
|
120
|
+
#
|
121
|
+
# @return [String] The result returned by the transaction function
|
122
|
+
#
|
123
|
+
def evaluate(options = {})
|
124
|
+
sign
|
125
|
+
|
126
|
+
evaluate_response = client.evaluate(new_evaluate_request, options)
|
127
|
+
evaluate_response.result.payload
|
128
|
+
end
|
129
|
+
|
130
|
+
#
|
131
|
+
# Obtain endorsement for the transaction proposal from sufficient peers to allow it to be committed to the ledger.
|
132
|
+
#
|
133
|
+
# @param [Hash] options gRPC call options @see https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
|
134
|
+
#
|
135
|
+
# @return [Fabric::Transaction] An endorsed transaction that can be submitted to the ledger.
|
136
|
+
#
|
137
|
+
def endorse(options = {})
|
138
|
+
sign
|
139
|
+
endorse_response = client.endorse(new_endorse_request, options)
|
140
|
+
|
141
|
+
raise Fabric::Error, 'Missing transaction envelope' if endorse_response.prepared_transaction.nil?
|
142
|
+
|
143
|
+
prepared_transaction = new_prepared_transaction(endorse_response.prepared_transaction)
|
144
|
+
|
145
|
+
Fabric::Transaction.new(network, prepared_transaction)
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
# Generates an evaluate request from this proposal.
|
150
|
+
#
|
151
|
+
# @return [Gateway::EvaluateRequest] evaluation request with the current proposal
|
152
|
+
#
|
153
|
+
def new_evaluate_request
|
154
|
+
::Gateway::EvaluateRequest.new(
|
155
|
+
channel_id: network_name,
|
156
|
+
proposed_transaction: signed_proposal,
|
157
|
+
target_organizations: proposed_transaction.endorsing_organizations
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
#
|
162
|
+
# Creates a new endorse request from this proposal.
|
163
|
+
#
|
164
|
+
# @return [Gateway::EndorseRequest] EndorseRequest protobuf message
|
165
|
+
#
|
166
|
+
def new_endorse_request
|
167
|
+
::Gateway::EndorseRequest.new(
|
168
|
+
transaction_id: transaction_id,
|
169
|
+
channel_id: network_name,
|
170
|
+
proposed_transaction: signed_proposal,
|
171
|
+
endorsing_organizations: proposed_transaction.endorsing_organizations
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
#
|
176
|
+
# Creates a new prepared transaction from a transaction envelope.
|
177
|
+
#
|
178
|
+
# @param [Common::Envelope] envelope transaction envelope
|
179
|
+
#
|
180
|
+
# @return [Gateway::PreparedTransaction] prepared transaction protobuf message
|
181
|
+
#
|
182
|
+
def new_prepared_transaction(envelope)
|
183
|
+
::Gateway::PreparedTransaction.new(
|
184
|
+
transaction_id: transaction_id,
|
185
|
+
envelope: envelope
|
186
|
+
)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fabric
|
4
|
+
#
|
5
|
+
# Manages the instantiation and creation of the Gateway::ProposedTransaction Protobuf Message.
|
6
|
+
#
|
7
|
+
# Adapted from official fabric-gateway SDK ProposalBuilder and hyperledger-fabric-sdk:
|
8
|
+
# https://github.com/hyperledger/fabric-gateway/blob/1518e03ed3d6db1b6809e23e61a92744fd18e724/node/src/proposalbuilder.ts
|
9
|
+
# https://github.com/kirshin/hyperledger-fabric-sdk/blob/95a5a1a37001852312df25946e960a9ff149207e/lib/fabric/proposal.rb
|
10
|
+
class ProposedTransaction
|
11
|
+
attr_reader :contract,
|
12
|
+
:transaction_name,
|
13
|
+
:transient_data,
|
14
|
+
:arguments,
|
15
|
+
:proposed_transaction
|
16
|
+
|
17
|
+
# Specifies the set of organizations that will attempt to endorse the proposal.
|
18
|
+
# No other organizations' peers will be sent this proposal.
|
19
|
+
# This is usually used in conjunction with transientData for private data scenarios.
|
20
|
+
attr_reader :endorsing_organizations
|
21
|
+
|
22
|
+
# @!parse include Fabric::Accessors::Network
|
23
|
+
# @!parse include Fabric::Accessors::Gateway
|
24
|
+
include Fabric::Accessors::Contract
|
25
|
+
|
26
|
+
def initialize(contract, transaction_name, arguments: [], transient_data: {}, endorsing_organizations: [])
|
27
|
+
@contract = contract
|
28
|
+
@transaction_name = transaction_name
|
29
|
+
@arguments = arguments
|
30
|
+
@transient_data = transient_data
|
31
|
+
@endorsing_organizations = endorsing_organizations
|
32
|
+
|
33
|
+
generate_proposed_transaction
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Builds the proposed transaction protobuf message
|
38
|
+
#
|
39
|
+
# @return [Gateway::ProposedTransaction]
|
40
|
+
#
|
41
|
+
def generate_proposed_transaction
|
42
|
+
@proposed_transaction = ::Gateway::ProposedTransaction.new(
|
43
|
+
transaction_id: transaction_id,
|
44
|
+
proposal: signed_proposal,
|
45
|
+
endorsing_organizations: endorsing_organizations
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def signed_proposal
|
50
|
+
@signed_proposal ||= Protos::SignedProposal.new(
|
51
|
+
proposal_bytes: proposal.to_proto
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def proposal
|
56
|
+
@proposal ||= Protos::Proposal.new header: header.to_proto,
|
57
|
+
payload: chaincode_proposal_payload.to_proto
|
58
|
+
end
|
59
|
+
|
60
|
+
def header
|
61
|
+
Common::Header.new channel_header: channel_header.to_proto,
|
62
|
+
signature_header: signature_header.to_proto
|
63
|
+
end
|
64
|
+
|
65
|
+
def channel_header
|
66
|
+
Common::ChannelHeader.new type: Common::HeaderType::ENDORSER_TRANSACTION,
|
67
|
+
channel_id: network_name, tx_id: transaction_id,
|
68
|
+
extension: channel_header_extension.to_proto,
|
69
|
+
timestamp: timestamp, epoch: 0
|
70
|
+
# version: Constants::CHANNEL_HEADER_VERSION # official SDK does not send this.
|
71
|
+
end
|
72
|
+
|
73
|
+
def channel_header_extension
|
74
|
+
Protos::ChaincodeHeaderExtension.new chaincode_id: chaincode_id
|
75
|
+
end
|
76
|
+
|
77
|
+
def chaincode_id
|
78
|
+
Protos::ChaincodeID.new name: chaincode_name
|
79
|
+
end
|
80
|
+
|
81
|
+
def chaincode_proposal_payload
|
82
|
+
chaincode_input = Protos::ChaincodeInput.new args: [transaction_name] + arguments
|
83
|
+
chaincode_spec = Protos::ChaincodeSpec.new type: Protos::ChaincodeSpec::Type::NODE,
|
84
|
+
chaincode_id: chaincode_id,
|
85
|
+
input: chaincode_input
|
86
|
+
input = Protos::ChaincodeInvocationSpec.new chaincode_spec: chaincode_spec
|
87
|
+
|
88
|
+
Protos::ChaincodeProposalPayload.new input: input.to_proto, TransientMap: transient_data
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# Returns the current timestamp
|
93
|
+
#
|
94
|
+
# @return [Google::Protobuf::Timestamp] gRPC timestamp
|
95
|
+
#
|
96
|
+
def timestamp
|
97
|
+
now = Time.now
|
98
|
+
|
99
|
+
@timestamp ||= Google::Protobuf::Timestamp.new seconds: now.to_i, nanos: now.nsec
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# Generates a random nonce
|
104
|
+
#
|
105
|
+
# @return [String] random nonce
|
106
|
+
#
|
107
|
+
def nonce
|
108
|
+
@nonce ||= signer.crypto_suite.generate_nonce
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Generates a unique transaction ID for the transaction based on a random number and the signer
|
113
|
+
# or returns the existing transaction ID if it has already been generated.
|
114
|
+
#
|
115
|
+
# @return [String] transaction ID
|
116
|
+
#
|
117
|
+
def transaction_id
|
118
|
+
@transaction_id ||= signer.crypto_suite.hexdigest(nonce + signer.to_proto)
|
119
|
+
end
|
120
|
+
|
121
|
+
#
|
122
|
+
# Generates a SignatureHeader protobuf message from the signer and nonce
|
123
|
+
#
|
124
|
+
# @return [Common::SignatureHeader] signature header protobuf message instance
|
125
|
+
#
|
126
|
+
def signature_header
|
127
|
+
Common::SignatureHeader.new creator: signer.to_proto, nonce: nonce
|
128
|
+
end
|
129
|
+
|
130
|
+
# Dev note: if we have more classes that encapsulate protobuffer messages, consider
|
131
|
+
# creating an EncapsulatedPBMessage to hold the message and expose the following methods
|
132
|
+
# as common interface.
|
133
|
+
|
134
|
+
#
|
135
|
+
# Returns the protobuf message instance
|
136
|
+
#
|
137
|
+
# @return [Gateway::ProposedTransaction] protobuf message instance
|
138
|
+
#
|
139
|
+
def as_proto
|
140
|
+
proposed_transaction
|
141
|
+
end
|
142
|
+
|
143
|
+
#
|
144
|
+
# Returns the serialized Protobuf binary form of the proposed transaction
|
145
|
+
#
|
146
|
+
# @return [String] serialized Protobuf binary form of the proposed transaction
|
147
|
+
#
|
148
|
+
def to_proto
|
149
|
+
proposed_transaction.to_proto
|
150
|
+
end
|
151
|
+
|
152
|
+
#
|
153
|
+
# Returns the serialized JSON form of the proposed transaction
|
154
|
+
#
|
155
|
+
# @param [Hash] options JSON serialization options @see https://ruby-doc.org/stdlib-2.6.3/libdoc/json/rdoc/JSON.html#method-i-generate
|
156
|
+
#
|
157
|
+
# @return [String] serialized JSON form of the proposed transaction
|
158
|
+
#
|
159
|
+
def to_json(options = {})
|
160
|
+
proposed_transaction.to_json(options)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fabric
|
4
|
+
#
|
5
|
+
# Status of a transaction that is to be committed to the ledger.
|
6
|
+
#
|
7
|
+
class Status
|
8
|
+
TRANSACTION_STATUSES = ::Protos::TxValidationCode.constants.map(&::Protos::TxValidationCode.method(:const_get))
|
9
|
+
.collect do |i|
|
10
|
+
[::Protos::TxValidationCode.lookup(i), i]
|
11
|
+
end.to_h
|
12
|
+
|
13
|
+
# @return [Integer] Block number in which the transaction committed.
|
14
|
+
attr_reader :block_number
|
15
|
+
|
16
|
+
# @return [Integer] Transaction status
|
17
|
+
attr_reader :code
|
18
|
+
|
19
|
+
# @return [Boolean] `true` if the transaction committed successfully; otherwise `false`.
|
20
|
+
attr_reader :successful
|
21
|
+
|
22
|
+
# @return [String] The ID of the transaction.
|
23
|
+
attr_reader :transaction_id
|
24
|
+
|
25
|
+
def initialize(transaction_id, block_number, code)
|
26
|
+
@transaction_id = transaction_id
|
27
|
+
@block_number = block_number
|
28
|
+
@code = code
|
29
|
+
@successful = @code == TRANSACTION_STATUSES[:VALID]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,247 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Fabric
|
4
|
+
#
|
5
|
+
# Represents an endorsed transaction that can be submitted to the orderer for commit to the ledger,
|
6
|
+
# query the transaction results and its commit status.
|
7
|
+
#
|
8
|
+
class Transaction
|
9
|
+
attr_reader :network
|
10
|
+
|
11
|
+
include Fabric::Accessors::Network
|
12
|
+
|
13
|
+
# @return [Gateway::PreparedTransaction] Prepared Transaction
|
14
|
+
attr_reader :prepared_transaction
|
15
|
+
|
16
|
+
# @return [Fabric::Envelope]
|
17
|
+
attr_reader :envelope
|
18
|
+
|
19
|
+
#
|
20
|
+
# Creates a new Transaction instance.
|
21
|
+
#
|
22
|
+
# @param [Fabric::Network] network
|
23
|
+
# @param [Gateway::PreparedTransaction] prepared_transaction
|
24
|
+
#
|
25
|
+
def initialize(network, prepared_transaction)
|
26
|
+
@network = network
|
27
|
+
@prepared_transaction = prepared_transaction
|
28
|
+
@envelope = Envelope.new(prepared_transaction.envelope)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Get the transaction result. This is obtained during the endorsement process when the transaction proposal is
|
33
|
+
# run on endorsing peers.
|
34
|
+
#
|
35
|
+
# @param [boolean] check_status set to true to raise exception if transaction has not yet been committed
|
36
|
+
#
|
37
|
+
# @return [String] Raw transaction result
|
38
|
+
#
|
39
|
+
def result(check_status: true)
|
40
|
+
raise Fabric::CommitError, status if check_status && !status.successful
|
41
|
+
|
42
|
+
envelope.result
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Returns the transaction ID from the prepared transaction.
|
47
|
+
#
|
48
|
+
# @return [String] transaction_id
|
49
|
+
#
|
50
|
+
def transaction_id
|
51
|
+
prepared_transaction.transaction_id
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Submit the transaction to the orderer to be committed to the ledger.
|
56
|
+
#
|
57
|
+
# @see https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
|
58
|
+
#
|
59
|
+
# @param [Hash] options gRPC call options
|
60
|
+
#
|
61
|
+
# @return [Fabric::Transaction] self
|
62
|
+
def submit(options = {})
|
63
|
+
sign_submit_request
|
64
|
+
|
65
|
+
client.submit(new_submit_request, options)
|
66
|
+
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
#
|
71
|
+
# Sign the transaction envelope.
|
72
|
+
#
|
73
|
+
# @return [void]
|
74
|
+
def sign_submit_request
|
75
|
+
return if submit_request_signed?
|
76
|
+
|
77
|
+
signature = signer.sign(envelope.payload_bytes)
|
78
|
+
self.submit_request_signature = signature
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Returns true if the transaction envelope has been signed.
|
83
|
+
#
|
84
|
+
# @return [Boolean] true if signed; false otherwise
|
85
|
+
#
|
86
|
+
def submit_request_signed?
|
87
|
+
@envelope.signed?
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Digest to be signed to support offline signing of the submit request
|
92
|
+
#
|
93
|
+
# @return [String] digest of the submit request
|
94
|
+
#
|
95
|
+
def submit_request_digest
|
96
|
+
envelope.payload_digest
|
97
|
+
end
|
98
|
+
|
99
|
+
#
|
100
|
+
# Sets the submit request signature. This is used to support offline signing of the submit request.
|
101
|
+
#
|
102
|
+
# @param [String] signature
|
103
|
+
#
|
104
|
+
# @return [void]
|
105
|
+
#
|
106
|
+
def submit_request_signature=(signature)
|
107
|
+
envelope.signature = signature
|
108
|
+
end
|
109
|
+
|
110
|
+
#
|
111
|
+
# Get status of the committed transaction. If the transaction has not yet committed, this method blocks until the
|
112
|
+
# commit occurs. If status is already queried, this returns status from cache and does not make additional queries.
|
113
|
+
#
|
114
|
+
# @see https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
|
115
|
+
#
|
116
|
+
# @param [Hash] options gRPC call options
|
117
|
+
#
|
118
|
+
# @return [Fabric::Status] status of the committed transaction
|
119
|
+
#
|
120
|
+
def status(options = {})
|
121
|
+
@status ||= query_status(options)
|
122
|
+
end
|
123
|
+
|
124
|
+
#
|
125
|
+
# Digest to be signed to support offline signing of the commit status request
|
126
|
+
#
|
127
|
+
# @return [String] digest of the commit status request
|
128
|
+
#
|
129
|
+
def status_request_digest
|
130
|
+
Fabric.crypto_suite.digest(signed_commit_status_request.request)
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# Sets the status request signature. This is used to support offline signing of the commit status request.
|
135
|
+
#
|
136
|
+
# @param [String] signature
|
137
|
+
#
|
138
|
+
# @return [void]
|
139
|
+
#
|
140
|
+
def status_request_signature=(signature)
|
141
|
+
signed_commit_status_request.signature = signature
|
142
|
+
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# Returns true if the signed commit status request has been signed.
|
146
|
+
#
|
147
|
+
# @return [Boolean] true if signed; false otherwise
|
148
|
+
#
|
149
|
+
def status_request_signed?
|
150
|
+
!signed_commit_status_request.signature.empty?
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# Sign the signed commit status request
|
155
|
+
#
|
156
|
+
# @return [Fabric::Transaction] self
|
157
|
+
#
|
158
|
+
def sign_status_request
|
159
|
+
return if status_request_signed?
|
160
|
+
|
161
|
+
signature = signer.sign(signed_commit_status_request.request)
|
162
|
+
signed_commit_status_request.signature = signature
|
163
|
+
|
164
|
+
self
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# Returns the current instance of the signed commit status request. Necessary so we can keep the state of the
|
169
|
+
# signature in the transaction object.
|
170
|
+
#
|
171
|
+
# @return [Gateway::SignedCommitStatusRequest] signed commit status request
|
172
|
+
#
|
173
|
+
def signed_commit_status_request
|
174
|
+
@signed_commit_status_request ||= new_signed_commit_status_request
|
175
|
+
end
|
176
|
+
|
177
|
+
private
|
178
|
+
|
179
|
+
#
|
180
|
+
# Actual status query call used by status method.
|
181
|
+
#
|
182
|
+
# @see https://www.rubydoc.info/gems/grpc/GRPC%2FClientStub:request_response
|
183
|
+
#
|
184
|
+
# @param [Hash] options gRPC call options
|
185
|
+
#
|
186
|
+
# @return [Fabric::Status] status of the committed transaction
|
187
|
+
#
|
188
|
+
def query_status(options = {})
|
189
|
+
sign_status_request
|
190
|
+
|
191
|
+
commit_status_response = client.commit_status(signed_commit_status_request, options)
|
192
|
+
new_status(commit_status_response)
|
193
|
+
end
|
194
|
+
|
195
|
+
#
|
196
|
+
# Generates a new signed commit status request
|
197
|
+
#
|
198
|
+
# @return [Gateway::SignedCommitStatusRequest] signed commit status request protobuf message
|
199
|
+
#
|
200
|
+
def new_signed_commit_status_request
|
201
|
+
::Gateway::SignedCommitStatusRequest.new(
|
202
|
+
request: new_commit_status_request.to_proto
|
203
|
+
)
|
204
|
+
end
|
205
|
+
|
206
|
+
#
|
207
|
+
# Generates a new commit status request
|
208
|
+
#
|
209
|
+
# @return [Gateway::CommitStatusRequest] commit status request protobuf message
|
210
|
+
#
|
211
|
+
def new_commit_status_request
|
212
|
+
::Gateway::CommitStatusRequest.new(
|
213
|
+
channel_id: network_name,
|
214
|
+
transaction_id: transaction_id,
|
215
|
+
identity: signer.to_proto
|
216
|
+
)
|
217
|
+
end
|
218
|
+
|
219
|
+
#
|
220
|
+
# Generates a new submit request.
|
221
|
+
#
|
222
|
+
# @return [Gateway::SubmitRequest] submit request protobuf message
|
223
|
+
#
|
224
|
+
def new_submit_request
|
225
|
+
::Gateway::SubmitRequest.new(
|
226
|
+
transaction_id: transaction_id,
|
227
|
+
channel_id: network_name,
|
228
|
+
prepared_transaction: envelope.envelope
|
229
|
+
)
|
230
|
+
end
|
231
|
+
|
232
|
+
#
|
233
|
+
# New Status from CommitStatusResponse
|
234
|
+
#
|
235
|
+
# @param [Gateway::CommitStatusResponse] response commit status response
|
236
|
+
#
|
237
|
+
# @return [Fabric::Status] transaction status
|
238
|
+
#
|
239
|
+
def new_status(response)
|
240
|
+
Fabric::Status.new(
|
241
|
+
transaction_id,
|
242
|
+
response.block_number,
|
243
|
+
Fabric::Status::TRANSACTION_STATUSES[response.result]
|
244
|
+
)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
data/lib/fabric/gateway.rb
CHANGED
@@ -1,10 +1,35 @@
|
|
1
|
-
|
2
|
-
require "gateway/gateway_pb"
|
3
|
-
require "gateway/gateway_services_pb"
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
3
|
module Fabric
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
#
|
5
|
+
# Gateway represents the connection of a specific client identity to a Fabric Gateway.
|
6
|
+
#
|
7
|
+
class Gateway
|
8
|
+
attr_reader :signer, :client
|
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
|
23
|
+
|
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)
|
33
|
+
end
|
9
34
|
end
|
10
35
|
end
|