Bitcoin Forum
July 30, 2024, 05:40:55 PM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: Malleated Transactions  (Read 743 times)
polynesia (OP)
Legendary
*
Offline Offline

Activity: 1358
Merit: 1000



View Profile
October 30, 2015, 12:30:48 AM
Last edit: October 30, 2015, 12:58:57 AM by polynesia
 #1

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.

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

I am a bit technically challenged and would like some help / pointers on how I can malleate a transaction.
CounterEntropy
Full Member
***
Offline Offline

Activity: 214
Merit: 278


View Profile
October 30, 2015, 10:56:51 AM
 #2

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:

Code:
<signature> <pubkey>

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:36d047abcb966f58aa668f050d60254730a3c07c9fd51e869e8b1a773c05d516



Step 2: split the signature into components



Via bitcoin.stackexchange.com

Note 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:

Code:
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...

Code:
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<QByteArray> 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
polynesia (OP)
Legendary
*
Offline Offline

Activity: 1358
Merit: 1000



View Profile
November 01, 2015, 01:15:15 PM
 #3


Thank you CounterEntropy. That was very helpful.
Could you please direct me to what headers I have to include, so that I can run the code?
I have included QCoreapplication and have libbitcoin installed. Is there anything else I should do?
Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!