sigHash (OP)
Newbie
Offline
Activity: 6
Merit: 2
|
|
February 22, 2019, 01:04:05 AM |
|
Hi everyone! I have an unsigned transaction consuming p2sh inputs, list of redeem scripts, master private key and the corresponding derivation paths and I would like to "half-sign" this multi sig transaction using this SPV library. https://github.com/breadwallet/breadwallet-core/blob/master/BRTransaction.c
I assume I need to parse the unsigned txHex using: // buf must contain a serialized tx // retruns a transaction that must be freed by calling BRTransactionFree() BRTransaction *BRTransactionParse(const uint8_t *buf, size_t bufLen) { assert(buf != NULL || bufLen == 0); if (! buf) return NULL; int isSigned = 1, witnessFlag = 0; uint8_t *sBuf; size_t i, j, off = 0, witnessOff = 0, sLen = 0, len = 0, count; BRTransaction *tx = BRTransactionNew(); BRTxInput *input; BRTxOutput *output; tx->version = (off + sizeof(uint32_t) <= bufLen) ? UInt32GetLE(&buf[off]) : 0; off += sizeof(uint32_t); tx->inCount = (size_t)BRVarInt(&buf[off], (off <= bufLen ? bufLen - off : 0), &len); off += len; if (tx->inCount == 0 && off + 1 <= bufLen) witnessFlag = buf[off++]; if (witnessFlag) { tx->inCount = (size_t)BRVarInt(&buf[off], (off <= bufLen ? bufLen - off : 0), &len); off += len; }
array_set_count(tx->inputs, tx->inCount); for (i = 0; off <= bufLen && i < tx->inCount; i++) { input = &tx->inputs[i]; input->txHash = (off + sizeof(UInt256) <= bufLen) ? UInt256Get(&buf[off]) : UINT256_ZERO; off += sizeof(UInt256); input->index = (off + sizeof(uint32_t) <= bufLen) ? UInt32GetLE(&buf[off]) : 0; off += sizeof(uint32_t); sLen = (size_t)BRVarInt(&buf[off], (off <= bufLen ? bufLen - off : 0), &len); off += len; if (off + sLen <= bufLen && BRAddressFromScriptPubKey(NULL, 0, &buf[off], sLen) > 0) { BRTxInputSetScript(input, &buf[off], sLen); input->amount = (off + sLen + sizeof(uint64_t) <= bufLen) ? UInt64GetLE(&buf[off + sLen]) : 0; off += sizeof(uint64_t); isSigned = 0; } else if (off + sLen <= bufLen) BRTxInputSetSignature(input, &buf[off], sLen); off += sLen; if (! witnessFlag) BRTxInputSetWitness(input, &buf[off], 0); // set witness to empty byte array input->sequence = (off + sizeof(uint32_t) <= bufLen) ? UInt32GetLE(&buf[off]) : 0; off += sizeof(uint32_t); } tx->outCount = (size_t)BRVarInt(&buf[off], (off <= bufLen ? bufLen - off : 0), &len); off += len; array_set_count(tx->outputs, tx->outCount); for (i = 0; off <= bufLen && i < tx->outCount; i++) { output = &tx->outputs[i]; output->amount = (off + sizeof(uint64_t) <= bufLen) ? UInt64GetLE(&buf[off]) : 0; off += sizeof(uint64_t); sLen = (size_t)BRVarInt(&buf[off], (off <= bufLen ? bufLen - off : 0), &len); off += len; if (off + sLen <= bufLen) BRTxOutputSetScript(output, &buf[off], sLen); off += sLen; } for (i = 0, witnessOff = off; witnessFlag && off <= bufLen && i < tx->inCount; i++) { input = &tx->inputs[i]; count = (size_t)BRVarInt(&buf[off], (off <= bufLen ? bufLen - off : 0), &len); off += len; for (j = 0, sLen = 0; j < count; j++) { sLen += (size_t)BRVarInt(&buf[off + sLen], (off + sLen <= bufLen ? bufLen - (off + sLen) : 0), &len); sLen += len; } if (off + sLen <= bufLen) BRTxInputSetWitness(input, &buf[off], sLen); off += sLen; } tx->lockTime = (off + sizeof(uint32_t) <= bufLen) ? UInt32GetLE(&buf[off]) : 0; off += sizeof(uint32_t); if (tx->inCount == 0 || off > bufLen) { BRTransactionFree(tx); tx = NULL; } else if (isSigned && witnessFlag) { BRSHA256_2(&tx->wtxHash, buf, off); sBuf = malloc((witnessOff - 2) + sizeof(uint32_t)); UInt32SetLE(sBuf, tx->version); memcpy(&sBuf[sizeof(uint32_t)], &buf[sizeof(uint32_t) + 2], witnessOff - (sizeof(uint32_t) + 2)); UInt32SetLE(&sBuf[witnessOff - 2], tx->lockTime); BRSHA256_2(&tx->txHash, sBuf, (witnessOff - 2) + sizeof(uint32_t)); free(sBuf); } else if (isSigned) { BRSHA256_2(&tx->txHash, buf, off); tx->wtxHash = tx->txHash; } return tx; }
and then sign it using: // adds signatures to any inputs with NULL signatures that can be signed with any keys // forkId is 0 for bitcoin, 0x40 for b-cash, 0x4f for b-gold // returns true if tx is signed int BRTransactionSign(BRTransaction *tx, int forkId, BRKey keys[], size_t keysCount) { UInt160 pkh[keysCount]; size_t i, j; assert(tx != NULL); assert(keys != NULL || keysCount == 0); for (i = 0; tx && i < keysCount; i++) { pkh[i] = BRKeyHash160(&keys[i]); } for (i = 0; tx && i < tx->inCount; i++) { BRTxInput *input = &tx->inputs[i]; const uint8_t *hash = BRScriptPKH(input->script, input->scriptLen); j = 0; while (j < keysCount && (! hash || ! UInt160Eq(pkh[j], UInt160Get(hash)))) j++; if (j >= keysCount) continue; const uint8_t *elems[BRScriptElements(NULL, 0, input->script, input->scriptLen)]; size_t elemsCount = BRScriptElements(elems, sizeof(elems)/sizeof(*elems), input->script, input->scriptLen); uint8_t pubKey[BRKeyPubKey(&keys[j], NULL, 0)]; size_t pkLen = BRKeyPubKey(&keys[j], pubKey, sizeof(pubKey)); uint8_t sig[73], script[1 + sizeof(sig) + 1 + sizeof(pubKey)]; size_t sigLen, scriptLen; UInt256 md = UINT256_ZERO; if (elemsCount == 2 && *elems[0] == OP_0 && *elems[1] == 20) { // pay-to-witness-pubkey-hash uint8_t data[_BRTransactionWitnessData(tx, NULL, 0, i, forkId | SIGHASH_ALL)]; size_t dataLen = _BRTransactionWitnessData(tx, data, sizeof(data), i, forkId | SIGHASH_ALL); BRSHA256_2(&md, data, dataLen); sigLen = BRKeySign(&keys[j], sig, sizeof(sig) - 1, md); sig[sigLen++] = forkId | SIGHASH_ALL; scriptLen = BRScriptPushData(script, sizeof(script), sig, sigLen); scriptLen += BRScriptPushData(&script[scriptLen], sizeof(script) - scriptLen, pubKey, pkLen); BRTxInputSetSignature(input, script, 0); BRTxInputSetWitness(input, script, scriptLen); } else if (elemsCount >= 2 && *elems[elemsCount - 2] == OP_EQUALVERIFY) { // pay-to-pubkey-hash uint8_t data[_BRTransactionData(tx, NULL, 0, i, forkId | SIGHASH_ALL)]; size_t dataLen = _BRTransactionData(tx, data, sizeof(data), i, forkId | SIGHASH_ALL); BRSHA256_2(&md, data, dataLen); sigLen = BRKeySign(&keys[j], sig, sizeof(sig) - 1, md); sig[sigLen++] = forkId | SIGHASH_ALL; scriptLen = BRScriptPushData(script, sizeof(script), sig, sigLen); scriptLen += BRScriptPushData(&script[scriptLen], sizeof(script) - scriptLen, pubKey, pkLen); BRTxInputSetSignature(input, script, scriptLen); BRTxInputSetWitness(input, script, 0); } else { // pay-to-pubkey uint8_t data[_BRTransactionData(tx, NULL, 0, i, forkId | SIGHASH_ALL)]; size_t dataLen = _BRTransactionData(tx, data, sizeof(data), i, forkId | SIGHASH_ALL);
BRSHA256_2(&md, data, dataLen); sigLen = BRKeySign(&keys[j], sig, sizeof(sig) - 1, md); sig[sigLen++] = forkId | SIGHASH_ALL; scriptLen = BRScriptPushData(script, sizeof(script), sig, sigLen); BRTxInputSetSignature(input, script, scriptLen); BRTxInputSetWitness(input, script, 0); } } if (tx && BRTransactionIsSigned(tx)) { uint8_t data[BRTransactionSerialize(tx, NULL, 0)]; size_t len = BRTransactionSerialize(tx, data, sizeof(data)); BRTransaction *t = BRTransactionParse(data, len); if (t) tx->txHash = t->txHash, tx->wtxHash = t->wtxHash; if (t) BRTransactionFree(t); return 1; } else return 0; }
Can anyone explain to me what's the purpose of BRScriptPushData in the signing step? Thanks a lot!
|