Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
37 changes: 35 additions & 2 deletions apps/sv/src/main/openapi/sv-internal.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,9 @@ paths:
operationId: "lookupSynchronizerRegistration"
description: |
Look up the registration of a dedicated synchronizer, so the SV UI can tell a
proposer that a synchronizer id is already registered. Forwarded to Scan, which
holds the registry; the SV app does not ingest it.
proposer that a synchronizer id is already registered, and resolve the registration
an offboard or set-parameters proposal targets. Forwarded to Scan, which holds the
registry; the SV app does not ingest it.
parameters:
- name: "synchronizer_id"
in: "path"
Expand All @@ -258,6 +259,29 @@ paths:
"$ref": "../../../../scan/src/main/openapi/scan.yaml#/components/schemas/LookupSynchronizerRegistrationResponse"
"404":
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/responses/404"
/v0/admin/sv/synchronizers/registrations/by-contract-id/{contract_id}:
get:
tags: [ sv ]
x-jvm-package: sv_operator
operationId: "lookupSynchronizerRegistrationByContractId"
description: |
Look up an active synchronizer registration by contract id, so the SV UI can show
what an offboard or set-parameters vote targets and flag a vote whose registration
is no longer active. Empty when the registration was archived. Served from the SV
app's DSO store.
parameters:
- name: "contract_id"
in: "path"
required: true
schema:
type: string
responses:
"200":
description: ok
content:
application/json:
schema:
$ref: "#/components/schemas/LookupSynchronizerRegistrationByContractIdResponse"
/v0/admin/sv/voteresults/count:
post:
tags: [ sv ]
Expand Down Expand Up @@ -1234,6 +1258,15 @@ components:
featured_app_right:
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/schemas/Contract"

LookupSynchronizerRegistrationByContractIdResponse:
description: |
An active `Splice.DecentralizedSynchronizer:RegisteredSynchronizer` contract looked up by
contract ID, if it exists.
type: object
properties:
registration:
$ref: "../../../../common/src/main/openapi/common-external.yaml#/components/schemas/Contract"

ListFeaturedAppRightsByProviderResponse:
description: |
The `Splice.Amulet.FeaturedAppRight` contract for a specific provider, if it exists.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,8 @@ class HttpSvOperatorHandler(
}

/** Intended use: the SV app UI, to warn a proposer that a synchronizer id is already
* registered. Forwarded to Scan, which holds the registry; the SV app does not ingest it.
* registered, and to resolve the registration an offboard or set-parameters proposal
* targets. Forwarded to Scan, which holds the registry; the SV app does not ingest it.
*/
override def lookupSynchronizerRegistration(
respond: r0.LookupSynchronizerRegistrationResponse.type
Expand All @@ -277,6 +278,29 @@ class HttpSvOperatorHandler(
}
}

/** Intended use: the SV app UI, to show what an offboard or set-parameters vote targets and
* to flag a vote whose pinned registration is no longer active. The DSO party signs every
* registration, so the SV app's own DSO store holds them.
*/
override def lookupSynchronizerRegistrationByContractId(
respond: r0.LookupSynchronizerRegistrationByContractIdResponse.type
)(contractId: String)(
extracted: ActAsKnownUserRequest
): Future[r0.LookupSynchronizerRegistrationByContractIdResponse] = {
implicit val ActAsKnownUserRequest(traceContext) = extracted
withSpan(s"$workflowId.lookupSynchronizerRegistrationByContractId") { _ => _ =>
for {
registration <- dsoStore.multiDomainAcsStore.lookupContractById(
spliceCodegen.decentralizedsynchronizer.RegisteredSynchronizer.COMPANION
)(new spliceCodegen.decentralizedsynchronizer.RegisteredSynchronizer.ContractId(contractId))
} yield respond.OK(
definitions.LookupSynchronizerRegistrationByContractIdResponse(
registration.map(_.contract.toHttp)
)
)
}
}

override def listValidatorLicenses(
respond: r0.ListValidatorLicensesResponse.type
)(after: Option[Long], limit: Option[Int])(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1507,6 +1507,11 @@ object SvDsoStore {
totalTrafficPurchased = Some(contract.payload.totalPurchased),
)
},
// Offboard and set-parameters votes pin a registration by contract id; the SV UI looks it
// up here to show what such a vote targets.
mkFilter(splice.decentralizedsynchronizer.RegisteredSynchronizer.COMPANION)(co =>
co.payload.dso == dso
)(DsoAcsStoreRowData(_)),
mkFilter(splice.ans.AnsRules.COMPANION)(co => co.payload.dso == dso)(DsoAcsStoreRowData(_)),
mkFilter(splice.ans.AnsEntry.COMPANION)(co => co.payload.dso == dso) { contract =>
DsoAcsStoreRowData(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,11 @@ import org.lfdecentralizedtrust.splice.codegen.java.splice.amuletrules.{
}
import org.lfdecentralizedtrust.splice.codegen.java.splice.ans.*
import org.lfdecentralizedtrust.splice.codegen.java.splice.cometbft.CometBftConfigLimits
import org.lfdecentralizedtrust.splice.codegen.java.splice.decentralizedsynchronizer.MemberTraffic
import org.lfdecentralizedtrust.splice.codegen.java.splice.decentralizedsynchronizer.{
GovernanceParameters,
MemberTraffic,
RegisteredSynchronizer,
}
import org.lfdecentralizedtrust.splice.codegen.java.splice.dso.decentralizedsynchronizer.{
DsoDecentralizedSynchronizerConfig,
SynchronizerNodeConfigLimits,
Expand Down Expand Up @@ -209,6 +213,34 @@ abstract class SvDsoStoreTest extends StoreTestBase with HasExecutionContext {
_.lookupFeaturedAppRightWithOffset(userParty(1))
)

"RegisteredSynchronizer" should {
def lookup(store: SvDsoStore, cid: RegisteredSynchronizer.ContractId) =
store.multiDomainAcsStore.lookupContractById(RegisteredSynchronizer.COMPANION)(cid)

"be found by contract id" in {
val wanted = registeredSynchronizer(userParty(1), "dedicated::1220aa")
val other = registeredSynchronizer(userParty(2), "dedicated::1220bb")
for {
store <- mkStore()
_ <- dummyDomain.create(wanted)(store.multiDomainAcsStore)
_ <- dummyDomain.create(other)(store.multiDomainAcsStore)
result <- lookup(store, wanted.contractId)
} yield result.map(_.contract) should be(Some(wanted))
}

// An offboard or set-parameters vote archives the registration it pins, which leaves any
// other vote pinning it stale.
"not be found once archived" in {
val registration = registeredSynchronizer(userParty(1), "dedicated::1220aa")
for {
store <- mkStore()
_ <- dummyDomain.create(registration)(store.multiDomainAcsStore)
_ <- dummyDomain.archive(registration)(store.multiDomainAcsStore)
result <- lookup(store, registration.contractId)
} yield result should be(None)
}
}

"getOpenMiningRoundTriple" should {

"return the oldest, middle, newest mining rounds" in {
Expand Down Expand Up @@ -2286,6 +2318,18 @@ abstract class SvDsoStoreTest extends StoreTestBase with HasExecutionContext {
)
}

private def registeredSynchronizer(operator: PartyId, synchronizerId: String) =
contract(
RegisteredSynchronizer.TEMPLATE_ID_WITH_PACKAGE_ID,
new RegisteredSynchronizer.ContractId(nextCid()),
new RegisteredSynchronizer(
dsoParty.toProtoPrimitive,
synchronizerId,
operator.toProtoPrimitive,
new GovernanceParameters(java.math.BigDecimal.ONE.setScale(10)),
),
)

private def memberTraffic(
member: Member,
synchronizerId: SynchronizerId,
Expand Down
Loading