Could you explain me how to process the transactions
(I know how to create & sign them), but how to send them now?
This is very "dirty" piece of code, but it works.
Of course, this one is "for educational purposes only"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "Bitcoin.h"
#define _xtrace(X) qDebug ( ) << ( X )
#define _xassert(X) Q_ASSERT ( X )
#define MAGIC_ID 0x709110b
#define TYPE_VERSION ( "version" "\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" )
#define TYPE_VERACK ( "verack" "\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" )
#define TYPE_GETDATA ( "getdata" "\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" )
#define TYPE_PING ( "ping" "\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" )
#define TYPE_PONG ( "pong" "\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" )
#define TYPE_TX ( "tx" "\0\0" "\0\0\0\0" "\0\0\0\0" "\0\0\0\0" )
#define USER_AGENT "/Satoshi:0.9.1/"
#define PROTO_VERSION 70002
#define PROTO_SERVICES (1) // can be NODE_NETWORK
Bitcoin::Bitcoin ( const QByteArray& tx, QObject* parent) : QObject ( parent )
{
this -> children = 0;
this -> tx = tx;
QTimer::singleShot ( 10, this, SLOT ( start ( ) ) );
}
//--------------------------------------------------------------
void Bitcoin::start ( )
{
QFile conf ( "PushTxTool.conf" );
_xassert ( conf.open ( QIODevice::ReadOnly ) );
QByteArray data ( conf.readAll ( ) );
data.replace ( '\n', ' ' );
data.replace ( '\r', ' ' );
data.replace ( '\t', ' ' );
const QList list ( data.split ( ' ' ) );
for ( int i ( 0 ); i < list.size ( ); i++ )
if ( !list.at ( i ).isEmpty ( ) )
{
static QSet set;
if ( !set.contains ( list.at ( i ) ) )
{
new NetSocket ( list.at ( i ), tx, this );
children++;
set.insert ( list.at ( i ) );
}
else
_xtrace ( QString ( "err %1 ..." ).arg ( list.at ( i ).constData ( ) ) );
}
}
//--------------------------------------------------------------
void Bitcoin::finished ( )
{
if ( --children == 0 )
QTimer::singleShot ( 10, this, SLOT ( done ( ) ) );
}
//--------------------------------------------------------------
void Bitcoin::done ( )
{
_xtrace ( "-----------done------------" );
QCoreApplication::exit ( );
}
//==============================================================
NetSocket::NetSocket ( const QByteArray& host, const QByteArray& tx, QObject* parent ) : QObject ( parent )
{
this -> host = host;
_xtrace ( QString ( "%1 ..." ).arg ( host.constData ( ) ) );
this -> tx = tx;
this -> socket = new QTcpSocket ( this );
connect ( this, SIGNAL ( destroyed ( ) ), parent, SLOT ( finished ( ) ) );
QTimer::singleShot ( 1, this, SLOT ( start ( ) ) );
}
//--------------------------------------------------------------
void NetSocket::start ( )
{
_xtrace ( QString ( "%1 start ..." ).arg ( host.constData ( ) ) );
connect ( socket, SIGNAL ( connected ( ) ), this, SLOT ( onConnected ( ) ) );
connect ( socket, SIGNAL ( readyRead ( ) ), this, SLOT ( onReadyRead ( ) ) );
connect ( socket, SIGNAL ( error ( QAbstractSocket::SocketError ) ), this, SLOT ( onError ( QAbstractSocket::SocketError ) ) );
socket -> connectToHost ( host.constData ( ), 18333 );
}
//--------------------------------------------------------------
void NetSocket::onConnected ( )
{
_xtrace ( QString ( "%1 connected" ).arg ( host.constData ( ) ) );
write ( packet ( TYPE_VERSION, versionPacket ( 600000, USER_AGENT ) ) );
}
//--------------------------------------------------------------
void NetSocket::onReadyRead ( )
{
QDataStream in ( socket );
for ( qint64 length; (length = socket -> bytesAvailable ( )) >= 0; )
{
char data [length];
const int read ( in.readRawData ( data, length ) );
if ( read > 0 )
buf.append ( data, read );
else
break;
}
for ( QByteArray b; buf.readPacket ( b ); )
proc ( b );
buf.squeeze ( );
}
//--------------------------------------------------------------
void NetSocket::proc ( const QByteArray& data )
{
const char* type = data.constData ( ) + 4;
if ( !strcmp ( type, "version" ) ) { procVersionPacket ( data ); return; }
if ( !strcmp ( type, "verack" ) ) { procVerackPacket ( data ); return; }
}
//--------------------------------------------------------------
void NetSocket::onError ( QAbstractSocket::SocketError code )
{
_xtrace ( QString ( "%1 : error %2" ).arg ( host.constData ( ) ).arg ( code ) );
deleteLater ( );
}
//--------------------------------------------------------------
const QByteArray NetSocket::packet ( const char* type, const QByteArray& payload )
{
return MyByteArray ( )
.putInt32 ( MAGIC_ID )
.putAscii_12 ( type )
.putInt32 ( payload.size ( ) )
.append ( MyKey32 ( payload.constData ( ), payload.size ( ) ).constData ( ), 4 )
.append ( payload );
}
//--------------------------------------------------------------
const QByteArray NetSocket::versionPacket ( const int known, const char* ua ) const
{
return MyByteArray ( )
.putInt32 ( PROTO_VERSION )
.putInt64 ( PROTO_SERVICES )
.putInt64 ( QDateTime::currentMSecsSinceEpoch ( ) / 1000 )
.putInt64 ( PROTO_SERVICES )
.putInt64 ( 0 ) // date
.putInt32 ( 0xFFFF0000 )
.putInt32_be ( socket -> peerAddress ( ).toIPv4Address ( ) )
.putInt16_be ( socket -> peerPort ( ) )
.putInt64 ( 0 )
.putInt64 ( 0 )
.putInt32 ( 0xFFFF0000 )
.putInt32 ( 0 )
.putInt16_be ( 18333 )
.putInt64 ( (quint64)qrand ( ) ^ QDateTime::currentMSecsSinceEpoch ( ) )
.putVarAscii ( ua )
.putInt32 ( known )
.putInt8 ( 1 );
}
//--------------------------------------------------------------
void NetSocket::procVersionPacket ( const QByteArray& )
{
write ( packet ( TYPE_VERACK, verackPacket ( ) ) );
}
//--------------------------------------------------------------
void NetSocket::procVerackPacket ( const QByteArray& )
{
write ( packet ( TYPE_TX, txPacket ( tx ) ) );
_xtrace ( QString ( "%1 sent %2" ).arg ( host.constData ( ) ).arg ( MyKey32 ( tx.constData ( ), tx.size ( ) ).toString ( ) ) );
QTimer::singleShot ( 0, this, SLOT ( deleteLater ( ) ) );
}
//--------------------------------------------------------------
MyByteArray& MyByteArray::putVarInt ( const unsigned value )
{
return ( value < 0xFD ) ? putInt8 ( value ) :
( value <= 0xFFFF ) ? putInt8 ( 0xFD ).putInt16 ( value ) :
putInt8 ( 0xFE ).putInt32 ( value );
}
//--------------------------------------------------------------
bool MyByteArray::readPacket ( QByteArray& buf )
{
if ( size ( ) > 20 )
{
_xassert ( *(quint32*)(constData ( )) == MAGIC_ID );
const int sz ( *(qint32*)(constData ( ) + 16) );
if ( size ( ) >= ( 24 + sz ) )
{
buf = QByteArray ( constData ( ), 24 + sz );
remove ( 0, 24 + sz );
return true;
}
}
return false;
}