Merge branch 'master' of github.com:novacoin-project/novacoin
authorMASM fan <masmfan@gmail.com>
Thu, 27 Nov 2014 23:30:17 +0000 (03:30 +0400)
committerMASM fan <masmfan@gmail.com>
Thu, 27 Nov 2014 23:30:17 +0000 (03:30 +0400)
19 files changed:
novacoin-qt.pro
src/kernelrecord.cpp
src/kernelrecord.h
src/makefile.bsd
src/makefile.linux-mingw
src/makefile.mingw
src/makefile.osx
src/makefile.unix
src/ministun.h [new file with mode: 0644]
src/net.cpp
src/qt/bitcoingui.cpp
src/qt/guiconstants.h
src/qt/locale/bitcoin_ru.ts
src/qt/mintingtablemodel.cpp
src/qt/mintingtablemodel.h
src/qt/mintingview.cpp
src/qt/mintingview.h
src/stun.cpp [new file with mode: 0644]
src/wallet.cpp

index feef761..5323ea0 100644 (file)
@@ -209,6 +209,7 @@ HEADERS += src/qt/bitcoingui.h \
     src/main.h \
     src/miner.h \
     src/net.h \
+    src/ministun.h \
     src/key.h \
     src/db.h \
     src/txdb.h \
@@ -288,6 +289,7 @@ SOURCES += src/qt/bitcoin.cpp src/qt/bitcoingui.cpp \
     src/miner.cpp \
     src/init.cpp \
     src/net.cpp \
+    src/stun.cpp \
     src/irc.cpp \
     src/checkpoints.cpp \
     src/addrman.cpp \
index 54f71e9..3121845 100644 (file)
@@ -2,7 +2,7 @@
 
 #include "wallet.h"
 #include "base58.h"
-#include "main.h"
+
 
 using namespace std;
 
@@ -75,6 +75,27 @@ int64 KernelRecord::getAge() const
     return (GetAdjustedTime() - nTime) / 86400;
 }
 
+uint64 KernelRecord::getCoinDay() const
+{
+    int64 nWeight = GetAdjustedTime() - nTime - nStakeMinAge;
+    if( nWeight <  0)
+        return 0;
+    nWeight = min(nWeight, (int64)nStakeMaxAge);
+    uint64 coinAge = (nValue * nWeight ) / (COIN * 86400);
+    return coinAge;
+}
+
+int64 KernelRecord::getPoSReward(int nBits, int minutes)
+{
+    double PoSReward;
+    int64 nWeight = GetAdjustedTime() - nTime + minutes * 60;
+    if( nWeight <  nStakeMinAge)
+        return 0;
+    uint64 coinAge = (nValue * nWeight ) / (COIN * 86400);
+    PoSReward = GetProofOfStakeReward(coinAge, nBits, GetAdjustedTime() + minutes * 60);
+    return PoSReward;
+}
+
 double KernelRecord::getProbToMintStake(double difficulty, int timeOffset) const
 {
     //double maxTarget = pow(static_cast<double>(2), 224);
index d54e551..fce7b24 100644 (file)
@@ -41,8 +41,10 @@ public:
 
     std::string getTxID();
     int64 getAge() const;
+    uint64 getCoinDay() const;
     double getProbToMintStake(double difficulty, int timeOffset = 0) const;
     double getProbToMintWithinNMinutes(double difficulty, int minutes);
+    int64 getPoSReward(int nBits, int timeOffset);
 protected:
     int prevMinutes;
     double prevDifficulty;
index 5a30ebc..79db3f0 100644 (file)
@@ -112,6 +112,7 @@ OBJS= \
     obj/main.o \
     obj/miner.o \
     obj/net.o \
+    obj/stun.o \
     obj/protocol.o \
     obj/bitcoinrpc.o \
     obj/rpcdump.o \
index 570bd3b..ebd2fdc 100644 (file)
@@ -75,6 +75,7 @@ OBJS= \
     obj/main.o \
     obj/miner.o \
     obj/net.o \
+    obj/stun.o \
     obj/protocol.o \
     obj/bitcoinrpc.o \
     obj/rpcdump.o \
index 74006f7..35f80ab 100644 (file)
@@ -66,6 +66,7 @@ OBJS= \
     obj/main.o \
     obj/miner.o \
     obj/net.o \
+    obj/stun.o \
     obj/protocol.o \
     obj/bitcoinrpc.o \
     obj/rpcdump.o \
index 1570fbd..72821e1 100644 (file)
@@ -77,6 +77,7 @@ OBJS= \
     obj/main.o \
     obj/miner.o \
     obj/net.o \
+    obj/stun.o \
     obj/protocol.o \
     obj/bitcoinrpc.o \
     obj/rpcdump.o \
index bcc102e..7c3b72a 100644 (file)
@@ -118,6 +118,7 @@ OBJS= \
     obj/miner.o \
     obj/main.o \
     obj/net.o \
+    obj/stun.o \
     obj/protocol.o \
     obj/bitcoinrpc.o \
     obj/rpcdump.o \
diff --git a/src/ministun.h b/src/ministun.h
new file mode 100644 (file)
index 0000000..8b19df8
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * ministun.h
+ * Part of the ministun package
+ *
+ * STUN support code borrowed from Asterisk -- An open source telephony toolkit.
+ * Copyright (C) 1999 - 2006, Digium, Inc.
+ * Mark Spencer <markster@digium.com>
+ * Standalone remake (c) 2009 Vladislav Grishenko <themiron@mail.ru>
+ *
+ * This software is licensed under the terms of the GNU General
+ * Public License (GPL). Please see the file COPYING for details.
+ *
+ */
+
+#define PACKAGE  "ministun"
+#define VERSION  "0.1"
+
+#define STUN_SERVER "stun.xten.com"
+#define STUN_PORT 3478
+#define STUN_COUNT 3
+#define STUN_TIMEOUT 3
+
+typedef struct { unsigned int id[4]; } __attribute__((packed)) stun_trans_id;
+
+struct stun_header {
+    unsigned short msgtype;
+    unsigned short msglen;
+    stun_trans_id  id;
+    unsigned char  ies[0];
+} __attribute__((packed));
+
+struct stun_attr {
+    unsigned short attr;
+    unsigned short len;
+    unsigned char  value[0];
+} __attribute__((packed));
+
+/*
+ * The format normally used for addresses carried by STUN messages.
+ */
+struct stun_addr {
+    unsigned char  unused;
+    unsigned char  family;
+    unsigned short port;
+    unsigned int   addr;
+} __attribute__((packed));
+
+#define STUN_IGNORE  (0)
+#define STUN_ACCEPT  (1)
+
+/* STUN message types
+ * 'BIND' refers to transactions used to determine the externally
+ * visible addresses. 'SEC' refers to transactions used to establish
+ * a session key for subsequent requests.
+ * 'SEC' functionality is not supported here.
+ */
+
+#define STUN_BINDREQ 0x0001
+#define STUN_BINDRESP 0x0101
+#define STUN_BINDERR 0x0111
+#define STUN_SECREQ 0x0002
+#define STUN_SECRESP 0x0102
+#define STUN_SECERR 0x0112
+
+/* Basic attribute types in stun messages.
+ * Messages can also contain custom attributes (codes above 0x7fff)
+ */
+#define STUN_MAPPED_ADDRESS 0x0001
+#define STUN_RESPONSE_ADDRESS 0x0002
+#define STUN_CHANGE_REQUEST 0x0003
+#define STUN_SOURCE_ADDRESS 0x0004
+#define STUN_CHANGED_ADDRESS 0x0005
+#define STUN_USERNAME  0x0006
+#define STUN_PASSWORD  0x0007
+#define STUN_MESSAGE_INTEGRITY 0x0008
+#define STUN_ERROR_CODE  0x0009
+#define STUN_UNKNOWN_ATTRIBUTES 0x000a
+#define STUN_REFLECTED_FROM 0x000b
index 3b1ef2a..9ce8ec3 100644 (file)
@@ -301,109 +301,20 @@ bool IsReachable(const CNetAddr& addr)
     return vfReachable[net] && !vfLimited[net];
 }
 
-bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet)
-{
-    SOCKET hSocket;
-    if (!ConnectSocket(addrConnect, hSocket))
-        return error("GetMyExternalIP() : connection to %s failed", addrConnect.ToString().c_str());
-
-    send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL);
-
-    string strLine;
-    while (RecvLine(hSocket, strLine))
-    {
-        if (strLine.empty()) // HTTP response is separated from headers by blank line
-        {
-            while (true)
-            {
-                if (!RecvLine(hSocket, strLine))
-                {
-                    closesocket(hSocket);
-                    return false;
-                }
-                if (pszKeyword == NULL)
-                    break;
-                if (strLine.find(pszKeyword) != string::npos)
-                {
-                    strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword));
-                    break;
-                }
-            }
-            closesocket(hSocket);
-            if (strLine.find("<") != string::npos)
-                strLine = strLine.substr(0, strLine.find("<"));
-            strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r"));
-            while (strLine.size() > 0 && isspace(strLine[strLine.size()-1]))
-                strLine.resize(strLine.size()-1);
-            CService addr(strLine,0,true);
-            printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str());
-            if (!addr.IsValid() || !addr.IsRoutable())
-                return false;
-            ipRet.SetIP(addr);
-            return true;
-        }
-    }
-    closesocket(hSocket);
-    return error("GetMyExternalIP() : connection closed");
-}
+extern int GetExternalIPbySTUN(uint64_t rnd, struct sockaddr_in *mapped, const char **srv);
 
 // We now get our external IP from the IRC server first and only use this as a backup
 bool GetMyExternalIP(CNetAddr& ipRet)
 {
-    CService addrConnect;
-    const char* pszGet;
-    const char* pszKeyword;
-
-    for (int nLookup = 0; nLookup <= 1; nLookup++)
-    for (int nHost = 1; nHost <= 2; nHost++)
-    {
-        // We should be phasing out our use of sites like these.  If we need
-        // replacements, we should ask for volunteers to put this simple
-        // php file on their web server that prints the client IP:
-        //  <?php echo $_SERVER["REMOTE_ADDR"]; ?>
-        if (nHost == 1)
-        {
-            addrConnect = CService("91.198.22.70",80); // checkip.dyndns.org
-
-            if (nLookup == 1)
-            {
-                CService addrIP("checkip.dyndns.org", 80, true);
-                if (addrIP.IsValid())
-                    addrConnect = addrIP;
-            }
-
-            pszGet = "GET / HTTP/1.1\r\n"
-                     "Host: checkip.dyndns.org\r\n"
-                     "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n"
-                     "Connection: close\r\n"
-                     "\r\n";
-
-            pszKeyword = "Address:";
-        }
-        else if (nHost == 2)
-        {
-            addrConnect = CService("74.208.43.192", 80); // www.showmyip.com
-
-            if (nLookup == 1)
-            {
-                CService addrIP("www.showmyip.com", 80, true);
-                if (addrIP.IsValid())
-                    addrConnect = addrIP;
-            }
-
-            pszGet = "GET /simple/ HTTP/1.1\r\n"
-                     "Host: www.showmyip.com\r\n"
-                     "User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n"
-                     "Connection: close\r\n"
-                     "\r\n";
-
-            pszKeyword = NULL; // Returns just IP address
-        }
-
-        if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet))
-            return true;
+    struct sockaddr_in mapped;
+    uint64 rnd = GetRand(~0LL);
+    const char *srv;
+    int rc = GetExternalIPbySTUN(rnd, &mapped, &srv);
+    if(rc >= 0) {
+        ipRet = CNetAddr(mapped.sin_addr);
+        printf("GetExternalIPbySTUN(%"PRI64u") returned %s in attempt %d; Server=%s\n", rnd, ipRet.ToStringIP().c_str(), rc, srv);
+        return true;
     }
-
     return false;
 }
 
index c9e17b6..a4c9a52 100644 (file)
@@ -385,6 +385,8 @@ void BitcoinGUI::createToolBars()
     QToolBar *toolbar2 = addToolBar(tr("Actions toolbar"));
     toolbar2->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
     toolbar2->addAction(exportAction);
+    toolbar2->setVisible(false);
+    
 }
 
 void BitcoinGUI::setClientModel(ClientModel *clientModel)
index da18c83..6149553 100644 (file)
@@ -32,8 +32,8 @@ static const int MAX_URI_LENGTH = 255;
 #define EXPORT_IMAGE_SIZE 256
 
 /* Colors for minting tab for each coin age group */
-#define COLOR_MINT_YOUNG QColor(127, 127, 240)
-#define COLOR_MINT_MATURE QColor(127, 240, 127)
-#define COLOR_MINT_OLD QColor(240, 127, 127)
+#define COLOR_MINT_YOUNG QColor(180, 180, 250)
+#define COLOR_MINT_MATURE QColor(180, 250, 180)
+#define COLOR_MINT_OLD QColor(250, 180, 180)
 
 #endif // GUICONSTANTS_H
index 4fbd50b..94d57d1 100644 (file)
       <translation>Показывать вероятность найти блок в течение : </translation>
     </message>
     <message>
-      <location filename="../mintingview.cpp" line="62"/>
+      <location filename="../mintingview.cpp" line="63"/>
       <source>10 min</source>
       <translation>10 минут</translation>
     </message>
     <message>
-      <location filename="../mintingview.cpp" line="63"/>
+      <location filename="../mintingview.cpp" line="64"/>
       <source>24 hours</source>
       <translation>24 часов</translation>
     </message>
     <message>
-      <location filename="../mintingview.cpp" line="64"/>
+      <location filename="../mintingview.cpp" line="65"/>
+      <source>7 days</source>
+      <translation>7 дней</translation>
+    </message>
+    <message>
+      <location filename="../mintingview.cpp" line="66"/>
       <source>30 days</source>
       <translation>30 дней</translation>
     </message>
     <message>
-      <location filename="../mintingview.cpp" line="65"/>
+      <location filename="../mintingview.cpp" line="67"/>
+      <source>60 days</source>
+      <translation>60 дней</translation>
+    </message>
+    <message>
+      <location filename="../mintingview.cpp" line="68"/>
       <source>90 days</source>
       <translation>90 дней</translation>
     </message>
     <message>
+      <location filename="../mintingview.cpp" line="103"/>
+      <source>Copy transaction ID of input</source>
+      <translation>Скопировать ID транзакции входа</translation>
+    </message>
+    <message>
+      <location filename="../mintingview.cpp" line="104"/>
+      <source>Copy address of input</source>
+      <translation>Скопировать адрес входа</translation>
+    </message>
+    <message>
+      <location filename="../mintingview.cpp" line="105"/>
+      <source>Show/hide 'Address' column</source>
+      <translation>Показать/скрыть столбец 'Адрес'</translation>
+    </message>
+    <message>
+      <location filename="../mintingview.cpp" line="106"/>
+      <source>Show/hide 'Transaction' column</source>
+      <translation>Показать/скрыть столбец 'Транзакция'</translation>
+    </message>
+    <message>
       <location filename="../mintingview.cpp" line="167"/>
       <source>Export Minting Data</source>
       <translation>Экспортировать данные таблицы</translation>
     </message>
     <message>
       <source>Balance</source>
-      <translation>Баланс</translation>
+      <translation>Сумма</translation>
     </message>
     <message>
       <source>MintProbability</source>
       <translation>Вероятность PoS</translation>
     </message>
     <message>
+      <source>MintReward</source>
+      <translation>Награда PoS</translation>
+    </message>
+    <message>
       <source>minutes</source>
       <translation>минут</translation>
     </message>
       <source>Chance to mint a block within given time interval.</source>
       <translation>Шанс найти блок в течение выбранного временного интервала.</translation>
     </message>
+    <message>
+      <source>The size of the potential rewards if the block is found at the beginning and the end given time interval.</source>
+      <translation>Размер потенциальной награды если блок найден в начале и конце выбранного временного интервала.</translation>
+    </message>
   </context>
 <context>
   <name>QObject</name>
   <message>
+    <source>from  %1 to %2</source>
+    <translation>от %1 до %2</translation>
+  </message>
+  <message>
     <source>%1 d</source>
     <translation>%1 д</translation>
   </message>
index 4973480..1007c95 100644 (file)
@@ -26,10 +26,11 @@ extern double GetDifficulty(const CBlockIndex* blockindex);
 static int column_alignments[] = {
     Qt::AlignLeft|Qt::AlignVCenter,
     Qt::AlignLeft|Qt::AlignVCenter,
-    Qt::AlignRight|Qt::AlignVCenter,
-    Qt::AlignRight|Qt::AlignVCenter,
-    Qt::AlignRight|Qt::AlignVCenter,
-    Qt::AlignRight|Qt::AlignVCenter
+    Qt::AlignLeft|Qt::AlignVCenter,
+    Qt::AlignLeft|Qt::AlignVCenter,
+    Qt::AlignLeft|Qt::AlignVCenter,
+    Qt::AlignLeft|Qt::AlignVCenter,
+    Qt::AlignLeft|Qt::AlignVCenter
 };
 
 struct TxLessThan
@@ -134,16 +135,17 @@ public:
                             KernelRecord::decomposeOutput(wallet, mi->second);
                     if(!toInsert.empty()) /* only if something to insert */
                     {
-                        parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex+toInsert.size()-1);
                         int insert_idx = lowerIndex;
                         BOOST_FOREACH(const KernelRecord &rec, toInsert)
                         {
-                            if(!rec.spent) {
+                            if(!rec.spent) 
+                            {
+                                parent->beginInsertRows(QModelIndex(), insert_idx, insert_idx);
                                 cachedWallet.insert(insert_idx, rec);
+                                parent->endInsertRows();
                                 insert_idx += 1;
                             }
                         }
-                        parent->endInsertRows();
                     }
                 }
                 else if(!inWallet && inModel)
@@ -202,7 +204,7 @@ MintingTableModel::MintingTableModel(CWallet *wallet, WalletModel *parent):
         mintingInterval(10),
         priv(new MintingTablePriv(wallet, this))
 {
-    columns << tr("Transaction") <<  tr("Address") << tr("Age") << tr("Balance") << tr("CoinDay") << tr("MintProbability");
+    columns << tr("Transaction") <<  tr("Address") << tr("Balance") << tr("Age") << tr("CoinDay") << tr("MintProbability") << tr("MintReward");
     priv->refreshWallet();
 
     QTimer *timer = new QTimer(this);
@@ -273,6 +275,8 @@ QVariant MintingTableModel::data(const QModelIndex &index, int role) const
             return formatTxCoinDay(rec);
         case MintProbability:
             return formatDayToMint(rec);
+        case MintReward:
+            return formatTxPoSReward(rec);
         }
         break;
       case Qt::TextAlignmentRole:
@@ -316,6 +320,8 @@ QVariant MintingTableModel::data(const QModelIndex &index, int role) const
             return rec->nValue;
         case MintProbability:
             return getDayToMint(rec);
+        case MintReward:
+            return formatTxPoSReward(rec);
         }
         break;
       case Qt::BackgroundColorRole:
@@ -359,6 +365,15 @@ QString MintingTableModel::lookupAddress(const std::string &address, bool toolti
     return description;
 }
 
+QString MintingTableModel::formatTxPoSReward(KernelRecord *wtx) const
+{
+    QString posReward;
+    int nBits = GetLastBlockIndex(pindexBest, true)->nBits;
+    posReward += QString(QObject::tr("from  %1 to %2")).arg(BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->getPoSReward(nBits, 0)), 
+        BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->getPoSReward(nBits, mintingInterval))); 
+    return posReward;
+}
+
 double MintingTableModel::getDayToMint(KernelRecord *wtx) const
 {
     const CBlockIndex *p = GetLastBlockIndex(pindexBest, true);
@@ -387,7 +402,7 @@ QString MintingTableModel::formatTxHash(const KernelRecord *wtx) const
 
 QString MintingTableModel::formatTxCoinDay(const KernelRecord *wtx) const
 {
-    return QString::number(wtx->coinAge);
+    return QString::number(wtx->getCoinDay());
 }
 
 QString MintingTableModel::formatTxAge(const KernelRecord *wtx) const
@@ -398,7 +413,7 @@ QString MintingTableModel::formatTxAge(const KernelRecord *wtx) const
 
 QString MintingTableModel::formatTxBalance(const KernelRecord *wtx) const
 {
-    return BitcoinUnits::format(walletModel->getOptionsModel()->getDisplayUnit(), wtx->nValue);
+    return BitcoinUnits::formatWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), wtx->nValue);
 }
 
 QVariant MintingTableModel::headerData(int section, Qt::Orientation orientation, int role) const
@@ -428,6 +443,8 @@ QVariant MintingTableModel::headerData(int section, Qt::Orientation orientation,
                 return tr("Coin age in the output.");
             case MintProbability:
                 return tr("Chance to mint a block within given time interval.");
+            case MintReward:
+                return tr("The size of the potential rewards if the block is found at the beginning and the end given time interval.");
             }
         }
     }
@@ -447,4 +464,3 @@ QModelIndex MintingTableModel::index(int row, int column, const QModelIndex &par
         return QModelIndex();
     }
 }
-
index c265ce5..47c5120 100644 (file)
@@ -20,10 +20,11 @@ public:
     enum ColumnIndex {
         TxHash = 0,
         Address = 1,
-        Age = 2,
-        Balance = 3,
+        Balance = 2,
+        Age = 3,
         CoinDay = 4,
-        MintProbability = 5
+        MintProbability = 5,
+        MintReward = 6
     };
 
 
@@ -51,6 +52,7 @@ private:
     QString formatTxAge(const KernelRecord *wtx) const;
     QString formatTxBalance(const KernelRecord *wtx) const;
     QString formatTxCoinDay(const KernelRecord *wtx) const;
+    QString formatTxPoSReward(KernelRecord *wtx) const;
 private slots:
     void update();
 
index 07ecadf..9a227e8 100644 (file)
@@ -17,6 +17,7 @@
 #include <QLineEdit>
 #include <QComboBox>
 #include <QMessageBox>
+#include <QMenu>
 
 MintingView::MintingView(QWidget *parent) :
     QWidget(parent), model(0), mintingView(0)
@@ -61,7 +62,9 @@ MintingView::MintingView(QWidget *parent) :
     mintingCombo = new QComboBox();
     mintingCombo->addItem(tr("10 min"), Minting10min);
     mintingCombo->addItem(tr("24 hours"), Minting1day);
+    mintingCombo->addItem(tr("7 days"), Minting7days);
     mintingCombo->addItem(tr("30 days"), Minting30days);
+    mintingCombo->addItem(tr("60 days"), Minting60days);
     mintingCombo->addItem(tr("90 days"), Minting90days);
     mintingCombo->setFixedWidth(120);
 
@@ -96,6 +99,24 @@ MintingView::MintingView(QWidget *parent) :
 
     connect(mintingCombo, SIGNAL(activated(int)), this, SLOT(chooseMintingInterval(int)));
 
+    // Actions
+    QAction *copyTxIDAction = new QAction(tr("Copy transaction ID of input"), this);
+    QAction *copyAddressAction = new QAction(tr("Copy address of input"), this);
+    QAction *showHideAddressAction = new QAction(tr("Show/hide 'Address' column"), this);
+    QAction *showHideTxIDAction = new QAction(tr("Show/hide 'Transaction' column"), this);
+
+    contextMenu = new QMenu();
+    contextMenu->addAction(copyAddressAction);
+    contextMenu->addAction(copyTxIDAction);
+    contextMenu->addAction(showHideAddressAction);
+    contextMenu->addAction(showHideTxIDAction);
+
+    connect(copyAddressAction, SIGNAL(triggered()), this, SLOT(copyAddress()));
+    connect(copyTxIDAction, SIGNAL(triggered()), this, SLOT(copyTxID()));
+    connect(showHideAddressAction, SIGNAL(triggered()), this, SLOT(showHideAddress()));
+    connect(showHideTxIDAction, SIGNAL(triggered()), this, SLOT(showHideTxID()));
+
+    connect(view, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextualMenu(QPoint)));
 }
 
 
@@ -118,22 +139,24 @@ void MintingView::setModel(WalletModel *model)
         mintingView->verticalHeader()->hide();
 
         mintingView->horizontalHeader()->resizeSection(
-                MintingTableModel::Address, 420);
+                MintingTableModel::Age, 60);
+        mintingView->horizontalHeader()->resizeSection(
+                MintingTableModel::Balance, 80);
+        mintingView->horizontalHeader()->resizeSection(
+                MintingTableModel::CoinDay,60);
+        mintingView->horizontalHeader()->resizeSection(
+                MintingTableModel::MintProbability, 105);
 #if QT_VERSION < 0x050000
         mintingView->horizontalHeader()->setResizeMode(
-                MintingTableModel::TxHash, QHeaderView::Stretch);
+                MintingTableModel::MintReward, QHeaderView::Stretch);
 #else
         mintingView->horizontalHeader()->setSectionResizeMode(
-                MintingTableModel::TxHash, QHeaderView::Stretch);
+                MintingTableModel::MintReward, QHeaderView::Stretch);
 #endif
         mintingView->horizontalHeader()->resizeSection(
-                MintingTableModel::Age, 120);
-        mintingView->horizontalHeader()->resizeSection(
-                MintingTableModel::Balance, 120);
+            MintingTableModel::Address, 245);
         mintingView->horizontalHeader()->resizeSection(
-                MintingTableModel::CoinDay,120);
-        mintingView->horizontalHeader()->resizeSection(
-                MintingTableModel::MintProbability, 160);
+            MintingTableModel::TxHash, 75);
     }
 }
 
@@ -148,9 +171,15 @@ void MintingView::chooseMintingInterval(int idx)
         case Minting1day:
             interval = 60*24;
             break;
+        case Minting7days:
+            interval = 60*24*7;
+            break;
         case Minting30days:
             interval = 60*24*30;
             break;
+        case Minting60days:
+            interval = 60*24*60;
+            break;
         case Minting90days:
             interval = 60*24*90;
             break;
@@ -173,12 +202,13 @@ void MintingView::exportClicked()
 
     // name, column, role
     writer.setModel(mintingProxyModel);
-    writer.addColumn(tr("Address"),0, MintingTableModel::Address);
-    writer.addColumn(tr("Transaction"), 0, MintingTableModel::TxHash);
-    writer.addColumn(tr("Age"), 0, MintingTableModel::Age);
-    writer.addColumn(tr("CoinDay"), 0, MintingTableModel::CoinDay);
-    writer.addColumn(tr("Balance"), 0, MintingTableModel::Balance);
-    writer.addColumn(tr("MintingProbability"), 0, MintingTableModel::MintProbability);
+    writer.addColumn(tr("Address"),MintingTableModel::Address,0);
+    writer.addColumn(tr("Transaction"),MintingTableModel::TxHash,0);
+    writer.addColumn(tr("Age"), MintingTableModel::Age,0);
+    writer.addColumn(tr("CoinDay"), MintingTableModel::CoinDay,0);
+    writer.addColumn(tr("Balance"), MintingTableModel::Balance,0);
+    writer.addColumn(tr("MintingProbability"), MintingTableModel::MintProbability,0);
+    writer.addColumn(tr("MintingReward"), MintingTableModel::MintReward,0);
 
     if(!writer.write())
     {
@@ -186,3 +216,34 @@ void MintingView::exportClicked()
                               QMessageBox::Abort, QMessageBox::Abort);
     }
 }
+
+void MintingView::copyTxID()
+{
+    GUIUtil::copyEntryData(mintingView, MintingTableModel::TxHash, 0);
+}
+
+void MintingView::copyAddress()
+{
+    GUIUtil::copyEntryData(mintingView, MintingTableModel::Address, 0 );
+}
+
+void MintingView::showHideAddress()
+{
+    mintingView->horizontalHeader()->setSectionHidden(MintingTableModel::Address, 
+        !(mintingView->horizontalHeader()->isSectionHidden(MintingTableModel::Address)));
+}
+
+void MintingView::showHideTxID()
+{
+    mintingView->horizontalHeader()->setSectionHidden(MintingTableModel::TxHash, 
+        !(mintingView->horizontalHeader()->isSectionHidden(MintingTableModel::TxHash)));
+}
+
+void MintingView::contextualMenu(const QPoint &point)
+{
+    QModelIndex index = mintingView->indexAt(point);
+    if(index.isValid())
+    {
+        contextMenu->exec(QCursor::pos());
+    }
+}
\ No newline at end of file
index 452deaa..f318011 100644 (file)
@@ -10,6 +10,7 @@ class WalletModel;
 
 QT_BEGIN_NAMESPACE
 class QTableView;
+class QMenu;
 QT_END_NAMESPACE
 
 class MintingView : public QWidget
@@ -23,7 +24,9 @@ public:
     {
         Minting10min,
         Minting1day,
+        Minting7days,
         Minting30days,
+        Minting60days,
         Minting90days
     };
 
@@ -35,11 +38,18 @@ private:
 
     MintingFilterProxy *mintingProxyModel;
 
+    QMenu *contextMenu;
+
 signals:
 
 public slots:
     void exportClicked();
     void chooseMintingInterval(int idx);
+    void copyTxID();
+    void copyAddress();
+    void showHideAddress();
+    void showHideTxID();
+    void contextualMenu(const QPoint &point);
 };
 
 #endif // MINTINGVIEW_H
diff --git a/src/stun.cpp b/src/stun.cpp
new file mode 100644 (file)
index 0000000..c5863f0
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ * Get External IP address by STUN protocol
+ *
+ * Based on project Minimalistic STUN client "ministun"
+ * https://code.google.com/p/ministun/
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ *
+ * STUN is described in RFC3489 and it is based on the exchange
+ * of UDP packets between a client and one or more servers to
+ * determine the externally visible address (and port) of the client
+ * once it has gone through the NAT boxes that connect it to the
+ * outside.
+ * The simplest request packet is just the header defined in
+ * struct stun_header, and from the response we may just look at
+ * one attribute, STUN_MAPPED_ADDRESS, that we find in the response.
+ * By doing more transactions with different server addresses we
+ * may determine more about the behaviour of the NAT boxes, of
+ * course - the details are in the RFC.
+ *
+ * All STUN packets start with a simple header made of a type,
+ * length (excluding the header) and a 16-byte random transaction id.
+ * Following the header we may have zero or more attributes, each
+ * structured as a type, length and a value (whose format depends
+ * on the type, but often contains addresses).
+ * Of course all fields are in network format.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#ifdef WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+
+
+#include "ministun.h"
+
+/*---------------------------------------------------------------------*/
+
+struct StunSrv {
+    char     name[30];
+    uint16_t port;
+};
+
+/*---------------------------------------------------------------------*/
+static const int StunSrvListQty = 263; // Must be PRIME!!!!!
+
+static struct StunSrv StunSrvList[263] = {
+    {"23.21.150.121", 3478},
+    {"iphone-stun.strato-iphone.de", 3478},
+    {"numb.viagenie.ca", 3478},
+    {"s1.taraba.net", 3478},
+    {"s2.taraba.net", 3478},
+    {"stun.12connect.com", 3478},
+    {"stun.12voip.com", 3478},
+    {"stun.1und1.de", 3478},
+    {"stun.2talk.co.nz", 3478},
+    {"stun.2talk.com", 3478},
+    {"stun.3clogic.com", 3478},
+    {"stun.3cx.com", 3478},
+    {"stun.a-mm.tv", 3478},
+    {"stun.aa.net.uk", 3478},
+    {"stun.acrobits.cz", 3478},
+    {"stun.actionvoip.com", 3478},
+    {"stun.advfn.com", 3478},
+    {"stun.aeta-audio.com", 3478},
+    {"stun.aeta.com", 3478},
+    {"stun.alltel.com.au", 3478},
+    {"stun.altar.com.pl", 3478},
+    {"stun.annatel.net", 3478},
+    {"stun.arbuz.ru", 3478},
+    {"stun.avigora.com", 3478},
+    {"stun.avigora.fr", 3478},
+    {"stun.awa-shima.com", 3478},
+    {"stun.awt.be", 3478},
+    {"stun.b2b2c.ca", 3478},
+    {"stun.bahnhof.net", 3478},
+    {"stun.barracuda.com", 3478},
+    {"stun.bluesip.net", 3478},
+    {"stun.bmwgs.cz", 3478},
+    {"stun.botonakis.com", 3478},
+    {"stun.budgetphone.nl", 3478},
+    {"stun.budgetsip.com", 3478},
+    {"stun.cablenet-as.net", 3478},
+    {"stun.callromania.ro", 3478},
+    {"stun.callwithus.com", 3478},
+    {"stun.cbsys.net", 3478},
+    {"stun.chathelp.ru", 3478},
+    {"stun.cheapvoip.com", 3478},
+    {"stun.ciktel.com", 3478},
+    {"stun.cloopen.com", 3478},
+    {"stun.colouredlines.com.au", 3478},
+    {"stun.comfi.com", 3478},
+    {"stun.commpeak.com", 3478},
+    {"stun.comtube.com", 3478},
+    {"stun.comtube.ru", 3478},
+    {"stun.cope.es", 3478},
+    {"stun.counterpath.com", 3478},
+    {"stun.counterpath.net", 3478},
+    {"stun.cryptonit.net", 3478},
+    {"stun.darioflaccovio.it", 3478},
+    {"stun.datamanagement.it", 3478},
+    {"stun.dcalling.de", 3478},
+    {"stun.decanet.fr", 3478},
+    {"stun.demos.ru", 3478},
+    {"stun.develz.org", 3478},
+    {"stun.dingaling.ca", 3478},
+    {"stun.doublerobotics.com", 3478},
+    {"stun.drogon.net", 3478},
+    {"stun.duocom.es", 3478},
+    {"stun.dus.net", 3478},
+    {"stun.e-fon.ch", 3478},
+    {"stun.easybell.de", 3478},
+    {"stun.easycall.pl", 3478},
+    {"stun.easyvoip.com", 3478},
+    {"stun.efficace-factory.com", 3478},
+    {"stun.einsundeins.com", 3478},
+    {"stun.einsundeins.de", 3478},
+    {"stun.ekiga.net", 3478},
+    {"stun.epygi.com", 3478},
+    {"stun.etoilediese.fr", 3478},
+    {"stun.eyeball.com", 3478},
+    {"stun.faktortel.com.au", 3478},
+    {"stun.freecall.com", 3478},
+    {"stun.freeswitch.org", 3478},
+    {"stun.freevoipdeal.com", 3478},
+    {"stun.fuzemeeting.com", 3478},
+    {"stun.gmx.de", 3478},
+    {"stun.gmx.net", 3478},
+    {"stun.gradwell.com", 3478},
+    {"stun.halonet.pl", 3478},
+    {"stun.hoiio.com", 3478},
+    {"stun.hosteurope.de", 3478},
+    {"stun.ideasip.com", 3478},
+    {"stun.imesh.com", 3478},
+    {"stun.infra.net", 3478},
+    {"stun.internetcalls.com", 3478},
+    {"stun.intervoip.com", 3478},
+    {"stun.ipcomms.net", 3478},
+    {"stun.ipfire.org", 3478},
+    {"stun.ippi.fr", 3478},
+    {"stun.ipshka.com", 3478},
+    {"stun.iptel.org", 3478},
+    {"stun.irian.at", 3478},
+    {"stun.it1.hr", 3478},
+    {"stun.ivao.aero", 3478},
+    {"stun.jappix.com", 3478},
+    {"stun.jumblo.com", 3478},
+    {"stun.justvoip.com", 3478},
+    {"stun.kanet.ru", 3478},
+    {"stun.kiwilink.co.nz", 3478},
+    {"stun.kundenserver.de", 3478},
+    {"stun.l.google.com", 19302},
+    {"stun.linea7.net", 3478},
+    {"stun.linphone.org", 3478},
+    {"stun.liveo.fr", 3478},
+    {"stun.lowratevoip.com", 3478},
+    {"stun.lugosoft.com", 3478},
+    {"stun.lundimatin.fr", 3478},
+    {"stun.magnet.ie", 3478},
+    {"stun.manle.com", 3478},
+    {"stun.mgn.ru", 3478},
+    {"stun.mit.de", 3478},
+    {"stun.mitake.com.tw", 3478},
+    {"stun.miwifi.com", 3478},
+    {"stun.modulus.gr", 3478},
+    {"stun.mozcom.com", 3478},
+    {"stun.myvoiptraffic.com", 3478},
+    {"stun.mywatson.it", 3478},
+    {"stun.nas.net", 3478},
+    {"stun.neotel.co.za", 3478},
+    {"stun.netappel.com", 3478},
+    {"stun.netappel.fr", 3478},
+    {"stun.netgsm.com.tr", 3478},
+    {"stun.nfon.net", 3478},
+    {"stun.noblogs.org", 3478},
+    {"stun.noc.ams-ix.net", 3478},
+    {"stun.node4.co.uk", 3478},
+    {"stun.nonoh.net", 3478},
+    {"stun.nottingham.ac.uk", 3478},
+    {"stun.nova.is", 3478},
+    {"stun.nventure.com", 3478},
+    {"stun.on.net.mk", 3478},
+    {"stun.ooma.com", 3478},
+    {"stun.ooonet.ru", 3478},
+    {"stun.outland-net.de", 3478},
+    {"stun.ozekiphone.com", 3478},
+    {"stun.patlive.com", 3478},
+    {"stun.personal-voip.de", 3478},
+    {"stun.petcube.com", 3478},
+    {"stun.phone.com", 3478},
+    {"stun.phoneserve.com", 3478},
+    {"stun.pjsip.org", 3478},
+    {"stun.poivy.com", 3478},
+    {"stun.powerpbx.org", 3478},
+    {"stun.powervoip.com", 3478},
+    {"stun.ppdi.com", 3478},
+    {"stun.prizee.com", 3478},
+    {"stun.qq.com", 3478},
+    {"stun.qvod.com", 3478},
+    {"stun.rackco.com", 3478},
+    {"stun.rapidnet.de", 3478},
+    {"stun.rb-net.com", 3478},
+    {"stun.remote-learner.net", 3478},
+    {"stun.rixtelecom.se", 3478},
+    {"stun.rockenstein.de", 3478},
+    {"stun.rolmail.net", 3478},
+    {"stun.rounds.com", 3478},
+    {"stun.rynga.com", 3478},
+    {"stun.samsungsmartcam.com", 3478},
+    {"stun.schlund.de", 3478},
+    {"stun.services.mozilla.com", 3478},
+    {"stun.sigmavoip.com", 3478},
+    {"stun.sip.us", 3478},
+    {"stun.sipdiscount.com", 3478},
+    {"stun.sipgate.net", 10000},
+    {"stun.sipgate.net", 3478},
+    {"stun.siplogin.de", 3478},
+    {"stun.sipnet.net", 3478},
+    {"stun.sipnet.ru", 3478},
+    {"stun.siportal.it", 3478},
+    {"stun.sippeer.dk", 3478},
+    {"stun.siptraffic.com", 3478},
+    {"stun.skylink.ru", 3478},
+    {"stun.sma.de", 3478},
+    {"stun.smartvoip.com", 3478},
+    {"stun.smsdiscount.com", 3478},
+    {"stun.snafu.de", 3478},
+    {"stun.softjoys.com", 3478},
+    {"stun.solcon.nl", 3478},
+    {"stun.solnet.ch", 3478},
+    {"stun.sonetel.com", 3478},
+    {"stun.sonetel.net", 3478},
+    {"stun.sovtest.ru", 3478},
+    {"stun.speedy.com.ar", 3478},
+    {"stun.spokn.com", 3478},
+    {"stun.srce.hr", 3478},
+    {"stun.ssl7.net", 3478},
+    {"stun.stunprotocol.org", 3478},
+    {"stun.symform.com", 3478},
+    {"stun.symplicity.com", 3478},
+    {"stun.sysadminman.net", 3478},
+    {"stun.t-online.de", 3478},
+    {"stun.tagan.ru", 3478},
+    {"stun.tatneft.ru", 3478},
+    {"stun.teachercreated.com", 3478},
+    {"stun.tel.lu", 3478},
+    {"stun.telbo.com", 3478},
+    {"stun.telefacil.com", 3478},
+    {"stun.tis-dialog.ru", 3478},
+    {"stun.tng.de", 3478},
+    {"stun.twt.it", 3478},
+    {"stun.u-blox.com", 3478},
+    {"stun.ucallweconn.net", 3478},
+    {"stun.ucsb.edu", 3478},
+    {"stun.ucw.cz", 3478},
+    {"stun.uls.co.za", 3478},
+    {"stun.unseen.is", 3478},
+    {"stun.usfamily.net", 3478},
+    {"stun.veoh.com", 3478},
+    {"stun.vidyo.com", 3478},
+    {"stun.vipgroup.net", 3478},
+    {"stun.virtual-call.com", 3478},
+    {"stun.viva.gr", 3478},
+    {"stun.vivox.com", 3478},
+    {"stun.vline.com", 3478},
+    {"stun.vo.lu", 3478},
+    {"stun.vodafone.ro", 3478},
+    {"stun.voicetrading.com", 3478},
+    {"stun.voip.aebc.com", 3478},
+    {"stun.voip.blackberry.com", 3478},
+    {"stun.voip.eutelia.it", 3478},
+    {"stun.voiparound.com", 3478},
+    {"stun.voipblast.com", 3478},
+    {"stun.voipbuster.com", 3478},
+    {"stun.voipbusterpro.com", 3478},
+    {"stun.voipcheap.co.uk", 3478},
+    {"stun.voipcheap.com", 3478},
+    {"stun.voipfibre.com", 3478},
+    {"stun.voipgain.com", 3478},
+    {"stun.voipgate.com", 3478},
+    {"stun.voipinfocenter.com", 3478},
+    {"stun.voipplanet.nl", 3478},
+    {"stun.voippro.com", 3478},
+    {"stun.voipraider.com", 3478},
+    {"stun.voipstunt.com", 3478},
+    {"stun.voipwise.com", 3478},
+    {"stun.voipzoom.com", 3478},
+    {"stun.voxgratia.org", 3478},
+    {"stun.voxox.com", 3478},
+    {"stun.voys.nl", 3478},
+    {"stun.voztele.com", 3478},
+    {"stun.vyke.com", 3478},
+    {"stun.webcalldirect.com", 3478},
+    {"stun.whoi.edu", 3478},
+    {"stun.wifirst.net", 3478},
+    {"stun.wwdl.net", 3478},
+    {"stun.xs4all.nl", 3478},
+    {"stun.xtratelecom.es", 3478},
+    {"stun.yesss.at", 3478},
+    {"stun.zadarma.com", 3478},
+    {"stun.zadv.com", 3478},
+    {"stun.zoiper.com", 3478},
+    {"stun1.faktortel.com.au", 3478},
+    {"stun1.l.google.com", 19302},
+    {"stun1.voiceeclipse.net", 3478},
+    {"stun2.l.google.com", 19302},
+    {"stun3.l.google.com", 19302},
+    {"stun4.l.google.com", 19302},
+    {"stunserver.org", 3478},
+    {"stun.antisip.com",    3478}
+};
+
+
+/* wrapper to send an STUN message */
+static int stun_send(int s, struct sockaddr_in *dst, struct stun_header *resp)
+{
+    return sendto(s, (const char *)resp, ntohs(resp->msglen) + sizeof(*resp), 0,
+                  (struct sockaddr *)dst, sizeof(*dst));
+}
+
+/* helper function to generate a random request id */
+static uint64_t randfiller;
+static void stun_req_id(struct stun_header *req)
+{
+    const uint64_t *S_block = (const uint64_t *)StunSrvList;
+    req->id.id[0] |= 0x55555555;
+    req->id.id[1] &= 0x55555555;
+    req->id.id[2] |= 0x55555555;
+    req->id.id[3] &= 0x55555555;
+    register char x = 20;
+    do {
+        uint32_t s_elm = S_block[(uint8_t)randfiller];
+        randfiller ^= (randfiller << 5) | (randfiller >> (64 - 5));
+        randfiller += s_elm ^ x;
+        req->id.id[x & 3] ^= randfiller + (randfiller >> 13);
+    } while(--x);
+}
+
+/* callback type to be invoked on stun responses. */
+typedef int (stun_cb_f)(struct stun_attr *attr, void *arg);
+
+/* handle an incoming STUN message.
+ *
+ * Do some basic sanity checks on packet size and content,
+ * try to extract a bit of information, and possibly reply.
+ * At the moment this only processes BIND requests, and returns
+ * the externally visible address of the request.
+ * If a callback is specified, invoke it with the attribute.
+ */
+static int stun_handle_packet(int s, struct sockaddr_in *src,
+                              unsigned char *data, size_t len, stun_cb_f *stun_cb, void *arg)
+{
+    struct stun_header *hdr = (struct stun_header *)data;
+    struct stun_attr *attr;
+    int ret = len;
+    unsigned int x;
+
+    /* On entry, 'len' is the length of the udp payload. After the
+   * initial checks it becomes the size of unprocessed options,
+   * while 'data' is advanced accordingly.
+   */
+    if (len < sizeof(struct stun_header))
+        return -20;
+
+    len -= sizeof(struct stun_header);
+    data += sizeof(struct stun_header);
+    x = ntohs(hdr->msglen); /* len as advertised in the message */
+    if(x < len)
+        len = x;
+
+    while (len) {
+        if (len < sizeof(struct stun_attr)) {
+            ret = -21;
+            break;
+        }
+        attr = (struct stun_attr *)data;
+        /* compute total attribute length */
+        x = ntohs(attr->len) + sizeof(struct stun_attr);
+        if (x > len) {
+            ret = -22;
+            break;
+        }
+        stun_cb(attr, arg);
+        //if (stun_process_attr(&st, attr)) {
+        //  ret = -23;
+        //  break;
+        // }
+        /* Clear attribute id: in case previous entry was a string,
+     * this will act as the terminator for the string.
+     */
+        attr->attr = 0;
+        data += x;
+        len -= x;
+    } // while
+    /* Null terminate any string.
+   * XXX NOTE, we write past the size of the buffer passed by the
+   * caller, so this is potentially dangerous. The only thing that
+   * saves us is that usually we read the incoming message in a
+   * much larger buffer
+   */
+    *data = '\0';
+
+    /* Now prepare to generate a reply, which at the moment is done
+   * only for properly formed (len == 0) STUN_BINDREQ messages.
+   */
+
+    return ret;
+}
+
+/* Extract the STUN_MAPPED_ADDRESS from the stun response.
+ * This is used as a callback for stun_handle_response
+ * when called from stun_request.
+ */
+static int stun_get_mapped(struct stun_attr *attr, void *arg)
+{
+    struct stun_addr *addr = (struct stun_addr *)(attr + 1);
+    struct sockaddr_in *sa = (struct sockaddr_in *)arg;
+
+    if (ntohs(attr->attr) != STUN_MAPPED_ADDRESS || ntohs(attr->len) != 8)
+        return 1; /* not us. */
+    sa->sin_port = addr->port;
+    sa->sin_addr.s_addr = addr->addr;
+    return 0;
+}
+
+/*---------------------------------------------------------------------*/
+
+static int StunRequest2(int sock, struct sockaddr_in *server, struct sockaddr_in *mapped) {
+
+    struct stun_header *req;
+    unsigned char reqdata[1024];
+
+    req = (struct stun_header *)reqdata;
+    stun_req_id(req);
+    int reqlen = 0;
+    req->msgtype = 0;
+    req->msglen = 0;
+    req->msglen = htons(reqlen);
+    req->msgtype = htons(STUN_BINDREQ);
+
+    unsigned char reply_buf[1024];
+    fd_set rfds;
+    struct timeval to = { STUN_TIMEOUT, 0 };
+    struct sockaddr_in src;
+#ifdef WIN32
+    int srclen;
+#else
+    socklen_t srclen;
+#endif
+
+    int res = stun_send(sock, server, req);
+    if(res < 0)
+        return -10;
+    FD_ZERO(&rfds);
+    FD_SET(sock, &rfds);
+    res = select(sock + 1, &rfds, NULL, NULL, &to);
+    if (res <= 0)  /* timeout or error */
+        return -11;
+    memset(&src, 0, sizeof(src));
+    srclen = sizeof(src);
+    /* XXX pass -1 in the size, because stun_handle_packet might
+   * write past the end of the buffer.
+   */
+    res = recvfrom(sock, (char *)reply_buf, sizeof(reply_buf) - 1,
+                   0, (struct sockaddr *)&src, &srclen);
+    if (res <= 0)
+        return -12;
+    memset(mapped, 0, sizeof(struct sockaddr_in));
+    return stun_handle_packet(sock, &src, reply_buf, res, stun_get_mapped, mapped);
+} // StunRequest2
+
+/*---------------------------------------------------------------------*/
+static int StunRequest(const char *host, uint16_t port, struct sockaddr_in *mapped) {
+    struct hostent *hostinfo = gethostbyname(host);
+    if(hostinfo == NULL)
+        return -1;
+
+    struct sockaddr_in server, client;
+    memset(&server, 0, sizeof(server));
+    memset(&client, 0, sizeof(client));
+    server.sin_family = client.sin_family = AF_INET;
+
+    server.sin_addr = *(struct in_addr*) hostinfo->h_addr;
+    server.sin_port = htons(port);
+
+    int sock = socket(AF_INET, SOCK_DGRAM, 0);
+    if(sock < 0)
+        return -2;
+
+    client.sin_addr.s_addr = htonl(INADDR_ANY);
+
+    int rc = -3;
+    if(bind(sock, (struct sockaddr*)&client, sizeof(client)) >= 0)
+        rc = StunRequest2(sock, &server, mapped);
+
+    close(sock);
+    return rc;
+} // StunRequest
+
+/*---------------------------------------------------------------------*/
+// Input: two random values (pos, step) for generate uniuqe way over server
+// list
+// Output: populate struct struct mapped
+// Retval:
+
+int GetExternalIPbySTUN(uint64_t rnd, struct sockaddr_in *mapped, const char **srv) {
+    randfiller    = rnd;
+    uint16_t pos  = rnd;
+    uint16_t step;
+    do {
+        rnd = (rnd >> 8) | 0xff00000000000000LL;
+        step = rnd % StunSrvListQty;
+    } while(step == 0);
+
+    uint16_t attempt;
+    for(attempt = 1; attempt < StunSrvListQty * 2; attempt++) {
+        pos = (pos + step) % StunSrvListQty;
+        int rc = StunRequest(*srv = StunSrvList[pos].name, StunSrvList[pos].port, mapped);
+        if(rc >= 0)
+            return attempt;
+        // fprintf(stderr, "Lookup: %s:%u\t%s\t%d\n", StunSrvList[pos].name,
+        // StunSrvList[pos].port, inet_ntoa(mapped->sin_addr), rc);
+    }
+    return -1;
+}
index 74a4a68..d12e28f 100644 (file)
@@ -648,8 +648,7 @@ bool CWallet::IsChange(const CTxOut& txout) const
 
 int64 CWalletTx::GetTxTime() const
 {
-    int64 n = nTimeSmart;
-    return n ? n : nTimeReceived;
+    return nTime;
 }
 
 int CWalletTx::GetRequestCount() const