Skip to content

Commit

Permalink
added BTC256 for holding hashes in fixed-length buffers
Browse files Browse the repository at this point in the history
  • Loading branch information
oleganza committed Mar 15, 2014
1 parent e61ee2c commit 142833f
Show file tree
Hide file tree
Showing 8 changed files with 732 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CoreBitcoin/BTC256+Tests.h
@@ -0,0 +1,3 @@
// CoreBitcoin by Oleg Andreev <oleganza@gmail.com>, WTFPL.

void BTC256RunAllTests();
179 changes: 179 additions & 0 deletions CoreBitcoin/BTC256+Tests.m
@@ -0,0 +1,179 @@
// CoreBitcoin by Oleg Andreev <oleganza@gmail.com>, WTFPL.

#import "BTC256+Tests.h"
#import "BTC256.h"
#import "BTCData.h"

void BTC256TestChunkSize()
{
NSCAssert(sizeof(BTC160) == 20, @"160-bit struct should by 160 bit long");
NSCAssert(sizeof(BTC256) == 32, @"256-bit struct should by 256 bit long");
NSCAssert(sizeof(BTC512) == 64, @"512-bit struct should by 512 bit long");
}

void BTC256TestNull()
{
NSCAssert([NSStringFromBTC160(BTC160Null) isEqualTo:@"82963d5edd842f1e6bd2b6bc2e9a97a40a7d8652"], @"null hash should be correct");
NSCAssert([NSStringFromBTC256(BTC256Null) isEqualTo:@"d1007a1fe826e95409e21595845f44c3b9411d5285b6b5982285aabfa5999a5e"], @"null hash should be correct");
NSCAssert([NSStringFromBTC512(BTC512Null) isEqualTo:@"62ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f0363e01b5d7a53c4a2e5a76d283f3e4a04d28ab54849c6e3e874ca31128bcb759e1"], @"null hash should be correct");
}

void BTC256TestOne()
{
BTC256 one = BTC256Zero;
one.words64[0] = 1;
NSCAssert([NSStringFromBTC256(one) isEqual:@"0100000000000000000000000000000000000000000000000000000000000000"], @"");
}

void BTC256TestEqual()
{
NSCAssert(BTC256Equal(BTC256Null, BTC256Null), @"equal");
NSCAssert(BTC256Equal(BTC256Zero, BTC256Zero), @"equal");
NSCAssert(BTC256Equal(BTC256Max, BTC256Max), @"equal");

NSCAssert(!BTC256Equal(BTC256Zero, BTC256Null), @"not equal");
NSCAssert(!BTC256Equal(BTC256Zero, BTC256Max), @"not equal");
NSCAssert(!BTC256Equal(BTC256Max, BTC256Null), @"not equal");
}

void BTC256TestCompare()
{
NSCAssert(BTC256Compare(BTC256FromNSString(@"62ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f036"),
BTC256FromNSString(@"62ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f036")) == NSOrderedSame, @"ordered same");

NSCAssert(BTC256Compare(BTC256FromNSString(@"62ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f035"),
BTC256FromNSString(@"62ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f036")) == NSOrderedAscending, @"ordered asc");

NSCAssert(BTC256Compare(BTC256FromNSString(@"62ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f037"),
BTC256FromNSString(@"62ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f036")) == NSOrderedDescending, @"ordered asc");

NSCAssert(BTC256Compare(BTC256FromNSString(@"61ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f036"),
BTC256FromNSString(@"62ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f036")) == NSOrderedAscending, @"ordered same");

NSCAssert(BTC256Compare(BTC256FromNSString(@"62ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f036"),
BTC256FromNSString(@"61ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f036")) == NSOrderedDescending, @"ordered same");

}

void BTC256TestInverse()
{
BTC256 chunk = BTC256FromNSString(@"62ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f036");
BTC256 chunk2 = BTC256Inverse(chunk);

NSCAssert(!BTC256Equal(chunk, chunk2), @"not equal");
NSCAssert(BTC256Equal(chunk, BTC256Inverse(chunk2)), @"equal");

NSCAssert(chunk2.words64[0] == ~chunk.words64[0], @"bytes are inversed");
NSCAssert(chunk2.words64[1] == ~chunk.words64[1], @"bytes are inversed");
NSCAssert(chunk2.words64[2] == ~chunk.words64[2], @"bytes are inversed");
NSCAssert(chunk2.words64[3] == ~chunk.words64[3], @"bytes are inversed");

NSCAssert(BTC256Equal(BTC256Zero, BTC256AND(chunk, chunk2)), @"(a & ~a) == 000000...");
NSCAssert(BTC256Equal(BTC256Max, BTC256OR(chunk, chunk2)), @"(a | ~a) == 111111...");
NSCAssert(BTC256Equal(BTC256Max, BTC256XOR(chunk, chunk2)), @"(a ^ ~a) == 111111...");
}

void BTC256TestSwap()
{
BTC256 chunk = BTC256FromNSString(@"62ce64dd92836e6e99d83eee3f623652f6049cf8c22272f295b262861738f036");
BTC256 chunk2 = BTC256Swap(chunk);
NSCAssert([BTCReversedData(NSDataFromBTC256(chunk)) isEqual:NSDataFromBTC256(chunk2)], @"swap should reverse all bytes");

NSCAssert(chunk2.words64[0] == OSSwapConstInt64(chunk.words64[3]), @"swap should reverse all bytes");
NSCAssert(chunk2.words64[1] == OSSwapConstInt64(chunk.words64[2]), @"swap should reverse all bytes");
NSCAssert(chunk2.words64[2] == OSSwapConstInt64(chunk.words64[1]), @"swap should reverse all bytes");
NSCAssert(chunk2.words64[3] == OSSwapConstInt64(chunk.words64[0]), @"swap should reverse all bytes");
}

void BTC256TestAND()
{
NSCAssert(BTC256Equal(BTC256AND(BTC256Max, BTC256Max), BTC256Max), @"1 & 1 == 1");
NSCAssert(BTC256Equal(BTC256AND(BTC256Max, BTC256Zero), BTC256Zero), @"1 & 0 == 0");
NSCAssert(BTC256Equal(BTC256AND(BTC256Zero, BTC256Max), BTC256Zero), @"0 & 1 == 0");
NSCAssert(BTC256Equal(BTC256AND(BTC256Zero, BTC256Null), BTC256Zero), @"0 & x == 0");
NSCAssert(BTC256Equal(BTC256AND(BTC256Null, BTC256Zero), BTC256Zero), @"x & 0 == 0");
NSCAssert(BTC256Equal(BTC256AND(BTC256Max, BTC256Null), BTC256Null), @"1 & x == x");
NSCAssert(BTC256Equal(BTC256AND(BTC256Null, BTC256Max), BTC256Null), @"x & 1 == x");
}

void BTC256TestOR()
{
NSCAssert(BTC256Equal(BTC256OR(BTC256Max, BTC256Max), BTC256Max), @"1 | 1 == 1");
NSCAssert(BTC256Equal(BTC256OR(BTC256Max, BTC256Zero), BTC256Max), @"1 | 0 == 1");
NSCAssert(BTC256Equal(BTC256OR(BTC256Zero, BTC256Max), BTC256Max), @"0 | 1 == 1");
NSCAssert(BTC256Equal(BTC256OR(BTC256Zero, BTC256Null), BTC256Null), @"0 | x == x");
NSCAssert(BTC256Equal(BTC256OR(BTC256Null, BTC256Zero), BTC256Null), @"x | 0 == x");
NSCAssert(BTC256Equal(BTC256OR(BTC256Max, BTC256Null), BTC256Max), @"1 | x == 1");
NSCAssert(BTC256Equal(BTC256OR(BTC256Null, BTC256Max), BTC256Max), @"x | 1 == 1");
}

void BTC256TestXOR()
{
NSCAssert(BTC256Equal(BTC256XOR(BTC256Max, BTC256Max), BTC256Zero), @"1 ^ 1 == 0");
NSCAssert(BTC256Equal(BTC256XOR(BTC256Max, BTC256Zero), BTC256Max), @"1 ^ 0 == 1");
NSCAssert(BTC256Equal(BTC256XOR(BTC256Zero, BTC256Max), BTC256Max), @"0 ^ 1 == 1");
NSCAssert(BTC256Equal(BTC256XOR(BTC256Zero, BTC256Null), BTC256Null), @"0 ^ x == x");
NSCAssert(BTC256Equal(BTC256XOR(BTC256Null, BTC256Zero), BTC256Null), @"x ^ 0 == x");
NSCAssert(BTC256Equal(BTC256XOR(BTC256Max, BTC256Null), BTC256Inverse(BTC256Null)), @"1 ^ x == ~x");
NSCAssert(BTC256Equal(BTC256XOR(BTC256Null, BTC256Max), BTC256Inverse(BTC256Null)), @"x ^ 1 == ~x");
}

void BTC256TestConcat()
{
BTC512 concat = BTC512Concat(BTC256Null, BTC256Max);
NSCAssert([NSStringFromBTC512(concat) isEqualTo:@"d1007a1fe826e95409e21595845f44c3b9411d5285b6b5982285aabfa5999a5e"
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"], @"should concatenate properly");

concat = BTC512Concat(BTC256Max, BTC256Null);
NSCAssert([NSStringFromBTC512(concat) isEqualTo:@"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
"d1007a1fe826e95409e21595845f44c3b9411d5285b6b5982285aabfa5999a5e"], @"should concatenate properly");

}

void BTC256TestConvertToData()
{
// TODO...
}

void BTC256TestConvertToString()
{
// Too short string should yield null value.
BTC256 chunk = BTC256FromNSString(@"000095409e215952"
"85b6b5982285aabf"
"a5999a5e845f44c3"
"b9411d5d1007a1");
NSCAssert(BTC256Equal(chunk, BTC256Null), @"too short string => null");

chunk = BTC256FromNSString(@"000095409e215952"
"85b6b5982285aabf"
"a5999a5e845f44c3"
"b9411d5d1007a1b166");
NSCAssert(chunk.words64[0] == OSSwapBigToHostConstInt64(0x000095409e215952), @"parse correctly");
NSCAssert(chunk.words64[1] == OSSwapBigToHostConstInt64(0x85b6b5982285aabf), @"parse correctly");
NSCAssert(chunk.words64[2] == OSSwapBigToHostConstInt64(0xa5999a5e845f44c3), @"parse correctly");
NSCAssert(chunk.words64[3] == OSSwapBigToHostConstInt64(0xb9411d5d1007a1b1), @"parse correctly");

NSCAssert([NSStringFromBTC256(chunk) isEqualTo:@"000095409e215952"
"85b6b5982285aabf"
"a5999a5e845f44c3"
"b9411d5d1007a1b1"], @"should serialize to the same string");
}


void BTC256RunAllTests()
{
BTC256TestChunkSize();
BTC256TestNull();
BTC256TestOne();
BTC256TestEqual();
BTC256TestCompare();
BTC256TestInverse();
BTC256TestSwap();
BTC256TestAND();
BTC256TestOR();
BTC256TestXOR();
BTC256TestConcat();
BTC256TestConvertToData();
BTC256TestConvertToString();
}

141 changes: 141 additions & 0 deletions CoreBitcoin/BTC256.h
@@ -0,0 +1,141 @@
// CoreBitcoin by Oleg Andreev <oleganza@gmail.com>, WTFPL.

#import <Foundation/Foundation.h>

// A set of ubiquitous types and functions to deal with fixed-length chunks of data
// (160-bit, 256-bit and 512-bit). These are relevant almost always to hashes,
// but there's no hash-specific about them.
// The purpose of these is to avoid dynamic memory allocations via NSData when
// we need to move exactly 32 bytes around.
//
// We don't call these BTCFixedData256 because these types are way too ubiquituous
// in CoreBitcoin to have such an explicit name.
//
// Somewhat similar to uint256 in bitcoind, but here we don't try
// to pretend that these are integers and then allow arithmetic on them
// and create a mess with the byte order.
// Use BTCBigNumber to do arithmetic on big numbers and convert
// to bignum format explicitly.
// BTCBigNumber has API for converting BTC256 to a big int.
//
// We also declare BTC160 and BTC512 for use with RIPEMD-160, SHA-1 and SHA-512 hashes.


// 1. Fixed-length types

struct private_BTC160
{
// 160 bits can't be formed with 64-bit words, so we have to use 32-bit ones instead.
uint32_t words32[5];
} __attribute__((packed));
typedef struct private_BTC160 BTC160;

struct private_BTC256
{
// Since all modern CPUs are 64-bit (ARM is 64-bit starting with iPhone 5s),
// we will use 64-bit words.
uint64_t words64[4];
} __attribute__((aligned(1)));
typedef struct private_BTC256 BTC256;

struct private_BTC512
{
// Since all modern CPUs are 64-bit (ARM is 64-bit starting with iPhone 5s),
// we will use 64-bit words.
uint64_t words64[8];
} __attribute__((aligned(1)));
typedef struct private_BTC512 BTC512;


// 2. Constants

// All-zero constants
extern const BTC160 BTC160Zero;
extern const BTC256 BTC256Zero;
extern const BTC512 BTC512Zero;

// All-one constants
extern const BTC160 BTC160Max;
extern const BTC256 BTC256Max;
extern const BTC512 BTC512Max;

// First 160 bits of SHA512("CoreBitcoin/BTC160Null")
extern const BTC160 BTC160Null;

// First 256 bits of SHA512("CoreBitcoin/BTC256Null")
extern const BTC256 BTC256Null;

// Value of SHA512("CoreBitcoin/BTC512Null")
extern const BTC512 BTC512Null;


// 3. Comparison

BOOL BTC160Equal(BTC160 chunk1, BTC160 chunk2);
BOOL BTC256Equal(BTC256 chunk1, BTC256 chunk2);
BOOL BTC512Equal(BTC512 chunk1, BTC512 chunk2);

NSComparisonResult BTC160Compare(BTC160 chunk1, BTC160 chunk2);
NSComparisonResult BTC256Compare(BTC256 chunk1, BTC256 chunk2);
NSComparisonResult BTC512Compare(BTC512 chunk1, BTC512 chunk2);


// 4. Operations


// Inverse (b = ~a)
BTC160 BTC160Inverse(BTC160 chunk);
BTC256 BTC256Inverse(BTC256 chunk);
BTC512 BTC512Inverse(BTC512 chunk);

// Swap byte order
BTC160 BTC160Swap(BTC160 chunk);
BTC256 BTC256Swap(BTC256 chunk);
BTC512 BTC512Swap(BTC512 chunk);

// Bitwise AND operation (a & b)
BTC160 BTC160AND(BTC160 chunk1, BTC160 chunk2);
BTC256 BTC256AND(BTC256 chunk1, BTC256 chunk2);
BTC512 BTC512AND(BTC512 chunk1, BTC512 chunk2);

// Bitwise OR operation (a | b)
BTC160 BTC160OR(BTC160 chunk1, BTC160 chunk2);
BTC256 BTC256OR(BTC256 chunk1, BTC256 chunk2);
BTC512 BTC512OR(BTC512 chunk1, BTC512 chunk2);

// Bitwise exclusive-OR operation (a ^ b)
BTC160 BTC160XOR(BTC160 chunk1, BTC160 chunk2);
BTC256 BTC256XOR(BTC256 chunk1, BTC256 chunk2);
BTC512 BTC512XOR(BTC512 chunk1, BTC512 chunk2);

// Concatenation of two 256-bit chunks
BTC512 BTC512Concat(BTC256 chunk1, BTC256 chunk2);


// 5. Conversion functions


// Conversion to NSData
NSData* NSDataFromBTC160(BTC160 chunk);
NSData* NSDataFromBTC256(BTC256 chunk);
NSData* NSDataFromBTC512(BTC512 chunk);

// Conversion from NSData.
// If NSData is not big enough, returns BTCHash{160,256,512}Null.
BTC160 BTC160FromNSData(NSData* data);
BTC256 BTC256FromNSData(NSData* data);
BTC512 BTC512FromNSData(NSData* data);

// Returns lowercase hex representation of the chunk
NSString* NSStringFromBTC160(BTC160 chunk);
NSString* NSStringFromBTC256(BTC256 chunk);
NSString* NSStringFromBTC512(BTC512 chunk);

// Conversion from hex NSString (lower- or uppercase).
// If string is invalid or data is too short, returns BTCHash{160,256,512}Null.
BTC160 BTC160FromNSString(NSString* string);
BTC256 BTC256FromNSString(NSString* string);
BTC512 BTC512FromNSString(NSString* string);



0 comments on commit 142833f

Please sign in to comment.