Skip to content
Permalink
Browse files

bech32 to address support

  • Loading branch information...
voisine committed Mar 12, 2018
1 parent 10c78c4 commit fd0abb92b07e41429e1170fb56716965cc7b64ab
Showing with 97 additions and 40 deletions.
  1. +60 −21 BRAddress.c
  2. +8 −2 BRAddress.h
  3. +2 −2 BRTransaction.h
  4. +0 −2 bcash/BRBCashAddr.c
  5. +27 −13 test.c
@@ -24,6 +24,7 @@

#include "BRAddress.h"
#include "BRBase58.h"
#include "BRBech32.h"
#include "BRInt.h"
#include <inttypes.h>
#include <assert.h>
@@ -241,39 +242,51 @@ size_t BRAddressFromScriptPubKey(char *addr, size_t addrLen, const uint8_t *scri
if (! script || scriptLen == 0 || scriptLen > MAX_SCRIPT_LENGTH) return 0;

uint8_t data[21];
const uint8_t *elems[BRScriptElements(NULL, 0, script, scriptLen)], *d = NULL;
size_t count = BRScriptElements(elems, sizeof(elems)/sizeof(*elems), script, scriptLen), l = 0;

data[0] = BITCOIN_PUBKEY_ADDRESS;
#if BITCOIN_TESTNET
data[0] = BITCOIN_PUBKEY_ADDRESS_TEST;
#endif
const uint8_t *d, *elems[BRScriptElements(NULL, 0, script, scriptLen)];
char a[91];
size_t r = 0, l = 0, count = BRScriptElements(elems, sizeof(elems)/sizeof(*elems), script, scriptLen);

if (count == 5 && *elems[0] == OP_DUP && *elems[1] == OP_HASH160 && *elems[2] == 20 &&
*elems[3] == OP_EQUALVERIFY && *elems[4] == OP_CHECKSIG) {
// pay-to-pubkey-hash scriptPubKey
d = BRScriptData(elems[2], &l);
if (l != 20) d = NULL;
if (d) memcpy(&data[1], d, 20);
data[0] = BITCOIN_PUBKEY_ADDRESS;
#if BITCOIN_TESTNET
data[0] = BITCOIN_PUBKEY_ADDRESS_TEST;
#endif
memcpy(&data[1], BRScriptData(elems[2], &l), 20);
r = BRBase58CheckEncode(addr, addrLen, data, 21);
}
else if (count == 3 && *elems[0] == OP_HASH160 && *elems[1] == 20 && *elems[2] == OP_EQUAL) {
// pay-to-script-hash scriptPubKey
data[0] = BITCOIN_SCRIPT_ADDRESS;
#if BITCOIN_TESTNET
data[0] = BITCOIN_SCRIPT_ADDRESS_TEST;
#endif
d = BRScriptData(elems[1], &l);
if (l != 20) d = NULL;
if (d) memcpy(&data[1], d, 20);
memcpy(&data[1], BRScriptData(elems[1], &l), 20);
r = BRBase58CheckEncode(addr, addrLen, data, 21);
}
else if (count == 2 && (*elems[0] == 65 || *elems[0] == 33) && *elems[1] == OP_CHECKSIG) {
// pay-to-pubkey scriptPubKey
data[0] = BITCOIN_PUBKEY_ADDRESS;
#if BITCOIN_TESTNET
data[0] = BITCOIN_PUBKEY_ADDRESS_TEST;
#endif
d = BRScriptData(elems[0], &l);
if (l != 65 && l != 33) d = NULL;
if (d) BRHash160(&data[1], d, l);
BRHash160(&data[1], d, l);
r = BRBase58CheckEncode(addr, addrLen, data, 21);
}
else if (count == 2 && ((*elems[0] == OP_0 && (*elems[1] == 20 || *elems[1] == 32)) ||
(*elems[0] >= OP_1 && *elems[0] <= OP_16 && *elems[1] >= 2 && *elems[1] <= 40))) {
// pay-to-witness scriptPubKey
r = BRBech32Encode(a, "bc", script);
#if BITCOIN_TESTNET
r = BRBech32Encode(a, "tb", script);
#endif
if (addr && r > addrLen) r = 0;
if (addr) memcpy(addr, a, r);
}

return (d) ? BRBase58CheckEncode(addr, addrLen, data, sizeof(data)) : 0;
return r;
}

// writes the bitcoin address for a scriptSig to addr
@@ -310,23 +323,33 @@ size_t BRAddressFromScriptSig(char *addr, size_t addrLen, const uint8_t *script,
else if (count >= 1 && *elems[count - 1] <= OP_PUSHDATA4 && *elems[count - 1] > 0) { // pay-to-pubkey scriptSig
// TODO: implement Peter Wullie's pubKey recovery from signature
}
// pay-to-witness scriptSig's are empty

return (d) ? BRBase58CheckEncode(addr, addrLen, data, sizeof(data)) : 0;
return (d) ? BRBase58CheckEncode(addr, addrLen, data, 21) : 0;
}

// writes the bitcoin address for a witness to addr
// returns the number of bytes written, or addrLen needed if addr is NULL
size_t BRAddressFromWitness(char *addr, size_t addrLen, const uint8_t *witness, size_t witLen)
{
return 0; // TODO: XXX implement
}

// writes the scriptPubKey for addr to script
// returns the number of bytes written, or scriptLen needed if script is NULL
size_t BRAddressScriptPubKey(uint8_t *script, size_t scriptLen, const char *addr)
{
static uint8_t pubkeyAddress = BITCOIN_PUBKEY_ADDRESS, scriptAddress = BITCOIN_SCRIPT_ADDRESS;
uint8_t data[21];
size_t r = 0;
uint8_t pubkeyAddress = BITCOIN_PUBKEY_ADDRESS, scriptAddress = BITCOIN_SCRIPT_ADDRESS;
uint8_t data[42];
char hrp[84], bech32Prefix[] = "bc";
size_t dataLen, r = 0;

assert(addr != NULL);

#if BITCOIN_TESTNET
pubkeyAddress = BITCOIN_PUBKEY_ADDRESS_TEST;
scriptAddress = BITCOIN_SCRIPT_ADDRESS_TEST;
bech32Prefix = "tb";
#endif

if (BRBase58CheckDecode(data, sizeof(data), addr) == 21) {
@@ -353,14 +376,23 @@ size_t BRAddressScriptPubKey(uint8_t *script, size_t scriptLen, const char *addr
r = (! script || 23 <= scriptLen) ? 23 : 0;
}
}
else {
dataLen = BRBech32Decode(hrp, data, addr);

if (dataLen > 2 && strcmp(hrp, bech32Prefix) == 0 && (data[0] != OP_0 || data[1] == 20 || data[1] == 32)) {
if (script && dataLen <= scriptLen) memcpy(script, data, dataLen);
r = (! script || dataLen <= scriptLen) ? dataLen : 0;
}
}

return r;
}

// returns true if addr is a valid bitcoin address
int BRAddressIsValid(const char *addr)
{
uint8_t data[21];
uint8_t data[42];
char hrp[84];
int r = 0;

assert(addr != NULL);
@@ -372,6 +404,13 @@ int BRAddressIsValid(const char *addr)
r = (data[0] == BITCOIN_PUBKEY_ADDRESS_TEST || data[0] == BITCOIN_SCRIPT_ADDRESS_TEST);
#endif
}
else if (BRBech32Decode(hrp, data, addr) > 2) {
r = (strcmp(hrp, "bc") == 0 && (data[0] != OP_0 || data[1] == 20 || data[1] == 32));

#if BITCOIN_TESTNET
r = (strcmp(hrp, "tb") == 0 && (data[0] != OP_0 || data[1] == 20 || data[1] == 32));
#endif
}

return r;
}
@@ -51,6 +51,7 @@ extern "C" {
#define OP_PUSHDATA4 0x4e
#define OP_1NEGATE 0x4f
#define OP_1 0x51
#define OP_16 0x60
#define OP_DUP 0x76
#define OP_EQUAL 0x87
#define OP_EQUALVERIFY 0x88
@@ -79,10 +80,11 @@ const uint8_t *BRScriptData(const uint8_t *elem, size_t *dataLen);
size_t BRScriptPushData(uint8_t *script, size_t scriptLen, const uint8_t *data, size_t dataLen);

typedef struct {
char s[36];
char s[75];
} BRAddress;

#define BR_ADDRESS_NONE ((BRAddress) { "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" })
#define BR_ADDRESS_NONE ((BRAddress) { "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"\
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" })

// writes the bitcoin address for a scriptPubKey to addr
// returns the number of bytes written, or addrLen needed if addr is NULL
@@ -92,6 +94,10 @@ size_t BRAddressFromScriptPubKey(char *addr, size_t addrLen, const uint8_t *scri
// returns the number of bytes written, or addrLen needed if addr is NULL
size_t BRAddressFromScriptSig(char *addr, size_t addrLen, const uint8_t *script, size_t scriptLen);

// writes the bitcoin address for a witness to addr
// returns the number of bytes written, or addrLen needed if addr is NULL
size_t BRAddressFromWitness(char *addr, size_t addrLen, const uint8_t *witness, size_t witLen);

// writes the scriptPubKey for addr to script
// returns the number of bytes written, or scriptLen needed if script is NULL
size_t BRAddressScriptPubKey(uint8_t *script, size_t scriptLen, const char *addr);
@@ -57,7 +57,7 @@ uint32_t BRRand(uint32_t upperBound);
typedef struct {
UInt256 txHash;
uint32_t index;
char address[36];
char address[75];
uint64_t amount;
uint8_t *script;
size_t scriptLen;
@@ -71,7 +71,7 @@ void BRTxInputSetScript(BRTxInput *input, const uint8_t *script, size_t scriptLe
void BRTxInputSetSignature(BRTxInput *input, const uint8_t *signature, size_t sigLen);

typedef struct {
char address[36];
char address[75];
uint64_t amount;
uint8_t *script;
size_t scriptLen;
@@ -25,8 +25,6 @@

#include "bcash/BRBCashAddr.h"
//#include "BRAddress.h" // WTF?! header guards not working? some kind of compiler bug?
#define OP_0 0x00
#define OP_1 0x51
#define BITCOIN_PUBKEY_ADDRESS 0
#define BITCOIN_SCRIPT_ADDRESS 5
#define BITCOIN_PUBKEY_ADDRESS_TEST 111
40 test.c
@@ -1404,17 +1404,29 @@ int BRAddressTests()

BRKeySetSecret(&k, &secret, 1);
if (! BRKeyAddress(&k, addr.s, sizeof(addr)))
r = 0, fprintf(stderr, "***FAILED*** %s: BRKeyAddress()\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRKeyAddress()", __func__);

uint8_t script[BRAddressScriptPubKey(NULL, 0, addr.s)];
size_t scriptLen = BRAddressScriptPubKey(script, sizeof(script), addr.s);

BRAddressFromScriptPubKey(addr2.s, sizeof(addr2), script, scriptLen);
if (! BRAddressEq(&addr, &addr2))
r = 0, fprintf(stderr, "***FAILED*** %s: BRAddressFromScriptPubKey()\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRAddressFromScriptPubKey() test 1", __func__);

// TODO: test BRAddressFromScriptSig()

BRAddress addr3;
char script2[] = "\0\x14\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
if (! BRAddressFromScriptPubKey(addr3.s, sizeof(addr3), (uint8_t *)script2, sizeof(script2)))
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRAddressFromScriptPubKey() test 2", __func__);

uint8_t script3[BRAddressScriptPubKey(NULL, 0, addr3.s)];
size_t script3Len = BRAddressScriptPubKey(script3, sizeof(script3), addr3.s);

if (script3Len != sizeof(script2) || memcmp(script2, script3, sizeof(script2)))
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRAddressScriptPubKey() test", __func__);

if (! r) fprintf(stderr, "\n ");
return r;
}

@@ -1695,18 +1707,18 @@ int BRTransactionTests()
uint8_t buf[BRTransactionSerialize(tx, NULL, 0)]; // test serializing/parsing unsigned tx
size_t len = BRTransactionSerialize(tx, buf, sizeof(buf));

if (len == 0) r = 0, fprintf(stderr, "***FAILED*** %s: BRTransactionSerialize() test 0\n", __func__);
if (len == 0) r = 0, fprintf(stderr, "\n***FAILED*** %s: BRTransactionSerialize() test 0", __func__);
BRTransactionFree(tx);
tx = BRTransactionParse(buf, len);

if (! tx || tx->inCount != 1 || tx->outCount != 2)
r = 0, fprintf(stderr, "***FAILED*** %s: BRTransactionParse() test 0\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRTransactionParse() test 0", __func__);
if (! tx) return r;

BRTransactionSign(tx, 0, k, 2);
BRAddressFromScriptSig(addr.s, sizeof(addr), tx->inputs[0].signature, tx->inputs[0].sigLen);
if (! BRTransactionIsSigned(tx) || ! BRAddressEq(&address, &addr))
r = 0, fprintf(stderr, "***FAILED*** %s: BRTransactionSign() test 1\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRTransactionSign() test 1", __func__);

uint8_t buf2[BRTransactionSerialize(tx, NULL, 0)];
size_t len2 = BRTransactionSerialize(tx, buf2, sizeof(buf2));
@@ -1715,14 +1727,14 @@ int BRTransactionTests()
tx = BRTransactionParse(buf2, len2);

if (! tx || ! BRTransactionIsSigned(tx))
r = 0, fprintf(stderr, "***FAILED*** %s: BRTransactionParse() test 1\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRTransactionParse() test 1", __func__);
if (! tx) return r;

uint8_t buf3[BRTransactionSerialize(tx, NULL, 0)];
size_t len3 = BRTransactionSerialize(tx, buf3, sizeof(buf3));

if (len2 != len3 || memcmp(buf2, buf3, len2) != 0)
r = 0, fprintf(stderr, "***FAILED*** %s: BRTransactionSerialize() test 1\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRTransactionSerialize() test 1", __func__);
BRTransactionFree(tx);

tx = BRTransactionNew();
@@ -1750,22 +1762,22 @@ int BRTransactionTests()
BRAddressFromScriptSig(addr.s, sizeof(addr), tx->inputs[tx->inCount - 1].signature,
tx->inputs[tx->inCount - 1].sigLen);
if (! BRTransactionIsSigned(tx) || ! BRAddressEq(&address, &addr))
r = 0, fprintf(stderr, "***FAILED*** %s: BRTransactionSign() test 2\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRTransactionSign() test 2", __func__);

uint8_t buf4[BRTransactionSerialize(tx, NULL, 0)];
size_t len4 = BRTransactionSerialize(tx, buf4, sizeof(buf4));

BRTransactionFree(tx);
tx = BRTransactionParse(buf4, len4);
if (! tx || ! BRTransactionIsSigned(tx))
r = 0, fprintf(stderr, "***FAILED*** %s: BRTransactionParse() test 2\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRTransactionParse() test 2", __func__);
if (! tx) return r;

uint8_t buf5[BRTransactionSerialize(tx, NULL, 0)];
size_t len5 = BRTransactionSerialize(tx, buf5, sizeof(buf5));

if (len4 != len5 || memcmp(buf4, buf5, len4) != 0)
r = 0, fprintf(stderr, "***FAILED*** %s: BRTransactionSerialize() test 2\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRTransactionSerialize() test 2", __func__);
BRTransactionFree(tx);

BRTransaction *src = BRTransactionNew ();
@@ -1777,21 +1789,23 @@ int BRTransactionTests()

BRTransaction *tgt = BRTransactionCopy(src);
if (!BRTransactionEqual(tgt, src))
r = 0, fprintf(stderr, "***FAILED*** %s: BRTransactionCopy() test 1\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRTransactionCopy() test 1", __func__);

tgt->blockHeight++;
if (BRTransactionEqual(tgt, src)) // fail if equal
r = 0, fprintf(stderr, "***FAILED*** %s: BRTransactionCopy() test 2\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRTransactionCopy() test 2", __func__);

BRTransactionFree(tgt);
BRTransactionFree(src);

src = BRTransactionParse(buf4, len4);
tgt = BRTransactionCopy(src);
if (!BRTransactionEqual(tgt, src))
r = 0, fprintf(stderr, "***FAILED*** %s: BRTransactionCopy() test 3\n", __func__);
r = 0, fprintf(stderr, "\n***FAILED*** %s: BRTransactionCopy() test 3", __func__);
BRTransactionFree(tgt);
BRTransactionFree(src);

if (! r) fprintf(stderr, "\n ");
return r;
}

0 comments on commit fd0abb9

Please sign in to comment.
You can’t perform that action at this time.