bool CBScriptExecute(CBScript * self,CBScriptStack * stack,CBDependencies * dependencies){ // This looks confusing but isn't too bad, trust me. CBScriptStack altStack = CBNewEmptyScriptStack(); u_int16_t skipIfElseBlock = 0xffff; // Skips all instructions on or over this if/else level. u_int16_t ifElseSize = 0; // Amount of if/else block levels if (CBGetByteArray(self->program)->length > 10000) return false; // Script is an illegal size. for (u_int8_t opCount = 0;;opCount++) { if (!(CBGetByteArray(self->program)->length - self->cursor)) break; // Reached end of program if (opCount == 201) { return false; // Too many Op codes } u_int8_t byte = CBGetByteArrayVT(self->program)->getByte(self->program,self->cursor); self->cursor++; // Control management for skipping if (ifElseSize >= skipIfElseBlock) { // Skip when "ifElseSize" level is over or at "skipIfElseBlock" if (byte == CB_SCRIPT_OP_ELSE && ifElseSize == skipIfElseBlock) { skipIfElseBlock = 0xffff; // No more skipping }else if (byte == CB_SCRIPT_OP_ENDIF){ if (ifElseSize == skipIfElseBlock) { skipIfElseBlock = 0xffff; // No more skipping } ifElseSize--; }else if (byte == CB_SCRIPT_OP_IF){ ifElseSize++; } }else{ // Execution for no skipping if (!byte) { // Push 0 onto stack CBScriptStackItem item; item.data = malloc(1); item.length = 1; item.data[0] = 0; CBScriptStackPushItem(stack, item); }else if (byte < 76){ // Check size if ((CBGetByteArray(self->program)->length - self->cursor) < byte) return false; // Not enough space. // Push data the size of the value of the byte CBScriptStackItem item; item.data = malloc(byte); item.length = byte; memmove(item.data, CBGetByteArrayVT(self->program)->getData(self->program) + self->cursor, byte); CBScriptStackPushItem(stack, item); self->cursor += byte; }else if (byte < 79){ // Push data with the length of bytes represented by the next bytes u_int32_t amount; if (byte == CB_SCRIPT_OP_PUSHDATA1){ amount = CBGetByteArrayVT(self->program)->getByte(self->program,self->cursor); self->cursor++; }else if (byte == CB_SCRIPT_OP_PUSHDATA2){ amount = CBGetByteArrayVT(self->program)->readUInt16(self->program,self->cursor); self->cursor += 2; }else{ amount = CBGetByteArrayVT(self->program)->readUInt32(self->program,self->cursor); self->cursor += 4; } // Check limitation if (amount > 520) return false; // Size of data to push is illegal. // Check size if ((CBGetByteArray(self->program)->length - self->cursor) < amount) return false; // Not enough space. CBScriptStackItem item; item.data = malloc(amount); item.length = amount; memmove(item.data, CBGetByteArrayVT(self->program)->getData(self->program) + self->cursor, amount); CBScriptStackPushItem(stack, item); self->cursor += amount; }else if (byte == CB_SCRIPT_OP_1NEGATE){ // Push -1 onto the stack CBScriptStackItem item; item.data = malloc(1); item.length = 1; item.data[0] = 0x81; // 10000001 Not like normal signed integers, most significant bit applies sign, making the rest of the bits take away from zero. CBScriptStackPushItem(stack, item); }else if (byte < 97){ // Push a number onto the stack CBScriptStackItem item; item.data = malloc(1); item.length = 1; item.data[0] = byte - CB_SCRIPT_OP_1 + 1; CBScriptStackPushItem(stack, item); }else if (byte == CB_SCRIPT_OP_NOP){ // Nothing... }else if (byte == CB_SCRIPT_OP_IF || byte == CB_SCRIPT_OP_NOTIF){ // If top of stack is true, continue, else goto OP_ELSE or OP_ENDIF. ifElseSize++; if (!stack->length) return false; // Stack empty bool res = CBScriptStackEvalBool(stack); if ((res && byte == CB_SCRIPT_OP_IF) || (!res && byte == CB_SCRIPT_OP_NOTIF)) skipIfElseBlock = 0xffff; else skipIfElseBlock = ifElseSize; // Is skipping on this level until OP_ELSE or OP_ENDIF is reached on this level // Remove top stack item CBScriptStackRemoveItem(stack); }else if (byte == CB_SCRIPT_OP_ELSE){ if (!ifElseSize) return false; // OP_ELSE on lowest level not possible skipIfElseBlock = ifElseSize; // Begin skipping }else if (byte == CB_SCRIPT_OP_ENDIF){ if (!ifElseSize) return false; // OP_ENDIF on lowest level not possible ifElseSize--; // Lower level }else if (byte == CB_SCRIPT_OP_VERIFY){ if (!stack->length) return false; // Stack empty if (CBScriptStackEvalBool(stack)) // Remove top stack item CBScriptStackRemoveItem(stack); else return false; // Failed verification }else if (byte == CB_SCRIPT_OP_RETURN){ return false; // Failed verification with OP_RETURN. }else if (byte == CB_SCRIPT_OP_TOALTSTACK){ if (!stack->length) return false; // Stack empty CBScriptStackPushItem(&altStack, CBScriptStackPopItem(stack)); }else if (byte == CB_SCRIPT_OP_FROMALTSTACK){ if (!altStack.length) return false; // Alternative stack empty CBScriptStackPushItem(stack, CBScriptStackPopItem(&altStack)); }else if (byte == CB_SCRIPT_OP_IFDUP){ if (!stack->length) return false; // Stack empty if (CBScriptStackEvalBool(stack)) //Duplicate top stack item CBScriptStackPushItem(stack, CBScriptStackCopyItem(stack,0)); }else if (byte == CB_SCRIPT_OP_DEPTH){ CBScriptStackItem item; item.data = malloc(2); item.length = 2; item.data[0] = stack->length >> 8; item.data[1] = stack->length; CBScriptStackPushItem(stack, item); }else if (byte == CB_SCRIPT_OP_DROP){ if (!stack->length) return false; // Stack empty CBScriptStackRemoveItem(stack); }else if (byte == CB_SCRIPT_OP_DUP){ if (!stack->length) return false; // Stack empty //Duplicate top stack item CBScriptStackPushItem(stack, CBScriptStackCopyItem(stack,0)); }else if (byte == CB_SCRIPT_OP_NIP){ if (stack->length < 2) return false; // Stack needs 2 or more elements. // Remove second from top item. stack->length--; free(stack->elements[stack->length-1].data); stack->elements[stack->length-1] = stack->elements[stack->length]; // Top item moves down stack->elements = realloc(stack->elements, sizeof(*stack->elements)*stack->length); }else if (byte == CB_SCRIPT_OP_OVER){ if (stack->length < 2) return false; // Stack needs 2 or more elements. CBScriptStackPushItem(stack, CBScriptStackCopyItem(stack,1)); // Copies second from top and pushes it on the top. }else if (byte == CB_SCRIPT_OP_PICK || byte == CB_SCRIPT_OP_ROLL){ if (stack->length < 2) return false; // Stack needs 2 or more elements. CBScriptStackItem item = CBScriptStackPopItem(stack); if (item.length > 4) return false; // Protocol does not except integers more than 32 bits. if (item.data[item.length-1] > 0x80) // Negative return false; // Must be positive u_int32_t i = item.data[0] & (item.data[1] << 8) & (item.data[2] << 16) & (item.data[3] << 24); if (i >= stack->length) return false; // Must be smaller than stack size if (byte == CB_SCRIPT_OP_PICK) { // Copy element CBScriptStackPushItem(stack, CBScriptStackCopyItem(stack,i)); }else{ // CB_SCRIPT_OP_ROLL // Move element. CBScriptStackItem temp = stack->elements[stack->length-i-1]; for (u_int32_t x = 0; x < i; x++) // Move other elements down stack->elements[stack->length-i+x-1] = stack->elements[stack->length-i+x]; stack->elements[stack->length] = temp; } }else if (byte == CB_SCRIPT_OP_ROT){ if (stack->length < 3) return false; // Stack needs 3 or more elements. // Rotate top three elements to the left. CBScriptStackItem temp = stack->elements[stack->length-3]; stack->elements[stack->length-3] = stack->elements[stack->length-2]; stack->elements[stack->length-2] = stack->elements[stack->length-1]; stack->elements[stack->length-1] = temp; }else if (byte == CB_SCRIPT_OP_SWAP){ if (stack->length < 2) return false; // Stack needs 2 or more elements. CBScriptStackItem temp = stack->elements[stack->length-2]; stack->elements[stack->length-2] = stack->elements[stack->length-1]; stack->elements[stack->length-1] = temp; }else if (byte == CB_SCRIPT_OP_TUCK){ if (stack->length < 2) return false; // Stack needs 2 or more elements. CBScriptStackItem item = CBScriptStackCopyItem(stack, 0); // New copy three down. stack->length++; stack->elements = realloc(stack->elements, sizeof(*stack->elements)*stack->length); stack->elements[stack->length-1] = stack->elements[stack->length-2]; stack->elements[stack->length-2] = stack->elements[stack->length-3]; stack->elements[stack->length-3] = item; }else if (byte == CB_SCRIPT_OP_2DROP){ if (stack->length < 2) return false; // Stack needs 2 or more elements. CBScriptStackRemoveItem(stack); CBScriptStackRemoveItem(stack); }else if (byte == CB_SCRIPT_OP_2DUP){ if (stack->length < 2) return false; // Stack needs 2 or more elements. CBScriptStackPushItem(stack, CBScriptStackCopyItem(stack,1)); CBScriptStackPushItem(stack, CBScriptStackCopyItem(stack,1)); }else if (byte == CB_SCRIPT_OP_3DUP){ if (stack->length < 3) return false; // Stack needs 3 or more elements. CBScriptStackPushItem(stack, CBScriptStackCopyItem(stack,2)); CBScriptStackPushItem(stack, CBScriptStackCopyItem(stack,2)); CBScriptStackPushItem(stack, CBScriptStackCopyItem(stack,2)); }else if (byte == CB_SCRIPT_OP_2OVER){ if (stack->length < 4) return false; // Stack needs 4 or more elements. CBScriptStackPushItem(stack, CBScriptStackCopyItem(stack,3)); CBScriptStackPushItem(stack, CBScriptStackCopyItem(stack,3)); }else if (byte == CB_SCRIPT_OP_2ROT){ if (stack->length < 6) return false; // Stack needs 6 or more elements. // Rotate top three pairs of elements to the left. CBScriptStackItem temp = stack->elements[stack->length-6]; CBScriptStackItem temp2 = stack->elements[stack->length-5]; stack->elements[stack->length-6] = stack->elements[stack->length-4]; stack->elements[stack->length-5] = stack->elements[stack->length-3]; stack->elements[stack->length-4] = stack->elements[stack->length-2]; stack->elements[stack->length-3] = stack->elements[stack->length-1]; stack->elements[stack->length-2] = temp; stack->elements[stack->length-1] = temp2; }else if (byte == CB_SCRIPT_OP_2SWAP){ if (stack->length < 4) return false; // Stack needs 4 or more elements. CBScriptStackItem temp = stack->elements[stack->length-4]; CBScriptStackItem temp2 = stack->elements[stack->length-3]; stack->elements[stack->length-4] = stack->elements[stack->length-2]; stack->elements[stack->length-3] = stack->elements[stack->length-1]; stack->elements[stack->length-2] = temp; stack->elements[stack->length-1] = temp2; }else if (byte == CB_SCRIPT_OP_SIZE){ // ??? Does this match the protocol? if (!stack->length) return false; // Stack empty u_int16_t len = stack->elements[stack->length-1].length; CBScriptStackItem item; if (len > 0x80) { // More than 0x80 so use two bytes as on a single byte it would be negative. item.data = malloc(2); item.length = 2; item.data[0] = len >> 8; item.data[1] = len; }else{
int main(){ unsigned int s = (unsigned int)time(NULL); s = 1337544566; printf("Session = %ui\n",s); srand(s); FILE * f = fopen("scriptCases.txt", "r"); if (!f) printf("FILE WONT OPEN\n"); for (uint16_t x = 0;;) { char * line = NULL; uint16_t lineLen = 0; bool eof = false; while (true) { line = realloc(line, lineLen + 101); if(!fgets(line + lineLen, 101, f)){ eof = true; break; } if (line[strlen(line)-1] == '\n') break; // Got to the end of the line lineLen += 100; } if(eof) break; line[strlen(line)-1] = '\0'; if (!(line[0] == '/' && line[1] == '/') && strlen(line)){ x++; CBScript * script = CBNewScriptFromString(line, logError); if (!script) { printf("%i: {%s} INVALID\n",x,line); return 1; }else{ CBScriptStack stack = CBNewEmptyScriptStack(); if (x == 31) { printf(""); } bool res = CBScriptExecute(script, &stack, NULL, NULL, 0, true); CBFreeScriptStack(stack); if (res != ((fgetc(f) == '1') ? CB_SCRIPT_VALID : CB_SCRIPT_INVALID)) { printf("%i: {%s} FAIL\n",x,line); return 1; }else{ printf("%i: {%s} OK\n",x,line); } CBReleaseObject(script); fseek(f, 1, SEEK_CUR); } } free(line); } fclose(f); // Test PUSHDATA CBScript * script = CBNewScriptWithDataCopy((uint8_t []){CB_SCRIPT_OP_PUSHDATA1,0x01,0x47,CB_SCRIPT_OP_DUP,CB_SCRIPT_OP_PUSHDATA2,0x01,0x00,0x47,CB_SCRIPT_OP_EQUALVERIFY,CB_SCRIPT_OP_PUSHDATA4,0x01,0x00,0x00,0x00,0x47,CB_SCRIPT_OP_EQUAL}, 16, logError); CBScriptStack stack = CBNewEmptyScriptStack(); if(CBScriptExecute(script, &stack, NULL, NULL, 0, true) != CB_SCRIPT_VALID){ printf("PUSHDATA TEST 1 FAIL\n"); return 1; } CBReleaseObject(script); script = CBNewScriptWithDataCopy((uint8_t []){CB_SCRIPT_OP_PUSHDATA1,0x01,0x00,CB_SCRIPT_OP_DUP,CB_SCRIPT_OP_PUSHDATA2,0x01,0x00,0x00,CB_SCRIPT_OP_EQUALVERIFY,CB_SCRIPT_OP_PUSHDATA4,0x01,0x00,0x00,0x00,0x00,CB_SCRIPT_OP_EQUAL}, 16, logError); stack = CBNewEmptyScriptStack(); if(CBScriptExecute(script, &stack, NULL, NULL, 0, true) != CB_SCRIPT_VALID){ printf("PUSHDATA TEST 2 FAIL\n"); return 1; } CBReleaseObject(script); // Test stack length limit script = CBNewScriptWithDataCopy((uint8_t []){CB_SCRIPT_OP_TRUE}, 1, logError); stack = CBNewEmptyScriptStack(); for (int x = 0; x < 1001; x++) CBScriptStackPushItem(&stack, (CBScriptStackItem){NULL,0}); if(CBScriptExecute(script, &stack, NULL, NULL, 0, true) != CB_SCRIPT_INVALID){ printf("STACK LIMIT TEST FAIL\n"); return 1; } CBReleaseObject(script); // Test P2SH CBScript * inputScript = CBNewScriptWithDataCopy((uint8_t []){CB_SCRIPT_OP_14,0x04,CB_SCRIPT_OP_5,CB_SCRIPT_OP_9,CB_SCRIPT_OP_ADD,CB_SCRIPT_OP_EQUAL}, 6, logError); CBScript * outputScript = CBNewScriptWithDataCopy((uint8_t []){CB_SCRIPT_OP_HASH160,0x14,0x87,0xF3,0xB6,0x21,0xF1,0x8C,0x50,0x06,0x8B,0x7D,0xAB,0xA1,0x60,0xBB,0x2C,0x51,0xFD,0xD6,0xA5,0xE2,CB_SCRIPT_OP_EQUAL}, 23, logError); stack = CBNewEmptyScriptStack(); CBScriptExecute(inputScript, &stack, NULL, NULL, 0, false); if (CBScriptExecute(outputScript, &stack, NULL, NULL, 0, false) != CB_SCRIPT_VALID) { printf("OK NO PS2H FAIL\n"); return 1; } stack = CBNewEmptyScriptStack(); CBScriptExecute(inputScript, &stack, NULL, NULL, 0, false); if (CBScriptExecute(outputScript, &stack, NULL, NULL, 0, true) != CB_SCRIPT_VALID) { printf("OK YES PS2H FAIL\n"); return 1; } CBByteArraySetByte(inputScript, 0, CB_SCRIPT_OP_13); stack = CBNewEmptyScriptStack(); CBScriptExecute(inputScript, &stack, NULL, NULL, 0, false); if (CBScriptExecute(outputScript, &stack, NULL, NULL, 0, false) != CB_SCRIPT_VALID) { printf("BAD NO PS2H FAIL\n"); return 1; } stack = CBNewEmptyScriptStack(); CBScriptExecute(inputScript, &stack, NULL, NULL, 0, false); CBReleaseObject(inputScript); if (CBScriptExecute(outputScript, &stack, NULL, NULL, 0, true) != CB_SCRIPT_INVALID) { printf("BAD YES PS2H FAIL\n"); return 1; } CBFreeScriptStack(stack); CBReleaseObject(outputScript); // Test CBScriptIsPushOnly script = CBNewScriptWithDataCopy((uint8_t [20]){0x02,0x04,0x73,CB_SCRIPT_OP_PUSHDATA1,0x03,0xA2,0x70,0x73,CB_SCRIPT_OP_PUSHDATA2,0x01,0x00,0x5A,CB_SCRIPT_OP_PUSHDATA4,0x03,0x0,0x0,0x0,0x5F,0x70,0x74}, 20, &logError); if (NOT CBScriptIsPushOnly(script)) { printf("IS PUSH PUSH FAIL\n"); return 1; } CBReleaseObject(script); script = CBNewScriptWithDataCopy((uint8_t [13]){0x02,0x04,0x73,CB_SCRIPT_OP_PUSHDATA1,0x03,0xA2,0x70,0x73,CB_SCRIPT_OP_PUSHDATA2,0x01,0x00,0x5A,CB_SCRIPT_OP_0}, 13, &logError); if (CBScriptIsPushOnly(script)) { printf("IS PUSH ZERO FAIL\n"); return 1; } CBReleaseObject(script); script = CBNewScriptWithDataCopy((uint8_t [12]){0x02,0x04,0x73,CB_SCRIPT_OP_1,0x03,0xA2,0x70,0x73,CB_SCRIPT_OP_PUSHDATA2,0x01,0x00,0x5A}, 12, &logError); if (CBScriptIsPushOnly(script)) { printf("IS PUSH ONE FAIL\n"); return 1; } CBReleaseObject(script); return 0; }