LCOV - code coverage report
Current view: top level - crypto/utxo/entities - script.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 70.8 % 72 51
Test Date: 2025-06-07 01:20:49 Functions: - 0 0

            Line data    Source code
       1              : import 'dart:typed_data';
       2              : 
       3              : import 'package:walletkit_dart/src/crypto/utxo/entities/op_codes.dart';
       4              : import 'package:walletkit_dart/src/domain/constants.dart';
       5              : import 'package:walletkit_dart/src/utils/int.dart';
       6              : import 'package:walletkit_dart/src/utils/var_uint.dart';
       7              : 
       8              : class Script {
       9              :   final Uint8List bytes;
      10              : 
      11            2 :   const Script(this.bytes);
      12              : 
      13            2 :   List<ScriptChunk> get chunks {
      14            2 :     List<ScriptChunk> chunks = [];
      15              :     int offset = 0;
      16              : 
      17            6 :     while (offset < bytes.length) {
      18            4 :       int opcode = bytes[offset];
      19            2 :       offset += 1;
      20              :       int? dataToRead;
      21              : 
      22            4 :       if (opcode >= 0 && opcode <= OP_PUSHDATA1) {
      23              :         // The opcode value itself is length of data
      24              :         dataToRead = opcode;
      25            2 :       } else if (opcode == OP_PUSHDATA1) {
      26              :         // Next byte is length of data
      27            0 :         if (bytes.length - offset < 1) throw Exception('Unexpected end of script');
      28            0 :         dataToRead = bytes.bytes.readUint8(offset).$1;
      29            0 :         offset += 1;
      30            2 :       } else if (opcode == OP_PUSHDATA2) {
      31              :         // Next two bytes are length of data
      32            0 :         if (bytes.length - offset < 2) throw Exception('Unexpected end of script');
      33            0 :         dataToRead = bytes.bytes.readUint16(offset).$1;
      34            0 :         offset += 2;
      35            2 :       } else if (opcode == OP_PUSHDATA4) {
      36              :         // Next four bytes are length of data
      37            0 :         if (bytes.length - offset < 4) throw Exception('Unexpected end of script');
      38            0 :         dataToRead = bytes.bytes.readUint32(offset).$1;
      39            0 :         offset += 4;
      40              :       }
      41              : 
      42              :       final ScriptChunk chunk;
      43              :       if (dataToRead == null) {
      44            2 :         chunk = ScriptChunk(opcode, null);
      45              :       } else {
      46            8 :         if (dataToRead > bytes.length - offset)
      47            0 :           throw Exception(
      48            0 :               'Push of data element that is larger than remaining data: $dataToRead vs ${bytes.length - offset} ');
      49            6 :         final data = bytes.sublist(offset, offset + dataToRead);
      50            2 :         chunk = ScriptChunk(opcode, data);
      51            2 :         offset += dataToRead;
      52              :       }
      53              : 
      54            2 :       chunks.add(chunk);
      55              :     }
      56            6 :     if (offset != bytes.length) throw Exception('Script parsing error');
      57              :     return chunks;
      58              :   }
      59              : }
      60              : 
      61              : class ScriptChunk {
      62              :   final int opcode;
      63              :   final Uint8List? data;
      64              : 
      65            2 :   const ScriptChunk(this.opcode, this.data);
      66              : }
      67              : 
      68            2 : BigInt getScriptWeight(Uint8List scriptBytes) {
      69            4 :   final scriptChunks = Script(scriptBytes).chunks;
      70              : 
      71            2 :   BigInt w = BigInt.zero;
      72              :   bool dataMode = false;
      73            2 :   int lastOpcode = -1; // OP_INVALIDOPCODE
      74            4 :   for (ScriptChunk scriptChunk in scriptChunks) {
      75           10 :     if (scriptChunk.data != null) w += scriptChunk.data!.length.toBI;
      76              : 
      77              :     if (dataMode) {
      78            0 :       w += 5.toBI;
      79              :       continue;
      80              :     }
      81              : 
      82            2 :     final opcode = scriptChunk.opcode;
      83              : 
      84              :     switch (opcode) {
      85            2 :       case OP_RETURN:
      86              :         dataMode = true;
      87            0 :         w += 5.toBI;
      88              :         break;
      89            2 :       case OP_RIPEMD160:
      90            2 :       case OP_SHA1:
      91            2 :       case OP_SHA256:
      92            2 :       case OP_HASH160:
      93            2 :       case OP_HASH256:
      94            4 :         w += 10.toBI;
      95              :         break;
      96            2 :       case OP_CODESEPARATOR:
      97            0 :         w += 10.toBI;
      98              :         break;
      99            2 :       case OP_CHECKSIG:
     100            2 :       case OP_CHECKSIGVERIFY:
     101            4 :         w += 100.toBI;
     102              :         break;
     103            2 :       case OP_CHECKMULTISIG:
     104            2 :       case OP_CHECKMULTISIGVERIFY:
     105            0 :         if (lastOpcode >= OP_1 && lastOpcode <= OP_16)
     106            0 :           w += (100 * decodeOP_N(lastOpcode)).toBI;
     107              :         else
     108            0 :           w += (100 * MAX_PUBKEYS_PER_MULTISIG).toBI;
     109              :         break;
     110            2 :       case OP_RESERVED1:
     111            2 :       case OP_RESERVED2:
     112            2 :       case OP_NOP1:
     113            2 :       case OP_NOP2:
     114            2 :       case OP_NOP3:
     115            2 :       case OP_NOP4:
     116            2 :       case OP_NOP5:
     117            2 :       case OP_NOP6:
     118            2 :       case OP_NOP7:
     119            2 :       case OP_NOP8:
     120            2 :       case OP_NOP9:
     121            2 :       case OP_NOP10:
     122            0 :         w += 10000.toBI;
     123              :         break;
     124              :       // case OP_INVALIDOPCODE:
     125              :       //   w += 100000.toBI;
     126              :       //   break;
     127              : 
     128              :       default:
     129            4 :         w += 5.toBI;
     130              :         break;
     131              :     }
     132              :     lastOpcode = opcode;
     133              :   }
     134              : 
     135            0 :   if (dataMode) w *= 20.toBI;
     136              : 
     137              :   return w;
     138              : }
     139              : 
     140            0 : int decodeOP_N(int opcode) {
     141              :   // Subtracting opcode by (OpCode.OP_1 - 1) to get the numeric value
     142            0 :   return opcode - (OPCODE.OP_1.hex - 1);
     143              : }
        

Generated by: LCOV version 2.0-1