uint32_t CBTransactionOutputSerialise(CBTransactionOutput * self){ CBByteArray * bytes = CBGetMessage(self)->bytes; if (NOT bytes) { CBLogError("Attempting to serialise a CBTransactionInput with no bytes."); return 0; } if (NOT self->scriptObject){ CBLogError("Attempting to serialise a CBTransactionOutput without scriptObject."); return 0; } CBVarInt scriptLen = CBVarIntFromUInt64(CBGetByteArray(self->scriptObject)->length); uint32_t reqLen = 8 + scriptLen.size + CBGetByteArray(self->scriptObject)->length; if (bytes->length < reqLen) { CBLogError("Attempting to serialise a CBTransactionOutput with less bytes than required. %i < %i", bytes->length, reqLen); return 0; } // Serialise data into the CBByteArray and rereference objects to this CBByteArray to save memory. CBByteArraySetInt64(bytes, 0, self->value); CBVarIntEncode(bytes, 8, scriptLen); CBByteArrayCopyByteArray(bytes, 8 + scriptLen.size, CBGetByteArray(self->scriptObject)); CBByteArrayChangeReference(CBGetByteArray(self->scriptObject), bytes, 8 + scriptLen.size); // Ensure length is correct bytes->length = reqLen; // Is serialised. CBGetMessage(self)->serialised = true; return reqLen; }
uint32_t CBTransactionInputSerialise(CBTransactionInput * self){ CBByteArray * bytes = CBGetMessage(self)->bytes; if (NOT bytes) { CBGetMessage(self)->onErrorReceived(CB_ERROR_MESSAGE_SERIALISATION_NULL_BYTES,"Attempting to serialise a CBTransactionInput with no bytes."); return 0; } if (NOT self->prevOut.hash){ CBGetMessage(self)->onErrorReceived(CB_ERROR_MESSAGE_SERIALISATION_BAD_DATA,"Attempting to serialise a CBTransactionInput without prevOut.hash."); return 0; } if (NOT self->scriptObject){ CBGetMessage(self)->onErrorReceived(CB_ERROR_MESSAGE_SERIALISATION_BAD_DATA,"Attempting to serialise a CBTransactionInput without scriptObject."); return 0; } CBVarInt scriptLen = CBVarIntFromUInt64(CBGetByteArray(self->scriptObject)->length); uint32_t reqLen = 40 + scriptLen.size + CBGetByteArray(self->scriptObject)->length; if (bytes->length < reqLen) { CBGetMessage(self)->onErrorReceived(CB_ERROR_MESSAGE_SERIALISATION_BAD_BYTES,"Attempting to serialise a CBTransactionInput with less bytes than required. %i < %i\n",bytes->length, reqLen); return 0; } // Serialise data into the CBByteArray and rereference objects to this CBByteArray to save memory. CBByteArrayCopyByteArray(bytes, 0, self->prevOut.hash); CBByteArrayChangeReference(self->prevOut.hash, bytes, 0); CBByteArraySetInt32(bytes, 32, self->prevOut.index); CBVarIntEncode(bytes, 36, scriptLen); CBByteArrayCopyByteArray(bytes, 36 + scriptLen.size,CBGetByteArray(self->scriptObject)); CBByteArrayChangeReference(CBGetByteArray(self->scriptObject),bytes,36 + scriptLen.size); CBByteArraySetInt32(bytes,36 + scriptLen.size + CBGetByteArray(self->scriptObject)->length,self->sequence); CBGetMessage(self)->serialised = true; return reqLen; }
CBByteArray * CBVersionChecksumBytesGetString(CBVersionChecksumBytes * self){ if (self->cachedString) { // Return cached string CBRetainObject(self->cachedString); return self->cachedString; }else{ // Make string CBByteArrayReverseBytes(CBGetByteArray(self)); // Make this into little-endian CBBigInt bytes; CBBigIntAlloc(&bytes, CBGetByteArray(self)->length); bytes.length = CBGetByteArray(self)->length; memcpy(bytes.data, CBByteArrayGetData(CBGetByteArray(self)), bytes.length); char * string = CBEncodeBase58(&bytes); if (NOT string) return NULL; CBByteArray * str = CBNewByteArrayFromString(string, true); if (NOT str) { free(string); return NULL; } CBByteArrayReverseBytes(CBGetByteArray(self)); // Now the string is got, back to big-endian. if (self->cacheString) { self->cachedString = str; CBRetainObject(str); // Retain for this object. } return str; // No additional retain. Retained from constructor. } }
bool CBInitVersionChecksumBytesFromBytes(CBVersionChecksumBytes * self,uint8_t * bytes,uint32_t size,bool cacheString,void (*logError)(char *,...)) { self->cacheString = cacheString; self->cachedString = NULL; if (NOT CBInitByteArrayWithData(CBGetByteArray(self), bytes, size, logError)) return false; return true; }
bool CBInitVersionChecksumBytesFromBytes(CBVersionChecksumBytes * self,uint8_t * bytes,uint32_t size,bool cacheString,CBEvents * events){ self->cacheString = cacheString; self->cachedString = NULL; if (NOT CBInitByteArrayWithData(CBGetByteArray(self), bytes, size, events)) return false; return true; }
CBByteArray * CBVersionChecksumBytesGetString(CBVersionChecksumBytes * self){ if (self->cachedString) { // Return cached string CBRetainObject(self->cachedString); return self->cachedString; }else{ // Make string CBByteArrayReverseBytes(CBGetByteArray(self)); // Make this into little-endian char * string = CBEncodeBase58(CBByteArrayGetData(CBGetByteArray(self)),CBGetByteArray(self)->length); CBByteArray * str = CBNewByteArrayWithData((uint8_t *)string, strlen(string), CBGetByteArray(self)->events); CBByteArrayReverseBytes(CBGetByteArray(self)); // Now the string is got, back to big-endian. if (self->cacheString) { self->cachedString = str; CBRetainObject(str); // Retain for this object. } return str; // No additional retain. Retained from constructor. } }
bool CBInitVersionChecksumBytesFromString(CBVersionChecksumBytes * self,CBByteArray * string,bool cacheString,void (*logError)(char *,...)) { // Cache string if needed if (cacheString) { self->cachedString = string; CBRetainObject(string); } else self->cachedString = NULL; self->cacheString = cacheString; // Get bytes from string conversion CBBigInt bytes; CBBigIntAlloc(&bytes, 25); // 25 is the number of bytes for bitcoin addresses. if (NOT CBDecodeBase58Checked(&bytes, (char *)CBByteArrayGetData(string), logError)) return false; // Take over the bytes with the CBByteArray if (NOT CBInitByteArrayWithData(CBGetByteArray(self), bytes.data, bytes.length, logError)) return false; CBByteArrayReverseBytes(CBGetByteArray(self)); // CBBigInt is in little-endian. Conversion needed to make bitcoin address the right way. return true; }
bool CBInitVersionChecksumBytesFromString(CBVersionChecksumBytes * self,CBByteArray * string,bool cacheString,CBEvents * events){ // Cache string if needed if (cacheString) { self->cachedString = string; CBRetainObject(string); }else self->cachedString = NULL; self->cacheString = cacheString; // Get bytes from string conversion CBBigInt bytes = CBDecodeBase58Checked((char *)CBByteArrayGetData(string), events); if (bytes.length == 1) { return false; } // Take over the bytes with the CBByteArray if (NOT CBInitByteArrayWithData(CBGetByteArray(self), bytes.data, bytes.length, events)) return false; CBByteArrayReverseBytes(CBGetByteArray(self)); // CBBigInt is in little-endian. Conversion needed to make bitcoin address the right way. return true; }
CBGetHashReturn CBTransactionGetInputHashForSignature(void * vself, CBByteArray * prevOutSubScript, uint32_t input, CBSignType signType, uint8_t * hash){ CBTransaction * self= vself; if (self->inputNum < input + 1) { CBLogError("Receiving transaction hash to sign cannot be done for because the input index goes past the number of inputs."); return CB_TX_HASH_BAD; } uint8_t last5Bits = (signType & 0x1f); // For some reason this is what the C++ client does. CBVarInt prevOutputSubScriptVarInt = CBVarIntFromUInt64(prevOutSubScript->length); uint32_t sizeOfData = 12 + prevOutSubScript->length + prevOutputSubScriptVarInt.size; // Version, lock time and the sign type make up 12 bytes. if (signType & CB_SIGHASH_ANYONECANPAY) { sizeOfData += 41; // Just this one input. 32 bytes for outPointerHash, 4 for outPointerIndex, 4 for sequence and one for the *inputNum* var int }else{ sizeOfData += CBVarIntSizeOf(self->inputNum) + self->inputNum * 41 - 1; // All inputs with 1 byte var int except one. } if (last5Bits == CB_SIGHASH_NONE){ sizeOfData++; // Just for the CBVarInt and no outputs. }else if ((signType & 0x1f) == CB_SIGHASH_SINGLE){ if (self->outputNum < input + 1) { CBLogError("Receiving transaction hash to sign cannot be done for CB_SIGHASH_SINGLE because there are not enough outputs."); return CB_TX_HASH_BAD; } sizeOfData += CBVarIntSizeOf(input + 1) + input * 9; // For outputs up to the input index // The size for the output at the input index. uint32_t len = CBGetByteArray(self->outputs[input]->scriptObject)->length; sizeOfData += 8 + CBVarIntSizeOf(len) + len; }else{ // All outputs. Default to SIGHASH_ALL sizeOfData += CBVarIntSizeOf(self->outputNum); for (uint32_t x = 0; x < self->outputNum; x++) { uint32_t len = CBGetByteArray(self->outputs[x]->scriptObject)->length; sizeOfData += 8 + CBVarIntSizeOf(len) + len; } } CBByteArray * data = CBNewByteArrayOfSize(sizeOfData); if (NOT data) return CB_TX_HASH_ERR; CBByteArraySetInt32(data, 0, self->version); // Copy input data. Scripts are not copied for the inputs. uint32_t cursor; if (signType & CB_SIGHASH_ANYONECANPAY) { CBVarIntEncode(data, 4, CBVarIntFromUInt64(1)); // Only the input the signature is for. CBByteArrayCopyByteArray(data, 5, self->inputs[input]->prevOut.hash); CBByteArraySetInt32(data, 37, self->inputs[input]->prevOut.index); // Add prevOutSubScript CBVarIntEncode(data, 41, prevOutputSubScriptVarInt); cursor = 41 + prevOutputSubScriptVarInt.size; CBByteArrayCopyByteArray(data, cursor, prevOutSubScript); cursor += prevOutSubScript->length; CBByteArraySetInt32(data, cursor, self->inputs[input]->sequence); cursor += 4; }else{ CBVarInt inputNum = CBVarIntFromUInt64(self->inputNum); CBVarIntEncode(data, 4, inputNum); cursor = 4 + inputNum.size; for (uint32_t x = 0; x < self->inputNum; x++) { CBByteArrayCopyByteArray(data, cursor, self->inputs[x]->prevOut.hash); cursor += 32; CBByteArraySetInt32(data, cursor, self->inputs[x]->prevOut.index); cursor += 4; // Add prevOutSubScript if the input is for the signature. if (x == input) { CBVarIntEncode(data, cursor, prevOutputSubScriptVarInt); cursor += prevOutputSubScriptVarInt.size; CBByteArrayCopyByteArray(data, cursor, prevOutSubScript); cursor += prevOutSubScript->length; }else{ CBVarIntEncode(data, cursor, CBVarIntFromUInt64(0)); cursor++; } if ((signType == CB_SIGHASH_NONE || signType == CB_SIGHASH_SINGLE) && x != input) { CBByteArraySetInt32(data, cursor, 0); } else // SIGHASH_ALL or input index for signing sequence CBByteArraySetInt32(data, cursor, self->inputs[x]->sequence); cursor += 4; } } // Copy output data if (last5Bits == CB_SIGHASH_NONE){ CBVarInt varInt = CBVarIntFromUInt64(0); CBVarIntEncode(data, cursor, varInt); cursor++; }else if (last5Bits == CB_SIGHASH_SINGLE){ CBVarInt varInt = CBVarIntFromUInt64(input + 1); CBVarIntEncode(data, cursor, varInt); cursor += varInt.size; for (uint32_t x = 0; x < input; x++) { CBByteArraySetInt64(data, cursor, CB_OUTPUT_VALUE_MINUS_ONE); cursor += 8; CBVarIntEncode(data, cursor, CBVarIntFromUInt64(0)); cursor++; } CBByteArraySetInt64(data, cursor, self->outputs[input]->value); cursor += 8; varInt = CBVarIntFromUInt64(CBGetByteArray(self->outputs[input]->scriptObject)->length); CBVarIntEncode(data, cursor, varInt); cursor += varInt.size; CBByteArrayCopyByteArray(data, cursor, CBGetByteArray(self->outputs[input]->scriptObject)); cursor += varInt.val; }else{ // SIGHASH_ALL CBVarInt varInt = CBVarIntFromUInt64(self->outputNum); CBVarIntEncode(data, cursor, varInt); cursor += varInt.size; for (uint32_t x = 0; x < self->outputNum; x++) { CBByteArraySetInt64(data, cursor, self->outputs[x]->value); cursor += 8; varInt = CBVarIntFromUInt64(CBGetByteArray(self->outputs[x]->scriptObject)->length); CBVarIntEncode(data, cursor, varInt); cursor += varInt.size; CBByteArrayCopyByteArray(data, cursor, CBGetByteArray(self->outputs[x]->scriptObject)); cursor += varInt.val; } } // Set lockTime CBByteArraySetInt32(data, cursor, self->lockTime); CBByteArraySetInt32(data, cursor + 4, signType); assert(sizeOfData == cursor + 8); // Must always be like this uint8_t firstHash[32]; CBSha256(CBByteArrayGetData(data), sizeOfData, firstHash); CBSha256(firstHash, 32, hash); return CB_TX_HASH_OK; }
uint8_t CBVersionChecksumBytesGetVersion(CBVersionChecksumBytes * self) { return CBByteArrayGetByte(CBGetByteArray(self), 0); }
void CBFreeVersionChecksumBytes(void * vself) { CBVersionChecksumBytes * self = vself; if (self->cachedString) CBReleaseObject(self->cachedString); CBFreeByteArray(CBGetByteArray(self)); }
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(){ CBByteArray * walletKeyString = CBNewByteArrayFromString("xpub6DRhpXssnj7X6CwJgseK9oyFxSC8jk6nJz2SWkf5pjsQs12xv89Dfr627TtaZKkFbG6Aq23fmaNaf5KRo9iGfEXTTXvtd6gsXJTB8Sdah3B", false); CBChecksumBytes * walletKeyData = CBNewChecksumBytesFromString(walletKeyString, false); CBHDKey * cbkey = CBNewHDKeyFromData(CBByteArrayGetData(CBGetByteArray(walletKeyData))); CBAddress * address = CBNewAddressFromRIPEMD160Hash(CBHDKeyGetHash(cbkey), CB_NETWORK_PRODUCTION, false); CBByteArray * str = CBChecksumBytesGetString(CBGetChecksumBytes(address)); printf("%s\n", CBByteArrayGetData(str)); CBReleaseObject(address); // Test type if (CBHDKeyGetType(CB_HD_KEY_VERSION_PROD_PRIVATE) != CB_HD_KEY_TYPE_PRIVATE) { printf("CB_HD_KEY_VERSION_PROD_PRIVATE TYPE FAIL\n"); return EXIT_FAILURE; } if (CBHDKeyGetType(CB_HD_KEY_VERSION_PROD_PUBLIC) != CB_HD_KEY_TYPE_PUBLIC) { printf("CB_HD_KEY_VERSION_PROD_PUBLIC TYPE FAIL\n"); return EXIT_FAILURE; } if (CBHDKeyGetType(CB_HD_KEY_VERSION_TEST_PRIVATE) != CB_HD_KEY_TYPE_PRIVATE) { printf("CB_HD_KEY_VERSION_TEST_PRIVATE TYPE FAIL\n"); return EXIT_FAILURE; } if (CBHDKeyGetType(CB_HD_KEY_VERSION_TEST_PUBLIC) != CB_HD_KEY_TYPE_PUBLIC) { printf("CB_HD_KEY_VERSION_TEST_PUBLIC TYPE FAIL\n"); return EXIT_FAILURE; } // Test HMAC-SHA512 uint8_t hash[64]; CBHDKeyHmacSha512((uint8_t [37]){0x2f, 0xf7, 0xd6, 0x9f, 0x7a, 0x59, 0x0b, 0xb0, 0x5e, 0x68, 0xd1, 0xdc, 0x0f, 0xcf, 0x8d, 0xc2, 0x17, 0x59, 0xc9, 0x39, 0xbb, 0x6b, 0x9b, 0x02, 0x0f, 0x65, 0x5d, 0x53, 0x85, 0x3c, 0xb5, 0xc2, 0x14, 0x61, 0x4b, 0x24, 0x42}, (uint8_t [32]){0xa2, 0x55, 0x21, 0xe3, 0xc5, 0x5b, 0x65, 0xd1, 0xcf, 0x25, 0x4b, 0x6c, 0x85, 0x23, 0xdc, 0xbf, 0x89, 0x46, 0x8d, 0x1f, 0x09, 0x1f, 0x15, 0x87, 0x6b, 0xbb, 0xc7, 0xfd, 0xd5, 0x44, 0x28, 0x43}, hash); if (memcmp(hash, (uint8_t [64]){0xfa, 0xa7, 0x9d, 0x85, 0xe0, 0xe4, 0x3d, 0xae, 0x8c, 0x3f, 0x99, 0xf0, 0x70, 0xdf, 0x97, 0x56, 0x2b, 0x3f, 0xbb, 0x17, 0x35, 0x20, 0xe0, 0x87, 0x32, 0xa6, 0x64, 0xca, 0xd4, 0x55, 0x0b, 0xbe, 0xc1, 0x11, 0xe5, 0xf8, 0x80, 0xdb, 0xb7, 0x3d, 0x67, 0x74, 0xbb, 0xc2, 0x9f, 0x67, 0xd9, 0x67, 0xaa, 0x10, 0xac, 0x60, 0x18, 0x90, 0x7f, 0x35, 0x53, 0xe3, 0x21, 0x38, 0xf6, 0x5b, 0xbe, 0x69}, 64) != 0) { printf("HMAC FAIL\n"); return EXIT_FAILURE; } for (uint8_t x = 0; x < NUM_TEST_VECTORS; x++) { // Deserialise private key CBByteArray * masterString = CBNewByteArrayFromString(testVectors[x][0].privString, true); CBChecksumBytes * masterData = CBNewChecksumBytesFromString(masterString, false); CBReleaseObject(masterString); CBHDKey * key = CBNewHDKeyFromData(CBByteArrayGetData(CBGetByteArray(masterData))); CBReleaseObject(masterData); checkKey(key, x, 0); for (uint8_t y = 0; y < NUM_CHILDREN; y++) { if (testVectors[x][y+1].childID.priv == false) { // Derive public child and check public key is correct by address CBHDKey * newKey = CBNewHDKey(false); key->versionBytes = CB_HD_KEY_VERSION_PROD_PUBLIC; CBHDKeyDeriveChild(key, testVectors[x][y+1].childID, newKey); key->versionBytes = CB_HD_KEY_VERSION_PROD_PRIVATE; CBAddress * address = CBNewAddressFromRIPEMD160Hash(CBHDKeyGetHash(newKey), CB_NETWORK_PRODUCTION, false); CBByteArray * str = CBChecksumBytesGetString(CBGetChecksumBytes(address)); CBReleaseObject(address); if (memcmp(CBByteArrayGetData(str), testVectors[x][y + 1].addr, 34) != 0) { printf("ADDR FROM PUB FAIL AT %u - %u\n", x, y + 1); exit(EXIT_FAILURE); } CBReleaseObject(str); // Check serialisation of public key uint8_t * keyData = malloc(82); CBHDKeySerialise(newKey, keyData); CBChecksumBytes * checksumBytes = CBNewChecksumBytesFromBytes(keyData, 82, false); str = CBChecksumBytesGetString(checksumBytes); CBReleaseObject(checksumBytes); if (memcmp(CBByteArrayGetData(str), testVectors[x][y+1].pubString, 111) != 0) { printf("PUBLIC KEY STRING FROM PUB FAIL AT %u - %u\n", x, y); exit(EXIT_FAILURE); } CBReleaseObject(str); free(newKey); } // Derive private child CBHDKey * newKey = CBNewHDKey(true); CBHDKeyDeriveChild(key, testVectors[x][y+1].childID, newKey); free(key); key = newKey; checkKey(key, x, y+1); } free(key); } return EXIT_SUCCESS; }