Skip to content

Commit

Permalink
address: remove witness prefixes.
Browse files Browse the repository at this point in the history
  • Loading branch information
nodech authored and pinheadmz committed Nov 8, 2021
1 parent ab8cf1a commit f74e507
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 130 deletions.
74 changes: 24 additions & 50 deletions lib/primitives/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,30 @@ class Address {
return Address.typesByVal[this.type].toLowerCase();
}

/**
* Get prefix for indexers
* It's a single byte encoded as follows:
* 1 bit whether it's legacy or witness.
* 7 bits used for the data.
* @param {Network|String} network
* @returns {Number}
*/

getPrefix(network) {
if (this.isProgram())
return this.version;

// Note: -1 | 0x80 = -1
return 0x80 | this.getBase58Prefix(network);
}

/**
* Get a network address prefix for the address.
* @param {Network?} network
* @returns {Number}
*/

getPrefix(network) {
getBase58Prefix(network) {
network = Network.get(network);

const prefixes = network.addressPrefix;
Expand All @@ -148,31 +165,6 @@ class Address {
return prefixes.pubkeyhash;
case Address.types.SCRIPTHASH:
return prefixes.scripthash;
case Address.types.WITNESS:
if (this.version === 0) {
if (this.hash.length === 20)
return prefixes.witnesspubkeyhash;

if (this.hash.length === 32)
return prefixes.witnessscripthash;

// BIP141 defines any version 0 data lengths besides 20 and 32
// as invalid. Note that this is not the case with version 1
// (and presumably, all other future versions) where specific
// lengths are defined as valid and trigger extra rule sets,
// but all other lengths remain unencumbered (ANYONECANSPEND).
return -1;
}

// Since all segwit address strings have the same prefix
// for each network, there really isn't any reason why version 0
// script/pubkey should have been explicitly identified (above).
// We keep them for backwards comptability but from witness version 1
// onward, we only need to retun the witness version number.
// Unlike legacy addresses, the prefix is not actually included in the
// address string. bcoin uses it internally to keep Address objects
// consistent (the addrIndexer expects it for each entry, for example).
return prefixes.witnessVersionMask + this.version;
}

return -1;
Expand Down Expand Up @@ -202,7 +194,7 @@ class Address {
toRaw(network) {
const size = this.getSize();
const bw = bio.write(size);
const prefix = this.getPrefix(network);
const prefix = this.getBase58Prefix(network);

assert(prefix !== -1, 'Not a valid address prefix.');

Expand Down Expand Up @@ -349,7 +341,7 @@ class Address {
}

/**
* Inject properties from serialized data.
* Decode base58.
* @private
* @param {Buffer} data
* @throws Parse error
Expand All @@ -359,29 +351,18 @@ class Address {
const br = bio.read(data, true);
const prefix = br.readU8();

network = Network.fromAddress(prefix, network);
network = Network.fromBase58(prefix, network);

const type = Address.getType(prefix, network);

let version = -1;
if (type === Address.types.WITNESS) {
if (data.length > 38)
throw new Error('Address is too long.');

version = br.readU8();

if (br.readU8() !== 0)
throw new Error('Address version padding is non-zero.');
} else {
if (data.length !== 25)
throw new Error('Address is too long.');
}
if (data.length !== 25)
throw new Error('Address is too long.');

const hash = br.readBytes(br.left() - 4);

br.verifyChecksum(hash256.digest);

return this.fromHash(hash, type, version);
return this.fromHash(hash, type);
}

/**
Expand Down Expand Up @@ -913,18 +894,11 @@ class Address {
static getType(prefix, network) {
const prefixes = network.addressPrefix;

// For witness versions > 0
if ((prefix & prefixes.witnessVersionMask) === prefixes.witnessVersionMask)
return Address.types.WITNESS;

switch (prefix) {
case prefixes.pubkeyhash:
return Address.types.PUBKEYHASH;
case prefixes.scripthash:
return Address.types.SCRIPTHASH;
case prefixes.witnesspubkeyhash:
case prefixes.witnessscripthash:
return Address.types.WITNESS;
default:
throw new Error('Unknown address prefix.');
}
Expand Down
12 changes: 3 additions & 9 deletions lib/protocol/network.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,8 @@ class Network {
* @returns {Network}
*/

static fromAddress(prefix, network) {
return Network.by(prefix, cmpAddress, network, 'base58 address');
static fromBase58(prefix, network) {
return Network.by(prefix, cmpBase58, network, 'base58 address');
}

/**
Expand Down Expand Up @@ -428,18 +428,12 @@ function cmpPriv58(network, prefix) {
return network.keyPrefix.xprivkey58 === prefix;
}

function cmpAddress(network, prefix) {
function cmpBase58(network, prefix) {
const prefixes = network.addressPrefix;

// For witness versions > 0
if ((prefix & prefixes.witnessVersionMask) === prefixes.witnessVersionMask)
return true;

switch (prefix) {
case prefixes.pubkeyhash:
case prefixes.scripthash:
case prefixes.witnesspubkeyhash:
case prefixes.witnessscripthash:
return true;
}

Expand Down
56 changes: 4 additions & 52 deletions lib/protocol/networks.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,42 +23,6 @@ function b(hash) {
return Buffer.from(hash, 'hex');
}

// To stay backwards compatible with legacy addresses, we use
// phony prefix bytes for all witness addresses which are only
// used internally and are not part of the actual address.
// Witness version 0 has two prefixes already for each network
// corresponding to the only two valid data lengths (20 and 32).
// All other data lengths are totally invalid in version 0 programs.
// Witness versions > 0 will not have any invalid data lengths
// (they will remain anyone-can-spend) so all we need to do is
// prefix them with their version number. Because values between
// 0-15 have already been assigned to other address types,
// we set the first four bits as a mask for all witness versions.
// There is no need to use different values for each network.
// Why different values were used for the witness 0 types for
// some networks is unknown, but we keep it for backwards compatability.

const WITNESS_VERSION_MASK = 0xf0;

const witnessVersionPrefixes = {
witnessVersionMask: WITNESS_VERSION_MASK,
witnessv1: WITNESS_VERSION_MASK + 1,
witnessv2: WITNESS_VERSION_MASK + 2,
witnessv3: WITNESS_VERSION_MASK + 3,
witnessv4: WITNESS_VERSION_MASK + 4,
witnessv5: WITNESS_VERSION_MASK + 5,
witnessv6: WITNESS_VERSION_MASK + 6,
witnessv7: WITNESS_VERSION_MASK + 7,
witnessv8: WITNESS_VERSION_MASK + 8,
witnessv9: WITNESS_VERSION_MASK + 9,
witnessv10: WITNESS_VERSION_MASK + 10,
witnessv11: WITNESS_VERSION_MASK + 11,
witnessv12: WITNESS_VERSION_MASK + 12,
witnessv13: WITNESS_VERSION_MASK + 13,
witnessv14: WITNESS_VERSION_MASK + 14,
witnessv15: WITNESS_VERSION_MASK + 15
};

/**
* Network type list.
* @memberof module:protocol/networks
Expand Down Expand Up @@ -469,10 +433,7 @@ main.keyPrefix = {
main.addressPrefix = {
pubkeyhash: 0x00,
scripthash: 0x05,
witnesspubkeyhash: 0x06,
witnessscripthash: 0x0a,
bech32: 'bc',
...witnessVersionPrefixes
bech32: 'bc'
};

/**
Expand Down Expand Up @@ -725,10 +686,7 @@ testnet.keyPrefix = {
testnet.addressPrefix = {
pubkeyhash: 0x6f,
scripthash: 0xc4,
witnesspubkeyhash: 0x03,
witnessscripthash: 0x28,
bech32: 'tb',
...witnessVersionPrefixes
bech32: 'tb'
};

testnet.requireStandard = false;
Expand Down Expand Up @@ -888,10 +846,7 @@ regtest.keyPrefix = {
regtest.addressPrefix = {
pubkeyhash: 0x6f,
scripthash: 0xc4,
witnesspubkeyhash: 0x03,
witnessscripthash: 0x28,
bech32: 'bcrt',
...witnessVersionPrefixes
bech32: 'bcrt'
};

regtest.requireStandard = false;
Expand Down Expand Up @@ -1059,10 +1014,7 @@ simnet.keyPrefix = {
simnet.addressPrefix = {
pubkeyhash: 0x3f,
scripthash: 0x7b,
witnesspubkeyhash: 0x19,
witnessscripthash: 0x28,
bech32: 'sb',
...witnessVersionPrefixes
bech32: 'sb'
};

simnet.requireStandard = false;
Expand Down
2 changes: 1 addition & 1 deletion test/address-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ describe('Address', function() {
const network = Network.get('regtest');
assert.strictEqual(
parsed.getPrefix(network),
network.addressPrefix.witnessVersionMask + 1
parsed.version
);
}
});
Expand Down
43 changes: 25 additions & 18 deletions test/mempool-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -786,21 +786,6 @@ describe('Mempool', function() {
});

describe('AddrIndexer', function () {
it('will not get key for invalid witness program v0', function() {
const addrindex = new AddrIndexer();

// Create a witness program version 0 with
// 10 byte data push.
const addr = new Address();
addr.type = Address.types.WITNESS;
addr.version = 0;
addr.hash = Buffer.alloc(10);

const key = addrindex.getKey(addr);

assert.strictEqual(key, null);
});

it('will get key for witness program v0', function() {
const addrindex = new AddrIndexer();

Expand All @@ -814,7 +799,7 @@ describe('Mempool', function() {

const key = addrindex.getKey(addr);

assert.bufferEqual(key, Buffer.from('0a' + '00'.repeat(32), 'hex'));
assert.bufferEqual(key, Buffer.from('00' + '00'.repeat(32), 'hex'));
});

it('will get key for witness program v1', function() {
Expand All @@ -830,7 +815,7 @@ describe('Mempool', function() {

const key = addrindex.getKey(addr);

assert.bufferEqual(key, Buffer.from('f1' + '00'.repeat(32), 'hex'));
assert.bufferEqual(key, Buffer.from('01' + '00'.repeat(32), 'hex'));
});

it('will get key for witness program v15', function() {
Expand All @@ -846,7 +831,29 @@ describe('Mempool', function() {

const key = addrindex.getKey(addr);

assert.bufferEqual(key, Buffer.from('ff' + '00'.repeat(32), 'hex'));
assert.bufferEqual(key, Buffer.from('0f' + '00'.repeat(32), 'hex'));
});

it('will get key for P2PKH', function() {
const addrindex = new AddrIndexer();

const script = Script.fromPubkeyhash(Buffer.alloc(20));
const addr = Address.fromScript(script);

const key = addrindex.getKey(addr);

assert.bufferEqual(key, Buffer.from('80' + '00'.repeat(20), 'hex'));
});

it('will get key for P2SH', function() {
const addrindex = new AddrIndexer();

const script = Script.fromScripthash(Buffer.alloc(20));
const addr = Address.fromScript(script);

const key = addrindex.getKey(addr);

assert.bufferEqual(key, Buffer.from('85' + '00'.repeat(20), 'hex'));
});
});

Expand Down

0 comments on commit f74e507

Please sign in to comment.