fiatjaf / scoin

simple bitcoin helpers for scala
Apache License 2.0
5 stars 1 forks source link

Taproot mvt minimum viable taproot #11

Closed VzxPLnHqr closed 1 year ago

VzxPLnHqr commented 1 year ago

Please do not merge until I get these tests passing. Opening this PR now though so we have a place to discuss the code as I will probably get stuck soon.

VzxPLnHqr commented 1 year ago

Ok, bare minimum tests pass. Can now send to and spend from taproot addresses. Key path only for now.

VzxPLnHqr commented 1 year ago

After shamelessly translating https://github.com/ACINQ/bitcoin-kmp/pull/40/commits/490bc0b4ac8f3850c09cb3d45a3d576b4960cb44#diff-435ce1291e658c59f775198e6107e93ae7803597ffeceede7066b51543e22cf3 over to Scala, we can now create and spend from tapscript transactions (keypath as well as script path).

VzxPLnHqr commented 1 year ago

Putting some notes here for us for when we get around to better documenting the taproot features of scoin as they currently exist in this pull request:

There are two schnorr signing methods Crypto.signSchnorr(...) and Crypto.signSchnorrWithTweak:

  def signSchnorr(
      data: ByteVector32,
      privateKey: PrivateKey,
      auxrand32: Option[ByteVector32] = None
  ): ByteVector64

  def signSchnorrWithTweak(
      data: ByteVector32, 
      privateKey: PrivateKey, 
      merkleRoot: Option[ByteVector32], 
      auxrand32: Option[ByteVector32] = None
  ): ByteVector64

signSchnorrWithTweak is used when spending with the "taproot" path -- before signing, the private key is "tweaked" by the provided merkleRoot. As such, in order to sign, the signer must still know the merkle root of the tapscript tree.

signSchnorr is used in all the other circumstances (at least so far in this codebase).

VzxPLnHqr commented 1 year ago

I was having a hard time figuring out why a bunch of the tests were failing in scoinNative but passing in scoinJS and scoinJVM. Probably something to do with the wiring of how the c-library libsecp256k1 is used by the fiatjaf/sn-secp256k1 project, which in turn is used here by scoin.

Not all the wiring is messed up, because some calls seem to work fine, such as PublicKey#toUncompressedBin, and probably many others. But point addition was not seeming to give me the correct results. I think things were maybe getting tweaked twice or something.

So, in order to make progress, I ended up just naively implementing the ecc operations we need using BigInt and modular arithmetic. I put it in a package scoin.inefficient, but maybe it should be called scoin.reckless?

commit 8478e77 adds a platform-independent and very untested implementation of the ecc math operations (point addition, point doubling, and multiplying a point by a scalar) ... reckless? yes, but it did pass all the existing tests at least.

Right now it is only used in the scoinNative project. Signing (both schnorr and ecdsa) and anything pertaining to random number generation was not changed.

When we get the other wiring figured out we can just delete it if it is not useful. Or maybe we leave it and people will find it helpful for learning the ecc math.