コード例 #1
0
ファイル: CBScript.c プロジェクト: graingert/cbitcoin
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{
コード例 #2
0
ファイル: testCBScript.c プロジェクト: applsdev/cbitcoin
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;
}