mlabs-haskell / TuxedoDapp

Cryptokitties on Polkadot using UTXO
Apache License 2.0
2 stars 1 forks source link

Create a temporary workaround in the back-end service to sign redeemer #48

Closed philoniare closed 2 months ago

JoshOrndorff commented 2 months ago

Do you guys need any help figuring out what is going wrong here? I'm open to helping. FYI, you need to sign the entire transaction and insert that signature into the redeemer. Not just sign the redeemer itself.

Winni- commented 2 months ago

Hello @JoshOrndorff , tell us what the redeemer consists of. We have the encoded transaction and the sign function from https://github.com/polkadot-js/wasm/blob/master/packages/wasm-crypto/src/rs/sr25519.rs is it the same signing algoritm that is used in Tuxedo? How can we leverage them to create redeemer?

JoshOrndorff commented 2 months ago

You are working against the Tuxedo template runtime. It's Verifier type is this one: https://off-narrative-labs.github.io/Tuxedo/tuxedo_template_runtime/enum.OuterVerifier.html You can see it is an enum with three variants. I think you are already focusing on the first variant which is an sr25519 signature. This is good. Continue to focus on this one. For this variant **The redeemer is a plain array of bytes that is exactly the signature.

I believe you guys are working with a Tuxedo version around 36d7baca4ae so I will share links to that commit as well. If you are using a different version, please let me know. A few bits have changed in this logic recently, but the big picture idea is the same.

The exact logic used by the blockchain to check the signatures is this https://github.com/Off-Narrative-Labs/Tuxedo/blob/36d7baca4ae159d77ea545d7cb999bd1e715557d/tuxedo-core/src/verifier.rs#L40-L49 I recommend you add a log line here to print out the bytes received. Then you can check if the Rust code produces the same bytes as the TS.

The signing algo you linked appears to be the correct one. Specifically this appears to be the signing function. https://github.com/polkadot-js/wasm/blob/master/packages/wasm-crypto/src/rs/sr25519.rs#L124

To debug, compare bytes each step of the way. Make sure you derived the same public and private keys between rust and js. Make sure you pass in the same exact message. Make sure you get out the same signature.

This reminds me about the message that needs to be signed. You should sign the entire transaction. But of course the transaction contains the signature itself. To get around this, you must first construct the transaction with everything except the redeemer. It is left blank. Then sign this entire transaction, then insert the signature into the proper place in the transaction. Here is a video of me explaining this at PBA Hong Kong https://youtu.be/cI75Je1Nvk8?si=YTFA20Fd7Ug4i5Tv&t=802 (the video starts at the exact place.)

Please let me know if I can help more, or if we should meet at some point. I really want you guys to have all the support you need.

philoniare commented 2 months ago

@JoshOrndorff Yep, thanks. We were able to isolate the issue by following these steps:

Given the above assumptions and the fact that sr25519 signature is deterministic, which means that signing the same bytes with the same private key should result in the same signature, the Talisman signRaw method is producing a different signature for some reason. So, we're stuck on it at the moment.

JoshOrndorff commented 2 months ago

the fact that sr25519 signature is deterministic, which means that signing the same bytes with the same private key should result in the same signature

I don't think this is true. https://polkadot.js.org/docs/keyring/start/sign-verify/#using-known-pairs says they are nondeterministic. (And I believe I've observed nondeterminism myself, but I don't remember clearly.)

We had some differences in the encoding, but I've...

Good that you are controlling for that :+1:

JoshOrndorff commented 2 months ago

Another thought. Remember that in scale, vectors are encoded by first encoding their compact length, then encoding the elements. The redeemer here is a &[u8], and TBH, I'm not sure how that is supposed to be encoded in scale (should it have a length prefix or not)? And I don't see it addressed in these reference docs https://docs.substrate.io/reference/scale-codec/

Inspect the working redeemer to see if it is length prefixed. Is it the same length as a plain signature? If so it is not prefixed. If it is longer, are the first few bytes the compact encoded length of the rest of the actual signature? (If the answer to both of those is no then idk wtf.)

Once you've determined whether it should be length prefixed, check whether the one from TS is also length prefixed.

philoniare commented 2 months ago

I don't think this is true. https://polkadot.js.org/docs/keyring/start/sign-verify/#using-known-pairs says they are nondeterministic. (And I believe I've observed nondeterminism myself, but I don't remember clearly.)

Got it, still the verifies doesn't pass for some reason.

Thanks for the tip, we'll be looking into it.

philoniare commented 2 months ago

Completed implementation with back-end webservice signing