uint32_t CBTransactionCalculateLength(CBTransaction * self){ uint32_t len = 8 + CBVarIntSizeOf(self->inputNum) + CBVarIntSizeOf(self->outputNum); // 8 is for version and lockTime. for (uint32_t x = 0; x < self->inputNum; x++) len += CBTransactionInputCalculateLength(self->inputs[x]); for (uint32_t x = 0; x < self->outputNum; x++) len += CBTransactionOutputCalculateLength(self->outputs[x]); return len; }
int CBAlertCalculateLength(CBAlert * self) { int len = 40 + CBVarIntSizeOf(self->userAgentNum) + CBVarIntSizeOf(self->setCancelNum) + self->setCancelNum * 4; len += self->displayedComment ? self->displayedComment->length : 1; len += self->hiddenComment ? self->hiddenComment->length : 1; len += self->reserved ? self->reserved->length : 1; for (int x = 0; x < self->userAgentNum; x++) len += CBVarIntSizeOf(self->userAgents[x]->length) + self->userAgents[x]->length; len += CBVarIntSizeOf(len); return len + CBVarIntSizeOf(self->signature->length) + self->signature->length; }
uint32_t CBTransactionOutputCalculateLength(CBTransactionOutput * self){ if (CBGetMessage(self)->serialised) { // If it is serailised, the var int may be of a different size. uint8_t byte = CBByteArrayGetByte(CBGetMessage(self)->bytes, 0); return (byte < 253 ? 1 : (byte == 253 ? 3 : (byte == 254 ? 5 : 9))) + self->scriptObject->length + 8; } else return CBVarIntSizeOf(self->scriptObject->length) + self->scriptObject->length + 8; }
uint32_t CBBlockCalculateLength(CBBlock * self, bool transactions){ uint32_t len = 80 + CBVarIntSizeOf(self->transactionNum); if (transactions) { for (uint32_t x = 0; x < self->transactionNum; x++) len += CBTransactionCalculateLength(self->transactions[x]); return len; }else return len + 1; // Plus the stupid pointless null byte. }
uint32_t CBVersionCalculateLength(CBVersion * self){ uint32_t len = 46; // Version, services, time and receiving address. if (self->version >= 106) { if (self->userAgent->length > 400) return 0; len += 38 + CBVarIntSizeOf(self->userAgent->length) + self->userAgent->length; // Source address, nounce, user-agent and block height. } return len; }
uint32_t CBNetworkAddressListCalculateLength(CBNetworkAddressList * self){ return CBVarIntSizeOf(self->addrNum) + self->addrNum * (self->timeStamps ? 30 : 26); }
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; }
bool CBAlertSerialisePayload(CBAlert * self) { CBByteArray * bytes = CBGetMessage(self)->bytes; if (! bytes) { CBLogError("Attempting to serialise a CBAlert with no bytes."); return false; } // Create varints and calculate length whilst at it. CBVarInt setCancelLen = CBVarIntFromUInt64(self->setCancelNum); CBVarInt userAgentLen = CBVarIntFromUInt64(self->userAgentNum); CBVarInt hiddenCommentLen = CBVarIntFromUInt64(self->hiddenComment ? self->hiddenComment->length : 0); CBVarInt displayedCommentLen = CBVarIntFromUInt64(self->displayedComment ? self->displayedComment->length : 0); CBVarInt reservedLen = CBVarIntFromUInt64(self->reserved ? self->reserved->length : 0); int length = 40 + setCancelLen.size + setCancelLen.val * 4 + userAgentLen.size + hiddenCommentLen.size + hiddenCommentLen.val + displayedCommentLen.size + displayedCommentLen.val + reservedLen.size + reservedLen.val; // Add user agents to length for (int x = 0; x < self->userAgentNum; x++) length += self->userAgents[x]->length + CBVarIntSizeOf(self->userAgents[x]->length); // So far length is for payload so make payload var int. CBVarInt payloadLen = CBVarIntFromUInt64(length); // Add the length of this var int. length += payloadLen.size; // Check length, with at least one byte for signature (could be 0 varint) if (bytes->length < length + 1) { CBLogError("Attempting to serialise a CBAlert with less bytes than required for the payload and a signature var int."); return false; } // Serialise the payload section. CBByteArraySetVarInt(bytes, 0, payloadLen); int cursor = payloadLen.size; CBByteArraySetInt32(bytes, cursor, self->version); cursor += 4; CBByteArraySetInt64(bytes, cursor, self->relayUntil); cursor += 8; CBByteArraySetInt64(bytes, cursor, self->expiration); cursor += 8; CBByteArraySetInt32(bytes, cursor, self->ID); cursor += 4; CBByteArraySetInt32(bytes, cursor, self->cancel); cursor += 4; // Cancel set CBByteArraySetVarInt(bytes, cursor, setCancelLen); cursor += setCancelLen.size; for (int x = 0; x < self->setCancelNum; x++) { CBByteArraySetInt32(bytes, cursor, self->setCancel[x]); cursor += 4; } CBByteArraySetInt32(bytes, cursor, self->minVer); cursor += 4; CBByteArraySetInt32(bytes, cursor, self->maxVer); cursor += 4; // User agent set CBByteArraySetVarInt(bytes, cursor, userAgentLen); cursor += userAgentLen.size; for (int x = 0; x < self->userAgentNum; x++) { CBVarInt aUserAgentLen = CBVarIntFromUInt64(self->userAgents[x]->length); CBByteArraySetVarInt(bytes, cursor, aUserAgentLen); cursor += aUserAgentLen.size; CBByteArrayCopyByteArray(bytes, cursor, self->userAgents[x]); CBByteArrayChangeReference(self->userAgents[x], bytes, cursor); cursor += aUserAgentLen.val; } CBByteArraySetInt32(bytes, cursor, self->priority); cursor += 4; // Hidden comment CBByteArraySetVarInt(bytes, cursor, hiddenCommentLen); cursor += hiddenCommentLen.size; if (self->hiddenComment) { CBByteArrayCopyByteArray(bytes, cursor, self->hiddenComment); CBByteArrayChangeReference(self->hiddenComment, bytes, cursor); cursor += hiddenCommentLen.val; } // Displayed comment CBByteArraySetVarInt(bytes, cursor, displayedCommentLen); cursor += displayedCommentLen.size; if (self->displayedComment) { CBByteArrayCopyByteArray(bytes, cursor, self->displayedComment); CBByteArrayChangeReference(self->displayedComment, bytes, cursor); cursor += displayedCommentLen.val; } // Reserved CBByteArraySetVarInt(bytes, cursor, reservedLen); cursor += reservedLen.size; if (self->reserved) { CBByteArrayCopyByteArray(bytes, cursor, self->reserved); CBByteArrayChangeReference(self->reserved, bytes, cursor); cursor += reservedLen.val; } self->payload = CBNewByteArraySubReference(bytes, payloadLen.size, cursor - payloadLen.size); return true; }
uint32_t CBBlockHeadersCalculateLength(CBBlockHeaders * self) { return CBVarIntSizeOf(self->headerNum) + self->headerNum * 81; }
uint32_t CBAddressBroadcastCalculateLength(CBAddressBroadcast * self){ return CBVarIntSizeOf(self->addrNum) + self->addrNum * (self->timeStamps ? 30 : 26); }
uint32_t CBInventoryBroadcastCalculateLength(CBInventoryBroadcast * self){ return CBVarIntSizeOf(self->itemNum) + self->itemNum * 36; }
int CBInventoryCalculateLength(CBInventory * self) { return CBVarIntSizeOf(self->itemNum) + self->itemNum * 36; }