github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/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 adminCerts = [org.getCertAsPEM()] 828 # Find the mspAdmin Tuple for org and add to admincerts folder 829 for pnt, cert in [(nat, cert) for nat, cert in directory.ordererAdminTuples.items() if 830 org.name == nat.organization and "configadmin" in nat.nodeName.lower()]: 831 adminCerts.append(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) 832 cacerts = [org.getCertAsPEM()] 833 # Currently only 1 component, CN=<orgName> 834 # name = self.getSelfSignedCert().get_subject().getComponents()[0][1] 835 fabricMSPConfig = msp_config_pb2.FabricMSPConfig(admins=adminCerts, root_certs=cacerts, name=org.name) 836 mspConfig = msp_config_pb2.MSPConfig(config=fabricMSPConfig.SerializeToString(), type=0) 837 return mspConfig 838 839 840 class CallbackHelper: 841 def __init__(self, discriminator, volumeRootPathInContainer = "/var/hyperledger/bddtests"): 842 self.volumeRootPathInContainer = volumeRootPathInContainer 843 self.discriminator = discriminator 844 845 def getVolumePath(self, project_name, pathType=PathType.Local): 846 assert pathType in PathType, "Expected pathType of {0}".format(PathType) 847 basePath = "." 848 if pathType == PathType.Container: 849 basePath = self.volumeRootPathInContainer 850 return "{0}/volumes/{1}/{2}".format(basePath, self.discriminator, project_name) 851 852 def getLocalMspConfigPath(self, project_name, compose_service, pathType=PathType.Local): 853 return "{0}/{1}/localMspConfig".format(self.getVolumePath(project_name=project_name, pathType=pathType), compose_service) 854 855 def getLocalTLSConfigPath(self, project_name, compose_service, pathType=PathType.Local): 856 return os.path.join(self.getVolumePath(project_name=project_name, pathType=pathType), compose_service, "tls_config") 857 858 def _getPathAndUserInfo(self, directory , project_name, compose_service, nat_discriminator="Signer", pathType=PathType.Local): 859 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)))] 860 assert len(matchingNATs)==1, "Unexpected number of matching NodeAdminTuples: {0}".format(matchingNATs) 861 localMspConfigPath = self.getLocalMspConfigPath(project_name=project_name, compose_service=compose_service,pathType=pathType) 862 return (localMspConfigPath, matchingNATs[0]) 863 864 def getLocalMspConfigPrivateKeyPath(self, directory , project_name, compose_service, pathType=PathType.Local): 865 (localMspConfigPath, nodeAdminTuple) = self._getPathAndUserInfo(directory=directory, project_name=project_name, compose_service=compose_service, pathType=pathType) 866 return "{0}/keystore/{1}.pem".format(localMspConfigPath, nodeAdminTuple.user) 867 868 def getLocalMspConfigPublicCertPath(self, directory , project_name, compose_service, pathType=PathType.Local): 869 (localMspConfigPath, nodeAdminTuple) = self._getPathAndUserInfo(directory=directory, project_name=project_name, compose_service=compose_service, pathType=pathType) 870 return "{0}/signcerts/{1}.pem".format(localMspConfigPath, nodeAdminTuple.user) 871 872 def getTLSKeyPaths(self, pnt , project_name, compose_service, pathType=PathType.Local): 873 localTLSConfigPath = self.getLocalTLSConfigPath(project_name=project_name, compose_service=compose_service, pathType=pathType) 874 certPath = os.path.join(localTLSConfigPath, 875 "{0}-{1}-{2}-tls.crt".format(pnt.user, pnt.nodeName, pnt.organization)) 876 keyPath = os.path.join(localTLSConfigPath, 877 "{0}-{1}-{2}-tls.key".format(pnt.user, pnt.nodeName, pnt.organization)) 878 return (keyPath, certPath) 879 880 881 def getLocalMspConfigRootCertPath(self, directory , project_name, compose_service, pathType=PathType.Local): 882 (localMspConfigPath, nodeAdminTuple) = self._getPathAndUserInfo(directory=directory, project_name=project_name, compose_service=compose_service, pathType=pathType) 883 return "{0}/cacerts/{1}.pem".format(localMspConfigPath, nodeAdminTuple.organization) 884 885 def _createCryptoMaterial(self,directory , project_name, compose_service, network): 886 self._writeMspFiles(directory , project_name=project_name, compose_service=compose_service, network=network) 887 self._writeTLSFiles(directory , project_name=project_name, compose_service=compose_service, network=network) 888 889 def _writeMspFiles(self, directory , project_name, compose_service, network): 890 localMspConfigPath = self.getLocalMspConfigPath(project_name, compose_service) 891 os.makedirs("{0}/{1}".format(localMspConfigPath, "signcerts")) 892 os.makedirs("{0}/{1}".format(localMspConfigPath, "admincerts")) 893 os.makedirs("{0}/{1}".format(localMspConfigPath, "cacerts")) 894 os.makedirs("{0}/{1}".format(localMspConfigPath, "keystore")) 895 896 # Find the peer signer Tuple for this peer and add to signcerts folder 897 for pnt, cert in [(peerNodeTuple, cert) for peerNodeTuple, cert in directory.ordererAdminTuples.items() if 898 compose_service in peerNodeTuple.user and "signer" in peerNodeTuple.user.lower()]: 899 # Put the PEM file in the signcerts folder 900 with open("{0}/signcerts/{1}.pem".format(localMspConfigPath, pnt.user), "w") as f: 901 f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) 902 # Put the associated private key into the keystore folder 903 user = directory.getUser(pnt.user, shouldCreate=False) 904 with open("{0}/keystore/{1}.pem".format(localMspConfigPath, pnt.user), "w") as f: 905 f.write(user.ecdsaSigningKey.to_pem()) 906 907 #Now put the signing Orgs cert in the cacerts folder 908 org_cert_as_pem = directory.getOrganization(pnt.organization).getCertAsPEM() 909 with open("{0}/cacerts/{1}.pem".format(localMspConfigPath, pnt.organization), "w") as f: 910 f.write(org_cert_as_pem) 911 912 # Find the peer admin Tuple for this peer and add to admincerts folder 913 for pnt, cert in [(peerNodeTuple, cert) for peerNodeTuple, cert in directory.ordererAdminTuples.items() if 914 compose_service in peerNodeTuple.user and "admin" in peerNodeTuple.user.lower()]: 915 # Put the PEM file in the signcerts folder 916 with open("{0}/admincerts/{1}.pem".format(localMspConfigPath, pnt.user), "w") as f: 917 f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) 918 919 def _writeTLSFiles(self, directory , project_name, compose_service, network): 920 localTLSConfigPath = self.getLocalTLSConfigPath(project_name, compose_service) 921 os.makedirs(localTLSConfigPath) 922 # Find the peer signer Tuple for this peer and add to signcerts folder 923 for pnt, cert in [(peerNodeTuple, cert) for peerNodeTuple, cert in directory.ordererAdminTuples.items() if 924 compose_service in peerNodeTuple.user and "signer" in peerNodeTuple.user.lower()]: 925 user = directory.getUser(userName=pnt.user) 926 # Add the subjectAlternativeName if the current entity is a signer, and the nodeName contains peer or orderer 927 extensions = directory._get_cert_extensions_ip_sans(user.name, pnt.nodeName) 928 userTLSCert = directory.getOrganization(pnt.organization).createCertificate(user.createTLSCertRequest(pnt.nodeName), extensions=extensions) 929 (keyPath, certPath) = self.getTLSKeyPaths(pnt=pnt, project_name=project_name, compose_service=compose_service, pathType=PathType.Local) 930 with open(keyPath, 'w') as f: 931 f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, user.rsaSigningKey)) 932 with open(certPath, 'w') as f: 933 f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, userTLSCert)) 934 935 def _getMspId(self, compose_service, directory): 936 matchingNATs = [nat for nat in directory.getNamedCtxTuples() if ((compose_service in nat.user) and ("Signer" in nat.user) and ((compose_service in nat.nodeName)))] 937 assert len(matchingNATs)==1, "Unexpected number of matching NodeAdminTuples: {0}".format(matchingNATs) 938 return matchingNATs[0].organization 939 940 941 class OrdererGensisBlockCompositionCallback(compose.CompositionCallback, CallbackHelper): 942 'Responsible for setting the GensisBlock for the Orderer nodes upon composition' 943 944 def __init__(self, context, genesisBlock, genesisFileName="genesis_file"): 945 CallbackHelper.__init__(self, discriminator="orderer") 946 self.context = context 947 self.genesisFileName = genesisFileName 948 self.genesisBlock = genesisBlock 949 compose.Composition.RegisterCallbackInContext(context, self) 950 951 def getGenesisFilePath(self, project_name, pathType=PathType.Local): 952 return "{0}/{1}".format(self.getVolumePath(project_name=project_name, pathType=pathType), self.genesisFileName) 953 954 def getOrdererList(self, composition): 955 return [serviceName for serviceName in composition.getServiceNames() if "orderer" in serviceName] 956 957 def composing(self, composition, context): 958 print("Will copy gensisiBlock over at this point ") 959 os.makedirs(self.getVolumePath(composition.projectName)) 960 with open(self.getGenesisFilePath(composition.projectName), "wb") as f: 961 f.write(self.genesisBlock.SerializeToString()) 962 directory = getDirectory(context) 963 964 for ordererService in self.getOrdererList(composition): 965 self._createCryptoMaterial(directory=directory, 966 compose_service=ordererService, 967 project_name=composition.projectName, 968 network=Network.Orderer) 969 970 def decomposing(self, composition, context): 971 'Will remove the orderer volume path folder for the context' 972 shutil.rmtree(self.getVolumePath(composition.projectName)) 973 974 def getEnv(self, composition, context, env): 975 directory = getDirectory(context) 976 env["ORDERER_GENERAL_GENESISMETHOD"] = "file" 977 env["ORDERER_GENERAL_GENESISFILE"] = self.getGenesisFilePath(composition.projectName, pathType=PathType.Container) 978 for ordererService in self.getOrdererList(composition): 979 localMspConfigPath = self.getLocalMspConfigPath(composition.projectName, ordererService, pathType=PathType.Container) 980 env["{0}_ORDERER_GENERAL_LOCALMSPDIR".format(ordererService.upper())] = localMspConfigPath 981 env["{0}_ORDERER_GENERAL_LOCALMSPID".format(ordererService.upper())] = self._getMspId(compose_service=ordererService, directory=directory) 982 # TLS Settings 983 (_, pnt) = self._getPathAndUserInfo(directory=directory, project_name=composition.projectName, compose_service=ordererService, pathType=PathType.Container) 984 (keyPath, certPath) = self.getTLSKeyPaths(pnt=pnt, project_name=composition.projectName, compose_service=ordererService, pathType=PathType.Container) 985 env["{0}_ORDERER_GENERAL_TLS_CERTIFICATE".format(ordererService.upper())] = certPath 986 env["{0}_ORDERER_GENERAL_TLS_PRIVATEKEY".format(ordererService.upper())] = keyPath 987 env["{0}_ORDERER_GENERAL_TLS_ROOTCAS".format(ordererService.upper())] = "[{0}]".format(self.getLocalMspConfigRootCertPath( 988 directory=directory, project_name=composition.projectName, compose_service=ordererService, pathType=PathType.Container)) 989 990 class PeerCompositionCallback(compose.CompositionCallback, CallbackHelper): 991 'Responsible for setting up Peer nodes upon composition' 992 993 def __init__(self, context): 994 CallbackHelper.__init__(self, discriminator="peer") 995 self.context = context 996 compose.Composition.RegisterCallbackInContext(context, self) 997 998 def getPeerList(self, composition): 999 return [serviceName for serviceName in composition.getServiceNames() if "peer" in serviceName] 1000 1001 def composing(self, composition, context): 1002 'Will copy local MSP info over at this point for each peer node' 1003 1004 directory = getDirectory(context) 1005 1006 for peerService in self.getPeerList(composition): 1007 self._createCryptoMaterial(directory=directory, 1008 compose_service=peerService, 1009 project_name=composition.projectName, 1010 network=Network.Peer) 1011 1012 def decomposing(self, composition, context): 1013 'Will remove the orderer volume path folder for the context' 1014 shutil.rmtree(self.getVolumePath(composition.projectName)) 1015 1016 def getEnv(self, composition, context, env): 1017 directory = getDirectory(context) 1018 # First figure out which organization provided the signer cert for this 1019 for peerService in self.getPeerList(composition): 1020 localMspConfigPath = self.getLocalMspConfigPath(composition.projectName, peerService, pathType=PathType.Container) 1021 env["{0}_CORE_PEER_MSPCONFIGPATH".format(peerService.upper())] = localMspConfigPath 1022 env["{0}_CORE_PEER_LOCALMSPID".format(peerService.upper())] = self._getMspId(compose_service=peerService, directory=directory) 1023 # TLS Settings 1024 # env["{0}_CORE_PEER_TLS_ENABLED".format(peerService.upper())] = self._getMspId(compose_service=peerService, directory=directory) 1025 (_, pnt) = self._getPathAndUserInfo(directory=directory, project_name=composition.projectName, compose_service=peerService, pathType=PathType.Container) 1026 (keyPath, certPath) = self.getTLSKeyPaths(pnt=pnt, project_name=composition.projectName, compose_service=peerService, pathType=PathType.Container) 1027 env["{0}_CORE_PEER_TLS_CERT_FILE".format(peerService.upper())] = certPath 1028 env["{0}_CORE_PEER_TLS_KEY_FILE".format(peerService.upper())] = keyPath 1029 env["{0}_CORE_PEER_TLS_ROOTCERT_FILE".format(peerService.upper())] = self.getLocalMspConfigRootCertPath( 1030 directory=directory, project_name=composition.projectName, compose_service=peerService, pathType=PathType.Container) 1031 env["{0}_CORE_PEER_TLS_SERVERHOSTOVERRIDE".format(peerService.upper())] = peerService 1032 1033 1034 def getDefaultConsortiumGroup(consortiums_mod_policy): 1035 default_config_group = common_dot_configtx_pb2.ConfigGroup() 1036 default_config_group.groups[ConsortiumsGroup].mod_policy=consortiums_mod_policy 1037 return default_config_group 1038 1039 1040 def create_config_update_envelope(config_update): 1041 return common_dot_configtx_pb2.ConfigUpdateEnvelope(config_update=config_update.SerializeToString(), signatures =[]) 1042 1043 def create_orderer_consortium_config_update(orderer_system_chain_id, orderer_channel_group, config_groups): 1044 'Creates the orderer config update' 1045 # First determine read set 1046 read_set = common_dot_configtx_pb2.ConfigGroup() 1047 read_set.groups[ConsortiumsGroup].CopyFrom(orderer_channel_group.groups[ConsortiumsGroup]) 1048 1049 write_set = common_dot_configtx_pb2.ConfigGroup() 1050 write_set.groups[ConsortiumsGroup].CopyFrom(orderer_channel_group.groups[ConsortiumsGroup]) 1051 write_set.groups[ConsortiumsGroup].version += 1 1052 for config_group in config_groups: 1053 mergeConfigGroups(write_set, config_group) 1054 config_update = common_dot_configtx_pb2.ConfigUpdate(channel_id=orderer_system_chain_id, 1055 read_set=read_set, 1056 write_set=write_set) 1057 # configUpdateEnvelope = common_dot_configtx_pb2.ConfigUpdateEnvelope(config_update=configUpdate.SerializeToString(), signatures =[]) 1058 return config_update 1059 1060 def create_channel_config_update(system_channel_version, channel_id, consortium_config_group): 1061 read_set = common_dot_configtx_pb2.ConfigGroup() 1062 read_set.version = system_channel_version 1063 read_set.values[BootstrapHelper.KEY_CONSORTIUM].value=toValue(common_dot_configuration_pb2.Consortium(name=consortium_config_group.groups[ConsortiumsGroup].groups.keys()[0])) 1064 1065 # Copying all of the consortium orgs into the ApplicationGroup 1066 read_set.groups[ApplicationGroup].CopyFrom(consortium_config_group.groups[ConsortiumsGroup].groups.values()[0]) 1067 read_set.groups[ApplicationGroup].values.clear() 1068 read_set.groups[ApplicationGroup].policies.clear() 1069 read_set.groups[ApplicationGroup].mod_policy="" 1070 for k, v in read_set.groups[ApplicationGroup].groups.iteritems(): 1071 v.values.clear() 1072 v.policies.clear() 1073 v.mod_policy="" 1074 1075 # Now the write_set 1076 write_set = common_dot_configtx_pb2.ConfigGroup() 1077 write_set.CopyFrom(read_set) 1078 write_set.groups[ApplicationGroup].version += 1 1079 # For now, setting same policies for each 'Non-Org' group 1080 typeImplicitMeta = common_dot_policies_pb2.Policy.PolicyType.Value("IMPLICIT_META") 1081 Policy = common_dot_policies_pb2.Policy 1082 IMP = common_dot_policies_pb2.ImplicitMetaPolicy 1083 ruleAny = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("ANY") 1084 ruleMajority = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("MAJORITY") 1085 write_set.groups[ApplicationGroup].policies[BootstrapHelper.KEY_POLICY_READERS].policy.CopyFrom( 1086 Policy(type=typeImplicitMeta, value=IMP( 1087 rule=ruleAny, sub_policy=BootstrapHelper.KEY_POLICY_READERS).SerializeToString())) 1088 write_set.groups[ApplicationGroup].policies[BootstrapHelper.KEY_POLICY_WRITERS].policy.CopyFrom( 1089 Policy(type=typeImplicitMeta, value=IMP( 1090 rule=ruleAny, sub_policy=BootstrapHelper.KEY_POLICY_WRITERS).SerializeToString())) 1091 write_set.groups[ApplicationGroup].policies[BootstrapHelper.KEY_POLICY_ADMINS].policy.CopyFrom( 1092 Policy(type=typeImplicitMeta, value=IMP( 1093 rule=ruleMajority, sub_policy=BootstrapHelper.KEY_POLICY_ADMINS).SerializeToString())) 1094 write_set.groups[ApplicationGroup].mod_policy = "Admins" 1095 for k, v in write_set.groups[ApplicationGroup].groups.iteritems(): 1096 v.mod_policy=BootstrapHelper.KEY_POLICY_ADMINS 1097 config_update = common_dot_configtx_pb2.ConfigUpdate(channel_id=channel_id, 1098 read_set=read_set, 1099 write_set=write_set) 1100 return config_update 1101 1102 def get_group_paths_from_config_group(config_group, paths, current_path = ""): 1103 if len(config_group.groups)==0: 1104 paths.append(current_path) 1105 else: 1106 for group_name, group in config_group.groups.iteritems(): 1107 get_group_paths_from_config_group(config_group=group, current_path="{0}/{1}".format(current_path, group_name), paths=paths) 1108 return paths 1109 1110 1111 def get_group_from_config_path(config_group, config_path): 1112 from os import path 1113 current_config_group = config_group 1114 for group_id in [g.strip("/") for g in path.split(config_path)]: 1115 current_config_group = current_config_group.groups[group_id] 1116 return current_config_group 1117 1118 def create_existing_channel_config_update(system_channel_version, channel_id, input_config_update, config_groups): 1119 # First make a copy of the input config update msg to manipulate 1120 read_set = common_dot_configtx_pb2.ConfigGroup() 1121 read_set.version = system_channel_version 1122 read_set.CopyFrom(input_config_update) 1123 1124 # Now the write_set 1125 write_set = common_dot_configtx_pb2.ConfigGroup() 1126 write_set.CopyFrom(read_set) 1127 1128 # Merge all of the supplied config_groups 1129 for config_group in config_groups: 1130 mergeConfigGroups(write_set, config_group) 1131 1132 # Will build a unique list of group paths from the supplied config_groups 1133 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])) 1134 1135 # Now increment the version for each path. 1136 # TODO: This logic will only work for the case of insertions 1137 for group in [get_group_from_config_path(write_set, g) for g in group_paths]: 1138 group.version += 1 1139 1140 # For now, setting same policies for each 'Non-Org' group 1141 config_update = common_dot_configtx_pb2.ConfigUpdate(channel_id=channel_id, 1142 read_set=read_set, 1143 write_set=write_set) 1144 return config_update 1145 1146 def update_config_group_version_info(input_config_update, proposed_config_group): 1147 for k,v in proposed_config_group.groups.iteritems(): 1148 if k in input_config_update.groups.keys(): 1149 # Make sure all keys of the current group match those of the proposed, if not either added or deleted. 1150 if not set(proposed_config_group.groups[k].groups.keys()) == set(input_config_update.groups[k].groups.keys()): 1151 proposed_config_group.groups[k].version +=1 1152 # Now recurse 1153 update_config_group_version_info(input_config_update.groups[k], proposed_config_group.groups[k]) 1154 1155 # def create_existing_channel_config_update2(system_channel_version, channel_id, input_config_update, config_group): 1156 # # First make a copy of the input config update msg to manipulate 1157 # read_set = common_dot_configtx_pb2.ConfigGroup() 1158 # read_set.version = system_channel_version 1159 # read_set.CopyFrom(config_group) 1160 # 1161 # # Now the write_set 1162 # write_set = common_dot_configtx_pb2.ConfigGroup() 1163 # write_set.CopyFrom(read_set) 1164 # mergeConfigGroups(write_set, config_groups[0]) 1165 # write_set.groups['Application'].groups['peerOrg0'].version +=1 1166 # 1167 # # write_set.groups[ApplicationGroup].version += 1 1168 # # For now, setting same policies for each 'Non-Org' group 1169 # config_update = common_dot_configtx_pb2.ConfigUpdate(channel_id=channel_id, 1170 # read_set=read_set, 1171 # write_set=write_set) 1172 # return config_update 1173 1174 1175 def createConsortium(context, consortium_name, org_names, mod_policy): 1176 channel = common_dot_configtx_pb2.ConfigGroup() 1177 directory = getDirectory(context=context) 1178 channel.groups[ConsortiumsGroup].groups[consortium_name].mod_policy = mod_policy 1179 # Add the orderer org groups MSPConfig info to consortiums group 1180 for consortium_org in [org for org in directory.getOrganizations().values() if org.name in org_names]: 1181 # channel.groups[ConsortiumsGroup].groups[consortium_name].groups[consortium_org.name].mod_policy = BootstrapHelper.KEY_POLICY_ADMINS 1182 channel.groups[ConsortiumsGroup].groups[consortium_name].groups[consortium_org.name].values[BootstrapHelper.KEY_MSP_INFO].value = toValue( 1183 getMSPConfig(org=consortium_org, directory=directory)) 1184 channel.groups[ConsortiumsGroup].groups[consortium_name].groups[consortium_org.name].values[BootstrapHelper.KEY_MSP_INFO].mod_policy=BootstrapHelper.KEY_POLICY_ADMINS 1185 typeImplicitMeta = common_dot_policies_pb2.Policy.PolicyType.Value("IMPLICIT_META") 1186 Policy = common_dot_policies_pb2.Policy 1187 IMP = common_dot_policies_pb2.ImplicitMetaPolicy 1188 ruleAny = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("ANY") 1189 ruleAll = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("ALL") 1190 ruleMajority = common_dot_policies_pb2.ImplicitMetaPolicy.Rule.Value("MAJORITY") 1191 channel.groups[ConsortiumsGroup].groups[consortium_name].values[BootstrapHelper.KEY_CHANNEL_CREATION_POLICY].value = toValue( 1192 Policy(type=typeImplicitMeta, value=IMP( 1193 rule=ruleAll, sub_policy=BootstrapHelper.KEY_POLICY_WRITERS).SerializeToString())) 1194 channel.groups[ConsortiumsGroup].groups[consortium_name].values[BootstrapHelper.KEY_CHANNEL_CREATION_POLICY].mod_policy=BootstrapHelper.KEY_POLICY_ADMINS 1195 # For now, setting same policies for each 'Non-Org' group 1196 orgs = [directory.getOrganization(orgName) for orgName in org_names] 1197 setDefaultPoliciesForOrgs(channel.groups[ConsortiumsGroup], orgs, consortium_name, version=0, policy_version=0) 1198 return channel 1199 1200 def setOrdererBootstrapGenesisBlock(genesisBlock): 1201 'Responsible for setting the GensisBlock for the Orderer nodes upon composition' 1202 1203 1204 def broadcastCreateChannelConfigTx(context, certAlias, composeService, configTxEnvelope, user): 1205 dataFunc = lambda x: configTxEnvelope 1206 user.broadcastMessages(context=context, numMsgsToBroadcast=1, composeService=composeService, 1207 dataFunc=dataFunc) 1208 1209 def get_latest_configuration_block(deliverer_stream_helper, channel_id): 1210 latest_config_block = None 1211 deliverer_stream_helper.seekToRange(chainID=channel_id, start="Newest", end="Newest") 1212 blocks = deliverer_stream_helper.getBlocks() 1213 assert len(blocks) == 1, "Expected single block, received: {0} blocks".format(len(blocks)) 1214 newest_block = blocks[0] 1215 last_config = common_dot_common_pb2.LastConfig() 1216 metadata = common_dot_common_pb2.Metadata() 1217 metadata.ParseFromString(newest_block.metadata.metadata[common_dot_common_pb2.BlockMetadataIndex.Value('LAST_CONFIG')]) 1218 last_config.ParseFromString(metadata.value) 1219 if last_config.index == newest_block.header.number: 1220 latest_config_block = newest_block 1221 else: 1222 deliverer_stream_helper.seekToRange(chainID=channel_id, start=last_config.index, end=last_config.index) 1223 blocks = deliverer_stream_helper.getBlocks() 1224 assert len(blocks) == 1, "Expected single block, received: {0} blocks".format(len(blocks)) 1225 assert len(blocks[0].data.data) == 1, "Expected single transaction for configuration block, instead found {0} transactions".format(len(block.data.data)) 1226 latest_config_block = blocks[0] 1227 return latest_config_block 1228 1229 def get_channel_group_from_config_block(block): 1230 assert len(block.data.data) == 1, "Expected single transaction for configuration block, instead found {0} transactions".format(len(block.data.data)) 1231 e = common_dot_common_pb2.Envelope() 1232 e.ParseFromString(block.data.data[0]) 1233 p = common_dot_common_pb2.Payload() 1234 p.ParseFromString(e.payload) 1235 config_envelope = common_dot_configtx_pb2.ConfigEnvelope() 1236 config_envelope.ParseFromString(p.data) 1237 return config_envelope.config.channel_group 1238 1239 def getArgsFromContextForUser(context, userName): 1240 directory = getDirectory(context) 1241 # Update the chaincodeSpec ctorMsg for invoke 1242 args = [] 1243 if 'table' in context: 1244 if context.table: 1245 # There are function arguments 1246 user = directory.getUser(userName) 1247 # Allow the user to specify expressions referencing tags in the args list 1248 pattern = re.compile('\{(.*)\}$') 1249 for arg in context.table[0].cells: 1250 m = pattern.match(arg) 1251 if m: 1252 # tagName reference found in args list 1253 tagName = m.groups()[0] 1254 # make sure the tagName is found in the users tags 1255 assert tagName in user.tags, "TagName '{0}' not found for user '{1}'".format(tagName, 1256 user.getUserName()) 1257 args.append(user.tags[tagName]) 1258 else: 1259 # No tag referenced, pass the arg 1260 args.append(arg) 1261 return args 1262 1263 1264 def get_args_for_user(args_input, user): 1265 args = [] 1266 pattern = re.compile('\{(.*)\}$') 1267 for arg in args_input: 1268 m = pattern.match(arg) 1269 if m: 1270 # tagName reference found in args list 1271 tagName = m.groups()[0] 1272 # make sure the tagName is found in the users tags 1273 assert tagName in user.tags, "TagName '{0}' not found for user '{1}'".format(tagName, 1274 user.getUserName()) 1275 args.append(user.tags[tagName]) 1276 else: 1277 # No tag referenced, pass the arg 1278 args.append(arg) 1279 return tuple(args) 1280 1281 1282 def getChannelIdFromConfigUpdateEnvelope(config_update_envelope): 1283 config_update = common_dot_configtx_pb2.ConfigUpdate() 1284 config_update.ParseFromString(config_update_envelope.config_update) 1285 return config_update