Skip to content

Commit b075e2c

Browse files
Count number of cells
Count the number of cells the bit machine requires and bound it by a consensus limit.
1 parent ba3e39f commit b075e2c

File tree

2 files changed

+60
-15
lines changed

2 files changed

+60
-15
lines changed

C/eval.c

Lines changed: 58 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -639,14 +639,14 @@ static bool antiDos(flags_type checks, const call* stack, const dag_node* dag, s
639639
* the Simplicity interpreter.
640640
*/
641641
typedef struct memBound {
642+
size_t extraCellsBound[2];
642643
size_t extraUWORDBound[2];
643644
size_t extraFrameBound[2]; /* extraStackBound[0] is for TCO off and extraStackBound[1] is for TCO on */
644645
} memBound;
645646

646647
/* :TODO: Document extraFrameBound in the Tech Report (and implement it in Haskell) */
647648
/* Given a well-typed dag representing a Simplicity expression, set '*dag_bound' to the memory requirement for evaluation.
648-
* For all 'i', 0 <= 'i' < 'len', compute 'bound[i].extraUWORDBound' and 'bound[i].extraFrameBound'
649-
* for the subexpression denoted by the slice
649+
* For all 'i', 0 <= 'i' < 'len', compute 'bound[i]' fields for the subexpression denoted by the slice
650650
*
651651
* (dag_nodes[i + 1])dag.
652652
*
@@ -657,7 +657,9 @@ typedef struct memBound {
657657
* Precondition: NULL != dag_bound
658658
* dag_node dag[len] and 'dag' is well-typed with 'type_dag'.
659659
* Postcondition: if the result is 'true'
660-
* then 'max(dag_bound->extraUWORDBound[0], dag_bound->extraUWORDBound[1]) == SIZE_MAX'.
660+
* then 'max(dag_bound->extraCellsBound[0], dag_bound->extraCellsBound[1]) == SIZE_MAX'.
661+
* or 'dag_bound->extraCellsBound' characterizes the number of cells needed during evaluation of 'dag';
662+
* 'max(dag_bound->extraUWORDBound[0], dag_bound->extraUWORDBound[1]) == SIZE_MAX'.
661663
* or 'dag_bound->extraUWORDBound' characterizes the number of UWORDs needed
662664
* for the frames allocated during evaluation of 'dag';
663665
* 'dag_bound->extraFrameBound[0]' bounds the the number of stack frames needed during execution of 'dag';
@@ -676,6 +678,11 @@ static bool computeEvalTCOBound(memBound *dag_bound, const dag_node* dag, const
676678
case ASSERTL:
677679
case ASSERTR:
678680
case CASE:
681+
bound[i].extraCellsBound[0] = max( bound[dag[i].child[0]].extraCellsBound[0]
682+
, bound[dag[i].child[1]].extraCellsBound[0] );
683+
bound[i].extraCellsBound[1] = max( bound[dag[i].child[0]].extraCellsBound[1]
684+
, bound[dag[i].child[1]].extraCellsBound[1] );
685+
679686
bound[i].extraUWORDBound[0] = max( bound[dag[i].child[0]].extraUWORDBound[0]
680687
, bound[dag[i].child[1]].extraUWORDBound[0] );
681688
bound[i].extraUWORDBound[1] = max( bound[dag[i].child[0]].extraUWORDBound[1]
@@ -687,13 +694,20 @@ static bool computeEvalTCOBound(memBound *dag_bound, const dag_node* dag, const
687694
, bound[dag[i].child[1]].extraFrameBound[1] );
688695
break;
689696
case DISCONNECT:
690-
/* :TODO: replace this check with a consensus critical limit. */
691697
if (SIZE_MAX <= type_dag[DISCONNECT_W256A(dag, type_dag, i)].bitSize ||
692698
SIZE_MAX <= type_dag[DISCONNECT_BC(dag, type_dag, i)].bitSize) {
693699
/* 'BITSIZE(WORD256 * A)' or 'BITSIZE(B * C)' has exceeded our limits. */
700+
bound[i].extraCellsBound[0] = SIZE_MAX;
701+
bound[i].extraCellsBound[1] = SIZE_MAX;
694702
bound[i].extraUWORDBound[0] = SIZE_MAX;
695703
bound[i].extraUWORDBound[1] = SIZE_MAX;
696704
} else {
705+
bound[i].extraCellsBound[1] = type_dag[DISCONNECT_W256A(dag, type_dag, i)].bitSize;
706+
bound[i].extraCellsBound[0] = max(
707+
bounded_add( type_dag[DISCONNECT_BC(dag, type_dag, i)].bitSize
708+
, max( bounded_add(bound[i].extraCellsBound[1], bound[dag[i].child[0]].extraCellsBound[1])
709+
, max(bound[dag[i].child[0]].extraCellsBound[0], bound[dag[i].child[1]].extraCellsBound[1]))),
710+
bound[dag[i].child[1]].extraCellsBound[0]);
697711
bound[i].extraUWORDBound[1] = ROUND_UWORD(type_dag[DISCONNECT_W256A(dag, type_dag, i)].bitSize);
698712
bound[i].extraUWORDBound[0] = max(
699713
bounded_add(
@@ -707,13 +721,20 @@ static bool computeEvalTCOBound(memBound *dag_bound, const dag_node* dag, const
707721
bound[i].extraFrameBound[0] = bound[i].extraFrameBound[1] + 1;
708722
break;
709723
case COMP:
710-
/* :TODO: replace this check with a consensus critical limit. */
711724
if (SIZE_MAX <= type_dag[COMP_B(dag, type_dag, i)].bitSize) {
712725
/* 'BITSIZE(B)' has exceeded our limits. */
726+
bound[i].extraCellsBound[0] = SIZE_MAX;
727+
bound[i].extraCellsBound[1] = SIZE_MAX;
713728
bound[i].extraUWORDBound[0] = SIZE_MAX;
714729
bound[i].extraUWORDBound[1] = SIZE_MAX;
715730
} else {
716-
size_t scratch = ROUND_UWORD(type_dag[COMP_B(dag, type_dag, i)].bitSize);
731+
size_t scratch = type_dag[COMP_B(dag, type_dag, i)].bitSize;
732+
bound[i].extraCellsBound[0] = max( bounded_add( scratch
733+
, max( bound[dag[i].child[0]].extraCellsBound[0]
734+
, bound[dag[i].child[1]].extraCellsBound[1] ))
735+
, bound[dag[i].child[1]].extraCellsBound[0] );
736+
bound[i].extraCellsBound[1] = bounded_add(scratch, bound[dag[i].child[0]].extraCellsBound[1]);
737+
scratch = ROUND_UWORD(scratch);
717738
bound[i].extraUWORDBound[0] = max( bounded_add( scratch
718739
, max( bound[dag[i].child[0]].extraUWORDBound[0]
719740
, bound[dag[i].child[1]].extraUWORDBound[1] ))
@@ -727,6 +748,11 @@ static bool computeEvalTCOBound(memBound *dag_bound, const dag_node* dag, const
727748
, bound[dag[i].child[1]].extraFrameBound[1] );
728749
break;
729750
case PAIR:
751+
bound[i].extraCellsBound[0] = bound[dag[i].child[1]].extraCellsBound[0];
752+
bound[i].extraCellsBound[1] = max( bound[dag[i].child[0]].extraCellsBound[0]
753+
, max( bound[dag[i].child[0]].extraCellsBound[1]
754+
, bound[dag[i].child[1]].extraCellsBound[1] ));
755+
730756
bound[i].extraUWORDBound[0] = bound[dag[i].child[1]].extraUWORDBound[0];
731757
bound[i].extraUWORDBound[1] = max( bound[dag[i].child[0]].extraUWORDBound[0]
732758
, max( bound[dag[i].child[0]].extraUWORDBound[1]
@@ -741,6 +767,9 @@ static bool computeEvalTCOBound(memBound *dag_bound, const dag_node* dag, const
741767
case INJR:
742768
case TAKE:
743769
case DROP:
770+
bound[i].extraCellsBound[0] = bound[dag[i].child[0]].extraCellsBound[0];
771+
bound[i].extraCellsBound[1] = bound[dag[i].child[0]].extraCellsBound[1];
772+
744773
bound[i].extraUWORDBound[0] = bound[dag[i].child[0]].extraUWORDBound[0];
745774
bound[i].extraUWORDBound[1] = bound[dag[i].child[0]].extraUWORDBound[1];
746775

@@ -753,6 +782,7 @@ static bool computeEvalTCOBound(memBound *dag_bound, const dag_node* dag, const
753782
case WITNESS:
754783
case JET:
755784
case WORD:
785+
bound[i].extraCellsBound[0] = bound[i].extraCellsBound[1] = 0;
756786
bound[i].extraUWORDBound[0] = bound[i].extraUWORDBound[1] = 0;
757787
bound[i].extraFrameBound[0] = bound[i].extraFrameBound[1] = 0;
758788
}
@@ -794,19 +824,32 @@ bool evalTCOExpression( bool *evalSuccess, flags_type anti_dos_checks, UWORD* ou
794824
memBound bound;
795825
if (!computeEvalTCOBound(&bound, dag, type_dag, len)) return false;
796826

797-
size_t UWORDBound = bounded_add( bounded_add(ROUND_UWORD(inputSize), ROUND_UWORD(outputSize))
798-
, max(bound.extraUWORDBound[0], bound.extraUWORDBound[1])
799-
);
800-
size_t frameBound = bound.extraFrameBound[0] + 2; /* add the initial input and output frames to the count. */
827+
const size_t cellsBound = bounded_add( bounded_add(inputSize, outputSize)
828+
, max(bound.extraCellsBound[0], bound.extraCellsBound[1])
829+
);
830+
const size_t UWORDBound = bounded_add( bounded_add(ROUND_UWORD(inputSize), ROUND_UWORD(outputSize))
831+
, max(bound.extraUWORDBound[0], bound.extraUWORDBound[1])
832+
);
833+
const size_t frameBound = bound.extraFrameBound[0] + 2; /* add the initial input and output frames to the count. */
834+
835+
static_assert(CELLS_MAX < SIZE_MAX, "CELLS_MAX is too large.");
836+
if (CELLS_MAX < cellsBound) {
837+
/* Cell count exceeds consensus limits. */
838+
*evalSuccess = false;
839+
return true;
840+
}
841+
801842
/* frameBound is at most 2*len. */
802843
static_assert(DAG_LEN_MAX <= SIZE_MAX / 2, "2*DAG_LEN_MAX does not fit in size_t.");
803844
assert(frameBound <= 2*len);
804845

805-
/* :TODO: add reasonable, consensus critical limits to cells. */
806-
if (SIZE_MAX <= outputSize || SIZE_MAX <= inputSize || SIZE_MAX <= UWORDBound) {
807-
*evalSuccess = false;
808-
return true;
809-
}
846+
/* UWORDBound * UWORD_BIT, the number of bits actually allocacted, is at most the cellBound count plus (worse case) padding bits in each frame. */
847+
static_assert(1 <= UWORD_BIT, "UWORD_BIT is zero.");
848+
static_assert(2*DAG_LEN_MAX <= (SIZE_MAX - CELLS_MAX) / (UWORD_BIT - 1), "cellsBound + frameBound*(UWORD_BIT - 1) doesn't fit in size_t.");
849+
assert(UWORDBound <= (cellsBound + frameBound*(UWORD_BIT - 1)) / UWORD_BIT);
850+
851+
/* UWORDBound, is also at most the cellsBound, with an entire UWORD per cell (the rest of the UWORD being padding). */
852+
assert(UWORDBound <= cellsBound);
810853

811854
/* We use calloc for 'cells' because the frame data must be initialized before we can perform bitwise operations. */
812855
UWORD* cells = calloc(UWORDBound ? UWORDBound : 1, sizeof(UWORD));

C/limitations.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
#define DAG_LEN_MAX 0x800000U
55
#define NUMBER_OF_TYPENAMES_MAX 0x1000U
6+
#define CELLS_MAX 0x500000U /* 5120 Kibibits ought to be enough for anyone. */
67
_Static_assert(DAG_LEN_MAX <= SIZE_MAX , "DAG_LEN_MAX doesn't fit in size_t.");
78
_Static_assert(NUMBER_OF_TYPENAMES_MAX <= SIZE_MAX, "NUMBER_OF_TYPENAMES_MAX doesn't fit in size_t.");
9+
_Static_assert(CELLS_MAX <= SIZE_MAX, "CELLS_MAX doesn't fit in size_t.");
810

911
#endif

0 commit comments

Comments
 (0)