1

OP_PICK should take the nth element from the back of the stack and take it on top: for input xn ... x2 x1 x0
gives xn ... x2 x1 x0 xn

I have used it in some scripts, but I've noticed some strange behaviour.

For instance, using the tool in: http://webbtc.com/script an input script 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 and an output script 19 OP_PICK produces an error. However 18 OP_PICK works. For 25 elements in the stack it should work in both cases. The problem doesn't seem to be the number 19. If I add one input
1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 the scripts works fine.

I have tried looking at the source code, but in https://github.com/bitcoin/bitcoin/blob/ce56f5621a94dcc2159ebe57e43da727eab18e6c/src/script/interpreter.cpp around line 551 I find:

            case OP_OVER:
            {
                // (x1 x2 -- x1 x2 x1)
                if (stack.size() < 2)
                    return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
                valtype vch = stacktop(-2);
                stack.push_back(vch);
            }
            break;

            case OP_PICK:
            case OP_ROLL:
            {
                // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
                // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
                if (stack.size() < 2)
                    return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
                int n = CScriptNum(stacktop(-1), fRequireMinimal).getint();
                popstack(stack);

I'm not really sure how OP_PICK is implemented. Can anyone point me to the code for OP_PICK or explain the errors?

  • 2
    Is that 18 in decimal, or 18 in hex? – Nick ODell Jun 21 '15 at 0:37
  • @NickODell: I think you've got it. It appears that site inteprets "18" to be hex. – Nate Eldredge Jun 21 '15 at 2:50
  • It really seems to be taking numbers above 16 as hexadecimal (with OP_16 the highest integer you can directly push to the stack). Still trying to figure out the whole format of webbtc.com/script and the minimaldata verification to work around the problem. – halftimepad Jun 21 '15 at 11:36
1

Here's that source code, annotated. Each annotation refers to the code after it.


This means that this part of the code handles both OP_PICK and OP_ROLL. This is because OP_PICK and OP_ROLL are very similar. The only difference is that PICK copies and ROLL moves.

            case OP_PICK:
            case OP_ROLL:
            {

These are two examples of the stack before and after. This is sometimes called a 'precondition' and a 'postcondition.'

This one documents OP_PICK.

Precondition: xn ... x2 x1 x0 n

Postcondition: xn ... x2 x1 x0 xn

                // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)

This one documents OP_ROLL.

Precondition: xn ... x2 x1 x0 n

Postcondition: ... x2 x1 x0 xn (Note the lack of xn at the beginning.

                // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)

You need to have at least two elements on the stack, or this operation makes no sense.

                if (stack.size() < 2)
                    return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

Copy (but do not remove) the top element on the stack. Interpret it as a number. Convert it to a 32-bit integer, so it's easier to work with.

                int n = CScriptNum(stacktop(-1), fRequireMinimal).getint();

Now remove the top element on the stack. (Which should be n, remember.)

                popstack(stack);

If n is negative, fail. If n is larger than the stack, fail. (This is where I think your script is having an error.)

                if (n < 0 || n >= (int)stack.size())
                    return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);

Copy the value we're after. Remember, 1 2 3 0 OP_PICK should get the element just below n, 3. Also, stacktop(-1) refers to the top. So therefore, we need to invert n and subtract 1.

                valtype vch = stacktop(-n-1);

Remember, OP_ROLL deletes the element from where it was originally. This code is not executed for OP_PICK.

                if (opcode == OP_ROLL)
                    stack.erase(stack.end()-n-1);

Now add the element that we just got back on the top of the stack.

                stack.push_back(vch);

We're done handling this case.

            }
            break;

Does that help?

  • It really does. I thought either it did nothing or used exactly the same code as OP_ROLL. The commented code around if (opcode == OP_ROLL) has been the key for me. – halftimepad Jun 21 '15 at 11:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.