github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/bddtests/steps/bootstrap_util.py (about) 1 # Copyright IBM Corp. 2016 All Rights Reserved. 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 # 15 16 import time 17 import sys 18 import hashlib 19 import uuid 20 21 if sys.version_info < (3, 6): 22 import sha3 23 24 from OpenSSL import crypto 25 from OpenSSL import rand 26 import ecdsa 27 28 from collections import namedtuple 29 from itertools import groupby 30 31 from enum import Enum 32 33 from google.protobuf import timestamp_pb2 34 from common import common_pb2 as common_dot_common_pb2 35 from common import configtx_pb2 as common_dot_configtx_pb2 36 from common import configuration_pb2 as common_dot_configuration_pb2 37 from common import policies_pb2 as common_dot_policies_pb2 38 from msp import msp_config_pb2, msp_principal_pb2, identities_pb2 39 from peer import configuration_pb2 as peer_dot_configuration_pb2 40 from orderer import configuration_pb2 as orderer_dot_configuration_pb2 41 import orderer_util 42 43 from contexthelper import ContextHelper 44 45 import os 46 import re 47 import shutil 48 import compose 49 50 # Type to represent tuple of user, nodeName, ogranization 51 NodeAdminTuple = namedtuple("NodeAdminTuple", ['user', 'nodeName', 'organization']) 52 53 54 ApplicationGroup = "Application" 55 OrdererGroup = "Orderer" 56 ConsortiumsGroup = "Consortiums" 57 MSPKey = "MSP" 58 toValue = lambda message: message.SerializeToString() 59 60 61 class Network(Enum): 62 Orderer = 1 63 Peer = 2 64 65 66 def GetUUID(): 67 return compose.Composition.GetUUID() 68 69 def GetUniqueChannelName(): 70 while True: 71 guiid = uuid.uuid4().hex[:6].lower() 72 if guiid[:1].isalpha(): 73 break 74 return guiid 75 76 def createRSAKey(): 77 # Create RSA key, 2048 bit 78 pk = crypto.PKey() 79 pk.generate_key(crypto.TYPE_RSA, 2048) 80 assert pk.check() == True 81 return pk 82 83 84 def createECDSAKey(curve=ecdsa.NIST256p): 85 # Create ECDSA key 86 sk = ecdsa.SigningKey.generate(curve=curve) 87 return sk 88 89 90 def computeCryptoHash(data): 91 ' This will currently return 128 hex characters' 92 # s = hashlib.sha3_256() 93 s = hashlib.sha256() 94 # s = hashlib.shake_256() 95 #return s.digest(64) 96 s.update(data) 97 return s.digest() 98 99 100 def createCertRequest(pkey, extensions=[], digest="sha256", **name): 101 """ 102 Create a certificate request. 103 Arguments: pkey - The key to associate with the request 104 digest - Digestion method to use for signing, default is sha256 105 **name - The name of the subject of the request, possible 106 arguments are: 107 C - Country name 108 ST - State or province name 109 L - Locality name 110 O - Organization name 111 OU - Organizational unit name 112 CN - Common name 113 emailAddress - E-mail address 114 Returns: The certificate request in an X509Req object 115 """ 116 req = crypto.X509Req() 117 subj = req.get_subject() 118 119 for key, value in name.items(): 120 setattr(subj, key, value) 121 122 req.add_extensions(extensions) 123 124 req.set_pubkey(pkey) 125 req.sign(pkey, digest) 126 return req 127 128 129 def createCertificate(req, issuerCertKey, serial, validityPeriod, digest="sha256", isCA=False, extensions=[]): 130 """ 131 Generate a certificate given a certificate request. 132 Arguments: req - Certificate request to use 133 issuerCert - The certificate of the issuer 134 issuerKey - The private key of the issuer 135 serial - Serial number for the certificate 136 notBefore - Timestamp (relative to now) when the certificate 137 starts being valid 138 notAfter - Timestamp (relative to now) when the certificate 139 stops being valid 140 digest - Digest method to use for signing, default is sha256 141 Returns: The signed certificate in an X509 object 142 """ 143 issuerCert, issuerKey = issuerCertKey 144 notBefore, notAfter = validityPeriod 145 cert = crypto.X509() 146 cert.set_version(3) 147 cert.set_serial_number(serial) 148 cert.gmtime_adj_notBefore(notBefore) 149 cert.gmtime_adj_notAfter(notAfter) 150 cert.set_issuer(issuerCert.get_subject()) 151 cert.set_subject(req.get_subject()) 152 cert.set_pubkey(req.get_pubkey()) 153 if isCA: 154 cert.add_extensions([crypto.X509Extension("basicConstraints", True, 155 "CA:TRUE, pathlen:0"), 156 crypto.X509Extension("subjectKeyIdentifier", False, "hash", 157 subject=cert)]) 158 #TODO: This only is appropriate for root self signed!!!! 159 cert.add_extensions([crypto.X509Extension("authorityKeyIdentifier", False, "keyid:always", issuer=cert)]) 160 else: 161 cert.add_extensions([crypto.X509Extension("basicConstraints", True, 162 "CA:FALSE"), 163 crypto.X509Extension("subjectKeyIdentifier", False, "hash", 164 subject=cert)]) 165 cert.add_extensions([crypto.X509Extension("authorityKeyIdentifier", False, "keyid:always", issuer=issuerCert)]) 166 167 cert.add_extensions(extensions) 168 cert.sign(issuerKey, digest) 169 return cert 170 171 172 # SUBJECT_DEFAULT = {countryName : "US", stateOrProvinceName : "NC", localityName : "RTP", organizationName : "IBM", organizationalUnitName : "Blockchain"} 173 174 class Entity: 175 def __init__(self, name, ecdsaSigningKey, rsaSigningKey): 176 self.name = name 177 # Create a ECDSA key, then a crypto pKey from the DER for usage with cert requests, etc. 178 self.ecdsaSigningKey = ecdsaSigningKey 179 self.rsaSigningKey = rsaSigningKey 180 if self.ecdsaSigningKey: 181 self.pKey = crypto.load_privatekey(crypto.FILETYPE_ASN1, self.ecdsaSigningKey.to_der()) 182 # Signing related ecdsa config 183 self.hashfunc = hashlib.sha256 184 self.sigencode = ecdsa.util.sigencode_der_canonize 185 self.sigdecode = ecdsa.util.sigdecode_der 186 187 def createCertRequest(self, nodeName, extensions = []): 188 req = createCertRequest(self.pKey, extensions=extensions, C="US", ST="North Carolina", L="RTP", O="IBM", CN=nodeName) 189 return req 190 191 def createTLSCertRequest(self, nodeName): 192 req = createCertRequest(self.rsaSigningKey, C="US", ST="North Carolina", L="RTP", O="IBM", CN=nodeName) 193 # print("request => {0}".format(crypto.dump_certificate_request(crypto.FILETYPE_PEM, req))) 194 return req 195 196 197 def computeHash(self, data): 198 s = self.hashfunc() 199 s.update(data) 200 return s.digest() 201 202 def sign(self, dataAsBytearray): 203 return self.ecdsaSigningKey.sign(dataAsBytearray, hashfunc=self.hashfunc, sigencode=self.sigencode) 204 205 def verifySignature(self, signature, signersCert, data): 206 'Will verify the signature of an entity based upon public cert' 207 vk = ecdsa.VerifyingKey.from_der(crypto.dump_publickey(crypto.FILETYPE_ASN1, signersCert.get_pubkey())) 208 assert vk.verify(signature, data, hashfunc=self.hashfunc, sigdecode=self.sigdecode), "Invalid signature!!" 209 210 def getPrivateKeyAsPEM(self): 211 return self.ecdsaSigningKey.to_pem() 212 213 def __getstate__(self): 214 state = dict(self.__dict__) 215 del state['ecdsaSigningKey'] 216 del state['rsaSigningKey'] 217 del state['pKey'] 218 return state 219 220 class User(Entity, orderer_util.UserRegistration): 221 def __init__(self, name, directory, ecdsaSigningKey, rsaSigningKey): 222 Entity.__init__(self, name, ecdsaSigningKey=ecdsaSigningKey, rsaSigningKey=rsaSigningKey) 223 orderer_util.UserRegistration.__init__(self, name, directory) 224 self.tags = {} 225 226 def setTagValue(self, tagKey, tagValue, overwrite=False): 227 if tagKey in self.tags: 228 assert not overwrite,"TagKey '{0}' already exists for user {1}, and did not provide overwrite=True".format(tagKey, self.getUserName()) 229 self.tags[tagKey] = tagValue 230 return tagValue 231 232 def getTagValue(self, tagKey): 233 return self.tags[tagKey] 234 235 def cleanup(self): 236 self.closeStreams() 237 238 239 class Organization(Entity): 240 241 def __init__(self, name, ecdsaSigningKey, rsaSigningKey): 242 Entity.__init__(self, name, ecdsaSigningKey, rsaSigningKey) 243 req = createCertRequest(self.pKey, C="US", ST="North Carolina", L="RTP", O="IBM", CN=name) 244 numYrs = 1 245 self.signedCert = createCertificate(req, (req, self.pKey), 1000, (0, 60 * 60 * 24 * 365 * numYrs), isCA=True) 246 # Which networks this organization belongs to 247 self.networks = [] 248 249 def __repr__(self): 250 return "name:{0}, networks: {1}".format(self.name, self.networks) 251 252 def getSelfSignedCert(self): 253 return self.signedCert 254 255 def getCertAsPEM(self): 256 return crypto.dump_certificate(crypto.FILETYPE_PEM, self.getSelfSignedCert()) 257 258 def isInNetwork(self, network): 259 for n in self.networks: 260 if str(n)==str(network): 261 return True 262 return False 263 264 def getMspPrincipalAsRole(self, mspRoleTypeAsString): 265 mspRole = msp_principal_pb2.MSPRole(msp_identifier=self.name, role=msp_principal_pb2.MSPRole.MSPRoleType.Value(mspRoleTypeAsString)) 266 mspPrincipal = msp_principal_pb2.MSPPrincipal( 267 principal_classification=msp_principal_pb2.MSPPrincipal.Classification.Value('ROLE'), 268 principal=mspRole.SerializeToString()) 269 return mspPrincipal 270 271 def createCertificate(self, certReq, extensions=[]): 272 numYrs = 1 273 return createCertificate(certReq, (self.signedCert, self.pKey), 1000, (0, 60 * 60 * 24 * 365 * numYrs), extensions=extensions) 274 275 def addToNetwork(self, network): 276 'Used to track which network this organization is defined in.' 277 # assert network in Network, 'Network not recognized ({0}), expected to be one of ({1})'.format(network, list(Network)) 278 if not network in self.networks: 279 self.networks.append(network) 280 281 282 class Directory: 283 def __init__(self): 284 import atexit 285 self.organizations = {} 286 self.users = {} 287 self.ordererAdminTuples = {} 288 atexit.register(self.cleanup) 289 290 def getNamedCtxTuples(self): 291 return self.ordererAdminTuples 292 293 def _registerOrg(self, orgName): 294 assert orgName not in self.organizations, "Organization already registered {0}".format(orgName) 295 self.organizations[orgName] = Organization(orgName, ecdsaSigningKey = createECDSAKey(), rsaSigningKey = createRSAKey()) 296 return self.organizations[orgName] 297 298 def _registerUser(self, userName): 299 assert userName not in self.users, "User already registered {0}".format(userName) 300 self.users[userName] = User(userName, directory=self, ecdsaSigningKey = createECDSAKey(), rsaSigningKey = createRSAKey()) 301 return self.users[userName] 302 303 def getUser(self, userName, shouldCreate=False): 304 if not userName in self.users and shouldCreate: 305 # self.users[userName] = User(userName) 306 self._registerUser(userName) 307 return self.users[userName] 308 309 def getUsers(self): 310 return self.users 311 312 def cleanup(self): 313 '''Perform cleanup of resources''' 314 [user.cleanup() for user in self.users.values()] 315 316 def getOrganization(self, orgName, shouldCreate=False): 317 if not orgName in self.organizations and shouldCreate: 318 # self.organizations[orgName] = Organization(orgName) 319 self._registerOrg(orgName) 320 return self.organizations[orgName] 321 322 def getOrganizations(self): 323 return self.organizations 324 325 def findCertByTuple(self, userName, contextName, orgName): 326 ordererAdminTuple = NodeAdminTuple(user=userName, nodeName=contextName, organization=orgName) 327 return self.ordererAdminTuples[ordererAdminTuple] 328 329 def findCertForNodeAdminTuple(self, nodeAdminTuple): 330 assert nodeAdminTuple in self.ordererAdminTuples, "Node admin tuple not found for: {0}".format(nodeAdminTuple) 331 return self.ordererAdminTuples[nodeAdminTuple] 332 333 def getCertAsPEM(self, nodeAdminTuple): 334 assert nodeAdminTuple in self.ordererAdminTuples, "Node admin tuple not found for: {0}".format(nodeAdminTuple) 335 return crypto.dump_certificate(crypto.FILETYPE_PEM, self.ordererAdminTuples[nodeAdminTuple]) 336 337 def findNodeAdminTuple(self, userName, contextName, orgName): 338 nodeAdminTuple = NodeAdminTuple(user=userName, nodeName=contextName, organization=orgName) 339 assert nodeAdminTuple in self.ordererAdminTuples, "Node admin tuple not found for: {0}".format(nodeAdminTuple) 340 return nodeAdminTuple 341 342 def getTrustedRootsForPeerNetworkAsPEM(self): 343 pems = [peerOrg.getCertAsPEM() for peerOrg in [org for org in self.getOrganizations().values() if org.isInNetwork(Network.Peer)]] 344 return "".join(pems) 345 346 def getTrustedRootsForOrdererNetworkAsPEM(self): 347 pems = [ordererOrg.getCertAsPEM() for ordererOrg in [org for org in self.getOrganizations().values() if org.isInNetwork(Network.Orderer)]] 348 return "".join(pems) 349 350 351 def _get_cert_extensions_ip_sans(self, user_name, node_name): 352 extensions = [] 353 if 'signer' in user_name.lower(): 354 if ('peer' in node_name.lower() or 'orderer' in node_name.lower()): 355 san_list = ["DNS:{0}".format(node_name)] 356 extensions.append(crypto.X509Extension(b"subjectAltName", False, ", ".join(san_list))) 357 return extensions 358 359 def registerOrdererAdminTuple(self, userName, ordererName, organizationName): 360 ' Assign the user as orderer admin' 361 ordererAdminTuple = NodeAdminTuple(user=userName, nodeName=ordererName, organization=organizationName) 362 assert ordererAdminTuple not in self.ordererAdminTuples, "Orderer admin tuple already registered {0}".format( 363 ordererAdminTuple) 364 assert organizationName in self.organizations, "Orderer Organization not defined {0}".format(organizationName) 365 366 user = self.getUser(userName, shouldCreate=True) 367 # Add the subjectAlternativeName if the current entity is a signer, and the nodeName contains peer or orderer 368 extensions = self._get_cert_extensions_ip_sans(userName, ordererName) 369 certReq = user.createCertRequest(ordererAdminTuple.nodeName, extensions=extensions) 370 userCert = self.getOrganization(organizationName).createCertificate(certReq, extensions=extensions) 371 372 # Verify the newly created certificate 373 store = crypto.X509Store() 374 # Assuming a list of trusted certs 375 for trustedCert in [self.getOrganization(organizationName).signedCert]: 376 store.add_cert(trustedCert) 377 # Create a certificate context using the store and the certificate to verify 378 store_ctx = crypto.X509StoreContext(store, userCert) 379 # Verify the certificate, returns None if it can validate the certificate 380 store_ctx.verify_certificate() 381 self.ordererAdminTuples[ordererAdminTuple] = userCert 382 return ordererAdminTuple 383 384 def dump(self, output): 385 'Will dump the directory to the provided store' 386 import cPickle 387 data = {'users' : {}, 'organizations' : {}, 'nats' : {}} 388 dump_cert = lambda cert: crypto.dump_certificate(crypto.FILETYPE_PEM, cert) 389 for userName, user in self.users.iteritems(): 390 # for k, v in user.tags.iteritems(): 391 # try: 392 # cPickle.dumps(v) 393 # except: 394 # raise Exception("Failed on key {0}".format(k)) 395 data['users'][userName] = (user.ecdsaSigningKey.to_pem(), crypto.dump_privatekey(crypto.FILETYPE_PEM, user.rsaSigningKey), user.tags) 396 for orgName, org in self.organizations.iteritems(): 397 networks = [n.name for n in org.networks] 398 data['organizations'][orgName] = ( 399 org.ecdsaSigningKey.to_pem(), crypto.dump_privatekey(crypto.FILETYPE_PEM, org.rsaSigningKey), 400 dump_cert(org.getSelfSignedCert()), networks) 401 for nat, cert in self.ordererAdminTuples.iteritems(): 402 data['nats'][nat] = dump_cert(cert) 403 cPickle.dump(data, output) 404 405 def initFromPath(self, path): 406 'Will initialize the directory from the path supplied' 407 import cPickle 408 data = None 409 with open(path,'r') as f: 410 data = cPickle.load(f) 411 assert data != None, "Expected some data, did not load any." 412 priv_key_from_pem = lambda x: crypto.load_privatekey(crypto.FILETYPE_PEM, x) 413 for userName, keyTuple in data['users'].iteritems(): 414 self.users[userName] = User(userName, directory=self, 415 ecdsaSigningKey=ecdsa.SigningKey.from_pem(keyTuple[0]), 416 rsaSigningKey=priv_key_from_pem(keyTuple[1])) 417 self.users[userName].tags = keyTuple[2] 418 for orgName, tuple in data['organizations'].iteritems(): 419 org = Organization(orgName, ecdsaSigningKey=ecdsa.SigningKey.from_pem(tuple[0]), 420 rsaSigningKey=priv_key_from_pem(tuple[0])) 421 org.signedCert = crypto.load_certificate(crypto.FILETYPE_PEM, tuple[2]) 422 org.networks = [Network[name] for name in tuple[3]] 423 self.organizations[orgName] = org 424 for nat, cert_as_pem in data['nats'].iteritems(): 425 self.ordererAdminTuples[nat] = crypto.load_certificate(crypto.FILETYPE_PEM, cert_as_pem) 426 427 class AuthDSLHelper: 428 @classmethod 429 def Envelope(cls, signaturePolicy, identities): 430 'Envelope builds an envelope message embedding a SignaturePolicy' 431 return common_dot_policies_pb2.SignaturePolicyEnvelope( 432 version=0, 433 rule=signaturePolicy, 434 identities=identities) 435 436 @classmethod 437 def NOutOf(cls, n, policies): 438 'NOutOf creates a policy which requires N out of the slice of policies to evaluate to true' 439 return common_dot_policies_pb2.SignaturePolicy( 440 n_out_of=common_dot_policies_pb2.SignaturePolicy.NOutOf( 441 n=n, 442 rules=policies, 443 ), 444 ) 445 446 @classmethod 447 def SignedBy(cls, index): 448 'NOutOf creates a policy which requires N out of the slice of policies to evaluate to true' 449 return common_dot_policies_pb2.SignaturePolicy( 450 signed_by=index 451 ) 452 453 class BootstrapHelper: 454 KEY_CONSENSUS_TYPE = "ConsensusType" 455 KEY_ORDERER_KAFKA_BROKERS = "KafkaBrokers" 456 KEY_CHAIN_CREATION_POLICY_NAMES = "ChainCreationPolicyNames" 457 KEY_CHANNEL_CREATION_POLICY = "ChannelCreationPolicy" 458 KEY_CHANNEL_RESTRICTIONS = "ChannelRestrictions" 459 KEY_CONSORTIUM = "Consortium" 460 KEY_ACCEPT_ALL_POLICY = "AcceptAllPolicy" 461 KEY_HASHING_ALGORITHM = "HashingAlgorithm" 462 KEY_BLOCKDATA_HASHING_STRUCTURE = "BlockDataHashingStructure" 463 KEY_BATCH_SIZE = "BatchSize" 464 KEY_BATCH_TIMEOUT = "BatchTimeout" 465 KEY_CREATIONPOLICY = "CreationPolicy" 466 KEY_MSP_INFO = "MSP" 467 KEY_ANCHOR_PEERS = "AnchorPeers" 468 469 KEY_NEW_CONFIGURATION_ITEM_POLICY = "NewConfigurationItemPolicy" 470 DEFAULT_CHAIN_CREATORS = [KEY_ACCEPT_ALL_POLICY] 471 472 # ReadersPolicyKey is the key used for the read policy 473 KEY_POLICY_READERS = "Readers" 474 # WritersPolicyKey is the key used for the writer policy 475 KEY_POLICY_WRITERS = "Writers" 476 # AdminsPolicyKey is the key used for the admins policy 477 KEY_POLICY_ADMINS = "Admins" 478 479 KEY_POLICY_BLOCK_VALIDATION = "BlockValidation" 480 481 # OrdererAddressesKey is the cb.ConfigItem type key name for the OrdererAddresses message 482 KEY_ORDERER_ADDRESSES = "OrdererAddresses" 483 484 485 DEFAULT_NONCE_SIZE = 24 486 487 @classmethod 488 def getNonce(cls): 489 return rand.bytes(BootstrapHelper.DEFAULT_NONCE_SIZE) 490 491 @classmethod 492 def addSignatureToSignedConfigItem(cls, configUpdateEnvelope, (entity, mspId, cert)): 493 serializedIdentity = identities_pb2.SerializedIdentity(mspid=mspId, id_bytes=crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) 494 sigHeader = common_dot_common_pb2.SignatureHeader(creator=serializedIdentity.SerializeToString(), 495 nonce=BootstrapHelper.getNonce()) 496 sigHeaderBytes = sigHeader.SerializeToString() 497 # Signature over the concatenation of configurationItem bytes and signatureHeader bytes 498 signature = entity.sign(sigHeaderBytes + configUpdateEnvelope.config_update) 499 # Now add new signature to Signatures repeated field 500 newConfigSig = configUpdateEnvelope.signatures.add() 501 newConfigSig.signature_header = sigHeaderBytes 502 newConfigSig.signature = signature 503 504 def __init__(self, chainId, lastModified=0, msgVersion=1, epoch=0, consensusType="solo", batchSize=10, 505 batchTimeout=".5s", absoluteMaxBytes=100000000, preferredMaxBytes=512 * 1024, signers=[]): 506 self.chainId = str(chainId) 507 self.lastModified = lastModified 508 self.msgVersion = msgVersion 509 self.epoch = epoch 510 self.consensusType = consensusType 511 self.batchSize = batchSize 512 self.batchTimeout = batchTimeout 513 self.absoluteMaxBytes = absoluteMaxBytes 514 self.preferredMaxBytes = preferredMaxBytes 515 self.signers = signers 516 517 def makeChainHeader(self, type, txID="", extension='', 518 version=1, 519 timestamp=timestamp_pb2.Timestamp(seconds=int(time.time()), nanos=0)): 520 return common_dot_common_pb2.ChannelHeader(type=type, 521 version=version, 522 timestamp=timestamp, 523 channel_id=self.chainId, 524 epoch=self.epoch, 525 tx_id=txID, 526 extension=extension) 527 528 def makeSignatureHeader(self, serializeCertChain, nonce): 529 return common_dot_common_pb2.SignatureHeader(creator=serializeCertChain, 530 nonce=nonce) 531 532 def signConfigItem(self, configItem): 533 # signedConfigItem = common_dot_configuration_pb2.SignedConfigurationItem( 534 # ConfigurationItem=configItem.SerializeToString(), Signatures=None) 535 # return signedConfigItem 536 return configItem 537 538 def getConfigItem(self, commonConfigType, key, value): 539 configItem = common_dot_configtx_pb2.ConfigItem( 540 version=self.lastModified, 541 configPath=commonConfigType, 542 key=key, 543 mod_policy=BootstrapHelper.KEY_NEW_CONFIGURATION_ITEM_POLICY, 544 value=value) 545 return configItem 546 547 def computeBlockDataHash(self, blockData): 548 return computeCryptoHash(blockData.SerializeToString()) 549 550 def getDirectory(context): 551 if 'bootstrapDirectory' not in context: 552 context.bootstrapDirectory = Directory() 553 return context.bootstrapDirectory 554 555 556 def getOrdererBootstrapAdmin(context, shouldCreate=False): 557 directory = getDirectory(context) 558 ordererBootstrapAdmin = directory.getUser(userName="ordererBootstrapAdmin", shouldCreate=shouldCreate) 559 return ordererBootstrapAdmin 560 561 562 def addOrdererBootstrapAdminOrgReferences(context, policyName, orgNames): 563 'Adds a key/value pair of policyName/[orgName,...]' 564 directory = getDirectory(context) 565 ordererBootstrapAdmin = directory.getUser(userName="ordererBootstrapAdmin", shouldCreate=False) 566 if not 'OrgReferences' in ordererBootstrapAdmin.tags: 567 ordererBootstrapAdmin.tags['OrgReferences'] = {} 568 policyNameToOrgNamesDict = ordererBootstrapAdmin.tags['OrgReferences'] 569 assert not policyName in policyNameToOrgNamesDict, "PolicyName '{0}' already registered with ordererBootstrapAdmin".format( 570 policyName) 571 policyNameToOrgNamesDict[policyName] = orgNames 572 return policyNameToOrgNamesDict 573 574 575 def getOrdererBootstrapAdminOrgReferences(context): 576 directory = getDirectory(context) 577 ordererBootstrapAdmin = directory.getUser(userName="ordererBootstrapAdmin", shouldCreate=False) 578 if not 'OrgReferences' in ordererBootstrapAdmin.tags: 579 ordererBootstrapAdmin.tags['OrgReferences'] = {} 580 return ordererBootstrapAdmin.tags['OrgReferences'] 581 582 def getAnchorPeersConfigGroup(context, nodeAdminTuples, peer_port=7051, mod_policy=BootstrapHelper.KEY_POLICY_ADMINS): 583 directory = getDirectory(context) 584 config_group = common_dot_configtx_pb2.ConfigGroup() 585 for orgName, group in groupby([(nat.organization, nat) for nat in nodeAdminTuples], lambda x: x[0]): 586 anchorPeers = peer_dot_configuration_pb2.AnchorPeers() 587 for (k,nodeAdminTuple) in group: 588 anchorPeer = anchorPeers.anchor_peers.add() 589 anchorPeer.host = nodeAdminTuple.nodeName 590 anchorPeer.port = peer_port 591 # anchorPeer.cert = crypto.dump_certificate(crypto.FILETYPE_PEM, 592 # directory.findCertForNodeAdminTuple(nodeAdminTuple)) 593 config_group.groups[ApplicationGroup].groups[orgName].values[BootstrapHelper.KEY_ANCHOR_PEERS].value=toValue(anchorPeers) 594 config_group.groups[ApplicationGroup].groups[orgName].values[BootstrapHelper.KEY_ANCHOR_PEERS].mod_policy = BootstrapHelper.KEY_POLICY_ADMINS 595 return config_group 596 597 def setDefaultPoliciesForOrgs(channel, orgs, group_name, version=0, policy_version=0): 598 for org in orgs: 599 groupName = group_name 600 channel.groups[groupName].groups[org.name].version=version 601 channel.groups[groupName].groups[org.name].mod_policy = BootstrapHelper.KEY_POLICY_ADMINS 602 mspPrincipalForMemberRole = org.getMspPrincipalAsRole(mspRoleTypeAsString='MEMBER') 603 signedBy = AuthDSLHelper.SignedBy(0) 604 605 memberSignaturePolicyEnvelope = AuthDSLHelper.Envelope(signaturePolicy=AuthDSLHelper.NOutOf(1, [signedBy]), identities=[mspPrincipalForMemberRole]) 606 memberPolicy = common_dot_policies_pb2.Policy( 607 type=common_dot_policies_pb2.Policy.PolicyType.Value("SIGNATURE"), 608 value=memberSignaturePolicyEnvelope.SerializeToString()) 609 channel.groups[groupName].groups[org.name].policies[BootstrapHelper.KEY_POLICY_READERS].version=policy_version 610 channel.groups[groupName].groups[org.name].policies[BootstrapHelper.KEY_POLICY_READERS].policy.CopyFrom(memberPolicy) 611 channel.groups[groupName].groups[org.name].policies[BootstrapHelper.KEY_POLICY_WRITERS].version=policy_version 612 channel.groups[groupName].groups[org.name].policies[BootstrapHelper.KEY_POLICY_WRITERS].policy.CopyFrom(memberPolicy) 613 614 mspPrincipalForAdminRole = org.getMspPrincipalAsRole(mspRoleTypeAsString='ADMIN') 615 adminSignaturePolicyEnvelope = AuthDSLHelper.Envelope(signaturePolicy=AuthDSLHelper.NOutOf(1, [signedBy]), identities=[mspPrincipalForAdminRole]) 616 adminPolicy = common_dot_policies_pb2.Policy( 617 type=common_dot_policies_pb2.Policy.PolicyType.Value("SIGNATURE"), 618 value=adminSignaturePolicyEnvelope.SerializeToString()) 619 channel.groups[groupName].groups[org.name].policies[BootstrapHelper.KEY_POLICY_ADMINS].version=policy_version 620 channel.groups[groupName].groups[org.name].policies[BootstrapHelper.KEY_POLICY_ADMINS].policy.CopyFrom(adminPolicy) 621 622 for pKey, pVal in channel.groups[groupName].groups[org.name].policies.iteritems(): 623 pVal.mod_policy = BootstrapHelper.KEY_POLICY_ADMINS 624 625 # signaturePolicyEnvelope = AuthDSLHelper.Envelope(signaturePolicy=AuthDSLHelper.SignedBy(0), identities=[mspPrincipal]) 626 627 628 629 def createChannelConfigGroup(directory, service_names, hashingAlgoName="SHA256", consensusType="solo", batchTimeout="1s", batchSizeMaxMessageCount=10, batchSizeAbsoluteMaxBytes=100000000, batchSizePreferredMaxBytes=512 * 1024, channel_max_count=0): 630 631 channel = common_dot_configtx_pb2.ConfigGroup() 632 # channel.groups[ApplicationGroup] = common_dot_configtx_pb2.ConfigGroup() 633 # channel.groups[OrdererGroup] = common_dot_configtx_pb2.ConfigGroup() 634 # channel.groups[ApplicationGroup] 635 channel.groups[OrdererGroup] 636 # v = common_dot_configtx_pb2.ConfigItem.ConfigType.Value 637 # configItems.append(bootstrapHelper.encodeHashingAlgorithm()) 638 channel.values[BootstrapHelper.KEY_HASHING_ALGORITHM].value = toValue( 639 common_dot_configuration_pb2.HashingAlgorithm(name=hashingAlgoName)) 640 641 golangMathMaxUint32 = 4294967295 642 channel.values[BootstrapHelper.KEY_BLOCKDATA_HASHING_STRUCTURE].value = toValue( 643 common_dot_configuration_pb2.BlockDataHashingStructure(width=golangMathMaxUint32)) 644 645 channel.groups[OrdererGroup].values[BootstrapHelper.KEY_BATCH_SIZE].value = toValue(orderer_dot_configuration_pb2.BatchSize(maxMessageCount=batchSizeMaxMessageCount,absoluteMaxBytes=batchSizeAbsoluteMaxBytes,preferredMaxBytes=batchSizePreferredMaxBytes)) 646 channel.groups[OrdererGroup].values[BootstrapHelper.KEY_BATCH_TIMEOUT].value = toValue(orderer_dot_configuration_pb2.BatchTimeout(timeout=batchTimeout)) 647 channel.groups[OrdererGroup].values[BootstrapHelper.KEY_CONSENSUS_TYPE].value = toValue(orderer_dot_configuration_pb2.ConsensusType(type=consensusType)) 648 channel.groups[OrdererGroup].values[BootstrapHelper.KEY_CHANNEL_RESTRICTIONS].value = toValue(orderer_dot_configuration_pb2.ChannelRestrictions(max_count=channel_max_count)) 649 650 651 acceptAllPolicy = common_dot_policies_pb2.Policy(type=1, value=AuthDSLHelper.Envelope( 652 signaturePolicy=AuthDSLHelper.NOutOf(0, []), identities=[]).SerializeToString()) 653 # channel.policies[BootstrapHelper.KEY_ACCEPT_ALL_POLICY].policy.CopyFrom(acceptAllPolicy) 654 655 # For now, setting same policies for each 'Non-Org' group 656 typeImplicitMeta = common_dot_policies_pb2.Policy.PolicyType.Value("IMPLICIT_META") 657 Policy = common_dot_policies_pb2.Policy 658 IMP = common_dot_policies_pb2.ImplicitMetaPolicy 659 ruleAny = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("ANY") 660 ruleMajority = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("MAJORITY") 661 for group in [channel, channel.groups[OrdererGroup]]: 662 group.policies[BootstrapHelper.KEY_POLICY_READERS].policy.CopyFrom(Policy(type=typeImplicitMeta, value=IMP( 663 rule=ruleAny, sub_policy=BootstrapHelper.KEY_POLICY_READERS).SerializeToString())) 664 group.policies[BootstrapHelper.KEY_POLICY_WRITERS].policy.CopyFrom(Policy(type=typeImplicitMeta, value=IMP( 665 rule=ruleAny, sub_policy=BootstrapHelper.KEY_POLICY_WRITERS).SerializeToString())) 666 group.policies[BootstrapHelper.KEY_POLICY_ADMINS].policy.CopyFrom(Policy(type=typeImplicitMeta, value=IMP( 667 rule=ruleMajority, sub_policy=BootstrapHelper.KEY_POLICY_ADMINS).SerializeToString())) 668 for pKey, pVal in group.policies.iteritems(): 669 pVal.mod_policy = BootstrapHelper.KEY_POLICY_ADMINS 670 671 # Setting block validation policy for the orderer group 672 channel.groups[OrdererGroup].policies[BootstrapHelper.KEY_POLICY_BLOCK_VALIDATION].policy.CopyFrom(Policy(type=typeImplicitMeta, value=IMP( 673 rule=ruleAny, sub_policy=BootstrapHelper.KEY_POLICY_WRITERS).SerializeToString())) 674 channel.groups[OrdererGroup].policies[BootstrapHelper.KEY_POLICY_BLOCK_VALIDATION].mod_policy=BootstrapHelper.KEY_POLICY_ADMINS 675 676 # Add the orderer org groups MSPConfig info 677 for ordererOrg in [org for org in directory.getOrganizations().values() if Network.Orderer in org.networks]: 678 channel.groups[OrdererGroup].groups[ordererOrg.name].values[BootstrapHelper.KEY_MSP_INFO].value = toValue( 679 getMSPConfig(org=ordererOrg, directory=directory)) 680 channel.groups[OrdererGroup].groups[ordererOrg.name].values[BootstrapHelper.KEY_MSP_INFO].mod_policy=BootstrapHelper.KEY_POLICY_ADMINS 681 682 #Kafka specific 683 kafka_brokers = ["{0}:9092".format(service_name) for service_name in service_names if "kafka" in service_name] 684 if len(kafka_brokers) > 0: 685 channel.groups[OrdererGroup].values[BootstrapHelper.KEY_ORDERER_KAFKA_BROKERS].value = toValue( 686 orderer_dot_configuration_pb2.KafkaBrokers(brokers=kafka_brokers)) 687 688 for vKey, vVal in channel.groups[OrdererGroup].values.iteritems(): 689 vVal.mod_policy=BootstrapHelper.KEY_POLICY_ADMINS 690 691 692 693 # Now set policies for each org group (Both peer and orderer) 694 #TODO: Revisit after Jason does a bit more refactoring on chain creation policy enforcement 695 ordererOrgs = [o for o in directory.getOrganizations().values() if Network.Orderer in o.networks] 696 setDefaultPoliciesForOrgs(channel, ordererOrgs , OrdererGroup, version=0, policy_version=0) 697 698 #New OrdererAddress 699 ordererAddress = common_dot_configuration_pb2.OrdererAddresses() 700 for orderer_service_name in [service_name for service_name in service_names if "orderer" in service_name]: 701 ordererAddress.addresses.append("{0}:7050".format(orderer_service_name)) 702 assert len(ordererAddress.addresses) > 0, "No orderer nodes were found while trying to create channel ConfigGroup" 703 channel.values[BootstrapHelper.KEY_ORDERER_ADDRESSES].value = toValue(ordererAddress) 704 705 706 for pKey, pVal in channel.values.iteritems(): 707 pVal.mod_policy = BootstrapHelper.KEY_POLICY_ADMINS 708 709 710 return channel 711 712 def createEnvelopeForMsg(directory, nodeAdminTuple, chainId, msg, typeAsString): 713 # configEnvelope = common_dot_configtx_pb2.ConfigEnvelope(last_update=envelope.SerializeToString()) 714 bootstrapHelper = BootstrapHelper(chainId=chainId) 715 payloadChainHeader = bootstrapHelper.makeChainHeader( 716 type=common_dot_common_pb2.HeaderType.Value(typeAsString)) 717 718 # Now the SignatureHeader 719 org = directory.getOrganization(nodeAdminTuple.organization) 720 user = directory.getUser(nodeAdminTuple.user) 721 cert = directory.findCertForNodeAdminTuple(nodeAdminTuple) 722 serializedIdentity = identities_pb2.SerializedIdentity(mspid=org.name, id_bytes=crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) 723 serializedCreatorCertChain = serializedIdentity.SerializeToString() 724 nonce = None 725 payloadSignatureHeader = common_dot_common_pb2.SignatureHeader( 726 creator=serializedCreatorCertChain, 727 nonce=bootstrapHelper.getNonce(), 728 ) 729 730 payloadHeader = common_dot_common_pb2.Header( 731 channel_header=payloadChainHeader.SerializeToString(), 732 signature_header=payloadSignatureHeader.SerializeToString(), 733 ) 734 payload = common_dot_common_pb2.Payload(header=payloadHeader, data=msg.SerializeToString()) 735 payloadBytes = payload.SerializeToString() 736 envelope = common_dot_common_pb2.Envelope(payload=payloadBytes, signature=user.sign(payloadBytes)) 737 return envelope 738 739 return configEnvelope 740 741 742 def createNewConfigUpdateEnvelope(channelConfig, chainId, readset_version=0): 743 read_set = common_dot_configtx_pb2.ConfigGroup() 744 read_set.values[BootstrapHelper.KEY_CONSORTIUM].version=readset_version 745 read_set.values[BootstrapHelper.KEY_CONSORTIUM].value=channelConfig.values[BootstrapHelper.KEY_CONSORTIUM].value 746 read_set.groups[ApplicationGroup].version=readset_version 747 for key, _ in channelConfig.groups['Application'].groups.iteritems(): 748 read_set.groups[ApplicationGroup].groups[key] 749 configUpdate = common_dot_configtx_pb2.ConfigUpdate(channel_id=chainId, 750 read_set=read_set, 751 write_set=channelConfig) 752 configUpdateEnvelope = common_dot_configtx_pb2.ConfigUpdateEnvelope(config_update=configUpdate.SerializeToString(), signatures =[]) 753 return configUpdateEnvelope 754 755 756 def mergeConfigGroups(configGroupTarget, configGroupSource, allow_value_overwrite=False): 757 for k, v in configGroupSource.groups.iteritems(): 758 if k in configGroupTarget.groups.keys(): 759 mergeConfigGroups(configGroupTarget.groups[k], configGroupSource.groups[k], allow_value_overwrite=allow_value_overwrite) 760 else: 761 configGroupTarget.groups[k].MergeFrom(v) 762 for k, v in configGroupSource.policies.iteritems(): 763 if k in configGroupTarget.policies.keys(): 764 mergeConfigGroups(configGroupTarget.policies[k], configGroupSource.policies[k], allow_value_overwrite=allow_value_overwrite) 765 else: 766 configGroupTarget.policies[k].MergeFrom(v) 767 for k, v in configGroupSource.values.iteritems(): 768 if not allow_value_overwrite: 769 assert not k in configGroupTarget.values.keys(), "Value already exists in target config group: {0}".format(k) 770 configGroupTarget.values[k].CopyFrom(v) 771 772 773 def createGenesisBlock(context, service_names, chainId, consensusType, nodeAdminTuple, signedConfigItems=[]): 774 'Generates the genesis block for starting the oderers and for use in the chain config transaction by peers' 775 # assert not "bootstrapGenesisBlock" in context,"Genesis block already created:\n{0}".format(context.bootstrapGenesisBlock) 776 directory = getDirectory(context) 777 assert len(directory.ordererAdminTuples) > 0, "No orderer admin tuples defined!!!" 778 779 channelConfig = createChannelConfigGroup(directory=directory, service_names=service_names, consensusType=consensusType) 780 for configGroup in signedConfigItems: 781 mergeConfigGroups(channelConfig, configGroup) 782 783 config = common_dot_configtx_pb2.Config( 784 sequence=0, 785 channel_group=channelConfig) 786 787 configEnvelope = common_dot_configtx_pb2.ConfigEnvelope(config=config) 788 envelope = createEnvelopeForMsg(directory=directory, chainId=chainId, nodeAdminTuple=nodeAdminTuple, msg=configEnvelope, typeAsString="CONFIG") 789 blockData = common_dot_common_pb2.BlockData(data=[envelope.SerializeToString()]) 790 791 # Spoke with kostas, for orderer in general 792 signaturesMetadata = "" 793 lastConfigurationBlockMetadata = common_dot_common_pb2.Metadata( 794 value=common_dot_common_pb2.LastConfig(index=0).SerializeToString()).SerializeToString() 795 ordererConfigMetadata = "" 796 transactionFilterMetadata = "" 797 bootstrapHelper = BootstrapHelper(chainId="NOT_USED") 798 block = common_dot_common_pb2.Block( 799 header=common_dot_common_pb2.BlockHeader( 800 number=0, 801 previous_hash=None, 802 data_hash=bootstrapHelper.computeBlockDataHash(blockData), 803 ), 804 data=blockData, 805 metadata=common_dot_common_pb2.BlockMetadata( 806 metadata=[signaturesMetadata, lastConfigurationBlockMetadata, transactionFilterMetadata, 807 ordererConfigMetadata]), 808 ) 809 810 # Add this back once crypto certs are required 811 for nodeAdminTuple in directory.ordererAdminTuples: 812 userCert = directory.ordererAdminTuples[nodeAdminTuple] 813 certAsPEM = crypto.dump_certificate(crypto.FILETYPE_PEM, userCert) 814 # print("UserCert for orderer genesis:\n{0}\n".format(certAsPEM)) 815 # print("") 816 817 return (block, envelope, channelConfig) 818 819 820 class PathType(Enum): 821 'Denotes whether Path relative to Local filesystem or Containers volume reference.' 822 Local = 1 823 Container = 2 824 825 826 def getMSPConfig(org, directory): 827 # CA certificates can't be admins of an MSP 828 # adminCerts = [org.getCertAsPEM()] 829 adminCerts = [] 830 # Find the mspAdmin Tuple for org and add to admincerts folder 831 for pnt, cert in [(nat, cert) for nat, cert in directory.ordererAdminTuples.items() if 832 org.name == nat.organization and "configadmin" in nat.nodeName.lower()]: 833 adminCerts.append(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) 834 cacerts = [org.getCertAsPEM()] 835 tls_root_certs = [org.getCertAsPEM()] 836 # Currently only 1 component, CN=<orgName> 837 # name = self.getSelfSignedCert().get_subject().getComponents()[0][1] 838 fabricMSPConfig = msp_config_pb2.FabricMSPConfig(admins=adminCerts, root_certs=cacerts, name=org.name, tls_root_certs=tls_root_certs) 839 mspConfig = msp_config_pb2.MSPConfig(config=fabricMSPConfig.SerializeToString(), type=0) 840 return mspConfig 841 842 843 class CallbackHelper: 844 def __init__(self, discriminator, volumeRootPathInContainer = "/var/hyperledger/bddtests"): 845 self.volumeRootPathInContainer = volumeRootPathInContainer 846 self.discriminator = discriminator 847 848 def getVolumePath(self, project_name, pathType=PathType.Local): 849 assert pathType in PathType, "Expected pathType of {0}".format(PathType) 850 basePath = "." 851 if pathType == PathType.Container: 852 basePath = self.volumeRootPathInContainer 853 return "{0}/volumes/{1}/{2}".format(basePath, self.discriminator, project_name) 854 855 def getLocalMspConfigPath(self, project_name, compose_service, pathType=PathType.Local): 856 return "{0}/{1}/localMspConfig".format(self.getVolumePath(project_name=project_name, pathType=pathType), compose_service) 857 858 def getLocalTLSConfigPath(self, project_name, compose_service, pathType=PathType.Local): 859 return os.path.join(self.getVolumePath(project_name=project_name, pathType=pathType), compose_service, "tls_config") 860 861 def _getPathAndUserInfo(self, directory , project_name, compose_service, nat_discriminator="Signer", pathType=PathType.Local): 862 matchingNATs = [nat for nat in directory.getNamedCtxTuples() if ((compose_service in nat.user) and (nat_discriminator in nat.user) and ((compose_service in nat.nodeName)))] 863 assert len(matchingNATs)==1, "Unexpected number of matching NodeAdminTuples: {0}".format(matchingNATs) 864 localMspConfigPath = self.getLocalMspConfigPath(project_name=project_name, compose_service=compose_service,pathType=pathType) 865 return (localMspConfigPath, matchingNATs[0]) 866 867 def getLocalMspConfigPrivateKeyPath(self, directory , project_name, compose_service, pathType=PathType.Local): 868 (localMspConfigPath, nodeAdminTuple) = self._getPathAndUserInfo(directory=directory, project_name=project_name, compose_service=compose_service, pathType=pathType) 869 return "{0}/keystore/{1}.pem".format(localMspConfigPath, nodeAdminTuple.user) 870 871 def getLocalMspConfigPublicCertPath(self, directory , project_name, compose_service, pathType=PathType.Local): 872 (localMspConfigPath, nodeAdminTuple) = self._getPathAndUserInfo(directory=directory, project_name=project_name, compose_service=compose_service, pathType=pathType) 873 return "{0}/signcerts/{1}.pem".format(localMspConfigPath, nodeAdminTuple.user) 874 875 def getTLSKeyPaths(self, pnt , project_name, compose_service, pathType=PathType.Local): 876 localTLSConfigPath = self.getLocalTLSConfigPath(project_name=project_name, compose_service=compose_service, pathType=pathType) 877 certPath = os.path.join(localTLSConfigPath, 878 "{0}-{1}-{2}-tls.crt".format(pnt.user, pnt.nodeName, pnt.organization)) 879 keyPath = os.path.join(localTLSConfigPath, 880 "{0}-{1}-{2}-tls.key".format(pnt.user, pnt.nodeName, pnt.organization)) 881 return (keyPath, certPath) 882 883 884 def getLocalMspConfigRootCertPath(self, directory , project_name, compose_service, pathType=PathType.Local): 885 (localMspConfigPath, nodeAdminTuple) = self._getPathAndUserInfo(directory=directory, project_name=project_name, compose_service=compose_service, pathType=pathType) 886 return "{0}/cacerts/{1}.pem".format(localMspConfigPath, nodeAdminTuple.organization) 887 888 def _createCryptoMaterial(self,directory , project_name, compose_service, network): 889 self._writeMspFiles(directory , project_name=project_name, compose_service=compose_service, network=network) 890 self._writeTLSFiles(directory , project_name=project_name, compose_service=compose_service, network=network) 891 892 def _writeMspFiles(self, directory , project_name, compose_service, network): 893 localMspConfigPath = self.getLocalMspConfigPath(project_name, compose_service) 894 os.makedirs("{0}/{1}".format(localMspConfigPath, "signcerts")) 895 os.makedirs("{0}/{1}".format(localMspConfigPath, "admincerts")) 896 os.makedirs("{0}/{1}".format(localMspConfigPath, "cacerts")) 897 #TODO: Consider how to accomodate intermediate CAs 898 os.makedirs("{0}/{1}".format(localMspConfigPath, "intermediatecacerts")) 899 os.makedirs("{0}/{1}".format(localMspConfigPath, "keystore")) 900 os.makedirs("{0}/{1}".format(localMspConfigPath, "tlscacerts")) 901 #TODO: Consider how to accomodate intermediate CAs 902 os.makedirs("{0}/{1}".format(localMspConfigPath, "tlsintermediatecacerts")) 903 904 # Find the peer signer Tuple for this peer and add to signcerts folder 905 for pnt, cert in [(peerNodeTuple, cert) for peerNodeTuple, cert in directory.ordererAdminTuples.items() if 906 compose_service in peerNodeTuple.user and "signer" in peerNodeTuple.user.lower()]: 907 # Put the PEM file in the signcerts folder 908 with open("{0}/signcerts/{1}.pem".format(localMspConfigPath, pnt.user), "w") as f: 909 f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) 910 # Put the associated private key into the keystore folder 911 user = directory.getUser(pnt.user, shouldCreate=False) 912 with open("{0}/keystore/{1}.pem".format(localMspConfigPath, pnt.user), "w") as f: 913 f.write(user.ecdsaSigningKey.to_pem()) 914 915 #Now put the signing Orgs cert in the cacerts folder 916 org_cert_as_pem = directory.getOrganization(pnt.organization).getCertAsPEM() 917 with open("{0}/cacerts/{1}.pem".format(localMspConfigPath, pnt.organization), "w") as f: 918 f.write(org_cert_as_pem) 919 with open("{0}/tlscacerts/{1}.pem".format(localMspConfigPath, pnt.organization), "w") as f: 920 f.write(org_cert_as_pem) 921 922 # Find the peer admin Tuple for this peer and add to admincerts folder 923 for pnt, cert in [(peerNodeTuple, cert) for peerNodeTuple, cert in directory.ordererAdminTuples.items() if 924 compose_service in peerNodeTuple.user and "admin" in peerNodeTuple.user.lower()]: 925 # Put the PEM file in the signcerts folder 926 with open("{0}/admincerts/{1}.pem".format(localMspConfigPath, pnt.user), "w") as f: 927 f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) 928 929 def _writeTLSFiles(self, directory , project_name, compose_service, network): 930 localTLSConfigPath = self.getLocalTLSConfigPath(project_name, compose_service) 931 os.makedirs(localTLSConfigPath) 932 # Find the peer signer Tuple for this peer and add to signcerts folder 933 for pnt, cert in [(peerNodeTuple, cert) for peerNodeTuple, cert in directory.ordererAdminTuples.items() if 934 compose_service in peerNodeTuple.user and "signer" in peerNodeTuple.user.lower()]: 935 user = directory.getUser(userName=pnt.user) 936 # Add the subjectAlternativeName if the current entity is a signer, and the nodeName contains peer or orderer 937 extensions = directory._get_cert_extensions_ip_sans(user.name, pnt.nodeName) 938 userTLSCert = directory.getOrganization(pnt.organization).createCertificate(user.createTLSCertRequest(pnt.nodeName), extensions=extensions) 939 (keyPath, certPath) = self.getTLSKeyPaths(pnt=pnt, project_name=project_name, compose_service=compose_service, pathType=PathType.Local) 940 with open(keyPath, 'w') as f: 941 f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, user.rsaSigningKey)) 942 with open(certPath, 'w') as f: 943 f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, userTLSCert)) 944 945 def _getMspId(self, compose_service, directory): 946 matchingNATs = [nat for nat in directory.getNamedCtxTuples() if ((compose_service in nat.user) and ("Signer" in nat.user) and ((compose_service in nat.nodeName)))] 947 assert len(matchingNATs)==1, "Unexpected number of matching NodeAdminTuples: {0}".format(matchingNATs) 948 return matchingNATs[0].organization 949 950 951 class OrdererGensisBlockCompositionCallback(compose.CompositionCallback, CallbackHelper): 952 'Responsible for setting the GensisBlock for the Orderer nodes upon composition' 953 954 def __init__(self, context, genesisBlock, genesisFileName="genesis_file"): 955 CallbackHelper.__init__(self, discriminator="orderer") 956 self.context = context 957 self.genesisFileName = genesisFileName 958 self.genesisBlock = genesisBlock 959 compose.Composition.RegisterCallbackInContext(context, self) 960 961 def getGenesisFilePath(self, project_name, pathType=PathType.Local): 962 return "{0}/{1}".format(self.getVolumePath(project_name=project_name, pathType=pathType), self.genesisFileName) 963 964 def getOrdererList(self, composition): 965 return [serviceName for serviceName in composition.getServiceNames() if "orderer" in serviceName] 966 967 def composing(self, composition, context): 968 print("Will copy gensisiBlock over at this point ") 969 os.makedirs(self.getVolumePath(composition.projectName)) 970 with open(self.getGenesisFilePath(composition.projectName), "wb") as f: 971 f.write(self.genesisBlock.SerializeToString()) 972 directory = getDirectory(context) 973 974 for ordererService in self.getOrdererList(composition): 975 self._createCryptoMaterial(directory=directory, 976 compose_service=ordererService, 977 project_name=composition.projectName, 978 network=Network.Orderer) 979 980 def decomposing(self, composition, context): 981 'Will remove the orderer volume path folder for the context' 982 shutil.rmtree(self.getVolumePath(composition.projectName)) 983 984 def getEnv(self, composition, context, env): 985 directory = getDirectory(context) 986 env["ORDERER_GENERAL_GENESISMETHOD"] = "file" 987 env["ORDERER_GENERAL_GENESISFILE"] = self.getGenesisFilePath(composition.projectName, pathType=PathType.Container) 988 for ordererService in self.getOrdererList(composition): 989 localMspConfigPath = self.getLocalMspConfigPath(composition.projectName, ordererService, pathType=PathType.Container) 990 env["{0}_ORDERER_GENERAL_LOCALMSPDIR".format(ordererService.upper())] = localMspConfigPath 991 env["{0}_ORDERER_GENERAL_LOCALMSPID".format(ordererService.upper())] = self._getMspId(compose_service=ordererService, directory=directory) 992 # TLS Settings 993 (_, pnt) = self._getPathAndUserInfo(directory=directory, project_name=composition.projectName, compose_service=ordererService, pathType=PathType.Container) 994 (keyPath, certPath) = self.getTLSKeyPaths(pnt=pnt, project_name=composition.projectName, compose_service=ordererService, pathType=PathType.Container) 995 env["{0}_ORDERER_GENERAL_TLS_CERTIFICATE".format(ordererService.upper())] = certPath 996 env["{0}_ORDERER_GENERAL_TLS_PRIVATEKEY".format(ordererService.upper())] = keyPath 997 env["{0}_ORDERER_GENERAL_TLS_ROOTCAS".format(ordererService.upper())] = "[{0}]".format(self.getLocalMspConfigRootCertPath( 998 directory=directory, project_name=composition.projectName, compose_service=ordererService, pathType=PathType.Container)) 999 1000 class PeerCompositionCallback(compose.CompositionCallback, CallbackHelper): 1001 'Responsible for setting up Peer nodes upon composition' 1002 1003 def __init__(self, context): 1004 CallbackHelper.__init__(self, discriminator="peer") 1005 self.context = context 1006 compose.Composition.RegisterCallbackInContext(context, self) 1007 1008 def getPeerList(self, composition): 1009 return [serviceName for serviceName in composition.getServiceNames() if "peer" in serviceName] 1010 1011 def composing(self, composition, context): 1012 'Will copy local MSP info over at this point for each peer node' 1013 1014 directory = getDirectory(context) 1015 1016 for peerService in self.getPeerList(composition): 1017 self._createCryptoMaterial(directory=directory, 1018 compose_service=peerService, 1019 project_name=composition.projectName, 1020 network=Network.Peer) 1021 1022 def decomposing(self, composition, context): 1023 'Will remove the orderer volume path folder for the context' 1024 shutil.rmtree(self.getVolumePath(composition.projectName)) 1025 1026 def getEnv(self, composition, context, env): 1027 directory = getDirectory(context) 1028 # First figure out which organization provided the signer cert for this 1029 for peerService in self.getPeerList(composition): 1030 localMspConfigPath = self.getLocalMspConfigPath(composition.projectName, peerService, pathType=PathType.Container) 1031 env["{0}_CORE_PEER_MSPCONFIGPATH".format(peerService.upper())] = localMspConfigPath 1032 env["{0}_CORE_PEER_LOCALMSPID".format(peerService.upper())] = self._getMspId(compose_service=peerService, directory=directory) 1033 # TLS Settings 1034 # env["{0}_CORE_PEER_TLS_ENABLED".format(peerService.upper())] = self._getMspId(compose_service=peerService, directory=directory) 1035 (_, pnt) = self._getPathAndUserInfo(directory=directory, project_name=composition.projectName, compose_service=peerService, pathType=PathType.Container) 1036 (keyPath, certPath) = self.getTLSKeyPaths(pnt=pnt, project_name=composition.projectName, compose_service=peerService, pathType=PathType.Container) 1037 env["{0}_CORE_PEER_TLS_CERT_FILE".format(peerService.upper())] = certPath 1038 env["{0}_CORE_PEER_TLS_KEY_FILE".format(peerService.upper())] = keyPath 1039 env["{0}_CORE_PEER_TLS_ROOTCERT_FILE".format(peerService.upper())] = self.getLocalMspConfigRootCertPath( 1040 directory=directory, project_name=composition.projectName, compose_service=peerService, pathType=PathType.Container) 1041 env["{0}_CORE_PEER_TLS_SERVERHOSTOVERRIDE".format(peerService.upper())] = peerService 1042 1043 1044 def getDefaultConsortiumGroup(consortiums_mod_policy): 1045 default_config_group = common_dot_configtx_pb2.ConfigGroup() 1046 default_config_group.groups[ConsortiumsGroup].mod_policy=consortiums_mod_policy 1047 return default_config_group 1048 1049 1050 def create_config_update_envelope(config_update): 1051 return common_dot_configtx_pb2.ConfigUpdateEnvelope(config_update=config_update.SerializeToString(), signatures =[]) 1052 1053 def create_orderer_consortium_config_update(orderer_system_chain_id, orderer_channel_group, config_groups): 1054 'Creates the orderer config update' 1055 # First determine read set 1056 read_set = common_dot_configtx_pb2.ConfigGroup() 1057 read_set.groups[ConsortiumsGroup].CopyFrom(orderer_channel_group.groups[ConsortiumsGroup]) 1058 1059 write_set = common_dot_configtx_pb2.ConfigGroup() 1060 write_set.groups[ConsortiumsGroup].CopyFrom(orderer_channel_group.groups[ConsortiumsGroup]) 1061 write_set.groups[ConsortiumsGroup].version += 1 1062 for config_group in config_groups: 1063 mergeConfigGroups(write_set, config_group) 1064 config_update = common_dot_configtx_pb2.ConfigUpdate(channel_id=orderer_system_chain_id, 1065 read_set=read_set, 1066 write_set=write_set) 1067 # configUpdateEnvelope = common_dot_configtx_pb2.ConfigUpdateEnvelope(config_update=configUpdate.SerializeToString(), signatures =[]) 1068 return config_update 1069 1070 def create_channel_config_update(system_channel_version, channel_id, consortium_config_group): 1071 read_set = common_dot_configtx_pb2.ConfigGroup() 1072 read_set.version = system_channel_version 1073 read_set.values[BootstrapHelper.KEY_CONSORTIUM].value=toValue(common_dot_configuration_pb2.Consortium(name=consortium_config_group.groups[ConsortiumsGroup].groups.keys()[0])) 1074 1075 # Copying all of the consortium orgs into the ApplicationGroup 1076 read_set.groups[ApplicationGroup].CopyFrom(consortium_config_group.groups[ConsortiumsGroup].groups.values()[0]) 1077 read_set.groups[ApplicationGroup].values.clear() 1078 read_set.groups[ApplicationGroup].policies.clear() 1079 read_set.groups[ApplicationGroup].mod_policy="" 1080 for k, v in read_set.groups[ApplicationGroup].groups.iteritems(): 1081 v.values.clear() 1082 v.policies.clear() 1083 v.mod_policy="" 1084 1085 # Now the write_set 1086 write_set = common_dot_configtx_pb2.ConfigGroup() 1087 write_set.CopyFrom(read_set) 1088 write_set.groups[ApplicationGroup].version += 1 1089 # For now, setting same policies for each 'Non-Org' group 1090 typeImplicitMeta = common_dot_policies_pb2.Policy.PolicyType.Value("IMPLICIT_META") 1091 Policy = common_dot_policies_pb2.Policy 1092 IMP = common_dot_policies_pb2.ImplicitMetaPolicy 1093 ruleAny = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("ANY") 1094 ruleMajority = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("MAJORITY") 1095 write_set.groups[ApplicationGroup].policies[BootstrapHelper.KEY_POLICY_READERS].policy.CopyFrom( 1096 Policy(type=typeImplicitMeta, value=IMP( 1097 rule=ruleAny, sub_policy=BootstrapHelper.KEY_POLICY_READERS).SerializeToString())) 1098 write_set.groups[ApplicationGroup].policies[BootstrapHelper.KEY_POLICY_WRITERS].policy.CopyFrom( 1099 Policy(type=typeImplicitMeta, value=IMP( 1100 rule=ruleAny, sub_policy=BootstrapHelper.KEY_POLICY_WRITERS).SerializeToString())) 1101 write_set.groups[ApplicationGroup].policies[BootstrapHelper.KEY_POLICY_ADMINS].policy.CopyFrom( 1102 Policy(type=typeImplicitMeta, value=IMP( 1103 rule=ruleMajority, sub_policy=BootstrapHelper.KEY_POLICY_ADMINS).SerializeToString())) 1104 write_set.groups[ApplicationGroup].mod_policy = "Admins" 1105 for k, v in write_set.groups[ApplicationGroup].policies.iteritems(): 1106 v.mod_policy=BootstrapHelper.KEY_POLICY_ADMINS 1107 config_update = common_dot_configtx_pb2.ConfigUpdate(channel_id=channel_id, 1108 read_set=read_set, 1109 write_set=write_set) 1110 return config_update 1111 1112 def get_group_paths_from_config_group(config_group, paths, current_path = ""): 1113 if len(config_group.groups)==0: 1114 paths.append(current_path) 1115 else: 1116 for group_name, group in config_group.groups.iteritems(): 1117 get_group_paths_from_config_group(config_group=group, current_path="{0}/{1}".format(current_path, group_name), paths=paths) 1118 return paths 1119 1120 1121 def get_group_from_config_path(config_group, config_path): 1122 from os import path 1123 current_config_group = config_group 1124 for group_id in [g.strip("/") for g in path.split(config_path)]: 1125 current_config_group = current_config_group.groups[group_id] 1126 return current_config_group 1127 1128 def create_existing_channel_config_update(system_channel_version, channel_id, input_config_update, config_groups): 1129 # First make a copy of the input config update msg to manipulate 1130 read_set = common_dot_configtx_pb2.ConfigGroup() 1131 read_set.version = system_channel_version 1132 read_set.CopyFrom(input_config_update) 1133 1134 # Now the write_set 1135 write_set = common_dot_configtx_pb2.ConfigGroup() 1136 write_set.CopyFrom(read_set) 1137 1138 # Merge all of the supplied config_groups 1139 for config_group in config_groups: 1140 mergeConfigGroups(write_set, config_group) 1141 1142 # Will build a unique list of group paths from the supplied config_groups 1143 group_paths = list(set([p for sublist in [get_group_paths_from_config_group(cg, paths=[]) for cg in config_groups] for p in sublist])) 1144 1145 # Now increment the version for each path. 1146 # TODO: This logic will only work for the case of insertions 1147 for group in [get_group_from_config_path(write_set, g) for g in group_paths]: 1148 group.version += 1 1149 1150 # For now, setting same policies for each 'Non-Org' group 1151 config_update = common_dot_configtx_pb2.ConfigUpdate(channel_id=channel_id, 1152 read_set=read_set, 1153 write_set=write_set) 1154 return config_update 1155 1156 def update_config_group_version_info(input_config_update, proposed_config_group): 1157 for k,v in proposed_config_group.groups.iteritems(): 1158 if k in input_config_update.groups.keys(): 1159 # Make sure all keys of the current group match those of the proposed, if not either added or deleted. 1160 if not set(proposed_config_group.groups[k].groups.keys()) == set(input_config_update.groups[k].groups.keys()): 1161 proposed_config_group.groups[k].version +=1 1162 # Now recurse 1163 update_config_group_version_info(input_config_update.groups[k], proposed_config_group.groups[k]) 1164 1165 # def create_existing_channel_config_update2(system_channel_version, channel_id, input_config_update, config_group): 1166 # # First make a copy of the input config update msg to manipulate 1167 # read_set = common_dot_configtx_pb2.ConfigGroup() 1168 # read_set.version = system_channel_version 1169 # read_set.CopyFrom(config_group) 1170 # 1171 # # Now the write_set 1172 # write_set = common_dot_configtx_pb2.ConfigGroup() 1173 # write_set.CopyFrom(read_set) 1174 # mergeConfigGroups(write_set, config_groups[0]) 1175 # write_set.groups['Application'].groups['peerOrg0'].version +=1 1176 # 1177 # # write_set.groups[ApplicationGroup].version += 1 1178 # # For now, setting same policies for each 'Non-Org' group 1179 # config_update = common_dot_configtx_pb2.ConfigUpdate(channel_id=channel_id, 1180 # read_set=read_set, 1181 # write_set=write_set) 1182 # return config_update 1183 1184 1185 def createConsortium(context, consortium_name, org_names, mod_policy): 1186 channel = common_dot_configtx_pb2.ConfigGroup() 1187 directory = getDirectory(context=context) 1188 channel.groups[ConsortiumsGroup].groups[consortium_name].mod_policy = mod_policy 1189 # Add the orderer org groups MSPConfig info to consortiums group 1190 for consortium_org in [org for org in directory.getOrganizations().values() if org.name in org_names]: 1191 # channel.groups[ConsortiumsGroup].groups[consortium_name].groups[consortium_org.name].mod_policy = BootstrapHelper.KEY_POLICY_ADMINS 1192 channel.groups[ConsortiumsGroup].groups[consortium_name].groups[consortium_org.name].values[BootstrapHelper.KEY_MSP_INFO].value = toValue( 1193 getMSPConfig(org=consortium_org, directory=directory)) 1194 channel.groups[ConsortiumsGroup].groups[consortium_name].groups[consortium_org.name].values[BootstrapHelper.KEY_MSP_INFO].mod_policy=BootstrapHelper.KEY_POLICY_ADMINS 1195 typeImplicitMeta = common_dot_policies_pb2.Policy.PolicyType.Value("IMPLICIT_META") 1196 Policy = common_dot_policies_pb2.Policy 1197 IMP = common_dot_policies_pb2.ImplicitMetaPolicy 1198 ruleAny = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("ANY") 1199 ruleAll = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("ALL") 1200 ruleMajority = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("MAJORITY") 1201 channel.groups[ConsortiumsGroup].groups[consortium_name].values[BootstrapHelper.KEY_CHANNEL_CREATION_POLICY].value = toValue( 1202 Policy(type=typeImplicitMeta, value=IMP( 1203 rule=ruleAll, sub_policy=BootstrapHelper.KEY_POLICY_WRITERS).SerializeToString())) 1204 channel.groups[ConsortiumsGroup].groups[consortium_name].values[BootstrapHelper.KEY_CHANNEL_CREATION_POLICY].mod_policy=BootstrapHelper.KEY_POLICY_ADMINS 1205 # For now, setting same policies for each 'Non-Org' group 1206 orgs = [directory.getOrganization(orgName) for orgName in org_names] 1207 setDefaultPoliciesForOrgs(channel.groups[ConsortiumsGroup], orgs, consortium_name, version=0, policy_version=0) 1208 return channel 1209 1210 def setOrdererBootstrapGenesisBlock(genesisBlock): 1211 'Responsible for setting the GensisBlock for the Orderer nodes upon composition' 1212 1213 1214 def broadcastCreateChannelConfigTx(context, certAlias, composeService, configTxEnvelope, user): 1215 dataFunc = lambda x: configTxEnvelope 1216 user.broadcastMessages(context=context, numMsgsToBroadcast=1, composeService=composeService, 1217 dataFunc=dataFunc) 1218 1219 def get_latest_configuration_block(deliverer_stream_helper, channel_id): 1220 latest_config_block = None 1221 deliverer_stream_helper.seekToRange(chainID=channel_id, start="Newest", end="Newest") 1222 blocks = deliverer_stream_helper.getBlocks() 1223 assert len(blocks) == 1, "Expected single block, received: {0} blocks".format(len(blocks)) 1224 newest_block = blocks[0] 1225 last_config = common_dot_common_pb2.LastConfig() 1226 metadata = common_dot_common_pb2.Metadata() 1227 metadata.ParseFromString(newest_block.metadata.metadata[common_dot_common_pb2.BlockMetadataIndex.Value('LAST_CONFIG')]) 1228 last_config.ParseFromString(metadata.value) 1229 if last_config.index == newest_block.header.number: 1230 latest_config_block = newest_block 1231 else: 1232 deliverer_stream_helper.seekToRange(chainID=channel_id, start=last_config.index, end=last_config.index) 1233 blocks = deliverer_stream_helper.getBlocks() 1234 assert len(blocks) == 1, "Expected single block, received: {0} blocks".format(len(blocks)) 1235 assert len(blocks[0].data.data) == 1, "Expected single transaction for configuration block, instead found {0} transactions".format(len(blocks.data.data)) 1236 latest_config_block = blocks[0] 1237 return latest_config_block 1238 1239 def get_channel_group_from_config_block(block): 1240 assert len(block.data.data) == 1, "Expected single transaction for configuration block, instead found {0} transactions".format(len(block.data.data)) 1241 e = common_dot_common_pb2.Envelope() 1242 e.ParseFromString(block.data.data[0]) 1243 p = common_dot_common_pb2.Payload() 1244 p.ParseFromString(e.payload) 1245 config_envelope = common_dot_configtx_pb2.ConfigEnvelope() 1246 config_envelope.ParseFromString(p.data) 1247 return config_envelope.config.channel_group 1248 1249 def getArgsFromContextForUser(context, userName): 1250 directory = getDirectory(context) 1251 # Update the chaincodeSpec ctorMsg for invoke 1252 args = [] 1253 if 'table' in context: 1254 if context.table: 1255 # There are function arguments 1256 user = directory.getUser(userName) 1257 # Allow the user to specify expressions referencing tags in the args list 1258 pattern = re.compile('\{(.*)\}$') 1259 for arg in context.table[0].cells: 1260 m = pattern.match(arg) 1261 if m: 1262 # tagName reference found in args list 1263 tagName = m.groups()[0] 1264 # make sure the tagName is found in the users tags 1265 assert tagName in user.tags, "TagName '{0}' not found for user '{1}'".format(tagName, 1266 user.getUserName()) 1267 args.append(user.tags[tagName]) 1268 else: 1269 # No tag referenced, pass the arg 1270 args.append(arg) 1271 return args 1272 1273 1274 def get_args_for_user(args_input, user): 1275 args = [] 1276 pattern = re.compile('\{(.*)\}$') 1277 for arg in args_input: 1278 m = pattern.match(arg) 1279 if m: 1280 # tagName reference found in args list 1281 tagName = m.groups()[0] 1282 # make sure the tagName is found in the users tags 1283 assert tagName in user.tags, "TagName '{0}' not found for user '{1}'".format(tagName, 1284 user.getUserName()) 1285 args.append(user.tags[tagName]) 1286 else: 1287 # No tag referenced, pass the arg 1288 args.append(arg) 1289 return tuple(args) 1290 1291 1292 def getChannelIdFromConfigUpdateEnvelope(config_update_envelope): 1293 config_update = common_dot_configtx_pb2.ConfigUpdate() 1294 config_update.ParseFromString(config_update_envelope.config_update) 1295 return config_update