Managedblockchain member additions (#2983)

* Added some member and proposal functions

* Added additional member and proposal functions

* Fixed admin password return and added update_member along with tests

* Added network removal and member removal proposal

* Fixed failing test

* Fixed Python 2.7 test
This commit is contained in:
James Belleau 2020-05-13 06:28:22 -05:00 committed by GitHub
parent 774a764b69
commit 9bc393801f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 2638 additions and 74 deletions

View File

@ -16,6 +16,16 @@ class BadRequestException(ManagedBlockchainClientError):
)
class InvalidRequestException(ManagedBlockchainClientError):
def __init__(self, pretty_called_method, operation_error):
super(InvalidRequestException, self).__init__(
"InvalidRequestException",
"An error occurred (InvalidRequestException) when calling the {0} operation: {1}".format(
pretty_called_method, operation_error
),
)
class ResourceNotFoundException(ManagedBlockchainClientError):
def __init__(self, pretty_called_method, operation_error):
self.code = 404
@ -25,3 +35,14 @@ class ResourceNotFoundException(ManagedBlockchainClientError):
pretty_called_method, operation_error
),
)
class ResourceLimitExceededException(ManagedBlockchainClientError):
def __init__(self, pretty_called_method, operation_error):
self.code = 429
super(ResourceLimitExceededException, self).__init__(
"ResourceLimitExceededException",
"An error occurred (ResourceLimitExceededException) when calling the {0} operation: {1}".format(
pretty_called_method, operation_error
),
)

View File

@ -1,14 +1,28 @@
from __future__ import unicode_literals
from __future__ import unicode_literals, division
import datetime
import re
from boto3 import Session
from moto.core import BaseBackend, BaseModel
from .exceptions import BadRequestException, ResourceNotFoundException
from .exceptions import (
BadRequestException,
ResourceNotFoundException,
InvalidRequestException,
ResourceLimitExceededException,
)
from .utils import get_network_id, get_member_id
from .utils import (
get_network_id,
get_member_id,
get_proposal_id,
get_invitation_id,
member_name_exist_in_network,
number_of_members_in_network,
admin_password_ok,
)
FRAMEWORKS = [
"HYPERLEDGER_FABRIC",
@ -18,10 +32,20 @@ FRAMEWORKVERSIONS = [
"1.2",
]
EDITIONS = [
"STARTER",
"STANDARD",
]
EDITIONS = {
"STARTER": {
"MaxMembers": 5,
"MaxNodesPerMember": 2,
"AllowedNodeInstanceTypes": ["bc.t3.small", "bc.t3.medium"],
},
"STANDARD": {
"MaxMembers": 14,
"MaxNodesPerMember": 3,
"AllowedNodeInstanceTypes": ["bc.t3", "bc.m5", "bc.c5"],
},
}
VOTEVALUES = ["YES", "NO"]
class ManagedBlockchainNetwork(BaseModel):
@ -48,6 +72,42 @@ class ManagedBlockchainNetwork(BaseModel):
self.member_configuration = member_configuration
self.region = region
@property
def network_name(self):
return self.name
@property
def network_framework(self):
return self.framework
@property
def network_framework_version(self):
return self.frameworkversion
@property
def network_creationdate(self):
return self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z")
@property
def network_description(self):
return self.description
@property
def network_edition(self):
return self.frameworkconfiguration["Fabric"]["Edition"]
@property
def vote_pol_proposal_duration(self):
return self.voting_policy["ApprovalThresholdPolicy"]["ProposalDurationInHours"]
@property
def vote_pol_threshold_percentage(self):
return self.voting_policy["ApprovalThresholdPolicy"]["ThresholdPercentage"]
@property
def vote_pol_threshold_comparator(self):
return self.voting_policy["ApprovalThresholdPolicy"]["ThresholdComparator"]
def to_dict(self):
# Format for list_networks
d = {
@ -63,7 +123,7 @@ class ManagedBlockchainNetwork(BaseModel):
return d
def get_format(self):
# Format for get_networks
# Format for get_network
frameworkattributes = {
"Fabric": {
"OrderingServiceEndpoint": "orderer.{0}.managedblockchain.{1}.amazonaws.com:30001".format(
@ -93,9 +153,272 @@ class ManagedBlockchainNetwork(BaseModel):
return d
class ManagedBlockchainProposal(BaseModel):
def __init__(
self,
id,
networkid,
memberid,
membername,
numofmembers,
actions,
network_expirtation,
network_threshold,
network_threshold_comp,
description=None,
):
# In general, passing all values instead of creating
# an apparatus to look them up
self.id = id
self.networkid = networkid
self.memberid = memberid
self.membername = membername
self.numofmembers = numofmembers
self.actions = actions
self.network_expirtation = network_expirtation
self.network_threshold = network_threshold
self.network_threshold_comp = network_threshold_comp
self.description = description
self.creationdate = datetime.datetime.utcnow()
self.expirtationdate = self.creationdate + datetime.timedelta(
hours=network_expirtation
)
self.yes_vote_count = 0
self.no_vote_count = 0
self.outstanding_vote_count = self.numofmembers
self.status = "IN_PROGRESS"
self.votes = {}
@property
def network_id(self):
return self.networkid
@property
def proposal_status(self):
return self.status
@property
def proposal_votes(self):
return self.votes
def proposal_actions(self, action_type):
default_return = []
if action_type.lower() == "invitations":
if "Invitations" in self.actions:
return self.actions["Invitations"]
elif action_type.lower() == "removals":
if "Removals" in self.actions:
return self.actions["Removals"]
return default_return
def to_dict(self):
# Format for list_proposals
d = {
"ProposalId": self.id,
"ProposedByMemberId": self.memberid,
"ProposedByMemberName": self.membername,
"Status": self.status,
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
}
return d
def get_format(self):
# Format for get_proposal
d = {
"ProposalId": self.id,
"NetworkId": self.networkid,
"Actions": self.actions,
"ProposedByMemberId": self.memberid,
"ProposedByMemberName": self.membername,
"Status": self.status,
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"YesVoteCount": self.yes_vote_count,
"NoVoteCount": self.no_vote_count,
"OutstandingVoteCount": self.outstanding_vote_count,
}
if self.description is not None:
d["Description"] = self.description
return d
def set_vote(self, votermemberid, votermembername, vote):
if datetime.datetime.utcnow() > self.expirtationdate:
self.status = "EXPIRED"
return False
if vote.upper() == "YES":
self.yes_vote_count += 1
else:
self.no_vote_count += 1
self.outstanding_vote_count -= 1
perct_yes = (self.yes_vote_count / self.numofmembers) * 100
perct_no = (self.no_vote_count / self.numofmembers) * 100
self.votes[votermemberid] = {
"MemberId": votermemberid,
"MemberName": votermembername,
"Vote": vote.upper(),
}
if self.network_threshold_comp == "GREATER_THAN_OR_EQUAL_TO":
if perct_yes >= self.network_threshold:
self.status = "APPROVED"
elif perct_no >= self.network_threshold:
self.status = "REJECTED"
else:
if perct_yes > self.network_threshold:
self.status = "APPROVED"
elif perct_no > self.network_threshold:
self.status = "REJECTED"
return True
class ManagedBlockchainInvitation(BaseModel):
def __init__(
self,
id,
networkid,
networkname,
networkframework,
networkframeworkversion,
networkcreationdate,
region,
networkdescription=None,
):
self.id = id
self.networkid = networkid
self.networkname = networkname
self.networkdescription = networkdescription
self.networkframework = networkframework
self.networkframeworkversion = networkframeworkversion
self.networkstatus = "AVAILABLE"
self.networkcreationdate = networkcreationdate
self.status = "PENDING"
self.region = region
self.creationdate = datetime.datetime.utcnow()
self.expirtationdate = self.creationdate + datetime.timedelta(days=7)
@property
def invitation_status(self):
return self.status
@property
def invitation_networkid(self):
return self.networkid
def to_dict(self):
d = {
"InvitationId": self.id,
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"ExpirationDate": self.expirtationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"Status": self.status,
"NetworkSummary": {
"Id": self.networkid,
"Name": self.networkname,
"Framework": self.networkframework,
"FrameworkVersion": self.networkframeworkversion,
"Status": self.networkstatus,
"CreationDate": self.networkcreationdate,
},
}
if self.networkdescription is not None:
d["NetworkSummary"]["Description"] = self.networkdescription
return d
def accept_invitation(self):
self.status = "ACCEPTED"
def reject_invitation(self):
self.status = "REJECTED"
def set_network_status(self, network_status):
self.networkstatus = network_status
class ManagedBlockchainMember(BaseModel):
def __init__(
self, id, networkid, member_configuration, region,
):
self.creationdate = datetime.datetime.utcnow()
self.id = id
self.networkid = networkid
self.member_configuration = member_configuration
self.status = "AVAILABLE"
self.region = region
self.description = None
@property
def network_id(self):
return self.networkid
@property
def name(self):
return self.member_configuration["Name"]
@property
def member_status(self):
return self.status
def to_dict(self):
# Format for list_members
d = {
"Id": self.id,
"Name": self.member_configuration["Name"],
"Status": self.status,
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
"IsOwned": True,
}
if "Description" in self.member_configuration:
self.description = self.member_configuration["Description"]
return d
def get_format(self):
# Format for get_member
frameworkattributes = {
"Fabric": {
"AdminUsername": self.member_configuration["FrameworkConfiguration"][
"Fabric"
]["AdminUsername"],
"CaEndpoint": "ca.{0}.{1}.managedblockchain.{2}.amazonaws.com:30002".format(
self.id.lower(), self.networkid.lower(), self.region
),
}
}
d = {
"NetworkId": self.networkid,
"Id": self.id,
"Name": self.name,
"FrameworkAttributes": frameworkattributes,
"LogPublishingConfiguration": self.member_configuration[
"LogPublishingConfiguration"
],
"Status": self.status,
"CreationDate": self.creationdate.strftime("%Y-%m-%dT%H:%M:%S.%f%z"),
}
if "Description" in self.member_configuration:
d["Description"] = self.description
return d
def delete(self):
self.status = "DELETED"
def update(self, logpublishingconfiguration):
self.member_configuration[
"LogPublishingConfiguration"
] = logpublishingconfiguration
class ManagedBlockchainBackend(BaseBackend):
def __init__(self, region_name):
self.networks = {}
self.members = {}
self.proposals = {}
self.invitations = {}
self.region_name = region_name
def reset(self):
@ -113,14 +436,6 @@ class ManagedBlockchainBackend(BaseBackend):
member_configuration,
description=None,
):
self.name = name
self.framework = framework
self.frameworkversion = frameworkversion
self.frameworkconfiguration = frameworkconfiguration
self.voting_policy = voting_policy
self.member_configuration = member_configuration
self.description = description
# Check framework
if framework not in FRAMEWORKS:
raise BadRequestException("CreateNetwork", "Invalid request body")
@ -141,19 +456,25 @@ class ManagedBlockchainBackend(BaseBackend):
## Generate network ID
network_id = get_network_id()
## Generate memberid ID - will need to actually create member
## Generate memberid ID and initial member
member_id = get_member_id()
self.members[member_id] = ManagedBlockchainMember(
id=member_id,
networkid=network_id,
member_configuration=member_configuration,
region=self.region_name,
)
self.networks[network_id] = ManagedBlockchainNetwork(
id=network_id,
name=name,
framework=self.framework,
frameworkversion=self.frameworkversion,
frameworkconfiguration=self.frameworkconfiguration,
voting_policy=self.voting_policy,
member_configuration=self.member_configuration,
framework=framework,
frameworkversion=frameworkversion,
frameworkconfiguration=frameworkconfiguration,
voting_policy=voting_policy,
member_configuration=member_configuration,
region=self.region_name,
description=self.description,
description=description,
)
# Return the network and member ID
@ -166,10 +487,324 @@ class ManagedBlockchainBackend(BaseBackend):
def get_network(self, network_id):
if network_id not in self.networks:
raise ResourceNotFoundException(
"CreateNetwork", "Network {0} not found".format(network_id)
"GetNetwork", "Network {0} not found.".format(network_id)
)
return self.networks.get(network_id)
def create_proposal(
self, networkid, memberid, actions, description=None,
):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"CreateProposal", "Network {0} not found.".format(networkid)
)
# Check if member exists
if memberid not in self.members:
raise ResourceNotFoundException(
"CreateProposal", "Member {0} not found.".format(memberid)
)
# CLI docs say that Invitations and Removals cannot both be passed - but it does
# not throw an error and can be performed
if "Invitations" in actions:
for propinvitation in actions["Invitations"]:
if re.match("[0-9]{12}", propinvitation["Principal"]) is None:
raise InvalidRequestException(
"CreateProposal",
"Account ID format specified in proposal is not valid.",
)
if "Removals" in actions:
for propmember in actions["Removals"]:
if propmember["MemberId"] not in self.members:
raise InvalidRequestException(
"CreateProposal",
"Member ID format specified in proposal is not valid.",
)
## Generate proposal ID
proposal_id = get_proposal_id()
self.proposals[proposal_id] = ManagedBlockchainProposal(
id=proposal_id,
networkid=networkid,
memberid=memberid,
membername=self.members.get(memberid).name,
numofmembers=number_of_members_in_network(self.members, networkid),
actions=actions,
network_expirtation=self.networks.get(networkid).vote_pol_proposal_duration,
network_threshold=self.networks.get(
networkid
).vote_pol_threshold_percentage,
network_threshold_comp=self.networks.get(
networkid
).vote_pol_threshold_comparator,
description=description,
)
# Return the proposal ID
d = {"ProposalId": proposal_id}
return d
def list_proposals(self, networkid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"ListProposals", "Network {0} not found.".format(networkid)
)
proposalsfornetwork = []
for proposal_id in self.proposals:
if self.proposals.get(proposal_id).network_id == networkid:
proposalsfornetwork.append(self.proposals[proposal_id])
return proposalsfornetwork
def get_proposal(self, networkid, proposalid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"GetProposal", "Network {0} not found.".format(networkid)
)
if proposalid not in self.proposals:
raise ResourceNotFoundException(
"GetProposal", "Proposal {0} not found.".format(proposalid)
)
return self.proposals.get(proposalid)
def vote_on_proposal(self, networkid, proposalid, votermemberid, vote):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"VoteOnProposal", "Network {0} not found.".format(networkid)
)
if proposalid not in self.proposals:
raise ResourceNotFoundException(
"VoteOnProposal", "Proposal {0} not found.".format(proposalid)
)
if votermemberid not in self.members:
raise ResourceNotFoundException(
"VoteOnProposal", "Member {0} not found.".format(votermemberid)
)
if vote.upper() not in VOTEVALUES:
raise BadRequestException("VoteOnProposal", "Invalid request body")
# Check to see if this member already voted
# TODO Verify exception
if votermemberid in self.proposals.get(proposalid).proposal_votes:
raise BadRequestException("VoteOnProposal", "Invalid request body")
# Will return false if vote was not cast (e.g., status wrong)
if self.proposals.get(proposalid).set_vote(
votermemberid, self.members.get(votermemberid).name, vote.upper()
):
if self.proposals.get(proposalid).proposal_status == "APPROVED":
## Generate invitations
for propinvitation in self.proposals.get(proposalid).proposal_actions(
"Invitations"
):
invitation_id = get_invitation_id()
self.invitations[invitation_id] = ManagedBlockchainInvitation(
id=invitation_id,
networkid=networkid,
networkname=self.networks.get(networkid).network_name,
networkframework=self.networks.get(networkid).network_framework,
networkframeworkversion=self.networks.get(
networkid
).network_framework_version,
networkcreationdate=self.networks.get(
networkid
).network_creationdate,
region=self.region_name,
networkdescription=self.networks.get(
networkid
).network_description,
)
## Delete members
for propmember in self.proposals.get(proposalid).proposal_actions(
"Removals"
):
self.delete_member(networkid, propmember["MemberId"])
def list_proposal_votes(self, networkid, proposalid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"ListProposalVotes", "Network {0} not found.".format(networkid)
)
if proposalid not in self.proposals:
raise ResourceNotFoundException(
"ListProposalVotes", "Proposal {0} not found.".format(proposalid)
)
# Output the vote summaries
proposalvotesfornetwork = []
for proposal_id in self.proposals:
if self.proposals.get(proposal_id).network_id == networkid:
for pvmemberid in self.proposals.get(proposal_id).proposal_votes:
proposalvotesfornetwork.append(
self.proposals.get(proposal_id).proposal_votes[pvmemberid]
)
return proposalvotesfornetwork
def list_invitations(self):
return self.invitations.values()
def reject_invitation(self, invitationid):
if invitationid not in self.invitations:
raise ResourceNotFoundException(
"RejectInvitation", "InvitationId {0} not found.".format(invitationid)
)
self.invitations.get(invitationid).reject_invitation()
def create_member(
self, invitationid, networkid, member_configuration,
):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"CreateMember", "Network {0} not found.".format(networkid)
)
if invitationid not in self.invitations:
raise InvalidRequestException(
"CreateMember", "Invitation {0} not valid".format(invitationid)
)
if self.invitations.get(invitationid).invitation_status != "PENDING":
raise InvalidRequestException(
"CreateMember", "Invitation {0} not valid".format(invitationid)
)
if (
member_name_exist_in_network(
self.members, networkid, member_configuration["Name"]
)
is True
):
raise InvalidRequestException(
"CreateMember",
"Member name {0} already exists in network {1}.".format(
member_configuration["Name"], networkid
),
)
networkedition = self.networks.get(networkid).network_edition
if (
number_of_members_in_network(self.members, networkid)
>= EDITIONS[networkedition]["MaxMembers"]
):
raise ResourceLimitExceededException(
"CreateMember",
"You cannot create a member in network {0}.{1} is the maximum number of members allowed in a {2} Edition network.".format(
networkid, EDITIONS[networkedition]["MaxMembers"], networkedition
),
)
memberadminpassword = member_configuration["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
]
if admin_password_ok(memberadminpassword) is False:
raise BadRequestException("CreateMember", "Invalid request body")
member_id = get_member_id()
self.members[member_id] = ManagedBlockchainMember(
id=member_id,
networkid=networkid,
member_configuration=member_configuration,
region=self.region_name,
)
# Accept the invitaiton
self.invitations.get(invitationid).accept_invitation()
# Return the member ID
d = {"MemberId": member_id}
return d
def list_members(self, networkid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"ListMembers", "Network {0} not found.".format(networkid)
)
membersfornetwork = []
for member_id in self.members:
if self.members.get(member_id).network_id == networkid:
membersfornetwork.append(self.members[member_id])
return membersfornetwork
def get_member(self, networkid, memberid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"GetMember", "Network {0} not found.".format(networkid)
)
if memberid not in self.members:
raise ResourceNotFoundException(
"GetMember", "Member {0} not found.".format(memberid)
)
## Cannot get a member than has been delted (it does show up in the list)
if self.members.get(memberid).member_status == "DELETED":
raise ResourceNotFoundException(
"GetMember", "Member {0} not found.".format(memberid)
)
return self.members.get(memberid)
def delete_member(self, networkid, memberid):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"DeleteMember", "Network {0} not found.".format(networkid)
)
if memberid not in self.members:
raise ResourceNotFoundException(
"DeleteMember", "Member {0} not found.".format(memberid)
)
self.members.get(memberid).delete()
# Is this the last member in the network? (all set to DELETED)
if number_of_members_in_network(
self.members, networkid, member_status="DELETED"
) == len(self.members):
# Set network status to DELETED for all invitations
for invitation_id in self.invitations:
if (
self.invitations.get(invitation_id).invitation_networkid
== networkid
):
self.invitations.get(invitation_id).set_network_status("DELETED")
# Remove network
del self.networks[networkid]
def update_member(self, networkid, memberid, logpublishingconfiguration):
# Check if network exists
if networkid not in self.networks:
raise ResourceNotFoundException(
"UpdateMember", "Network {0} not found.".format(networkid)
)
if memberid not in self.members:
raise ResourceNotFoundException(
"UpdateMember", "Member {0} not found.".format(memberid)
)
self.members.get(memberid).update(logpublishingconfiguration)
managedblockchain_backends = {}
for region in Session().get_available_regions("managedblockchain"):

View File

@ -8,6 +8,9 @@ from .models import managedblockchain_backends
from .utils import (
region_from_managedblckchain_url,
networkid_from_managedblockchain_url,
proposalid_from_managedblockchain_url,
invitationid_from_managedblockchain_url,
memberid_from_managedblockchain_url,
)
@ -66,7 +69,7 @@ class ManagedBlockchainResponse(BaseResponse):
member_configuration,
description,
)
return 201, headers, json.dumps(response)
return 200, headers, json.dumps(response)
@classmethod
def networkid_response(clazz, request, full_url, headers):
@ -88,3 +91,236 @@ class ManagedBlockchainResponse(BaseResponse):
response = json.dumps({"Network": mbcnetwork.get_format()})
headers["content-type"] = "application/json"
return 200, headers, response
@classmethod
def proposal_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._proposal_response(request, full_url, headers)
def _proposal_response(self, request, full_url, headers):
method = request.method
if hasattr(request, "body"):
body = request.body
else:
body = request.data
parsed_url = urlparse(full_url)
querystring = parse_qs(parsed_url.query, keep_blank_values=True)
network_id = networkid_from_managedblockchain_url(full_url)
if method == "GET":
return self._all_proposals_response(network_id, headers)
elif method == "POST":
json_body = json.loads(body.decode("utf-8"))
return self._proposal_response_post(
network_id, json_body, querystring, headers
)
def _all_proposals_response(self, network_id, headers):
proposals = self.backend.list_proposals(network_id)
response = json.dumps(
{"Proposals": [proposal.to_dict() for proposal in proposals]}
)
headers["content-type"] = "application/json"
return 200, headers, response
def _proposal_response_post(self, network_id, json_body, querystring, headers):
memberid = json_body["MemberId"]
actions = json_body["Actions"]
# Optional
description = json_body.get("Description", None)
response = self.backend.create_proposal(
network_id, memberid, actions, description,
)
return 200, headers, json.dumps(response)
@classmethod
def proposalid_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._proposalid_response(request, full_url, headers)
def _proposalid_response(self, request, full_url, headers):
method = request.method
network_id = networkid_from_managedblockchain_url(full_url)
if method == "GET":
proposal_id = proposalid_from_managedblockchain_url(full_url)
return self._proposalid_response_get(network_id, proposal_id, headers)
def _proposalid_response_get(self, network_id, proposal_id, headers):
proposal = self.backend.get_proposal(network_id, proposal_id)
response = json.dumps({"Proposal": proposal.get_format()})
headers["content-type"] = "application/json"
return 200, headers, response
@classmethod
def proposal_votes_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._proposal_votes_response(request, full_url, headers)
def _proposal_votes_response(self, request, full_url, headers):
method = request.method
if hasattr(request, "body"):
body = request.body
else:
body = request.data
parsed_url = urlparse(full_url)
querystring = parse_qs(parsed_url.query, keep_blank_values=True)
network_id = networkid_from_managedblockchain_url(full_url)
proposal_id = proposalid_from_managedblockchain_url(full_url)
if method == "GET":
return self._all_proposal_votes_response(network_id, proposal_id, headers)
elif method == "POST":
json_body = json.loads(body.decode("utf-8"))
return self._proposal_votes_response_post(
network_id, proposal_id, json_body, querystring, headers
)
def _all_proposal_votes_response(self, network_id, proposal_id, headers):
proposalvotes = self.backend.list_proposal_votes(network_id, proposal_id)
response = json.dumps({"ProposalVotes": proposalvotes})
headers["content-type"] = "application/json"
return 200, headers, response
def _proposal_votes_response_post(
self, network_id, proposal_id, json_body, querystring, headers
):
votermemberid = json_body["VoterMemberId"]
vote = json_body["Vote"]
self.backend.vote_on_proposal(
network_id, proposal_id, votermemberid, vote,
)
return 200, headers, ""
@classmethod
def invitation_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._invitation_response(request, full_url, headers)
def _invitation_response(self, request, full_url, headers):
method = request.method
if method == "GET":
return self._all_invitation_response(request, full_url, headers)
def _all_invitation_response(self, request, full_url, headers):
invitations = self.backend.list_invitations()
response = json.dumps(
{"Invitations": [invitation.to_dict() for invitation in invitations]}
)
headers["content-type"] = "application/json"
return 200, headers, response
@classmethod
def invitationid_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._invitationid_response(request, full_url, headers)
def _invitationid_response(self, request, full_url, headers):
method = request.method
if method == "DELETE":
invitation_id = invitationid_from_managedblockchain_url(full_url)
return self._invitationid_response_delete(invitation_id, headers)
def _invitationid_response_delete(self, invitation_id, headers):
self.backend.reject_invitation(invitation_id)
headers["content-type"] = "application/json"
return 200, headers, ""
@classmethod
def member_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._member_response(request, full_url, headers)
def _member_response(self, request, full_url, headers):
method = request.method
if hasattr(request, "body"):
body = request.body
else:
body = request.data
parsed_url = urlparse(full_url)
querystring = parse_qs(parsed_url.query, keep_blank_values=True)
network_id = networkid_from_managedblockchain_url(full_url)
if method == "GET":
return self._all_members_response(network_id, headers)
elif method == "POST":
json_body = json.loads(body.decode("utf-8"))
return self._member_response_post(
network_id, json_body, querystring, headers
)
def _all_members_response(self, network_id, headers):
members = self.backend.list_members(network_id)
response = json.dumps({"Members": [member.to_dict() for member in members]})
headers["content-type"] = "application/json"
return 200, headers, response
def _member_response_post(self, network_id, json_body, querystring, headers):
invitationid = json_body["InvitationId"]
member_configuration = json_body["MemberConfiguration"]
response = self.backend.create_member(
invitationid, network_id, member_configuration,
)
return 200, headers, json.dumps(response)
@classmethod
def memberid_response(clazz, request, full_url, headers):
region_name = region_from_managedblckchain_url(full_url)
response_instance = ManagedBlockchainResponse(
managedblockchain_backends[region_name]
)
return response_instance._memberid_response(request, full_url, headers)
def _memberid_response(self, request, full_url, headers):
method = request.method
if hasattr(request, "body"):
body = request.body
else:
body = request.data
network_id = networkid_from_managedblockchain_url(full_url)
member_id = memberid_from_managedblockchain_url(full_url)
if method == "GET":
return self._memberid_response_get(network_id, member_id, headers)
elif method == "PATCH":
json_body = json.loads(body.decode("utf-8"))
return self._memberid_response_patch(
network_id, member_id, json_body, headers
)
elif method == "DELETE":
return self._memberid_response_delete(network_id, member_id, headers)
def _memberid_response_get(self, network_id, member_id, headers):
member = self.backend.get_member(network_id, member_id)
response = json.dumps({"Member": member.get_format()})
headers["content-type"] = "application/json"
return 200, headers, response
def _memberid_response_patch(self, network_id, member_id, json_body, headers):
logpublishingconfiguration = json_body["LogPublishingConfiguration"]
self.backend.update_member(
network_id, member_id, logpublishingconfiguration,
)
return 200, headers, ""
def _memberid_response_delete(self, network_id, member_id, headers):
self.backend.delete_member(network_id, member_id)
headers["content-type"] = "application/json"
return 200, headers, ""

View File

@ -6,4 +6,11 @@ url_bases = ["https?://managedblockchain.(.+).amazonaws.com"]
url_paths = {
"{0}/networks$": ManagedBlockchainResponse.network_response,
"{0}/networks/(?P<networkid>[^/.]+)$": ManagedBlockchainResponse.networkid_response,
"{0}/networks/(?P<networkid>[^/.]+)/proposals$": ManagedBlockchainResponse.proposal_response,
"{0}/networks/(?P<networkid>[^/.]+)/proposals/(?P<proposalid>[^/.]+)$": ManagedBlockchainResponse.proposalid_response,
"{0}/networks/(?P<networkid>[^/.]+)/proposals/(?P<proposalid>[^/.]+)/votes$": ManagedBlockchainResponse.proposal_votes_response,
"{0}/invitations$": ManagedBlockchainResponse.invitation_response,
"{0}/invitations/(?P<invitationid>[^/.]+)$": ManagedBlockchainResponse.invitationid_response,
"{0}/networks/(?P<networkid>[^/.]+)/members$": ManagedBlockchainResponse.member_response,
"{0}/networks/(?P<networkid>[^/.]+)/members/(?P<memberid>[^/.]+)$": ManagedBlockchainResponse.memberid_response,
}

View File

@ -1,4 +1,5 @@
import random
import re
import string
from six.moves.urllib.parse import urlparse
@ -6,15 +7,18 @@ from six.moves.urllib.parse import urlparse
def region_from_managedblckchain_url(url):
domain = urlparse(url).netloc
region = "us-east-1"
if "." in domain:
return domain.split(".")[1]
else:
return "us-east-1"
region = domain.split(".")[1]
return region
def networkid_from_managedblockchain_url(full_url):
return full_url.split("/")[-1]
id_search = re.search("\/n-[A-Z0-9]{26}", full_url, re.IGNORECASE)
return_id = None
if id_search:
return_id = id_search.group(0).replace("/", "")
return return_id
def get_network_id():
@ -23,7 +27,80 @@ def get_network_id():
)
def memberid_from_managedblockchain_url(full_url):
id_search = re.search("\/m-[A-Z0-9]{26}", full_url, re.IGNORECASE)
return_id = None
if id_search:
return_id = id_search.group(0).replace("/", "")
return return_id
def get_member_id():
return "m-" + "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(26)
)
def proposalid_from_managedblockchain_url(full_url):
id_search = re.search("\/p-[A-Z0-9]{26}", full_url, re.IGNORECASE)
return_id = None
if id_search:
return_id = id_search.group(0).replace("/", "")
return return_id
def get_proposal_id():
return "p-" + "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(26)
)
def invitationid_from_managedblockchain_url(full_url):
id_search = re.search("\/in-[A-Z0-9]{26}", full_url, re.IGNORECASE)
return_id = None
if id_search:
return_id = id_search.group(0).replace("/", "")
return return_id
def get_invitation_id():
return "in-" + "".join(
random.choice(string.ascii_uppercase + string.digits) for _ in range(26)
)
def member_name_exist_in_network(members, networkid, membername):
membernamexists = False
for member_id in members:
if members.get(member_id).network_id == networkid:
if members.get(member_id).name == membername:
membernamexists = True
break
return membernamexists
def number_of_members_in_network(members, networkid, member_status=None):
return len(
[
membid
for membid in members
if members.get(membid).network_id == networkid
and (
member_status is None
or members.get(membid).member_status == member_status
)
]
)
def admin_password_ok(password):
if not re.search("[a-z]", password):
return False
elif not re.search("[A-Z]", password):
return False
elif not re.search("[0-9]", password):
return False
elif re.search("['\"@\\/]", password):
return False
else:
return True

View File

@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@ -0,0 +1,67 @@
from __future__ import unicode_literals
default_frameworkconfiguration = {"Fabric": {"Edition": "STARTER"}}
default_votingpolicy = {
"ApprovalThresholdPolicy": {
"ThresholdPercentage": 50,
"ProposalDurationInHours": 24,
"ThresholdComparator": "GREATER_THAN_OR_EQUAL_TO",
}
}
default_memberconfiguration = {
"Name": "testmember1",
"Description": "Test Member 1",
"FrameworkConfiguration": {
"Fabric": {"AdminUsername": "admin", "AdminPassword": "Admin12345"}
},
"LogPublishingConfiguration": {
"Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": False}}}
},
}
default_policy_actions = {"Invitations": [{"Principal": "123456789012"}]}
multiple_policy_actions = {
"Invitations": [{"Principal": "123456789012"}, {"Principal": "123456789013"}]
}
def member_id_exist_in_list(members, memberid):
memberidxists = False
for member in members:
if member["Id"] == memberid:
memberidxists = True
break
return memberidxists
def create_member_configuration(
name, adminuser, adminpass, cloudwatchenabled, description=None
):
d = {
"Name": name,
"FrameworkConfiguration": {
"Fabric": {"AdminUsername": adminuser, "AdminPassword": adminpass}
},
"LogPublishingConfiguration": {
"Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": cloudwatchenabled}}}
},
}
if description is not None:
d["Description"] = description
return d
def select_invitation_id_for_network(invitations, networkid, status=None):
# Get invitations based on network and maybe status
invitationsfornetwork = []
for invitation in invitations:
if invitation["NetworkSummary"]["Id"] == networkid:
if status is None or invitation["Status"] == status:
invitationsfornetwork.append(invitation["InvitationId"])
return invitationsfornetwork

View File

@ -0,0 +1,142 @@
from __future__ import unicode_literals
import boto3
import sure # noqa
from moto.managedblockchain.exceptions import BadRequestException
from moto import mock_managedblockchain
from . import helpers
@mock_managedblockchain
def test_create_2_invitations():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.multiple_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
response["Invitations"].should.have.length_of(2)
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][0]["Status"].should.equal("PENDING")
response["Invitations"][1]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][1]["Status"].should.equal("PENDING")
@mock_managedblockchain
def test_reject_invitation():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][0]["Status"].should.equal("PENDING")
invitation_id = response["Invitations"][0]["InvitationId"]
# Reject - thanks but no thanks
response = conn.reject_invitation(InvitationId=invitation_id)
# Check the invitation status
response = conn.list_invitations()
response["Invitations"][0]["InvitationId"].should.equal(invitation_id)
response["Invitations"][0]["Status"].should.equal("REJECTED")
@mock_managedblockchain
def test_reject_invitation_badinvitation():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
response = conn.reject_invitation.when.called_with(
InvitationId="in-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "InvitationId in-ABCDEFGHIJKLMNOP0123456789 not found.")

View File

@ -0,0 +1,669 @@
from __future__ import unicode_literals
import boto3
import sure # noqa
from moto.managedblockchain.exceptions import BadRequestException
from moto import mock_managedblockchain
from . import helpers
@mock_managedblockchain
def test_create_another_member():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Description="Test Network 1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][0]["Status"].should.equal("PENDING")
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False
),
)
member_id2 = response["MemberId"]
# Check the invitation status
response = conn.list_invitations()
response["Invitations"][0]["InvitationId"].should.equal(invitation_id)
response["Invitations"][0]["Status"].should.equal("ACCEPTED")
# Find member in full list
response = conn.list_members(NetworkId=network_id)
members = response["Members"]
members.should.have.length_of(2)
helpers.member_id_exist_in_list(members, member_id2).should.equal(True)
# Get member 2 details
response = conn.get_member(NetworkId=network_id, MemberId=member_id2)
response["Member"]["Name"].should.equal("testmember2")
# Update member
logconfignewenabled = not helpers.default_memberconfiguration[
"LogPublishingConfiguration"
]["Fabric"]["CaLogs"]["Cloudwatch"]["Enabled"]
logconfignew = {
"Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": logconfignewenabled}}}
}
conn.update_member(
NetworkId=network_id,
MemberId=member_id2,
LogPublishingConfiguration=logconfignew,
)
# Get member 2 details
response = conn.get_member(NetworkId=network_id, MemberId=member_id2)
response["Member"]["LogPublishingConfiguration"]["Fabric"]["CaLogs"]["Cloudwatch"][
"Enabled"
].should.equal(logconfignewenabled)
@mock_managedblockchain
def test_create_another_member_withopts():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][0]["Status"].should.equal("PENDING")
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2"
),
)
member_id2 = response["MemberId"]
# Check the invitation status
response = conn.list_invitations()
response["Invitations"][0]["InvitationId"].should.equal(invitation_id)
response["Invitations"][0]["Status"].should.equal("ACCEPTED")
# Find member in full list
response = conn.list_members(NetworkId=network_id)
members = response["Members"]
members.should.have.length_of(2)
helpers.member_id_exist_in_list(members, member_id2).should.equal(True)
# Get member 2 details
response = conn.get_member(NetworkId=network_id, MemberId=member_id2)
response["Member"]["Description"].should.equal("Test Member 2")
# Try to create member with already used invitation
response = conn.create_member.when.called_with(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2 Duplicate"
),
).should.throw(Exception, "Invitation {0} not valid".format(invitation_id))
# Delete member 2
conn.delete_member(NetworkId=network_id, MemberId=member_id2)
# Member is still in the list
response = conn.list_members(NetworkId=network_id)
members = response["Members"]
members.should.have.length_of(2)
# But cannot get
response = conn.get_member.when.called_with(
NetworkId=network_id, MemberId=member_id2,
).should.throw(Exception, "Member {0} not found".format(member_id2))
# Delete member 1
conn.delete_member(NetworkId=network_id, MemberId=member_id)
# Network should be gone
response = conn.list_networks()
mbcnetworks = response["Networks"]
mbcnetworks.should.have.length_of(0)
# Verify the invitation network status is DELETED
# Get the invitation
response = conn.list_invitations()
response["Invitations"].should.have.length_of(1)
response["Invitations"][0]["NetworkSummary"]["Id"].should.equal(network_id)
response["Invitations"][0]["NetworkSummary"]["Status"].should.equal("DELETED")
@mock_managedblockchain
def test_create_and_delete_member():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal (create additional member)
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2"
),
)
member_id2 = response["MemberId"]
both_policy_actions = {
"Invitations": [{"Principal": "123456789012"}],
"Removals": [{"MemberId": member_id2}],
}
# Create proposal (invite and remove member)
response = conn.create_proposal(
NetworkId=network_id, MemberId=member_id, Actions=both_policy_actions,
)
proposal_id2 = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id2)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id2,
VoterMemberId=member_id,
Vote="YES",
)
# Check the invitation status
response = conn.list_invitations()
invitations = helpers.select_invitation_id_for_network(
response["Invitations"], network_id, "PENDING"
)
invitations.should.have.length_of(1)
# Member is still in the list
response = conn.list_members(NetworkId=network_id)
members = response["Members"]
members.should.have.length_of(2)
foundmember2 = False
for member in members:
if member["Id"] == member_id2 and member["Status"] == "DELETED":
foundmember2 = True
foundmember2.should.equal(True)
@mock_managedblockchain
def test_create_too_many_members():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create 4 more members - create invitations for 5
for counter in range(2, 7):
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
for counter in range(2, 6):
# Get the invitation
response = conn.list_invitations()
invitation_id = helpers.select_invitation_id_for_network(
response["Invitations"], network_id, "PENDING"
)[0]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember" + str(counter),
"admin",
"Admin12345",
False,
"Test Member " + str(counter),
),
)
member_id = response["MemberId"]
# Find member in full list
response = conn.list_members(NetworkId=network_id)
members = response["Members"]
members.should.have.length_of(counter)
helpers.member_id_exist_in_list(members, member_id).should.equal(True)
# Get member details
response = conn.get_member(NetworkId=network_id, MemberId=member_id)
response["Member"]["Description"].should.equal("Test Member " + str(counter))
# Try to create the sixth
response = conn.list_invitations()
invitation_id = helpers.select_invitation_id_for_network(
response["Invitations"], network_id, "PENDING"
)[0]
# Try to create member with already used invitation
response = conn.create_member.when.called_with(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember6", "admin", "Admin12345", False, "Test Member 6"
),
).should.throw(
Exception,
"5 is the maximum number of members allowed in a STARTER Edition network",
)
@mock_managedblockchain
def test_create_another_member_alreadyhave():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Description="Test Network 1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
# Should fail trying to create with same name
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember1", "admin", "Admin12345", False
),
).should.throw(
Exception,
"Member name {0} already exists in network {1}".format(
"testmember1", network_id
),
)
@mock_managedblockchain
def test_create_another_member_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.create_member.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
InvitationId="id-ABCDEFGHIJKLMNOP0123456789",
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False
),
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_create_another_member_badinvitation():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId="in-ABCDEFGHIJKLMNOP0123456789",
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False
),
).should.throw(Exception, "Invitation in-ABCDEFGHIJKLMNOP0123456789 not valid")
@mock_managedblockchain
def test_create_another_member_adminpassword():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
badadminpassmemberconf = helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False
)
# Too short
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
] = "badap"
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=badadminpassmemberconf,
).should.throw(
Exception,
"Invalid length for parameter MemberConfiguration.FrameworkConfiguration.Fabric.AdminPassword",
)
# No uppercase or numbers
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
] = "badadminpwd"
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=badadminpassmemberconf,
).should.throw(Exception, "Invalid request body")
# No lowercase or numbers
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
] = "BADADMINPWD"
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=badadminpassmemberconf,
).should.throw(Exception, "Invalid request body")
# No numbers
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
] = "badAdminpwd"
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=badadminpassmemberconf,
).should.throw(Exception, "Invalid request body")
# Invalid character
badadminpassmemberconf["FrameworkConfiguration"]["Fabric"][
"AdminPassword"
] = "badAdmin@pwd1"
response = conn.create_member.when.called_with(
NetworkId=network_id,
InvitationId=invitation_id,
MemberConfiguration=badadminpassmemberconf,
).should.throw(Exception, "Invalid request body")
@mock_managedblockchain
def test_list_members_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.list_members.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_get_member_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.get_member.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_get_member_badmember():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.get_member.when.called_with(
NetworkId=network_id, MemberId="m-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_delete_member_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.delete_member.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_delete_member_badmember():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.delete_member.when.called_with(
NetworkId=network_id, MemberId="m-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_update_member_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.update_member.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
LogPublishingConfiguration=helpers.default_memberconfiguration[
"LogPublishingConfiguration"
],
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_update_member_badmember():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.update_member.when.called_with(
NetworkId=network_id,
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
LogPublishingConfiguration=helpers.default_memberconfiguration[
"LogPublishingConfiguration"
],
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")

View File

@ -5,28 +5,7 @@ import sure # noqa
from moto.managedblockchain.exceptions import BadRequestException
from moto import mock_managedblockchain
default_frameworkconfiguration = {"Fabric": {"Edition": "STARTER"}}
default_votingpolicy = {
"ApprovalThresholdPolicy": {
"ThresholdPercentage": 50,
"ProposalDurationInHours": 24,
"ThresholdComparator": "GREATER_THAN_OR_EQUAL_TO",
}
}
default_memberconfiguration = {
"Name": "testmember1",
"Description": "Test Member 1",
"FrameworkConfiguration": {
"Fabric": {"AdminUsername": "admin", "AdminPassword": "Admin12345"}
},
"LogPublishingConfiguration": {
"Fabric": {"CaLogs": {"Cloudwatch": {"Enabled": False}}}
},
}
from . import helpers
@mock_managedblockchain
@ -37,12 +16,14 @@ def test_create_network():
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=default_frameworkconfiguration,
VotingPolicy=default_votingpolicy,
MemberConfiguration=default_memberconfiguration,
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
response["NetworkId"].should.match("n-[A-Z0-9]{26}")
response["MemberId"].should.match("m-[A-Z0-9]{26}")
network_id = response["NetworkId"]
member_id = response["MemberId"]
network_id.should.match("n-[A-Z0-9]{26}")
member_id.should.match("m-[A-Z0-9]{26}")
# Find in full list
response = conn.list_networks()
@ -51,7 +32,6 @@ def test_create_network():
mbcnetworks[0]["Name"].should.equal("testnetwork1")
# Get network details
network_id = mbcnetworks[0]["Id"]
response = conn.get_network(NetworkId=network_id)
response["Network"]["Name"].should.equal("testnetwork1")
@ -65,12 +45,14 @@ def test_create_network_withopts():
Description="Test Network 1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=default_frameworkconfiguration,
VotingPolicy=default_votingpolicy,
MemberConfiguration=default_memberconfiguration,
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
response["NetworkId"].should.match("n-[A-Z0-9]{26}")
response["MemberId"].should.match("m-[A-Z0-9]{26}")
network_id = response["NetworkId"]
member_id = response["MemberId"]
network_id.should.match("n-[A-Z0-9]{26}")
member_id.should.match("m-[A-Z0-9]{26}")
# Find in full list
response = conn.list_networks()
@ -79,7 +61,6 @@ def test_create_network_withopts():
mbcnetworks[0]["Description"].should.equal("Test Network 1")
# Get network details
network_id = mbcnetworks[0]["Id"]
response = conn.get_network(NetworkId=network_id)
response["Network"]["Description"].should.equal("Test Network 1")
@ -93,9 +74,9 @@ def test_create_network_noframework():
Description="Test Network 1",
Framework="HYPERLEDGER_VINYL",
FrameworkVersion="1.2",
FrameworkConfiguration=default_frameworkconfiguration,
VotingPolicy=default_votingpolicy,
MemberConfiguration=default_memberconfiguration,
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
).should.throw(Exception, "Invalid request body")
@ -108,9 +89,9 @@ def test_create_network_badframeworkver():
Description="Test Network 1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.X",
FrameworkConfiguration=default_frameworkconfiguration,
VotingPolicy=default_votingpolicy,
MemberConfiguration=default_memberconfiguration,
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
).should.throw(
Exception, "Invalid version 1.X requested for framework HYPERLEDGER_FABRIC"
)
@ -128,8 +109,8 @@ def test_create_network_badedition():
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=frameworkconfiguration,
VotingPolicy=default_votingpolicy,
MemberConfiguration=default_memberconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
).should.throw(Exception, "Invalid request body")
@ -138,5 +119,5 @@ def test_get_network_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.get_network.when.called_with(
NetworkId="n-BADNETWORK",
).should.throw(Exception, "Network n-BADNETWORK not found")
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")

View File

@ -0,0 +1,199 @@
from __future__ import unicode_literals
import boto3
import sure # noqa
from moto.managedblockchain.exceptions import BadRequestException
from moto import mock_managedblockchain
from . import helpers
@mock_managedblockchain
def test_create_proposal():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
network_id.should.match("n-[A-Z0-9]{26}")
member_id.should.match("m-[A-Z0-9]{26}")
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
proposal_id.should.match("p-[A-Z0-9]{26}")
# Find in full list
response = conn.list_proposals(NetworkId=network_id)
proposals = response["Proposals"]
proposals.should.have.length_of(1)
proposals[0]["ProposalId"].should.equal(proposal_id)
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
@mock_managedblockchain
def test_create_proposal_withopts():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
network_id.should.match("n-[A-Z0-9]{26}")
member_id.should.match("m-[A-Z0-9]{26}")
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
Description="Adding a new member",
)
proposal_id = response["ProposalId"]
proposal_id.should.match("p-[A-Z0-9]{26}")
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["Description"].should.equal("Adding a new member")
@mock_managedblockchain
def test_create_proposal_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.create_proposal.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
Actions=helpers.default_policy_actions,
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_create_proposal_badmember():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.create_proposal.when.called_with(
NetworkId=network_id,
MemberId="m-ABCDEFGHIJKLMNOP0123456789",
Actions=helpers.default_policy_actions,
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_create_proposal_badinvitationacctid():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Must be 12 digits
actions = {"Invitations": [{"Principal": "1234567890"}]}
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal.when.called_with(
NetworkId=network_id, MemberId=member_id, Actions=actions,
).should.throw(Exception, "Account ID format specified in proposal is not valid")
@mock_managedblockchain
def test_create_proposal_badremovalmemid():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Must be 12 digits
actions = {"Removals": [{"MemberId": "m-ABCDEFGHIJKLMNOP0123456789"}]}
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal.when.called_with(
NetworkId=network_id, MemberId=member_id, Actions=actions,
).should.throw(Exception, "Member ID format specified in proposal is not valid")
@mock_managedblockchain
def test_list_proposal_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.list_proposals.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_get_proposal_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.get_proposal.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_get_proposal_badproposal():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.get_proposal.when.called_with(
NetworkId=network_id, ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Proposal p-ABCDEFGHIJKLMNOP0123456789 not found")

View File

@ -0,0 +1,529 @@
from __future__ import unicode_literals
import os
import boto3
import sure # noqa
from freezegun import freeze_time
from nose import SkipTest
from moto.managedblockchain.exceptions import BadRequestException
from moto import mock_managedblockchain, settings
from . import helpers
@mock_managedblockchain
def test_vote_on_proposal_one_member_total_yes():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# List proposal votes
response = conn.list_proposal_votes(NetworkId=network_id, ProposalId=proposal_id)
response["ProposalVotes"][0]["MemberId"].should.equal(member_id)
# Get proposal details - should be APPROVED
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["Status"].should.equal("APPROVED")
response["Proposal"]["YesVoteCount"].should.equal(1)
response["Proposal"]["NoVoteCount"].should.equal(0)
response["Proposal"]["OutstandingVoteCount"].should.equal(0)
@mock_managedblockchain
def test_vote_on_proposal_one_member_total_no():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
# Create proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote no
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="NO",
)
# List proposal votes
response = conn.list_proposal_votes(NetworkId=network_id, ProposalId=proposal_id)
response["ProposalVotes"][0]["MemberId"].should.equal(member_id)
# Get proposal details - should be REJECTED
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["Status"].should.equal("REJECTED")
response["Proposal"]["YesVoteCount"].should.equal(0)
response["Proposal"]["NoVoteCount"].should.equal(1)
response["Proposal"]["OutstandingVoteCount"].should.equal(0)
@mock_managedblockchain
def test_vote_on_proposal_yes_greater_than():
conn = boto3.client("managedblockchain", region_name="us-east-1")
votingpolicy = {
"ApprovalThresholdPolicy": {
"ThresholdPercentage": 50,
"ProposalDurationInHours": 24,
"ThresholdComparator": "GREATER_THAN",
}
}
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2"
),
)
member_id2 = response["MemberId"]
# Create another proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes with member 1
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
@mock_managedblockchain
def test_vote_on_proposal_no_greater_than():
conn = boto3.client("managedblockchain", region_name="us-east-1")
votingpolicy = {
"ApprovalThresholdPolicy": {
"ThresholdPercentage": 50,
"ProposalDurationInHours": 24,
"ThresholdComparator": "GREATER_THAN",
}
}
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2"
),
)
member_id2 = response["MemberId"]
# Create another proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote no with member 1
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="NO",
)
# Vote no with member 2
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id2,
Vote="NO",
)
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("REJECTED")
@mock_managedblockchain
def test_vote_on_proposal_expiredproposal():
if os.environ.get("TEST_SERVER_MODE", "false").lower() == "true":
raise SkipTest("Cant manipulate time in server mode")
votingpolicy = {
"ApprovalThresholdPolicy": {
"ThresholdPercentage": 50,
"ProposalDurationInHours": 1,
"ThresholdComparator": "GREATER_THAN_OR_EQUAL_TO",
}
}
conn = boto3.client("managedblockchain", region_name="us-east-1")
with freeze_time("2015-01-01 12:00:00"):
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
with freeze_time("2015-02-01 12:00:00"):
# Vote yes - should set status to expired
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get proposal details - should be EXPIRED
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["Status"].should.equal("EXPIRED")
@mock_managedblockchain
def test_vote_on_proposal_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.vote_on_proposal.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
VoterMemberId="m-ABCDEFGHIJKLMNOP0123456789",
Vote="YES",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_vote_on_proposal_badproposal():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
response = conn.vote_on_proposal.when.called_with(
NetworkId=network_id,
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
VoterMemberId="m-ABCDEFGHIJKLMNOP0123456789",
Vote="YES",
).should.throw(Exception, "Proposal p-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_vote_on_proposal_badmember():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
response = conn.vote_on_proposal.when.called_with(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId="m-ABCDEFGHIJKLMNOP0123456789",
Vote="YES",
).should.throw(Exception, "Member m-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_vote_on_proposal_badvote():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
response = conn.vote_on_proposal.when.called_with(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="FOO",
).should.throw(Exception, "Invalid request body")
@mock_managedblockchain
def test_vote_on_proposal_alreadyvoted():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network - need a good network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Vote yes
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Get the invitation
response = conn.list_invitations()
invitation_id = response["Invitations"][0]["InvitationId"]
# Create the member
response = conn.create_member(
InvitationId=invitation_id,
NetworkId=network_id,
MemberConfiguration=helpers.create_member_configuration(
"testmember2", "admin", "Admin12345", False, "Test Member 2"
),
)
member_id2 = response["MemberId"]
# Create another proposal
response = conn.create_proposal(
NetworkId=network_id,
MemberId=member_id,
Actions=helpers.default_policy_actions,
)
proposal_id = response["ProposalId"]
# Get proposal details
response = conn.get_proposal(NetworkId=network_id, ProposalId=proposal_id)
response["Proposal"]["NetworkId"].should.equal(network_id)
response["Proposal"]["Status"].should.equal("IN_PROGRESS")
# Vote yes with member 1
response = conn.vote_on_proposal(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
)
# Vote yes with member 1 again
response = conn.vote_on_proposal.when.called_with(
NetworkId=network_id,
ProposalId=proposal_id,
VoterMemberId=member_id,
Vote="YES",
).should.throw(Exception, "Invalid request body")
@mock_managedblockchain
def test_list_proposal_votes_badnetwork():
conn = boto3.client("managedblockchain", region_name="us-east-1")
response = conn.list_proposal_votes.when.called_with(
NetworkId="n-ABCDEFGHIJKLMNOP0123456789",
ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Network n-ABCDEFGHIJKLMNOP0123456789 not found")
@mock_managedblockchain
def test_list_proposal_votes_badproposal():
conn = boto3.client("managedblockchain", region_name="us-east-1")
# Create network
response = conn.create_network(
Name="testnetwork1",
Framework="HYPERLEDGER_FABRIC",
FrameworkVersion="1.2",
FrameworkConfiguration=helpers.default_frameworkconfiguration,
VotingPolicy=helpers.default_votingpolicy,
MemberConfiguration=helpers.default_memberconfiguration,
)
network_id = response["NetworkId"]
member_id = response["MemberId"]
response = conn.list_proposal_votes.when.called_with(
NetworkId=network_id, ProposalId="p-ABCDEFGHIJKLMNOP0123456789",
).should.throw(Exception, "Proposal p-ABCDEFGHIJKLMNOP0123456789 not found")