mirror of
https://github.com/digital-asset/daml.git
synced 2024-09-20 01:07:18 +03:00
Correctly document and report malformed party names when allocating (#8642)
* Correctly document and report malformed party names when allocating changelog_begin [Ledger API] Documented the hard-coded limit of 255 characters for Daml-LF party names. [Ledger API] Malformed party names are now correctly reported back with an INVALID_ARGUMENT error changelog_end * Make sure that what is given is a suggestion Co-authored-by: Robert Autenrieth <31539813+rautenrieth-da@users.noreply.github.com> * Use Future.successful where possible Co-authored-by: Robert Autenrieth <31539813+rautenrieth-da@users.noreply.github.com> * Address https://github.com/digital-asset/daml/pull/8642#discussion_r565116571 * Address https://github.com/digital-asset/daml/pull/8642#discussion_r565122362 * Keep fields private where possible * Whitelist new tests on compatibility suite * Don't run the new tests on Canton Co-authored-by: Robert Autenrieth <31539813+rautenrieth-da@users.noreply.github.com>
This commit is contained in:
parent
48daf33586
commit
793fd8fdfc
@ -155,6 +155,19 @@ excluded_test_tool_tests = [
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"start": "0.0.0", # TODO This should become the latest available snapshot when it becomes available
|
||||
"platform_ranges": [
|
||||
{
|
||||
"end": "1.10.0-snapshot.20210120.6106.0.58ef725a",
|
||||
"exclusions": [
|
||||
# See https://github.com/digital-asset/daml/pull/8642
|
||||
"PartyManagementServiceIT:PMRejectLongPartyHints",
|
||||
"PartyManagementServiceIT:PMRejectInvalidPartyHints",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
def in_range(version, range):
|
||||
|
@ -61,6 +61,8 @@ service PartyManagementService {
|
||||
// canton: completely different globally unique identifier is allocated.
|
||||
// Behind the scenes calls to an internal protocol are made. As that protocol
|
||||
// is richer than the surface protocol, the arguments take implicit values
|
||||
// The party identifier suggestion must be a valid party name. Party names are required to be non-empty US-ASCII strings built from letters, digits, space,
|
||||
// colon, minus and underscore limited to 255 chars
|
||||
rpc AllocateParty (AllocatePartyRequest) returns (AllocatePartyResponse);
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,9 @@ conformance_test(
|
||||
"--exclude=ContractKeysIT,ContractKeysIT:CKFetchOrLookup,ContractKeysIT:CKNoFetchUndisclosed,ContractKeysIT:CKMaintainerScoped" +
|
||||
",ParticipantPruningIT" + # see "conformance-test-participant-pruning" below
|
||||
",ConfigManagementServiceIT,LedgerConfigurationServiceIT" + # dynamic config management not supported by Canton
|
||||
",ClosedWorldIT", # Canton currently fails this test with a different error (missing namespace in "unallocated" party id)
|
||||
",ClosedWorldIT" + # Canton currently fails this test with a different error (missing namespace in "unallocated" party id)
|
||||
# The following tests fail because Canton raises a different error (see https://github.com/digital-asset/daml/pull/8642)
|
||||
",PartyManagementServiceIT:PMRejectLongPartyHints,PartyManagementServiceIT:PMRejectInvalidPartyHints",
|
||||
],
|
||||
) if not is_windows else None
|
||||
|
||||
|
@ -4,11 +4,13 @@
|
||||
package com.daml.ledger.api.testtool.suites
|
||||
|
||||
import com.daml.ledger.api.testtool.infrastructure.Allocation._
|
||||
import com.daml.ledger.api.testtool.infrastructure.Assertions.assertGrpcError
|
||||
import com.daml.ledger.api.testtool.infrastructure.LedgerTestSuite
|
||||
import com.daml.ledger.api.v1.admin.party_management_service.PartyDetails
|
||||
import com.daml.ledger.client.binding
|
||||
import com.daml.ledger.test.model.Test.Dummy
|
||||
import com.daml.lf.data.Ref
|
||||
import io.grpc.Status
|
||||
import scalaz.Tag
|
||||
import scalaz.syntax.tag.ToTagOps
|
||||
|
||||
@ -90,6 +92,41 @@ final class PartyManagementServiceIT extends LedgerTestSuite {
|
||||
}
|
||||
})
|
||||
|
||||
test(
|
||||
"PMRejectLongPartyHints",
|
||||
"A party identifier which is too long should be rejected with the proper error",
|
||||
allocate(NoParties),
|
||||
)(implicit ec => { case Participants(Participant(ledger)) =>
|
||||
for {
|
||||
error <- ledger
|
||||
.allocateParty(
|
||||
partyIdHint = Some(Random.alphanumeric.take(256).mkString),
|
||||
displayName = None,
|
||||
)
|
||||
.failed
|
||||
} yield {
|
||||
assertGrpcError(error, Status.Code.INVALID_ARGUMENT, "Party is too long")
|
||||
}
|
||||
})
|
||||
|
||||
test(
|
||||
"PMRejectInvalidPartyHints",
|
||||
"A party identifier that contains invalid characters should be rejected with the proper error",
|
||||
allocate(NoParties),
|
||||
)(implicit ec => { case Participants(Participant(ledger)) =>
|
||||
for {
|
||||
error <- ledger
|
||||
.allocateParty(
|
||||
// Assumption: emojis will never be acceptable in a party identifier
|
||||
partyIdHint = Some("\uD83D\uDE00"),
|
||||
displayName = None,
|
||||
)
|
||||
.failed
|
||||
} yield {
|
||||
assertGrpcError(error, Status.Code.INVALID_ARGUMENT, "non expected character")
|
||||
}
|
||||
})
|
||||
|
||||
test(
|
||||
"PMAllocateOneHundred",
|
||||
"It should create unique party names when allocating many parties",
|
||||
|
@ -40,6 +40,8 @@ private[apiserver] final class ApiPartyManagementService private (
|
||||
) extends PartyManagementService
|
||||
with GrpcApiService {
|
||||
|
||||
import ApiPartyManagementService.CreateSubmissionId
|
||||
|
||||
private val logger = ContextualizedLogger.get(this.getClass)
|
||||
|
||||
private val synchronousResponse = new SynchronousResponse(
|
||||
@ -81,19 +83,24 @@ private[apiserver] final class ApiPartyManagementService private (
|
||||
.andThen(logger.logErrorsOnCall[ListKnownPartiesResponse])
|
||||
|
||||
override def allocateParty(request: AllocatePartyRequest): Future[AllocatePartyResponse] = {
|
||||
// TODO: This should do proper validation.
|
||||
def randomSubmissionId(prefix: String): Ref.IdString.LedgerString =
|
||||
v1.SubmissionId.assertFromString(s"${prefix}_${UUID.randomUUID().toString}")
|
||||
|
||||
val party =
|
||||
if (request.partyIdHint.isEmpty) None
|
||||
else Some(Ref.Party.assertFromString(request.partyIdHint))
|
||||
val submissionId = randomSubmissionId(prefix = party.getOrElse(""))
|
||||
val validatedPartyIdentifier =
|
||||
if (request.partyIdHint.isEmpty) {
|
||||
Future.successful(None)
|
||||
} else {
|
||||
Ref.Party
|
||||
.fromString(request.partyIdHint)
|
||||
.fold(
|
||||
error => Future.failed(ErrorFactories.invalidArgument(error)),
|
||||
party => Future.successful(Some(party)),
|
||||
)
|
||||
}
|
||||
|
||||
validatedPartyIdentifier
|
||||
.flatMap(party => {
|
||||
val displayName = if (request.displayName.isEmpty) None else Some(request.displayName)
|
||||
|
||||
synchronousResponse
|
||||
.submitAndWait(submissionId, (party, displayName))
|
||||
.submitAndWait(CreateSubmissionId.withPrefix(party), (party, displayName))
|
||||
.map { case PartyEntry.AllocationAccepted(_, partyDetails) =>
|
||||
AllocatePartyResponse(
|
||||
Some(
|
||||
@ -105,6 +112,7 @@ private[apiserver] final class ApiPartyManagementService private (
|
||||
)
|
||||
)
|
||||
}
|
||||
})
|
||||
.andThen(logger.logErrorsOnCall[AllocatePartyResponse])
|
||||
}
|
||||
|
||||
@ -129,6 +137,18 @@ private[apiserver] object ApiPartyManagementService {
|
||||
managementServiceTimeout,
|
||||
)
|
||||
|
||||
private object CreateSubmissionId {
|
||||
// Suffix is `-` followed by a random UUID as a string
|
||||
private val SuffixLength: Int = 1 + UUID.randomUUID().toString.length
|
||||
private val MaxLength: Int = 255
|
||||
private val PrefixMaxLength: Int = MaxLength - SuffixLength
|
||||
def withPrefix(maybeParty: Option[Ref.Party]): v1.SubmissionId = {
|
||||
val uuid = UUID.randomUUID().toString
|
||||
val raw = maybeParty.fold(uuid)(party => s"${party.take(PrefixMaxLength)}-$uuid")
|
||||
v1.SubmissionId.assertFromString(raw)
|
||||
}
|
||||
}
|
||||
|
||||
private final class SynchronousResponseStrategy(
|
||||
ledgerEndService: LedgerEndService,
|
||||
writeService: WritePartyService,
|
||||
|
Loading…
Reference in New Issue
Block a user