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

make opcode internals public #5

Closed
tals opened this issue Feb 16, 2014 · 7 comments
Closed

make opcode internals public #5

tals opened this issue Feb 16, 2014 · 7 comments

Comments

@tals
Copy link

tals commented Feb 16, 2014

Hey there,

I'd like to construct some custom scripts (functionality it's similar to PayToScriptHashScript(), but the payload is different).
Sadly, opcodemap and unparseScript are private.

Any chance of making those public?

Thanks

@davecgh
Copy link
Member

davecgh commented Feb 16, 2014

@tals

The opcodes themselves are exported, so you can build custom scripts byte wise. I do agree that some type of "ScriptBuilder" interface might be a good addition for creating custom scripts, but I'm not really sure that simply exposing opcodemap/unparseScript is the best approach for that. It would most likely be the simplest approach however.

@owainga: What are your thoughts on this one?

In the mean time, while it's not the most user friendly thing, you can build a script by doing something similar to the following:

package main

import(
    "fmt"
    "github.com/conformal/btcscript"
    "github.com/conformal/btcutil"
)

func main() {
    // This would obviously be a real script like a multi-sig or similar.
    redeemScript := []byte{}
    scriptHash := btcutil.Hash160(redeemScript)

    // Construct the pay-to-script-hash script.
    script := make([]byte, 23)
    script[0] = btcscript.OP_HASH160
    script[1] = btcscript.OP_DATA_20
    copy(script[2:], scriptHash)
    script[22] = btcscript.OP_EQUAL

    // Ignoring the error here for demo purposes.
    disasm, _ := btcscript.DisasmString(script)
    fmt.Printf("Script: %x\n", script)
    fmt.Printf("Script disassembly: %s\n", disasm)
}

Which would produce the output:

Script: a914b472a266d0bd89c13706a4132ccfb16f7c3b9fcb87
Script disassembly: OP_HASH160 b472a266d0bd89c13706a4132ccfb16f7c3b9fcb OP_EQUAL

@tals
Copy link
Author

tals commented Feb 16, 2014

@davecgh: Yes, the current interface is probably not the most interface for writing Script either :)
I guess I still have some coding anxiety from being new to Go :)

btw, since we're building the script bytes by hand, shouldn't the OP_DATA_20 part be:

script[0] = btcscript.OP_HASH160
script[1] = btcscript.OP_PUSHDATA1
script[2] = 20
copy(script[3:], scriptHash)
...

?

@tals tals closed this as completed Feb 16, 2014
@davecgh
Copy link
Member

davecgh commented Feb 16, 2014

@tals:

No, OP_DATA_20 is the better choice. It is a single byte versus 2 bytes for the [OP_PUSHDATA1 20] pair. Also, I highly suspect that ultimately the standard transaction rules will be changed to only allow scripts which are canonical in regards to pushing data, meaning they only consume the least number of bytes possible to represent the data push. There has been some discussion amongst the community about it, and I, for one, believe it is something that really should happen.

@davecgh
Copy link
Member

davecgh commented Feb 17, 2014

@tals:

What would you think of an interface like the following?

builder := btcscript.NewScriptBuilder()
builder.PushOp(btcscript.OP_HASH160).PushData(scriptHash).PushOp(btcscript.OP_EQUAL)
fmt.Printf("Script: %x\n", builder.Script())

@tals
Copy link
Author

tals commented Feb 17, 2014

Aaah, I didn't notice this fragment in scripts.h:

            if (opcode < OP_PUSHDATA1)
            {
                nSize = opcode;
            }

and GetOpName() doesn't know about (neither does the wiki), so I thought that the OP_DATA_XX was just something internal to btcscript :p

About the interface: I actually also planned on doing a "builder" style interface, so it looks awesome to me :)
What about opcode that require args? PushOp(btcscript.OP_HASH160, 123456)?
It might be a little nicer to have the opcode as funcs, so:

 ..Hash160().Data(scriptHash).Equal().

but either way is fine tbh.

davecgh added a commit that referenced this issue Feb 20, 2014
This commit adds a new ScriptBuilder interface that can be used to build
custom scripts.  It currently is fairly basic, but it allows you to push
raw opcodes, ints, and data while respecting canonical encoding.  These
primitives are sufficient to build any script.

This could be improved upon with quite a few things. One example would be
functions for certain opcodes that take properly typed parameters to make
it harder to create invalid scripts.

For now though, it is already quite useful since it handles all of the
opcode selection for canonical data pushes and integer encoding.

The initial discussion took place in #5.
@davecgh
Copy link
Member

davecgh commented Feb 20, 2014

@tals:

I've pushed the initial ScriptBuilder to master.

@tals
Copy link
Author

tals commented Feb 20, 2014

Oh thanks! That's pretty handy!

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

No branches or pull requests

2 participants