Skip to content

implement OP check multisig#1

Open
warp-records wants to merge 6 commits intoJonatanc05:masterfrom
warp-records:master
Open

implement OP check multisig#1
warp-records wants to merge 6 commits intoJonatanc05:masterfrom
warp-records:master

Conversation

@warp-records
Copy link

This is my first time writing Zig, please lmk if I need to change anything. I plan to work on other multisig stuff soon

@Jonatanc05
Copy link
Owner

Hey! Great to see some interest on the project, I'll try to take the time to review this soon. Reach me on Discord (or propose other channel) if you want to talk

Discord UserID: fr0dg3r

Copy link
Owner

@Jonatanc05 Jonatanc05 left a comment

Choose a reason for hiding this comment

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

Nice job with the implementation. Some changes pointed.

Also, could you write a test for this opcode? You could use example values in tools like https://learnmeabitcoin.com/technical/script/p2ms/

You can make a test as simple as the existing test "script: Basic script execution"

src/bitcoin.zig Outdated
}

const num_sigs: i64 = stack.popInt() catch return error.BadScript;
if (num_sigs > MAX_SIGNATURES) { return error.BadScript; }
Copy link
Owner

Choose a reason for hiding this comment

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

We should also prevent num_sigs from exceeding num_keys

src/bitcoin.zig Outdated
// see https://github.com/bitcoin/bips/blob/master/bip-0147.mediawiki
const dummy_buf: [1]u8 = undefined;
stack.pop(dummy_buf) catch |err| return Local.handlePopError(err);
if (dummy_buf[0] != Op.OP_0) return error.BadScript;
Copy link
Owner

Choose a reason for hiding this comment

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

We should actually assign the returned value of stack.pop
The way I wrote stack.pop is to use the returned slice, which is subset of the buffer sent. In this case, though, we would be reading garbage on dummy_buf[0]. What we actually want is check that slice returned by pop has len == 0

  • It's a shame that we don't get to read OP_0 explicitly, but the comment makes clear what's happening.
  • It's also a shame that the compiler doesn't force us to assign the return value of stack.pop... that's not what I expected (in the general case, it's a compile error to implicitly ignore a return value, maybe catching errors changes that for some reason)

src/bitcoin.zig Outdated
Comment on lines +833 to +839
for (pubkeys) |pubkey| {
const signature = signatures[num_valid_sigs];
if (try Tx.checksig(transaction.?, input_index.?, pubkey, signature, script, alloc)) {
num_valid_sigs += 1;
}
if (num_valid_sigs == num_sigs) { break; }
}
Copy link
Owner

Choose a reason for hiding this comment

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

I am a little tired to think about wether it makes a difference or not... but we should scan all pubkeys for each signature instead of scanning all signatures for each pubkey. That might have performance impact, not sure if we would get different output.

Also we could exit early when the number of remaining pubkeys is less the number of remaning signatures to be checked. In that case, we know we'll fail. See bitcoin\src\script\interpreter.cpp

Copy link
Author

Choose a reason for hiding this comment

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

The variable name is a bit misleading but num_valid_sigs is actually used as an index into which signature we're checking. Keys are guaranteed to be ordered, and they're not checked repeatedly for each signature.

I changed it to return early when there's not enough keys left

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

Successfully merging this pull request may close these issues.

3 participants