[Dev] Documenting Feathercoin Specific Software settings - Part 9
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/qt/multisigdialog.cpp
Large new file, 966 lines of code
+#include "multisigdialog.h" +#include "ui_multisigdialog.h" + +#include "wallet.h" +#include "walletmodel.h" +#include "bitcoinunits.h" +#include "addressbookpage.h" +#include "optionsmodel.h" +#include "sendcoinsentry.h" +#include "guiutil.h" +#include "askpassphrasedialog.h" +#include "base58.h" +#include "init.h" +#include "coincontrol.h" +#include "createmultisigaddrdialog.h" +#include "rpcprotocol.h" + +#include "json/json_spirit.h" +#include "json/json_spirit_reader_template.h" +#include "json/json_spirit_writer_template.h" +#include "json/json_spirit_utils.h" +#include "json/json_spirit_value.h" +using namespace json_spirit; + +#include <QMessageBox> +#include <QTextDocument> +#include <QScrollBar> +#include <QFile> +#include <QTextStream> + +CCoinControl* MultiSigDialog::coinControl = new CCoinControl(); +CTransaction* MultiSigDialog::rawTx = new CTransaction(); + +MultiSigDialog::MultiSigDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::MultiSigDialog), + model(0) +{ + ui->setupUi(this);
Example / start of new code
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/qt/multisigdialog.h
Large new file 91 lines o code
+#ifndef MULTISIGDIALOG_H +#define MULTISIGDIALOG_H + +#include <QDialog> +#include <QString> + +namespace Ui { + class MultiSigDialog; +} + +class CWallet; +class WalletModel; +class SendCoinsEntry; +class SendCoinsRecipient; +class CCoinControl; +class CTransaction; + +QT_BEGIN_NAMESPACE +class QUrl; +QT_END_NAMESPACE + +/** Dialog for sending bitcoins */ +class MultiSigDialog : public QDialog +{ + Q_OBJECT + +public: + explicit MultiSigDialog(QWidget *parent = 0); + ~MultiSigDialog(); + + void setModel(WalletModel *model); + + /** Set up the tab chain manually, as Qt messes up the tab chain by default in some cases (issue https://bugreports.qt-project.org/browse/QTBUG-10907). + */ + QWidget *setupTabChain(QWidget *prev); + + void setAddress(const QString &address); + void pasteEntry(const SendCoinsRecipient &rv); + bool handleURI(const QString &uri); + + static CCoinControl *coinControl; + static CTransaction *rawTx; + +public slots: + void clear(); + void reject(); + void accept(); + void exportDraft(); + void importDraft(); + void editEnable(bool enable); + + void signAddress0(); + void signAddress1(); + void signAddress2(); + void signTransaction(QString *addrStr = NULL); + + SendCoinsEntry *addEntry(); + void updateRemoveEnabled(); + void setSharedBalance(qint64 balance, qint64 unconfirmedBalance, qint64 immatureBalance);
Start of new file code
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/qt/res/icons/*
src/qt/res/icons/multisig.png
Icon File added
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/qt/sendcoinsentry.cpp
+void SendCoinsEntry::setRemoveEnabled(bool enabled) +{ + ui->deleteButton->setEnabled(enabled); +} +
Code added
+void SendCoinsEntry::setFieldEnable(bool enable) +{ + ui->payTo->setEnabled(enable); + ui->addAsLabel->setEnabled(enable); + ui->payAmount->setEnabled(enable); + ui->addressBookButton->setEnabled(enable); + ui->pasteButton->setEnabled(enable); + ui->deleteButton->setEnabled(enable); +} +
Code added
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/qt/sendcoinsentry.h
+ void setFieldEnable(bool enable);
Code added
+ void setRemoveEnabled(bool enabled);
Code added
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/qt/walletframe.cpp
+void WalletFrame::gotoMultiSigPage() +{ + QMap<QString, WalletView*>::const_iterator i; + for (i = mapWalletViews.constBegin(); i != mapWalletViews.constEnd(); ++i) + i.value()->gotoMultiSigPage(); +} +
Code added
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/qt/walletframe.cpp
+qint64 WalletModel::getSharedBalance(const CCoinControl *coinControl) const +{ + if (coinControl) + { + int64 nBalance = 0; + std::vector<COutput> vCoins; + wallet->AvailableSharedCoins(vCoins, true, coinControl); + BOOST_FOREACH(const COutput& out, vCoins) + nBalance += out.tx->vout[out.i].nValue; + + return nBalance; + } + + return wallet->GetSharedBalance(); +} + +qint64 WalletModel::getSharedUnconfirmedBalance() const +{ + return wallet->GetSharedUnconfirmedBalance(); +} + +qint64 WalletModel::getSharedImmatureBalance() const +{ + return wallet->GetSharedImmatureBalance(); +} +
Code added
+WalletModel::SendCoinsReturn WalletModel::createRawTransaction( + const QList<SendCoinsRecipient> &recipients, CTransaction& txNew, const CCoinControl *coinControl, bool isMultiSig) +{ + qint64 total = 0; + QSet<QString> setAddress; + QString hex; + + if(recipients.empty()) + { + return OK; + } + + // Pre-check input data for validity + foreach(const SendCoinsRecipient &rcp, recipients) + { + if(!validateAddress(rcp.address)) + { + return InvalidAddress; + } + setAddress.insert(rcp.address); + + if(rcp.amount <= 0) + { + return InvalidAmount; + } + total += rcp.amount; + } + + if(recipients.size() > setAddress.size()) + { + return DuplicateAddress; + } + + int64 nBalance; + if ( isMultiSig ) + nBalance = getSharedBalance(coinControl); + else + nBalance = getBalance(coinControl); + + if(total > nBalance) + { + return AmountExceedsBalance; + } + + if((total + nTransactionFee) > nBalance) + { + return SendCoinsReturn(AmountWithFeeExceedsBalance, nTransactionFee); + } + + { + LOCK2(cs_main, wallet->cs_wallet); + + // Sendmany + std::vector<std::pair<CScript, int64> > vecSend; + foreach(const SendCoinsRecipient &rcp, recipients) + { + CScript scriptPubKey; + scriptPubKey.SetDestination(CBitcoinAddress(rcp.address.toStdString()).Get()); + vecSend.push_back(make_pair(scriptPubKey, rcp.amount)); + } + + int64 nFeeRequired = 0; + std::string strFailReason; + CReserveKey reservekey(wallet); + bool fCreated = wallet->CreateRawTransaction(vecSend, txNew, nFeeRequired, strFailReason, isMultiSig, reservekey, coinControl); + + if(!fCreated) + { + if((total + nFeeRequired) > nBalance) + { + return SendCoinsReturn(AmountWithFeeExceedsBalance, nFeeRequired); + } + emit message(tr("Send Coins"), QString::fromStdString(strFailReason), + CClientUIInterface::MSG_ERROR); + return TransactionCreationFailed; + } + /*if(!uiInterface.ThreadSafeAskFee(nFeeRequired)) + { + return Aborted; + }*/ + hex = QString::fromStdString(txNew.GetHash().GetHex()); + } + + // Add addresses / update labels that we've sent to to the address book + foreach(const SendCoinsRecipient &rcp, recipients) + { + std::string strAddress = rcp.address.toStdString(); + CTxDestination dest = CBitcoinAddress(strAddress).Get(); + std::string strLabel = rcp.label.toStdString(); + { + LOCK(wallet->cs_wallet); + + //std::map<CTxDestination, std::string>::iterator mi = wallet->mapAddressBook.find(dest); + std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(dest); + + // Check if we have a new address or an updated label + if (mi == wallet->mapAddressBook.end() || mi->second.name != strLabel) + { + std::string purpose; + //wallet->SetAddressBookName(dest, strLabel); + wallet->SetAddressBook(dest, strLabel,purpose); + } + } + } + + return SendCoinsReturn(OK, 0, hex); +} +
Code added
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/qt/walletmodel.h
+ + qint64 getSharedBalance(const CCoinControl *coinControl=NULL) const; + qint64 getSharedUnconfirmedBalance() const; + qint64 getSharedImmatureBalance() const;
Code added
+ /*SendCoinsReturn(StatusCode status = OK): + status(status) {}*/ + SendCoinsReturn(StatusCode status=Aborted, + qint64 fee=0, + QString hex=QString()): + status(status), fee(fee), hex(hex) {}
Code replaced
+ qint64 fee; // is used in case status is "AmountWithFeeExceedsBalance" + QString hex; // is filled with the transaction hash if status is "OK"
Code added
+ SendCoinsReturn createRawTransaction(const QList<SendCoinsRecipient> &recipients, CTransaction& txNew, const CCoinControl *coinControl, bool isMultiSig); + bool isMultiSig; + bool was_locked; + CWallet *getWallet(){ return wallet; }
Code added
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/qt/walletview.cpp
+#include "addressbookpage.h" +#include "multisigdialog.h" + multiSigPage = new MultiSigDialog(); + addWidget(multiSigPage);
Code added
+ connect(transactionView, SIGNAL(doubleClicked(QModelIndex)), transactionView, SLOT(showDetails()));
Code replaced
+ connect(sendCoinsPage, SIGNAL(sendCoins(QString)), this, SLOT(gotoSendCoinsPage(QString))); + + // Clicking on "Send Coins" in the address book sends you to the send coins tab + connect(transactionView, SIGNAL(sendCoins(QString)), this, SLOT(gotoSendCoinsPage(QString))); + // Clicking on "Verify Message" in the address book opens the verify message tab in the Sign/Verify Message dialog + connect(transactionView, SIGNAL(verifyMessage(QString)), this, SLOT(gotoVerifyMessageTab(QString))); + // Clicking on "Sign Message" in the receive coins page opens the sign message tab in the Sign/Verify Message dialog + connect(transactionView, SIGNAL(signMessage(QString)), this, SLOT(gotoSignMessageTab(QString)));
Code replaced
+ multiSigPage->setModel(walletModel); +void WalletView::gotoMultiSigPage() +{ + setCurrentWidget(multiSigPage); +} +
Code added
+ AddressBookPage *dlg = new AddressBookPage(AddressBookPage::ForEditing, AddressBookPage::SendingTab, this);
Code replaced
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/qt/walletview.h
+class AddressBookPage; +class MultiSigDialog; + AddressBookPage *addressBookPage; + MultiSigDialog *multiSigPage;
Code added
+ ReportView *reportView;
Code replaced
+ /** Switch to multisig page */ + void gotoMultiSigPage();
Code added
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/script.cpp
+static const size_t nMaxNumSize = 4; +CBigNum CastToBigNum(const valtype& vch) +{ + if (vch.size() > nMaxNumSize) + throw runtime_error("CastToBigNum() : overflow"); + // Get rid of extra leading zeros + return CBigNum(CBigNum(vch).getvch()); +} +
Code added
+class CKeyStoreIsMyShareVisitor : public boost::static_visitor<bool> +{ +private: + const CKeyStore *keystore; +public: + CKeyStoreIsMyShareVisitor(const CKeyStore *keystoreIn) : keystore(keystoreIn) { } + bool operator()(const CNoDestination &dest) const { return false; } + bool operator()(const CKeyID &keyID) const { return false; } + bool operator()(const CScriptID &scriptID) const { + CScript scriptPubKey; + bool haveCScript = keystore->GetCScript(scriptID, scriptPubKey); + if ( !haveCScript ) + return false; + + return IsMyShare(*keystore, scriptPubKey); + } + bool operator()(const CStealthAddress &stxAddr) const + { + return false; + } +}; + +bool IsMyShare(const CKeyStore& keystore, const CTxDestination &dest) +{ + return boost::apply_visitor(CKeyStoreIsMyShareVisitor(&keystore), dest); +} +
Code added
+bool IsMyShare(const CKeyStore& keystore, const CScript& scriptPubKey) +{ + vector<valtype> vSolutions; + txnouttype whichType; + if (!Solver(scriptPubKey, whichType, vSolutions)) + return false; + + CKeyID keyID; + switch (whichType) + { + case TX_NONSTANDARD: + case TX_NULL_DATA: + case TX_PUBKEY: + case TX_PUBKEYHASH: + return false; + case TX_SCRIPTHASH: + { + CScript subscript; + if (!keystore.GetCScript(CScriptID(uint160(vSolutions[0])), subscript)) + return false; + + return IsMyShare(keystore, subscript); + } + case TX_MULTISIG: + { + vector<valtype> keys(vSolutions.begin()+1, vSolutions.begin() +vSolutions.size()-1); + int nkeys = HaveKeys(keys, keystore) ; + return nkeys > 0 && nkeys < keys.size(); + } + default: + break; + } + return false; +} +
Code added
+bool EvalMultiSigScript(vector<vector<unsigned char> >& stack, const CScript& script, const CTransaction& txTo, unsigned int nIn, unsigned int flags, int nHashType, bool *bIsSign) +{ + CAutoBN_CTX pctx; + CScript::const_iterator pc = script.begin(); + CScript::const_iterator pend = script.end(); + CScript::const_iterator pbegincodehash = script.begin(); + opcodetype opcode; + valtype vchPushValue; + vector<valtype> altstack; + if (script.size() > 10000) + return false; + int nOpCount = 0; + bool fStrictEncodings = flags & SCRIPT_VERIFY_STRICTENC; + + try + { + while (pc < pend) + { + bool fExec = true; + + // + // Read instruction + // + if (!script.GetOp(pc, opcode, vchPushValue)) + return false; + if (vchPushValue.size() > MAX_SCRIPT_ELEMENT_SIZE) + return false; + if (opcode > OP_16 && ++nOpCount > 201) + return false; + + if (opcode == OP_CAT || + opcode == OP_SUBSTR || + opcode == OP_LEFT || + opcode == OP_RIGHT || + opcode == OP_INVERT || + opcode == OP_AND || + opcode == OP_OR || + opcode == OP_XOR || + opcode == OP_2MUL || + opcode == OP_2DIV || + opcode == OP_MUL || + opcode == OP_DIV || + opcode == OP_MOD || + opcode == OP_LSHIFT || + opcode == OP_RSHIFT) + return false; // Disabled opcodes. + + if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) + stack.push_back(vchPushValue); + else if (fExec || (OP_IF <= opcode && opcode <= OP_ENDIF)) + switch (opcode) + { + // + // Push value + // + case OP_1NEGATE: + case OP_1: + case OP_2: + case OP_3: + case OP_4: + case OP_5: + case OP_6: + case OP_7: + case OP_8: + case OP_9: + case OP_10: + case OP_11: + case OP_12: + case OP_13: + case OP_14: + case OP_15: + case OP_16: + { + // ( -- value) + //CBigNum bn((int)opcode - (int)(OP_1 - 1)); + CScriptNum bn((int)opcode - (int)(OP_1 - 1)); + stack.push_back(bn.getvch()); + } + break; + + + // + // Control + // + case OP_NOP: + case OP_NOP1: case OP_NOP2: case OP_NOP3: case OP_NOP4: case OP_NOP5: + case OP_NOP6: case OP_NOP7: case OP_NOP8: case OP_NOP9: case OP_NOP10: + break; + + case OP_IF: + case OP_NOTIF: + case OP_ELSE: + case OP_ENDIF: + return false; + + case OP_VERIFY: + { + // (true -- ) or + // (false -- false) and return + if (stack.size() < 1) + return false; + bool fValue = CastToBool(stacktop(-1)); + if (fValue) + popstack(stack); + else + return false; + } + break; + + case OP_RETURN: + { + return false; + } + break; + + + // + // Stack ops + // + case OP_TOALTSTACK: + { + if (stack.size() < 1) + return false; + altstack.push_back(stacktop(-1)); + popstack(stack); + } + break; + + case OP_FROMALTSTACK: + { + if (altstack.size() < 1) + return false; + stack.push_back(altstacktop(-1)); + popstack(altstack); + } + break; + + case OP_2DROP: + { + // (x1 x2 -- ) + if (stack.size() < 2) + return false; + popstack(stack); + popstack(stack); + } + break; + + case OP_2DUP: + { + // (x1 x2 -- x1 x2 x1 x2) + if (stack.size() < 2) + return false; + valtype vch1 = stacktop(-2); + valtype vch2 = stacktop(-1); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_3DUP: + { + // (x1 x2 x3 -- x1 x2 x3 x1 x2 x3) + if (stack.size() < 3) + return false; + valtype vch1 = stacktop(-3); + valtype vch2 = stacktop(-2); + valtype vch3 = stacktop(-1); + stack.push_back(vch1); + stack.push_back(vch2); + stack.push_back(vch3); + } + break; + + case OP_2OVER: + { + // (x1 x2 x3 x4 -- x1 x2 x3 x4 x1 x2) + if (stack.size() < 4) + return false; + valtype vch1 = stacktop(-4); + valtype vch2 = stacktop(-3); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_2ROT: + { + // (x1 x2 x3 x4 x5 x6 -- x3 x4 x5 x6 x1 x2) + if (stack.size() < 6) + return false; + valtype vch1 = stacktop(-6); + valtype vch2 = stacktop(-5); + stack.erase(stack.end()-6, stack.end()-4); + stack.push_back(vch1); + stack.push_back(vch2); + } + break; + + case OP_2SWAP: + { + // (x1 x2 x3 x4 -- x3 x4 x1 x2) + if (stack.size() < 4) + return false; + swap(stacktop(-4), stacktop(-2)); + swap(stacktop(-3), stacktop(-1)); + } + break; + + case OP_IFDUP: + { + // (x - 0 | x x) + if (stack.size() < 1) + return false; + valtype vch = stacktop(-1); + if (CastToBool(vch)) + stack.push_back(vch); + } + break; + + case OP_DEPTH: + { + // -- stacksize + //CBigNum bn(stack.size()); + CScriptNum bn(stack.size()); + stack.push_back(bn.getvch()); + } + break; + + case OP_DROP: + { + // (x -- ) + if (stack.size() < 1) + return false; + popstack(stack); + } + break; + + case OP_DUP: + { + // (x -- x x) + if (stack.size() < 1) + return false; + valtype vch = stacktop(-1); + stack.push_back(vch); + } + break; + + case OP_NIP: + { + // (x1 x2 -- x2) + if (stack.size() < 2) + return false; + stack.erase(stack.end() - 2); + } + break; + + case OP_OVER: + { + // (x1 x2 -- x1 x2 x1) + if (stack.size() < 2) + return false; + valtype vch = stacktop(-2); + stack.push_back(vch); + } + break; + + case OP_PICK: + case OP_ROLL: + { + // (xn ... x2 x1 x0 n - xn ... x2 x1 x0 xn) + // (xn ... x2 x1 x0 n - ... x2 x1 x0 xn) + if (stack.size() < 2) + return false; + int n = CastToBigNum(stacktop(-1)).getint(); + popstack(stack); + if (n < 0 || n >= (int)stack.size()) + return false; + valtype vch = stacktop(-n-1); + if (opcode == OP_ROLL) + stack.erase(stack.end()-n-1); + stack.push_back(vch); + } + break; + + case OP_ROT: + { + // (x1 x2 x3 -- x2 x3 x1) + // x2 x1 x3 after first swap + // x2 x3 x1 after second swap + if (stack.size() < 3) + return false; + swap(stacktop(-3), stacktop(-2)); + swap(stacktop(-2), stacktop(-1)); + } + break; + + case OP_SWAP: + { + // (x1 x2 -- x2 x1) + if (stack.size() < 2) + return false; + swap(stacktop(-2), stacktop(-1)); + } + break; + + case OP_TUCK: + { + // (x1 x2 -- x2 x1 x2) + if (stack.size() < 2) + return false; + valtype vch = stacktop(-1); + stack.insert(stack.end()-2, vch); + } + break; + + + case OP_SIZE: + { + // (in -- in size) + if (stack.size() < 1) + return false; + //CBigNum bn(stacktop(-1).size()); + CScriptNum bn(stacktop(-1).size()); + stack.push_back(bn.getvch()); + } + break; + + + // + // Bitwise logic + // + case OP_EQUAL: + case OP_EQUALVERIFY: + //case OP_NOTEQUAL: // use OP_NUMNOTEQUAL + { + // (x1 x2 - bool) + if (stack.size() < 2) + return false; + valtype& vch1 = stacktop(-2); + valtype& vch2 = stacktop(-1); + bool fEqual = (vch1 == vch2); + // OP_NOTEQUAL is disabled because it would be too easy to say + // something like n != 1 and have some wiseguy pass in 1 with extra + // zero bytes after it (numerically, 0x01 == 0x0001 == 0x000001) + //if (opcode == OP_NOTEQUAL) + // fEqual = !fEqual; + popstack(stack); + popstack(stack); + stack.push_back(fEqual ? vchTrue : vchFalse); + if (opcode == OP_EQUALVERIFY) + { + if (fEqual) + popstack(stack); + else + return false; + } + } + break; + + + // + // Numeric + // + case OP_1ADD: + case OP_1SUB: + case OP_NEGATE: + case OP_ABS: + case OP_NOT: + case OP_0NOTEQUAL: + { + // (in -- out) + if (stack.size() < 1) + return false; + //CBigNum bn = CastToBigNum(stacktop(-1)); + CScriptNum bn(stacktop(-1)); + switch (opcode) + { + case OP_1ADD: bn += bnOne; break; + case OP_1SUB: bn -= bnOne; break; + case OP_NEGATE: bn = -bn; break; + case OP_ABS: if (bn < bnZero) bn = -bn; break; + case OP_NOT: bn = (bn == bnZero); break; + case OP_0NOTEQUAL: bn = (bn != bnZero); break; + default: assert(!"invalid opcode"); break; + } + popstack(stack); + stack.push_back(bn.getvch()); + } + break; + + case OP_ADD: + case OP_SUB: + case OP_BOOLAND: + case OP_BOOLOR: + case OP_NUMEQUAL: + case OP_NUMEQUALVERIFY: + case OP_NUMNOTEQUAL: + case OP_LESSTHAN: + case OP_GREATERTHAN: + case OP_LESSTHANOREQUAL: + case OP_GREATERTHANOREQUAL: + case OP_MIN: + case OP_MAX: + { + // (x1 x2 -- out) + if (stack.size() < 2) + return false; + /*CBigNum bn1 = CastToBigNum(stacktop(-2)); + CBigNum bn2 = CastToBigNum(stacktop(-1)); + CBigNum bn;*/ + CScriptNum bn1(stacktop(-2)); + CScriptNum bn2(stacktop(-1)); + CScriptNum bn(0); + switch (opcode) + { + case OP_ADD: + bn = bn1 + bn2; + break; + + case OP_SUB: + bn = bn1 - bn2; + break; + + case OP_BOOLAND: bn = (bn1 != bnZero && bn2 != bnZero); break; + case OP_BOOLOR: bn = (bn1 != bnZero || bn2 != bnZero); break; + case OP_NUMEQUAL: bn = (bn1 == bn2); break; + case OP_NUMEQUALVERIFY: bn = (bn1 == bn2); break; + case OP_NUMNOTEQUAL: bn = (bn1 != bn2); break; + case OP_LESSTHAN: bn = (bn1 < bn2); break; + case OP_GREATERTHAN: bn = (bn1 > bn2); break; + case OP_LESSTHANOREQUAL: bn = (bn1 <= bn2); break; + case OP_GREATERTHANOREQUAL: bn = (bn1 >= bn2); break; + case OP_MIN: bn = (bn1 < bn2 ? bn1 : bn2); break; + case OP_MAX: bn = (bn1 > bn2 ? bn1 : bn2); break; + default: assert(!"invalid opcode"); break; + } + popstack(stack); + popstack(stack); + stack.push_back(bn.getvch()); + + if (opcode == OP_NUMEQUALVERIFY) + { + if (CastToBool(stacktop(-1))) + popstack(stack); + else + return false; + } + } + break; + + case OP_WITHIN: + { + // (x min max -- out) + if (stack.size() < 3) + return false; + /*CBigNum bn1 = CastToBigNum(stacktop(-3)); + CBigNum bn2 = CastToBigNum(stacktop(-2)); + CBigNum bn3 = CastToBigNum(stacktop(-1));*/ + CScriptNum bn1(stacktop(-3)); + CScriptNum bn2(stacktop(-2)); + CScriptNum bn3(stacktop(-1)); + bool fValue = (bn2 <= bn1 && bn1 < bn3); + popstack(stack); + popstack(stack); + popstack(stack); + stack.push_back(fValue ? vchTrue : vchFalse); + } + break; + + + // + // Crypto + // + case OP_RIPEMD160: + case OP_SHA1: + case OP_SHA256: + case OP_HASH160: + case OP_HASH256: + { + // (in -- hash) + if (stack.size() < 1) + return false; + valtype& vch = stacktop(-1); + valtype vchHash((opcode == OP_RIPEMD160 || opcode == OP_SHA1 || opcode == OP_HASH160) ? 20 : 32); + if (opcode == OP_RIPEMD160) + RIPEMD160(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_SHA1) + SHA1(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_SHA256) + SHA256(&vch[0], vch.size(), &vchHash[0]); + else if (opcode == OP_HASH160) + { + uint160 hash160 = Hash160(vch); + memcpy(&vchHash[0], &hash160, sizeof(hash160)); + } + else if (opcode == OP_HASH256) + { + uint256 hash = Hash(vch.begin(), vch.end()); + memcpy(&vchHash[0], &hash, sizeof(hash)); + } + popstack(stack); + stack.push_back(vchHash); + } + break; + + case OP_CODESEPARATOR: + { + // Hash starts after the code separator + pbegincodehash = pc; + } + break; + + case OP_CHECKSIG: + case OP_CHECKSIGVERIFY: + { + // (sig pubkey -- bool) + if (stack.size() < 2) + return false; + + valtype& vchSig = stacktop(-2); + valtype& vchPubKey = stacktop(-1); + + ////// debug print + //PrintHex(vchSig.begin(), vchSig.end(), "sig: %s\n"); + //PrintHex(vchPubKey.begin(), vchPubKey.end(), "pubkey: %s\n"); + + // Subset of script starting at the most recent codeseparator + CScript scriptCode(pbegincodehash, pend); + + // Drop the signature, since there's no way for a signature to sign itself + scriptCode.FindAndDelete(CScript(vchSig)); + + bool fSuccess = (!fStrictEncodings || (IsCanonicalSignature(vchSig,flags) && IsCanonicalPubKey(vchPubKey,flags))); + if (fSuccess) + fSuccess = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); + + popstack(stack); + popstack(stack); + stack.push_back(fSuccess ? vchTrue : vchFalse); + if (opcode == OP_CHECKSIGVERIFY) + { + if (fSuccess) + popstack(stack); + else + return false; + } + } + break; + + case OP_CHECKMULTISIG: + case OP_CHECKMULTISIGVERIFY: + { + // ([sig ...] num_of_signatures [pubkey ...] num_of_pubkeys -- bool) + int i = 1; + if ((int)stack.size() < i) + return false; + + int nKeysCount = CastToBigNum(stacktop(-i)).getint(); + if (nKeysCount < 0 || nKeysCount > 20) + return false; + nOpCount += nKeysCount; + if (nOpCount > 201) + return false; + int ikey = ++i; + i += nKeysCount; + if ((int)stack.size() < i) + return false; + + int nSigsCount = CastToBigNum(stacktop(-i)).getint(); + if (nSigsCount < 0 || nSigsCount > nKeysCount) + return false; + int isig = ++i; + i += nSigsCount; + //if ((int)stack.size() < i) + //return false; + int curSigsCount = nSigsCount + stack.size()- i; + if ( curSigsCount > nSigsCount ) + curSigsCount = nSigsCount; + + // Subset of script starting at the most recent codeseparator + CScript scriptCode(pbegincodehash, pend); + + // Drop the signatures, since there's no way for a signature to sign itself + for (int k = 0; k < curSigsCount; k++) + { + valtype& vchSig = stacktop(-isig-k); + scriptCode.FindAndDelete(CScript(vchSig)); + } + + bool fSuccess = true; + int okCount = 0; + for ( int n = 0; n < nKeysCount; ++ n ) + { + for ( int m = 0; m < curSigsCount; ++ m ) + { + valtype& vchSig = stacktop(-isig - m); + valtype& vchPubKey = stacktop(-ikey - n); + + // Check signature + bool fOk = (!fStrictEncodings || (IsCanonicalSignature(vchSig,flags) && IsCanonicalPubKey(vchPubKey,flags))); + if (fOk) + fOk = CheckSig(vchSig, vchPubKey, scriptCode, txTo, nIn, nHashType, flags); + + if (fOk) { + bIsSign[nKeysCount - 1 - n] = true; + okCount += 1; + } + } + } + + if ( okCount < nSigsCount ) + fSuccess = false; + + return fSuccess; + } + break; + + default: + return false; + } + } + } + catch (...) + { + return false; + } + + + return true; +} + +bool VerifyMultiSigScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType, bool *bIsSign) +{ + vector<vector<unsigned char> > stack, stackCopy; + if (!EvalScript(stack, scriptSig, txTo, nIn, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, nHashType)) + return false; + + stackCopy = stack; + if (!EvalScript(stack, scriptPubKey, txTo, nIn, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, nHashType)) + return false; + + if (stack.empty()) + return false; + + if (CastToBool(stack.back()) == false) + return false; + + if (scriptPubKey.IsPayToScriptHash()) + { + if (!scriptSig.IsPushOnly()) + return false; + + assert(!stackCopy.empty()); + + const valtype& pubKeySerialized = stackCopy.back(); + CScript pubKey2(pubKeySerialized.begin(), pubKeySerialized.end()); + popstack(stackCopy); + + if (!EvalMultiSigScript(stackCopy, pubKey2, txTo, nIn, SCRIPT_VERIFY_P2SH, nHashType, bIsSign)) + return false; + + if (stackCopy.empty()) + return false; + + return CastToBool(stackCopy.back()); + } + + return false; +} +
Code added
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/script.h
+bool IsMyShare(const CKeyStore& keystore, const CScript& scriptPubKey); +bool IsMyShare(const CKeyStore& keystore, const CTxDestination &dest); +bool VerifyMultiSigScript(const CScript& scriptSig, const CScript& scriptPubKey, const CTransaction& txTo, unsigned int nIn, int nHashType, bool *bIsSign);
Code added
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/wallet.cpp
+#include "walletdb.h" +#include "crypter.h" +#include "ui_interface.h"
Code added
- NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address), - strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); + bool fMine = ::IsMine(*this, address); + if ( ::IsMyShare(*this, address) ) + fMine = false; + //NotifyAddressBookChanged(this, address, strName, ::IsMine(*this, address), strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) ); + NotifyAddressBookChanged(this, address, strName, fMine, strPurpose, (fUpdated ? CT_UPDATED : CT_NEW) );
Code replaced
+} + + + + + +/* + * for shared wallet + */ + +bool CWallet::SelectSharedCoins(int64 nTargetValue, set<pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet, const CCoinControl* coinControl) const +{ + vector<COutput> vCoins; + AvailableSharedCoins(vCoins, false, coinControl); + + // coin control -> return all selected outputs (we want all selected to go into the transaction for sure) + if (coinControl && coinControl->HasSelected()) + { + BOOST_FOREACH(const COutput& out, vCoins) + { + nValueRet += out.tx->vout[out.i].nValue; + setCoinsRet.insert(make_pair(out.tx, out.i)); + } + return (nValueRet >= nTargetValue); + } + + return (SelectCoinsMinConf(nTargetValue, 1, 6, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 1, 1, vCoins, setCoinsRet, nValueRet) || + SelectCoinsMinConf(nTargetValue, 0, 1, vCoins, setCoinsRet, nValueRet)); +} + +bool CWallet::CreateRawTransaction(const std::vector<std::pair<CScript, int64> >& vecSend, CTransaction& txNew, + int64& nFeeRet, std::string& strFailReason, bool isMultiSig, CReserveKey& reservekey, const CCoinControl *coinControl) +{ + int64 nValue = 0; + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + { + if (nValue < 0) + { + strFailReason = _("Transaction amounts must be positive"); + return false; + } + nValue += s.second; + } + if (vecSend.empty() || nValue < 0) + { + strFailReason = _("Transaction amounts must be positive"); + return false; + } + + int64 nTotalValue = nValue + nFeeRet; + CTransaction rawTx; + + // vouts to the payees + BOOST_FOREACH (const PAIRTYPE(CScript, int64)& s, vecSend) + { + CTxOut txout(s.second, s.first); + if (txout.IsDust((CTransaction::nMinRelayTxFee))) + { + strFailReason = _("Transaction amount too small"); + return false; + } + rawTx.vout.push_back(txout); + } + + // Choose coins to use + set<pair<const CWalletTx*,unsigned int> > setCoins; + int64 nValueIn = 0; + if ( isMultiSig ) + { + if (!SelectSharedCoins(nTotalValue, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + } + else + { + if (!SelectCoins(nTotalValue, setCoins, nValueIn, coinControl)) + { + strFailReason = _("Insufficient funds"); + return false; + } + } + + int64 nChange = nValueIn - nTotalValue; + if (nChange > 0) + { + CScript scriptChange; + + // coin control: send change to custom address + if (coinControl && !boost::get<CNoDestination>(&coinControl->destChange)) + scriptChange.SetDestination(coinControl->destChange); + else + { + // Reserve a new key pair from key pool + CPubKey vchPubKey; + assert(reservekey.GetReservedKey(vchPubKey)); // should never fail, as we just unlocked + + scriptChange.SetDestination(vchPubKey.GetID()); + } + + CTxOut newTxOut(nChange, scriptChange); + + if (newTxOut.IsDust((CTransaction::nMinRelayTxFee))) + { + nFeeRet += nChange; + } + else + { + vector<CTxOut>::iterator position = rawTx.vout.begin()+GetRandInt(rawTx.vout.size()+1); + rawTx.vout.insert(position, newTxOut); + } + } + else + reservekey.ReturnKey(); + + // Fill vin + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) + rawTx.vin.push_back(CTxIn(coin.first->GetHash(),coin.second)); + + if ( !isMultiSig ) + { + // Sign + int nIn = 0; + BOOST_FOREACH(const PAIRTYPE(const CWalletTx*,unsigned int)& coin, setCoins) + if (!SignSignature(*this, *coin.first, rawTx, nIn++)) + { + strFailReason = _("Signing transaction failed"); + return false; + } + } + + // Limit size + unsigned int nBytes = ::GetSerializeSize(rawTx, SER_NETWORK, PROTOCOL_VERSION); + if (nBytes >= MAX_STANDARD_TX_SIZE) + { + strFailReason = _("Transaction too large"); + return false; + } + + txNew = rawTx; + return true; +} + +bool CWallet::IsMyShare(const CTxIn& txin) const +{ + { + LOCK(cs_wallet); + map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size()) + if (IsMyShare(prev.vout[txin.prevout.n])) + return true; + } + } + return false; +} + +bool CWallet::IsMyShare(const CTxOut& txout) const +{ + return ::IsMyShare(*this, txout.scriptPubKey); +} + +bool CWallet::IsMyShare(const CTransaction& tx) const +{ + BOOST_FOREACH(const CTxOut& txout, tx.vout) + if (IsMyShare(txout) && txout.nValue >= DUST_HARD_LIMIT) + return true; + + return false; +} + +int64 CWallet::GetSharedBalance() const +{ + int64 nTotal = 0; + { + LOCK(cs_wallet); + for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (pcoin->IsConfirmed()) + nTotal += pcoin->GetSharedAvailableCredit(); + } + } + + return nTotal; +} + +int64 CWallet::GetSharedUnconfirmedBalance() const +{ + int64 nTotal = 0; + { + LOCK(cs_wallet); + for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + if (!pcoin->IsFinal() || !pcoin->IsConfirmed()) + nTotal += pcoin->GetSharedAvailableCredit(); + } + } + return nTotal; +} + +int64 CWallet::GetSharedImmatureBalance() const +{ + return 0; +} + +int64 CWallet::GetShareDebit(const CTxIn &txin) const +{ + { + LOCK(cs_wallet); + map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(txin.prevout.hash); + if (mi != mapWallet.end()) + { + const CWalletTx& prev = (*mi).second; + if (txin.prevout.n < prev.vout.size()) + if (IsMyShare(prev.vout[txin.prevout.n])) + return prev.vout[txin.prevout.n].nValue; + } + } + return 0; +} + +void CWallet::AvailableSharedCoins(vector<COutput>& vCoins, bool fOnlyConfirmed, const CCoinControl *coinControl) const +{ + vCoins.clear(); + + { + LOCK(cs_wallet); + for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it) + { + const CWalletTx* pcoin = &(*it).second; + + if (!pcoin->IsFinal()) + continue; + + if (fOnlyConfirmed && !pcoin->IsConfirmed()) + continue; + + if (pcoin->IsCoinBase() && pcoin->GetBlocksToMaturity() > 0) + continue; + + for (unsigned int i = 0; i < pcoin->vout.size(); i++) { + if (!(pcoin->IsSpent(i)) && IsMyShare(pcoin->vout[i]) && + !IsLockedCoin((*it).first, i) && pcoin->vout[i].nValue >= DUST_HARD_LIMIT && + (!coinControl || !coinControl->HasSelected() || coinControl->IsSelected((*it).first, i))) + vCoins.push_back(COutput(pcoin, i, pcoin->GetDepthInMainChain())); + } + } + } +} + + +
Code added
-
Feathercoin specific changes made to convert Bitcoin to FTC 0.9.6.*
Add Multisgin Page : - commit
https://github.com/FeatherCoin/Feathercoin/commit/83737e90c292f18fe0285677cefbd70125492e1a
Include a multi-signature address Page
src/wallet.h
+#include "script.h"
Code added
+ + bool CreateRawTransaction(const std::vector<std::pair<CScript, int64> >& vecSend, CTransaction& txNew, + int64& nFeeRet, std::string& strFailReason, bool isMultiSig, CReserveKey& reservekey, const CCoinControl *coinControl=NULL); + + /* + * for shared wallet + */ + bool IsMyShare(const CTxIn& txin) const; + bool IsMyShare(const CTxOut& txout) const; + bool IsMyShare(const CTransaction& tx) const; + int64 GetSharedBalance() const; + int64 GetSharedUnconfirmedBalance() const; + int64 GetSharedImmatureBalance() const; + int64 GetSharedCredit(const CTxOut& txout) const + { + if (!MoneyRange(txout.nValue)) + throw std::runtime_error("CWallet::GetSharedCredit() : value out of range"); + return (IsMyShare(txout) ? txout.nValue : 0); + } + bool IsFromMyShare(const CTransaction& tx) const + { + return (GetShareDebit(tx) > 0); + } + int64 GetShareDebit(const CTransaction& tx) const + { + int64 nDebit = 0; + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + nDebit += GetShareDebit(txin); + if (!MoneyRange(nDebit)) + throw std::runtime_error("CWallet::GetDebit() : value out of range"); + } + return nDebit; + } + int64 GetShareDebit(const CTxIn& txin) const; + void AvailableSharedCoins(std::vector<COutput>& vCoins, bool fOnlyConfirmed=true, const CCoinControl *coinControl=NULL) const; + bool SelectSharedCoins(int64 nTargetValue, std::set<std::pair<const CWalletTx*,unsigned int> >& setCoinsRet, int64& nValueRet, const CCoinControl* coinControl=NULL) const;
Code added
+ std::vector<CMerkleTx> vtxPrev; + std::vector<char> vfSpent; // which outputs are already spent + fSharedAvailableCreditCached = false;
Code added
+ + bool IsSpent(unsigned int nOut) const + { + if (nOut >= vout.size()) + throw std::runtime_error("CWalletTx::IsSpent() : nOut out of range"); + if (nOut >= vfSpent.size()) + return false; + return (!!vfSpent[nOut]); + }
Code added
+ + bool IsConfirmed() const + { + // Quick answer in most cases + if (!IsFinal()) + return false; + if (GetDepthInMainChain() >= 1) + return true; + if (!IsFromMe()) // using wtx's cached debit + return false; + + // If no confirmations but it's from us, we can still + // consider it confirmed if all dependencies are confirmed + std::map<uint256, const CMerkleTx*> mapPrev; + std::vector<const CMerkleTx*> vWorkQueue; + vWorkQueue.reserve(vtxPrev.size()+1); + vWorkQueue.push_back(this); + for (unsigned int i = 0; i < vWorkQueue.size(); i++) + { + const CMerkleTx* ptx = vWorkQueue[i]; + + if (!ptx->IsFinal()) + return false; + if (ptx->GetDepthInMainChain() >= 1) + continue; + if (!pwallet->IsFromMe(*ptx)) + return false; + + if (mapPrev.empty()) + { + BOOST_FOREACH(const CMerkleTx& tx, vtxPrev) + mapPrev[tx.GetHash()] = &tx; + } + + BOOST_FOREACH(const CTxIn& txin, ptx->vin) + { + if (!mapPrev.count(txin.prevout.hash)) + return false; + vWorkQueue.push_back(mapPrev[txin.prevout.hash]); + } + } + return true; + }
Code added
+ + /* + * for shared wallet + */ + mutable bool fSharedAvailableCreditCached; + mutable int64 nSharedAvailableCreditCached; + int64 GetSharedAvailableCredit(bool fUseCache=true) const + { + // Must wait until coinbase is safely deep enough in the chain before valuing it + if (IsCoinBase() && GetBlocksToMaturity() > 0) + return 0; + + //if (fUseCache && fSharedAvailableCreditCached) + //return nSharedAvailableCreditCached; + + int64 nCredit = 0; + for (unsigned int i = 0; i < vout.size(); i++) + { + if (!IsSpent(i)) + { + const CTxOut &txout = vout[i]; + nCredit += pwallet->GetSharedCredit(txout); + if (!MoneyRange(nCredit)) + throw std::runtime_error("CWalletTx::GetSharedAvailableCredit() : value out of range"); + } + } + + nSharedAvailableCreditCached = nCredit; + fSharedAvailableCreditCached = true; + return nCredit; + }
Code added