mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-15 12:51:00 +02:00
Implement arithmetic opcodes
This commit is contained in:
parent
6092d94c1d
commit
4b9032209a
5 changed files with 208 additions and 2 deletions
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue