UPDATE: Der Code in dieser Frage funktioniert für alle Testfälle (Hurra!), aber ich mag nicht, wie GetCompact
die Math.Abs
Funktion 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();
}
}
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;
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.
Nick Odell
Macher7
Macher7
CodesInChaos