WabiSabi icon indicating copy to clipboard operation
WabiSabi copied to clipboard

The Protocol

Open nopara73 opened this issue 5 years ago • 34 comments

[Obsolated] See https://github.com/zkSNACKs/WabiSabi/blob/master/protocol.md

nopara73 avatar Jun 15 '20 16:06 nopara73

Confirmation

As we discussed accepting unconfirmed coins creates more issues than it solves.

[INPUT REGISTRATION]
Alice1 =input1=> Coordinator
IsConfirmed()
Alice1 <=ACK/NACK= Coordinator

Alice2 =input2=> Coordinator
Alice2 <=ACK/NACK= Coordinator



[OUTPUT REGISTRATION]
Bob =output=> Coordinator
Bob <=ACK/NACK= Coordinator



[TRANSACTION SIGNING]
Satoshi =get coinjoin=> Coordinator
Satoshi <=coinjoin= Coordinator

Alice1 =input1, signature1=> Coordinator
Alice1 <=ACK/NACK= Coordinator

Alice2 =input2, signature2=> Coordinator
Alice2 <=ACK/NACK= Coordinator

nopara73 avatar Jun 15 '20 16:06 nopara73

Unspent

It's obvious, but it should be specified nevertheless that unspent coins should not be registered:

[INPUT REGISTRATION]
Alice1 =input1=> Coordinator
IsUnspent()
IsConfirmed()
Alice1 <=ACK/NACK= Coordinator

Alice2 =input2=> Coordinator
Alice2 <=ACK/NACK= Coordinator



[OUTPUT REGISTRATION]
Bob =output=> Coordinator
Bob <=ACK/NACK= Coordinator



[TRANSACTION SIGNING]
Satoshi =get coinjoin=> Coordinator
Satoshi <=coinjoin= Coordinator

Alice1 =input1, signature1=> Coordinator
Alice1 <=ACK/NACK= Coordinator

Alice2 =input2, signature2=> Coordinator
Alice2 <=ACK/NACK= Coordinator

nopara73 avatar Jun 15 '20 16:06 nopara73

It's obvious, but it should be specified nevertheless that unspent coins should not be registered:

You mean spent.

yahiheb avatar Jun 15 '20 19:06 yahiheb

This method doesn't scale.

nopara73 avatar Jun 16 '20 10:06 nopara73

FYI, there is the documentation on zero link step by step, and I think some of the explanations are still valid for WabiSabi.

MaxHillebrand avatar Jun 16 '20 18:06 MaxHillebrand

Today's progress with https://www.planttext.com/

@startuml

== Input Registration ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinStatuses[]

Alice1 -> Coordinator : Input, RoundParamSig, ProofOfOwnership
note over Coordinator :IsUnspent()\nIsConfirmed()
Coordinator -> Alice1 : Credentials[]

...

Alice1 -> Coordinator: I'm still here.
Coordinator -> Alice1 : TempACK

Alice2 -> Coordinator : Input, RoundParamSig, ProofOfOwnership
note over Coordinator :IsUnspent()\nIsConfirmed()
Coordinator -> Alice2 : Credentials[]

...

Alice1 -> Coordinator: I'm still here.
Coordinator -> Alice1 : TempACK
Alice2 -> Coordinator: I'm still here.
Coordinator -> Alice2 : TempACK

== Connection Confirmation ==

Alice1 -> Coordinator: I'm instill here.
Coordinator -> Alice1 : FinalACK
Alice2 -> Coordinator: I'm still here.
Coordinator -> Alice2 : FinalACK

== Output Registration ==

Bob -> Coordinator: Output, SumProof
Coordinator -> Bob : ACK

== Transaction Signing ==

Alice1 -> Coordinator: Input, Signature
Coordinator -> Alice1 : ACK

Alice2 -> Coordinator: Input, Signature
Coordinator -> Alice2 : ACK

== Transaction Broadcasting ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinStatuses[]

@enduml

image

nopara73 avatar Jun 17 '20 15:06 nopara73

For the approach we discussed fetching of the tx is required between outptut transaction signing, or for the alternative approach Alices should fetch the tx. The round param signatures should also be given at that point.

To prevent the targetted DoS attack, the tx data can still be provided in GetStatuses requests, but instead of the client giving a secret to the coordinator, the coordinator could encrypt the tx so that only round participants can decrypt and sign.

Finally, I think the connection confirmation phase can just be omitted since final ack does not provide any new information using the unified protocol, output registration could just conclude when the sum of the registered output amounts is equal to the sum of the inputs with recent enough confirmation after taking fees into account

nothingmuch avatar Jun 17 '20 18:06 nothingmuch

also i don't think we should list SumProof here, unless we also want to break down everything else (initial credentials, and for each registration including inputs, the list of requested credentials, presented credentials, and serial numbers as well as the sum proof)

nothingmuch avatar Jun 17 '20 18:06 nothingmuch

For the approach we discussed fetching of the tx is required between outptut transaction signing, or for the alternative approach Alices should fetch the tx.

I don't see why it cannot be in transaction signing phase itself. The assumption is now everyone will get back the unsigned tx who ask for CJ status.

nopara73 avatar Jun 18 '20 11:06 nopara73

The round param signatures should also be given at that point.

Yup.

nopara73 avatar Jun 18 '20 11:06 nopara73

but instead of the client giving a secret to the coordinator, the coordinator could encrypt the tx so that only round participants can decrypt and sign.

Wow, that's a great idea!

nopara73 avatar Jun 18 '20 11:06 nopara73

Finally, I think the connection confirmation phase can just be omitted since final ack does not provide any new information using the unified protocol, output registration could just conclude when the sum of the registered output amounts is equal to the sum of the inputs with recent enough confirmation after taking fees into account

Why it cannot be omitted is because if someone doesn't confirm connection then he has to be marked malicous, otherwise it'd be a DoS attack.

nopara73 avatar Jun 18 '20 11:06 nopara73

also i don't think we should list SumProof here, unless we also want to break down everything else (initial credentials, and for each registration including inputs, the list of requested credentials, presented credentials, and serial numbers as well as the sum proof)

I couldn't figure out the proper term there, suggestions are welcome. It should be something that denotes all crypto magic in a single gathering term. We could call it MagicalCryptoProofs 😄

nopara73 avatar Jun 18 '20 11:06 nopara73

Why it cannot be omitted is because if someone doesn't confirm connection then he has to be marked malicous, otherwise it'd be a DoS attack.

Also with your previous idea, this is the right place to give to the participant the round secret with what he can decrypt the unsigned coinjoin.

nopara73 avatar Jun 18 '20 11:06 nopara73

I incorporated your ideas.

@startuml

title Scenario: A Participant Is Consolidating 2 Coins (Alice1, Alice2) Into 1 (Bob)\n

== Input Registration ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinStatuses[]

Alice1 -> Coordinator : Input, RoundParamSig, WabiSabiProof
note over Coordinator :CheckRoundParamSig()\nIsUnspent()\nIsConfirmed()
Coordinator -> Alice1 : WabiSabiCredentials[], AliceId

...

Alice1 -> Coordinator: AliceId
Coordinator -> Alice1 : ACK

Alice2 -> Coordinator : Input, RoundParamSig, WabiSabiProof
note over Coordinator :CheckRoundParamSig()\nIsUnspent()\nIsConfirmed()
Coordinator -> Alice2 : WabiSabiCredentials[], AliceId

...

Alice1 -> Coordinator: AliceId
Coordinator -> Alice1 : ACK
Alice2 -> Coordinator: AliceId
Coordinator -> Alice2 : ACK

== Connection Confirmation ==

Alice1 -> Coordinator: AliceId
Coordinator -> Alice1 : UnsignedTransactionSecret

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinStatuses[]

Alice2 -> Coordinator: AliceId
Coordinator -> Alice2 : UnsignedTransactionSecret

== Output Registration ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinStatuses[]

Bob -> Coordinator: Output, WabiSabiProof
Coordinator -> Bob : ACK

== Transaction Signing ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinStatuses[]

Alice1 -> Coordinator: AliceId, InputSignature
Coordinator -> Alice1 : ACK

Alice2 -> Coordinator: AliceId, InputSignature
Coordinator -> Alice2 : ACK

== Transaction Broadcasting ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinStatuses[]

@enduml

image

nopara73 avatar Jun 18 '20 12:06 nopara73

Today's Progress (2020-06-18)

@startuml

title Scenario: A Participant Is Consolidating 2 Coins (Alice1, Alice2) Into 1 (Bob)\n

== Input Registration ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Alice1 -> Coordinator : RoundId, Input, RoundParamSig, RealCredentialRequests[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueInput()\nCheckRoundParamSig()\nIsUnspent()\nIsConfirmed()
Coordinator -> Alice1 : AliceId, ZeroCredentials[]
...

Alice1 -> Coordinator: RoundId, AliceId, CredentialProofs[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueCredentialProofs()\nCheckCredentialProofs()\nWithdrawCredentialProofs()
Coordinator -> Alice1 : ZeroCredentials[]

Alice2 -> Coordinator : RoundId, Input, RoundParamSig, RealCredentialRequests[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueInput()\nCheckRoundParamSig()\nIsUnspent()\nIsConfirmed()
Coordinator -> Alice2 : AliceId, ZeroCredentials[]

...

Alice1 -> Coordinator: RoundId, AliceId, CredentialProofs[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueCredentialProofs()\nCheckCredentialProofs()\nWithdrawCredentialProofs()
Coordinator -> Alice1 : ZeroCredentials[]

== Connection Confirmation ==

Alice1 -> Coordinator: RoundId, AliceId, CredentialProofs[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueCredentialProofs()\nCheckCredentialProofs()\nWithdrawCredentialProofs()
Coordinator -> Alice1 : RealCredentials[]

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Alice2 -> Coordinator: RoundId, AliceId, CredentialProofs[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueCredentialProofs()\nCheckCredentialProofs()\nWithdrawCredentialProofs()
Coordinator -> Alice2 : RealCredentials[]

== Output Registration ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Bob -> Coordinator: RoundId, Output, CredentialProofs[]
note over Coordinator :EnsureUniqueCredentialProofs()\nCheckCredentialProofs()\nWithdrawCredentialProofs()
Coordinator -> Bob : UnsignedTransactionSecret

== Transaction Signing ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Alice1 -> Coordinator: RoundId, AliceId, InputSignature
Coordinator -> Alice1 : ACK

Alice2 -> Coordinator: RoundId, AliceId, InputSignature
Coordinator -> Alice2 : ACK

== Transaction Broadcasting ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinStatuses[]

@enduml

image

nopara73 avatar Jun 18 '20 15:06 nopara73

i don't understand why the same alice has to request zero credentials more than once (i believe you discussed that after i had to leave the call?)

IMO the simplest way to manage this is to provide a zero credential request with each input registration, those can't be used to DoS so there's no concern, and then at connection confirmation to provide the credential request for the input balance.

since credential requests that merge or split amounts need to prove the balance with respect to presented credentials, this also means that real credential requests would not be submitted until after the connection confirmation phase is reached (which maybe we should rename this to input registration, and refer to what we currently call input registration as input pre-registration?)

also note that output registrations as specified in the unified protocol also require credential requests and issuance, i think this can be omitted depending on how output amounts are constrained, but the general approach may require getting change from an output registration that is then used in a later output registration, if the client can't know in advance exactly what output registrations it will attempt and whether or not they will be accepted by the coordinator. one solution to this is to issue credentials not just for amounts, but for weight units, and ensuring that these don't exceed MAX_STANDARD_TX_WEIGHT after accounting for the shared overhead and the input registrations.

also i believe the 4th interaction during input registration is meant to be for alice2, not alice1?

nothingmuch avatar Jun 18 '20 22:06 nothingmuch

Some bikeshedding

  1. ZeroCredentials and RealCredentials should just be Credentials, and RealCredentialRequests should be CredentialRequests but ZeroCredentialRequest should stay that way because it has a different structure

  2. CredentialProofs should be PresentedCredentials or CredentialPresentations

  3. EnsureUniqueCredentialProofs - the proofs are randomized, but the serial number is not, so I think that should be EnsureUniqueSerialNumber

  4. what is WithdrawCredentialProofs? is that marking the serial number as used? I don't like the term "withdraw" here since withdrawal has different connotations in a money context, if so I suggest MarkSerialNumberUsed or something

updated diagram

This incorporates my previous comment as well as this bikeshedding

@startuml
collections Satoshis
participant Coordinator
participant Alice1
participant Alice2
participant Bob
database Mempool

title Scenario: A Participant Is Consolidating 2 Coins (Alice1, Alice2) Into 1 (Bob)\n

== Input Pre-Registration ==

Satoshis -> Coordinator ++ : GetCoinjoinStatuses
return CoinjoinStatuses[]
...
Alice1 -> Coordinator ++: RoundId, Input, RoundParamSig, ZeroCredentialRequests[]
note right of Coordinator :EnsureUniqueInput()\nCheckRoundParamSig()\nIsUnspent()\nIsConfirmed()\nMarkInputUsed()
return AliceId, Credentials[]
note left of Alice1 :CheckCredentialIparams()
...
Alice2 -> Coordinator ++ : RoundId, Input, RoundParamSig, ZeroCredentialRequests[]
note right of Coordinator :EnsureUniqueInput()\nCheckRoundParamSig()\nIsUnspent()\nIsConfirmed()\nMarkInputUsed()
return AliceId, Credentials[]
note left of Alice1 :CheckCredentialIparams()
...
Alice1 -> Coordinator ++: RoundId, AliceId, CredentialPresentations[], CredentialRequests[], BalanceProof, ZeroCredentialsRequest[]
note right of Coordinator :EnsureUniqueSerialNumber()\nCheckCredentialProofs()\nMarkSerialNumberUsed()
return ZeroCredentials[] (NACK)
note left of Alice1 :CheckCredentialIparams()
...
Alice2 -> Coordinator ++: RoundId, AliceId, CredentialPresentations[], CredentialRequests[], BalanceProof, ZeroCredentialsRequest[]
note right of Coordinator :EnsureUniqueSerialNumber()\nCheckCredentialProofs()\nMarkSerialNumberUsed()
return ZeroCredentials[] (NACK)
note left of Alice1 :CheckCredentialIparams()
== Connection Confirmation ==
Satoshis -> Coordinator ++ : GetCoinjoinStatuses
return CoinjoinStatuses[]
...
Alice1 -> Coordinator ++: RoundId, AliceId, CredentialPresentations[], CredentialRequests[], BalanceProof, ZeroCredentialsRequests[]
note right of Coordinator :EnsureUniqueSerialNumber()\nCheckCredentialProofs()\nMarkSerialNumberUsed()
return Credentials[]
note left of Alice1 :CheckCredentialIparams()
|||
Alice2 -> Coordinator ++: RoundId, AliceId, CredentialPresentations[], CredentialRequests[], BalanceProof, ZeroCredentialRequests[]
note right of Coordinator :EnsureUniqueSerialNumber()\nCheckCredentialProofs()\nMarkSerialNumberUsed()
return Credentials[]
note left of Alice1 :CheckCredentialIparams()

== Output Registration ==

Satoshis -> Coordinator ++ : GetCoinjoinStatuses
return CoinjoinStatuses[]
...
Bob -> Coordinator ++: RoundId, Output, CredentialPresentations[], CredentialRequests[], BalanceProof
note right of Coordinator :EnsureUniqueSerialNumber()\nCheckCredentialProofs()\nMarkSerialNumberUsed()
return UnsignedTransactionSecret, Credentials[]
note left of Alice1 :CheckCredentialIparams()

== Transaction Signing ==

Satoshis -> Coordinator ++ : GetCoinjoinStatuses
return CoinjoinStatuses[]
...
note left of Alice1 :CheckRoundParamSigs()
Alice1 -> Coordinator ++: RoundId, AliceId, InputSignature
note right of Coordinator :CheckInputSig()
return ACK
|||
Alice2 -> Coordinator ++: RoundId, AliceId, InputSignature
note right of Coordinator :CheckInputSig()
return ACK

== Transaction Broadcasting ==

Coordinator -> Mempool**  : SignedTransaction

Satoshis -> Coordinator ++ : GetCoinjoinStatuses
return CoinjoinStatuses[]

@enduml

I omitted the updated picture because it's too big for the PNG and github doesn't allow SVG

nothingmuch avatar Jun 19 '20 02:06 nothingmuch

i don't understand why the same alice has to request zero credentials more than once (i believe you discussed that after i had to leave the call?)

Zero credentials must be reused and rerequested for every connection confirmation otherwise the Coordinator would know in connection confirmation phase which Alices come for their real credentials the first time.

IMO the simplest way to manage this is to provide a zero credential request with each input registration, those can't be used to DoS so there's no concern, and then at connection confirmation to provide the credential request for the input balance.

You assume that Alice there's a difference between connection confirmation request between Input Registration phase and Connection Confirmation phase. You could assume that, but if you don't, then things go faster.

nopara73 avatar Jun 19 '20 10:06 nopara73

also note that output registrations as specified in the unified protocol also require credential requests and issuance, i think this can be omitted depending on how output amounts are constrained, but the general approach may require getting change from an output registration that is then used in a later output registration, if the client can't know in advance exactly what output registrations it will attempt and whether or not they will be accepted by the coordinator. one solution to this is to issue credentials not just for amounts, but for weight units, and ensuring that these don't exceed MAX_STANDARD_TX_WEIGHT after accounting for the shared overhead and the input registrations.

Probably it'd be better like that, let's think through this during the call.

nopara73 avatar Jun 19 '20 10:06 nopara73

also i believe the 4th interaction during input registration is meant to be for alice2, not alice1?

No, this is how I'm depicting the fact that Alice doesn't know if she's going to get zero credentials or real credentials for her connection confirmation-request. In fact, it'd be even better to introduce Alice3, whose input-registration request starts in Input Registration phase, but the Coordinator switches to Connection Confirmation phase as and the response will be a real credentials instead of zero credentials.

nopara73 avatar Jun 19 '20 11:06 nopara73

I added the Alice3 idea.

@startuml

title Scenario: A Participant Is Consolidating 3 Coins (Alice1, Alice2, Alice3) Into 1 (Bob)\n

== Input Registration ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Alice1 -> Coordinator : RoundId, Input, RoundParamSig, RealCredentialRequests[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueInput()\nCheckRoundParamSig()\nIsUnspent()\nIsConfirmed()\nIsRoundFull() -> 1/3
Coordinator -> Alice1 : AliceId, ZeroCredentials[]
...

Alice1 -> Coordinator: RoundId, AliceId, CredentialProofs[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueCredentialProofs()\nCheckCredentialProofs()\nWithdrawCredentialProofs()
Coordinator -> Alice1 : ZeroCredentials[]

Alice2 -> Coordinator : RoundId, Input, RoundParamSig, RealCredentialRequests[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueInput()\nCheckRoundParamSig()\nIsUnspent()\nIsConfirmed()\nIsRoundFull() -> 2/3
Coordinator -> Alice2 : AliceId, ZeroCredentials[]

...

Alice1 -> Coordinator: RoundId, AliceId, CredentialProofs[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueCredentialProofs()\nCheckCredentialProofs()\nWithdrawCredentialProofs()
Coordinator -> Alice1 : ZeroCredentials[]

Alice3 -> Coordinator: RoundId, AliceId, CredentialProofs[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueCredentialProofs()\nCheckCredentialProofs()\nWithdrawCredentialProofs()\nIsRoundFull() -> 3/3
== Connection Confirmation ==
Coordinator -> Alice3 : RealCredentials[]

Alice1 -> Coordinator: RoundId, AliceId, CredentialProofs[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueCredentialProofs()\nCheckCredentialProofs()\nWithdrawCredentialProofs()
Coordinator -> Alice1 : RealCredentials[]

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Alice2 -> Coordinator: RoundId, AliceId, CredentialProofs[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueCredentialProofs()\nCheckCredentialProofs()\nWithdrawCredentialProofs()
Coordinator -> Alice2 : RealCredentials[]

== Output Registration ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Bob -> Coordinator: RoundId, Output, CredentialProofs[]
note over Coordinator :EnsureUniqueCredentialProofs()\nCheckCredentialProofs()\nWithdrawCredentialProofs()
Coordinator -> Bob : UnsignedTransactionSecret

== Transaction Signing ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Alice1 -> Coordinator: RoundId, AliceId, InputSignature
Coordinator -> Alice1 : ACK

Alice2 -> Coordinator: RoundId, AliceId, InputSignature
Coordinator -> Alice2 : ACK

== Transaction Broadcasting ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinStatuses[]

@enduml

image

nopara73 avatar Jun 19 '20 11:06 nopara73

ZeroCredentials and RealCredentials should just be Credentials, and RealCredentialRequests should be CredentialRequests but ZeroCredentialRequest should stay that way because it has a different structure

I won't incorporate that, because what the server gives back is a signal of phase change.

nopara73 avatar Jun 19 '20 11:06 nopara73

CredentialProofs should be PresentedCredentials or CredentialPresentations

I incorporated it.

@startuml

title Scenario: A Participant Is Consolidating 3 Coins (Alice1, Alice2, Alice3) Into 1 (Bob)\n

== Input Registration ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Alice1 -> Coordinator : RoundId, Input, RoundParamSig, RealCredentialRequests[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueInput()\nCheckRoundParamSig()\nIsUnspent()\nIsConfirmed()\nIsRoundFull() -> 1/3
Coordinator -> Alice1 : AliceId, ZeroCredentials[]
...

Alice1 -> Coordinator: RoundId, AliceId, PresentedCredentials[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniquePresentedCredentials()\nCheckPresentedCredentials()\nWithdrawPresentedCredentials()
Coordinator -> Alice1 : ZeroCredentials[]

Alice2 -> Coordinator : RoundId, Input, RoundParamSig, RealCredentialRequests[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniqueInput()\nCheckRoundParamSig()\nIsUnspent()\nIsConfirmed()\nIsRoundFull() -> 2/3
Coordinator -> Alice2 : AliceId, ZeroCredentials[]

...

Alice1 -> Coordinator: RoundId, AliceId, PresentedCredentials[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniquePresentedCredentials()\nCheckPresentedCredentials()\nWithdrawPresentedCredentials()
Coordinator -> Alice1 : ZeroCredentials[]

Alice3 -> Coordinator: RoundId, AliceId, PresentedCredentials[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniquePresentedCredentials()\nCheckPresentedCredentials()\nWithdrawPresentedCredentials()\nIsRoundFull() -> 3/3
== Connection Confirmation ==
Coordinator -> Alice3 : RealCredentials[]

Alice1 -> Coordinator: RoundId, AliceId, PresentedCredentials[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniquePresentedCredentials()\nCheckPresentedCredentials()\nWithdrawPresentedCredentials()
Coordinator -> Alice1 : RealCredentials[]

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Alice2 -> Coordinator: RoundId, AliceId, PresentedCredentials[], ZeroCredentialRequests[]
note over Coordinator :EnsureUniquePresentedCredentials()\nCheckPresentedCredentials()\nWithdrawPresentedCredentials()
Coordinator -> Alice2 : RealCredentials[]

== Output Registration ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Bob -> Coordinator: RoundId, Output, PresentedCredentials[]
note over Coordinator :EnsureUniquePresentedCredentials()\nCheckPresentedCredentials()\nWithdrawPresentedCredentials()
Coordinator -> Bob : UnsignedTransactionSecret

== Transaction Signing ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinjoinStatuses[]

Alice1 -> Coordinator: RoundId, AliceId, InputSignature
Coordinator -> Alice1 : ACK

Alice2 -> Coordinator: RoundId, AliceId, InputSignature
Coordinator -> Alice2 : ACK

== Transaction Broadcasting ==

Satoshis -> Coordinator : GetCoinjoinStatuses
Satoshis <- Coordinator : CoinStatuses[]

@enduml

image

nopara73 avatar Jun 19 '20 11:06 nopara73

Next Meeting ToDo

1. Go through @nothingmuch's idea.

2. Go through @nopara73's idea.

3. Address these 3 points in the already conflictless scheme:

EnsureUniqueCredentialProofs - the proofs are randomized, but the serial number is not, so I think that should be EnsureUniqueSerialNumber

what is WithdrawCredentialProofs? is that marking the serial number as used? I don't like the term "withdraw" here since withdrawal has different connotations in a money context, if so I suggest MarkSerialNumberUsed or something

also note that output registrations as specified in the unified protocol also require credential requests and issuance, i think this can be omitted depending on how output amounts are constrained, but the general approach may require getting change from an output registration that is then used in a later output registration, if the client can't know in advance exactly what output registrations it will attempt and whether or not they will be accepted by the coordinator. one solution to this is to issue credentials not just for amounts, but for weight units, and ensuring that these don't exceed MAX_STANDARD_TX_WEIGHT after accounting for the shared overhead and the input registrations.

nopara73 avatar Jun 19 '20 11:06 nopara73

Today's Progress (2020-06-19)

@startuml
collections Satoshis
participant Coordinator
participant Alice1
participant Alice2
participant Bob
database Mempool

title Scenario: A Participant Is Consolidating 2 Coins (Alice1, Alice2) Into 1 (Bob)\n

== Input Registration ==

Satoshis -> Coordinator ++ : GetCoinjoinStatuses
return CoinjoinStatuses[]

Alice1 -> Coordinator ++: IREG - RoundId, Input, RoundParamSig, ZeroCredReqs[]
note right of Coordinator :EnsureUniqueInput()\nCheckRoundParamSig()\nIsUnspent()\nIsConfirmed()
return AliceId, ZeroCreds[]
...

Alice1 -> Coordinator ++: CCONF - RoundId, AliceId, PresentedCreds[], RealCredReqs[], BalanceProof, ZeroCredReqs[]
note right of Coordinator :EnsureUniqueSerialNumber()\nCheckCredProofs()
return ZeroCreds[]

Alice2 -> Coordinator ++: IREG - RoundId, Input, RoundParamSig, ZeroCredReqs[]
note right of Coordinator :EnsureUniqueInput()\nCheckRoundParamSig()\nIsUnspent()\nIsConfirmed()
return AliceId, ZeroCreds[]

== Connection Confirmation ==

Alice1 -> Coordinator ++: CCONF - RoundId, AliceId, PresentedCreds[], RealCredReqs[], BalanceProof, ZeroCredReqs[]
note right of Coordinator :EnsureUniqueSerialNumber()\nCheckCredProofs()
return RealCreds[]

Alice2 -> Coordinator ++: CCONF - RoundId, AliceId, PresentedCreds[], RealCredReqs[], BalanceProof, ZeroCredReqs[]
note right of Coordinator :EnsureUniqueSerialNumber()\nCheckCredProofs()
return RealCreds[]

== Output Registration ==

Bob -> Coordinator ++: OREG - RoundId, Output, PresentedCreds[]
note right of Coordinator :EnsureUniqueSerialNumber()\nCheckCredProofs()
return UnsignedTransactionSecret

== Transaction Signing ==

Alice1 -> Coordinator ++: SIG - RoundId, AliceId, InputSig
note right of Coordinator :CheckInputSig()
return ACK

Alice2 -> Coordinator ++: SIG - RoundId, AliceId, InputSig
note right of Coordinator :CheckInputSig()
return ACK

== Transaction Broadcasting ==

Coordinator -> Mempool**  : SignedTransaction

Satoshis -> Coordinator ++ : GetCoinjoinStatuses
return CoinjoinStatuses[]

@enduml

image

nopara73 avatar Jun 19 '20 16:06 nopara73

This might be prettified with composite states, one for each phase, but i found plantuml's output a little unpredictable with that

@startuml

title Round State Diagram

hide empty description
state "Input Registration" as in
state "Input Registered" as regin <<sdlreceive>>
state "Connection Confirmation" as conf
state "Input Confirmed" as confin <<sdlreceive>>
state "Output Registration" as out
state "Output Registered" as regout <<sdlreceive>>
state "Transaction Signing" as sign
note left of sign
    if signing times out a new MAC key must be generated
    and only signing inputs permitted to re-register
end note
state "Input Signed" as sig <<sdlreceive>>
state "Transaction Broadcast" as bcast
state c1 <<choice>>
state c2 <<choice>>
state c3 <<choice>>
state c4 <<choice>>
state c5 <<choice>>
state c6 <<choice>>

[*] --> in
in --> regin
regin --> c1
in --> c5 : timeout
c5 --> conf : [registered >= min]
c5 --> [*] : [registered < min]
c1 --> in : [registered < max]
c1 --> conf : [registered == max]
conf --> c6 : timeout
c6 --> [*] : [confirmed < min]
c6 --> out : [confirmed >= min]
conf --> confin
confin --> c2
c2 --> conf  : [unconfirmed > 0]
c2 --> out : [unconfirmed == 0]
out --> regout
regout --> c3
c3 --> sign : [balance == 0]
c3 --> out : [balance > 0]
out --> sign : timeout
sign --> sig
sig --> c4
c4 --> sign : [unsigned > 0]
c4 --> bcast : [unsigned == 0]
sign --> in  : timeout
bcast --> [*]



regin: nonempty description
confin: nonempty description
regout: nonempty description
sig: nonempty description

@enduml

image

nothingmuch avatar Jun 20 '20 03:06 nothingmuch

Looks nice, but better to go through together to get familiar with the notation.

nopara73 avatar Jun 21 '20 10:06 nopara73

Today's progress and (hopefully) final interaction diagram.

@startuml
collections Satoshis
participant Coordinator
participant Alice1
participant Alice2
participant Bob
database Mempool

title Scenario: A Participant Is Consolidating 2 Coins (Alice1, Alice2) Into 1 (Bob)\n

== Input Registration ==

Satoshis -> Coordinator ++ : GetCoinjoinStatuses
return CoinjoinStatuses[]

Alice1 -> Coordinator ++: IREG - RoundId, Input, RoundParamSig, ZeroCredReqs[]
note right of Coordinator
    IsRoundFound()
    IsIRegPhase()
    IsUnspent()
    IsConfirmed()
    IsMature()
    IsStandardScript()
    IsNotMalleable()
    CheckRoundParamSig()
    IsAllowedIfBlameRound()
    IsNotBanned()
    UpdateIfDuplicateInput()
end note
return AliceId, ZeroCreds[]
...

Alice1 -> Coordinator ++: CCONF - RoundId, AliceId, PresentedCreds[], RealCredReqs[], BalanceProof, ZeroCredReqs[]
note right of Coordinator
    IsRoundFound()
    IsIRegOrCConfPhase()
    IsAliceFound()
    EnsureUniqueSerialNumber()
    CheckCredProofs()
end note
return ZeroCreds[]

Alice2 -> Coordinator ++: IREG - RoundId, Input, RoundParamSig, ZeroCredReqs[]
note right of Coordinator
    IsRoundFound()
    IsIRegPhase()
    IsUnspent()
    IsConfirmed()
    IsMature()
    IsStandardScript()
    IsNotMalleable()
    CheckRoundParamSig()
    IsAllowedIfBlameRound()
    IsNotBanned()
    UpdateIfDuplicateInput()
end note
return AliceId, ZeroCreds[]

== Connection Confirmation ==

Alice1 -> Coordinator ++: CCONF - RoundId, AliceId, PresentedCreds[], RealCredReqs[], BalanceProof, ZeroCredReqs[]
note right of Coordinator
    IsRoundFound()
    IsIRegOrCConfPhase()
    IsAliceFound()
    EnsureUniqueSerialNumber()
    CheckCredProofs()
end note
return RealCreds[]

Alice2 -> Coordinator ++: CCONF - RoundId, AliceId, PresentedCreds[], RealCredReqs[], BalanceProof, ZeroCredReqs[]
note right of Coordinator
    IsRoundFound()
    IsIRegOrCConfPhase()
    IsAliceFound()
    EnsureUniqueSerialNumber()
    CheckCredProofs()
end note
return RealCreds[]

== Output Registration ==

Bob -> Coordinator ++: OREG - RoundId, Output, PresentedCreds[]
note right of Coordinator
    IsRoundFound()
    IsORegPhase()
    IsStandardScript()
    EnsureUniqueSerialNumber()
    CheckCredProofs()
end note
return UnsignedTransactionSecret

== Transaction Signing ==

Alice1 -> Coordinator ++: TSIG - RoundId, AliceId, InputSig
note right of Coordinator
    IsRoundFound()
    IsTSigPhase()
    IsAliceFound()
    CheckInputSig()
end note
return ACK

Alice2 -> Coordinator ++: TSIG - RoundId, AliceId, InputSig
note right of Coordinator
    IsRoundFound()
    IsTSigPhase()
    IsAliceFound()
    CheckInputSig()
end note
return ACK

== Transaction Broadcasting ==

Coordinator -> Mempool**  : SignedTransaction

Satoshis -> Coordinator ++ : GetCoinjoinStatuses
return CoinjoinStatuses[]

@enduml

image

nopara73 avatar Jun 22 '20 14:06 nopara73

something that is not clear to me about the state diagram and confirmation: suppose that a round fails, and a restricted round is attempted, and connection confirmation times out without reaching a minimum... should the inputs that did register and confirm be treated differently than the inputs who did not? without a signing phase there is no way to assign blame, but a signing phase may be detrimental for privacy. a malicious user intending to deny service still gets at least one input blamed in that case, since it only applies to retried rounds

nothingmuch avatar Jun 22 '20 16:06 nothingmuch