2019-03-11 14:39:38 +01:00
|
|
|
/*
|
|
|
|
|
* This file is part of the Flowee project
|
|
|
|
|
* Copyright (C) 2019 Tom Zander <tomz@freedommail.ch>
|
|
|
|
|
*
|
|
|
|
|
* This program is free software: you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
#include "TransactionBuilder.h"
|
|
|
|
|
|
2019-03-13 20:32:18 +01:00
|
|
|
#include <primitives/pubkey.h>
|
|
|
|
|
#include <primitives/key.h>
|
|
|
|
|
|
2019-03-11 14:39:38 +01:00
|
|
|
TransactionBuilder::TransactionBuilder()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TransactionBuilder::TransactionBuilder(const Tx &existingTx)
|
|
|
|
|
: m_transaction(existingTx.createOldTransaction())
|
|
|
|
|
{
|
2019-03-13 20:32:18 +01:00
|
|
|
m_signInfo.resize(m_transaction.vin.size());
|
2019-03-11 14:39:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TransactionBuilder::TransactionBuilder(const CTransaction &existingTx)
|
|
|
|
|
: m_transaction(existingTx)
|
|
|
|
|
{
|
2019-03-13 20:32:18 +01:00
|
|
|
m_signInfo.resize(m_transaction.vin.size());
|
2019-03-11 14:39:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TransactionBuilder::appendInput(const uint256 &txid, int outputIndex)
|
|
|
|
|
{
|
|
|
|
|
const size_t pos = m_transaction.vin.size();
|
|
|
|
|
if (pos > 1000) // kind of random large number
|
|
|
|
|
throw std::runtime_error("Too many inputs");
|
|
|
|
|
m_transaction.vin.resize(pos + 1);
|
2019-03-13 20:32:18 +01:00
|
|
|
m_signInfo.resize(pos + 1);
|
2019-03-11 14:39:38 +01:00
|
|
|
CTxIn &in = m_transaction.vin[pos];
|
|
|
|
|
in.prevout.hash = txid;
|
|
|
|
|
in.prevout.n = outputIndex;
|
|
|
|
|
switch (m_defaultLocking) {
|
|
|
|
|
case TransactionBuilder::LockMiningOnTime:
|
|
|
|
|
case TransactionBuilder::LockMiningOnBlock:
|
|
|
|
|
in.nSequence = in.SEQUENCE_LOCKTIME_DISABLE_FLAG;
|
|
|
|
|
break;
|
|
|
|
|
default: // default of the instance is fine
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
m_curInput = static_cast<int>(pos);
|
|
|
|
|
return m_curInput;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TransactionBuilder::selectInput(int index)
|
|
|
|
|
{
|
2019-03-28 20:23:09 +01:00
|
|
|
assert(index >= 0);
|
|
|
|
|
if (index < 0) throw std::runtime_error("Index is a natural number");
|
2019-03-11 14:39:38 +01:00
|
|
|
m_curInput = std::min(static_cast<int>(m_transaction.vin.size()) -1, index);
|
|
|
|
|
return m_curInput;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 20:32:18 +01:00
|
|
|
void TransactionBuilder::pushInputSignature(const CKey &privKey, const CScript &prevOutScript, int64_t amount, SignInputs inputs, SignOutputs outputs)
|
2019-03-11 14:39:38 +01:00
|
|
|
{
|
2019-03-13 20:32:18 +01:00
|
|
|
checkCurInput();
|
|
|
|
|
SignInfo &si = m_signInfo[m_curInput];
|
|
|
|
|
si.hashType = inputs == SignOnlyThisInput ? 0xC0 : 0x40;
|
2019-03-11 14:39:38 +01:00
|
|
|
switch (outputs) {
|
2019-03-13 20:32:18 +01:00
|
|
|
case SignAllOuputs: si.hashType += 1; break;
|
|
|
|
|
case SignNoOutputs: si.hashType += 2; break;
|
|
|
|
|
case SignSingleOutput: si.hashType += 3; break;
|
2019-03-11 14:39:38 +01:00
|
|
|
}
|
2019-03-13 20:32:18 +01:00
|
|
|
|
|
|
|
|
si.privKey = privKey;
|
|
|
|
|
si.prevOutScript = prevOutScript;
|
|
|
|
|
si.amount = amount;
|
2019-03-11 14:39:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransactionBuilder::deleteInput(int index)
|
|
|
|
|
{
|
|
|
|
|
assert(index >= 0);
|
|
|
|
|
assert(index < m_transaction.vin.size());
|
2019-03-13 20:32:18 +01:00
|
|
|
assert(index < m_signInfo.size());
|
2019-03-11 14:39:38 +01:00
|
|
|
auto iter = m_transaction.vin.begin();
|
2019-03-13 20:32:18 +01:00
|
|
|
iter += index;
|
2019-03-11 14:39:38 +01:00
|
|
|
m_transaction.vin.erase(iter);
|
2019-03-13 20:32:18 +01:00
|
|
|
|
|
|
|
|
auto iter2 = m_signInfo.begin();
|
|
|
|
|
iter2 += index;
|
|
|
|
|
m_signInfo.erase(iter2);
|
|
|
|
|
|
2019-03-11 14:39:38 +01:00
|
|
|
selectInput(index);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TransactionBuilder::appendOutput(int64_t amount)
|
|
|
|
|
{
|
|
|
|
|
const size_t pos = m_transaction.vout.size();
|
|
|
|
|
if (pos > 1000) // kind of random large number
|
|
|
|
|
throw std::runtime_error("Too many outputs");
|
|
|
|
|
m_transaction.vout.resize(pos + 1);
|
|
|
|
|
CTxOut &out = m_transaction.vout[pos];
|
|
|
|
|
out.nValue = amount;
|
|
|
|
|
|
|
|
|
|
m_curOutput = static_cast<int>(pos);
|
|
|
|
|
return m_curOutput;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TransactionBuilder::selectOutput(int index)
|
|
|
|
|
{
|
2019-03-28 20:23:09 +01:00
|
|
|
assert(index >= 0);
|
|
|
|
|
if (index < 0) throw std::runtime_error("Index is a natural number");
|
2019-03-11 14:39:38 +01:00
|
|
|
m_curOutput = std::min(static_cast<int>(m_transaction.vout.size()) -1, index);
|
|
|
|
|
return m_curOutput;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 20:32:18 +01:00
|
|
|
void TransactionBuilder::pushOutputPay2Address(const CKeyID &address)
|
2019-03-11 14:39:38 +01:00
|
|
|
{
|
2019-03-13 20:32:18 +01:00
|
|
|
checkCurOutput();
|
2019-03-11 14:39:38 +01:00
|
|
|
CScript outScript;
|
|
|
|
|
outScript << OP_DUP << OP_HASH160;
|
|
|
|
|
std::vector<unsigned char> data(address.begin(), address.end());
|
|
|
|
|
outScript << data;
|
|
|
|
|
outScript << OP_EQUALVERIFY << OP_CHECKSIG;
|
2019-10-09 19:36:29 +02:00
|
|
|
pushOutputScript(outScript);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransactionBuilder::pushOutputScript(const CScript &script)
|
|
|
|
|
{
|
|
|
|
|
assert(m_curOutput >= 0);
|
|
|
|
|
assert(static_cast<size_t>(m_curOutput) < m_transaction.vout.size());
|
|
|
|
|
m_transaction.vout[static_cast<size_t>(m_curOutput)].scriptPubKey = script;
|
2019-03-11 14:39:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransactionBuilder::deleteOutput(int index)
|
|
|
|
|
{
|
|
|
|
|
assert(index >= 0);
|
|
|
|
|
assert(index < m_transaction.vout.size());
|
|
|
|
|
auto iter = m_transaction.vout.begin();
|
|
|
|
|
iter+=index;
|
|
|
|
|
m_transaction.vout.erase(iter);
|
|
|
|
|
selectOutput(index);
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-13 20:32:18 +01:00
|
|
|
Tx TransactionBuilder::createTransaction(Streaming::BufferPool *pool)
|
2019-03-11 14:39:38 +01:00
|
|
|
{
|
2019-03-13 20:32:18 +01:00
|
|
|
// sign all inputs we can.
|
|
|
|
|
assert(m_transaction.vin.size() == m_signInfo.size());
|
|
|
|
|
for (size_t i = 0; i < m_transaction.vin.size(); ++i) {
|
|
|
|
|
const SignInfo &si = m_signInfo[i];
|
|
|
|
|
if (si.prevOutScript.empty())
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
uint256 hashPrevouts;
|
|
|
|
|
if (!(si.hashType & SignOnlyThisInput)) {
|
|
|
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
|
|
|
for (size_t n = 0; n < m_transaction.vin.size(); ++n) {
|
|
|
|
|
ss << m_transaction.vin[n].prevout;
|
|
|
|
|
}
|
|
|
|
|
hashPrevouts = ss.GetHash();
|
|
|
|
|
}
|
|
|
|
|
uint256 hashSequence;
|
|
|
|
|
if (!(si.hashType & SignOnlyThisInput) && (si.hashType & 0x1f) != SignSingleOutput
|
|
|
|
|
&& (si.hashType & 0x1f) != SignNoOutputs) {
|
|
|
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
|
|
|
for (size_t n = 0; n < m_transaction.vin.size(); ++n) {
|
|
|
|
|
ss << m_transaction.vin[n].nSequence;
|
|
|
|
|
}
|
|
|
|
|
hashSequence = ss.GetHash();
|
|
|
|
|
}
|
|
|
|
|
uint256 hashOutputs;
|
|
|
|
|
if ((si.hashType & 0x1f) != SignSingleOutput && (si.hashType & 0x1f) != SignNoOutputs) {
|
|
|
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
|
|
|
for (size_t n = 0; n < m_transaction.vout.size(); ++n) {
|
|
|
|
|
ss << m_transaction.vout[n];
|
|
|
|
|
}
|
|
|
|
|
hashOutputs = ss.GetHash();
|
|
|
|
|
} else if ((si.hashType & 0x1f) == SignSingleOutput && i < m_transaction.vout.size()) {
|
|
|
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
|
|
|
ss << m_transaction.vout[i];
|
|
|
|
|
hashOutputs = ss.GetHash();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// use FORKID based creation of the hash we will sign.
|
|
|
|
|
CHashWriter ss(SER_GETHASH, 0);
|
|
|
|
|
ss << m_transaction.nVersion << hashPrevouts << hashSequence;
|
|
|
|
|
ss << m_transaction.vin[i].prevout;
|
|
|
|
|
ss << static_cast<const CScriptBase &>(si.prevOutScript);
|
|
|
|
|
ss << si.amount << m_transaction.vin[i].nSequence << hashOutputs;
|
|
|
|
|
ss << m_transaction.nLockTime << (int) si.hashType;
|
|
|
|
|
const uint256 hash = ss.GetHash();
|
|
|
|
|
|
|
|
|
|
// the rest assumes P2PKH for now.
|
|
|
|
|
std::vector<unsigned char> vchSig;
|
|
|
|
|
si.privKey.Sign(hash, vchSig);
|
|
|
|
|
vchSig.push_back((uint8_t) si.hashType);
|
|
|
|
|
|
|
|
|
|
m_transaction.vin[i].scriptSig = CScript();
|
|
|
|
|
m_transaction.vin[i].scriptSig << vchSig;
|
|
|
|
|
m_transaction.vin[i].scriptSig << ToByteVector(si.privKey.GetPubKey());
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-11 14:39:38 +01:00
|
|
|
return Tx::fromOldTransaction(m_transaction, pool);
|
|
|
|
|
}
|
2019-03-13 20:32:18 +01:00
|
|
|
|
|
|
|
|
void TransactionBuilder::checkCurInput()
|
|
|
|
|
{
|
|
|
|
|
assert(0 <= m_curInput);
|
|
|
|
|
assert(m_transaction.vin.size() > m_curInput);
|
|
|
|
|
if (0 > m_curInput || m_transaction.vin.size() <= m_curInput)
|
|
|
|
|
throw std::runtime_error("current input out of range");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TransactionBuilder::checkCurOutput()
|
|
|
|
|
{
|
|
|
|
|
assert(m_curOutput >= 0);
|
|
|
|
|
assert(m_curOutput < m_transaction.vout.size());
|
|
|
|
|
}
|