Function extractEvaluationSamples

  • Extract a set of "evaluation samples" from the result of a CashAssembly compilation and a matching debug trace (from vm.debug), pairing program states with the source ranges that produced them – like a "source map" for complete evaluations. This is useful for omniscient debuggers like Bitauth IDE.

    Returns an array of samples and an array of unmatched program states remaining if nodes doesn't contain enough instructions to consume all program states provided in trace. Returned samples are ordered by the ending position (line and column) of their range.

    If all program states are consumed before the available nodes are exhausted, the remaining nodes are ignored (the produced samples end at the last instruction for which a program state exists). This usually occurs when an error halts evaluation before the end of the script. (Note: if this occurs, the final trace state will not be used, as it is expected to be the duplicated final result produced by vm.debug, and should not be matched with the next instruction. The returned unmatchedStates will have a length of 0.)

    This method allows for samples to be extracted from a single evaluation; most applications should use extractEvaluationSamplesRecursive instead.

    Type Parameters

    • ProgramState

    Parameters

    Returns SampleExtractionResult<ProgramState>

    Remarks

    This method incrementally concatenates the reduced bytecode from each node, parsing the result into evaluation samples.

    Each node can contain only a portion of an instruction (like a long push operation), or it can contain multiple instructions (like a long hex literal representing a string of bytecode or an evaluation that is not wrapped by a push).

    If a node contains only a portion of an instruction, the bytecode from additional nodes are concatenated (and ranges merged) until an instruction can be created. If any bytecode remains after a sample has been created, the next sample begins in the same range. (For this reason, it's possible that samples overlap.)

    If a node contains more than one instruction, the intermediate states produced before the final state for that sample are saved to the sample's intermediateStates array.

    If the program states in trace are exhausted before the final instruction in a sample (usually caused by an evaluation error), the last instruction with a matching program state is used for the sample (with its program state), and the unmatched instructions are ignored. (This allows the "last known state" to be displayed for the sample that caused evaluation to halt.)


    For example, the following script demonstrates many of these cases:

    0x00 0x01 0xab01 0xcd9300 $(OP_3 <0x00> OP_SWAP OP_CAT) 0x010203

    Which compiles to 0x0001ab01cd93000003010203, disassembled:

    OP_0 OP_PUSHBYTES_1 0xab OP_PUSHBYTES_1 0xcd OP_ADD OP_0 OP_0 OP_PUSHBYTES_3 0x010203

    In the script, there are 6 top-level nodes (identified below within []):

    [0x00] [0x01] [0xab01] [0xcd9300] [$(OP_3 <0x00> OP_SWAP OP_CAT)] [0x010203]

    These nodes together encode 7 instructions, some within a single node, and some split between several nodes. Below we substitute the evaluation for its result 0x0003 to group instructions by []:

    [0x00] [0x01 0xab][01 0xcd][93][00] [0x00][03 0x010203]

    The "resolution" of samples is limited to the range of single nodes: nodes cannot always be introspected to determine where contained instructions begin and end. For example, it is ambiguous which portions of the evaluation are responsible for the initial 0x00 and which are responsible for the 0x03.

    For this reason, the range of each sample is limited to the range(s) of one or more adjacent nodes. Samples may overlap in the range of a node that is responsible for both ending a previous sample and beginning a new sample. (Though, only 2 samples can overlap. If a node is responsible for more than 2 instructions, the second sample includes internalStates for instructions that occur before the end of the second sample.)

    In this case, there are 6 samples identified below within [], where each [ is closed by the closest following ] (no nesting):

    [0x00] [0x01 [0xab01] [0xcd9300]] [[$(OP_3 <0x00> OP_SWAP OP_CAT)] 0x010203]

    The ranges for each sample (in terms of nodes) are as follows:

    • Sample 1: node 1
    • Sample 2: node 2 + node 3
    • Sample 3: node 3 + node 4
    • Sample 4: node 4
    • Sample 5: node 5
    • Sample 6: node 5 + node 6

    Note that the following samples overlap:

    • Sample 2 and Sample 3
    • Sample 3 and Sample 4
    • Sample 5 and Sample 6

    Finally, note that Sample 4 will have one internal state produced by the OP_ADD instruction. Sample 4 then ends with the OP_0 (0x00) instruction at the end of the 0xcd9300 node.


    Note, this implementation relies on the expectation that trace begins with the initial program state, contains a single program state per instruction, and ends with the final program state (as produced by vm.debug). It also expects the bytecode provided by nodes to be parsable by decodeAuthenticationInstructions.

Generated using TypeDoc