Skip to content

Update Splice main to version 0.4.0-snapshot.20250509.9258.0.va4bd72a4 (automatic PR) #388

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

Closed
wants to merge 1 commit into from
Closed
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ abstract class ScanAppReference(
def getTransferFactory(
choiceArgs: transferinstructionv1.TransferFactory_Transfer
): (
FactoryChoiceWithDisclosures,
FactoryChoiceWithDisclosures[transferinstructionv1.TransferInstructionResult],
transferinstruction.v1.definitions.TransferFactoryWithChoiceContext.TransferKind,
) = {
consoleEnvironment.run {
Expand Down Expand Up @@ -541,7 +541,7 @@ abstract class ScanAppReference(

def getAllocationFactory(
choiceArgs: allocationinstructionv1.AllocationFactory_Allocate
): FactoryChoiceWithDisclosures = {
): FactoryChoiceWithDisclosures[allocationinstructionv1.AllocationInstructionResult] = {
consoleEnvironment.run {
httpCommand(HttpScanAppClient.GetAllocationFactory(choiceArgs))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ abstract class ValidatorAppReference(

@Help.Summary("Create a namespace delegation and party transaction")
@Help.Description(
"""Create a namespace delegation and party transaction
|Return the topology transaction and transaction authorization hash (this should be signed by CCSP).""".stripMargin
"""Create a namespace delegation and party transaction. Return the topology transaction and transaction authorization hash (this should be signed by CCSP)."""
)
def generateExternalPartyTopology(
partyHint: String,
Expand Down Expand Up @@ -92,8 +91,9 @@ abstract class ValidatorAppReference(
}

@Help.Summary("Onboard a new user")
@Help.Description("""Onboard individual canton-amulet user with a fresh or existing party-id.
|Return the user's partyId.""".stripMargin)
@Help.Description(
"""Onboard individual canton-amulet user with a fresh or existing party-id. Return the user's partyId."""
)
def onboardUser(user: String, existingPartyId: Option[PartyId] = None): PartyId = {
consoleEnvironment.run {
httpCommand(
Expand All @@ -104,8 +104,7 @@ abstract class ValidatorAppReference(

@Help.Summary("Register a new user identified by token")
@Help.Description(
"""Register the authenticated canton-amulet user with a fresh party-id.
|Return the newly set up partyId.""".stripMargin
"""Register the authenticated canton-amulet user with a fresh party-id. Return the newly set up partyId."""
)
def register(): PartyId = {
consoleEnvironment.run {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import org.lfdecentralizedtrust.splice.environment.SpliceConsoleEnvironment
import org.lfdecentralizedtrust.splice.http.v0.definitions.{
GetBuyTrafficRequestStatusResponse,
GetTransferOfferStatusResponse,
ListTokenStandardTransfersResponse,
TransferInstructionResultResponse,
}
import org.lfdecentralizedtrust.splice.util.{Contract, ContractWithState}
import org.lfdecentralizedtrust.splice.wallet.admin.api.client.commands.HttpWalletAppClient
Expand All @@ -27,7 +29,8 @@ import org.lfdecentralizedtrust.splice.wallet.config.WalletAppClientConfig
import org.lfdecentralizedtrust.splice.wallet.store.TxLogEntry
import com.digitalasset.canton.console.Help
import com.digitalasset.canton.data.CantonTimestamp
import com.digitalasset.canton.topology.{SynchronizerId, PartyId}
import com.digitalasset.canton.topology.{PartyId, SynchronizerId}
import org.lfdecentralizedtrust.splice.codegen.java.splice.api.token.transferinstructionv1

abstract class WalletAppReference(
override val spliceConsoleEnvironment: SpliceConsoleEnvironment,
Expand Down Expand Up @@ -477,6 +480,66 @@ abstract class WalletAppReference(
consoleEnvironment.run {
httpCommand(HttpWalletAppClient.TransferPreapprovalSend(receiver, amount, deduplicationId))
}

@Help.Summary("List active Token Standard transfers")
@Help.Description("Shows both incoming and outgoing Token Standard transfers.")
def listTokenStandardTransfers(): ListTokenStandardTransfersResponse =
consoleEnvironment.run {
httpCommand(
HttpWalletAppClient.TokenStandard.ListTransfers
)
}

@Help.Summary("Creates a transfer via the token standard")
@Help.Description(
"Send the given amulet to the receiver via the Token Standard. To be accepted by the receiver."
)
def createTokenStandardTransfer(
receiver: PartyId,
amount: BigDecimal,
description: String,
expiresAt: CantonTimestamp,
trackingId: String,
): TransferInstructionResultResponse =
consoleEnvironment.run {
httpCommand(
HttpWalletAppClient.TokenStandard
.CreateTransfer(receiver, amount, description, expiresAt, trackingId)
)
}

@Help.Summary("Accepts a transfer created via the token standard")
@Help.Description("Accept a specific offer for a Token Standard transfer.")
def acceptTokenStandardTransfer(
contractId: transferinstructionv1.TransferInstruction.ContractId
): TransferInstructionResultResponse =
consoleEnvironment.run {
httpCommand(
HttpWalletAppClient.TokenStandard.AcceptTransfer(contractId)
)
}

@Help.Summary("Rejects a transfer created via the token standard")
@Help.Description("Reject a specific offer for a Token Standard transfer.")
def rejectTokenStandardTransfer(
contractId: transferinstructionv1.TransferInstruction.ContractId
): TransferInstructionResultResponse =
consoleEnvironment.run {
httpCommand(
HttpWalletAppClient.TokenStandard.RejectTransfer(contractId)
)
}

@Help.Summary("Withdraws a transfer created via the token standard")
@Help.Description("Withdraw a specific offer for a Token Standard transfer.")
def withdrawTokenStandardTransfer(
contractId: transferinstructionv1.TransferInstruction.ContractId
): TransferInstructionResultResponse =
consoleEnvironment.run {
httpCommand(
HttpWalletAppClient.TokenStandard.WithdrawTransfer(contractId)
)
}
}

/** Client (aka remote) reference to a wallet app in the style of ParticipantClientReference, i.e.,
Expand Down
2 changes: 1 addition & 1 deletion apps/app/src/test/resources/include/canton-basic.conf
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ canton {
}
monitoring {
logging {
event-details = true
# event-details = true
api {
message-payloads = true
max-method-length = 1000
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,7 @@ class AppUpgradeIntegrationTest
)
)

actAndCheck(
actAndCheck(timeUntilSuccess = 40.seconds)(
"Voting on a AmuletRules config change for upgraded packages", {
val (_, voteRequest) = actAndCheck(
"Creating vote request",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,20 +174,21 @@ class ExternalPartySetupProposalIntegrationTest

// Onboard and Create/Accept ExternalPartySetupProposal for Bob
val onboardingBob @ OnboardingResult(bobParty, _, _) =
onboardExternalParty(aliceValidatorBackend, Some("bobExternal"))
aliceValidatorBackend.participantClient.parties
onboardExternalParty(bobValidatorBackend, Some("bobExternal"))
bobValidatorBackend.participantClient.parties
.hosted(filterParty = bobParty.filterString) should not be empty
bobValidatorWalletClient.tap(50.0)
val (cidBob, _) =
createAndAcceptExternalPartySetupProposal(
aliceValidatorBackend,
bobValidatorBackend,
onboardingBob,
verboseHashing = true,
)
eventually() {
aliceValidatorBackend.lookupTransferPreapprovalByParty(bobParty) should not be empty
aliceValidatorBackend.scanProxy.lookupTransferPreapprovalByParty(bobParty) should not be empty
bobValidatorBackend.lookupTransferPreapprovalByParty(bobParty) should not be empty
bobValidatorBackend.scanProxy.lookupTransferPreapprovalByParty(bobParty) should not be empty
}
aliceValidatorBackend
bobValidatorBackend
.listTransferPreapprovals()
.map(tp => tp.contract.contractId) contains cidBob

Expand Down Expand Up @@ -279,7 +280,7 @@ class ExternalPartySetupProposalIntegrationTest
BigDecimal(2000 - 1000 - 16.0 - 6.0 /* 16 output fees, 6.0 sender change fees */ ) +
BigDecimal(issuingRound.issuancePerUnfeaturedAppRewardCoupon) * appRewardAmount
)
aliceValidatorBackend
bobValidatorBackend
.getExternalPartyBalance(bobParty)
.totalUnlockedCoin shouldBe "1000.0000000000"
aliceValidatorBackend.participantClientWithAdminToken.ledger_api_extensions.acs
Expand Down Expand Up @@ -376,6 +377,65 @@ class ExternalPartySetupProposalIntegrationTest
}
}

// Check that transfer works correctly with featured app rights
bobValidatorWalletClient.selfGrantFeaturedAppRight()
// Transfer 500.0 from Alice to Bob
val prepareSendFeatured =
aliceValidatorBackend.prepareTransferPreapprovalSend(
aliceParty,
bobParty,
BigDecimal(500.0),
CantonTimestamp.now().plus(Duration.ofHours(24)),
1L,
verboseHashing = true,
)
prepareSendFeatured.hashingDetails should not be empty
val (_, _) = actAndCheck(
"Submit signed TransferCommand creation",
aliceValidatorBackend.submitTransferPreapprovalSend(
aliceParty,
prepareSendFeatured.transaction,
HexString.toHexString(
crypto
.signBytes(
HexString.parseToByteString(prepareSendFeatured.txHash).value,
alicePrivateKey.asInstanceOf[SigningPrivateKey],
usage = SigningKeyUsage.ProtocolOnly,
)
.value
.toProtoV30
.signature
),
publicKeyAsHexString(alicePublicKey),
),
)(
"validator automation completes transfer",
_ => {
BigDecimal(
aliceValidatorBackend
.getExternalPartyBalance(aliceParty)
.totalUnlockedCoin
) should beAround(
BigDecimal(
2000 - 1000 - 500 - 34.0 /* last number is fees from the prior transfer and this combined */
)
)
bobValidatorBackend
.getExternalPartyBalance(bobParty)
.totalUnlockedCoin shouldBe "1500.0000000000"
val rewards = bobValidatorBackend.participantClientWithAdminToken.ledger_api_extensions.acs
.filterJava(amuletCodegen.AppRewardCoupon.COMPANION)(
bobValidatorBackend.getValidatorUserInfo().primaryParty,
c =>
c.data.provider == bobValidatorBackend
.getValidatorUserInfo()
.primaryParty
.toProtoPrimitive,
)
rewards.loneElement.data.featured shouldBe true
},
)

// Check that transfer command gets archived if preapproval does not exist.
val sv1Party = sv1Backend.getDsoInfo().svParty
val now = env.environment.clock.now.toInstant
Expand Down Expand Up @@ -411,7 +471,7 @@ class ExternalPartySetupProposalIntegrationTest
sv1Party,
BigDecimal(10.0),
CantonTimestamp.now().plus(Duration.ofHours(24)),
1L,
2L,
)
prepareSendNoPreapproval.hashingDetails shouldBe empty

Expand Down Expand Up @@ -470,7 +530,7 @@ class ExternalPartySetupProposalIntegrationTest
val result = aliceValidatorBackend.scanProxy
.lookupTransferCommandStatus(
aliceParty,
1L,
2L,
)
.value
result.transferCommandsByContractId.values.loneElement.status shouldBe definitions.TransferCommandContractStatus.members
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.digitalasset.canton.admin.api.client.commands.TopologyAdminCommands.W
import com.digitalasset.canton.config.RequireTypes.PositiveInt
import com.digitalasset.canton.crypto.*
import com.digitalasset.canton.data.CantonTimestamp
import com.digitalasset.canton.SynchronizerAlias
import com.digitalasset.canton.topology.admin.grpc.TopologyStoreId
import com.digitalasset.canton.topology.transaction.*
import com.digitalasset.canton.util.HexString
Expand Down Expand Up @@ -60,7 +61,7 @@ class RecoverExternalPartyIntegrationTest

clue("Submit PartyToParticipant to migrate to bob's validator") {
val synchronizerId =
bobValidatorBackend.participantClient.synchronizers.list_connected().head.synchronizerId
sv1Backend.participantClient.synchronizers.id_of(SynchronizerAlias.tryCreate("global"))

val partyToParticipant = PartyToParticipant
.create(
Expand Down Expand Up @@ -94,6 +95,17 @@ class RecoverExternalPartyIntegrationTest

bobValidatorBackend.participantClient.topology.transactions
.load(signedTxsParticipant, TopologyStoreId.Synchronizer(synchronizerId))
clue("PartyToParticipant transaction gets sequenced") {
eventually() {
sv1Backend.participantClient.topology.party_to_participant_mappings
.list(synchronizerId, filterParty = aliceParty.filterString)
.loneElement
.item
.participants
.loneElement
.participantId shouldBe bobValidatorBackend.participantClient.id
}
}
}

// Note: This has a hard dependency on their not being any transaction for the party between
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class TokenStandardAllocationIntegrationTest
legId: String,
)(implicit
env: SpliceTestConsoleEnvironment
): FactoryChoiceWithDisclosures = {
): FactoryChoiceWithDisclosures[allocationinstructionv1.AllocationInstructionResult] = {
val leg = request.transferLegs.get(legId)
clue(
s"Creating command to request allocation for leg $legId to transfer ${leg.amount} amulets from ${leg.sender} to ${leg.receiver}"
Expand Down Expand Up @@ -478,11 +478,11 @@ class TokenStandardAllocationIntegrationTest

},
)(
"There exists a trade proposal",
"There exists a trade proposal visible to both alice and bob's participants",
_ => {
aliceValidatorBackend.participantClientWithAdminToken.ledger_api_extensions.acs
bobValidatorBackend.participantClientWithAdminToken.ledger_api_extensions.acs
.awaitJava(tradingapp.OTCTradeProposal.COMPANION)(
aliceParty
bobParty
)
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,10 @@ trait TokenStandardTest extends ExternallySignedPartyTestUtil {
timeToLife: Duration = Duration.ofMinutes(10),
)(implicit
env: SpliceTestConsoleEnvironment
): (FactoryChoiceWithDisclosures, Seq[holdingv1.Holding.ContractId]) = {
): (
FactoryChoiceWithDisclosures[transferinstructionv1.TransferInstructionResult],
Seq[holdingv1.Holding.ContractId],
) = {
val now = env.environment.clock.now.toInstant
def unlocked(optLock: java.util.Optional[holdingv1.Lock]): Boolean =
optLock.toScala.forall(lock => lock.expiresAt.toScala.exists(t => t.isBefore(now)))
Expand Down
Loading