Implement arithmetic opcodes

This commit is contained in:
sanket1729 2021-08-25 20:48:20 +00:00
parent 6092d94c1d
commit 4b9032209a
5 changed files with 208 additions and 2 deletions

View file

@ -15,6 +15,10 @@
typedef std::vector<unsigned char> valtype;
// These asserts are consensus critical for elements tapscript arithmetic opcodes
static_assert(static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) == UINT64_C(0x7FFFFFFFFFFFFFFF));
static_assert(static_cast<uint64_t>(std::numeric_limits<int64_t>::min()) == UINT64_C(0x8000000000000000));
namespace {
inline bool set_success(ScriptError* ret)
@ -61,6 +65,19 @@ static inline void popstack(std::vector<valtype>& stack)
stack.pop_back();
}
static inline int64_t cast_signed64(uint64_t v)
{
uint64_t int64_min = static_cast<uint64_t>(std::numeric_limits<int64_t>::min());
if (v >= int64_min)
return static_cast<int64_t>(v - int64_min) + std::numeric_limits<int64_t>::min();
return static_cast<int64_t>(v);
}
static inline int64_t read_le8_signed(const unsigned char* ptr)
{
return cast_signed64(ReadLE64(ptr));
}
static inline void push4_le(std::vector<valtype>& stack, uint32_t v)
{
uint32_t v_le = htole32(v);
@ -1948,6 +1965,160 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript&
}
break;
case OP_ADD64:
case OP_SUB64:
case OP_MUL64:
case OP_DIV64:
case OP_LESSTHAN64:
case OP_LESSTHANOREQUAL64:
case OP_GREATERTHAN64:
case OP_GREATERTHANOREQUAL64:
{
// Opcodes only available post tapscript
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
if (stack.size() < 2)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vcha = stacktop(-2);
valtype& vchb = stacktop(-1);
if (vchb.size() != 8 || vcha.size() != 8)
return set_error(serror, SCRIPT_ERR_EXPECTED_8BYTES);
int64_t b = read_le8_signed(vchb.data());
int64_t a = read_le8_signed(vcha.data());
switch(opcode)
{
case OP_ADD64:
if ((a > 0 && b > std::numeric_limits<int64_t>::max() - a) ||
(a < 0 && b < std::numeric_limits<int64_t>::min() - a))
stack.push_back(vchFalse);
else {
popstack(stack);
popstack(stack);
push8_le(stack, a + b);
stack.push_back(vchTrue);
}
break;
case OP_SUB64:
if ((b > 0 && a < std::numeric_limits<int64_t>::min() + b) ||
(b < 0 && a > std::numeric_limits<int64_t>::max() + b))
stack.push_back(vchFalse);
else {
popstack(stack);
popstack(stack);
push8_le(stack, a - b);
stack.push_back(vchTrue);
}
break;
case OP_MUL64:
if ((a > 0 && b > 0 && a > std::numeric_limits<int64_t>::max() / b) ||
(a > 0 && b < 0 && b < std::numeric_limits<int64_t>::min() / a) ||
(a < 0 && b > 0 && a < std::numeric_limits<int64_t>::min() / b) ||
(a < 0 && b < 0 && b < std::numeric_limits<int64_t>::max() / a))
stack.push_back(vchFalse);
else {
popstack(stack);
popstack(stack);
push8_le(stack, a * b);
stack.push_back(vchTrue);
}
break;
case OP_DIV64:
{
if (b == 0 || (b == -1 && a == std::numeric_limits<int64_t>::min())) { stack.push_back(vchFalse); break; }
int64_t r = a % b;
int64_t q = a / b;
if (r < 0 && b > 0) { r += b; q-=1;} // ensures that 0<=r<|b|
else if (r < 0 && b < 0) { r -= b; q+=1;} // ensures that 0<=r<|b|
popstack(stack);
popstack(stack);
push8_le(stack, r);
push8_le(stack, q);
stack.push_back(vchTrue);
}
break;
break;
case OP_LESSTHAN64: popstack(stack); popstack(stack); stack.push_back( (a < b) ? vchTrue : vchFalse ); break;
case OP_LESSTHANOREQUAL64: popstack(stack); popstack(stack); stack.push_back( (a <= b) ? vchTrue : vchFalse ); break;
case OP_GREATERTHAN64: popstack(stack); popstack(stack); stack.push_back( (a > b) ? vchTrue : vchFalse ); break;
case OP_GREATERTHANOREQUAL64: popstack(stack); popstack(stack); stack.push_back( (a >= b) ? vchTrue : vchFalse ); break;
default: assert(!"invalid opcode"); break;
}
}
break;
case OP_NEG64:
{
// Opcodes only available post tapscript
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vcha = stacktop(-1);
if (vcha.size() != 8)
return set_error(serror, SCRIPT_ERR_EXPECTED_8BYTES);
int64_t a = read_le8_signed(vcha.data());
if (a == std::numeric_limits<int64_t>::min()) { stack.push_back(vchFalse); break; }
popstack(stack);
push8_le(stack, -a);
stack.push_back(vchTrue);
}
break;
case OP_SCRIPTNUMTOLE64:
{
// Opcodes only available post tapscript
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
int64_t num = CScriptNum(stacktop(-1), fRequireMinimal).getint();
popstack(stack);
push8_le(stack, num);
}
break;
case OP_LE64TOSCRIPTNUM:
{
// Opcodes only available post tapscript
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vchnum = stacktop(-1);
if (vchnum.size() != 8)
return set_error(serror, SCRIPT_ERR_EXPECTED_8BYTES);
valtype vchscript_num = CScriptNum(read_le8_signed(vchnum.data())).getvch();
if (vchscript_num.size() > CScriptNum::nDefaultMaxNumSize) {
return set_error(serror, SCRIPT_ERR_ARITHMETIC64);
} else {
popstack(stack);
stack.push_back(std::move(vchscript_num));
}
}
break;
case OP_LE32TOLE64:
{
// Opcodes only available post tapscript
if (sigversion == SigVersion::BASE || sigversion == SigVersion::WITNESS_V0) return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
if (stack.size() < 1)
return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
valtype& vchnum = stacktop(-1);
if (vchnum.size() != 4)
return set_error(serror, SCRIPT_ERR_ARITHMETIC64);
uint32_t num = ReadLE32(vchnum.data());
popstack(stack);
push8_le(stack, static_cast<int64_t>(num));
}
break;
default:
return set_error(serror, SCRIPT_ERR_BAD_OPCODE);
}