LCOV - code coverage report
Current view: top level - crypto/utxo/entities - script.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 68.0 % 75 51
Test Date: 2025-01-30 01:10:00 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)
      28            0 :           throw Exception('Unexpected end of script');
      29            0 :         dataToRead = bytes.bytes.readUint8(offset).$1;
      30            0 :         offset += 1;
      31            2 :       } else if (opcode == OP_PUSHDATA2) {
      32              :         // Next two bytes are length of data
      33            0 :         if (bytes.length - offset < 2)
      34            0 :           throw Exception('Unexpected end of script');
      35            0 :         dataToRead = bytes.bytes.readUint16(offset).$1;
      36            0 :         offset += 2;
      37            2 :       } else if (opcode == OP_PUSHDATA4) {
      38              :         // Next four bytes are length of data
      39            0 :         if (bytes.length - offset < 4)
      40            0 :           throw Exception('Unexpected end of script');
      41            0 :         dataToRead = bytes.bytes.readUint32(offset).$1;
      42            0 :         offset += 4;
      43              :       }
      44              : 
      45              :       final ScriptChunk chunk;
      46              :       if (dataToRead == null) {
      47            2 :         chunk = ScriptChunk(opcode, null);
      48              :       } else {
      49            8 :         if (dataToRead > bytes.length - offset)
      50            0 :           throw Exception(
      51            0 :               'Push of data element that is larger than remaining data: $dataToRead vs ${bytes.length - offset} ');
      52            6 :         final data = bytes.sublist(offset, offset + dataToRead);
      53            2 :         chunk = ScriptChunk(opcode, data);
      54            2 :         offset += dataToRead;
      55              :       }
      56              : 
      57            2 :       chunks.add(chunk);
      58              :     }
      59            6 :     if (offset != bytes.length) throw Exception('Script parsing error');
      60              :     return chunks;
      61              :   }
      62              : }
      63              : 
      64              : class ScriptChunk {
      65              :   final int opcode;
      66              :   final Uint8List? data;
      67              : 
      68            2 :   const ScriptChunk(this.opcode, this.data);
      69              : }
      70              : 
      71            2 : BigInt getScriptWeight(Uint8List scriptBytes) {
      72            4 :   final scriptChunks = Script(scriptBytes).chunks;
      73              : 
      74            2 :   BigInt w = BigInt.zero;
      75              :   bool dataMode = false;
      76            2 :   int lastOpcode = -1; // OP_INVALIDOPCODE
      77            4 :   for (ScriptChunk scriptChunk in scriptChunks) {
      78           10 :     if (scriptChunk.data != null) w += scriptChunk.data!.length.toBI;
      79              : 
      80              :     if (dataMode) {
      81            0 :       w += 5.toBI;
      82              :       continue;
      83              :     }
      84              : 
      85            2 :     final opcode = scriptChunk.opcode;
      86              : 
      87              :     switch (opcode) {
      88            2 :       case OP_RETURN:
      89              :         dataMode = true;
      90            0 :         w += 5.toBI;
      91              :         break;
      92            2 :       case OP_RIPEMD160:
      93            2 :       case OP_SHA1:
      94            2 :       case OP_SHA256:
      95            2 :       case OP_HASH160:
      96            2 :       case OP_HASH256:
      97            4 :         w += 10.toBI;
      98              :         break;
      99            2 :       case OP_CODESEPARATOR:
     100            0 :         w += 10.toBI;
     101              :         break;
     102            2 :       case OP_CHECKSIG:
     103            2 :       case OP_CHECKSIGVERIFY:
     104            4 :         w += 100.toBI;
     105              :         break;
     106            2 :       case OP_CHECKMULTISIG:
     107            2 :       case OP_CHECKMULTISIGVERIFY:
     108            0 :         if (lastOpcode >= OP_1 && lastOpcode <= OP_16)
     109            0 :           w += (100 * decodeOP_N(lastOpcode)).toBI;
     110              :         else
     111            0 :           w += (100 * MAX_PUBKEYS_PER_MULTISIG).toBI;
     112              :         break;
     113            2 :       case OP_RESERVED1:
     114            2 :       case OP_RESERVED2:
     115            2 :       case OP_NOP1:
     116            2 :       case OP_NOP2:
     117            2 :       case OP_NOP3:
     118            2 :       case OP_NOP4:
     119            2 :       case OP_NOP5:
     120            2 :       case OP_NOP6:
     121            2 :       case OP_NOP7:
     122            2 :       case OP_NOP8:
     123            2 :       case OP_NOP9:
     124            2 :       case OP_NOP10:
     125            0 :         w += 10000.toBI;
     126              :         break;
     127              :       // case OP_INVALIDOPCODE:
     128              :       //   w += 100000.toBI;
     129              :       //   break;
     130              : 
     131              :       default:
     132            4 :         w += 5.toBI;
     133              :         break;
     134              :     }
     135              :     lastOpcode = opcode;
     136              :   }
     137              : 
     138            0 :   if (dataMode) w *= 20.toBI;
     139              : 
     140              :   return w;
     141              : }
     142              : 
     143            0 : int decodeOP_N(int opcode) {
     144              :   // Subtracting opcode by (OpCode.OP_1 - 1) to get the numeric value
     145            0 :   return opcode - (OPCODE.OP_1.hex - 1);
     146              : }
        

Generated by: LCOV version 2.0-1