hyperledger-fabric-sdk 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/crypto_suite/ecdsa_aes.rb +86 -0
- data/lib/fabric/channel.rb +54 -0
- data/lib/fabric/client.rb +45 -0
- data/lib/fabric/configuration.rb +25 -0
- data/lib/fabric/constants.rb +18 -0
- data/lib/fabric/identity.rb +36 -0
- data/lib/fabric/orderer.rb +19 -0
- data/lib/fabric/peer.rb +58 -0
- data/lib/fabric/peer_endorser.rb +85 -0
- data/lib/fabric/transaction_id.rb +11 -0
- data/lib/fabric/user.rb +24 -0
- data/lib/fabric/version.rb +3 -0
- data/lib/fabric_ca/client.rb +51 -0
- data/lib/fabric_ca/configuration.rb +25 -0
- data/lib/fabric_ca/connection.rb +32 -0
- data/lib/fabric_ca/error.rb +11 -0
- data/lib/fabric_ca/faraday_middleware/basic_auth.rb +33 -0
- data/lib/fabric_ca/faraday_middleware/raise_http_exception.rb +47 -0
- data/lib/fabric_ca/faraday_middleware/token_auth.rb +38 -0
- data/lib/fabric_ca/request.rb +50 -0
- data/lib/fabric_ca/response.rb +10 -0
- data/lib/fabric_ca/tools.rb +35 -0
- data/lib/fabric_ca/version.rb +3 -0
- data/lib/hyperledger-fabric-sdk.rb +47 -0
- data/lib/protos/common/collection_pb.rb +42 -0
- data/lib/protos/common/common_pb.rb +106 -0
- data/lib/protos/common/configtx_pb.rb +71 -0
- data/lib/protos/common/configuration_pb.rb +33 -0
- data/lib/protos/common/ledger_pb.rb +16 -0
- data/lib/protos/common/policies_pb.rb +52 -0
- data/lib/protos/discovery/protocol_pb.rb +119 -0
- data/lib/protos/discovery/protocol_services_pb.rb +30 -0
- data/lib/protos/gossip/message_pb.rb +233 -0
- data/lib/protos/gossip/message_services_pb.rb +31 -0
- data/lib/protos/idemix/idemix_pb.rb +93 -0
- data/lib/protos/ledger/queryresult/kv_query_result_pb.rb +24 -0
- data/lib/protos/ledger/rwset/kvrwset/kv_rwset_pb.rb +85 -0
- data/lib/protos/ledger/rwset/rwset_pb.rb +46 -0
- data/lib/protos/msp/identities_pb.rb +23 -0
- data/lib/protos/msp/msp_config_pb.rb +72 -0
- data/lib/protos/msp/msp_principal_pb.rb +54 -0
- data/lib/protos/orderer/ab_pb.rb +52 -0
- data/lib/protos/orderer/ab_services_pb.rb +41 -0
- data/lib/protos/orderer/configuration_pb.rb +32 -0
- data/lib/protos/orderer/kafka_pb.rb +45 -0
- data/lib/protos/peer/admin_pb.rb +41 -0
- data/lib/protos/peer/admin_services_pb.rb +33 -0
- data/lib/protos/peer/chaincode_event_pb.rb +17 -0
- data/lib/protos/peer/chaincode_pb.rb +60 -0
- data/lib/protos/peer/chaincode_shim_pb.rb +94 -0
- data/lib/protos/peer/chaincode_shim_services_pb.rb +30 -0
- data/lib/protos/peer/configuration_pb.rb +27 -0
- data/lib/protos/peer/events_pb.rb +98 -0
- data/lib/protos/peer/events_services_pb.rb +59 -0
- data/lib/protos/peer/peer_pb.rb +21 -0
- data/lib/protos/peer/peer_services_pb.rb +37 -0
- data/lib/protos/peer/proposal_pb.rb +40 -0
- data/lib/protos/peer/proposal_response_pb.rb +35 -0
- data/lib/protos/peer/query_pb.rb +32 -0
- data/lib/protos/peer/resources_pb.rb +34 -0
- data/lib/protos/peer/signed_cc_dep_spec_pb.rb +17 -0
- data/lib/protos/peer/transaction_pb.rb +72 -0
- data/lib/protos/transientstore/transientstore_pb.rb +18 -0
- metadata +123 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8868d76ed70adc4cedf983ad30718b1964bdd4a4a06291bc336d7381bc152a74
|
4
|
+
data.tar.gz: 97809db15dee6a3f8ac534a63eb3933895e8f0fa0ebd255b10569124e5ff06c4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e4c1c49bdc093de69d552136954eacf30f0e92128222f5e878d760e71dd94ac08720ad832e8172014fb2363ce507963fe1099f1c7589878875c412704063de8f
|
7
|
+
data.tar.gz: e91abaeeee3053694b18362ab2fff01132267782ae6fdc0e01ed8bcc04540ff565fa70ae6bd5e4931068664f8ae51230008792e3a9cf1592c23ba5284bead9e0
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module CryptoSuite
|
4
|
+
class ECDSA_AES
|
5
|
+
DEFAULT_KEY_SIZE = 256.freeze
|
6
|
+
DEFAULT_NONCE_LENGTH = 24.freeze
|
7
|
+
DEFAULT_DIGEST_ALGORITHM = 'SHA256'.freeze
|
8
|
+
DEFAULT_AES_KEY_SIZE = 128.freeze
|
9
|
+
|
10
|
+
EC_CURVES = { 256 => 'prime256v1', 384 => 'secp384r1' }.freeze
|
11
|
+
|
12
|
+
attr_reader :key_size, :nonce_length, :digest_algorithm, :digest, :cipher
|
13
|
+
|
14
|
+
def initialize(opts = {})
|
15
|
+
@key_size = opts[:key_size] || DEFAULT_KEY_SIZE
|
16
|
+
@nonce_length = opts[:nonce_length] || DEFAULT_NONCE_LENGTH
|
17
|
+
@digest_algorithm = opts[:digest_algorithm] || DEFAULT_DIGEST_ALGORITHM
|
18
|
+
@digest = OpenSSL::Digest.new(digest_algorithm)
|
19
|
+
@cipher = OpenSSL::Cipher::AES.new(opts[:aes_key_size] || DEFAULT_AES_KEY_SIZE, :CBC)
|
20
|
+
end
|
21
|
+
|
22
|
+
def generate_nonce
|
23
|
+
OpenSSL::Random.random_bytes nonce_length
|
24
|
+
end
|
25
|
+
|
26
|
+
def hexdigest(message)
|
27
|
+
@digest.hexdigest message
|
28
|
+
end
|
29
|
+
|
30
|
+
def digest(message)
|
31
|
+
@digest.digest message
|
32
|
+
end
|
33
|
+
|
34
|
+
def sign(private_key, msg)
|
35
|
+
key = OpenSSL::PKey::EC.new private_key
|
36
|
+
signature = key.dsa_sign_asn1 msg
|
37
|
+
sequence = OpenSSL::ASN1.decode signature
|
38
|
+
sequence = prevent_malleability sequence, key.group.order
|
39
|
+
|
40
|
+
sequence.to_der
|
41
|
+
end
|
42
|
+
|
43
|
+
def generate_private_key
|
44
|
+
key = OpenSSL::PKey::EC.new EC_CURVES[key_size]
|
45
|
+
key.generate_key!
|
46
|
+
|
47
|
+
key.to_pem
|
48
|
+
end
|
49
|
+
|
50
|
+
def generate_csr(private_key, options = [])
|
51
|
+
key = OpenSSL::PKey::EC.new private_key
|
52
|
+
req = OpenSSL::X509::Request.new
|
53
|
+
req.public_key = key
|
54
|
+
req.subject = OpenSSL::X509::Name.new(options)
|
55
|
+
req.sign key, @digest
|
56
|
+
|
57
|
+
req
|
58
|
+
end
|
59
|
+
|
60
|
+
def encrypt(key, iv, message)
|
61
|
+
cipher.encrypt key, iv
|
62
|
+
|
63
|
+
Base64.strict_encode64(cipher.update(message) + cipher.final)
|
64
|
+
end
|
65
|
+
|
66
|
+
def decrypt(key, iv, message)
|
67
|
+
cipher.decrypt key, iv
|
68
|
+
|
69
|
+
cipher.update(Base64.strict_decode64(message)) + cipher.final
|
70
|
+
rescue OpenSSL::Cipher::CipherError
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def prevent_malleability(sequence, order)
|
77
|
+
half_order = order >> 1
|
78
|
+
|
79
|
+
if (half_key = sequence.value[1].value) > half_order
|
80
|
+
sequence.value[1].value = order - half_key
|
81
|
+
end
|
82
|
+
|
83
|
+
sequence
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'peer/transaction_pb'
|
2
|
+
|
3
|
+
module Fabric
|
4
|
+
class Channel
|
5
|
+
attr_reader :identity_context, :name, :peers, :orderers
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@identity_context = args[:identity_context]
|
9
|
+
@name = args[:name]
|
10
|
+
@peers = args[:peers] || []
|
11
|
+
@orderers = args[:orderers] || []
|
12
|
+
end
|
13
|
+
|
14
|
+
def query_by_chaincode(request)
|
15
|
+
request[:targets] ||= peers
|
16
|
+
request[:transaction] = TransactionID.new identity_context
|
17
|
+
|
18
|
+
PeerEndorser.send_transaction_proposal request, name, identity_context
|
19
|
+
end
|
20
|
+
|
21
|
+
def send_transaction(request, &block)
|
22
|
+
header = Common::Header.decode request[:proposal].header
|
23
|
+
chaincode_action_payload = build_chaincode_action request
|
24
|
+
|
25
|
+
transaction_action = Protos::TransactionAction.new header: header.signature_header,
|
26
|
+
payload: chaincode_action_payload.to_proto
|
27
|
+
transaction = Protos::Transaction.new actions: [transaction_action]
|
28
|
+
|
29
|
+
payload = Common::Payload.new header: header,
|
30
|
+
data: transaction.to_proto
|
31
|
+
|
32
|
+
envelope = Common::Envelope.new signature: identity_context.identity.sign(payload.to_proto),
|
33
|
+
payload: payload.to_proto
|
34
|
+
|
35
|
+
orderers.first.send_broadcast envelope, &block
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def build_chaincode_action(request)
|
41
|
+
responses = request[:responses].select { |response| response.response.status == 200 }
|
42
|
+
endorsements = responses.map { |response| response.endorsement }
|
43
|
+
chaincode_endorser_action =
|
44
|
+
Protos::ChaincodeEndorsedAction.new proposal_response_payload: responses.first.payload,
|
45
|
+
endorsements: endorsements
|
46
|
+
|
47
|
+
payload = Protos::ChaincodeProposalPayload.decode request[:proposal].payload
|
48
|
+
payload_no_trans = Protos::ChaincodeProposalPayload.new input: payload.input
|
49
|
+
|
50
|
+
Protos::ChaincodeActionPayload.new action: chaincode_endorser_action,
|
51
|
+
chaincode_proposal_payload: payload_no_trans.to_proto
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Fabric
|
2
|
+
class Client
|
3
|
+
attr_reader :identity_context, :orderers, :peers, :channels
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
options = Fabric.options.merge(options)
|
7
|
+
|
8
|
+
@identity_context = options[:identity_context]
|
9
|
+
@orderers = {}
|
10
|
+
@channels = {}
|
11
|
+
@peers = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
def config
|
15
|
+
conf = {}
|
16
|
+
Configuration::VALID_OPTIONS_KEYS.each do |key|
|
17
|
+
conf[key] = public_send key
|
18
|
+
end
|
19
|
+
|
20
|
+
conf
|
21
|
+
end
|
22
|
+
|
23
|
+
def new_channel(name)
|
24
|
+
channels[name] ||= Channel.new name: name,
|
25
|
+
orderers: orderers.values , peers: peers.values,
|
26
|
+
identity_context: identity_context
|
27
|
+
|
28
|
+
channels[name]
|
29
|
+
end
|
30
|
+
|
31
|
+
def new_peer(url, opts = {})
|
32
|
+
peers[url] ||= Peer.new url: url, opts: opts,
|
33
|
+
identity_context: identity_context
|
34
|
+
|
35
|
+
peers[url]
|
36
|
+
end
|
37
|
+
|
38
|
+
def new_orderer(url, opts = {})
|
39
|
+
orderers[url] ||= Orderer.new url: url, opts: opts,
|
40
|
+
identity_context: identity_context
|
41
|
+
|
42
|
+
orderers[url]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Fabric
|
2
|
+
module Configuration
|
3
|
+
VALID_OPTIONS_KEYS = [:identity_context, :orderers, :peers]
|
4
|
+
|
5
|
+
attr_accessor *VALID_OPTIONS_KEYS
|
6
|
+
|
7
|
+
def self.extended(base)
|
8
|
+
base.reset
|
9
|
+
end
|
10
|
+
|
11
|
+
def configure
|
12
|
+
yield self
|
13
|
+
end
|
14
|
+
|
15
|
+
def options
|
16
|
+
VALID_OPTIONS_KEYS.inject({}) do |option, key|
|
17
|
+
option.merge!(key => send(key))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset
|
22
|
+
VALID_OPTIONS_KEYS.each {|key| send("#{key}=", nil)}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Fabric
|
2
|
+
module Constants
|
3
|
+
## System Chaincodes
|
4
|
+
LSCC = 'lscc'.freeze
|
5
|
+
QSCC = 'qscc'.freeze
|
6
|
+
CSCC = 'cscc'.freeze
|
7
|
+
|
8
|
+
## System Channels
|
9
|
+
SYSTEM_CHANNEL_NAME = 'testchainid'.freeze
|
10
|
+
|
11
|
+
## System Functions
|
12
|
+
FUNC_GET_CHANNELS = 'GetChannels'.freeze
|
13
|
+
FUNC_GET_CONFIG_BLOCK = 'GetConfigBlock'.freeze
|
14
|
+
|
15
|
+
## Variables
|
16
|
+
CHANNEL_HEADER_VERSION = 1.freeze
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'msp/identities_pb'
|
2
|
+
|
3
|
+
module Fabric
|
4
|
+
class Identity
|
5
|
+
attr_reader :certificate, :public_key, :private_key, :msp_id, :crypto_suite,
|
6
|
+
:key, :iv
|
7
|
+
|
8
|
+
def initialize(args)
|
9
|
+
@certificate = args[:certificate]
|
10
|
+
@public_key = args[:public_key]
|
11
|
+
@private_key = args[:private_key]
|
12
|
+
@msp_id = args[:msp_id]
|
13
|
+
@crypto_suite = args[:crypto_suite]
|
14
|
+
@key = args[:key].to_s
|
15
|
+
@iv = args[:iv].to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def serialize
|
19
|
+
Msp::SerializedIdentity.new(mspid: msp_id, id_bytes: certificate).to_proto
|
20
|
+
end
|
21
|
+
|
22
|
+
def sign(message)
|
23
|
+
digest = crypto_suite.digest message
|
24
|
+
|
25
|
+
crypto_suite.sign private_key, digest
|
26
|
+
end
|
27
|
+
|
28
|
+
def encrypt(message)
|
29
|
+
crypto_suite.encrypt key, iv, message
|
30
|
+
end
|
31
|
+
|
32
|
+
def decrypt(message)
|
33
|
+
crypto_suite.decrypt key, iv, message
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'orderer/ab_services_pb.rb'
|
2
|
+
|
3
|
+
module Fabric
|
4
|
+
class Orderer
|
5
|
+
attr_reader :url, :opts, :client
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@url = args[:url]
|
9
|
+
@opts = args[:opts]
|
10
|
+
@client = ::Orderer::AtomicBroadcast::Stub.new url, :this_channel_is_insecure
|
11
|
+
end
|
12
|
+
|
13
|
+
def send_broadcast(envelope)
|
14
|
+
client.broadcast([envelope]) do |response|
|
15
|
+
yield response if block_given?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/fabric/peer.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'peer/query_pb'
|
2
|
+
|
3
|
+
module Fabric
|
4
|
+
class Peer
|
5
|
+
attr_reader :url, :opts, :identity_context, :client
|
6
|
+
|
7
|
+
def initialize(args)
|
8
|
+
@url = args[:url]
|
9
|
+
@opts = args[:opts]
|
10
|
+
@identity_context = args[:identity_context]
|
11
|
+
@client = Protos::Endorser::Stub.new url, :this_channel_is_insecure
|
12
|
+
end
|
13
|
+
|
14
|
+
def send_process_proposal(proposal)
|
15
|
+
client.process_proposal proposal
|
16
|
+
end
|
17
|
+
|
18
|
+
def query_channels
|
19
|
+
request = {
|
20
|
+
targets: [self],
|
21
|
+
chaincode_id: Constants::CSCC,
|
22
|
+
transaction: TransactionID.new(identity_context),
|
23
|
+
function: Constants::FUNC_GET_CHANNELS,
|
24
|
+
args: []
|
25
|
+
}
|
26
|
+
|
27
|
+
responses = PeerEndorser.send_transaction_proposal request, '', identity_context
|
28
|
+
response = responses.first
|
29
|
+
|
30
|
+
Protos::ChannelQueryResponse.decode response.response.payload
|
31
|
+
end
|
32
|
+
|
33
|
+
def query_config_block(channel_id)
|
34
|
+
request = {
|
35
|
+
targets: [self],
|
36
|
+
chaincode_id: Constants::CSCC,
|
37
|
+
transaction: TransactionID.new(identity_context),
|
38
|
+
function: Constants::FUNC_GET_CONFIG_BLOCK,
|
39
|
+
args: [channel_id]
|
40
|
+
}
|
41
|
+
|
42
|
+
responses = PeerEndorser.send_transaction_proposal request, '', identity_context
|
43
|
+
proposal_response = responses.first
|
44
|
+
|
45
|
+
extract_config_enveloper proposal_response
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def extract_config_enveloper(proposal_response)
|
51
|
+
block = Common::Block.decode proposal_response.response.payload
|
52
|
+
envelope = Common::Envelope.decode block.data.data.first
|
53
|
+
payload = Common::Payload.decode envelope.payload
|
54
|
+
|
55
|
+
Common::ConfigEnvelope.decode payload.data
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'peer/peer_services_pb'
|
2
|
+
|
3
|
+
module Fabric
|
4
|
+
class PeerEndorser
|
5
|
+
def self.send_transaction_proposal(request, channel_id, identity_context)
|
6
|
+
args = request[:args].unshift request[:function]
|
7
|
+
|
8
|
+
transaction = request[:transaction]
|
9
|
+
|
10
|
+
channel_header = build_channel_header Common::HeaderType::ENDORSER_TRANSACTION,
|
11
|
+
channel_id, transaction.id, request[:chaincode_id]
|
12
|
+
header = build_header channel_header, identity_context.identity, transaction.nonce
|
13
|
+
proposal = build_proposal header, request[:chaincode_id], args
|
14
|
+
signed_proposal = sign_proposal identity_context.identity, proposal
|
15
|
+
|
16
|
+
responses = send_peers_process_proposal request[:targets], signed_proposal
|
17
|
+
|
18
|
+
{ responses: responses, proposal: proposal, header: header }
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def self.build_header(channel_header, identity, nonce)
|
24
|
+
signature_header = Common::SignatureHeader.new creator: identity.serialize,
|
25
|
+
nonce: nonce
|
26
|
+
|
27
|
+
Common::Header.new channel_header: channel_header.to_proto,
|
28
|
+
signature_header: signature_header.to_proto
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.build_channel_header(type, channel_id, tx_id, chaincode_id)
|
32
|
+
attrs = { type: type, channel_id: channel_id, tx_id: tx_id }
|
33
|
+
attrs[:extension] = build_channel_header_extension(chaincode_id).to_proto if chaincode_id
|
34
|
+
attrs[:timestamp] = build_current_timestamp
|
35
|
+
attrs[:version] = Constants::CHANNEL_HEADER_VERSION
|
36
|
+
|
37
|
+
Common::ChannelHeader.new attrs
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.build_channel_header_extension(chaincode_id)
|
41
|
+
id = Protos::ChaincodeID.new name: chaincode_id
|
42
|
+
|
43
|
+
Protos::ChaincodeHeaderExtension.new chaincode_id: id
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.build_current_timestamp
|
47
|
+
now = Time.current
|
48
|
+
|
49
|
+
Google::Protobuf::Timestamp.new seconds: now.to_i, nanos: now.sec
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.build_proposal(header, chaincode_id, args)
|
53
|
+
chaincode_proposal = build_chaincode_proposal chaincode_id, args
|
54
|
+
|
55
|
+
Protos::Proposal.new header: header.to_proto,
|
56
|
+
payload: chaincode_proposal.to_proto
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.build_chaincode_proposal(chaincode_id, args)
|
60
|
+
id = Protos::ChaincodeID.new name: chaincode_id
|
61
|
+
chaincode_input = Protos::ChaincodeInput.new args: args
|
62
|
+
chaincode_spec = Protos::ChaincodeSpec.new type: Protos::ChaincodeSpec::Type::GOLANG,
|
63
|
+
chaincode_id: id,
|
64
|
+
input: chaincode_input
|
65
|
+
chaincode_invocation_spec =
|
66
|
+
Protos::ChaincodeInvocationSpec.new chaincode_spec: chaincode_spec
|
67
|
+
|
68
|
+
Protos::ChaincodeProposalPayload.new input: chaincode_invocation_spec.to_proto
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.sign_proposal(identity, proposal)
|
72
|
+
proposal_bytes = proposal.to_proto
|
73
|
+
signature = identity.sign(proposal_bytes)
|
74
|
+
|
75
|
+
Protos::SignedProposal.new proposal_bytes: proposal_bytes,
|
76
|
+
signature: signature
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.send_peers_process_proposal(peers, proposal)
|
80
|
+
peers.map do |peer|
|
81
|
+
peer.send_process_proposal(proposal)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|