BIP-322: switch to using tx based approach #1003

pull kallewoof wants to merge 1 commits into bitcoin:master from kallewoof:202010-signmsg changing 1 files +59 −137
  1. kallewoof commented at 7:54 AM on October 1, 2020: member

    It seems more people are leaning towards this being an update to BIP-322 over a new BIP, so I am swapping this to being an update instead of a new BIP.

    I am not sure if I like the legacy format compatibility, but people asked for it elsewhere, so I'm keeping it for now.

  2. kallewoof commented at 7:57 AM on October 1, 2020: member

    No idea what's up with Travis.

  3. kallewoof force-pushed on Oct 1, 2020
  4. in README.mediawiki:937 in fc7479dffa outdated
     936 | @@ -937,7 +937,7 @@ Those proposing changes should consider that ultimately consent may rest with th
     937 |  | Generic Signed Message Format
    


    MarcoFalke commented at 8:06 AM on October 1, 2020:

    You'll have to add |- style="background-color: #ffcfcf" two lines above this one


    kallewoof commented at 8:36 AM on October 1, 2020:

    Thanks, done

  5. kallewoof force-pushed on Oct 1, 2020
  6. kallewoof force-pushed on Oct 1, 2020
  7. stepansnigirev commented at 1:17 PM on October 1, 2020: contributor

    Regarding compatibility with legacy message signing, I think at the moment when you are signing messages with any wallet it calculates the hash in the following way: SHA256d(0x18 || "Bitcoin Signed Message:\n" || compactint(len(m)) || m) where 0x18 is the length of the magic prefix. So digest for the message Hello will be: SHA256d(0x18 || "Bitcoin Signed Message:\n" || 0x05 || "Hello")

    If this digest is used for legacy p2pkh signing I think it will be easier to integrate this BIP because it will be 100% backward compatible with currently used message signing.

  8. kallewoof commented at 3:06 AM on October 2, 2020: member

    @stepansnigirev So the current approach is that people may use the legacy signing if they want to, in which case they will do exactly the same way (including digest derivation). If they don't, then the thing won't be backwards compatible anyway, even if we reuse the same digest derivation method. Or am I misunderstanding you?

  9. kallewoof commented at 5:50 AM on October 2, 2020: member

    Pushed a to-squash that overwrites BIP-322 instead of making this a new BIP. Will drop or squash once decided.

  10. kallewoof force-pushed on Oct 2, 2020
  11. stepansnigirev commented at 9:51 PM on October 2, 2020: contributor

    So the current approach is that people may use the legacy signing if they want to, in which case they will do exactly the same way (including digest derivation). If they don't, then the thing won't be backwards compatible anyway, even if we reuse the same digest derivation method. Or am I misunderstanding you?

    I think it's ok to have a different digest (tagged hash) for the new message signing, I am just saying that legacy message signing is described in the document a bit unclear (I think).

    Does this: SHA56d("Bitcoin Signed Message:\n"||m) mean I need to simply concatenate magic and the message, or do I need to prepend it with it's length?

    I was not able to find any documentation for legacy signing standard so I had to look up in the code of the wallets:

    Bitcoin Core: https://github.com/bitcoin/bitcoin/blob/a12d9e5fd24a25bef476c10620317e43a5905754/src/util/message.cpp#L75

    Electrum: https://github.com/spesmilo/electrum/blob/9c5e49f432a4310aa4aec2f7985b52f032210b7e/electrum/ecc.py#L355-L358

    Bitcoinlib: https://github.com/petertodd/python-bitcoinlib/blob/master/bitcoin/signmessage.py#L48-L63

    I am not 100% sure but it looks like the magic and the message are serialized with their lengths. It would be nice to make this a bit more clear in the bip.

    Regarding the new standard to sign messages - I really like it.

  12. ChristopherA commented at 1:41 AM on October 3, 2020: none

    Concept ACK: @BlockchainCommons and a number of our patrons are very interested in supporting new message signing approach.

  13. kallewoof commented at 6:25 AM on October 3, 2020: member

    I think it's ok to have a different digest (tagged hash) for the new message signing, I am just saying that legacy message signing is described in the document a bit unclear (I think).

    OH! That is definitely an error on my end. I'll fix the legacy description.

  14. kallewoof force-pushed on Oct 3, 2020
  15. kallewoof force-pushed on Oct 3, 2020
  16. kallewoof force-pushed on Oct 3, 2020
  17. kallewoof commented at 6:44 AM on October 3, 2020: member

    Dropped 992c5e3 tentatively. Will cherry-pick if we decide this should replace BIP322 after all.

  18. in bip-signmessage-redone.mediawiki:28 in fe91069e93 outdated
      23 | +* Let <code>V</code> generate a message <code>M</code> and hand this to <code>P</code>.
      24 | +* <code>P</code> generates a signature <code>S</code> by signing the message <code>M</code> using <code>k</code>. Given <code>S</code>, <code>V</code> can prove that <code>P</code> has the private key associated with <code>A</code>.
      25 | +
      26 | +The astute reader will notice that the above is missing a critical part, namely the pubkey <code>kG</code>, without which the verifier cannot actually verify the message. The current message signing standard solves this via a cryptographic trick, wherein the signature <code>S</code> above is a special "recoverable signature" type. Given the message <code>M</code> and the signature <code>S</code>, it is then possible to recover the pubkey <code>kG</code>. The system thus derives the address for the pubkey <code>kG</code>, and if it does not match <code>A</code>, the proof is deemed invalid.
      27 | +
      28 | +While this is a neat trick, it unnecessarily restricts and complicates the message signing mechanism; for instance, it is currently not possible to sign a message for a P2SH address, because there is no pubkey to recover from the resulting signature.
    


    dgpv commented at 1:00 PM on October 3, 2020:

    Isn't it "there's no standard way to get from pubkey to the address" rather than "no pubkey to recover from the resulting signature" ?

    Surely the signature could be of recoverable type, and if a prover and verifier use the same script template, the verifier can reconstruct the address and check (if all the other info such as other pubkeys are known to verifier and the template can be filled completely)


    kallewoof commented at 4:29 AM on October 5, 2020:

    With a p2pkh there is a pubkey hash, and with p2sh there isn't. Yes, there may be one or multiple keys embedded in the script hash, but there's no standardized way of determining that, as opposed to the p2pkh case.

    Edit: more importantly, the recoverable ECDSA signature can recover only one pubkey, so a multisig key, for example, doesn't actually work.


    dgpv commented at 8:06 AM on October 5, 2020:

    That was exactly my point in regards to the phrase "because there is no pubkey to recover from the resulting signature." -- It does not convey the correct meaning. The correct meaning is along the lines of "there's no standardized way of determining ...", "there's no standard way to get from recovered pubkey to the address"


    kallewoof commented at 8:58 AM on October 5, 2020:

    I see what you mean, changing.

  19. in bip-signmessage-redone.mediawiki:24 in fe91069e93 outdated
      19 | +== Background ==
      20 | +
      21 | +* Assume two actors, a prover <code>P</code> and a verifier <code>V</code>.
      22 | +* <code>P</code> wants to prove that they own the private key <code>k</code> associated with a given address <code>A</code> (which in turn is derived from the pubkey <code>kG</code>).
      23 | +* Let <code>V</code> generate a message <code>M</code> and hand this to <code>P</code>.
      24 | +* <code>P</code> generates a signature <code>S</code> by signing the message <code>M</code> using <code>k</code>. Given <code>S</code>, <code>V</code> can prove that <code>P</code> has the private key associated with <code>A</code>.
    


    dgpv commented at 1:02 PM on October 3, 2020:

    I think it should read "V can check" rather than "V can prove", as it is the prover who proves, and the verifier who verifies (checks)


    kallewoof commented at 4:30 AM on October 5, 2020:

    Good point, yeah. Changing.

  20. in bip-signmessage-redone.mediawiki:72 in fe91069e93 outdated
      67 | +
      68 | +1. Valid, if it is a successful spend according to the current consensus rules (sometimes called "policy").
      69 | +2. Inconclusive, if it is a successful spend according to consensus rules, but NOT according to policy rules
      70 | +3. Invalid, if it is not a successful spend according to consensus rules
      71 | +
      72 | +A proof is the base64-encoding of the message_signature as is. A validator takes the to be proven pubkey and the proof and transforms it to virtual transactions as described above.
    


    dgpv commented at 1:21 PM on October 3, 2020:

    It seems to me that it should be "to be proven script and the message" rather than "pubkey".

    The "to_spend" transaction contains a hash of the message in the input and the script "message_challenge" in the ouptut. If no standard script templates are defined that would receive only one pubkey, then the verifier will need to know the script and how to insert the pubkey there, along with a way to construct witness to spend the script (miniscript could help, although won't cover all possible scripts).


    kallewoof commented at 4:35 AM on October 5, 2020:

    Yeah, you're right. Added 'message'.


    kallewoof commented at 4:36 AM on October 5, 2020:

    I'm not sure what you mean by the second part though -- I think the spending transaction's witness data should be enough, no? I need to write the reference implementation to verify though.


    dgpv commented at 8:10 AM on October 5, 2020:

    Maybe I do not fully get what data the prover would provide. Do they provide the full script (including the pubkeys) and the witness data to satisfy this script, that will contain any needed signatures ? Will it be the verifier's job to check that the script matches some template that prover and verifier agree beforehand ?


    kallewoof commented at 9:05 AM on October 5, 2020:

    The prover provides the message_signature:

    1. Prover and verifier establish message and address.
    2. Prover generates the transactions, signs the second one, and takes the signature (witness data) and base64 encodes that and gives it to verifier.
    3. Verifier base64-decodes, regenerates the two transactions, with the signature in place.
    4. Verifier then verifies the transactions, in the same way Signet does it.

    kallewoof commented at 9:11 AM on October 5, 2020:

    To be even more clear, the way this is meant to be done is exactly like the Signet approach, except I'm disallowing scriptSigs and only allowing script witness, where Signet allows both types.

    I.e. the signet block signature is the equivalent of the proof by the prover here, the block hash in Signet is the equivalent of the message hash here, and the block challenge in Signet is the equivalent of the prover's address.


    dgpv commented at 9:17 AM on October 5, 2020:

    Ah, I believe I understand now:

    The witness data will include the script, if the scriptPubKey in the to_spend transaction is p2sh or p2wsh. And the because the prover and verifier agreed on a certain address beforehand, they agreed on the exact script, too.


    kallewoof commented at 9:35 AM on October 5, 2020:

    Yup!

  21. in bip-signmessage-redone.mediawiki:66 in fe91069e93 outdated
      61 | +    vout[0].nValue = 0
      62 | +    vout[0].scriptPubKey = message_signature
      63 | +
      64 | +It may include other inputs, to facilitate a proof of funds.
      65 | +
      66 | +A message signature is considered valid, inconclusive, or invalid based on whether the to_sign transaction is a valid spend of the to_spend transaction or not, according to the following steps (also see Consensus and standard flags section further down):
    


    dgpv commented at 1:27 PM on October 3, 2020:

    If the signature check is performed by the script interpreter, it will honor sighash flags. I think the document should specify that the signature must include SIGHASH_ALL flag, otherwise signature with SIGHASH_NONE would verify with any message_challenge


    dgpv commented at 1:41 PM on October 3, 2020:

    Also, scriptSig is not included in segwit signature hash, rather the script is included. It may not be clear for the implementors how they should make the signature commit to message_hash. I think the spec should say that the signature should be over sighash calculated with:

    script = OP_0 PUSH32[ message_hash ]
    hash_type = SIGHASH_ALL
    amount = 0
    sigversion = SIGVERSION_WITNESS_V0
    

    (witness sigversion will avoid stripping CODESEPARATOR that happens with sighash calculating with sigversion = SIGVERSION_BASE)


    kallewoof commented at 4:43 AM on October 5, 2020:

    Thanks, yep. I think requiring it to be SIGHASH_ALL is beneficial, since this also supports proof of funds.


    kallewoof commented at 5:13 AM on October 5, 2020:

    For some reason I didn't see your 2nd response until now. I added a note that they must use SIGHASH_ALL, which commits to the message hash as expected. What do you think?


    dgpv commented at 8:26 AM on October 5, 2020:

    Moved the conversation about the rest to the vin[0].scriptSig = OP_0 PUSH32[ message_hash ] line.

  22. kallewoof force-pushed on Oct 5, 2020
  23. kallewoof force-pushed on Oct 5, 2020
  24. in bip-signmessage-redone.mediawiki:47 in aa6ceaf8c0 outdated
      42 | +    nVersion = 0
      43 | +    nLockTime = 0
      44 | +    vin[0].prevout.hash = 0000...000
      45 | +    vin[0].prevout.n = 0xFFFFFFFF
      46 | +    vin[0].nSequence = 0
      47 | +    vin[0].scriptSig = OP_0 PUSH32[ message_hash ]
    


    dgpv commented at 8:25 AM on October 5, 2020:

    The SignatureHash function in Core does not take anything from scriptSig. It takes the scriptCode as the supplied argument, and later uses it in the hashing.

    AFAIU this scriptSig = OP_0 PUSH32[ message_hash ] is intended to convey that the message hash will be committed to as part of the script supplied to SignatureHash. But as SignatureHash does not use the contents of scriptSig in the input at all, and only uses the script that is supplied as argument, I believe that it would be more clear to state this explicitly. This is what I meant in an earlier message. The document should just specify all the relevant arguments to SignatureHash, roughly: SignatureHash(CScript([OP_0, message_hash]), tx_to_spend, 0 /* nIn */, SIGHASH_ALL, 0 /* amount */, SigVersion::WITNESS_V0)


    dgpv commented at 8:29 AM on October 5, 2020:

    (edited the above message, added sigversion as the parameter to SignatureHash)

    The way to calculate the hash that will be signed need to be unambiguous. If we choose to use SigVersion::WITNESS_V0 for sigversion, then amount should be fixed, because it will affect the hash.


    kallewoof commented at 9:09 AM on October 5, 2020:

    We do not actually sign this transaction, we sign the transaction spending it.


    dgpv commented at 9:28 AM on October 5, 2020:

    So it will be included (committed to) in the sighash via txid in the prevout of to_sign transaction. And all the arguments to the SignatureHash except signature type can be derived from the message_challenge script to_spend transaction structure. It is clear to me now, thanks.

  25. kallewoof force-pushed on Oct 5, 2020
  26. kallewoof force-pushed on Oct 5, 2020
  27. kallewoof force-pushed on Oct 5, 2020
  28. kallewoof force-pushed on Oct 5, 2020
  29. in bip-signmessage-redone.mediawiki:62 in a47de99c8a outdated
      57 | +    nLockTime = 0
      58 | +    vin[0].prevout.hash = to_spend.txid
      59 | +    vin[0].prevout.n = 0
      60 | +    vin[0].nSequence = 0
      61 | +    vout[0].nValue = 0
      62 | +    vout[0].scriptPubKey = message_signature
    


    dgpv commented at 9:33 AM on October 5, 2020:

    Why is message_signature is put in the output ?

    IIUC, message_signature is supposed to be provided as base64-encoded proof, not included in the transaction itself.

    If the signature is over this exact "to_sign" transaction, it cannot be included at the time of signing, because there will be a circular dependency (the sighash will be dependent on the output, which have to include the signature)


    kallewoof commented at 9:45 PM on October 5, 2020:

    That looks like an oopsie (that is also repeated in BIP-325). Good catch, fixing!

  30. luke-jr commented at 5:19 PM on October 5, 2020: member

    This seems like it should just be a revision to BIP 322?

    P.S. I still think this whole concept is misguided since it doesn't address the conceptual problems with legacy signmessage.

  31. kallewoof commented at 9:42 PM on October 5, 2020: member

    This seems like it should just be a revision to BIP 322?

    People seem to have differing opinions, but I am inclined to agree with you.

    P.S. I still think this whole concept is misguided since it doesn't address the conceptual problems with legacy signmessage.

    Yeah, I know. I haven't seen any concrete suggestions on how to make it to your liking, though. My channels are open, as the Lightning folks would say..

  32. kallewoof force-pushed on Oct 5, 2020
  33. kallewoof force-pushed on Oct 5, 2020
  34. kallewoof cross-referenced this on Oct 5, 2020 from issue bip-325: correct placement of challenge by kallewoof
  35. kallewoof renamed this:
    BIP: signmessage (replaces BIP-322)
    BIP-322: switch to using tx based approach
    on Oct 5, 2020
  36. kallewoof force-pushed on Oct 5, 2020
  37. luke-jr commented at 10:47 PM on October 5, 2020: member
  38. kallewoof commented at 12:30 AM on October 6, 2020: member

    @luke-jr Going through your diff and commenting on stuff:

    1. I added the background because people actually had no idea what this thing was supposed to be doing. Not sure removing it is the right approach.
    2. I like/agree with the clarifications in the motivation; adopting. I will also adopt the "invoice address" terminology.
    3. Requiring message_challenge to be OP_TRUE for proof of funds is a good idea, yep. They're proving by signing the inputs after all, as long as the signatures are actually committing to the bip-322 input (is this guaranteed?).
    4. Isn't this "additional inputs should be included"? +When a proof of funds is being created, additional outputs should be included for virtually spending transaction outputs of desired value.
    5. I don't like having all the deterministic cruft in the virtual transactions be embedded in the proof, but you're right that in its current form, the proof of funds aspect is not fully covered. What if the proof is the message signature followed by an optional transaction which adds proof of fund inputs? If present, the virtual to_sign transaction uses that transaction as its base and appends the to_spend vin (no need for the OP_RETURN output since there are 1+ outputs already).
    6. I'm totally cool with the rest, e.g. Policy -> Upgradable.

    Pushing a to-squash for comparison.

  39. kallewoof force-pushed on Oct 6, 2020
  40. kallewoof force-pushed on Oct 6, 2020
  41. luke-jr commented at 12:41 AM on October 6, 2020: member

    I removed the background because it was wrong and I didn't see an obvious way to fix it. Current signed messages don't prove you have the private key.

    I don't care strongly how the signatures are serialised, but I don't think more than two elements (message+signature) should be required. Any other serialisation I could think of quickly, however, seemed to increase complexity excessively.

  42. luke-jr commented at 12:43 AM on October 6, 2020: member

    Oh, and note that serialising the entire transactions leaves a lot of room for future extension... Minimising it might not.

  43. kallewoof force-pushed on Oct 6, 2020
  44. kallewoof force-pushed on Oct 6, 2020
  45. kallewoof commented at 1:02 AM on October 6, 2020: member

    Serializing the entire thing means you need to do that extra step as the verifier to ensure it actually contains the necessary stuff, but it seems like it's probably worth it.

    I have removed the Background for now.

  46. kallewoof force-pushed on Oct 6, 2020
  47. kallewoof force-pushed on Oct 6, 2020
  48. kallewoof force-pushed on Oct 6, 2020
  49. kallewoof commented at 1:13 AM on October 6, 2020: member

    With everything included, there are several foot-gun points, so I clarified what validators need to check, beyond the actual tx validation.

  50. kallewoof force-pushed on Oct 6, 2020
  51. kallewoof force-pushed on Oct 6, 2020
  52. kallewoof force-pushed on Oct 7, 2020
  53. kallewoof commented at 5:36 AM on October 7, 2020: member

    Note: a discussion on how to handle OP_CLTV/CSV was made on IRC, resulting in a pushed additional section and two added outcomes (valid_in_future and inconclusive_in_future).

    There are some parts that may need discussion, in particular:

    1. What to do when a CLTV/CSV is encountered in the to_spend transaction.
    2. What to do when a CLTV/CSV is encountered in one of the proof-of-fund inputs.
    3. Whether 1 and 2 above should be considered equivalent or not.

    Right now,

    1. Unconditionally set "in_future" flag (valid becomes valid_in_future; inconclusive becomes inconclusive_in_future).
    2. If the CLTV/CSV condition(s) are not met, set the "in_future" flag.
    3. Equivalent.
  54. kallewoof commented at 6:44 AM on October 15, 2020: member

    @luke-jr I think this is mergeable, but would like if you took a last look at the latest addition (CSV/CLTV).

  55. kallewoof force-pushed on Oct 15, 2020
  56. kallewoof commented at 7:36 AM on October 15, 2020: member

    ~Pushed a change to the to_spend transaction: the first output's nValue was originally set to 0, but is now set to the sum of all inputs (which is zero when no additional inputs exist).~

  57. kallewoof cross-referenced this on Oct 15, 2020 from issue BIP-322 support by kallewoof
  58. kallewoof force-pushed on Oct 15, 2020
  59. dgpv commented at 3:07 PM on October 15, 2020: contributor

    To determine if 'in the future' flag should be present will require knowledge of whether CLTV/CSV opcodes will actually be executed in the script given particular witness data.

    Executing the script with CLTV/CSV will require the to_sign transaction to have locktime / sequence to be set accordingly, and the signature would need to be made over the transaction with these fields set.

    Checking that CLTV/CSV was actually executed would require additional instrumentation of the interpreter, or running the interpreter two times for each opcode: first with transaction locktime / sequence set for CLTV/CSV to be always successful, and then to always fail (and also likely to need distinct signatures for each variant).

    I doubt that handling CLTV/CSV is needed at all for the simple "signature for the address" case. Just say that if your script has these opcodes, you should use "proof of funds" feature.

    The "need to set relevant tx fields in to_sign" issue is still there for "proof of funds" case, so I think it should be specified how checking "the CLTV and/or CSV conditions are not yet met" means that nLockTime on to_sign transaction has to be set for CLTV and that "proof of funds" UTXOs should specify correct sequences.

  60. kallewoof force-pushed on Oct 16, 2020
  61. kallewoof commented at 6:07 AM on October 16, 2020: member

    Writing code for this, I realize it's really foot-gunnery, so I added a validation section specifying what a validator must do. @dgpv Fair points. What if the wording was changed to say the in_future part is optional? I.e. the verifier may check if there is a time lock encumbrance, and if it does, it may reflect this using the in_future flag. Would love to hear Murch's thoughts as he brought this up initially.

  62. kallewoof force-pushed on Oct 16, 2020
  63. dgpv commented at 6:46 AM on October 16, 2020: contributor

    What if the wording was changed to say the in_future part is optional?

    Even if it is optional, it still have to be specified.

    For example, current spec says nLockTime = 0 on to_sign transaction, but for proof-of-funds when scripts has CLTV, nLockTime has to be set accordingly. For the spec to be consistent, it has to account for this.

    It seems that the prover need to to set certain transaction fields on to_sign and Validator has to check them (is nLockTime in the future according to current chain ?)

    For "signature for the address" case, if the verifier chooses to support the in_future flag, the prover has to set nLockTime/nSequence fields on to_sign before signing, and the verifier needs to set them to the same values, and these values need to be communicated. Of course they can be communicated as a full transaction, and then verifier would need to validate the transaction to check that other fields are as written in the spec. It seems as in this case the complexity gets closer to the 'proof of funds' case, and also the data to be exchanged is the same (except the to_sign transaction passed from prover to verifier does not contain real inputs).

    It seems to me that it is easier to introduce a degenerate 'no inputs' proof-of-funds case where prover passes the signed transaction to the verifier, and the verifier validates and then verifies.

    The simple "signature for the address" case then becomes a further simplified case where the to_sign transaction is static.

    Looks like there are three cases:

    1. The full "proof of funds": multiple dynamic to_spend (taken from blockchain) and a dynamic to_sign (provided by the prover)

    2. The complex case of "signature for the address": single static to_spend and a dynamic to_sign (provided by the prover)

    3. The simple case of "signature for the address": single static to_spend and single static to_sign, only the signature provided by the prover, and put by the verifier into the input of to_sign

    It makes sense to make the case 3 mandatory and other two optional, but they need to be thoroughly specified anyway.

    It might make sense to specify the case 3 in one BIP and the other two (more complex) cases in another BIP, so that a simple "signature for the address/script without fancy opcodes" spec can become complete and be adopted earlier

  64. dgpv commented at 6:56 AM on October 16, 2020: contributor

    so that a simple "signature for the address/script without fancy opcodes" spec can become complete and be adopted earlier

    I think this is the most practical case where the lack of modern "signature for invoice address" standard causes pain for people, and the "proof of funds" and "proof of invoice address with fancy script" are much less profound cases.

  65. kallewoof commented at 7:12 AM on October 16, 2020: member

    For example, current spec says nLockTime = 0 on to_sign transaction, but for proof-of-funds when scripts has CLTV, nLockTime has to be set accordingly. For the spec to be consistent, it has to account for this.

    Ah, yes you're right. I'll update the spec to reflect this.

    For "signature for the address" case, if the verifier chooses to support the in_future flag, the prover has to set nLockTime/nSequence fields on to_sign before signing, and the verifier needs to set them to the same values, and these values need to be communicated. Of course they can be communicated as a full transaction, and then verifier would need to validate the transaction to check that other fields are as written in the spec. It seems as in this case the complexity gets closer to the 'proof of funds' case, and also the data to be exchanged is the same (except the to_sign transaction passed from prover to verifier does not contain real inputs).

    Currently, the values are communicated as the two transaction pairs serialized as normal, which means the signer can set an arbitrary nLockTime and the verifier can just adopt/check that.

    It seems to me that it is easier to introduce a degenerate 'no inputs' proof-of-funds case where prover passes the signed transaction to the verifier, and the verifier validates and then verifies.

    This was the initial approach. The reason it's now the two transactions is that it becomes a lot more complex to have multiple types. For instance, if I prove with no additional inputs ("signature for the address") you get a signature only, but when I prove with inputs, you get a signature and the inputs, and now you have to decide if those should include the virtual input, and you get complexities when signing where you have to put it in and then remove it, unless you keep it there.

    Yes, proofs are a little bigger, and you need deserialization code even for the non-PoF case, but you need that anyway, cause you need to be able to independently generate the sighash.

    Looks like there are three cases:

    1. The full "proof of funds": multiple dynamic to_spend (taken from blockchain) and a dynamic to_sign (provided by the prover)
    2. The complex case of "signature for the address": single static to_spend and a dynamic to_sign (provided by the prover)
    3. The simple case of "signature for the address": single static to_spend and single static to_sign, only the signature provided by the prover, and put by the verifier into the input of to_sign

    It makes sense to make the case 3 mandatory and other two optional, but they need to be thoroughly specified anyway.

    It might make sense to specify the case 3 in one BIP and the other two (more complex) cases in another BIP, so that a simple "signature for the address/script without fancy opcodes" spec can become complete and be adopted earlier

    It feels like it wouldn't be useful to have 1-2 unless they were a non-optional part of the specification. Even 3 would require you to have most of the functionality that is required by 1 (serialization, signature verification, transactions, etc).

    (As a sidenote, I'm still wondering if it should optionally allow for "help" transactions, i.e. taken from the (U)TXO set or wallet. This would help when proving for a spent outpoint, and when giving a proof to a verifier who doesn't have access to the UTXO set.)

  66. kallewoof force-pushed on Oct 16, 2020
  67. kallewoof force-pushed on Oct 16, 2020
  68. kallewoof commented at 7:41 AM on October 16, 2020: member

    Checking that CLTV/CSV was actually executed would require additional instrumentation of the interpreter, or running the interpreter two times for each opcode: first with transaction locktime / sequence set for CLTV/CSV to be always successful, and then to always fail (and also likely to need distinct signatures for each variant).

    Btw, I may be mistaken, but I believe the CLTV/CSV checks will succeed, but the locktime/sequence values in the transaction itself will determine if it is in the future or not.

    In other words, the check will succeed, but the lock time will be e.g. 100 blocks from now, or 7 days in the future.

  69. kallewoof force-pushed on Oct 16, 2020
  70. kallewoof force-pushed on Oct 16, 2020
  71. dgpv commented at 8:43 AM on October 16, 2020: contributor

    Btw, I may be mistaken, but I believe the CLTV/CSV checks will succeed, but the locktime/sequence values in the transaction itself will determine if it is in the future or not.

    In other words, the check will succeed, but the lock time will be e.g. 100 blocks from now, or 7 days in the future.

    When the prover provides the to_sign transaction with locktime/sequence fields already set, the verifier that just executes the script will not know if the script is only spendable in the future. To know that, they will need a way to detect that CLTV/CSV in the script is executing. If the verifier only has one to_sign (already signed) transaction from the prover, then without instrumented script interpreter, they won't know if the script can fail with different locktime/sequence.

    On the other hand, they can just assume that if the to_sign transaction has locktime or any input sequence set to non-zero, the script can conatin CLTV/CSV, and therefore they shall set in_the_future flag to true, regardless of whether these opcodes are actually present/executed.

    There seems to be no need to set these fields on to_sign for any other purpose, so maybe the spec can say something like "if nLocktime or any of nSequence for the inputs are not zero on the to_spend virtual transaction, the in_future flag is set to true"

  72. dgpv commented at 8:51 AM on October 16, 2020: contributor

    Currently, the values are communicated as the two transaction pairs serialized as normal, which means the signer can set an arbitrary nLockTime and the verifier can just adopt/check that.

    For the simplest "sign for address" case where only message, script, and witness is required to be communicated it may be more convenient to send just that data, without sending redundant transaction data. The transaction data is redundant in this case because it can be constructed by the prover. Why spend space to send prevout.hash of to_spend transaction if it is known to be all-zeroes ? (edit: the simplest case I'm talking about here is also "no fancy opcodes" case, but even with CLTV/CSV, nLocktime/nSequence can be optionally added without other "static" tx parts)

    There may be usecases when the number of characters to display the proof is limited by the medium

  73. dgpv commented at 9:03 AM on October 16, 2020: contributor

    but when I prove with inputs, you get a signature and the inputs, and now you have to decide if those should include the virtual input

    If the "sig for address" and "proof of funds" are distinct cases, there should be no confusion - in the first case you have virtual input, in the second case you'r not. Or alternatively, always have virtual input, but do not sign it for the "proof of funds" case. The verifier are concerned with proof for individual inputs, nothing bad happens if they would check the witness of the inputs when to_sign is "partially signed" (no sig for virtual input) - they won't be able to broadcast the tx anyway, so no need for it to be fully signed

  74. dgpv commented at 9:09 AM on October 16, 2020: contributor

    Yes, proofs are a little bigger, and you need deserialization code even for the non-PoF case, but you need that anyway, cause you need to be able to independently generate the sighash.

    To generate a sighash you only need serialization code, not deserialization. (re "this is the same conceptually" - yes, but unifying ser/deser usually requires a language powerful enough and some effort to engineer this)

  75. kallewoof commented at 9:33 AM on October 16, 2020: member

    When the prover provides the to_sign transaction with locktime/sequence fields already set, the verifier that just executes the script will not know if the script is only spendable in the future.

    I believe you've got it wrong: OP_CSV and OP_CLTV put constraints on the transaction's locktime. If they fail, it means the locktime is too low. The locktime is enforced outside of the transaction script check, because a transaction with a locktime in the future may not be put into a block. So simply looking at the locktime of the transaction is sufficient to know if it's spendable now or not.

  76. kallewoof commented at 9:42 AM on October 16, 2020: member

    You're right that providing the two transactions and everything for a simple address proof is overkill. I'll switch it to optionally allow only the script witness for cases without proof of funds.

  77. kallewoof force-pushed on Oct 16, 2020
  78. kallewoof force-pushed on Oct 16, 2020
  79. dgpv commented at 9:54 AM on October 16, 2020: contributor

    It feels like it wouldn't be useful to have 1-2 unless they were a non-optional part of the specification. Even 3 would require you to have most of the functionality that is required by 1 (serialization, signature verification, transactions, etc).

    1-2 add complexity because they have more dynamic parts. The case 3 may set the basis for the functionality requirements, but uses this functionality in fewer ways because more of the data is static.

    Having the basic case specified in one spec, and then further extend it in a separate spec for other use-cases seems to me like an adequate way to manage complexity. I'm talking more about spec complexity than implementation complexity. The simple spec without extra details (details that are relevant only to non-mainstream cases) is likely to be better understood and implemented. For example, for the "case 3 only", the spec do not need to say anything about locktime/sequence other than "they are zero".

  80. dgpv commented at 9:58 AM on October 16, 2020: contributor

    So simply looking at the locktime of the transaction is sufficient to know if it's spendable now or not.

    That's right, but this only works if you know the current height of the blockchain.

    If you don't know the current height, you can only assume that transaction may be unspendable. This may be sufficient, as I said in

    On the other hand, they can just assume that if the to_sign transaction has locktime or any input sequence set to non-zero, the script can conatin CLTV/CSV, and therefore they shall set in_the_future flag to true, regardless of whether these opcodes are actually present/executed.

  81. kallewoof commented at 10:20 AM on October 16, 2020: member

    I'm talking more about spec complexity than implementation complexity.

    So am I. The spec grew in a way I disliked which is why I aborted. I've now pivoted back, as you can see in the latest revision.

    Splitting into two specs may be the best course of action in the end - for now, I've split the validation for the two types into two paragraphs..

  82. kallewoof force-pushed on Oct 16, 2020
  83. in bip-0322.mediawiki:64 in d8b4049d34 outdated
     115 | +* All signatures must use the SIGHASH_ALL flag.
     116 | +* The proof is considered valid, inconclusive, or invalid based on whether the to_sign transaction is a valid spend of the to_spend transaction or not, according to the rules specified in the "Consensus and standard flags" section below.
     117 | +* It may (optionally) be encumbered with the in_future flag, according to the rules specified in the "Locktime and Sequence" section below, in which case we refer to the result in text form as "valid_in_future", "inconclusive_in_future", etc.
     118 |  
     119 | -A private key may be used directly to sign a message. In this case, its P2WPKH bech32 address shall be derived, and used as the input.
     120 | +Proofs with additional inputs are the base64-encoding of the to_spend and to_sign transactions concatenated in standard network serialisation, and proofs without additional inputs (simple proofs) are the base64-encoding of the to_sign script witness.
    


    dgpv commented at 11:54 AM on October 16, 2020:

    Simple proofs are not only without inputs, but also without scripts that require nVersion, nLockTime and/or nSequence to be set to non-zero values


    murchandamus commented at 6:04 PM on October 16, 2020:
    Proofs of funds are the base64-encoding of the to_spend and to_sign transactions concatenated in standard network serialisation, and proofs without additional inputs (simple proofs) are the base64-encoding of the to_sign script witness.
    

    Let's use the same term for this concept consistently.


    kallewoof commented at 6:45 AM on October 24, 2020:

    Added "or time locks".

  84. in bip-0322.mediawiki:77 in d8b4049d34 outdated
     135 | +# deserialize the to_spend and to_sign transactions from the proof, and fail if the proof contains extraneous bytes
     136 | +# verify that any additional inputs being proven (proof of funds) are included as inputs in the to_sign transaction, exactly once
     137 | +# reconstruct the to_spend' and to_sign' transactions, based on the specification above, copying the version, lock time, and sequence values
     138 | +# verify that to_spend = to_spend', that to_sign has at least 1 input, has exactly 1 output, and that to_sign.vin[0] = to_sign'.vin[0]
     139 | +# optional: set the "in_future" flag if the transaction's lock time is in the future according to consensus rules
     140 | +# in "coins", a mapping of outpoints (hash, vout) to coins (scriptPubKey, amount), let coins(to_spend.txid, 0) = (to_spend.vout[0], 0)
    


    dgpv commented at 12:03 PM on October 16, 2020:

    The next sentence uses the phrase "coins map". I think using it here would make this easier to read: Establish a "coins map": a mapping of outpoints (hash, vout) to coins (scriptPubKey, amount), initialized to coins(to_spend.txid, 0) => (to_spend.vout[0].scriptPubKey, 0)


    kallewoof commented at 6:46 AM on October 24, 2020:

    Sounds good

  85. in bip-0322.mediawiki:81 in d8b4049d34 outdated
     140 | +# in "coins", a mapping of outpoints (hash, vout) to coins (scriptPubKey, amount), let coins(to_spend.txid, 0) = (to_spend.vout[0], 0)
     141 | +# for each proof of fund input, set the corresponding values in the coins map; abort if the input cannot be found
     142 | +# check the signature of each input using consensus rules, then upgradable rules
     143 |  
     144 | -While omitted below, ERROR is returned if an unforeseen error occurs at any point in the process. A concrete example of this is if a legacy proof is given as input to a non-legacy address; the deserialization of the proof will fail in this case, and this should result in an ERROR result.
     145 | +To validate a simple proof, the following steps must be taken:
    


    dgpv commented at 12:08 PM on October 16, 2020:

    I think simple proof have to come first, so if the reader is only interested in basic "simple proof" usecase, they will be presented with it immediately, rather than read a long list of items and then realize "hey, I do not need all that, I only need these two items"


    kallewoof commented at 6:47 AM on October 24, 2020:

    Of course, swapped.

  86. murchandamus commented at 5:29 PM on October 16, 2020: none

    Hey, I just wanted to clarify: I did bring up the question what would happen if the script contained CSV/CLTV, but it was more from a "what breaks this"-kinda perspective. My intention would be to have the proposal sufficiently robust that it doesn't mislead verifiers about spendability in the case of locktime, i.e. something like "may only be spendable in the future" seems reasonable. It may be interesting to chat with people at Lightning Labs that are working on Lightning Loop whether they have ideas. Being able to sign messages including HTLCs may be of interest to them.

  87. in bip-0322.mediawiki:16 in d8b4049d34 outdated
      23 | -* <code>P</code> generates a signature <code>S</code> by signing the message <code>M</code> using <code>k</code>. Given <code>S</code>, <code>V</code> can prove that <code>P</code> has the private key associated with <code>A</code>.
      24 | -
      25 | -The astute reader will notice that the above is missing a critical part, namely the pubkey <code>kG</code>, without which the verifier cannot actually verify the message. The current message signing standard solves this via a cryptographic trick, wherein the signature <code>S</code> above is a special "recoverable signature" type. Given the message <code>M</code> and the signature <code>S</code>, it is then possible to recover the pubkey <code>kG</code>. The system thus derives the address for the pubkey <code>kG</code>, and if it does not match <code>A</code>, the proof is deemed invalid.
      26 | -
      27 | -While this is a neat trick, it unnecessarily restricts and complicates the message signing mechanism; for instance, it is currently not possible to sign a message for a P2SH address, because there is no pubkey to recover from the resulting signature.
      28 | +A standard for interoperable signed messages based on the Bitcoin Script format, either for proving fund availability, or committing to a message as a recipient of an invoice address.
    


    murchandamus commented at 5:36 PM on October 16, 2020:
    A standard for interoperable signed messages based on the Bitcoin Script format, either for proving fund availability, or committing to a message as the owner of an invoice address.
    

    "Recipient" feels a bit odd in the context, because one could sign a message before ever receiving a payment to an address. I must admit that "owner" doesn't feel great either, but it's the best I have for "person in control of the private key corresponding to the invoice address in question".


    dgpv commented at 7:36 PM on October 16, 2020:

    A custodian might allow customers to sign messages with their designated deposit addresses, but the customers may not control the keys and just receive the promise of being able to withdraw equivalent amount in the future (potentially from completely unrelated UTXO).

    It seems to me that "committing to a message as the intended recipient of the funds to be sent to the invoice address" would be more fitting description


    murchandamus commented at 8:29 PM on October 16, 2020:

    Excellent point! I like the rephrasing, but think it could be put a bit more succinctly: "committing to a message as the intended recipient of funds sent to the invoice address".


    kallewoof commented at 6:50 AM on October 24, 2020:

    Changed!

  88. in bip-0322.mediawiki:20 in d8b4049d34 outdated
      30 |  == Motivation ==
      31 |  
      32 | -The current message signing standard only works for P2PKH (1...) addresses. By extending it to use a Bitcoin Script based approach, it could be made more generic without causing a too big burden on implementers, who most likely have access to Bitcoin Script interpreters already.
      33 | -
      34 | -== Specification ==
      35 | +The current message signing standard only works for P2PKH (1...) invoice addresses. By extending it to use a Bitcoin Script based approach, it could be made more generic without causing a too big burden on implementers, who most likely have access to Bitcoin Script interpreters already.
    


    murchandamus commented at 5:45 PM on October 16, 2020:
    The current message signing standard only works for P2PKH (1...) invoice addresses. We propose to extend and generalize the standard by using a Bitcoin Script based approach. This approach minimizes the burden for implementers as message signing can be expected to be part of a library or project that includes Bitcoin Script interpreters already.
    
  89. in bip-0322.mediawiki:23 in d8b4049d34 outdated
      34 | -== Specification ==
      35 | +The current message signing standard only works for P2PKH (1...) invoice addresses. By extending it to use a Bitcoin Script based approach, it could be made more generic without causing a too big burden on implementers, who most likely have access to Bitcoin Script interpreters already.
      36 |  
      37 | -A new structure <code>SignatureProof</code> is added, which is a simple serializable scriptSig & witness container.
      38 | +Additionally, the current message signing only proves that the message has been committed to by the recipient of a given invoice address.
      39 | +It does not prove anything about the invoice address itself, nor that the signer has access to the private keys used to implement this invoice.
    


    murchandamus commented at 5:54 PM on October 16, 2020:

    I'm not sure I understand this sentence. My impression was that the signer has to have access to the private key to create a signature for the message. Since the current standard only supports messages for P2PKH, would that then not be equivalent to being able to spend funds locked to the address invoice? I'm also not sure what you are trying to express with "implement this invoice". If that just means "spend funds from the address", it would be best to repeat the same terminology for the same procedure. Being a bit repetitive in a technical text is acceptable for clarity's sake.


    kallewoof commented at 6:55 AM on October 24, 2020:

    I can forward the message to Fred, who owns the keys, and he signs it and hands it back to me. Now I don't have the keys myself, but I am able to produce that signature for you, because Fred allows me. He may not allow me to send from a UTXO associated with that invoice address though, but that's another story.

  90. in bip-0322.mediawiki:58 in d8b4049d34 outdated
     106 | +    vin[0].scriptWitness = message_signature
     107 | +    vout[0].nValue = 0
     108 | +    vout[0].scriptPubKey = OP_RETURN
     109 |  
     110 | -For both cases, generate a sighash based on the given scriptPubKey and message as follows:
     111 | +When a proof of funds is being created, additional inputs should be included for virtually spending transaction outputs of desired value.
    


    murchandamus commented at 5:59 PM on October 16, 2020:
    When a proof of funds is being created, additional inputs MUST be included for virtually spending transaction outputs of desired value.
    

    It is not clear to me how the proof of funds would work if the inclusion of the additional funds is optional. Wouldn't that have to be a requirement?


    kallewoof commented at 6:57 AM on October 24, 2020:

    Well, to turn it around, if I give you a proof and it has inputs, it's a proof of funds for those inputs. If you ask me for a proof of funds for input A and I give you one for input B, it's the same as if I gave you a proof for bc1qA when you asked for bc1qB.

    In that sense, I think "should" is correct, since it's up to the prover to put in what the verifier is asking for.


    murchandamus commented at 10:21 PM on October 29, 2020:

    Still a bit on the fence for this one. Obviously, the challenged does not have to fulfill a challenge, but responding with other information instead seems a bit pointless. :)

  91. in bip-0322.mediawiki:66 in d8b4049d34 outdated
     118 |  
     119 | -A private key may be used directly to sign a message. In this case, its P2WPKH bech32 address shall be derived, and used as the input.
     120 | +Proofs with additional inputs are the base64-encoding of the to_spend and to_sign transactions concatenated in standard network serialisation, and proofs without additional inputs (simple proofs) are the base64-encoding of the to_sign script witness.
     121 |  
     122 | -=== Signing ===
     123 | +A validator must verify it is valid and meets the description of virtual transactions as specified above. See "Validation" below.
    


    murchandamus commented at 6:00 PM on October 16, 2020:
    A validator must verify that the to_sign transaction is valid and meets the description of virtual transactions as specified above. See "Validation" below.
    

    Let's avoid "it" and err on repeating the exact data that needs to be valid.


    kallewoof commented at 6:59 AM on October 24, 2020:

    No they also have to make sure both of them are valid, because the prover could give a to_spend transaction that e.g. doesn't include the challenge.


    murchandamus commented at 10:25 PM on October 29, 2020:

    It seems to me that it was not completely clear what is encompassed by "it" in that sentence. "It" is singular, so if this "it" refers to more than one item that needs to be validated, it may be helpful to explicitly list all the items again.


    kallewoof commented at 7:01 AM on October 30, 2020:

    It refers to "the proof" which is both transactions together, which is why it's singular. But you're right it could use clarification: #1028

  92. in bip-0322.mediawiki:70 in d8b4049d34 outdated
     127 |  
     128 | -# Derive the private key privkey for the scriptPubKey; FAIL if not VALID
     129 | -# Generate and return a signature sig with privkey=privkey, sighash=sighash
     130 | -
     131 | -=== Verifying ===
     132 | +To validate a proof with additional inputs, the following steps must be taken:
    


    murchandamus commented at 6:04 PM on October 16, 2020:
    To validate a proof of funds, the following steps must be taken:
    

    We should always use the same term to refer to the same concept.


    kallewoof commented at 6:59 AM on October 24, 2020:

    Good point, yeah

  93. in bip-0322.mediawiki:73 in d8b4049d34 outdated
     131 | -=== Verifying ===
     132 | +To validate a proof with additional inputs, the following steps must be taken:
     133 |  
     134 | -Verify a proof, given a standard flags value, a script sig, an optional witness, and a derived sighash as described above.
     135 | +# deserialize the to_spend and to_sign transactions from the proof, and fail if the proof contains extraneous bytes
     136 | +# verify that any additional inputs being proven (proof of funds) are included as inputs in the to_sign transaction, exactly once
    


    murchandamus commented at 6:09 PM on October 16, 2020:
    # verify that the to_sign transaction uses all inputs covered by the proof of funds exactly once
    
  94. in bip-0322.mediawiki:76 in d8b4049d34 outdated
     134 | -Verify a proof, given a standard flags value, a script sig, an optional witness, and a derived sighash as described above.
     135 | +# deserialize the to_spend and to_sign transactions from the proof, and fail if the proof contains extraneous bytes
     136 | +# verify that any additional inputs being proven (proof of funds) are included as inputs in the to_sign transaction, exactly once
     137 | +# reconstruct the to_spend' and to_sign' transactions, based on the specification above, copying the version, lock time, and sequence values
     138 | +# verify that to_spend = to_spend', that to_sign has at least 1 input, has exactly 1 output, and that to_sign.vin[0] = to_sign'.vin[0]
     139 | +# optional: set the "in_future" flag if the transaction's lock time is in the future according to consensus rules
    


    murchandamus commented at 6:13 PM on October 16, 2020:

    I would propose that we choose to either not cover locktime at all (i.e. have the spec fail to make a determination), or explicitly state how it should be treated. I would worry that optional features in a standard would otherwise lead to variable behavior that could cause compatibility issues later on.


    kallewoof commented at 7:04 AM on October 24, 2020:

    I think it may make sense to not make this optional now that we've split out into two types of proofs (simple proofs and PoF).

  95. in bip-0322.mediawiki:90 in d8b4049d34 outdated
     155 |  
     156 | -The legacy format is restricted to the legacy P2PKH address format.
     157 | +The legacy format is restricted to the legacy P2PKH invoice address format.
     158 |  
     159 | -Any other input (i.e. non-P2PKH address format) must be signed using the new format described above.
     160 | +Any other input (i.e. non-P2PKH invoice address format) must be signed using the new format described above.
    


    murchandamus commented at 6:17 PM on October 16, 2020:

    Perhaps it should be recommended here to prefer the general format even for P2PKH going forth.


    kallewoof commented at 7:08 AM on October 24, 2020:

    This was discussed in an earlier draft of this proposal, and then people were opposing the conclusion (that was to use legacy for P2PK(H)). I'm going to push for using the new format and see what people think, so:

    New proofs should use the new format for all invoice address formats, including P2PKH.

    The legacy format MAY be used, but must be restricted to the legacy P2PKH invoice address format.

  96. BIP-322: switch to tx based approach
    Co-authored-by: Stepan Snigirev <stepan.snigirev@mpq.mpg.de>
    Co-authored-by: Luke Dashjr <luke_github1@dashjr.org>
    75ec9631ef
  97. kallewoof force-pushed on Oct 24, 2020
  98. kallewoof commented at 7:11 AM on October 24, 2020: member

    @luke-jr There may be more changes, but this PR has grown pretty large, and people are still looking at the outdated version, so I suggest we merge this.

  99. luke-jr merged this on Oct 24, 2020
  100. luke-jr closed this on Oct 24, 2020

  101. kallewoof deleted the branch on Oct 27, 2020

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bips. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-27 08:10 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me