Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

daml ledger export: export all parties #10588

Merged
merged 3 commits into from
Aug 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import com.daml.ledger.api.v1.value
import com.daml.ledger.api.domain
import com.daml.lf.archive.DarDecoder
import com.daml.SdkVersion
import com.daml.ledger.api.refinements.ApiTypes.Party

import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.duration.Duration
Expand Down Expand Up @@ -196,7 +197,10 @@ object LF16ExportClient {
Config.Empty.copy(
ledgerHost = "localhost",
ledgerPort = ledgerPort,
parties = Seq("Alice"),
partyConfig = PartyConfig(
parties = Seq.empty[Party],
allParties = true,
),
exportType = Some(
Config.EmptyExportScript.copy(
sdkVersion = SdkVersion.sdkVersion,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import java.time.Duration

import com.daml.SdkVersion
import com.daml.fs.Utils.deleteRecursively
import com.daml.ledger.api.refinements.ApiTypes.Party
import com.daml.ledger.api.tls.TlsConfiguration
import com.daml.lf.engine.script.{RunnerConfig, RunnerMain}

Expand Down Expand Up @@ -105,7 +106,10 @@ object ExampleExportClient {
Config.Empty.copy(
ledgerHost = "localhost",
ledgerPort = clientConfig.targetPort,
parties = Seq("Alice", "Bob"),
partyConfig = PartyConfig(
allParties = false,
parties = Party.subst(Seq("Alice", "Bob")),
),
exportType = Some(
Config.EmptyExportScript.copy(
sdkVersion = SdkVersion.sdkVersion,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import com.daml.lf.data.Ref
import com.daml.lf.data.Ref.PackageId
import com.daml.lf.engine.script.{Participants, Runner}
import com.daml.ledger.api.domain
import com.daml.ledger.api.refinements.ApiTypes.ApplicationId
import com.daml.ledger.api.refinements.ApiTypes.{ApplicationId, Party}
import com.daml.ledger.api.testing.utils.{AkkaBeforeAndAfterAll, SuiteResourceManagementAroundAll}
import com.daml.ledger.api.v1.command_service.SubmitAndWaitRequest
import com.daml.ledger.api.v1.commands._
Expand Down Expand Up @@ -129,7 +129,10 @@ trait ReproducesTransactions
ledgerPort = serverPort.value,
tlsConfig = TlsConfiguration(false, None, None, None),
accessToken = None,
parties = parties,
partyConfig = PartyConfig(
parties = Party.subst(parties),
allParties = false,
),
start = offset,
end = ledgerEnd,
exportType = Some(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import java.nio.file.{Path, Paths}
import java.io.File

import com.daml.auth.TokenHolder
import com.daml.ledger.api.refinements.ApiTypes.Party
import com.daml.ledger.api.tls.{TlsConfiguration, TlsConfigurationCli}
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset

Expand All @@ -15,7 +16,7 @@ final case class Config(
ledgerPort: Int,
tlsConfig: TlsConfiguration,
accessToken: Option[TokenHolder],
parties: Seq[String],
partyConfig: PartyConfig,
start: LedgerOffset,
end: LedgerOffset,
exportType: Option[ExportType],
Expand All @@ -30,6 +31,13 @@ final case class ExportScript(
damlScriptLib: String,
) extends ExportType

// This is a product rather than a sum, so that we can raise
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could consider using that only during config parsing and switching to a different type afterwards but probably not worth the complexity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I wish scopt had something like optparse-applicative's Alternative instance.

// an error of both --party and --all-parties is configured.
final case class PartyConfig(
allParties: Boolean,
parties: Seq[Party],
)

object Config {
def parse(args: Array[String]): Option[Config] =
parser.parse(args, Empty)
Expand Down Expand Up @@ -63,12 +71,22 @@ object Config {
"File from which the access token will be read, required to interact with an authenticated ledger."
)
opt[Seq[String]]("party")
.required()
.unbounded()
.action((x, c) => c.copy(parties = c.parties ++ x.toList))
.action((x, c) =>
c.copy(partyConfig =
c.partyConfig.copy(parties = c.partyConfig.parties ++ Party.subst(x.toList))
)
)
.text(
"Export ledger state as seen by these parties. " +
"Pass --party multiple times or use a comma-separated list of party names to specify multiple parties."
"Pass --party multiple times or use a comma-separated list of party names to specify multiple parties. " +
"Use either --party or --all-parties but not both."
)
opt[Unit]("all-parties")
.action((_, c) => c.copy(partyConfig = c.partyConfig.copy(allParties = true)))
.text(
"Export ledger state as seen by all known parties. " +
"Use either --party or --all-parties but not both."
)
opt[String]("start")
.optional()
Expand Down Expand Up @@ -115,6 +133,16 @@ object Config {
case Some(_) => success
}
)
checkConfig(c => {
val pc = c.partyConfig
if (pc.allParties && pc.parties.nonEmpty) {
failure("Set either --party or --all-parties but not both at the same time")
} else if (!pc.allParties && pc.parties.isEmpty) {
failure("Set one of --party or --all-parties")
} else {
success
}
})
}

val EmptyExportScript = ExportScript(
Expand All @@ -140,7 +168,7 @@ object Config {
ledgerPort = -1,
tlsConfig = TlsConfiguration(false, None, None, None),
accessToken = None,
parties = List(),
partyConfig = PartyConfig(parties = Seq.empty[Party], allParties = false),
start = LedgerOffset(LedgerOffset.Value.Boundary(LedgerOffset.LedgerBoundary.LEDGER_BEGIN)),
end = LedgerOffset(LedgerOffset.Value.Boundary(LedgerOffset.LedgerBoundary.LEDGER_END)),
exportType = None,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,45 @@ package com.daml.script.export

import akka.stream.Materializer
import akka.stream.scaladsl.Sink
import com.daml.ledger.api.refinements.ApiTypes.ContractId
import com.daml.auth.TokenHolder
import com.daml.ledger.api.domain
import com.daml.ledger.api.refinements.ApiTypes.{ContractId, Party}
import com.daml.ledger.api.v1.event.CreatedEvent
import com.daml.ledger.api.v1.event.Event.Event
import com.daml.ledger.api.v1.ledger_offset.LedgerOffset
import com.daml.ledger.api.v1.transaction.TransactionTree
import com.daml.ledger.api.v1.transaction_filter.{Filters, TransactionFilter}
import com.daml.ledger.client.LedgerClient

import scala.concurrent.Future
import scala.concurrent.{ExecutionContext, Future}

object LedgerUtils {

/** Fetch all known parties from the ledger if requested by the given PartyConfig.
*/
def getAllParties(
client: LedgerClient,
token: Option[TokenHolder],
config: PartyConfig,
)(implicit ec: ExecutionContext): Future[Seq[Party]] = {
if (config.allParties) {
val tokenString = token.flatMap(_.token)
def fromDetails(details: Seq[domain.PartyDetails]): Seq[Party] =
Party.subst(details.map(_.party))
client.partyManagementClient.listKnownParties(tokenString).map(fromDetails)
} else {
Future.successful(config.parties)
}
}

/** Fetch the active contract set from the ledger.
*
* @param parties Fetch the ACS for these parties.
* @param offset Fetch the ACS as of this ledger offset.
*/
def getACS(
client: LedgerClient,
parties: Seq[String],
parties: Seq[Party],
offset: LedgerOffset,
)(implicit
mat: Materializer
Expand All @@ -36,7 +55,7 @@ object LedgerUtils {
Future.successful(Map.empty)
} else {
client.transactionClient
.getTransactions(ledgerBegin, Some(offset), filter(parties), verbose = true)
.getTransactions(ledgerBegin, Some(offset), filter(Party.unsubst(parties)), verbose = true)
.runFold(Map.empty[ContractId, CreatedEvent]) { case (acs, tx) =>
tx.events.foldLeft(acs) { case (acs, ev) =>
ev.event match {
Expand All @@ -57,7 +76,7 @@ object LedgerUtils {
*/
def getTransactionTrees(
client: LedgerClient,
parties: Seq[String],
parties: Seq[Party],
start: LedgerOffset,
end: LedgerOffset,
)(implicit
Expand All @@ -67,7 +86,7 @@ object LedgerUtils {
Future.successful(Seq.empty)
} else {
client.transactionClient
.getTransactionTrees(start, Some(end), filter(parties), verbose = true)
.getTransactionTrees(start, Some(end), filter(Party.unsubst(parties)), verbose = true)
.runWith(Sink.seq)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,9 @@ object Main {
config.ledgerPort,
clientConfig(config),
)
acs <- LedgerUtils.getACS(client, config.parties, config.start)
trees <- LedgerUtils.getTransactionTrees(client, config.parties, config.start, config.end)
parties <- LedgerUtils.getAllParties(client, config.accessToken, config.partyConfig)
acs <- LedgerUtils.getACS(client, parties, config.start)
trees <- LedgerUtils.getTransactionTrees(client, parties, config.start, config.end)
acsPkgRefs = TreeUtils.contractsReferences(acs.values)
treePkgRefs = TreeUtils.treesReferences(trees)
pkgRefs = acsPkgRefs ++ treePkgRefs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,22 +63,41 @@ class ConfigSpec extends AnyFreeSpec with Matchers with OptionValues {
optConfig.value.end shouldBe LedgerOffset().withAbsolute("00100")
}
}
"--party" - {
"--party or --all-parties" - {
val defaultRequiredArgs = outputTypeArgs ++ outputArgs ++ sdkVersionArgs ++ ledgerArgs
"--party Alice" in {
val args = defaultRequiredArgs ++ Array("--party", "Alice")
val optConfig = Config.parse(args)
optConfig.value.parties should contain only ("Alice")
optConfig.value.partyConfig.parties should contain only ("Alice")
optConfig.value.partyConfig.allParties shouldBe false
}
"--party Alice --party Bob" in {
val args = defaultRequiredArgs ++ Array("--party", "Alice", "--party", "Bob")
val optConfig = Config.parse(args)
optConfig.value.parties should contain only ("Alice", "Bob")
optConfig.value.partyConfig.parties should contain only ("Alice", "Bob")
optConfig.value.partyConfig.allParties shouldBe false
}
"--party Alice,Bob" in {
val args = defaultRequiredArgs ++ Array("--party", "Alice,Bob")
val optConfig = Config.parse(args)
optConfig.value.parties should contain only ("Alice", "Bob")
optConfig.value.partyConfig.parties should contain only ("Alice", "Bob")
optConfig.value.partyConfig.allParties shouldBe false
}
"--all-parties" in {
val args = defaultRequiredArgs ++ Array("--all-parties")
val optConfig = Config.parse(args)
optConfig.value.partyConfig.parties shouldBe empty
optConfig.value.partyConfig.allParties shouldBe true
}
"missing" in {
val args = defaultRequiredArgs
val optConfig = Config.parse(args)
optConfig shouldBe empty
}
"--party and --all-parties" in {
val args = defaultRequiredArgs ++ Array("--party", "Alice", "--all-parties")
val optConfig = Config.parse(args)
optConfig shouldBe empty
}
}
"TLS" - {
Expand Down Expand Up @@ -118,5 +137,17 @@ class ConfigSpec extends AnyFreeSpec with Matchers with OptionValues {
optConfig.value.accessToken.value.token.value shouldBe token
}
}
"Output type" - {
"missing" in {
val args = ledgerArgs ++ partyArgs
val optConfig = Config.parse(args)
optConfig shouldBe empty
}
"script" in {
val args = outputTypeArgs ++ outputArgs ++ sdkVersionArgs ++ ledgerArgs ++ partyArgs
val optConfig = Config.parse(args)
optConfig.value.exportType.value shouldBe an[ExportScript]
}
}
}
}
5 changes: 3 additions & 2 deletions docs/source/tools/export/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ with a running ledger, e.g. with a running ``daml start``.

The ``--party`` flags define which contracts will be included in the export. In
the above example only contracts visible to the parties Alice and Bob will be
included in the export. Lack of visibility of certain events may cause
references to :ref:`unknown contract ids <export-unknown-cids>`.
included in the export. Alternatively, you can set ``--all-parties`` to export
contracts seen by all known parties. Lack of visibility of certain events may
cause references to :ref:`unknown contract ids <export-unknown-cids>`.

The ``--output`` flag defines the directory prefix under which to generate the
Daml project that contains the Daml script that represents the ledger export.
Expand Down