import { range } from '../format/format' ; import { AuthenticationProgramStateBCH } from './instruction-sets/instruction-sets' ; import { AuthenticationProgramStateMinimum } from './vm-types' ; /**
 * Operations define the behavior of an opcode in an `InstructionSet`.
 *
 * Operations should be written as efficiently as possible, and may safely
 * mutate the `ProgramState`. If needed, the `AuthenticationVirtualMachine`
 * will clone the `ProgramState` before providing it to an operation.
 */ export type Operation < ProgramState > = ( state : ProgramState ) => ProgramState ; /**
 * Test a program state, returning an error message
 */ export type TestState < ProgramState > = ( state : ProgramState ) => string | true ; export interface InstructionSetOperationMapping < ProgramState > { [ opcode ] : Operation < ProgramState > ; } /**
 * An `InstructionSet` is a mapping of methods which define the operation of
 * an `AuthenticationVirtualMachine`. An instruction set is specific to a
 * single consensus setting of a single network, e.g. `BCH_2019_05_Mandatory`, `BCH_2019_05_Standard` or `BTC_2017_08_Mandatory`.
 *
 * An instruction set is composed of `Operation`s which take a `ProgramState`
 * and return a `ProgramState`, as well as the `clone`, `continue`, `evaluate`,
 * and `verify` "lifecycle" methods.
 *
 * Each operation is assigned to its `opcode` number (between 0 and 255). When
 * evaluating instructions, the virtual machine will select an Operation based
 * on its opcode. Any opcodes which are unassigned by the instruction set will
 * use the `undefined` operation.
 */ export interface InstructionSet < AuthenticationProgram , ProgramState > { remarks clone : Operation < ProgramState > ; remarks continue : ( state : ProgramState ) => boolean ; remarks privateRemarks evaluate : ( program : AuthenticationProgram , stateEvaluate : ( state : Readonly < ProgramState > ) => ProgramState ) => ProgramState ; /**
     * A mapping of `opcode` numbers (between 0 and 255) to `Operations`. When the
     * `AuthenticationVirtualMachine` encounters an instruction for the specified
     * `opcode`, the program state will be passed to the specified operation.
     */ operations : InstructionSetOperationMapping < ProgramState > ; remarks undefined : Operation < ProgramState > ; remarks verify : ( state : ProgramState ) => string | true ; } /**
 * A set of pure-functions allowing authentication programs to be evaluated and
 * inspected.
 */ export interface AuthenticationVirtualMachine < AuthenticationProgram , ProgramState > { remarks param state debug : ( program : Readonly < AuthenticationProgram > ) => ProgramState [ ] ; param state evaluate : ( program : Readonly < AuthenticationProgram > ) => ProgramState ; /**
     * Test the ProgramState to determine if execution should continue.
     */ stateContinue : ( state : Readonly < ProgramState > ) => boolean ; /**
     * Return an array of program states by fully evaluating `state`, cloning and
     * adding each intermediate state to the returned array. The first
     * `ProgramState` in the returned array is the initial program state, and the
     * last `ProgramState` in the returned array is the first program state which
     * returns `false` when provided to `stateContinue`.
     *
     * Note, this method is typically an implementation detail of the virtual
     * machine and cannot produce a final result. In most cases, `debug` is the
     * proper method to debug a program.
     */ stateDebug : ( state : Readonly < ProgramState > ) => ProgramState [ ] ; param state stateEvaluate : ( state : Readonly < ProgramState > ) => ProgramState ; param state stateStep : ( state : Readonly < ProgramState > ) => ProgramState ; param state stateStepMutate : ( state : ProgramState ) => ProgramState ; remarks param state verify : ( state : ProgramState ) => true | string ; } param instructionSet export const createAuthenticationVirtualMachine = < AuthenticationProgram , ProgramState extends AuthenticationProgramStateMinimum = AuthenticationProgramStateBCH > ( instructionSet ) : AuthenticationVirtualMachine < AuthenticationProgram , ProgramState > => { const availableOpcodes = 256 ; const operators = range ( availableOpcodes ) . map ( ( codepoint ) => ( instructionSet . operations [ codepoint ] as Operation < ProgramState > | undefined ) === undefined ? instructionSet . undefined : instructionSet . operations [ codepoint ] ) ; const getCodepoint = ( state ) => state . instructions [ state . ip ] ; const after = ( state ) => { state . ip += 1 ; return state ; } ; const getOperation = ( state ) => operators [ getCodepoint ( state ) . opcode ] ; const stateStepMutate = ( state ) => after ( getOperation ( state ) ( state ) ) ; const stateContinue = instructionSet . continue ; /**
     * When we get real tail call optimization, this can be replaced
     * with recursion.
     */ const untilComplete = ( state , stepFunction ) => { while ( stateContinue ( state ) ) { state = stepFunction ( state ) ; } return state ; } ; const clone = ( state ) => instructionSet . clone ( state ) ; const { verify } = instructionSet ; const stateEvaluate = ( state ) => untilComplete ( clone ( state ) , stateStepMutate ) ; const stateDebugStep = ( state ) => { const operator = getOperation ( state ) ; return after ( operator ( clone ( state ) ) ) ; } ; const stateDebug = ( state ) => { const trace = [ ] ; trace . push ( state ) ; untilComplete ( state , ( currentState ) => { const nextState = stateDebugStep ( currentState ) ; trace . push ( nextState ) ; return nextState ; } ) ; return trace ; } ; const stateStep = ( state ) => stateStepMutate ( clone ( state ) ) ; const evaluate = ( program ) => instructionSet . evaluate ( program , stateEvaluate ) ; const debug = ( program ) => { const results = [ ] ; const proxyDebug = ( state ) => { const debugResult = stateDebug ( state ) ; results . push ( ... debugResult ) ; return ( ( debugResult [ debugResult . length - 1 ] as ProgramState | undefined ) ?? state ) ; } ; const finalResult = instructionSet . evaluate ( program , proxyDebug ) ; return [ ... results , finalResult ] ; } ; return { debug , evaluate , stateContinue , stateDebug , stateEvaluate , stateStep , stateStepMutate , verify , } ; } ;