Bitcoin Forum
April 25, 2024, 04:15:34 PM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Need help for signing P2SH in C  (Read 123 times)
sigHash (OP)
Newbie
*
Offline Offline

Activity: 6
Merit: 2


View Profile
February 22, 2019, 01:04:05 AM
Merited by bones261 (2)
 #1

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:

Code:
// 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:

Code:
// 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!

Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!