Run the Bit Machine on the well-typed Simplicity expression 'dag[len]' of type A |- B. * If bitSize(A) > 0, initialize the active read frame's data with 'input[ROUND_UWORD(bitSize(A))]'. * * If malloc fails, returns 'SIMPLICITY_ERR_MALLOC'. * When a budget is given, if static analysis results determines the bound on cpu requirements exceed the allowed budget, returns 'SIMPLICITY_ERR_EXEC_BUDGE
| 774 | * if 'dag[len]' represents a Simplicity expression with primitives then 'NULL != env'; |
| 775 | */ |
| 776 | simplicity_err simplicity_evalTCOExpression( flags_type anti_dos_checks, UWORD* output, const UWORD* input |
| 777 | , const dag_node* dag, type* type_dag, size_t len, ubounded minCost, const ubounded* budget, const txEnv* env |
| 778 | ) { |
| 779 | simplicity_assert(1 <= len); |
| 780 | simplicity_assert(len <= DAG_LEN_MAX); |
| 781 | if (budget) { |
| 782 | simplicity_assert(*budget <= BUDGET_MAX); |
| 783 | simplicity_assert(minCost <= *budget); |
| 784 | } |
| 785 | simplicity_assert(minCost <= BUDGET_MAX); |
| 786 | static_assert(1 <= UBOUNDED_MAX, "UBOUNDED_MAX is zero."); |
| 787 | static_assert(BUDGET_MAX <= (UBOUNDED_MAX - 1) / 1000, "BUDGET_MAX is too large."); |
| 788 | static_assert(CELLS_MAX < UBOUNDED_MAX, "CELLS_MAX is too large."); |
| 789 | ubounded cellsBound, UWORDBound, frameBound, costBound; |
| 790 | simplicity_err result = simplicity_analyseBounds(&cellsBound, &UWORDBound, &frameBound, &costBound, CELLS_MAX, minCost*1000, budget ? *budget*1000 : UBOUNDED_MAX, dag, type_dag, len); |
| 791 | if (!IS_OK(result)) return result; |
| 792 | |
| 793 | /* frameBound is at most 2*len. */ |
| 794 | static_assert(DAG_LEN_MAX <= UBOUNDED_MAX / 2, "2*DAG_LEN_MAX does not fit in size_t."); |
| 795 | simplicity_assert(frameBound <= 2*len); |
| 796 | |
| 797 | /* UWORDBound * UWORD_BIT, the number of bits actually allocacted, is at most the cellBound count plus (worse case) padding bits in each frame. */ |
| 798 | static_assert(1 <= UWORD_BIT, "UWORD_BIT is zero."); |
| 799 | static_assert(2*DAG_LEN_MAX <= (SIZE_MAX - CELLS_MAX) / (UWORD_BIT - 1), "cellsBound + frameBound*(UWORD_BIT - 1) doesn't fit in size_t."); |
| 800 | simplicity_assert(UWORDBound <= (cellsBound + frameBound*(UWORD_BIT - 1)) / UWORD_BIT); |
| 801 | |
| 802 | /* UWORDBound, is also at most the cellsBound, with an entire UWORD per cell (the rest of the UWORD being padding). */ |
| 803 | simplicity_assert(UWORDBound <= cellsBound); |
| 804 | |
| 805 | /* We use calloc for 'cells' because the frame data must be initialized before we can perform bitwise operations. */ |
| 806 | static_assert(CELLS_MAX - 1 <= UINT32_MAX, "cells array index does not fit in uint32_t."); |
| 807 | UWORD* cells = simplicity_calloc(UWORDBound ? UWORDBound : 1, sizeof(UWORD)); |
| 808 | static_assert(2*DAG_LEN_MAX <= SIZE_MAX / sizeof(frameItem), "frames array does not fit in size_t."); |
| 809 | static_assert(1 <= DAG_LEN_MAX, "DAG_LEN_MAX is zero."); |
| 810 | static_assert(2*DAG_LEN_MAX - 1 <= UINT32_MAX, "frames array index does not fit in uint32_t."); |
| 811 | frameItem* frames = simplicity_malloc(frameBound * sizeof(frameItem)); |
| 812 | call* stack = simplicity_calloc(len, sizeof(call)); |
| 813 | |
| 814 | result = cells && frames && stack ? SIMPLICITY_NO_ERROR : SIMPLICITY_ERR_MALLOC; |
| 815 | if (IS_OK(result)) { |
| 816 | const ubounded inputSize = type_dag[dag[len-1].sourceType].bitSize; |
| 817 | const ubounded outputSize = type_dag[dag[len-1].targetType].bitSize; |
| 818 | simplicity_assert(NULL != input || 0 == inputSize); |
| 819 | if (inputSize) memcpy(cells, input, ROUND_UWORD(inputSize) * sizeof(UWORD)); |
| 820 | |
| 821 | evalState state = |
| 822 | { .activeReadFrame = frames |
| 823 | , .activeWriteFrame = frames + (frameBound - 1) |
| 824 | }; |
| 825 | *(state.activeReadFrame) = initReadFrame(inputSize, cells); |
| 826 | *(state.activeWriteFrame) = initWriteFrame(outputSize, cells + UWORDBound); |
| 827 | |
| 828 | result = runTCO(state, stack, dag, type_dag, len, env); |
| 829 | |
| 830 | if (IS_OK(result)) { |
| 831 | simplicity_assert(NULL != output || 0 == outputSize); |
| 832 | if (outputSize) memcpy(output, state.activeWriteFrame->edge, ROUND_UWORD(outputSize) * sizeof(UWORD)); |
| 833 |