Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP] OP_WITHDRAWPROOFVERIFY implementation #5

Open
wants to merge 32 commits into
base: master
Choose a base branch
from

Conversation

Christewart
Copy link

This pull request implements the op code OP_WITHDRAWPROOFVERIFY found in the elements project.

The purpose of OP_WITHDRAWPROOFVERIFY is to support sidechains.

To see a detailed description of OP_WPV functionality please read this blog post:

https://medium.com/@Chris_Stewart_5/op-withdrawproofverify-the-op-code-that-powers-spv-sidechains-cefce996a324#.y2dcegslq

This pull request has a generator inside of it to test all cases that should be successful for OP_WPV. We test that generator produces only valid OP_WPV withdrawls here.
TODO:

Modifying generators to be more flexible
Fixing bug in generator for merkle blocks with specific txs
Adding more flexibility to block header generators
… coins in the OP_WPV output when a user is not withdrawing all of them
Fixing bug in calculating pushops for ScriptNumberOperations
…iptNumberOperations -- for instance pushing an 83 byte transaction (whose op code is OP_3) -- whose script looks like OP_PUSHDATA1 OP_3 <83 byte tx> would fail before this
Fixing bug in pushing constants onto the stack whose op codes are Scr…
…pt_num_op

Fixing bug in calculating pushops for ScriptNumberOperations
(sidechainCreditingTx,outputIndex) = TransactionGenerators.buildSidechainCreditingTx(genesisBlockHash, reserveAmount)
sidechainCreditingOutput = sidechainCreditingTx.outputs(outputIndex.toInt)

contract = BitcoinSUtil.decodeHex("5032504800000000000000000000000000000000") ++ userSidechainAddr.bytes
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to use generator here

@@ -71,6 +71,21 @@ trait ScriptGenerators extends BitcoinSLogger {
} yield CSVScriptSignature(csv, sigs, pubKeys)


/** Generator for a contract used within a [[WithdrawScriptSignature]] */
def contract: Gen[Seq[Byte]] = for {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably need to create a type for 'contract' and create a generator that takes in a Sha256Hash160Digest

* @param program
* @return
*/
def opWithdrawProofVerify(program : ScriptProgram) : ScriptProgram = {
// comment from bitcoin core
require(program.script.headOption == Some(OP_WITHDRAWPROOFVERIFY), "Script operation is required to be OP_WITHDRAWPROOFVERIFY")
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to implement the flag 'ScriptVerifyMinimalData`, this is implemented in elements but I haven't implemented yet in bitcoin-s-sidechains

/** Checks the given [[MerkleBlock]] to see if we have enough proof of work
* [[https://github.com/ElementsProject/elements/blob/edaaa8b0f92653d9f770e671c7493f4bab4b48c7/src/pow.cpp#L40]]
* */
private def checkBitcoinProofOfWork(merkleBlock: MerkleBlock): Boolean = {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implement this

@@ -77,9 +77,11 @@ case object OP_CHECKMULTISIGVERIFY extends CryptoSignatureEvaluation {

case object OP_WITHDRAWPROOFVERIFY extends CryptoOperation {
override def opCode = 179
override def toString = "OP_NOP4"
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add comment to why this is is set to OP_NOP4 and not OP_WPV, same with OP_NOP5

@@ -499,7 +528,21 @@ trait ScriptInterpreter extends CryptoInterpreter with StackInterpreter with Con
* Return true if witness was NOT used, return false if witness was used. */
private def hasUnexpectedWitness(program: ScriptProgram): Boolean = {
val txSigComponent = program.txSignatureComponent
logger.debug("TxSigComponent: " + txSigComponent)
/** Helper function to check if the witness was used */
def witnessUsed(w: WitnessV0TransactionSignatureComponent) : Boolean = {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider moving this refactor upstream into bitcoin-s-core

(scriptSig,scriptPubKey,fedPegScript,reserveAmount) <- ScriptGenerators.withdrawlScript
(sidechainCreditingTx,outputIndex) = buildSidechainCreditingTx(genesisBlockHash, reserveAmount)
sidechainCreditingOutput = sidechainCreditingTx.outputs(outputIndex.toInt)
sidechainUserOutput = TransactionOutput(scriptSig.withdrawlAmount,P2PKHScriptPubKey(scriptSig.userHash))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not always a P2PKHScriptPubKey, can be P2SHScriptPubKey depending on what the contract specifies.

//TODO: Investigate this further, can we technically have negative output indexes as script numbers can be negative?
UInt32(num.toLong)
case constant: ScriptConstant =>
logger.error("Asm: " + asm)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove this logger.error

require(program.txSignatureComponent.isInstanceOf[FedPegTransactionSignatureComponent])
val fPegTxSigComponent = program.txSignatureComponent.asInstanceOf[FedPegTransactionSignatureComponent]

val relockScript: ScriptPubKey = ScriptPubKey(Seq(BytesToPushOntoStack(32),genesisHashToken, OP_WITHDRAWPROOFVERIFY))
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call WithdrawScriptPubKey constracut here instead of constructing the asm ourselves.

val partialMerkleTree = merkleBlock.partialMerkleTree
val matchedTxs: Seq[DoubleSha256Digest] = partialMerkleTree.extractMatches

if (partialMerkleTree.tree.value != Some(merkleBlock.blockHeader.merkleRootHash) || matchedTxs.length != 1) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of != I think scala recommends using .contains()

return ScriptProgram(program, ScriptErrorWithdrawVerifyOutputScriptDest)
}

val contractWithoutNonce = contract.take(4) ++ contract.slice(20,contract.length)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After creating a type for 'contract` create a method to access the user's hash

val (sidechainCreditingTx,outputIndex) = buildSidechainCreditingTx(genesisBlockHash)
val sidechainCreditingOutput = sidechainCreditingTx.outputs(outputIndex)
val sidechainUserOutput = TransactionOutput(amount,P2PKHScriptPubKey(usersHash))
val relockScriptPubKey = ScriptPubKey.fromAsm(Seq(BytesToPushOntoStack(32),
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Call WithdrawScriptPubKey.fromAsm

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
1 participant