consensus: Consistently encode and decode OP_1NEGATE similar to other small ints in Script

This commit is contained in:
Chris Stewart 2024-03-07 10:39:20 -06:00
parent da10e0bab4
commit f2f87965ea
3 changed files with 19 additions and 14 deletions

View file

@ -518,14 +518,18 @@ public:
{ {
if (opcode == OP_0) if (opcode == OP_0)
return 0; return 0;
if (opcode == OP_1NEGATE)
return -1;
assert(opcode >= OP_1 && opcode <= OP_16); assert(opcode >= OP_1 && opcode <= OP_16);
return (int)opcode - (int)(OP_1 - 1); return (int)opcode - (int)(OP_1 - 1);
} }
static opcodetype EncodeOP_N(int n) static opcodetype EncodeOP_N(int n)
{ {
assert(n >= 0 && n <= 16); assert(n >= -1 && n <= 16);
if (n == 0) if (n == 0)
return OP_0; return OP_0;
if (n == -1)
return OP_1NEGATE;
return (opcodetype)(OP_1+n-1); return (opcodetype)(OP_1+n-1);
} }

View file

@ -55,10 +55,10 @@ static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
return false; return false;
} }
/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */ /** Test for "small positive integer" script opcodes - OP_0 through OP_16 and OP_1NEGATE. */
static constexpr bool IsSmallInteger(opcodetype opcode) static constexpr bool IsSmallInteger(opcodetype opcode)
{ {
return opcode >= OP_1 && opcode <= OP_16; return opcode == OP_0 || (opcode >= OP_1 && opcode <= OP_16) || opcode == OP_1NEGATE;
} }
/** Retrieve a minimally-encoded number in range [min,max] from an (opcode, data) pair, /** Retrieve a minimally-encoded number in range [min,max] from an (opcode, data) pair,

View file

@ -66,27 +66,30 @@ class CScriptOp(int):
@staticmethod @staticmethod
def encode_op_n(n): def encode_op_n(n):
"""Encode a small integer op, returning an opcode""" """Encode a small integer op, returning an opcode"""
if not (0 <= n <= 16): if not (-1 <= n <= 16):
raise ValueError('Integer must be in range 0 <= n <= 16, got %d' % n) raise ValueError('Integer must be in range -1 <= n <= 16, got %d' % n)
if n == 0: if n == 0:
return OP_0 return OP_0
elif n == -1:
return OP_1NEGATE
else: else:
return CScriptOp(OP_1 + n - 1) return CScriptOp(OP_1 + n - 1)
def decode_op_n(self): def decode_op_n(self):
"""Decode a small integer opcode, returning an integer""" """Decode a small integer opcode, returning an integer"""
if not self.is_small_int():
raise ValueError('op %r is not an OP_N' % self)
if self == OP_0: if self == OP_0:
return 0 return 0
elif self == OP_1NEGATE:
if not (self == OP_0 or OP_1 <= self <= OP_16): return -1
raise ValueError('op %r is not an OP_N' % self) else:
return int(self - OP_1 + 1)
return int(self - OP_1 + 1)
def is_small_int(self): def is_small_int(self):
"""Return true if the op pushes a small integer to the stack""" """Return true if the op pushes a small integer to the stack"""
if 0x51 <= self <= 0x60 or self == 0: if self == OP_0 or OP_1 <= self <= OP_16 or self == OP_1NEGATE:
return True return True
else: else:
return False return False
@ -443,10 +446,8 @@ class CScript(bytes):
else: else:
other = CScriptNum.encode(other) other = CScriptNum.encode(other)
elif isinstance(other, int): elif isinstance(other, int):
if 0 <= other <= 16: if -1 <= other <= 16:
other = bytes([CScriptOp.encode_op_n(other)]) other = bytes([CScriptOp.encode_op_n(other)])
elif other == -1:
other = bytes([OP_1NEGATE])
else: else:
other = CScriptOp.encode_op_pushdata(bn2vch(other)) other = CScriptOp.encode_op_pushdata(bn2vch(other))
elif isinstance(other, (bytes, bytearray)): elif isinstance(other, (bytes, bytearray)):