Was ist falsch an diesem C#-Code, der die Bits eines Blocks in ein Ziel umwandelt?

UPDATE: Der Code in dieser Frage funktioniert für alle Testfälle (Hurra!), aber ich mag nicht, wie GetCompactdie Math.AbsFunktion verwendet wird, und glaube nicht, dass sie der OpenSSL-Implementierung entspricht.

Die Behebung dieses Problems wird diese Implementierung wahrscheinlich "perfekt" machen.

Das Kernproblem (glaube ich) ist, wenn .NET eine Bitverschiebung einer negativen Zahl durchführt, es die negative Zahl erweitert, und weil es im Kompliment von Two gespeichert ist, sind die zusätzlichen Bytes der Linksverschiebung alle Einsen.

C++ macht wahrscheinlich etwas anderes, wenn es eine BigInteger nach links verschiebt, da gemäß der C++-Spezifikation die Linksverschiebung negativer Zahlen undefiniert ist.

Die Lösung besteht darin, anstelle einer Verschiebung die entsprechende Multiplikation oder Division zu verwenden. ... Ich bin mir nicht sicher, wie ich das machen soll, also wäre Ihre Hilfe dankbar.


Ich arbeite an dem folgenden C#-Code und habe versucht, ihn der ursprünglichen C++-Quelle treu zu halten. Und ich versuche, diesen Code so zu bekommen, dass er mit den hier beschriebenen Komponententests übereinstimmt.

Mein Ziel ist nicht nur eine .NET-Darstellung der QT-Datenstrukturen, sondern auch das Lesen und Analysieren von JSON-RPC-Code.

C#-Tests

       BigInteger bb =   BitcoinQT.SetCompact(numToCompact);


        bb = BitcoinQT.SetCompact(0x00123456);
       /*
       00000000000100100011010001010110 SetCompact:
       00000000011111111111111111111111 Bitmask & (extract 0..23)
       00000000000100100011010001010110 result
       00000000000000000000000000000000 Read bytes 25..32 (>> 24)
       000100100011010001010110 preshifted 24
       00000000 postshifted 24
       00000000100000000000000000000000 ... check bit is neg
       00000000000000000000000000000000 ... Result

       00000000000000000000000000001100 ERROR RESULT SHOULD BE THIS 
        */

C#-Code

   class BlockTargetBits
{

   static  bool debug = false;

   internal static string GetCompact(BigInteger originalBigNumber)
   {
       // 
       // 
       // Get Compact
       BigInteger num = originalBigNumber;
       byte[] numAsBytes = num.ToByteArray();
       uint compactBitsRepresentation = 0;
       uint size2;// BN_num_bytes(num);
       size2 = (uint)originalBigNumber.NumberOfBytes();
       if (size2 <= 3)
       {
           uint amountToShift2 = 8 * (3 - size2);
           if (debug) Console.WriteLine(GetBits(num) + " will be shifted " + amountToShift2);
           compactBitsRepresentation = (uint)(int)(BigInteger.Abs(num) << (int)amountToShift2);  // HACK: -- ABS MAY NOT BE THE CORRECT THING TO USE HERE
           if (debug) Console.WriteLine(GetBits(compactBitsRepresentation) + " was shifted " + amountToShift2);
       }
       else
       {
           BigInteger bn = num;
           uint amountToShift2 = 8 * (size2 - 3);
           if (debug) Console.WriteLine(GetBits(bn) + " will be shifted " + amountToShift2);
           var bnShifted = BigInteger.Abs(bn) >> (int)amountToShift2;  // HACK: -- ABS MAY NOT BE THE CORRECT THING TO USE HERE
           compactBitsRepresentation = (uint)bnShifted;
       }

       // The 0x00800000 bit denotes the sign.
       // Thus, if it is already set, divide the mantissa by 256 and increase the exponent.
       Console.WriteLine(compactBitsRepresentation.ToString("x"));
       if ((compactBitsRepresentation & 0x00800000) != 0)
       {
           compactBitsRepresentation >>= 8;
           size2++;
       }
       if (debug) Console.WriteLine(GetBits(size2) + " size ");

       var tmp = size2 << 24;
       if (debug) Console.WriteLine(GetBits(tmp) + " size (shifted to proper postion)");
       compactBitsRepresentation |= size2 << 24;
       if (debug) Console.WriteLine("21 987654321 987654321 987654321");
       if (debug) Console.WriteLine(GetBits(compactBitsRepresentation) + " size # then compact");

       compactBitsRepresentation |= (num.Sign < 0 ? (uint)0x00800000 : 0);

       if (compactBitsRepresentation == 0)
           return "0";
       return "0x" + compactBitsRepresentation.ToString("x8");
   }


     internal static System.Numerics.BigInteger SetCompact(uint numToCompact)
    {
        if (debug)   Console.WriteLine(GetBits(numToCompact) + " This number will be compacted ");

        //
        //  SetCompact
        // Extract the number from bits 0..23
        if (debug)  Console.WriteLine(GetBits(0x007fffff) + " Bitmask & (extract 0..23) ");

        uint nWord = numToCompact & 0x007fffff;
        if (debug)  Console.WriteLine(GetBits(nWord) + " result ");


        BigInteger ret = new BigInteger(nWord);

        // Add zeroes to the left according to bits 25..32
        var ttt =  ret.ToByteArray();

        uint size = numToCompact >> 24;
        if (debug)  Console.WriteLine(GetBits(size) + " Read bytes 25..32 (>> 24) ");


        uint amountToShift = 0;
        if (size <= 3)
        {
            amountToShift = 8 * (3 - size);
            if (debug)  Console.WriteLine(GetBits(ret) + " preshifted " + amountToShift);

            ret = ret >> (int)amountToShift;
            if (debug) Console.WriteLine( GetBits(ret)+ " postshifted " + amountToShift );
        }
        else
        {
            amountToShift = 8 * (size - 3);
            if (debug) Console.WriteLine(GetBits(ret) + " preshifted " + amountToShift);

            ret = ret << (int)amountToShift;

            if (debug)   Console.WriteLine(GetBits(ret) + " shifted " + amountToShift);
        }

        // Set the value negative if required per bit 24
        if (debug) Console.WriteLine(GetBits(0x00800000) + " ... check bit is neg");

        UInt32 isNegative = 0x00800000 & numToCompact;

        if (debug)  Console.WriteLine(GetBits(isNegative) + " ... Result");

        if (isNegative != 0)
            ret = ret * -1;  

        var test = ret.ToByteArray();
        if (debug) Console.WriteLine(ret + " return");
        if (debug) Console.WriteLine();
        return ret;
    }

    internal static string GetHex(BigInteger bb)
    {
        if (bb == 0)
            return "0";
        else 
        return bb.ToSignedHexString().TrimStart("0".ToCharArray());
    }

    public static string GetBits(BigInteger num)
    {
        return GetBits(num.ToByteArray());

    }
    public static string GetBits(int num)
    {
        return GetBits(BitConverter.GetBytes(num));
    }
    public static string GetBits(uint num)
    {
       return  GetBits(BitConverter.GetBytes(num));
    }
    public static string GetBits(byte[] bytes)
    {
        StringBuilder sb = new StringBuilder();


        int bitPos = (8 * bytes.Length) -1;
        while (bitPos >  -1)
        {
            int byteIndex = bitPos / 8;
            int offset = bitPos % 8;
            bool isSet = (bytes[byteIndex] & (1 << offset)) != 0;

            // isSet = [True] if the bit at bitPos is set, false otherwise
            if (isSet)
                sb.Append("1");
            else
                sb.Append("0");
            bitPos--;
        }


        return sb.ToString();
    }

}
Können Sie einen fehlgeschlagenen Testfall posten?
@NickODell Viele Tests schlagen fehl ... Ich kann nicht herausfinden, warum
Vielleicht OpenSSL bitshifting a number != .NETs Implementierung der Bitverschiebung derselben Zahl
Vielleicht ein Endianness-Problem. Ich weiß nicht, welche Endianness Bitcoin verwendet, aber Sie verwenden native Endianness. Die Verwendung von nativer Endianness in einem plattformunabhängigen Dateiformat ist immer ein Fehler, selbst wenn es mit Ihrer aktuellen Plattform funktionieren könnte. Bearbeiten: Oh, es ist einer der großartigen Dokumentationsfälle von MS, der zu einem Widerspruch für Big-Endian-Architekturen führt.

Antworten (1)

Hier ist eine Sache, bei der ich mir ziemlich sicher bin, dass es sich um einen Fehler in Ihrer Methode compact to BigInteger handelt:

        ret = ret << (int)amountToShift;
        amountToShift = 8 * (size - 3);

Sollte sein:

        amountToShift = 8 * (size - 3);
        ret = ret << (int)amountToShift;

BEARBEITEN

Gefunden eine andere, in Ihrer BigInteger to compact-Methode:

        ret = ret << (int)amountToShift2;

Hier ist die Sache: ret wird nirgendwo anders verwendet. Ich bin mir ziemlich sicher, dass Sie ret2 zuweisen und in eine Ganzzahl ohne Vorzeichen umwandeln wollten. Denken Sie daran, beide Instanzen davon zu beheben.

Lassen Sie mich wissen, ob diese beiden Vorschläge die Dinge nicht beheben.

Hmm .NET hat einen Fehler in ToString("x") ... das hat mich auch aus der Fassung gebracht