diff --git a/include/kth/domain/chain/script.hpp b/include/kth/domain/chain/script.hpp
index f7a1f20c1..a774b8f35 100644
--- a/include/kth/domain/chain/script.hpp
+++ b/include/kth/domain/chain/script.hpp
@@ -100,7 +100,7 @@ class KD_API script : public script_basis {
     //-------------------------------------------------------------------------
 
     static
-    hash_digest generate_signature_hash(transaction const& tx,
+    std::pair<hash_digest, size_t> generate_signature_hash(transaction const& tx,
                                         uint32_t input_index,
                                         script const& script_code,
                                         uint8_t sighash_type,
@@ -108,7 +108,7 @@ class KD_API script : public script_basis {
                                         uint64_t value = max_uint64);
 
     static
-    bool check_signature(ec_signature const& signature,
+    std::pair<bool, size_t> check_signature(ec_signature const& signature,
                             uint8_t sighash_type,
                             data_chunk const& public_key,
                             script const& script_code,
@@ -231,6 +231,16 @@ class KD_API script : public script_basis {
 
     bool is_pay_to_script_hash(uint32_t forks) const;
 
+    // Validation.
+    //-----------------------------------------------------------------------------
+
+    //TODO: move to script_basis (?)
+    static
+    code verify(transaction const& tx, uint32_t input_index, uint32_t forks, script const& input_script, script const& prevout_script, uint64_t /*value*/);
+
+    static
+    code verify(transaction const& tx, uint32_t input, uint32_t forks);
+
 private:
     static
     size_t serialized_size(operation::list const& ops);
@@ -239,10 +249,10 @@ class KD_API script : public script_basis {
     data_chunk operations_to_data(operation::list const& ops);
 
     static
-    hash_digest generate_unversioned_signature_hash(transaction const& tx, uint32_t input_index, script const& script_code, uint8_t sighash_type);
+    std::pair<hash_digest, size_t> generate_unversioned_signature_hash(transaction const& tx, uint32_t input_index, script const& script_code, uint8_t sighash_type);
 
     static
-    hash_digest generate_version_0_signature_hash(transaction const& tx,
+    std::pair<hash_digest, size_t> generate_version_0_signature_hash(transaction const& tx,
                                                   uint32_t input_index,
                                                   script const& script_code,
                                                   uint64_t value,
diff --git a/include/kth/domain/chain/script_basis.hpp b/include/kth/domain/chain/script_basis.hpp
index 68e0e8334..7c5d3edc2 100644
--- a/include/kth/domain/chain/script_basis.hpp
+++ b/include/kth/domain/chain/script_basis.hpp
@@ -284,7 +284,7 @@ class KD_API script_basis {
     // hash_digest generate_unversioned_signature_hash(transaction const& tx, uint32_t input_index, script_basis const& script_code, uint8_t sighash_type);
 
     static
-    hash_digest generate_version_0_signature_hash(transaction const& tx, uint32_t input_index, script_basis const& script_code, uint64_t value, uint8_t sighash_type);
+    std::pair<hash_digest, size_t> generate_version_0_signature_hash(transaction const& tx, uint32_t input_index, script_basis const& script_code, uint64_t value, uint8_t sighash_type);
 
     void find_and_delete_(data_chunk const& endorsement);
 
diff --git a/include/kth/domain/config/parser.hpp b/include/kth/domain/config/parser.hpp
index 246e3be01..3c3951c10 100644
--- a/include/kth/domain/config/parser.hpp
+++ b/include/kth/domain/config/parser.hpp
@@ -178,7 +178,7 @@ kth::infrastructure::config::checkpoint::list default_checkpoints(config::networ
         checkpoints.emplace_back("00000000e4627a1a0bf9aaae007af5cea32720fb54cf2ccf0aa20b02a18392ab", 16869);  //mediantime: 1605444236. New rules activated in this block.
 
     } else if (network == domain::config::network::chipnet) {
-        checkpoints.reserve(12);
+        checkpoints.reserve(17);
 
         checkpoints.emplace_back("000000001dd410c49a788668ce26751718cc797474d3152a5fc073dd44fd9f7b", 0);
 
@@ -208,6 +208,11 @@ kth::infrastructure::config::checkpoint::list default_checkpoints(config::networ
 
         // A block significantly after Upgrade 10 activated (which activated on Nov. 15, 2023)
         checkpoints.emplace_back("000000003c37cc0372a5b9ccacca921786bbfc699722fc41e9fdbb1de4146ef1", 178140);
+        checkpoints.emplace_back("00000000146a073b9d4e172adbee5252014a8b4d75c56cce36858311565ae251", 206364);
+
+        // A block after Upgrade 11 activated (Nov. 15, 2024), first block after upgrade: 227229
+        checkpoints.emplace_back("00000000144b00db5736b33bd572b3a3a52aa9b4c26ba59fc212aeb68a9b7a20", 228000);
+        checkpoints.emplace_back("0000000017d92f88ed2c81885c57f999184860a042250510be06b3edd12e0dc5", 232000);
 
     } else if (network == domain::config::network::mainnet) {
         checkpoints.reserve(60);
diff --git a/include/kth/domain/constants/common.hpp b/include/kth/domain/constants/common.hpp
index 12c953cd3..1fdc4c41a 100644
--- a/include/kth/domain/constants/common.hpp
+++ b/include/kth/domain/constants/common.hpp
@@ -35,12 +35,27 @@ constexpr size_t max_counted_ops = 201;
 constexpr size_t max_stack_size = 1000;
 constexpr size_t max_script_size = 10000;
 // constexpr size_t max_push_data_size = 520;
+constexpr size_t max_push_data_size_legacy = 520;
 constexpr size_t max_script_public_keys = 20;
 constexpr size_t multisig_default_sigops = 20;
-constexpr size_t max_number_size = 4;
+constexpr size_t max_number_size_32_bits = 4;
+constexpr size_t max_number_size_64_bits = 8;
 constexpr size_t max_check_locktime_verify_number_size = 5;
 constexpr size_t max_check_sequence_verify_number_size = 5;
 
+// The below constants are used after activation of the May 2025 upgrade (Targeted VM Limits CHIP)
+namespace may2025 {
+
+    // Maximum number of bytes pushable to the stack
+    constexpr size_t max_push_data_size = max_script_size;      // BCHN: MAX_SCRIPT_ELEMENT_SIZE
+    // Base cost for each executed opcode; no opcodes incur a cost less than this, but some may incur more.
+    constexpr size_t opcode_cost = 100u;                        // BCHN: OPCODE_COST
+    // Conditional stack depth limit (max depth of OP_IF and friends)
+    constexpr size_t max_conditional_stack_depth = 100u;        // BCHN: MAX_CONDITIONAL_STACK_DEPTH
+    // Each sigcheck done by an input adds this amount to the total op cost
+    constexpr uint64_t sig_check_cost_factor = 26'000u;         // BCHN: SIG_CHECK_COST_FACTOR
+}
+
 // Policy.
 constexpr size_t max_null_data_size = 80;
 
diff --git a/include/kth/domain/impl/machine/interpreter.ipp b/include/kth/domain/impl/machine/interpreter.ipp
index 8c6a450e6..8db14821f 100644
--- a/include/kth/domain/impl/machine/interpreter.ipp
+++ b/include/kth/domain/impl/machine/interpreter.ipp
@@ -21,10 +21,18 @@
 #include <kth/infrastructure/utility/assert.hpp>
 #include <kth/infrastructure/utility/data.hpp>
 
+
+// TODO: move to a concepts file
+template <typename Op>
+concept bitwise_op = requires(Op op, uint8_t a, uint8_t b) {
+    { op(a, b) } -> std::same_as<uint8_t>;
+};
+
+
 namespace kth::domain::machine {
 
 static constexpr
-auto op_75 = static_cast<uint8_t>(opcode::push_size_75);
+auto op_75 = uint8_t(opcode::push_size_75);
 
 // Operations (shared).
 //-----------------------------------------------------------------------------
@@ -47,6 +55,7 @@ interpreter::result interpreter::op_reserved(opcode /*unused*/) {
 inline
 interpreter::result interpreter::op_push_number(program& program, uint8_t value) {
     program.push_move({value});
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
@@ -57,9 +66,12 @@ interpreter::result interpreter::op_push_size(program& program, operation const&
     }
 
     program.push_copy(op.data());
+    // metrics.TallyPushOp(stack.back().size());
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
+// TODO: std::move the data chunk to the program
 inline
 interpreter::result interpreter::op_push_data(program& program, data_chunk const& data, uint32_t size_limit) {
     if (data.size() > size_limit) {
@@ -67,6 +79,7 @@ interpreter::result interpreter::op_push_data(program& program, data_chunk const
     }
 
     program.push_copy(data);
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
@@ -76,6 +89,7 @@ interpreter::result interpreter::op_push_data(program& program, data_chunk const
 
 inline
 interpreter::result interpreter::op_if(program& program) {
+    //TODO: SCRIPT_VERIFY_MINIMALIF
     auto value = false;
 
     if (program.succeeded()) {
@@ -93,6 +107,7 @@ interpreter::result interpreter::op_if(program& program) {
 
 inline
 interpreter::result interpreter::op_notif(program& program) {
+    //TODO: SCRIPT_VERIFY_MINIMALNOTIF
     auto value = false;
 
     if (program.succeeded()) {
@@ -154,6 +169,7 @@ interpreter::result interpreter::op_to_alt_stack(program& program) {
     }
 
     program.push_alternate(program.pop());
+    // Intentional: no tallying is done to get_metrics().add_op_cost()
     return error::success;
 }
 
@@ -164,6 +180,8 @@ interpreter::result interpreter::op_from_alt_stack(program& program) {
     }
 
     program.push_move(program.pop_alternate());
+    // metrics.TallyPushOp(stack.back().size());
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
@@ -188,7 +206,12 @@ interpreter::result interpreter::op_dup2(program& program) {
     auto item0 = program.item(0);
 
     program.push_move(std::move(item1));
+    // metrics.TallyPushOp(stack.back().size());
+    program.get_metrics().add_op_cost(program.top().size());
+
     program.push_move(std::move(item0));
+    // metrics.TallyPushOp(stack.back().size());
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
@@ -203,8 +226,11 @@ interpreter::result interpreter::op_dup3(program& program) {
     auto item0 = program.item(0);
 
     program.push_move(std::move(item2));
+    program.get_metrics().add_op_cost(program.top().size());
     program.push_move(std::move(item1));
+    program.get_metrics().add_op_cost(program.top().size());
     program.push_move(std::move(item0));
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
@@ -218,7 +244,9 @@ interpreter::result interpreter::op_over2(program& program) {
     auto item2 = program.item(2);
 
     program.push_move(std::move(item3));
+    program.get_metrics().add_op_cost(program.top().size());
     program.push_move(std::move(item2));
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
@@ -236,7 +264,9 @@ interpreter::result interpreter::op_rot2(program& program) {
 
     program.erase(position_5, position_4 + 1);
     program.push_move(std::move(copy_5));
+    program.get_metrics().add_op_cost(program.top().size());
     program.push_move(std::move(copy_4));
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
@@ -248,6 +278,7 @@ interpreter::result interpreter::op_swap2(program& program) {
 
     program.swap(3, 1);
     program.swap(2, 0);
+    // Intentional: no tallying is done to get_metrics().add_op_cost()
     return error::success;
 }
 
@@ -259,6 +290,7 @@ interpreter::result interpreter::op_if_dup(program& program) {
 
     if (program.stack_true(false)) {
         program.duplicate(0);
+        program.get_metrics().add_op_cost(program.top().size());
     }
 
     return error::success;
@@ -267,6 +299,7 @@ interpreter::result interpreter::op_if_dup(program& program) {
 inline
 interpreter::result interpreter::op_depth(program& program) {
     program.push_move(number(program.size()).data());
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
@@ -277,6 +310,7 @@ interpreter::result interpreter::op_drop(program& program) {
     }
 
     program.pop();
+    // No metrics
     return error::success;
 }
 
@@ -287,6 +321,7 @@ interpreter::result interpreter::op_dup(program& program) {
     }
 
     program.duplicate(0);
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
@@ -297,6 +332,7 @@ interpreter::result interpreter::op_nip(program& program) {
     }
 
     program.erase(program.position(1));
+    // No metrics
     return error::success;
 }
 
@@ -307,22 +343,26 @@ interpreter::result interpreter::op_over(program& program) {
     }
 
     program.duplicate(1);
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_pick(program& program) {
+    // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn)
     program::stack_iterator position;
     if ( ! program.pop_position(position)) {
         return error::op_pick;
     }
 
     program.push_copy(*position);
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_roll(program& program) {
+    // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn)
     program::stack_iterator position;
     if ( ! program.pop_position(position)) {
         return error::op_roll;
@@ -330,18 +370,25 @@ interpreter::result interpreter::op_roll(program& program) {
 
     auto copy = *position;
     program.erase(position);
+    // metrics.TallyOp(n); // erasing in the middle is linear with `n`
+    program.get_metrics().add_op_cost(program.index(position));
     program.push_move(std::move(copy));
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_rot(program& program) {
+    // (x1 x2 x3 -- x2 x3 x1)
+    //  x2 x1 x3  after first swap
+    //  x2 x3 x1  after second swap
     if (program.size() < 3) {
         return error::op_rot;
     }
 
     program.swap(2, 1);
     program.swap(1, 0);
+    // Intentional: no tallying is done to get_metrics().add_op_cost()
     return error::success;
 }
 
@@ -352,25 +399,257 @@ interpreter::result interpreter::op_swap(program& program) {
     }
 
     program.swap(1, 0);
+    // Intentional: no tallying is done to get_metrics().add_op_cost()
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_tuck(program& program) {
+    // (x1 x2 -- x2 x1 x2)
     if (program.size() < 2) {
         return error::op_tuck;
     }
 
     auto first = program.pop();
     auto second = program.pop();
+    program.get_metrics().add_op_cost(first.size());
     program.push_copy(first);
     program.push_move(std::move(second));
     program.push_move(std::move(first));
     return error::success;
 }
 
+
+// case OP_CAT: {
+//     // (x1 x2 -- out)
+//     if (stack.size() < 2) {
+//         return set_error(serror, ScriptError::INVALID_STACK_OPERATION);
+//     }
+//     valtype &vch1 = stacktop(-2);
+//     const valtype &vch2 = stacktop(-1);
+//     if (vch1.size() + vch2.size() > maxScriptElementSize) {
+//         return set_error(serror, ScriptError::PUSH_SIZE);
+//     }
+//     vch1.insert(vch1.end(), vch2.begin(), vch2.end());
+//     popstack(stack);
+//     metrics.TallyPushOp(stack.back().size());
+// } break;
+
+inline
+interpreter::result interpreter::op_cat(program& program) {
+    // (x1 x2 -- out)
+    if (program.size() < 2) {
+        return error::op_cat;
+    }
+
+    auto last = program.pop();
+    auto& but_last = program.top();
+
+    if (but_last.size() + last.size() > program.max_script_element_size()) {
+        return error::invalid_push_data_size;
+    }
+
+    but_last.insert(but_last.end(), last.begin(), last.end());
+    program.get_metrics().add_op_cost(but_last.size());
+
+    return error::success;
+}
+
+
+// case OP_SPLIT: {
+//     // (in position -- x1 x2)
+//     if (stack.size() < 2) {
+//         return set_error(serror, ScriptError::INVALID_STACK_OPERATION);
+//     }
+
+//     const valtype &data = stacktop(-2);
+
+//     // Make sure the split point is appropriate.
+//     int64_t const position = CScriptNum(stacktop(-1), fRequireMinimal, maxIntegerSizeLegacy).getint64();
+//     if (position < 0 || uint64_t(position) > data.size()) {
+//         return set_error(serror, ScriptError::INVALID_SPLIT_RANGE);
+//     }
+
+//     // Prepare the results in their own buffer as `data` will be invalidated.
+//     valtype n1(data.begin(), data.begin() + position);
+//     valtype n2(data.begin() + position, data.end());
+
+//     // Replace existing stack values by the new values.
+//     const size_t totalSize = n1.size() + n2.size();
+//     stacktop(-2) = std::move(n1);
+//     stacktop(-1) = std::move(n2);
+//     metrics.TallyPushOp(totalSize);
+// } break;
+
+
+inline
+interpreter::result interpreter::op_split(program& program) {
+    if (program.size() < 2) {
+        return error::op_split;
+    }
+
+    auto& pos = program.item(0);  // last item
+    auto& data = program.item(1); // but last item
+
+    number position;
+    if ( ! position.set_data(pos, program.max_integer_size_legacy())) {
+        return error::op_split;
+    }
+    auto const pos64 = position.int64();
+
+    if (pos64 < 0 || size_t(pos64) > data.size()) {
+        return error::op_split;
+    }
+
+    auto n1 = data_chunk(data.begin(), data.begin() + pos64);
+    auto n2 = data_chunk(data.begin() + pos64, data.end());
+    size_t const total_size = n1.size() + n2.size();
+
+    data = std::move(n1);
+    pos = std::move(n2);
+
+    program.get_metrics().add_op_cost(total_size);
+
+    return error::success;
+}
+
+inline
+interpreter::result interpreter::op_reverse_bytes(program& program) {
+    return error::op_reverse_bytes;
+}
+
+
+// //
+// // Conversion operations
+// //
+// case OP_NUM2BIN: {
+//     // (in size -- out)
+//     if (stack.size() < 2) {
+//         return set_error(serror, ScriptError::INVALID_STACK_OPERATION);
+//     }
+
+//     uint64_t const size = CScriptNum(stacktop(-1), fRequireMinimal, maxIntegerSizeLegacy).getint64();
+//     if (size > maxScriptElementSize) {
+//         return set_error(serror, ScriptError::PUSH_SIZE);
+//     }
+
+//     popstack(stack);
+//     valtype &rawnum = stacktop(-1);
+
+//     // Try to see if we can fit that number in the number of byte requested.
+//     ScriptNumType::MinimallyEncode(rawnum);
+//     if (rawnum.size() > size) {
+//         // We definitively cannot.
+//         return set_error(serror, ScriptError::IMPOSSIBLE_ENCODING);
+//     }
+
+//     // We already have an element of the right size, we don't need to do anything.
+//     if (rawnum.size() == size) {
+//         metrics.TallyPushOp(rawnum.size());
+//         break;
+//     }
+
+//     uint8_t signbit = 0x00;
+//     if (rawnum.size() > 0) {
+//         signbit = rawnum.back() & 0x80;
+//         rawnum[rawnum.size() - 1] &= 0x7f;
+//     }
+
+//     rawnum.reserve(size);
+//     while (rawnum.size() < size - 1) {
+//         rawnum.push_back(0x00);
+//     }
+
+//     rawnum.push_back(signbit);
+//     metrics.TallyPushOp(rawnum.size());
+// } break;
+
+inline
+interpreter::result interpreter::op_num2bin(program& program) {
+    if (program.size() < 2) {
+        return error::op_num2bin;
+    }
+
+    number size;
+    if ( ! program.top(size, program.max_integer_size_legacy())) {
+        return error::op_num2bin_invalid_size;
+    }
+    auto const size64 = size.int64();
+    if (size64 < 0 || size_t(size64) > program.max_script_element_size()) {
+        return error::op_num2bin_size_exceeded;
+    }
+
+    auto& rawnum = program.item(1); // but last item
+    number::minimally_encode(rawnum);
+
+    // Check if the number can be adjusted to the desired size.
+    if (rawnum.size() > size64) {
+        return error::op_num2bin_impossible_encoding;
+    }
+
+    // If the size is already correct, no more is needed.
+    if (rawnum.size() == size64) {
+        program.get_metrics().add_op_cost(rawnum.size());
+        return error::success;
+    }
+
+    // Adjust the size of `rawnum` by adding padding zeros.
+    uint8_t signbit = 0x00;
+    if ( ! rawnum.empty() && (rawnum.back() & 0x80)) {
+        signbit = 0x80;
+        rawnum.back() &= 0x7f;
+    }
+
+    rawnum.reserve(size64);
+    while (rawnum.size() < size64 - 1) {
+        rawnum.push_back(0x00);
+    }
+
+    rawnum.push_back(signbit);
+
+    program.get_metrics().add_op_cost(rawnum.size());
+    return error::success;
+}
+
+
+// case OP_BIN2NUM: {
+//     // (in -- out)
+//     if (stack.size() < 1) {
+//         return set_error(serror, ScriptError::INVALID_STACK_OPERATION);
+//     }
+
+//     valtype &n = stacktop(-1);
+//     ScriptNumType::MinimallyEncode(n);
+//     metrics.TallyPushOp(n.size());
+
+//     // The resulting number must be a valid number.
+//     // Note: IsMinimallyEncoded() here is really just checking if the number is in range.
+//     if ( ! ScriptNumType::IsMinimallyEncoded(n, maxIntegerSize)) {
+//         return set_error(serror, invalidNumberRangeError);
+//     }
+// } break;
+
+inline
+interpreter::result interpreter::op_bin2num(program& program) {
+    // (in -- out)
+    if (program.empty()) {
+        return error::op_bin2num;
+    }
+
+    auto& n = program.top();
+    number::minimally_encode(n);
+    program.get_metrics().add_op_cost(n.size());
+
+    if ( ! number::is_minimally_encoded(n, program.max_integer_size_legacy())) {
+        return error::op_bin2num_invalid_number_range;
+    }
+
+    return error::success;
+}
+
 inline
 interpreter::result interpreter::op_size(program& program) {
+    // (in -- in size)
     if (program.empty()) {
         return error::op_size;
     }
@@ -379,68 +658,145 @@ interpreter::result interpreter::op_size(program& program) {
     auto const size = top.size();
     program.push_move(std::move(top));
     program.push_move(number(size).data());
+    program.get_metrics().add_op_cost(size);
     return error::success;
 }
 
+// Disabled
+// inline
+// interpreter::result interpreter::op_invert(program& program) {
+// }
+
+
+template <bitwise_op Op>
+inline
+interpreter::result bitwise_operation_generic(program& program, error::error_code_t error, Op op) {
+    if (program.size() < 2) {
+        return error;
+    }
+
+    auto& vch1 = program.item(1);
+    auto& vch2 = program.item(0);
+
+    if (vch1.size() != vch2.size()) {
+        return error;
+    }
+
+    for (size_t i = 0; i < vch1.size(); ++i) {
+        vch1[i] = op(vch1[i], vch2[i]);
+    }
+
+    program.pop();
+    program.get_metrics().add_op_cost(vch1.size());
+
+    return error::success;
+}
+
+inline
+interpreter::result interpreter::op_and(program& program) {
+    return bitwise_operation_generic(program,
+        error::op_and,
+        [](uint8_t a, uint8_t b) { return uint8_t(a & b); });
+}
+
+inline
+interpreter::result interpreter::op_or(program& program) {
+    return bitwise_operation_generic(program,
+        error::op_or,
+        [](uint8_t a, uint8_t b) { return uint8_t(a | b); });
+}
+
+inline
+interpreter::result interpreter::op_xor(program& program) {
+    return bitwise_operation_generic(program,
+        error::op_xor,
+        [](uint8_t a, uint8_t b) { return uint8_t(a ^ b); });
+}
+
 inline
 interpreter::result interpreter::op_equal(program& program) {
+    // (x1 x2 - bool)
     if (program.size() < 2) {
         return error::op_equal;
     }
 
     program.push(program.pop() == program.pop());
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_equal_verify(program& program) {
+    // (x1 x2 - bool)
     if (program.size() < 2) {
         return error::op_equal_verify1;
     }
+    auto const res = program.pop() == program.pop(); //res is a bool
+    // program.get_metrics().add_op_cost(res.size());
 
-    return (program.pop() == program.pop()) ? error::success : error::op_equal_verify2;
+    // void program::push(bool value) {
+    //     push_move(value ? value_type{number::positive_1} : value_type{});
+    // }
+    program.get_metrics().add_op_cost(res ? 1 : 0);
+
+    return res ? error::success : error::op_equal_verify2;
 }
 
 inline
 interpreter::result interpreter::op_add1(program& program) {
+    constexpr auto push_cost_factor = 2;
     number number;
-    if ( ! program.pop(number)) {
+    if ( ! program.pop(number, program.max_integer_size_legacy())) {
         return error::op_add1;
     }
 
-    number += 1;
+    // number += 1;
+    bool const res = number.safe_add(1);
+    if ( ! res) {
+        return error::op_add_overflow;
+    }
+    program.get_metrics().add_op_cost(number.data().size() * push_cost_factor);
     program.push_move(number.data());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_sub1(program& program) {
+    constexpr auto push_cost_factor = 2;
     number number;
-    if ( ! program.pop(number)) {
+    if ( ! program.pop(number, program.max_integer_size_legacy())) {
         return error::op_sub1;
     }
 
-    number -= 1;
+    // number -= 1;
+    bool const res = number.safe_sub(1);
+    if ( ! res) {
+        return error::op_sub_underflow;
+    }
+    program.get_metrics().add_op_cost(number.data().size() * push_cost_factor);
     program.push_move(number.data());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_negate(program& program) {
+    constexpr auto push_cost_factor = 2;
     number number;
-    if ( ! program.pop(number)) {
+    if ( ! program.pop(number, program.max_integer_size_legacy())) {
         return error::op_negate;
     }
 
     number = -number;
+    program.get_metrics().add_op_cost(number.data().size() * push_cost_factor);
     program.push_move(number.data());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_abs(program& program) {
+    constexpr auto push_cost_factor = 2;
     number number;
-    if ( ! program.pop(number)) {
+    if ( ! program.pop(number, program.max_integer_size_legacy())) {
         return error::op_abs;
     }
 
@@ -448,6 +804,7 @@ interpreter::result interpreter::op_abs(program& program) {
         number = -number;
     }
 
+    program.get_metrics().add_op_cost(number.data().size() * push_cost_factor);
     program.push_move(number.data());
     return error::success;
 }
@@ -455,200 +812,310 @@ interpreter::result interpreter::op_abs(program& program) {
 inline
 interpreter::result interpreter::op_not(program& program) {
     number number;
-    if ( ! program.pop(number)) {
+    if ( ! program.pop(number, program.max_integer_size_legacy())) {
         return error::op_not;
     }
 
     program.push(number.is_false());
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_nonzero(program& program) {
     number number;
-    if ( ! program.pop(number)) {
+    if ( ! program.pop(number, program.max_integer_size_legacy())) {
         return error::op_nonzero;
     }
 
     program.push(number.is_true());
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_add(program& program) {
-    number first, second; //NOLINT
+    constexpr auto push_cost_factor = 2;
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_add;
     }
 
-    auto const result = first + second;
-    program.push_move(result.data());
+    // auto const result = first + second;
+    auto result = number::safe_add(first, second);
+    if ( ! result) {
+        return error::op_add_overflow;
+    }
+    program.get_metrics().add_op_cost(result->data().size() * push_cost_factor);
+    program.push_move(result->data());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_sub(program& program) {
-    number first, second; //NOLINT
+    constexpr auto push_cost_factor = 2;
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_sub;
     }
 
-    auto const result = second - first;
+    // auto const result = second - first;
+    auto result = number::safe_sub(second, first);
+    if ( ! result) {
+        return error::op_sub_underflow;
+    }
+    program.get_metrics().add_op_cost(result->data().size() * push_cost_factor);
+    program.push_move(result->data());
+    return error::success;
+}
+
+inline
+interpreter::result interpreter::op_mul(program& program) {
+    constexpr auto push_cost_factor = 2;
+    number first;
+    number second;
+    if ( ! program.pop_binary(first, second)) {
+        return error::op_mul;
+    }
+
+    // auto const result = first * second;
+    auto result = number::safe_mul(first, second);
+    if ( ! result) {
+        return error::op_mul_overflow;
+    }
+    uint32_t const quadratic_op_cost = first.data().size() * second.data().size();
+    program.get_metrics().add_op_cost(quadratic_op_cost);
+    program.get_metrics().add_op_cost(result->data().size() * push_cost_factor);
+    program.push_move(result->data());
+    return error::success;
+}
+
+inline
+interpreter::result interpreter::op_div(program& program) {
+    constexpr auto push_cost_factor = 2;
+    number first;
+    number second;
+    if ( ! program.pop_binary(first, second)) {
+        return error::op_div;
+    }
+
+    if (first == 0) {
+        return error::op_div_by_zero;
+    }
+
+    auto result = second / first;
+    uint32_t const quadratic_op_cost = first.data().size() * second.data().size();
+    program.get_metrics().add_op_cost(quadratic_op_cost);
     program.push_move(result.data());
+    program.get_metrics().add_op_cost(result.data().size() * push_cost_factor);
+    return error::success;
+}
+
+inline
+interpreter::result interpreter::op_mod(program& program) {
+    constexpr auto push_cost_factor = 2;
+    number first;
+    number second;
+    if ( ! program.pop_binary(first, second)) {
+        return error::op_mod;
+    }
+
+    if (first == 0) {
+        return error::op_mod_by_zero;
+    }
+
+    auto result = second % first;
+    uint32_t const quadratic_op_cost = first.data().size() * second.data().size();
+    program.get_metrics().add_op_cost(quadratic_op_cost);
+    program.push_move(result.data());
+    program.get_metrics().add_op_cost(result.data().size() * push_cost_factor);
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_bool_and(program& program) {
-    number first, second; //NOLINT
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_bool_and;
     }
 
     program.push(first.is_true() && second.is_true());
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_bool_or(program& program) {
-    number first, second; //NOLINT
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_bool_or;
     }
 
     program.push(first.is_true() || second.is_true());
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_num_equal(program& program) {
-    number first, second; //NOLINT
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_num_equal;
     }
 
     program.push(first == second);
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_num_equal_verify(program& program) {
-    number first, second; //NOLINT
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_num_equal_verify1;
     }
-
-    return (first == second) ? error::success : error::op_num_equal_verify2;
+    auto const res = first == second;
+    program.get_metrics().add_op_cost(1);
+    return res ? error::success : error::op_num_equal_verify2;
 }
 
 inline
 interpreter::result interpreter::op_num_not_equal(program& program) {
-    number first, second; //NOLINT
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_num_not_equal;
     }
 
     program.push(first != second);
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_less_than(program& program) {
-    number first, second; //NOLINT
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_less_than;
     }
 
     program.push(second < first);
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_greater_than(program& program) {
-    number first, second; //NOLINT
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_greater_than;
     }
 
     program.push(second > first);
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_less_than_or_equal(program& program) {
-    number first, second; //NOLINT
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_less_than_or_equal;
     }
 
     program.push(second <= first);
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
-interpreter::result interpreter::op_greater_than_or_equal(
-    program& program) {
-    number first, second; //NOLINT
+interpreter::result interpreter::op_greater_than_or_equal(program& program) {
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_greater_than_or_equal;
     }
 
     program.push(second >= first);
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_min(program& program) {
-    number first, second; //NOLINT
+    constexpr auto push_cost_factor = 2;
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_min;
     }
 
     program.push_move(second < first ? second.data() : first.data());
+    program.get_metrics().add_op_cost(program.top().size() * push_cost_factor);
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_max(program& program) {
-    number first, second; //NOLINT
+    constexpr auto push_cost_factor = 2;
+    number first;
+    number second;
     if ( ! program.pop_binary(first, second)) {
         return error::op_max;
     }
 
     program.push_move(second > first ? second.data() : first.data());
+    program.get_metrics().add_op_cost(program.top().size() * push_cost_factor);
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_within(program& program) {
-    number first, second, third; //NOLINT
+    // (x min max -- out)
+    number first;
+    number second;
+    number third;
     if ( ! program.pop_ternary(first, second, third)) {
         return error::op_within;
     }
 
     program.push(second <= third && third < first);
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_ripemd160(program& program) {
+    // (in -- hash)
     if (program.empty()) {
         return error::op_ripemd160;
     }
-
+    program.get_metrics().add_hash_iterations(program.top().size(), false);
     program.push_move(ripemd160_hash_chunk(program.pop()));
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
 interpreter::result interpreter::op_sha1(program& program) {
+    // (in -- hash)
     if (program.empty()) {
         return error::op_sha1;
     }
-
+    program.get_metrics().add_hash_iterations(program.top().size(), false);
     program.push_move(sha1_hash_chunk(program.pop()));
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
-    }
+}
 
 inline
 interpreter::result interpreter::op_sha256(program& program) {
@@ -656,7 +1123,9 @@ interpreter::result interpreter::op_sha256(program& program) {
         return error::op_sha256;
     }
 
+    program.get_metrics().add_hash_iterations(program.top().size(), false);
     program.push_move(sha256_hash_chunk(program.pop()));
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
@@ -666,7 +1135,9 @@ interpreter::result interpreter::op_hash160(program& program) {
         return error::op_hash160;
     }
 
+    program.get_metrics().add_hash_iterations(program.top().size(), true);
     program.push_move(ripemd160_hash_chunk(sha256_hash(program.pop())));
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
@@ -676,31 +1147,33 @@ interpreter::result interpreter::op_hash256(program& program) {
         return error::op_hash256;
     }
 
+    program.get_metrics().add_hash_iterations(program.top().size(), true);
     program.push_move(sha256_hash_chunk(sha256_hash(program.pop())));
+    program.get_metrics().add_op_cost(program.top().size());
     return error::success;
 }
 
 inline
-interpreter::result interpreter::op_codeseparator(program& program,
-                                                         operation const& op) {
+interpreter::result interpreter::op_codeseparator(program& program, operation const& op) {
     return program.set_jump_register(op, +1) ? error::success : error::op_code_seperator;
 }
 
 inline
-interpreter::result interpreter::op_check_sig_verify(program& program) {
+std::pair<interpreter::result, size_t> op_check_sig_common(program& program, interpreter::result err) {
+    //TODO: SCRIPT_VERIFY_NULLFAIL
     if (program.size() < 2) {
-        return error::op_check_sig_verify1;
+        return {err, 0};
     }
 
     uint8_t sighash;
     ec_signature signature;
     der_signature distinguished;
-    auto bip66 = chain::script::is_enabled(program.forks(), rule_fork::bip66_rule);
+    auto const bip66 = chain::script::is_enabled(program.forks(), rule_fork::bip66_rule);
 
-#if ! defined(KTH_CURRENCY_BCH)
-    auto bip143 = chain::script::is_enabled(program.forks(), bip143_rule);
+#if defined(KTH_CURRENCY_BCH)
+    auto const bip143 = false;
 #else
-    auto bip143 = false;
+    auto const bip143 = chain::script::is_enabled(program.forks(), bip143_rule);
 #endif
 
     auto const public_key = program.pop();
@@ -716,37 +1189,65 @@ interpreter::result interpreter::op_check_sig_verify(program& program) {
 
     // BIP62: An empty endorsement is not considered lax encoding.
     if ( ! parse_endorsement(sighash, distinguished, std::move(endorsement))) {
-        return error::invalid_signature_encoding;
+        return {error::invalid_signature_encoding, 0};
     }
 
     // Parse DER signature into an EC signature.
     if ( ! parse_signature(signature, distinguished, bip66)) {
-        return bip66 ? error::invalid_signature_lax_encoding : error::invalid_signature_encoding;
+        return {bip66 ? error::invalid_signature_lax_encoding : error::invalid_signature_encoding, 0};
     }
 
     // Version condition preserves independence of bip141 and bip143.
     auto version = bip143 ? program.version() : script_version::unversioned;
 
-    return chain::script::check_signature(signature, sighash, public_key,
+    auto const [res, size] = chain::script::check_signature(signature, sighash, public_key,
                                           script_code, program.transaction(), program.input_index(),
-                                          version, program.value())
-               ? error::success
-               : error::incorrect_signature;
+                                          version, program.value());
+    return {res ? error::success : error::incorrect_signature, size};
 }
 
 inline
 interpreter::result interpreter::op_check_sig(program& program) {
-    auto const verified = op_check_sig_verify(program);
+    auto const [verified, size] = op_check_sig_common(program, error::op_check_sig);
 
     // BIP62: only lax encoding fails the operation.
     if (verified == error::invalid_signature_lax_encoding) {
         return error::op_check_sig;
-}
-
+    }
     program.push(verified == error::success);
+    if (verified == error::success) {
+        program.get_metrics().add_op_cost(program.top().size());
+        program.get_metrics().add_sig_checks(1);
+        program.get_metrics().add_hash_iterations(size, true);
+    }
     return error::success;
 }
 
+inline
+interpreter::result interpreter::op_check_sig_verify(program& program) {
+    auto const [verified, size] = op_check_sig_common(program, error::op_check_sig_verify1);
+    program.get_metrics().add_op_cost(program.top().size());
+    program.get_metrics().add_sig_checks(1);
+    program.get_metrics().add_hash_iterations(size, true);
+    return verified;
+}
+
+inline
+interpreter::result op_check_data_common(program& program, bool verify, interpreter::result err) {
+    // TODO: implement it
+    return err;
+}
+
+inline
+interpreter::result interpreter::op_check_data_sig(program& program) {
+    return op_check_data_common(program, false, error::op_check_data_sig);
+}
+
+inline
+interpreter::result interpreter::op_check_data_sig_verify(program& program) {
+    return op_check_data_common(program, true, error::op_check_data_sig_verify);
+}
+
 inline
 interpreter::result interpreter::op_check_multisig_verify(program& program) {
     int32_t key_count;
@@ -833,9 +1334,10 @@ interpreter::result interpreter::op_check_multisig_verify(program& program) {
 
         while (true) {
             // Version condition preserves independence of bip141 and bip143.
-            if (chain::script::check_signature(signature, sighash, *public_key,
+            auto const [res, size] = chain::script::check_signature(signature, sighash, *public_key,
                                                script_code, program.transaction(), program.input_index(),
-                                               version, program.value())) {
+                                               version, program.value());
+            if (res) {
                 break;
             }
 
@@ -862,8 +1364,7 @@ interpreter::result interpreter::op_check_multisig(program& program) {
 }
 
 inline
-interpreter::result interpreter::op_check_locktime_verify(
-    program& program) {
+interpreter::result interpreter::op_check_locktime_verify(program& program) {
     // BIP65: nop2 subsumed by checklocktimeverify when bip65 fork is active.
     if ( ! chain::script::is_enabled(program.forks(), rule_fork::bip65_rule)) {
         return op_nop(opcode::nop2);
@@ -894,7 +1395,7 @@ interpreter::result interpreter::op_check_locktime_verify(
     }
 
     // The top stack item is positive, so cast is safe.
-    auto const locktime = static_cast<uint64_t>(stack.int64());
+    auto const locktime = uint64_t(stack.int64());
 
     // BIP65: the stack locktime type differs from that of tx.
     if ((locktime < locktime_threshold) !=
@@ -907,8 +1408,7 @@ interpreter::result interpreter::op_check_locktime_verify(
 }
 
 inline
-interpreter::result interpreter::op_check_sequence_verify(
-    program& program) {
+interpreter::result interpreter::op_check_sequence_verify(program& program) {
     // BIP112: nop3 subsumed by checksequenceverify when bip112 fork is active.
     if ( ! chain::script::is_enabled(program.forks(), rule_fork::bip112_rule)) {
         return op_nop(opcode::nop3);
@@ -934,7 +1434,7 @@ interpreter::result interpreter::op_check_sequence_verify(
     }
 
     // The top stack item is positive, so cast is safe.
-    auto const sequence = static_cast<uint64_t>(stack.int64());
+    auto const sequence = uint64_t(stack.int64());
 
     // BIP112: the stack sequence is disabled, treat as nop3.
     if ((sequence & relative_locktime_disabled) != 0) {
@@ -966,14 +1466,117 @@ interpreter::result interpreter::op_check_sequence_verify(
                : error::success;
 }
 
+inline
+interpreter::result interpreter::op_input_index(program& program) {
+    return error::op_input_index;
+}
+
+inline
+interpreter::result interpreter::op_active_bytecode(program& program) {
+    return error::op_active_bytecode;
+}
+
+inline
+interpreter::result interpreter::op_tx_version(program& program) {
+    return error::op_tx_version;
+}
+
+inline
+interpreter::result interpreter::op_tx_input_count(program& program) {
+    return error::op_tx_input_count;
+}
+
+inline
+interpreter::result interpreter::op_tx_output_count(program& program) {
+    return error::op_tx_output_count;
+}
+
+inline
+interpreter::result interpreter::op_tx_locktime(program& program) {
+    return error::op_tx_locktime;
+}
+
+inline
+interpreter::result interpreter::op_utxo_value(program& program) {
+    return error::op_utxo_value;
+}
+
+inline
+interpreter::result interpreter::op_utxo_bytecode(program& program) {
+    return error::op_utxo_bytecode;
+}
+
+inline
+interpreter::result interpreter::op_outpoint_tx_hash(program& program) {
+    return error::op_outpoint_tx_hash;
+}
+
+inline
+interpreter::result interpreter::op_outpoint_index(program& program) {
+    return error::op_outpoint_index;
+}
+
+inline
+interpreter::result interpreter::op_input_bytecode(program& program) {
+    return error::op_input_bytecode;
+}
+
+inline
+interpreter::result interpreter::op_input_sequence_number(program& program) {
+    return error::op_input_sequence_number;
+}
+
+inline
+interpreter::result interpreter::op_output_value(program& program) {
+    return error::op_output_value;
+}
+
+inline
+interpreter::result interpreter::op_output_bytecode(program& program) {
+    return error::op_output_bytecode;
+}
+
+inline
+interpreter::result interpreter::op_utxo_token_category(program& program) {
+    return error::op_utxo_token_category;
+}
+
+inline
+interpreter::result interpreter::op_utxo_token_commitment(program& program) {
+    return error::op_utxo_token_commitment;
+}
+
+inline
+interpreter::result interpreter::op_utxo_token_amount(program& program) {
+    return error::op_utxo_token_amount;
+}
+
+inline
+interpreter::result interpreter::op_output_token_category(program& program) {
+    return error::op_output_token_category;
+}
+
+inline
+interpreter::result interpreter::op_output_token_commitment(program& program) {
+    return error::op_output_token_commitment;
+}
+
+inline
+interpreter::result interpreter::op_output_token_amount(program& program) {
+    return error::op_output_token_amount;
+}
+
+
 // It is expected that the compiler will produce a very efficient jump table.
 inline
-interpreter::result interpreter::run_op(operation const& op,
-                                               program& program) {
+interpreter::result interpreter::run_op(operation const& op, program& program) {
     auto const code = op.code();
     KTH_ASSERT(op.data().empty() || op.is_push());
 
+    program.get_metrics().add_op_cost(kth::may2025::opcode_cost);
+
     switch (op.code()) {
+    // push value
         case opcode::push_size_0:
         case opcode::push_size_1:
         case opcode::push_size_2:
@@ -1051,16 +1654,19 @@ interpreter::result interpreter::run_op(operation const& op,
         case opcode::push_size_74:
         case opcode::push_size_75:
             return op_push_size(program, op);
+
         case opcode::push_one_size:
             return op_push_data(program, op.data(), max_uint8);
         case opcode::push_two_size:
             return op_push_data(program, op.data(), max_uint16);
         case opcode::push_four_size:
             return op_push_data(program, op.data(), max_uint32);
-        case opcode::push_negative_1:
-            return op_push_number(program, number::negative_1);
+
         case opcode::reserved_80:
             return op_reserved(code);
+
+        case opcode::push_negative_1:
+            return op_push_number(program, number::negative_1);
         case opcode::push_positive_1:
             return op_push_number(program, number::positive_1);
         case opcode::push_positive_2:
@@ -1093,6 +1699,8 @@ interpreter::result interpreter::run_op(operation const& op,
             return op_push_number(program, number::positive_15);
         case opcode::push_positive_16:
             return op_push_number(program, number::positive_16);
+
+    // control
         case opcode::nop:
             return op_nop(code);
         case opcode::reserved_98:
@@ -1113,6 +1721,8 @@ interpreter::result interpreter::run_op(operation const& op,
             return op_verify(program);
         case opcode::return_:
             return op_return(program);
+
+    // stack ops
         case opcode::toaltstack:
             return op_to_alt_stack(program);
         case opcode::fromaltstack:
@@ -1151,24 +1761,92 @@ interpreter::result interpreter::run_op(operation const& op,
             return op_swap(program);
         case opcode::tuck:
             return op_tuck(program);
-        case opcode::disabled_cat:
-            return op_disabled(code);
-        case opcode::disabled_substr:
-            return op_disabled(code);
-        case opcode::disabled_left:
-            return op_disabled(code);
-        case opcode::disabled_right:
-            return op_disabled(code);
+
+    // splice ops
+        case opcode::cat:
+            return op_cat(program);
+        case opcode::split:                 // after pythagoras/monolith upgrade (May 2018)
+            return op_split(program);
+        case opcode::reverse_bytes:
+            return op_reverse_bytes(program);
+        case opcode::num2bin:               // after pythagoras/monolith upgrade (May 2018)
+            return op_num2bin(program);
+        case opcode::bin2num:               // after pythagoras/monolith upgrade (May 2018)
+            return op_bin2num(program);
         case opcode::size:
             return op_size(program);
+
+    // Native Introspection opcodes (Nullary)
+        case opcode::input_index:
+            return op_input_index(program);
+        case opcode::active_bytecode:
+            return op_active_bytecode(program);
+        case opcode::tx_version:
+            return op_tx_version(program);
+        case opcode::tx_input_count:
+            return op_tx_input_count(program);
+        case opcode::tx_output_count:
+            return op_tx_output_count(program);
+        case opcode::tx_locktime:
+            return op_tx_locktime(program);
+
+    // Native Introspection opcodes (Unary)
+        // case OP_UTXOTOKENCATEGORY:
+        // case OP_UTXOTOKENCOMMITMENT:
+        // case OP_UTXOTOKENAMOUNT:
+        // case OP_OUTPUTTOKENCATEGORY:
+        // case OP_OUTPUTTOKENCOMMITMENT:
+        // case OP_OUTPUTTOKENAMOUNT:
+        //     // These require native tokens (upgrade9)
+        //     if ( ! nativeTokens) {
+        //         return set_error(serror, ScriptError::BAD_OPCODE);
+        //     }
+        //     [[fallthrough]];
+        // case OP_UTXOVALUE:
+        // case OP_UTXOBYTECODE:
+        // case OP_OUTPOINTTXHASH:
+        // case OP_OUTPOINTINDEX:
+        // case OP_INPUTBYTECODE:
+        // case OP_INPUTSEQUENCENUMBER:
+        // case OP_OUTPUTVALUE:
+        // case OP_OUTPUTBYTECODE: {
+
+        case opcode::utxo_token_category:
+            return op_utxo_token_category(program);
+        case opcode::utxo_token_commitment:
+            return op_utxo_token_commitment(program);
+        case opcode::utxo_token_amount:
+            return op_utxo_token_amount(program);
+        case opcode::output_token_category:
+            return op_output_token_category(program);
+        case opcode::output_token_commitment:
+            return op_output_token_commitment(program);
+        case opcode::utxo_value:
+            return op_utxo_value(program);
+        case opcode::utxo_bytecode:
+            return op_utxo_bytecode(program);
+        case opcode::outpoint_tx_hash:
+            return op_outpoint_tx_hash(program);
+        case opcode::outpoint_index:
+            return op_outpoint_index(program);
+        case opcode::input_bytecode:
+            return op_input_bytecode(program);
+        case opcode::input_sequence_number:
+            return op_input_sequence_number(program);
+        case opcode::output_value:
+            return op_output_value(program);
+        case opcode::output_bytecode:
+            return op_output_bytecode(program);
+
+    // bit logic
         case opcode::disabled_invert:
             return op_disabled(code);
-        case opcode::disabled_and:
-            return op_disabled(code);
-        case opcode::disabled_or:
-            return op_disabled(code);
-        case opcode::disabled_xor:
-            return op_disabled(code);
+        case opcode::and_:
+            return op_and(program);
+        case opcode::or_:
+            return op_or(program);
+        case opcode::xor_:
+            return op_xor(program);
         case opcode::equal:
             return op_equal(program);
         case opcode::equalverify:
@@ -1177,6 +1855,8 @@ interpreter::result interpreter::run_op(operation const& op,
             return op_reserved(code);
         case opcode::reserved_138:
             return op_reserved(code);
+
+    // numeric
         case opcode::add1:
             return op_add1(program);
         case opcode::sub1:
@@ -1197,12 +1877,12 @@ interpreter::result interpreter::run_op(operation const& op,
             return op_add(program);
         case opcode::sub:
             return op_sub(program);
-        case opcode::disabled_mul:
-            return op_disabled(code);
-        case opcode::disabled_div:
-            return op_disabled(code);
-        case opcode::disabled_mod:
-            return op_disabled(code);
+        case opcode::mul:
+            return op_mul(program);
+        case opcode::div:
+            return op_div(program);
+        case opcode::mod:
+            return op_mod(program);
         case opcode::disabled_lshift:
             return op_disabled(code);
         case opcode::disabled_rshift:
@@ -1229,8 +1909,11 @@ interpreter::result interpreter::run_op(operation const& op,
             return op_min(program);
         case opcode::max:
             return op_max(program);
+
         case opcode::within:
             return op_within(program);
+
+    // crypto
         case opcode::ripemd160:
             return op_ripemd160(program);
         case opcode::sha1:
@@ -1247,10 +1930,18 @@ interpreter::result interpreter::run_op(operation const& op,
             return op_check_sig(program);
         case opcode::checksigverify:
             return op_check_sig_verify(program);
+
+        case opcode::checkdatasig:
+            return op_check_data_sig(program);
+        case opcode::checkdatasigverify:
+            return op_check_data_sig_verify(program);
+
         case opcode::checkmultisig:
             return op_check_multisig(program);
         case opcode::checkmultisigverify:
             return op_check_multisig_verify(program);
+
+    // expansion
         case opcode::nop1:
             return op_nop(code);
         case opcode::checklocktimeverify:
@@ -1264,36 +1955,12 @@ interpreter::result interpreter::run_op(operation const& op,
         case opcode::nop8:
         case opcode::nop9:
         case opcode::nop10:
+            //TODO: SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_NOPS
             return op_nop(code);
 
         //TODO(kth): Implement OP_CHECKDATASIG and OP_CHECKDATASIGVERIFY
 
-        case opcode::reserved_186:
-        case opcode::reserved_187:
-        case opcode::reserved_188:
-        case opcode::reserved_189:
-        case opcode::reserved_190:
-        case opcode::reserved_191:
-        case opcode::reserved_192:
-        case opcode::reserved_193:
-        case opcode::reserved_194:
-        case opcode::reserved_195:
-        case opcode::reserved_196:
-        case opcode::reserved_197:
-        case opcode::reserved_198:
-        case opcode::reserved_199:
-        case opcode::reserved_200:
-        case opcode::reserved_201:
-        case opcode::reserved_202:
-        case opcode::reserved_203:
-        case opcode::reserved_204:
-        case opcode::reserved_205:
-        case opcode::reserved_206:
-        case opcode::reserved_207:
-        case opcode::reserved_208:
-        case opcode::reserved_209:
-        case opcode::reserved_210:
-        case opcode::reserved_211:
+
         case opcode::reserved_212:
         case opcode::reserved_213:
         case opcode::reserved_214:
diff --git a/include/kth/domain/impl/machine/operation.ipp b/include/kth/domain/impl/machine/operation.ipp
index c568691d7..915fc4b92 100644
--- a/include/kth/domain/impl/machine/operation.ipp
+++ b/include/kth/domain/impl/machine/operation.ipp
@@ -21,7 +21,7 @@ inline
 operation::operation(data_chunk&& uncoded, bool minimal)
     : code_(opcode_from_data(uncoded, minimal))
     , data_(std::move(uncoded))
-    , valid_( ! is_oversized())
+    , valid_( ! is_oversized(max_push_data_size_legacy))   //TODO: max_push_data_size_legacy change on 2025 May
 {
     if ( ! valid_) {
         reset();
@@ -38,7 +38,7 @@ inline
 operation::operation(data_chunk const& uncoded, bool minimal)
     : code_(opcode_from_data(uncoded, minimal))
     , data_(uncoded)
-    , valid_( ! is_oversized())
+    , valid_( ! is_oversized(max_push_data_size_legacy))   //TODO: max_push_data_size_legacy change on 2025 May
 {
     if ( ! valid_) {
         reset();
@@ -276,7 +276,7 @@ bool operation::is_positive(opcode code) {
 // opcode: [80, 98, 137, 138, 186..255]
 inline
 bool operation::is_reserved(opcode code) {
-    constexpr auto op_186 = static_cast<uint8_t>(opcode::reserved_186);
+    constexpr auto op_212 = static_cast<uint8_t>(opcode::reserved_212);
     constexpr auto op_255 = static_cast<uint8_t>(opcode::reserved_255);
 
     switch (code) {
@@ -286,8 +286,8 @@ bool operation::is_reserved(opcode code) {
         case opcode::reserved_138:
             return true;
         default:
-            auto const value = static_cast<uint8_t>(code);
-            return value >= op_186 && value <= op_255;
+            auto const value = uint8_t(code);
+            return value >= op_212 && value <= op_255;
     }
 }
 
@@ -301,31 +301,43 @@ bool operation::is_reserved(opcode code) {
 // this was an unintended consequence of range testing enums.
 //*****************************************************************************
 inline
-bool operation::is_disabled(opcode code) {
+bool operation::is_disabled(opcode code, uint32_t active_forks) {
+    // SCRIPT_64_BIT_INTEGERS = (1U << 24),
+    constexpr auto script_64_bit_integers = 1U << 24;   // the flag is repeated, it is in Consensus lib.
     switch (code) {
-        case opcode::disabled_cat:
-        case opcode::disabled_substr:
-        case opcode::disabled_left:
-        case opcode::disabled_right:
         case opcode::disabled_invert:
-        case opcode::disabled_and:
-        case opcode::disabled_or:
-        case opcode::disabled_xor:
         case opcode::disabled_mul2:
         case opcode::disabled_div2:
-        case opcode::disabled_mul:
-        case opcode::disabled_div:
-        case opcode::disabled_mod:
         case opcode::disabled_lshift:
         case opcode::disabled_rshift:
         case opcode::disabled_verif:
         case opcode::disabled_vernotif:
             return true;
+        case opcode::mul:
+            return ! is_enabled(active_forks, rule_fork::bch_gauss);
         default:
             return false;
     }
 }
 
+// 2025 - Still disabled opcodes
+// static bool IsOpcodeDisabled(opcodetype opcode, uint32_t flags) {
+//     switch (opcode) {
+//         case OP_INVERT:
+//         case OP_2MUL:
+//         case OP_2DIV:
+//         case OP_LSHIFT:
+//         case OP_RSHIFT:
+//             // Disabled opcodes.
+//             return true;
+//         case OP_MUL:
+//             return (flags & SCRIPT_64_BIT_INTEGERS) == 0;
+//         default:
+//             break;
+//     }
+//     return false;
+// }
+
 //*****************************************************************************
 // CONSENSUS: in order to properly treat VERIF and VERNOTIF as disabled (see
 // is_disabled comments) those codes must not be included here.
@@ -377,8 +389,8 @@ bool operation::is_positive() const {
 }
 
 inline
-bool operation::is_disabled() const {
-    return is_disabled(code_);
+bool operation::is_disabled(uint32_t active_forks) const {
+    return is_disabled(code_, active_forks);
 }
 
 inline
@@ -392,9 +404,9 @@ bool operation::is_relaxed_push() const {
 }
 
 inline
-bool operation::is_oversized() const {
+bool operation::is_oversized(size_t max_size) const {
     // bit.ly/2eSDkOJ
-    return data_.size() > max_push_data_size;
+    return data_.size() > max_size;
 }
 
 inline
diff --git a/include/kth/domain/impl/machine/program.ipp b/include/kth/domain/impl/machine/program.ipp
index 353826ae6..dfc3fd8b6 100644
--- a/include/kth/domain/impl/machine/program.ipp
+++ b/include/kth/domain/impl/machine/program.ipp
@@ -22,6 +22,18 @@ namespace kth::domain::machine {
 
 using script_version = ::kth::infrastructure::machine::script_version;
 
+// Metrics
+//-----------------------------------------------------------------------------
+inline
+metrics& program::get_metrics() {
+    return metrics_;
+}
+
+inline
+metrics const& program::get_metrics() const {
+    return metrics_;
+}
+
 // Constant registers.
 //-----------------------------------------------------------------------------
 
@@ -36,6 +48,23 @@ uint32_t program::forks() const {
     return forks_;
 }
 
+inline
+size_t program::max_script_element_size() const {
+    auto const galois_enabled = chain::script::is_enabled(forks(), rule_fork::bch_galois);
+    return galois_enabled ? ::kth::may2025::max_push_data_size : max_push_data_size_legacy;
+}
+
+inline
+size_t program::max_integer_size_legacy() const {
+    auto const gauss_enabled = chain::script::is_enabled(forks(), rule_fork::bch_gauss);
+    return gauss_enabled ? max_number_size_64_bits : max_number_size_32_bits;
+}
+
+inline
+bool program::is_chip_vm_limits_enabled() const {
+    return chain::script::is_enabled(forks(), rule_fork::bch_galois);
+}
+
 inline
 uint32_t program::input_index() const {
     return input_index_;
@@ -99,7 +128,7 @@ bool program::increment_operation_count(operation const& op) {
 
 inline
 bool program::increment_operation_count(int32_t public_keys) {
-    static auto const max_keys = static_cast<int32_t>(max_script_public_keys);
+    static auto const max_keys = int32_t(max_script_public_keys);
 
     // bit.ly/2d1bsdB
     if (public_keys < 0 || public_keys > max_keys) {
@@ -162,9 +191,10 @@ void program::push_copy(value_type const& item) {
 //-----------------------------------------------------------------------------
 
 // This must be guarded.
-inline data_chunk program::pop() {
+inline
+data_chunk program::pop() {
     KTH_ASSERT( ! empty());
-    auto const value = primary_.back();
+    auto value = std::move(primary_.back());
     primary_.pop_back();
     return value;
 }
@@ -172,7 +202,7 @@ inline data_chunk program::pop() {
 inline
 bool program::pop(int32_t& out_value) {
     number value;
-    if ( ! pop(value)) {
+    if ( ! pop(value, max_integer_size_legacy())) {
         return false;
 }
 
@@ -181,20 +211,31 @@ bool program::pop(int32_t& out_value) {
 }
 
 inline
-bool program::pop(number& out_number, size_t maxiumum_size) {
-    return !empty() && out_number.set_data(pop(), maxiumum_size);
+bool program::pop(int64_t& out_value) {
+    number value;
+    if ( ! pop(value, max_integer_size_legacy())) {
+        return false;
+    }
+
+    out_value = value.int64();
+    return true;
+}
+
+inline
+bool program::pop(number& out_number, size_t maximum_size) {
+    return !empty() && out_number.set_data(pop(), maximum_size);
 }
 
 inline
 bool program::pop_binary(number& first, number& second) {
     // The right hand side number is at the top of the stack.
-    return pop(first) && pop(second);
+    return pop(first, max_integer_size_legacy()) && pop(second, max_integer_size_legacy());
 }
 
 inline
 bool program::pop_ternary(number& first, number& second, number& third) {
     // The upper bound is at stack top, lower bound next, value next.
-    return pop(first) && pop(second) && pop(third);
+    return pop(first, max_integer_size_legacy()) && pop(second, max_integer_size_legacy()) && pop(third, max_integer_size_legacy());
 }
 
 // Determines if popped value is valid post-pop stack index and returns index.
@@ -211,7 +252,7 @@ bool program::pop_position(stack_iterator& out_position) {
         return false;
     }
 
-    auto const index = static_cast<uint32_t>(signed_index);
+    auto const index = uint32_t(signed_index);
 
     if (index >= size()) {
         return false;
@@ -332,11 +373,25 @@ data_stack::value_type& program::item(size_t index) {
     return *position(index);
 }
 
+// This must be guarded.
 inline
-bool program::top(number& out_number, size_t maxiumum_size) const {
-    return !empty() && out_number.set_data(item(0), maxiumum_size);
+data_chunk& program::top() {
+    KTH_ASSERT( ! empty());
+    return primary_.back();
 }
 
+inline
+data_chunk const& program::top() const {
+    KTH_ASSERT( ! empty());
+    return primary_.back();
+}
+
+inline
+bool program::top(number& out_number, size_t maximum_size) const {
+    return !empty() && out_number.set_data(item(0), maximum_size);
+}
+
+
 inline
 program::stack_iterator program::position(size_t index) const {
     // Subtracting 1 makes the stack indexes zero-based (unlike satoshi).
@@ -351,6 +406,11 @@ program::stack_mutable_iterator program::position(size_t index) {
     return (primary_.end() - 1) - index;
 }
 
+inline
+size_t program::index(stack_iterator const& position) const {
+    return size() - (position - primary_.begin());
+}
+
 // Pop jump-to-end, push all back, use to construct a script.
 inline
 operation::list program::subscript() const {
@@ -368,6 +428,12 @@ size_t program::size() const {
     return primary_.size();
 }
 
+inline
+size_t program::conditional_stack_size() const {
+    return condition_.size();
+}
+
+
 // Alternate stack.
 //-----------------------------------------------------------------------------
 
diff --git a/include/kth/domain/machine/interpreter.hpp b/include/kth/domain/machine/interpreter.hpp
index 1a320ef9c..f3221e05a 100644
--- a/include/kth/domain/machine/interpreter.hpp
+++ b/include/kth/domain/machine/interpreter.hpp
@@ -119,9 +119,33 @@ class KD_API interpreter {
     static
     result op_tuck(program& program);
 
+    static
+    result op_cat(program& program);
+
+    static
+    result op_split(program& program);
+
+    static
+    result op_reverse_bytes(program& program);
+
+    static
+    result op_num2bin(program& program);
+
+    static
+    result op_bin2num(program& program);
+
     static
     result op_size(program& program);
 
+    static
+    result op_and(program& program);
+
+    static
+    result op_or(program& program);
+
+    static
+    result op_xor(program& program);
+
     static
     result op_equal(program& program);
 
@@ -152,6 +176,15 @@ class KD_API interpreter {
     static
     result op_sub(program& program);
 
+    static
+    result op_mul(program& program);
+
+    static
+    result op_div(program& program);
+
+    static
+    result op_mod(program& program);
+
     static
     result op_bool_and(program& program);
 
@@ -206,11 +239,17 @@ class KD_API interpreter {
     static
     result op_codeseparator(program& program, operation const& op);
 
+    static
+    result op_check_sig(program& program);
+
     static
     result op_check_sig_verify(program& program);
 
     static
-    result op_check_sig(program& program);
+    result op_check_data_sig(program& program);
+
+    static
+    result op_check_data_sig_verify(program& program);
 
     static
     result op_check_multisig_verify(program& program);
@@ -224,6 +263,66 @@ class KD_API interpreter {
     static
     result op_check_sequence_verify(program& program);
 
+    static
+    result op_input_index(program& program);
+
+    static
+    result op_active_bytecode(program& program);
+
+    static
+    result op_tx_version(program& program);
+
+    static
+    result op_tx_input_count(program& program);
+
+    static
+    result op_tx_output_count(program& program);
+
+    static
+    result op_tx_locktime(program& program);
+
+    static
+    result op_utxo_value(program& program);
+
+    static
+    result op_utxo_bytecode(program& program);
+
+    static
+    result op_outpoint_tx_hash(program& program);
+
+    static
+    result op_outpoint_index(program& program);
+
+    static
+    result op_input_bytecode(program& program);
+
+    static
+    result op_input_sequence_number(program& program);
+
+    static
+    result op_output_value(program& program);
+
+    static
+    result op_output_bytecode(program& program);
+
+    static
+    result op_utxo_token_category(program& program);
+
+    static
+    result op_utxo_token_commitment(program& program);
+
+    static
+    result op_utxo_token_amount(program& program);
+
+    static
+    result op_output_token_category(program& program);
+
+    static
+    result op_output_token_commitment(program& program);
+
+    static
+    result op_output_token_amount(program& program);
+
     /// Run program script.
     static
     code run(program& program);
diff --git a/include/kth/domain/machine/metrics.hpp b/include/kth/domain/machine/metrics.hpp
new file mode 100644
index 000000000..40d1a6245
--- /dev/null
+++ b/include/kth/domain/machine/metrics.hpp
@@ -0,0 +1,98 @@
+// Copyright (c) 2016-2024 Knuth Project developers.
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef KTH_DOMAIN_MACHINE_METRICS_HPP
+#define KTH_DOMAIN_MACHINE_METRICS_HPP
+
+#include <cstdint>
+#include <optional>
+
+#include <kth/domain/constants/common.hpp>
+#include <kth/domain/define.hpp>
+#include <kth/domain/machine/script_limits.hpp>
+
+namespace kth::domain::machine {
+
+inline constexpr
+bool is_vm_limits_standard(uint32_t script_flags) {
+    // This constant is defined in consensus lib, but we also need it here.
+    constexpr uint32_t verify_flags_enable_vm_limits_standard = (1U << 29); //SCRIPT_VM_LIMITS_STANDARD
+    return script_flags & verify_flags_enable_vm_limits_standard;
+}
+
+struct KD_API metrics {
+    using script_limits_opt_t = std::optional<may2025::script_limits>;
+
+    // Getters
+    uint32_t sig_checks() const {
+        return sig_checks_;
+    }
+
+    uint64_t op_cost() const {
+        return op_cost_;
+    }
+
+    uint64_t hash_digest_iterations() const {
+        return hash_digest_iterations_;
+    }
+
+    // Setters
+    void add_op_cost(uint32_t cost) {
+        op_cost_ += int64_t(cost);
+    }
+
+    void add_hash_iterations(uint32_t message_length, bool is_two_round_hash /* set to true iff OP_HASH256 or OP_HASH160 */) {
+        hash_digest_iterations_ += may2025::calculate_hash_iters(message_length, is_two_round_hash);
+    }
+
+    void add_sig_checks(int n_checks) {
+        sig_checks_ += n_checks;
+    }
+
+    // Checks
+    bool is_over_op_cost_limit(uint32_t script_flags) const {
+        return script_limits && composite_op_cost(script_flags) > script_limits->op_cost_limit();
+    }
+
+    bool is_over_hash_iters_limit() const {
+        return script_limits && hash_digest_iterations() > script_limits->hash_iters_limit();
+    }
+
+    bool has_valid_script_limits() const {
+        return script_limits.has_value();
+    }
+
+    void set_script_limits(uint32_t script_flags, uint64_t script_sig_size) {
+        script_limits.emplace(is_vm_limits_standard(script_flags), script_sig_size);
+    }
+
+    script_limits_opt_t const& get_script_limits() const {
+        return script_limits;
+    }
+
+    // Returns the composite value that is: nOpCost + nHashDigestIterators * {192 or 64} + nSigChecks * 26,000
+    // Consensus code uses a 64 for the hashing iter cost, standard/relay code uses the more restrictive cost of 192.
+    uint64_t composite_op_cost(uint32_t script_flags) const {
+        uint64_t const factor = may2025::hash_iter_op_cost_factor(is_vm_limits_standard(script_flags));
+        return op_cost_ // base cost: encompasses ops + pushes, etc
+               // additional cost: add hash iterations * {192 or 64}
+               + hash_digest_iterations_ * factor
+               // additional cost: add sig checks * 26,000
+               + uint64_t(sig_checks_) * ::kth::may2025::sig_check_cost_factor;
+    }
+
+private:
+    uint32_t sig_checks_ = 0;
+
+    /** CHIP-2021-05-vm-limits: Targeted Virtual Machine Limits */
+    uint64_t op_cost_ = 0;
+    uint64_t hash_digest_iterations_ = 0;
+    script_limits_opt_t script_limits;
+
+    uint64_t get_hash_iter_cost_factor(uint32_t script_flags) const;
+};
+
+} // namespace kth::domain::machine
+
+#endif // KTH_DOMAIN_MACHINE_METRICS_HPP
diff --git a/include/kth/domain/machine/opcode.hpp b/include/kth/domain/machine/opcode.hpp
index ed8d3432a..e3af79c56 100644
--- a/include/kth/domain/machine/opcode.hpp
+++ b/include/kth/domain/machine/opcode.hpp
@@ -30,320 +30,309 @@ enum class opcode : uint8_t {
     //-------------------------------------------------------------------------
     // is_relaxed_push, is_push (excluding reserved_80)
 
-    push_size_0 = 0,        // is_version (pushes [] to the stack, not 0)
-    push_size_1 = 1,
-    push_size_2 = 2,
-    push_size_3 = 3,
-    push_size_4 = 4,
-    push_size_5 = 5,
-    push_size_6 = 6,
-    push_size_7 = 7,
-    push_size_8 = 8,
-    push_size_9 = 9,
-    push_size_10 = 10,
-    push_size_11 = 11,
-    push_size_12 = 12,
-    push_size_13 = 13,
-    push_size_14 = 14,
-    push_size_15 = 15,
-    push_size_16 = 16,
-    push_size_17 = 17,
-    push_size_18 = 18,
-    push_size_19 = 19,
-    push_size_20 = 20,
-    push_size_21 = 21,
-    push_size_22 = 22,
-    push_size_23 = 23,
-    push_size_24 = 24,
-    push_size_25 = 25,
-    push_size_26 = 26,
-    push_size_27 = 27,
-    push_size_28 = 28,
-    push_size_29 = 29,
-    push_size_30 = 30,
-    push_size_31 = 31,
-    push_size_32 = 32,
-    push_size_33 = 33,
-    push_size_34 = 34,
-    push_size_35 = 35,
-    push_size_36 = 36,
-    push_size_37 = 37,
-    push_size_38 = 38,
-    push_size_39 = 39,
-    push_size_40 = 40,
-    push_size_41 = 41,
-    push_size_42 = 42,
-    push_size_43 = 43,
-    push_size_44 = 44,
-    push_size_45 = 45,
-    push_size_46 = 46,
-    push_size_47 = 47,
-    push_size_48 = 48,
-    push_size_49 = 49,
-    push_size_50 = 50,
-    push_size_51 = 51,
-    push_size_52 = 52,
-    push_size_53 = 53,
-    push_size_54 = 54,
-    push_size_55 = 55,
-    push_size_56 = 56,
-    push_size_57 = 57,
-    push_size_58 = 58,
-    push_size_59 = 59,
-    push_size_60 = 60,
-    push_size_61 = 61,
-    push_size_62 = 62,
-    push_size_63 = 63,
-    push_size_64 = 64,
-    push_size_65 = 65,
-    push_size_66 = 66,
-    push_size_67 = 67,
-    push_size_68 = 68,
-    push_size_69 = 69,
-    push_size_70 = 70,
-    push_size_71 = 71,
-    push_size_72 = 72,
-    push_size_73 = 73,
-    push_size_74 = 74,
-    push_size_75 = 75,
-    push_one_size = 76,
-    push_two_size = 77,
-    push_four_size = 78,
-    push_negative_1 = 79,   // is_numeric
-    reserved_80 = 80,       // [reserved]
-    push_positive_1 = 81,   // is_numeric, is_positive, is_version
-    push_positive_2 = 82,   // is_numeric, is_positive, is_version
-    push_positive_3 = 83,   // is_numeric, is_positive, is_version
-    push_positive_4 = 84,   // is_numeric, is_positive, is_version
-    push_positive_5 = 85,   // is_numeric, is_positive, is_version
-    push_positive_6 = 86,   // is_numeric, is_positive, is_version
-    push_positive_7 = 87,   // is_numeric, is_positive, is_version
-    push_positive_8 = 88,   // is_numeric, is_positive, is_version
-    push_positive_9 = 89,   // is_numeric, is_positive, is_version
-    push_positive_10 = 90,  // is_numeric, is_positive, is_version
-    push_positive_11 = 91,  // is_numeric, is_positive, is_version
-    push_positive_12 = 92,  // is_numeric, is_positive, is_version
-    push_positive_13 = 93,  // is_numeric, is_positive, is_version
-    push_positive_14 = 94,  // is_numeric, is_positive, is_version
-    push_positive_15 = 95,  // is_numeric, is_positive, is_version
-    push_positive_16 = 96,  // is_numeric, is_positive, is_version
+// push value
+    push_size_0 = 0x00,     // 0        // is_version (pushes [] to the stack, not 0)
+    push_size_1 = 0x01,     // 1
+    push_size_2 = 0x02,     // 2
+    push_size_3 = 0x03,     // 3
+    push_size_4 = 0x04,     // 4
+    push_size_5 = 0x05,     // 5
+    push_size_6 = 0x06,     // 6
+    push_size_7 = 0x07,     // 7
+    push_size_8 = 0x08,     // 8
+    push_size_9 = 0x09,     // 9
+    push_size_10 = 0x0a,    // 10
+    push_size_11 = 0x0b,    // 11
+    push_size_12 = 0x0c,    // 12
+    push_size_13 = 0x0d,    // 13
+    push_size_14 = 0x0e,    // 14
+    push_size_15 = 0x0f,    // 15
+    push_size_16 = 0x10,    // 16
+    push_size_17 = 0x11,    // 17
+    push_size_18 = 0x12,    // 18
+    push_size_19 = 0x13,    // 19
+    push_size_20 = 0x14,    // 20
+    push_size_21 = 0x15,    // 21
+    push_size_22 = 0x16,    // 22
+    push_size_23 = 0x17,    // 23
+    push_size_24 = 0x18,    // 24
+    push_size_25 = 0x19,    // 25
+    push_size_26 = 0x1a,    // 26
+    push_size_27 = 0x1b,    // 27
+    push_size_28 = 0x1c,    // 28
+    push_size_29 = 0x1d,    // 29
+    push_size_30 = 0x1e,    // 30
+    push_size_31 = 0x1f,    // 31
+    push_size_32 = 0x20,    // 32
+    push_size_33 = 0x21,    // 33
+    push_size_34 = 0x22,    // 34
+    push_size_35 = 0x23,    // 35
+    push_size_36 = 0x24,    // 36
+    push_size_37 = 0x25,    // 37
+    push_size_38 = 0x26,    // 38
+    push_size_39 = 0x27,    // 39
+    push_size_40 = 0x28,    // 40
+    push_size_41 = 0x29,    // 41
+    push_size_42 = 0x2a,    // 42
+    push_size_43 = 0x2b,    // 43
+    push_size_44 = 0x2c,    // 44
+    push_size_45 = 0x2d,    // 45
+    push_size_46 = 0x2e,    // 46
+    push_size_47 = 0x2f,    // 47
+    push_size_48 = 0x30,    // 48
+    push_size_49 = 0x31,    // 49
+    push_size_50 = 0x32,    // 50
+    push_size_51 = 0x33,    // 51
+    push_size_52 = 0x34,    // 52
+    push_size_53 = 0x35,    // 53
+    push_size_54 = 0x36,    // 54
+    push_size_55 = 0x37,    // 55
+    push_size_56 = 0x38,    // 56
+    push_size_57 = 0x39,    // 57
+    push_size_58 = 0x3a,    // 58
+    push_size_59 = 0x3b,    // 59
+    push_size_60 = 0x3c,    // 60
+    push_size_61 = 0x3d,    // 61
+    push_size_62 = 0x3e,    // 62
+    push_size_63 = 0x3f,    // 63
+    push_size_64 = 0x40,    // 64
+    push_size_65 = 0x41,    // 65
+    push_size_66 = 0x42,    // 66
+    push_size_67 = 0x43,    // 67
+    push_size_68 = 0x44,    // 68
+    push_size_69 = 0x45,    // 69
+    push_size_70 = 0x46,    // 70
+    push_size_71 = 0x47,    // 71
+    push_size_72 = 0x48,    // 72
+    push_size_73 = 0x49,    // 73
+    push_size_74 = 0x4a,    // 74
+    push_size_75 = 0x4b,    // 75
+    push_one_size = 0x4c,   // 76
+    push_two_size = 0x4d,   // 77
+    push_four_size = 0x4e,  // 78
+
+    push_negative_1 = 0x4f, // 79 is_numeric
+    reserved_80 = 0x50,     // 80 [reserved]
+
+    push_positive_1 = 0x51, // 81, is_numeric, is_positive, is_version
+    push_positive_2 = 0x52, // 82, is_numeric, is_positive, is_version
+    push_positive_3 = 0x53, // 83, is_numeric, is_positive, is_version
+    push_positive_4 = 0x54, // 84, is_numeric, is_positive, is_version
+    push_positive_5 = 0x55, // 85, is_numeric, is_positive, is_version
+    push_positive_6 = 0x56, // 86, is_numeric, is_positive, is_version
+    push_positive_7 = 0x57, // 87, is_numeric, is_positive, is_version
+    push_positive_8 = 0x58, // 88, is_numeric, is_positive, is_version
+    push_positive_9 = 0x59, // 89, is_numeric, is_positive, is_version
+    push_positive_10 = 0x5a, // 90, is_numeric, is_positive, is_version
+    push_positive_11 = 0x5b, // 91, is_numeric, is_positive, is_version
+    push_positive_12 = 0x5c, // 92, is_numeric, is_positive, is_version
+    push_positive_13 = 0x5d, // 93, is_numeric, is_positive, is_version
+    push_positive_14 = 0x5e, // 94, is_numeric, is_positive, is_version
+    push_positive_15 = 0x5f, // 95, is_numeric, is_positive, is_version
+    push_positive_16 = 0x60, // 96, is_numeric, is_positive, is_version
 
     //-------------------------------------------------------------------------
     // is_counted
 
-    nop = 97,
-    reserved_98 = 98,       // [ver]
-    if_ = 99,               // is_conditional
-    notif = 100,            // is_conditional
-    disabled_verif = 101,   // is_disabled
-    disabled_vernotif = 102,// is_disabled
-    else_ = 103,            // is_conditional
-    endif = 104,            // is_conditional
-    verify = 105,
-    return_ = 106,
-    toaltstack = 107,
-    fromaltstack = 108,
-    drop2 = 109,
-    dup2 = 110,
-    dup3 = 111,
-    over2 = 112,
-    rot2 = 113,
-    swap2 = 114,
-    ifdup = 115,
-    depth = 116,
-    drop = 117,
-    dup = 118,
-    nip = 119,
-    over = 120,
-    pick = 121,
-    roll = 122,
-    rot = 123,
-    swap = 124,
-    tuck = 125,
-    disabled_cat = 126,     // is_disabled
-    disabled_substr = 127,  // is_disabled
-    disabled_left = 128,    // is_disabled
-    disabled_right = 129,   // is_disabled
-    size = 130,
-    disabled_invert = 131,  // is_disabled
-    disabled_and = 132,     // is_disabled
-    disabled_or = 133,      // is_disabled
-    disabled_xor = 134,     // is_disabled
-    equal = 135,
-    equalverify = 136,
-    reserved_137 = 137,     // [reserved1]
-    reserved_138 = 138,     // [reserved2]
-    add1 = 139,
-    sub1 = 140,
-    disabled_mul2 = 141,    // is_disabled
-    disabled_div2 = 142,    // is_disabled
-    negate = 143,
-    abs = 144,
-    not_ = 145,
-    nonzero = 146,
-    add = 147,
-    sub = 148,
-    disabled_mul = 149,     // is_disabled
-    disabled_div = 150,     // is_disabled
-    disabled_mod = 151,     // is_disabled
-    disabled_lshift = 152,  // is_disabled
-    disabled_rshift = 153,  // is_disabled
-    booland = 154,
-    boolor = 155,
-    numequal = 156,
-    numequalverify = 157,
-    numnotequal = 158,
-    lessthan = 159,
-    greaterthan = 160,
-    lessthanorequal = 161,
-    greaterthanorequal = 162,
-    min = 163,
-    max = 164,
-    within = 165,
-    ripemd160 = 166,
-    sha1 = 167,
-    sha256 = 168,
-    hash160 = 169,
-    hash256 = 170,
-    codeseparator = 171,
-    checksig = 172,
-    checksigverify = 173,
-    checkmultisig = 174,
-    checkmultisigverify = 175,
-
-
-    nop1 = 176,
-    nop2 = 177,
-    checklocktimeverify = nop2,
-    nop3 = 178,
-    checksequenceverify = nop3,
-    nop4 = 179,
-    nop5 = 180,
-    nop6 = 181,
-    nop7 = 182,
-    nop8 = 183,
-    nop9 = 184,
-    nop10 = 185,
-
-    // More crypto
-    checkdatasig = 0xba,
-    checkdatasigverify = 0xbb,
-
-    // additional byte string operations
-    reversebytes = 0xbc,
-
-    // Available codepoints
-    // 0xbd,
-    // 0xbe,
-    // 0xbf,
-
-    // Native Introspection opcodes
-    inputindex = 0xc0,
-    activebytecode = 0xc1,
-    txversion = 0xc2,
-    txinputcount = 0xc3,
-    txoutputcount = 0xc4,
-    txlocktime = 0xc5,
-    utxovalue = 0xc6,
-    utxobytecode = 0xc7,
-    outpointtxhash = 0xc8,
-    outpointindex = 0xc9,
-    inputbytecode = 0xca,
-    inputsequencenumber = 0xcb,
-    outputvalue = 0xcc,
-    outputbytecode = 0xcd,
-
-    // Native Introspection of tokens (SCRIPT_ENABLE_TOKENS must be set)
-    utxotokencategory = 0xce,
-    utxotokencommitment = 0xcf,
-    utxotokenamount = 0xd0,
-    outputtokencategory = 0xd1,
-    outputtokencommitment = 0xd2,
-    outputtokenamount = 0xd3,
-
-    reserved3 = 0xd4,
-    reserved4 = 0xd5,
-
-    // The first op_code value after all defined opcodes
-    first_undefined_op_value,
-
-    // Invalid opcode if executed, but used for special token prefix if at
-    // position 0 in scriptPubKey. See: primitives/token.h
-    special_token_prefix = 0xef,
-
-    invalidopcode = 0xff,   ///< Not a real OPCODE!
-
-    reserved_186 = 186,
-    reserved_187 = 187,
-
-    reserved_188 = 188,
-    reserved_189 = 189,
-    reserved_190 = 190,
-    reserved_191 = 191,
-    reserved_192 = 192,
-    reserved_193 = 193,
-    reserved_194 = 194,
-    reserved_195 = 195,
-    reserved_196 = 196,
-    reserved_197 = 197,
-    reserved_198 = 198,
-    reserved_199 = 199,
-    reserved_200 = 200,
-    reserved_201 = 201,
-    reserved_202 = 202,
-    reserved_203 = 203,
-    reserved_204 = 204,
-    reserved_205 = 205,
-    reserved_206 = 206,
-    reserved_207 = 207,
-    reserved_208 = 208,
-    reserved_209 = 209,
-    reserved_210 = 210,
-    reserved_211 = 211,
-    reserved_212 = 212,
-    reserved_213 = 213,
-    reserved_214 = 214,
-    reserved_215 = 215,
-    reserved_216 = 216,
-    reserved_217 = 217,
-    reserved_218 = 218,
-    reserved_219 = 219,
-    reserved_220 = 220,
-    reserved_221 = 221,
-    reserved_222 = 222,
-    reserved_223 = 223,
-    reserved_224 = 224,
-    reserved_225 = 225,
-    reserved_226 = 226,
-    reserved_227 = 227,
-    reserved_228 = 228,
-    reserved_229 = 229,
-    reserved_230 = 230,
-    reserved_231 = 231,
-    reserved_232 = 232,
-    reserved_233 = 233,
-    reserved_234 = 234,
-    reserved_235 = 235,
-    reserved_236 = 236,
-    reserved_237 = 237,
-    reserved_238 = 238,
-    reserved_239 = 239,
-    reserved_240 = 240,
-    reserved_241 = 241,
-    reserved_242 = 242,
-    reserved_243 = 243,
-    reserved_244 = 244,
-    reserved_245 = 245,
-    reserved_246 = 246,
-    reserved_247 = 247,
-    reserved_248 = 248,
-    reserved_249 = 249,
-    reserved_250 = 250,
-    reserved_251 = 251,
-    reserved_252 = 252,
-    reserved_253 = 253,
-    reserved_254 = 254,
-    reserved_255 = 255
+// control
+    nop = 0x61,               // 97
+    reserved_98 = 0x62,       // 98 [ver]
+    if_ = 0x63,               // 99 is_conditional
+    notif = 0x64,             // 100 is_conditional
+    disabled_verif = 0x65,    // 101 is_disabled
+    disabled_vernotif = 0x66, // 102 is_disabled
+    else_ = 0x67,             // 103 is_conditional
+    endif = 0x68,             // 104 is_conditional
+    verify = 0x69,            // 105
+    return_ = 0x6a,           // 106
+
+// stack ops
+    toaltstack = 0x6b,        // 107
+    fromaltstack = 0x6c,      // 108
+    drop2 = 0x6d,             // 109
+    dup2 = 0x6e,              // 110
+    dup3 = 0x6f,              // 111
+    over2 = 0x70,             // 112
+    rot2 = 0x71,              // 113
+    swap2 = 0x72,             // 114
+    ifdup = 0x73,             // 115
+    depth = 0x74,             // 116
+    drop = 0x75,              // 117
+    dup = 0x76,               // 118
+    nip = 0x77,               // 119
+    over = 0x78,              // 120
+    pick = 0x79,              // 121
+    roll = 0x7a,              // 122
+    rot = 0x7b,               // 123
+    swap = 0x7c,              // 124
+    tuck = 0x7d,              // 125
+
+// splice ops
+    cat = 0x7e,             // 126
+    split = 0x7f,           // 127 was called substr before (disabled and re-enabled after pythagoras/monolith upgrade, May 2018)
+    num2bin = 0x80,         // 128 was called left before (disabled and re-enabled after pythagoras/monolith upgrade, May 2018)
+    bin2num = 0x81,         // 129 was called right before (disabled and re-enabled after pythagoras/monolith upgrade, May 2018)
+    size = 0x82,            // 130
+
+// bit logic
+    disabled_invert = 0x83, // 131, is_disabled
+    and_ = 0x84,            // 132, disabled and re-enabled after pythagoras/monolith upgrade, May 2018
+    or_ = 0x85,             // 133, disabled and re-enabled after pythagoras/monolith upgrade, May 2018
+    xor_ = 0x86,            // 134, disabled and re-enabled after pythagoras/monolith upgrade, May 2018
+    equal = 0x87,           // 135
+    equalverify = 0x88,     // 136
+    reserved_137 = 0x89,    // 137 [reserved1]
+    reserved_138 = 0x8a,    // 138 [reserved2]
+
+// numeric
+    add1 = 0x8b,            //  139
+    sub1 = 0x8c,            // 140
+    disabled_mul2 = 0x8d,   // 141 is_disabled
+    disabled_div2 = 0x8e,   // 142 is_disabled
+    negate = 0x8f,          // 143
+    abs = 0x90,             // 144
+    not_ = 0x91,            // 145
+    nonzero = 0x92,         // 146
+
+    add = 0x93,             // 147
+    sub = 0x94,             // 148
+    mul = 0x95,             // 149, disabled and re-enabled after gauss/upgrade8 upgrade, May 2022
+    div = 0x96,             // 150, disabled and re-enabled after pythagoras/monolith upgrade, May 2018
+    mod = 0x97,             // 151, disabled and re-enabled after pythagoras/monolith upgrade, May 2018
+    disabled_lshift = 0x98, // 152 is_disabled
+    disabled_rshift = 0x99, // 153 is_disabled
+
+    booland = 0x9a,            // 154
+    boolor = 0x9b,             // 155
+    numequal = 0x9c,           // 156
+    numequalverify = 0x9d,     // 157
+    numnotequal = 0x9e,        // 158
+    lessthan = 0x9f,           // 159
+    greaterthan = 0xa0,        // 160
+    lessthanorequal = 0xa1,    // 161
+    greaterthanorequal = 0xa2, // 162
+    min = 0xa3,                // 163
+    max = 0xa4,                // 164
+
+    within = 0xa5,             // 165
+
+// crypto
+    ripemd160 = 0xa6,           // 166
+    sha1 = 0xa7,                // 167
+    sha256 = 0xa8,              // 168
+    hash160 = 0xa9,             // 169
+    hash256 = 0xaa,             // 170
+    codeseparator = 0xab,       // 171
+    checksig = 0xac,            // 172
+    checksigverify = 0xad,      // 173
+    checkmultisig = 0xae,       // 174
+    checkmultisigverify = 0xaf, // 175
+
+// expansion
+    nop1 = 0xb0,                 // 176
+    nop2 = 0xb1,                 // 177
+    checklocktimeverify = nop2,  // 177
+    nop3 = 0xb2,                 // 178
+    checksequenceverify = nop3,  // 178
+    nop4 = 0xb3,                 // 179
+    nop5 = 0xb4,                 // 180
+    nop6 = 0xb5,                 // 181
+    nop7 = 0xb6,                 // 182
+    nop8 = 0xb7,                 // 183
+    nop9 = 0xb8,                 // 184
+    nop10 = 0xb9,                // 185
+
+// more crypto
+    checkdatasig = 0xba,         // 186
+    checkdatasigverify = 0xbb,   // 187
+
+// additional byte string operations
+    reverse_bytes = 0xbc,         // 188
+
+// Available codepoints
+    // 0xbd,                     // 189
+    // 0xbe,                     // 190
+    // 0xbf,                     // 191
+
+// Native Introspection opcodes
+    input_index = 0xc0,           // 192
+    active_bytecode = 0xc1,       // 193
+    tx_version = 0xc2,            // 194
+    tx_input_count = 0xc3,        // 195
+    tx_output_count = 0xc4,       // 196
+    tx_locktime = 0xc5,           // 197
+    utxo_value = 0xc6,            // 198
+    utxo_bytecode = 0xc7,         // 199
+    outpoint_tx_hash = 0xc8,      // 200
+    outpoint_index = 0xc9,        // 201
+    input_bytecode = 0xca,        // 202
+    input_sequence_number = 0xcb, // 203
+    output_value = 0xcc,          // 204
+    output_bytecode = 0xcd,       // 205
+
+// Native Introspection of tokens (SCRIPT_ENABLE_TOKENS must be set)
+    utxo_token_category = 0xce,     // 206
+    utxo_token_commitment = 0xcf,   // 207
+    utxo_token_amount = 0xd0,       // 208
+    output_token_category = 0xd1,   // 209
+    output_token_commitment = 0xd2, // 210
+    output_token_amount = 0xd3,     // 211
+
+    reserved_212 = 0xd4,          // 212
+    reserved_213 = 0xd5,          // 213
+    reserved_214 = 0xd6,          // 214
+
+// The first op_code value after all defined opcodes
+    first_undefined_op_value = reserved_214, // 0xd6 214
+
+    reserved_215 = 0xd7,          // 215
+    reserved_216 = 0xd8,          // 216
+    reserved_217 = 0xd9,          // 217
+    reserved_218 = 0xda,          // 218
+    reserved_219 = 0xdb,          // 219
+    reserved_220 = 0xdc,          // 220
+    reserved_221 = 0xdd,          // 221
+    reserved_222 = 0xde,          // 222
+    reserved_223 = 0xdf,          // 223
+    reserved_224 = 0xe0,          // 224
+    reserved_225 = 0xe1,          // 225
+    reserved_226 = 0xe2,          // 226
+    reserved_227 = 0xe3,          // 227
+    reserved_228 = 0xe4,          // 228
+    reserved_229 = 0xe5,          // 229
+    reserved_230 = 0xe6,          // 230
+    reserved_231 = 0xe7,          // 231
+    reserved_232 = 0xe8,          // 232
+    reserved_233 = 0xe9,          // 233
+    reserved_234 = 0xea,          // 234
+    reserved_235 = 0xeb,          // 235
+    reserved_236 = 0xec,          // 236
+    reserved_237 = 0xed,          // 237
+    reserved_238 = 0xee,          // 238
+    reserved_239 = 0xef,          // 239
+
+// Invalid opcode if executed, but used for special token prefix if at
+// position 0 in scriptPubKey. See: primitives/token.h
+    special_token_prefix = reserved_239, // 0xef 239
+
+    reserved_240 = 0xf0,        // 240
+    reserved_241 = 0xf1,        // 241
+    reserved_242 = 0xf2,        // 242
+    reserved_243 = 0xf3,        // 243
+    reserved_244 = 0xf4,        // 244
+    reserved_245 = 0xf5,        // 245
+    reserved_246 = 0xf6,        // 246
+    reserved_247 = 0xf7,        // 247
+    reserved_248 = 0xf8,        // 248
+    reserved_249 = 0xf9,        // 249
+    reserved_250 = 0xfa,        // 250
+    reserved_251 = 0xfb,        // 251
+    reserved_252 = 0xfc,        // 252
+    reserved_253 = 0xfd,        // 253
+    reserved_254 = 0xfe,        // 254
+    reserved_255 = 0xff,        // 255
+
+    invalidopcode = reserved_255,  //  0xff 255  < Not a real OPCODE!
 };
 
 /// Convert the opcode to a mnemonic string.
diff --git a/include/kth/domain/machine/operation.hpp b/include/kth/domain/machine/operation.hpp
index cb95a1e6d..a88d8ab72 100644
--- a/include/kth/domain/machine/operation.hpp
+++ b/include/kth/domain/machine/operation.hpp
@@ -30,7 +30,8 @@ namespace kth::domain::machine {
 
 //TODO(fernando): static?
 constexpr
-auto invalid_code = opcode::disabled_xor;
+// auto invalid_code = opcode::disabled_xor;
+auto invalid_code = opcode::invalidopcode;
 
 class KD_API operation {
 public:
@@ -179,7 +180,7 @@ class KD_API operation {
     bool is_reserved(opcode code);
 
     static
-    bool is_disabled(opcode code);
+    bool is_disabled(opcode code, uint32_t active_forks);
 
     static
     bool is_conditional(opcode code);
@@ -201,7 +202,7 @@ class KD_API operation {
     bool is_positive() const;
 
     [[nodiscard]]
-    bool is_disabled() const;
+    bool is_disabled(uint32_t active_forks) const;
 
     [[nodiscard]]
     bool is_conditional() const;
@@ -210,7 +211,7 @@ class KD_API operation {
     bool is_relaxed_push() const;
 
     [[nodiscard]]
-    bool is_oversized() const;
+    bool is_oversized(size_t max_size) const;
 
     [[nodiscard]]
     bool is_minimal_push() const;
diff --git a/include/kth/domain/machine/program.hpp b/include/kth/domain/machine/program.hpp
index ff003487a..43f951c0e 100644
--- a/include/kth/domain/machine/program.hpp
+++ b/include/kth/domain/machine/program.hpp
@@ -11,6 +11,7 @@
 #include <kth/domain/chain/transaction.hpp>
 #include <kth/domain/constants.hpp>
 #include <kth/domain/define.hpp>
+#include <kth/domain/machine/metrics.hpp>
 #include <kth/domain/machine/opcode.hpp>
 #include <kth/domain/machine/operation.hpp>
 #include <kth/infrastructure/machine/number.hpp>
@@ -28,12 +29,6 @@ class KD_API program {
     using value_type = data_stack::value_type;
     using op_iterator = operation::iterator;
 
-    //TODO(fernando): check this comment
-    // Older libstdc++ does not allow erase with const iterator.
-    // This is a bug that requires we up the minimum compiler version.
-    // So presently stack_iterator is a non-const iterator.
-    ////using stack_iterator = data_stack::const_iterator;
-    // using stack_iterator = data_stack::iterator;
     using stack_iterator = data_stack::const_iterator;
     using stack_mutable_iterator = data_stack::iterator;
 
@@ -59,6 +54,9 @@ class KD_API program {
     /// Create using copied tx, input, forks, value and moved stack (p2sh run).
     program(chain::script const& script, program&& x, bool move);
 
+    metrics& get_metrics();
+    metrics const& get_metrics() const;
+
     /// Constant registers.
     [[nodiscard]]
     bool is_valid() const;
@@ -66,6 +64,15 @@ class KD_API program {
     [[nodiscard]]
     uint32_t forks() const;
 
+    [[nodiscard]]
+    size_t max_script_element_size() const;
+
+    [[nodiscard]]
+    size_t max_integer_size_legacy() const;
+
+    [[nodiscard]]
+    bool is_chip_vm_limits_enabled() const;
+
     [[nodiscard]]
     uint32_t input_index() const;
 
@@ -109,7 +116,8 @@ class KD_API program {
     /// Primary pop.
     data_chunk pop();
     bool pop(int32_t& out_value);
-    bool pop(number& out_number, size_t maxiumum_size = max_number_size);
+    bool pop(int64_t& out_value);
+    bool pop(number& out_number, size_t maximum_size);
     bool pop_binary(number& first, number& second);
     bool pop_ternary(number& first, number& second, number& third);
     bool pop_position(stack_iterator& out_position);
@@ -145,19 +153,27 @@ class KD_API program {
 
     value_type& item(size_t index);
 
-    bool top(number& out_number, size_t maxiumum_size = max_number_size) const;
+    data_chunk const& top() const;
+    data_chunk& top();
+    bool top(number& out_number, size_t maximum_size) const;
 
     [[nodiscard]]
     stack_iterator position(size_t index) const;
 
     stack_mutable_iterator position(size_t index);
 
+    [[nodiscard]]
+    size_t index(stack_iterator const& position) const;
+
     [[nodiscard]]
     operation::list subscript() const;
 
     [[nodiscard]]
     size_t size() const;
 
+    [[nodiscard]]
+    size_t conditional_stack_size() const;
+
     // Alternate stack.
     //-------------------------------------------------------------------------
 
@@ -202,6 +218,8 @@ class KD_API program {
     data_stack primary_;
     data_stack alternate_;
     bool_stack condition_;
+
+    metrics metrics_;
 };
 
 } // namespace kth::domain::machine
diff --git a/include/kth/domain/machine/script_limits.hpp b/include/kth/domain/machine/script_limits.hpp
new file mode 100644
index 000000000..803466fc2
--- /dev/null
+++ b/include/kth/domain/machine/script_limits.hpp
@@ -0,0 +1,93 @@
+// Copyright (c) 2016-2024 Knuth Project developers.
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef KTH_DOMAIN_MACHINE_SCRIPT_LIMITS_HPP
+#define KTH_DOMAIN_MACHINE_SCRIPT_LIMITS_HPP
+
+#include <cassert>
+#include <cstdint>
+
+#include <kth/domain/define.hpp>
+
+namespace kth::domain::machine {
+
+namespace may2025 {
+
+// Some constants used by helper code.
+namespace detail {
+
+/// Bonus factor (7x) applied to hash iteration limits for non-standard transactions (block transactions).
+constexpr unsigned int hash_iter_bonus_nonstd = 7u;
+
+/// Multiplier for determining the total operational cost allowance per input byte.
+constexpr unsigned int op_cost_per_input_byte = 800u;
+
+/// Penalty factor for standard transactions, where hash operations cost 3x more.
+constexpr unsigned int hash_cost_penalty_std = 3u;
+
+/// Block size used by all supported hash operations (e.g., OP_HASH160, OP_HASH256).
+constexpr unsigned int hash_block_size = 64u;
+
+/// Fixed serialization overhead in bytes credited to each input script, as specified in the VM Limits CHIP.
+constexpr unsigned int input_script_fixed_credit = 41u;
+
+/// Calculates the maximum hash iteration limit for a given input.
+/// The limit depends on whether standard rules are applied and the scriptSig size.
+inline constexpr
+uint64_t calculate_input_hash_iters_limit(bool standard, uint64_t script_sig_size) noexcept {
+    auto const factor = standard ? 1u : detail::hash_iter_bonus_nonstd;
+    uint64_t const result = ((script_sig_size + detail::input_script_fixed_credit) * factor) / 2u;
+    assert(result >= 0);
+    return result;
+}
+
+/// Calculates the maximum operational cost for an input based on its scriptSig size.
+inline constexpr
+uint64_t calculate_input_op_cost_limit(uint64_t script_sig_size) noexcept {
+    uint64_t const result = (script_sig_size + detail::input_script_fixed_credit) * detail::op_cost_per_input_byte;
+    assert(result >= 0);
+    return result;
+}
+
+} // namespace detail
+
+/// Determines the cost factor for hash iterations based on whether standard rules apply.
+/// Returns 64 for non-standard transactions, or 192 for standard transactions.
+// See: https://github.com/bitjson/bch-vm-limits/tree/master?tab=readme-ov-file#summary
+inline constexpr
+uint64_t hash_iter_op_cost_factor(bool standard) noexcept {
+    return standard ?
+        detail::hash_block_size * detail::hash_cost_penalty_std :
+        detail::hash_block_size;
+}
+
+/// Calculates the number of hash iterations for a given message length and hash operation type.
+/// Supports one-round and two-round hash operations.
+inline constexpr
+uint64_t calculate_hash_iters(uint32_t message_length, bool is_two_round_hash) noexcept {
+    return is_two_round_hash + 1u + ((uint64_t(message_length) + 8u) / detail::hash_block_size);
+}
+
+/// Defines the execution limits for the virtual machine (VM) when running a specific script.
+/// These limits are calculated based on the scriptSig size and whether the script is executed
+/// in standard or non-standard mode. The class provides a structured way to enforce these limits
+/// during script evaluation.
+struct KD_API script_limits {
+    script_limits(bool standard, uint64_t script_sig_size)
+        : op_cost_limit_{detail::calculate_input_op_cost_limit(script_sig_size)}
+        , hash_iters_limit_{detail::calculate_input_hash_iters_limit(standard, script_sig_size)}
+    {}
+
+    uint64_t op_cost_limit() const { return op_cost_limit_; }
+    uint64_t hash_iters_limit() const { return hash_iters_limit_; }
+
+private:
+    uint64_t op_cost_limit_;
+    uint64_t hash_iters_limit_;
+};
+
+} // namespace may2025
+} // namespace kth::domain::machine
+
+#endif // KTH_DOMAIN_MACHINE_SCRIPT_LIMITS_HPP
diff --git a/src/chain/script.cpp b/src/chain/script.cpp
index 2a17ea9f8..088ca90b4 100644
--- a/src/chain/script.cpp
+++ b/src/chain/script.cpp
@@ -305,13 +305,13 @@ operation::list const& script::operations() const {
 //-----------------------------------------------------------------------------
 
 inline
-hash_digest signature_hash(transaction const& tx, uint32_t sighash_type) {
+std::pair<hash_digest, size_t> signature_hash(transaction const& tx, uint32_t sighash_type) {
     // There is no rational interpretation of a signature hash for a coinbase.
     KTH_ASSERT( ! tx.is_coinbase());
 
     auto serialized = tx.to_data(true, false);
     extend_data(serialized, to_little_endian(sighash_type));
-    return bitcoin_hash(serialized);
+    return {bitcoin_hash(serialized), serialized.size()};
 }
 
 //*****************************************************************************
@@ -339,7 +339,7 @@ uint8_t is_sighash_enum(uint8_t sighash_type, sighash_algorithm value) {
 }
 
 static
-hash_digest sign_none(transaction const& tx, uint32_t input_index, script const& script_code, uint8_t sighash_type) {
+std::pair<hash_digest, size_t> sign_none(transaction const& tx, uint32_t input_index, script const& script_code, uint8_t sighash_type) {
     input::list ins;
     auto const& inputs = tx.inputs();
     auto const any = (sighash_type & sighash_algorithm::anyone_can_pay) != 0;
@@ -367,7 +367,7 @@ hash_digest sign_none(transaction const& tx, uint32_t input_index, script const&
 }
 
 static
-hash_digest sign_single(transaction const& tx, uint32_t input_index, script const& script_code, uint8_t sighash_type) {
+std::pair<hash_digest, size_t> sign_single(transaction const& tx, uint32_t input_index, script const& script_code, uint8_t sighash_type) {
     input::list ins;
     auto const& inputs = tx.inputs();
     auto const any = (sighash_type & sighash_algorithm::anyone_can_pay) != 0;
@@ -404,7 +404,7 @@ hash_digest sign_single(transaction const& tx, uint32_t input_index, script cons
 }
 
 static
-hash_digest sign_all(transaction const& tx, uint32_t input_index, script const& script_code, uint8_t sighash_type) {
+std::pair<hash_digest, size_t> sign_all(transaction const& tx, uint32_t input_index, script const& script_code, uint8_t sighash_type) {
     input::list ins;
     auto const& inputs = tx.inputs();
     auto const any = (sighash_type & sighash_algorithm::anyone_can_pay) != 0;
@@ -448,7 +448,7 @@ script strip_code_seperators(script const& script_code) {
 }
 
 // private/static
-hash_digest script::generate_unversioned_signature_hash(transaction const& tx,
+std::pair<hash_digest, size_t> script::generate_unversioned_signature_hash(transaction const& tx,
                                                         uint32_t input_index,
                                                         script const& script_code,
                                                         uint8_t sighash_type) {
@@ -458,7 +458,7 @@ hash_digest script::generate_unversioned_signature_hash(transaction const& tx,
         //*********************************************************************
         // CONSENSUS: wacky satoshi behavior.
         //*********************************************************************
-        return one_hash;
+        return {one_hash, 0}; // zero hashed bytes
     }
 
     //*************************************************************************
@@ -553,7 +553,7 @@ size_t preimage_size(size_t script_size) {
 }
 
 // private/static
-hash_digest script::generate_version_0_signature_hash(transaction const& tx,
+std::pair<hash_digest, size_t> script::generate_version_0_signature_hash(transaction const& tx,
                                                       uint32_t input_index,
                                                       script const& script_code,
                                                       uint64_t value,
@@ -621,14 +621,14 @@ hash_digest script::generate_version_0_signature_hash(transaction const& tx,
 
     ostream.flush();
     KTH_ASSERT(data.size() == size);
-    return bitcoin_hash(data);
+    return {bitcoin_hash(data), data.size()};
 }
 
 // Signing (common).
 //-----------------------------------------------------------------------------
 
 // static
-hash_digest script::generate_signature_hash(transaction const& tx,
+std::pair<hash_digest, size_t> script::generate_signature_hash(transaction const& tx,
                                             uint32_t input_index,
                                             script const& script_code,
                                             uint8_t sighash_type,
@@ -648,7 +648,7 @@ hash_digest script::generate_signature_hash(transaction const& tx,
 }
 
 // static
-bool script::check_signature(ec_signature const& signature,
+std::pair<bool, size_t> script::check_signature(ec_signature const& signature,
                              uint8_t sighash_type,
                              data_chunk const& public_key,
                              script const& script_code,
@@ -657,15 +657,19 @@ bool script::check_signature(ec_signature const& signature,
                              script_version version,
                              uint64_t value) {
     if (public_key.empty()) {
-        return false;
+        return {false, 0};
     }
 
     // This always produces a valid signature hash, including one_hash.
-    auto const sighash = chain::script::generate_signature_hash(tx,
-                                                                input_index, script_code, sighash_type, version, value);
+    auto const [sighash, size] = chain::script::generate_signature_hash(tx,
+                                                                input_index,
+                                                                script_code,
+                                                                sighash_type,
+                                                                version,
+                                                                value);
 
     // Validate the EC signature.
-    return verify_signature(public_key, sighash, signature);
+    return {verify_signature(public_key, sighash, signature), size};
 }
 
 // static
@@ -673,7 +677,7 @@ bool script::create_endorsement(endorsement& out, ec_secret const& secret, scrip
     out.reserve(max_endorsement_size);
 
     // This always produces a valid signature hash, including one_hash.
-    auto const sighash = chain::script::generate_signature_hash(tx, input_index, prevout_script, sighash_type, version, value);
+    auto const [sighash, size] = chain::script::generate_signature_hash(tx, input_index, prevout_script, sighash_type, version, value);
 
     // Create the EC signature and encode as DER.
     ec_signature signature;
@@ -1098,104 +1102,61 @@ bool script::is_unspendable() const {
 // Validation.
 //-----------------------------------------------------------------------------
 
-// #if ! defined(KTH_SEGWIT_ENABLED)
-// code script::verify(transaction const& tx, uint32_t input_index, uint32_t forks, script const& input_script, script const& prevout_script, uint64_t /*value*/) {
-// #else
-// code script::verify(transaction const& tx, uint32_t input_index, uint32_t forks, script const& input_script, witness const& input_witness, script const& prevout_script, uint64_t value) {
-// #endif
-//     code ec;
-
-//     // Evaluate input script.
-//     program input(input_script, tx, input_index, forks);
-//     if ((ec = input.evaluate())) {
-//         return ec;
-//     }
+code script::verify(transaction const& tx, uint32_t input_index, uint32_t forks, script const& input_script, script const& prevout_script, uint64_t /*value*/) {
+    code ec;
 
-//     // Evaluate output script using stack result from input script.
-//     program prevout(prevout_script, input);
-//     if ((ec = prevout.evaluate())) {
-//         return ec;
-//     }
+    // Evaluate input script.
+    program input(input_script, tx, input_index, forks);
+    if ((ec = input.evaluate())) {
+        return ec;
+    }
 
-//     // This precludes bare witness programs of -0 (undocumented).
-//     if ( ! prevout.stack_result(false)) {
-//         return error::stack_false;
-//     }
+    // Evaluate output script using stack result from input script.
+    program prevout(prevout_script, input);
+    if ((ec = prevout.evaluate())) {
+        return ec;
+    }
 
-// #if defined(KTH_SEGWIT_ENABLED)
-//     bool witnessed;
-//     // Triggered by output script push of version and witness program (bip141).
-//     if ((witnessed = prevout_script.is_pay_to_witness(forks))) {
-//         // The input script must be empty (bip141).
-//         if ( ! input_script.empty()) {
-//             return error::dirty_witness;
-//         }
-
-//         // This is a valid witness script so validate it.
-//         if ((ec = input_witness.verify(tx, input_index, forks, prevout_script, value))) {
-//             return ec;
-//         }
-//     } else
-// #endif
-//     // p2sh and p2w are mutually exclusive.
-//     /*else*/
-//     if (prevout_script.is_pay_to_script_hash(forks)) {
-//         if ( ! is_relaxed_push(input_script.operations())) {
-//             return error::invalid_script_embed;
-//         }
-
-//         // Embedded script must be at the top of the stack (bip16).
-//         script embedded_script(input.pop(), false);
-
-//         program embedded(embedded_script, std::move(input), true);
-//         if ((ec = embedded.evaluate())) {
-//             return ec;
-//         }
-
-//         // This precludes embedded witness programs of -0 (undocumented).
-//         if ( ! embedded.stack_result(false)) {
-//             return error::stack_false;
-//         }
-
-// #if defined(KTH_SEGWIT_ENABLED)
-//         // Triggered by embedded push of version and witness program (bip141).
-//         if ((witnessed = embedded_script.is_pay_to_witness(forks))) {
-//             // The input script must be a push of the embedded_script (bip141).
-//             if (input_script.size() != 1) {
-//                 return error::dirty_witness;
-//             }
-
-//             // This is a valid embedded witness script so validate it.
-//             if ((ec = input_witness.verify(tx, input_index, forks, embedded_script, value))) {
-//                 return ec;
-//             }
-//         }
-// #endif
-//     }
+    // This precludes bare witness programs of -0 (undocumented).
+    if ( ! prevout.stack_result(false)) {
+        return error::stack_false;
+    }
 
-// #if defined(KTH_SEGWIT_ENABLED)
-//     // Witness must be empty if no bip141 or valid witness program (bip141).
-//     if ( ! witnessed && !input_witness.empty()) {
-//         return error::unexpected_witness;
-//     }
-// #endif
+    if (prevout_script.is_pay_to_script_hash(forks)) {
+        if ( ! is_relaxed_push(input_script.operations())) {
+            return error::invalid_script_embed;
+        }
 
-//     return error::success;
-// }
+        // Embedded script must be at the top of the stack (bip16).
+        script embedded_script(input.pop(), false);
 
-// code script::verify(transaction const& tx, uint32_t input, uint32_t forks) {
-//     if (input >= tx.inputs().size()) {
-//         return error::operation_failed;
-//     }
+        program embedded(embedded_script, std::move(input), true);
+        if ((ec = embedded.evaluate())) {
+            return ec;
+        }
 
-//     auto const& in = tx.inputs()[input];
-//     auto const& prevout = in.previous_output().validation.cache;
+        // This precludes embedded witness programs of -0 (undocumented).
+        if ( ! embedded.stack_result(false)) {
+            return error::stack_false;
+        }
+    }
 
-// #if ! defined(KTH_SEGWIT_ENABLED)
-//     return verify(tx, input, forks, in.script(), prevout.script(), prevout.value());
-// #else
-//     return verify(tx, input, forks, in.script(), in.witness(), prevout.script(), prevout.value());
-// #endif
-// }
+    return error::success;
+}
+
+code script::verify(transaction const& tx, uint32_t input, uint32_t forks) {
+    if (input >= tx.inputs().size()) {
+        return error::operation_failed;
+    }
+
+    auto const& in = tx.inputs()[input];
+    auto const& prevout = in.previous_output().validation.cache;
+
+#if ! defined(KTH_SEGWIT_ENABLED)
+    return verify(tx, input, forks, in.script(), prevout.script(), prevout.value());
+#else
+    return verify(tx, input, forks, in.script(), in.witness(), prevout.script(), prevout.value());
+#endif
+}
 
 } // namespace kth::domain::chain
diff --git a/src/chain/script_basis.cpp b/src/chain/script_basis.cpp
index 25f25c500..e4339b6bb 100644
--- a/src/chain/script_basis.cpp
+++ b/src/chain/script_basis.cpp
@@ -243,7 +243,7 @@ size_t preimage_size(size_t script_size) {
 }
 
 // private/static
-hash_digest script_basis::generate_version_0_signature_hash(transaction const& tx,
+std::pair<hash_digest, size_t> script_basis::generate_version_0_signature_hash(transaction const& tx,
                                                       uint32_t input_index,
                                                       script_basis const& script_code,
                                                       uint64_t value,
@@ -310,7 +310,7 @@ hash_digest script_basis::generate_version_0_signature_hash(transaction const& t
 
     ostream.flush();
     KTH_ASSERT(data.size() == size);
-    return bitcoin_hash(data);
+    return {bitcoin_hash(data), data.size()};
 }
 
 // Utilities (static).
diff --git a/src/machine/interpreter.cpp b/src/machine/interpreter.cpp
index fdec83722..0641b8256 100644
--- a/src/machine/interpreter.cpp
+++ b/src/machine/interpreter.cpp
@@ -18,12 +18,30 @@ code interpreter::run(program& program) {
         return error::invalid_script;
     }
 
+    // BCHN code:
+    // bool const chipVmLimitsEnabled = (flags & SCRIPT_ENABLE_MAY2025) != 0;
+    // size_t const maxScriptElementSize = chipVmLimitsEnabled ? may2025::MAX_SCRIPT_ELEMENT_SIZE
+    //                                                         : MAX_SCRIPT_ELEMENT_SIZE_LEGACY;
+
+    // if (chipVmLimitsEnabled && !metrics.HasValidScriptLimits() && context) {
+    //     // Calculate metrics "scriptLimits", if not already passed-in, and if we have a `context` object
+    //     // from which to get the scriptSig size.
+    //     metrics.SetScriptLimits(flags, context->scriptSig().size());
+    // }
+
+    // TODO: flags vs forks
+    // if (program.is_chip_vm_limits_enabled() && !program.get_metrics().has_valid_script_limits() && context) {
+    //     // Calculate metrics "scriptLimits", if not already passed-in, and if we have a `context` object
+    //     // from which to get the scriptSig size.
+    //     program.get_metrics().set_script_limits(program.get_flags(), context->scriptSig().size());
+    // }
+
     for (auto const& op : program) {
-        if (op.is_oversized()) {
+        if (op.is_oversized(program.max_script_element_size())) {
             return error::invalid_push_data_size;
         }
 
-        if (op.is_disabled()) {
+        if (op.is_disabled(program.forks())) {
             return error::op_disabled;
         }
 
@@ -39,6 +57,40 @@ code interpreter::run(program& program) {
             if (program.is_stack_overflow()) {
                 return error::invalid_stack_size;
             }
+
+            // Enforce May 2025 VM limits
+            if (program.is_chip_vm_limits_enabled()) {
+                // // Check that this opcode did not cause us to exceed opCost and/or hashIters limits.
+                // // Note: `metrics` may lack a valid "scriptLimits" object in rare cases (tests only), in which case
+                // // the below two limit checks are always going to return false.
+
+                // if (metrics.IsOverOpCostLimit(flags)) {
+                //     return set_error(serror, ScriptError::OP_COST);
+                // }
+                // if (metrics.IsOverHashItersLimit()) {
+                //     return set_error(serror, ScriptError::TOO_MANY_HASH_ITERS);
+                // }
+
+                // // Conditional stack may not exceed depth of 100
+                // if (vfExec.size() > may2025::MAX_CONDITIONAL_STACK_DEPTH) {
+                //     return set_error(serror, ScriptError::CONDITIONAL_STACK_DEPTH);
+                // }
+
+
+                // TODO: flags vs forks
+                // if (program.get_metrics().is_over_op_cost_limit(program.get_flags())) {
+                //     return error::op_cost;
+                // }
+
+                if (program.get_metrics().is_over_hash_iters_limit()) {
+                    return error::too_many_hash_iters;
+                }
+
+                // Conditional stack may not exceed depth of 100
+                if (program.conditional_stack_size() > ::kth::may2025::max_conditional_stack_depth) {
+                    return error::conditional_stack_depth;
+                }
+            }
         }
     }
 
diff --git a/src/machine/opcode.cpp b/src/machine/opcode.cpp
index 757602579..f49176bbc 100644
--- a/src/machine/opcode.cpp
+++ b/src/machine/opcode.cpp
@@ -190,23 +190,23 @@ std::string opcode_to_string(opcode value, uint32_t active_forks) {
             return "swap";
         case opcode::tuck:
             return "tuck";
-        case opcode::disabled_cat:
+        case opcode::cat:
             return "cat";
-        case opcode::disabled_substr:
-            return "substr";
-        case opcode::disabled_left:
-            return "left";
-        case opcode::disabled_right:
-            return "right";
+        case opcode::split:
+            return "split";
+        case opcode::num2bin:
+            return "num2bin";
+        case opcode::bin2num:
+            return "bin2num";
         case opcode::size:
             return "size";
         case opcode::disabled_invert:
             return "invert";
-        case opcode::disabled_and:
+        case opcode::and_:
             return "and";
-        case opcode::disabled_or:
+        case opcode::or_:
             return "or";
-        case opcode::disabled_xor:
+        case opcode::xor_:
             return "xor";
         case opcode::equal:
             return "equal";
@@ -236,11 +236,11 @@ std::string opcode_to_string(opcode value, uint32_t active_forks) {
             return "add";
         case opcode::sub:
             return "sub";
-        case opcode::disabled_mul:
+        case opcode::mul:
             return "mul";
-        case opcode::disabled_div:
+        case opcode::div:
             return "div";
-        case opcode::disabled_mod:
+        case opcode::mod:
             return "mod";
         case opcode::disabled_lshift:
             return "lshift";
@@ -315,34 +315,61 @@ std::string opcode_to_string(opcode value, uint32_t active_forks) {
         case opcode::nop10:
             return "nop10";
 
-        //TODO(kth): Implement OP_CHECKDATASIG and OP_CHECKDATASIGVERIFY
-
-        case opcode::reserved_186:
-        case opcode::reserved_187:
-        case opcode::reserved_188:
-        case opcode::reserved_189:
-        case opcode::reserved_190:
-        case opcode::reserved_191:
-        case opcode::reserved_192:
-        case opcode::reserved_193:
-        case opcode::reserved_194:
-        case opcode::reserved_195:
-        case opcode::reserved_196:
-        case opcode::reserved_197:
-        case opcode::reserved_198:
-        case opcode::reserved_199:
-        case opcode::reserved_200:
-        case opcode::reserved_201:
-        case opcode::reserved_202:
-        case opcode::reserved_203:
-        case opcode::reserved_204:
-        case opcode::reserved_205:
-        case opcode::reserved_206:
-        case opcode::reserved_207:
-        case opcode::reserved_208:
-        case opcode::reserved_209:
-        case opcode::reserved_210:
-        case opcode::reserved_211:
+// more crypto
+        case opcode::checkdatasig:
+            return "checkdatasig";
+        case opcode::checkdatasigverify:
+            return "checkdatasigverify";
+
+// additional byte string operations
+        case opcode::reverse_bytes:
+            return "reverse_bytes";
+
+
+// Native Introspection opcodes
+        case opcode::input_index:
+            return "input_index";
+        case opcode::active_bytecode:
+            return "active_bytecode";
+        case opcode::tx_version:
+            return "tx_version";
+        case opcode::tx_input_count:
+            return "tx_input_count";
+        case opcode::tx_output_count:
+            return "tx_output_count";
+        case opcode::tx_locktime:
+            return "tx_locktime";
+        case opcode::utxo_value:
+            return "utxo_value";
+        case opcode::utxo_bytecode:
+            return "utxo_bytecode";
+        case opcode::outpoint_tx_hash:
+            return "outpoint_tx_hash";
+        case opcode::outpoint_index:
+            return "outpoint_index";
+        case opcode::input_bytecode:
+            return "input_bytecode";
+        case opcode::input_sequence_number:
+            return "input_sequence_number";
+        case opcode::output_value:
+            return "output_value";
+        case opcode::output_bytecode:
+            return "output_bytecode";
+
+// Native Introspection of tokens (SCRIPT_ENABLE_TOKENS must be set)
+        case opcode::utxo_token_category:
+            return "utxo_token_category";
+        case opcode::utxo_token_commitment:
+            return "utxo_token_commitment";
+        case opcode::utxo_token_amount:
+            return "utxo_token_amount";
+        case opcode::output_token_category:
+            return "output_token_category";
+        case opcode::output_token_commitment:
+            return "output_token_commitment";
+        case opcode::output_token_amount:
+            return "output_token_amount";
+
         case opcode::reserved_212:
         case opcode::reserved_213:
         case opcode::reserved_214:
@@ -525,19 +552,22 @@ bool opcode_from_string(opcode& out_code, std::string const& value) {       //NO
     RETURN_IF_OPCODE("rot", rot);
     RETURN_IF_OPCODE("swap", swap);
     RETURN_IF_OPCODE("tuck", tuck);
-    RETURN_IF_OPCODE("cat", disabled_cat);
-    RETURN_IF_OPCODE("substr", disabled_substr);
-    RETURN_IF_OPCODE("left", disabled_left);
-    RETURN_IF_OPCODE("right", disabled_right);
+
+    RETURN_IF_OPCODE("cat", cat);
+    RETURN_IF_OPCODE("split", split);       // was called substr before (disabled and re-enabled after pythagoras/monolith upgrade, May 2018)
+    RETURN_IF_OPCODE("num2bin", num2bin);   // was called left before (disabled and re-enabled after pythagoras/monolith upgrade, May 2018)
+    RETURN_IF_OPCODE("bin2num", bin2num);   // was called right before (disabled and re-enabled after pythagoras/monolith upgrade, May 2018)
     RETURN_IF_OPCODE("size", size);
+
     RETURN_IF_OPCODE("invert", disabled_invert);
-    RETURN_IF_OPCODE("and", disabled_and);
-    RETURN_IF_OPCODE("or", disabled_or);
-    RETURN_IF_OPCODE("xor", disabled_xor);
+    RETURN_IF_OPCODE("and", and_);          // disabled and re-enabled after pythagoras/monolith upgrade, May 2018
+    RETURN_IF_OPCODE("or", or_);            // disabled and re-enabled after pythagoras/monolith upgrade, May 2018
+    RETURN_IF_OPCODE("xor", xor_);          // disabled and re-enabled after pythagoras/monolith upgrade, May 2018
     RETURN_IF_OPCODE("equal", equal);
     RETURN_IF_OPCODE("equalverify", equalverify);
     RETURN_IF_OPCODE_OR_ALIAS("reserved_137", "reserved1", reserved_137);
     RETURN_IF_OPCODE_OR_ALIAS("reserved_138", "reserved2", reserved_138);
+
     RETURN_IF_OPCODE_OR_ALIAS("add1", "1add", add1);
     RETURN_IF_OPCODE_OR_ALIAS("sub1", "1sub", sub1);
     RETURN_IF_OPCODE_OR_ALIAS("mul2", "2mul", disabled_mul2);
@@ -546,13 +576,15 @@ bool opcode_from_string(opcode& out_code, std::string const& value) {       //NO
     RETURN_IF_OPCODE("abs", abs);
     RETURN_IF_OPCODE("not", not_);
     RETURN_IF_OPCODE_OR_ALIAS("nonzero", "0notequal", nonzero);
+
     RETURN_IF_OPCODE("add", add);
     RETURN_IF_OPCODE("sub", sub);
-    RETURN_IF_OPCODE("mul", disabled_mul);
-    RETURN_IF_OPCODE("div", disabled_div);
-    RETURN_IF_OPCODE("mod", disabled_mod);
+    RETURN_IF_OPCODE("mul", mul);
+    RETURN_IF_OPCODE("div", div);
+    RETURN_IF_OPCODE("mod", mod);
     RETURN_IF_OPCODE("lshift", disabled_lshift);
     RETURN_IF_OPCODE("rshift", disabled_rshift);
+
     RETURN_IF_OPCODE("booland", booland);
     RETURN_IF_OPCODE("boolor", boolor);
     RETURN_IF_OPCODE("numequal", numequal);
@@ -564,17 +596,21 @@ bool opcode_from_string(opcode& out_code, std::string const& value) {       //NO
     RETURN_IF_OPCODE("greaterthanorequal", greaterthanorequal);
     RETURN_IF_OPCODE("min", min);
     RETURN_IF_OPCODE("max", max);
+
     RETURN_IF_OPCODE("within", within);
+
     RETURN_IF_OPCODE("ripemd160", ripemd160);
     RETURN_IF_OPCODE("sha1", sha1);
     RETURN_IF_OPCODE("sha256", sha256);
     RETURN_IF_OPCODE("hash160", hash160);
     RETURN_IF_OPCODE("hash256", hash256);
+
     RETURN_IF_OPCODE("codeseparator", codeseparator);
     RETURN_IF_OPCODE("checksig", checksig);
     RETURN_IF_OPCODE("checksigverify", checksigverify);
     RETURN_IF_OPCODE("checkmultisig", checkmultisig);
     RETURN_IF_OPCODE("checkmultisigverify", checkmultisigverify);
+
     RETURN_IF_OPCODE("nop1", nop1);
     RETURN_IF_OPCODE_OR_ALIAS("checklocktimeverify", "nop2", checklocktimeverify);
     RETURN_IF_OPCODE_OR_ALIAS("checksequenceverify", "nop3", checksequenceverify);
@@ -585,32 +621,38 @@ bool opcode_from_string(opcode& out_code, std::string const& value) {       //NO
     RETURN_IF_OPCODE("nop8", nop8);
     RETURN_IF_OPCODE("nop9", nop9);
     RETURN_IF_OPCODE("nop10", nop10);
-    RETURN_IF_OPCODE("reserved_186", reserved_186);
-    RETURN_IF_OPCODE("reserved_187", reserved_187);
-    RETURN_IF_OPCODE("reserved_188", reserved_188);
-    RETURN_IF_OPCODE("reserved_189", reserved_189);
-    RETURN_IF_OPCODE("reserved_190", reserved_190);
-    RETURN_IF_OPCODE("reserved_191", reserved_191);
-    RETURN_IF_OPCODE("reserved_192", reserved_192);
-    RETURN_IF_OPCODE("reserved_193", reserved_193);
-    RETURN_IF_OPCODE("reserved_194", reserved_194);
-    RETURN_IF_OPCODE("reserved_195", reserved_195);
-    RETURN_IF_OPCODE("reserved_196", reserved_196);
-    RETURN_IF_OPCODE("reserved_197", reserved_197);
-    RETURN_IF_OPCODE("reserved_198", reserved_198);
-    RETURN_IF_OPCODE("reserved_199", reserved_199);
-    RETURN_IF_OPCODE("reserved_200", reserved_200);
-    RETURN_IF_OPCODE("reserved_201", reserved_201);
-    RETURN_IF_OPCODE("reserved_202", reserved_202);
-    RETURN_IF_OPCODE("reserved_203", reserved_203);
-    RETURN_IF_OPCODE("reserved_204", reserved_204);
-    RETURN_IF_OPCODE("reserved_205", reserved_205);
-    RETURN_IF_OPCODE("reserved_206", reserved_206);
-    RETURN_IF_OPCODE("reserved_207", reserved_207);
-    RETURN_IF_OPCODE("reserved_208", reserved_208);
-    RETURN_IF_OPCODE("reserved_209", reserved_209);
-    RETURN_IF_OPCODE("reserved_210", reserved_210);
-    RETURN_IF_OPCODE("reserved_211", reserved_211);
+
+// more crypto
+    RETURN_IF_OPCODE("checkdatasig", checkdatasig);
+    RETURN_IF_OPCODE("checkdatasigverify", checkdatasigverify);
+
+// additional byte string operations
+    RETURN_IF_OPCODE("reverse_bytes", reverse_bytes);
+
+// Native Introspection opcodes
+    RETURN_IF_OPCODE("input_index", input_index);
+    RETURN_IF_OPCODE("active_bytecode", active_bytecode);
+    RETURN_IF_OPCODE("tx_version", tx_version);
+    RETURN_IF_OPCODE("tx_input_count", tx_input_count);
+    RETURN_IF_OPCODE("tx_output_count", tx_output_count);
+    RETURN_IF_OPCODE("tx_locktime", tx_locktime);
+    RETURN_IF_OPCODE("utxo_value", utxo_value);
+    RETURN_IF_OPCODE("utxo_bytecode", utxo_bytecode);
+    RETURN_IF_OPCODE("outpoint_tx_hash", outpoint_tx_hash);
+    RETURN_IF_OPCODE("outpoint_index", outpoint_index);
+    RETURN_IF_OPCODE("input_bytecode", input_bytecode);
+    RETURN_IF_OPCODE("input_sequence_number", input_sequence_number);
+    RETURN_IF_OPCODE("output_value", output_value);
+    RETURN_IF_OPCODE("output_bytecode", output_bytecode);
+
+// Native Introspection of tokens (SCRIPT_ENABLE_TOKENS must be set)
+    RETURN_IF_OPCODE("utxo_token_category", utxo_token_category);
+    RETURN_IF_OPCODE("utxo_token_commitment", utxo_token_commitment);
+    RETURN_IF_OPCODE("utxo_token_amount", utxo_token_amount);
+    RETURN_IF_OPCODE("output_token_category", output_token_category);
+    RETURN_IF_OPCODE("output_token_commitment", output_token_commitment);
+    RETURN_IF_OPCODE("output_token_amount", output_token_amount);
+
     RETURN_IF_OPCODE("reserved_212", reserved_212);
     RETURN_IF_OPCODE("reserved_213", reserved_213);
     RETURN_IF_OPCODE("reserved_214", reserved_214);
diff --git a/test/machine/operation.cpp b/test/machine/operation.cpp
index 1aa72ec93..3b21668a8 100644
--- a/test/machine/operation.cpp
+++ b/test/machine/operation.cpp
@@ -17,7 +17,8 @@ TEST_CASE("operation  constructor 1  always  returns default initialized", "[ope
 
     REQUIRE( ! instance.is_valid());
     REQUIRE(instance.data().empty());
-    REQUIRE(instance.code() == opcode::disabled_xor);
+    // REQUIRE(instance.code() == opcode::disabled_xor);
+    REQUIRE(instance.code() == opcode::invalidopcode);
 }
 
 TEST_CASE("operation  constructor 2  valid input  returns input initialized", "[operation]") {