From 779d1193b5beefc3a9cc9ce8f5e2c47165acd037 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 27 May 2021 16:07:52 +0800 Subject: [PATCH 1/9] add test --- .../__tests__/address.test.ts | 32 +++++++++++++++++++ packages/jellyfish-address/src/address.ts | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 packages/jellyfish-address/__tests__/address.test.ts diff --git a/packages/jellyfish-address/__tests__/address.test.ts b/packages/jellyfish-address/__tests__/address.test.ts new file mode 100644 index 0000000000..894163d990 --- /dev/null +++ b/packages/jellyfish-address/__tests__/address.test.ts @@ -0,0 +1,32 @@ +import { Script } from '@defichain/jellyfish-transaction' +import { RegTest } from '@defichain/jellyfish-network' +import { Address, Validator } from '../src' + +class DummyAddress extends Address { + getScript (): Script { + return { + stack: [] + } + } + + validators (): Validator[] { + return [ + () => true, + () => false + ] + } +} + +describe('Address', () => { + it('validate()', () => { + const test = new DummyAddress(RegTest, 'address utf8', true, 'Unknown') + + expect(test.valid).toBeTruthy() + expect(test.validatorPassed).toStrictEqual(0) + + test.validate() + + expect(test.valid).toBeFalsy() + expect(test.validatorPassed).toStrictEqual(1) + }) +}) diff --git a/packages/jellyfish-address/src/address.ts b/packages/jellyfish-address/src/address.ts index d9369e42b2..aa12c17c10 100644 --- a/packages/jellyfish-address/src/address.ts +++ b/packages/jellyfish-address/src/address.ts @@ -25,7 +25,7 @@ export abstract class Address { validate (): boolean { this.valid = true this.validatorPassed = 0 - this.validators().forEach((validator, index) => { + this.validators().forEach((validator) => { const passed = validator() this.valid = this.valid && passed if (passed) { From c06f3f42ce4ec08c3aa1fe105ce9250292151098 Mon Sep 17 00:00:00 2001 From: ivan Date: Thu, 27 May 2021 20:37:10 +0800 Subject: [PATCH 2/9] rework jellyfish address lib, remove generic type, and ambigual address type guess --- .../__tests__/address.test.ts | 32 --------- .../__tests__/base58_address.test.ts | 57 --------------- .../__tests__/bech32_address.test.ts | 51 ------------- .../jellyfish-address/__tests__/p2pkh.test.ts | 37 +++------- .../jellyfish-address/__tests__/p2sh.test.ts | 37 +++------- .../__tests__/p2wpkh.test.ts | 36 +++------- .../jellyfish-address/__tests__/p2wsh.test.ts | 36 +++------- packages/jellyfish-address/src/address.ts | 52 +++----------- .../jellyfish-address/src/base58_address.ts | 60 ---------------- .../jellyfish-address/src/bech32_address.ts | 50 ------------- packages/jellyfish-address/src/index.ts | 72 +++++-------------- packages/jellyfish-address/src/p2pkh.ts | 51 +++++++------ packages/jellyfish-address/src/p2sh.ts | 50 +++++++------ packages/jellyfish-address/src/p2wpkh.ts | 62 +++++++++------- packages/jellyfish-address/src/p2wsh.ts | 61 ++++++++++------ ...builder_account_account_to_account.test.ts | 9 ++- ...txn_builder_liq_pool_add_liquidity.test.ts | 3 +- .../jellyfish-wallet/src/wallet_account.ts | 2 +- 18 files changed, 197 insertions(+), 561 deletions(-) delete mode 100644 packages/jellyfish-address/__tests__/address.test.ts delete mode 100644 packages/jellyfish-address/__tests__/base58_address.test.ts delete mode 100644 packages/jellyfish-address/__tests__/bech32_address.test.ts delete mode 100644 packages/jellyfish-address/src/base58_address.ts delete mode 100644 packages/jellyfish-address/src/bech32_address.ts diff --git a/packages/jellyfish-address/__tests__/address.test.ts b/packages/jellyfish-address/__tests__/address.test.ts deleted file mode 100644 index 894163d990..0000000000 --- a/packages/jellyfish-address/__tests__/address.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Script } from '@defichain/jellyfish-transaction' -import { RegTest } from '@defichain/jellyfish-network' -import { Address, Validator } from '../src' - -class DummyAddress extends Address { - getScript (): Script { - return { - stack: [] - } - } - - validators (): Validator[] { - return [ - () => true, - () => false - ] - } -} - -describe('Address', () => { - it('validate()', () => { - const test = new DummyAddress(RegTest, 'address utf8', true, 'Unknown') - - expect(test.valid).toBeTruthy() - expect(test.validatorPassed).toStrictEqual(0) - - test.validate() - - expect(test.valid).toBeFalsy() - expect(test.validatorPassed).toStrictEqual(1) - }) -}) diff --git a/packages/jellyfish-address/__tests__/base58_address.test.ts b/packages/jellyfish-address/__tests__/base58_address.test.ts deleted file mode 100644 index 61658d43a0..0000000000 --- a/packages/jellyfish-address/__tests__/base58_address.test.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { Script } from '@defichain/jellyfish-transaction' -import { Network } from '@defichain/jellyfish-network' -import { Base58Address } from '../src' - -class DummyB58Address extends Base58Address { - getScript (): Script { - return { - stack: [] - } - } - - getPrefix (): number { - return this.network.pubKeyHashPrefix // match the fixture p2pkh prefix - } -} - -describe('Base58Address', () => { - const b58Fixture = { - p2sh: 'dFFPENo7FPMJpDV6fUcfo4QfkZrfrV1Uf8', // prefix = 0x12 - p2pkh: '8JBuS81VT8ouPrT6YS55qoS74D13Cw7h1Y' - } - - const dummyNetwork: Network = { - name: 'regtest', - bech32: { - hrp: 'dummy' - }, - bip32: { - publicPrefix: 0x00000000, - privatePrefix: 0x00000000 - }, - wifPrefix: 0x00, - pubKeyHashPrefix: 0x12, - scriptHashPrefix: 0x00, - messagePrefix: '\x00Dummy Msg Prefix:\n' - } as any - - describe('extensible, should work for any defined network protocol', () => { - it('fromAddress() - valid', () => { - const valid = Base58Address.fromAddress(dummyNetwork, b58Fixture.p2pkh, DummyB58Address) - expect(valid.validate()).toBeTruthy() - }) - - it('fromAddress() - invalid character set', () => { - const invalid = Base58Address.fromAddress(dummyNetwork, 'invalid b58 address', DummyB58Address) - expect(invalid.validate()).toBeFalsy() - }) - - it('fromAddress() - invalid prefix', () => { - const invalid = Base58Address.fromAddress(dummyNetwork, b58Fixture.p2sh, DummyB58Address) - expect(invalid.validate()).toBeFalsy() - - const valid = Base58Address.fromAddress(dummyNetwork, b58Fixture.p2pkh, DummyB58Address) - expect(valid.validate()).toBeTruthy() - }) - }) -}) diff --git a/packages/jellyfish-address/__tests__/bech32_address.test.ts b/packages/jellyfish-address/__tests__/bech32_address.test.ts deleted file mode 100644 index e6c533725b..0000000000 --- a/packages/jellyfish-address/__tests__/bech32_address.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Script } from '@defichain/jellyfish-transaction' -import { Network } from '@defichain/jellyfish-network' -import { Bech32Address } from '../src' - -class DummyBech32Address extends Bech32Address { - getScript (): Script { - return { - stack: [] - } - } -} - -describe('Bech32Address', () => { - const bech32Fixture = { - p2wpkh: 'dummy1qpe7q4vvtxpdunpazvmwqdh3xlnatfdt2xr8mpv', // edited prefix to match test network - invalidPrefix: 'prefix1qpe7q4vvtxpdunpazvmwqdh3xlnatfdt2xr8mpv', // original p2wpkh address sample - invalidCharset: 'dummy1qpe7q4vvtxpdunpazvmwqdh3xlnatfdt2xr8mpo' // character 'o' - } - - const dummyNetwork: Network = { - name: 'regtest', - bech32: { - hrp: 'dummy' - }, - bip32: { - publicPrefix: 0x00000000, - privatePrefix: 0x00000000 - }, - wifPrefix: 0x00, - pubKeyHashPrefix: 0x00, - scriptHashPrefix: 0x00, - messagePrefix: '\x00Dummy Msg Prefix:\n' - } as any - - describe('extensible, should work for any defined network protocol', () => { - it('fromAddress() - valid', () => { - const valid = Bech32Address.fromAddress(dummyNetwork, bech32Fixture.p2wpkh, DummyBech32Address) - expect(valid.validate()).toBeTruthy() - }) - - it('fromAddress() - invalid character set', () => { - const invalid = Bech32Address.fromAddress(dummyNetwork, bech32Fixture.invalidCharset, DummyBech32Address) - expect(invalid.validate()).toBeFalsy() - }) - - it('fromAddress() - invalid prefix', () => { - const invalid = Bech32Address.fromAddress(dummyNetwork, bech32Fixture.invalidPrefix, DummyBech32Address) - expect(invalid.validate()).toBeFalsy() - }) - }) -}) diff --git a/packages/jellyfish-address/__tests__/p2pkh.test.ts b/packages/jellyfish-address/__tests__/p2pkh.test.ts index 51abde4820..b2aed72f59 100644 --- a/packages/jellyfish-address/__tests__/p2pkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2pkh.test.ts @@ -1,4 +1,3 @@ -import bs58 from 'bs58' import { MainNet, RegTest, TestNet } from '@defichain/jellyfish-network' import { OP_CODES } from '@defichain/jellyfish-transaction' import { RegTestContainer } from '@defichain/testcontainers' @@ -25,7 +24,7 @@ describe('P2PKH', () => { describe('from() - valid address', () => { it('should get the type precisely', () => { - const p2pkh = DeFiAddress.from('mainnet', p2pkhFixture.mainnet) + const p2pkh = DeFiAddress.from(p2pkhFixture.mainnet) expect(p2pkh.valid).toBeTruthy() expect(p2pkh.type).toStrictEqual('P2PKH') expect(p2pkh.constructor.name).toStrictEqual('P2PKH') @@ -33,13 +32,13 @@ describe('P2PKH', () => { }) it('should work for all recognized network type', () => { - const testnet = DeFiAddress.from('testnet', p2pkhFixture.testnet) + const testnet = DeFiAddress.from(p2pkhFixture.testnet) expect(testnet.valid).toBeTruthy() expect(testnet.type).toStrictEqual('P2PKH') expect(testnet.constructor.name).toStrictEqual('P2PKH') expect(testnet.network).toStrictEqual(TestNet) - const regtest = DeFiAddress.from('regtest', p2pkhFixture.regtest) + const regtest = DeFiAddress.from(p2pkhFixture.regtest) expect(regtest.valid).toBeTruthy() expect(regtest.type).toStrictEqual('P2PKH') expect(regtest.constructor.name).toStrictEqual('P2PKH') @@ -49,20 +48,20 @@ describe('P2PKH', () => { describe('from() - invalid address', () => { it('should be able to validate in address prefix with network', () => { - const invalid = DeFiAddress.from('mainnet', p2pkhFixture.invalid) + const invalid = DeFiAddress.from(p2pkhFixture.invalid) expect(invalid.valid).toBeFalsy() }) it('should be able to validate in address prefix with network', () => { // valid address, used on different network - const p2pkh = DeFiAddress.from('testnet', p2pkhFixture.mainnet) + const p2pkh = DeFiAddress.from(p2pkhFixture.mainnet) expect(p2pkh.valid).toBeFalsy() // expect(p2pkh.type).toStrictEqual('P2PKH') // invalid address guessed type is not promising, as p2pkh and p2sh are versy similar expect(p2pkh.network).toStrictEqual(TestNet) }) it('should get the type precisely', () => { - const invalid = DeFiAddress.from('mainnet', p2pkhFixture.invalidChecksum) + const invalid = DeFiAddress.from(p2pkhFixture.invalidChecksum) expect(invalid.valid).toBeFalsy() }) }) @@ -97,7 +96,7 @@ describe('P2PKH', () => { describe('getScript()', () => { it('should refuse to build ops code stack for invalid address', () => { - const invalid = DeFiAddress.from('testnet', p2pkhFixture.mainnet) + const invalid = DeFiAddress.from(p2pkhFixture.mainnet) expect(invalid.valid).toBeFalsy() try { invalid.getScript() @@ -107,7 +106,7 @@ describe('P2PKH', () => { }) it('should be able to build script', async () => { - const p2pkh = DeFiAddress.from('mainnet', p2pkhFixture.mainnet) + const p2pkh = DeFiAddress.from(p2pkhFixture.mainnet) const scriptStack = p2pkh.getScript() expect(scriptStack.stack.length).toStrictEqual(5) @@ -118,24 +117,4 @@ describe('P2PKH', () => { expect(scriptStack.stack[4]).toStrictEqual(OP_CODES.OP_CHECKSIG) }) }) - - it('validate()', () => { - const hex = bs58.decode(p2pkhFixture.mainnet).toString('hex').substring(2, 42) // take 20 bytes data only - const p2pkh = new P2PKH(MainNet, p2pkhFixture.mainnet, hex) - - expect(p2pkh.validatorPassed).toStrictEqual(0) - expect(p2pkh.valid).toBeFalsy() - - const isValid = p2pkh.validate() - expect(p2pkh.validatorPassed).toStrictEqual(5) - expect(isValid).toBeTruthy() - }) - - it('guess()', () => { - const p2pkh = DeFiAddress.guess(p2pkhFixture.mainnet) - expect(p2pkh.valid).toBeTruthy() - expect(p2pkh.type).toStrictEqual('P2PKH') - expect(p2pkh.constructor.name).toStrictEqual('P2PKH') - expect(p2pkh.network).toStrictEqual(MainNet) - }) }) diff --git a/packages/jellyfish-address/__tests__/p2sh.test.ts b/packages/jellyfish-address/__tests__/p2sh.test.ts index 202dfd8426..82142ba5e8 100644 --- a/packages/jellyfish-address/__tests__/p2sh.test.ts +++ b/packages/jellyfish-address/__tests__/p2sh.test.ts @@ -1,4 +1,3 @@ -import bs58 from 'bs58' import { MainNet, RegTest, TestNet } from '@defichain/jellyfish-network' import { OP_CODES } from '@defichain/jellyfish-transaction' import { RegTestContainer } from '@defichain/testcontainers' @@ -25,7 +24,7 @@ describe('P2SH', () => { describe('from() - valid address', () => { it('should get the type precisely', () => { - const p2sh = DeFiAddress.from('mainnet', p2shFixture.mainnet) + const p2sh = DeFiAddress.from(p2shFixture.mainnet) expect(p2sh.valid).toBeTruthy() expect(p2sh.type).toStrictEqual('P2SH') expect(p2sh.constructor.name).toStrictEqual('P2SH') @@ -33,13 +32,13 @@ describe('P2SH', () => { }) it('should work for all recognized network type', () => { - const testnet = DeFiAddress.from('testnet', p2shFixture.testnet) + const testnet = DeFiAddress.from(p2shFixture.testnet) expect(testnet.valid).toBeTruthy() expect(testnet.type).toStrictEqual('P2SH') expect(testnet.constructor.name).toStrictEqual('P2SH') expect(testnet.network).toStrictEqual(TestNet) - const regtest = DeFiAddress.from('regtest', p2shFixture.regtest) + const regtest = DeFiAddress.from(p2shFixture.regtest) expect(regtest.valid).toBeTruthy() expect(regtest.type).toStrictEqual('P2SH') expect(regtest.constructor.name).toStrictEqual('P2SH') @@ -49,20 +48,20 @@ describe('P2SH', () => { describe('from() - invalid address', () => { it('trimmed prefix', () => { - const invalid = DeFiAddress.from('mainnet', p2shFixture.invalid) + const invalid = DeFiAddress.from(p2shFixture.invalid) expect(invalid.valid).toBeFalsy() }) it('should be able to validate in address prefix with network', () => { // valid address, used on different network - const p2sh = DeFiAddress.from('testnet', p2shFixture.mainnet) + const p2sh = DeFiAddress.from(p2shFixture.mainnet) expect(p2sh.valid).toBeFalsy() // expect(p2sh.type).toStrictEqual('P2SH') // invalid address guessed type is not promising, as p2sh and p2sh are versy similar expect(p2sh.network).toStrictEqual(TestNet) }) it('should get the type precisely', () => { - const invalid = DeFiAddress.from('mainnet', p2shFixture.invalidChecksum) + const invalid = DeFiAddress.from(p2shFixture.invalidChecksum) expect(invalid.valid).toBeFalsy() }) }) @@ -98,7 +97,7 @@ describe('P2SH', () => { describe('getScript()', () => { it('should refuse to build ops code stack for invalid address', () => { - const invalid = DeFiAddress.from('testnet', p2shFixture.mainnet) + const invalid = DeFiAddress.from(p2shFixture.mainnet) expect(invalid.valid).toBeFalsy() try { invalid.getScript() @@ -108,7 +107,7 @@ describe('P2SH', () => { }) it('should be able to build script', async () => { - const p2sh = DeFiAddress.from('mainnet', p2shFixture.mainnet) + const p2sh = DeFiAddress.from(p2shFixture.mainnet) const scriptStack = p2sh.getScript() expect(scriptStack.stack.length).toStrictEqual(3) @@ -117,24 +116,4 @@ describe('P2SH', () => { expect(scriptStack.stack[2]).toStrictEqual(OP_CODES.OP_EQUAL) }) }) - - it('validate()', () => { - const hex = bs58.decode(p2shFixture.mainnet).toString('hex').substring(2, 42) // take 20 bytes data only - const p2sh = new P2SH(MainNet, p2shFixture.mainnet, hex) - - expect(p2sh.validatorPassed).toStrictEqual(0) - expect(p2sh.valid).toBeFalsy() - - const isValid = p2sh.validate() - expect(p2sh.validatorPassed).toStrictEqual(5) - expect(isValid).toBeTruthy() - }) - - it('guess()', () => { - const p2sh = DeFiAddress.guess(p2shFixture.mainnet) - expect(p2sh.valid).toBeTruthy() - expect(p2sh.type).toStrictEqual('P2SH') - expect(p2sh.constructor.name).toStrictEqual('P2SH') - expect(p2sh.network).toStrictEqual(MainNet) - }) }) diff --git a/packages/jellyfish-address/__tests__/p2wpkh.test.ts b/packages/jellyfish-address/__tests__/p2wpkh.test.ts index fb71ccda3d..31c9e54b48 100644 --- a/packages/jellyfish-address/__tests__/p2wpkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wpkh.test.ts @@ -24,7 +24,7 @@ describe('P2WPKH', () => { describe('from() - valid address', () => { it('should get the type precisely', () => { - const p2wpkh = DeFiAddress.from('mainnet', p2wpkhFixture.mainnet) + const p2wpkh = DeFiAddress.from(p2wpkhFixture.mainnet) expect(p2wpkh.valid).toBeTruthy() expect(p2wpkh.type).toStrictEqual('P2WPKH') expect(p2wpkh.constructor.name).toStrictEqual('P2WPKH') @@ -32,13 +32,13 @@ describe('P2WPKH', () => { }) it('should work for all recognized network type', () => { - const testnet = DeFiAddress.from('testnet', p2wpkhFixture.testnet) + const testnet = DeFiAddress.from(p2wpkhFixture.testnet) expect(testnet.valid).toBeTruthy() expect(testnet.type).toStrictEqual('P2WPKH') expect(testnet.constructor.name).toStrictEqual('P2WPKH') expect(testnet.network).toStrictEqual(TestNet) - const regtest = DeFiAddress.from('regtest', p2wpkhFixture.regtest) + const regtest = DeFiAddress.from(p2wpkhFixture.regtest) expect(regtest.valid).toBeTruthy() expect(regtest.type).toStrictEqual('P2WPKH') expect(regtest.constructor.name).toStrictEqual('P2WPKH') @@ -48,18 +48,18 @@ describe('P2WPKH', () => { describe('from() - invalid address', () => { it('trimmed prefix', () => { - const invalid = DeFiAddress.from('mainnet', p2wpkhFixture.trimmedPrefix) + const invalid = DeFiAddress.from(p2wpkhFixture.trimmedPrefix) expect(invalid.valid).toBeFalsy() }) it('invalid character set', () => { - const invalid = DeFiAddress.from('mainnet', p2wpkhFixture.invalid) + const invalid = DeFiAddress.from(p2wpkhFixture.invalid) expect(invalid.valid).toBeFalsy() }) it('should be able to validate in address prefix with network', () => { // valid address, used on different network - const p2wpkh = DeFiAddress.from('testnet', p2wpkhFixture.mainnet) + const p2wpkh = DeFiAddress.from(p2wpkhFixture.mainnet) expect(p2wpkh.valid).toBeFalsy() // expect(p2wpkh.type).toStrictEqual('P2WPKH') // invalid address guessed type is not promising, as p2wpkh and p2wpkh are versy similar expect(p2wpkh.network).toStrictEqual(TestNet) @@ -96,7 +96,7 @@ describe('P2WPKH', () => { describe('getScript()', () => { it('should refuse to build ops code stack for invalid address', () => { - const invalid = DeFiAddress.from('testnet', p2wpkhFixture.mainnet) + const invalid = DeFiAddress.from(p2wpkhFixture.mainnet) expect(invalid.valid).toBeFalsy() try { invalid.getScript() @@ -106,7 +106,7 @@ describe('P2WPKH', () => { }) it('should be able to build script', async () => { - const p2wpkh = DeFiAddress.from('mainnet', p2wpkhFixture.mainnet) + const p2wpkh = DeFiAddress.from(p2wpkhFixture.mainnet) const scriptStack = p2wpkh.getScript() expect(scriptStack.stack.length).toStrictEqual(2) @@ -114,24 +114,4 @@ describe('P2WPKH', () => { expect(scriptStack.stack[1].type).toStrictEqual('OP_PUSHDATA') }) }) - - it('validate()', () => { - const data = '0e7c0ab18b305bc987a266dc06de26fcfab4b56a' // 20 bytes - - const p2wpkh = new P2WPKH(RegTest, p2wpkhFixture.regtest, data) - expect(p2wpkh.validatorPassed).toStrictEqual(0) - expect(p2wpkh.valid).toBeFalsy() - - const isValid = p2wpkh.validate() - expect(p2wpkh.validatorPassed).toStrictEqual(5) // length, network prefix, data character set - expect(isValid).toBeTruthy() - }) - - it('guess()', () => { - const p2wpkh = DeFiAddress.guess(p2wpkhFixture.mainnet) - expect(p2wpkh.valid).toBeTruthy() - expect(p2wpkh.type).toStrictEqual('P2WPKH') - expect(p2wpkh.constructor.name).toStrictEqual('P2WPKH') - expect(p2wpkh.network).toStrictEqual(MainNet) - }) }) diff --git a/packages/jellyfish-address/__tests__/p2wsh.test.ts b/packages/jellyfish-address/__tests__/p2wsh.test.ts index 9520b7b47f..c196640c48 100644 --- a/packages/jellyfish-address/__tests__/p2wsh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wsh.test.ts @@ -14,7 +14,7 @@ describe('P2WSH', () => { describe('from() - valid address', () => { it('should get the type precisely', () => { - const p2sh = DeFiAddress.from('mainnet', p2wshFixture.mainnet) + const p2sh = DeFiAddress.from(p2wshFixture.mainnet) expect(p2sh.valid).toBeTruthy() expect(p2sh.type).toStrictEqual('P2WSH') expect(p2sh.constructor.name).toStrictEqual('P2WSH') @@ -22,13 +22,13 @@ describe('P2WSH', () => { }) it('should work for all recognized network type', () => { - const testnet = DeFiAddress.from('testnet', p2wshFixture.testnet) + const testnet = DeFiAddress.from(p2wshFixture.testnet) expect(testnet.valid).toBeTruthy() expect(testnet.type).toStrictEqual('P2WSH') expect(testnet.constructor.name).toStrictEqual('P2WSH') expect(testnet.network).toStrictEqual(TestNet) - const regtest = DeFiAddress.from('regtest', p2wshFixture.regtest) + const regtest = DeFiAddress.from(p2wshFixture.regtest) expect(regtest.valid).toBeTruthy() expect(regtest.type).toStrictEqual('P2WSH') expect(regtest.constructor.name).toStrictEqual('P2WSH') @@ -38,18 +38,18 @@ describe('P2WSH', () => { describe('from() - invalid address', () => { it('trimmed prefix', () => { - const invalid = DeFiAddress.from('mainnet', p2wshFixture.trimmedPrefix) + const invalid = DeFiAddress.from(p2wshFixture.trimmedPrefix) expect(invalid.valid).toBeFalsy() }) it('invalid character set', () => { - const invalid = DeFiAddress.from('mainnet', p2wshFixture.invalid) + const invalid = DeFiAddress.from(p2wshFixture.invalid) expect(invalid.valid).toBeFalsy() }) it('should be able to validate in address prefix with network', () => { // valid address, used on different network - const p2sh = DeFiAddress.from('testnet', p2wshFixture.mainnet) + const p2sh = DeFiAddress.from(p2wshFixture.mainnet) expect(p2sh.valid).toBeFalsy() // expect(p2sh.type).toStrictEqual('P2WSH') // invalid address guessed type is not promising, as p2wsh and p2sh are versy similar expect(p2sh.network).toStrictEqual(TestNet) @@ -86,7 +86,7 @@ describe('P2WSH', () => { describe('getScript()', () => { it('should refuse to build ops code stack for invalid address', () => { - const invalid = DeFiAddress.from('testnet', p2wshFixture.mainnet) + const invalid = DeFiAddress.from(p2wshFixture.mainnet) expect(invalid.valid).toBeFalsy() try { invalid.getScript() @@ -96,7 +96,7 @@ describe('P2WSH', () => { }) it('should be able to build script', async () => { - const p2wsh = DeFiAddress.from('mainnet', p2wshFixture.mainnet) + const p2wsh = DeFiAddress.from(p2wshFixture.mainnet) const scriptStack = p2wsh.getScript() expect(scriptStack.stack.length).toStrictEqual(2) @@ -104,24 +104,4 @@ describe('P2WSH', () => { expect(scriptStack.stack[1].type).toStrictEqual('OP_PUSHDATA') }) }) - - it('validate()', () => { - const data = '9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19' // 32 bytes - - const p2wsh = new P2WSH(RegTest, p2wshFixture.regtest, data) - expect(p2wsh.validatorPassed).toStrictEqual(0) - expect(p2wsh.valid).toBeFalsy() - - const isValid = p2wsh.validate() - // expect(p2wsh.validatorPassed).toStrictEqual(4) // length, network prefix, data character set - expect(isValid).toBeTruthy() - }) - - it('guess()', () => { - const p2wsh = DeFiAddress.guess(p2wshFixture.mainnet) - expect(p2wsh.valid).toBeTruthy() - expect(p2wsh.type).toStrictEqual('P2WSH') - expect(p2wsh.constructor.name).toStrictEqual('P2WSH') - expect(p2wsh.network).toStrictEqual(MainNet) - }) }) diff --git a/packages/jellyfish-address/src/address.ts b/packages/jellyfish-address/src/address.ts index aa12c17c10..9607b7dc38 100644 --- a/packages/jellyfish-address/src/address.ts +++ b/packages/jellyfish-address/src/address.ts @@ -5,54 +5,20 @@ export type AddressType = 'Unknown' | 'P2PKH' | 'P2SH' | 'P2WPKH' | 'P2WSH' export type Validator = () => boolean export abstract class Address { - network: Network - utf8String: string - type: AddressType - valid: boolean - validatorPassed: number + readonly network?: Network + readonly utf8String: string + readonly type: AddressType + readonly valid: boolean - constructor (network: Network, utf8String: string, valid: boolean, type: AddressType) { + constructor (network: Network | undefined, utf8String: string, valid: boolean, type: AddressType) { this.network = network this.utf8String = utf8String this.valid = valid this.type = type - this.validatorPassed = 0 } - abstract validators (): Validator[] - abstract getScript (): Script - - validate (): boolean { - this.valid = true - this.validatorPassed = 0 - this.validators().forEach((validator) => { - const passed = validator() - this.valid = this.valid && passed - if (passed) { - this.validatorPassed += 1 - } - }) - return this.valid - } -} - -/** - * Default Address implementation when parsed address do not matched any type - */ -export class UnknownTypeAddress extends Address { - constructor (network: Network, raw: string) { - super(network, raw, false, 'Unknown') - } - - validators (): Validator[] { - return [] - } - - validate (): boolean { - return false - } - - getScript (): Script { - throw new Error('InvalidDeFiAddress') - } + /** + * should throw if called with address.valid === false + */ + abstract getScript (): Script | never } diff --git a/packages/jellyfish-address/src/base58_address.ts b/packages/jellyfish-address/src/base58_address.ts deleted file mode 100644 index 55a47c4c81..0000000000 --- a/packages/jellyfish-address/src/base58_address.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { Bs58 } from '@defichain/jellyfish-crypto' -import { Network } from '@defichain/jellyfish-network' -import { Address, AddressType, Validator } from './address' - -export abstract class Base58Address extends Address { - static MIN_LENGTH = 26 - static MAX_LENGTH = 35 - - // 20 bytes data - hex: string - static DATA_HEX_LENGTH = 40 // hex char count - - constructor (network: Network, utf8String: string, hex: string, valid: boolean, type: AddressType) { - super(network, utf8String, valid, type) - this.hex = hex - } - - abstract getPrefix (): number - - validators (): Validator[] { - return [ - () => (this.utf8String.length >= Base58Address.MIN_LENGTH), - () => (this.utf8String.length <= Base58Address.MAX_LENGTH), - () => { - const charset = '[1-9A-HJ-NP-Za-km-z]' - return new RegExp(`${charset}{${this.utf8String.length}}$`).test(this.utf8String) - }, - () => { - try { - const { prefix } = Bs58.toHash160(this.utf8String) // built in checksum check - return prefix === this.getPrefix() - } catch (e) { - return false - } - }, - () => { - try { - const { buffer } = Bs58.toHash160(this.utf8String) // built in checksum check - return buffer.toString('hex') === this.hex - } catch (e) { - return false - } - } - ] - } - - getPrefixString (): string { - return Buffer.from([this.getPrefix()]).toString('hex') - } - - static fromAddress (network: Network, utf8String: string, AddressClass: new (...a: any[]) => T): T { - try { - const { buffer } = Bs58.toHash160(utf8String) - return new AddressClass(network, utf8String, buffer.toString('hex')) - } catch (e) { - // non b58 string, invalid address - return new AddressClass(network, utf8String, '', false, 'Unknown') - } - } -} diff --git a/packages/jellyfish-address/src/bech32_address.ts b/packages/jellyfish-address/src/bech32_address.ts deleted file mode 100644 index 6366f546af..0000000000 --- a/packages/jellyfish-address/src/bech32_address.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { Network } from '@defichain/jellyfish-network' -import { bech32 } from 'bech32' -import { Address, AddressType, Validator } from './address' - -export abstract class Bech32Address extends Address { - static MAX_LENGTH = 90 - static MAX_HUMAN_READABLE_LENGTH = 83 - - constructor (network: Network, utf8String: string, valid: boolean, addressType: AddressType) { - super(network, utf8String.toLowerCase(), valid, addressType) - } - - validators (): Validator[] { - return [ - () => (new RegExp(`^${this.getHrp()}`).test(this.utf8String)), - () => { - const charset = '[02-9ac-hj-np-z]' // 0-9, a-z, and reject: [1, b, i, o] - const arr = this.utf8String.split('1') - const excludeHrp = arr[arr.length - 1] - const regex = new RegExp(`${charset}{${excludeHrp.length}}$`) - return regex.test(excludeHrp) - } - ] - } - - getHrp (): string { - return this.network.bech32.hrp - } - - static fromAddress(network: Network, raw: string, AddressClass: new (...a: any[]) => T): T { - let valid: boolean - let prefix: string - let data: string = '' - try { - const decoded = bech32.decode(raw) - valid = true - prefix = decoded.prefix - const trimmedVersion = decoded.words.slice(1) - data = Buffer.from(bech32.fromWords(trimmedVersion)).toString('hex') - - if (prefix !== network.bech32.hrp) { - valid = false - } - } catch (e) { - valid = false - } - - return new AddressClass(network, raw, data, valid) - } -} diff --git a/packages/jellyfish-address/src/index.ts b/packages/jellyfish-address/src/index.ts index 70793817ac..25b39967d5 100644 --- a/packages/jellyfish-address/src/index.ts +++ b/packages/jellyfish-address/src/index.ts @@ -1,81 +1,41 @@ -import { getNetwork, NetworkName } from '@defichain/jellyfish-network' -import { Address, AddressType, UnknownTypeAddress } from './address' -import { Base58Address } from './base58_address' -import { Bech32Address } from './bech32_address' +import { Address } from './address' import { P2PKH } from './p2pkh' import { P2SH } from './p2sh' import { P2WSH } from './p2wsh' import { P2WPKH } from './p2wpkh' export * from './address' -export * from './base58_address' -export * from './bech32_address' export * from './p2pkh' export * from './p2sh' export * from './p2wpkh' export * from './p2wsh' -/** - * When insist to use the "network" decoded from raw address, instead of passing one based on running application environment - * @param address raw human readable address (utf-8) - * @returns DefiAddress or a child class - */ -function guess (address: string): Address { - const networks: NetworkName[] = ['mainnet', 'testnet', 'regtest'] - const defaultOne = new UnknownTypeAddress(getNetwork('mainnet'), address) - for (let i = 0; i < networks.length; i += 1) { - const guessed = from(networks[i], address) - if (guessed.valid) { - return guessed - } - } - return defaultOne -} - /** * @param net to be validated against the decoded one from the raw address * @param address raw human readable address (utf-8) * @returns DefiAddress or a child class */ -function from (net: NetworkName, address: string): T { - const network = getNetwork(net) - const possible: Map = new Map() - possible.set('Unknown', new UnknownTypeAddress(network, address)) - possible.set('P2PKH', Base58Address.fromAddress(network, address, P2PKH)) - possible.set('P2SH', Base58Address.fromAddress(network, address, P2SH)) - possible.set('P2WPKH', Bech32Address.fromAddress(network, address, P2WPKH)) - possible.set('P2WSH', Bech32Address.fromAddress(network, address, P2WSH)) - - possible.forEach(each => each.validate()) - - let valid - possible.forEach(each => { - if (each.valid) { - valid = each - } - }) +function from (address: string): Address { + let guess: Address = P2PKH.from(address) + if (guess.valid) { + return guess + } - /* eslint-disable @typescript-eslint/strict-boolean-expressions */ - if (valid) { - // find if any has all validator passed - return valid + guess = P2SH.from(address) + if (guess.valid) { + return guess } - // else select the closest guess (most validator passed) - // default, when non have validator passed - let highestKey: AddressType = 'Unknown' - let highestCount = 0 + guess = P2WPKH.from(address) + if (guess.valid) { + return guess + } - possible.forEach((val, key) => { - if (val.validatorPassed > highestCount) { - highestKey = key - highestCount = val.validatorPassed - } - }) - return (possible.get(highestKey) as T) + // default, return `address.valid` can be false + guess = P2WSH.from(address) + return guess } export const DeFiAddress = { - guess, from } diff --git a/packages/jellyfish-address/src/p2pkh.ts b/packages/jellyfish-address/src/p2pkh.ts index f8c4a1b0eb..9093e40c28 100644 --- a/packages/jellyfish-address/src/p2pkh.ts +++ b/packages/jellyfish-address/src/p2pkh.ts @@ -1,23 +1,22 @@ -import { Bs58 } from '@defichain/jellyfish-crypto' -import { getNetwork, Network, NetworkName } from '@defichain/jellyfish-network' +import { Bs58 } from '@defichain/jellyfish-crypto/src' +import { getNetwork, MainNet, Network, NetworkName, RegTest, TestNet } from '@defichain/jellyfish-network' import { Script, OP_CODES, OP_PUSHDATA } from '@defichain/jellyfish-transaction' +import { Address } from './address' -import { Base58Address } from './base58_address' +export class P2PKH extends Address { + readonly pubKeyHash: Buffer | undefined // H160(33 bytes pubKey) -export class P2PKH extends Base58Address { - constructor (network: Network, utf8String: string, hex: string, validated: boolean = false) { - super(network, utf8String, hex, validated, 'P2PKH') - } + constructor (network: Network | undefined, utf8String: string, pubKeyHash: Buffer | undefined, valid: boolean) { + super(network, utf8String, valid, 'P2PKH') + this.pubKeyHash = pubKeyHash - getPrefix (): number { - return this.network.pubKeyHashPrefix + // safety precaution + if (valid && (utf8String.length < 26 || utf8String.length > 35 || pubKeyHash?.length !== 20)) { + throw new Error('Invalid P2PKH address marked valid') + } } getScript (): Script { - if (!this.valid) { - this.validate() - } - if (!this.valid) { throw new Error('InvalidDefiAddress') } @@ -26,20 +25,32 @@ export class P2PKH extends Base58Address { stack: [ OP_CODES.OP_DUP, OP_CODES.OP_HASH160, - new OP_PUSHDATA(Buffer.from(this.hex, 'hex'), 'little'), + new OP_PUSHDATA(this.pubKeyHash as Buffer, 'little'), OP_CODES.OP_EQUALVERIFY, OP_CODES.OP_CHECKSIG ] } } - static to (net: NetworkName | Network, h160: string): P2PKH { - if (h160.length !== Base58Address.DATA_HEX_LENGTH) { - throw new Error('InvalidDataLength') - } - + static to (net: NetworkName | Network, h160: string | Buffer): P2PKH { const network = typeof net === 'string' ? getNetwork(net) : net const address = Bs58.fromHash160(h160, network.pubKeyHashPrefix) - return new P2PKH(network, address, h160, true) + const buffer = typeof h160 === 'string' ? Buffer.from(h160, 'hex') : h160 + return new P2PKH(network, address, buffer, true) + } + + static from (utf8String: string): P2PKH { + let network: Network | undefined + let buffer: Buffer | undefined + let valid = false + try { + const decoded = Bs58.toHash160(utf8String) + buffer = decoded.buffer + network = [MainNet, TestNet, RegTest].find(net => net.pubKeyHashPrefix === decoded.prefix) + valid = true + } catch { + // non b58 string, invalid address + } + return new P2PKH(network, utf8String, buffer, valid) } } diff --git a/packages/jellyfish-address/src/p2sh.ts b/packages/jellyfish-address/src/p2sh.ts index deaf56842c..6d41130fb9 100644 --- a/packages/jellyfish-address/src/p2sh.ts +++ b/packages/jellyfish-address/src/p2sh.ts @@ -1,25 +1,23 @@ import { Bs58 } from '@defichain/jellyfish-crypto' -import { getNetwork, Network, NetworkName } from '@defichain/jellyfish-network' +import { getNetwork, MainNet, Network, NetworkName, RegTest, TestNet } from '@defichain/jellyfish-network' import { Script, OP_CODES, OP_PUSHDATA } from '@defichain/jellyfish-transaction' +import { Address } from './address' -import { Base58Address } from './base58_address' +export class P2SH extends Address { + readonly scriptHash: Buffer | undefined // dSHA256() -export class P2SH extends Base58Address { - static SCRIPT_HASH_LENGTH = 50 // 25 bytes, 50 char + constructor (network: Network | undefined, utf8String: string, scriptHash: Buffer | undefined, valid: boolean = false) { + super(network, utf8String, valid, 'P2SH') - constructor (network: Network, utf8String: string, hex: string, validated: boolean = false) { - super(network, utf8String, hex, validated, 'P2SH') - } + // safety precaution + if (valid && (utf8String.length < 26 || utf8String.length > 35 || scriptHash?.length !== 20)) { + throw new Error('Invalid P2SH address marked valid') + } - getPrefix (): number { - return this.network.scriptHashPrefix + this.scriptHash = scriptHash } getScript (): Script { - if (!this.valid) { - this.validate() - } - if (!this.valid) { throw new Error('InvalidDefiAddress') } @@ -27,19 +25,31 @@ export class P2SH extends Base58Address { return { stack: [ OP_CODES.OP_HASH160, - new OP_PUSHDATA(Buffer.from(this.hex, 'hex'), 'little'), + new OP_PUSHDATA(this.scriptHash as Buffer, 'little'), OP_CODES.OP_EQUAL ] } } - static to (net: NetworkName | Network, h160: string): P2SH { - if (h160.length !== Base58Address.DATA_HEX_LENGTH) { - throw new Error('InvalidDataLength') - } - + static to (net: NetworkName | Network, h160: string | Buffer): P2SH { const network = typeof net === 'string' ? getNetwork(net) : net const address = Bs58.fromHash160(h160, network.scriptHashPrefix) - return new P2SH(network, address, h160, true) + const buffer = typeof h160 === 'string' ? Buffer.from(h160, 'hex') : h160 + return new P2SH(network, address, buffer, true) + } + + static from (utf8String: string): P2SH { + let network: Network | undefined + let buffer: Buffer | undefined + let valid = false + try { + const decoded = Bs58.toHash160(utf8String) + buffer = decoded.buffer + network = [MainNet, TestNet, RegTest].find(net => net.scriptHashPrefix === decoded.prefix) + valid = true + } catch { + // non b58 string, invalid address + } + return new P2SH(network, utf8String, buffer, valid) } } diff --git a/packages/jellyfish-address/src/p2wpkh.ts b/packages/jellyfish-address/src/p2wpkh.ts index 6b0d893263..ca4929279a 100644 --- a/packages/jellyfish-address/src/p2wpkh.ts +++ b/packages/jellyfish-address/src/p2wpkh.ts @@ -1,42 +1,19 @@ import { bech32 } from 'bech32' import { getNetwork, Network, NetworkName } from '@defichain/jellyfish-network' import { Script, OP_CODES, OP_PUSHDATA } from '@defichain/jellyfish-transaction' +import { Address } from './address' -import { Bech32Address } from './bech32_address' -import { Validator } from './address' - -export class P2WPKH extends Bech32Address { - static SAMPLE = 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' - static LENGTH_EXCLUDE_HRP = 39 // exclude hrp and separator - +export class P2WPKH extends Address { // 20 bytes, data only, 40 char pubKeyHash: string static PUB_KEY_HASH_LENGTH = 40 - constructor (network: Network, utf8String: string, pubKeyHash: string, validated: boolean = false) { + constructor (network: Network | undefined, utf8String: string, pubKeyHash: string, validated: boolean = false) { super(network, utf8String, validated, 'P2WPKH') this.pubKeyHash = pubKeyHash } - validators (): Validator[] { - const rawAdd = this.utf8String - return [ - ...super.validators(), - () => (rawAdd.length <= P2WPKH.LENGTH_EXCLUDE_HRP + this.getHrp().length + 1), - () => (rawAdd.length === P2WPKH.LENGTH_EXCLUDE_HRP + this.getHrp().length + 1), - () => (this.pubKeyHash.length === P2WPKH.PUB_KEY_HASH_LENGTH) - ] - } - - getHrp (): string { - return this.network.bech32.hrp - } - getScript (): Script { - if (!this.valid) { - this.validate() - } - if (!this.valid) { throw new Error('InvalidDefiAddress') } @@ -53,9 +30,10 @@ export class P2WPKH extends Bech32Address { * @param net network * @param hex data, public key hash (20 bytes, 40 characters) * @param witnessVersion default 0 + * @throws when h160 input string is not 40 characters long (20 bytes) * @returns */ - static to (net: Network | NetworkName, h160: string, witnessVersion = 0x00): P2WPKH { + static to (net: Network | NetworkName, h160: string, witnessVersion = 0x00): P2WPKH | never { const network: Network = typeof net === 'string' ? getNetwork(net) : net if (h160.length !== P2WPKH.PUB_KEY_HASH_LENGTH) { @@ -68,4 +46,34 @@ export class P2WPKH extends Bech32Address { const utf8 = bech32.encode(network.bech32.hrp, includeVersion) return new P2WPKH(network, utf8, h160, true) } + + /** + * @param {string} raw jellyfish p2wpkh (bech32 address) string + * @throws when decoded prefix is not found in DeFiChain ecosystem (mainnet / testnet / regtest) + * @returns {P2WPKH} + */ + static from (raw: string): P2WPKH { + let valid: boolean + let prefix: string + let data: string = '' + try { + const decoded = bech32.decode(raw) + valid = true + prefix = decoded.prefix + const trimmedVersion = decoded.words.slice(1) + data = Buffer.from(bech32.fromWords(trimmedVersion)).toString('hex') + + if (data.length !== P2WPKH.PUB_KEY_HASH_LENGTH) { + valid = false + } + } catch (e) { + valid = false + } + + const network = (['mainnet', 'testnet', 'regtest'] as NetworkName[]) + .map(netName => getNetwork(netName)) + .find(net => net.bech32.hrp === prefix) + + return new P2WPKH(network, raw, data, valid) + } } diff --git a/packages/jellyfish-address/src/p2wsh.ts b/packages/jellyfish-address/src/p2wsh.ts index 984cf9fa12..7f7c97a749 100644 --- a/packages/jellyfish-address/src/p2wsh.ts +++ b/packages/jellyfish-address/src/p2wsh.ts @@ -1,38 +1,19 @@ import { bech32 } from 'bech32' import { getNetwork, Network, NetworkName } from '@defichain/jellyfish-network' import { Script, OP_CODES, OP_PUSHDATA } from '@defichain/jellyfish-transaction' +import { Address } from './address' -import { Bech32Address } from './bech32_address' -import { Validator } from './address' - -export class P2WSH extends Bech32Address { - // the raw utf8, eg bc1... - // supposed to be 62, regtest prefix is longer - static MAX_LENGTH = 64 - +export class P2WSH extends Address { // 32 bytes, data only, 64 char data: string static SCRIPT_HASH_LENGTH = 64 - constructor (network: Network, utf8String: string, data: string, validated: boolean = false) { - super(network, utf8String, validated, 'P2WSH') + constructor (network: Network, utf8String: string, data: string, valid: boolean = false) { + super(network, utf8String, valid, 'P2WSH') this.data = data } - // bcrt1ncd7qa2cafwv3cpw68vqczg3qj904k2f4lard4wrj50rzkwmagvs3ttd5f - validators (): Validator[] { - return [ - ...super.validators(), - () => (this.utf8String.length <= P2WSH.MAX_LENGTH), - () => (this.data.length === P2WSH.SCRIPT_HASH_LENGTH) - ] - } - getScript (): Script { - if (!this.valid) { - this.validate() - } - if (!this.valid) { throw new Error('InvalidDefiAddress') } @@ -64,4 +45,38 @@ export class P2WSH extends Bech32Address { const utf8 = bech32.encode(network.bech32.hrp, includeVersion) return new P2WSH(network, utf8, hex, true) } + + /** + * @param {string} raw jellyfish p2wpkh (bech32 address) string + * @throws when decoded prefix is not found in DeFiChain ecosystem (mainnet / testnet / regtest) + * @returns {P2WSH} + */ + static from (raw: string): P2WSH { + let valid: boolean + let prefix: string + let data: string = '' + try { + const decoded = bech32.decode(raw) + valid = true + prefix = decoded.prefix + const trimmedVersion = decoded.words.slice(1) + data = Buffer.from(bech32.fromWords(trimmedVersion)).toString('hex') + + if (data.length !== P2WSH.SCRIPT_HASH_LENGTH) { + valid = false + } + } catch (e) { + valid = false + } + + const network = (['mainnet', 'testnet', 'regtest'] as NetworkName[]) + .map(netName => getNetwork(netName)) + .find(net => net.bech32.hrp === prefix) + + if (network === undefined) { + throw new Error('Unexpected network prefix') + } + + return new P2WSH(network, raw, data, valid) + } } diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_account_to_account.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_account_to_account.test.ts index 99d00a72af..51f7a015cb 100644 --- a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_account_to_account.test.ts +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_account_account_to_account.test.ts @@ -13,7 +13,6 @@ import { sendTransaction } from '../test.utils' import { Bech32, HASH160 } from '@defichain/jellyfish-crypto' -import { RegTest } from '@defichain/jellyfish-network' const container = new MasterNodeRegTestContainer() let providers: MockProviders @@ -72,7 +71,7 @@ describe('account.accountToAccount()', () => { const newAddress = await container.getNewAddress() // output token address - const newP2wpkh = P2WPKH.fromAddress(RegTest, newAddress, P2WPKH) + const newP2wpkh = P2WPKH.from(newAddress) const destPubKey = await providers.ellipticPair.publicKey() const script = await providers.elliptic.script() @@ -120,7 +119,7 @@ describe('account.accountToAccount()', () => { const newAddress = await container.getNewAddress() // output token address - const newP2wpkh = P2WPKH.fromAddress(RegTest, newAddress, P2WPKH) + const newP2wpkh = P2WPKH.from(newAddress) const destPubKey = await providers.ellipticPair.publicKey() const script = await providers.elliptic.script() @@ -176,8 +175,8 @@ describe('account.accountToAccount()', () => { const destTwoAddress = await container.getNewAddress() // output token addresses - const destOneP2wpkh = P2WPKH.fromAddress(RegTest, destOneAddress, P2WPKH) - const destTwoP2wpkh = P2WPKH.fromAddress(RegTest, destTwoAddress, P2WPKH) + const destOneP2wpkh = P2WPKH.from(destOneAddress) + const destTwoP2wpkh = P2WPKH.from(destTwoAddress) const destPubKey = await providers.ellipticPair.publicKey() const script = await providers.elliptic.script() diff --git a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_liq_pool_add_liquidity.test.ts b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_liq_pool_add_liquidity.test.ts index 7d5b5d086a..3ca588b81a 100644 --- a/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_liq_pool_add_liquidity.test.ts +++ b/packages/jellyfish-transaction-builder/__tests__/txn/txn_builder_liq_pool_add_liquidity.test.ts @@ -1,6 +1,5 @@ import BigNumber from 'bignumber.js' import { JsonRpcClient } from '@defichain/jellyfish-api-jsonrpc' -import { RegTest } from '@defichain/jellyfish-network' import { OP_CODES } from '@defichain/jellyfish-transaction' import { P2WPKH } from '@defichain/jellyfish-address' import { MasterNodeRegTestContainer } from '@defichain/testcontainers' @@ -133,7 +132,7 @@ describe('liqPool.addLiquidity()', () => { const script = await providers.elliptic.script() const shareDestAddress = await container.getNewAddress() - const shareDest = P2WPKH.fromAddress(RegTest, shareDestAddress, P2WPKH) + const shareDest = P2WPKH.from(shareDestAddress) const tokenAAmount = 2.34 const tokenBAmount = 90.87 diff --git a/packages/jellyfish-wallet/src/wallet_account.ts b/packages/jellyfish-wallet/src/wallet_account.ts index a7495cbf1f..eac3149b88 100644 --- a/packages/jellyfish-wallet/src/wallet_account.ts +++ b/packages/jellyfish-wallet/src/wallet_account.ts @@ -48,7 +48,7 @@ export abstract class WalletAccount { * @return {Script} parsed from address */ addressToScript (address: string): Script { - const parsed = DeFiAddress.from(this.network.name, address) + const parsed = DeFiAddress.from(address) return parsed.getScript() } From c612ad169d4554c06ae0d997af30287b363bb89a Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 28 May 2021 09:44:55 +0800 Subject: [PATCH 3/9] simplified entire jlf address implementation to be more deterministic --- .../jellyfish-address/__tests__/p2pkh.test.ts | 26 +++++-------------- .../jellyfish-address/__tests__/p2sh.test.ts | 26 +++++-------------- .../__tests__/p2wpkh.test.ts | 18 +++++-------- .../jellyfish-address/__tests__/p2wsh.test.ts | 26 +++++-------------- packages/jellyfish-address/src/p2pkh.ts | 6 ++--- packages/jellyfish-address/src/p2sh.ts | 2 +- packages/jellyfish-address/src/p2wsh.ts | 6 +---- 7 files changed, 33 insertions(+), 77 deletions(-) diff --git a/packages/jellyfish-address/__tests__/p2pkh.test.ts b/packages/jellyfish-address/__tests__/p2pkh.test.ts index b2aed72f59..12429054a9 100644 --- a/packages/jellyfish-address/__tests__/p2pkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2pkh.test.ts @@ -11,7 +11,8 @@ describe('P2PKH', () => { regtest: '', invalid: 'JBuS81VT8ouPrT6YS55qoS74D13Cw7h1Y', // edited, removed prefix - invalidChecksum: '8JBuS81VT8ouPrT6YS55qoS74D13Cw7h1X' // edited checksum (last char) + invalidChecksum: '8JBuS81VT8ouPrT6YS55qoS74D13Cw7h1X', // edited checksum (last char) + validBtcAddress: '1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2' // valid btc p2pkh } beforeAll(async () => { @@ -44,6 +45,11 @@ describe('P2PKH', () => { expect(regtest.constructor.name).toStrictEqual('P2PKH') expect(regtest.network).toStrictEqual(RegTest) }) + + it('non DFI (network) address should be invalid', () => { + const btc = DeFiAddress.from(p2pkhFixture.validBtcAddress) + expect(btc.valid).toBeFalsy() + }) }) describe('from() - invalid address', () => { @@ -52,14 +58,6 @@ describe('P2PKH', () => { expect(invalid.valid).toBeFalsy() }) - it('should be able to validate in address prefix with network', () => { - // valid address, used on different network - const p2pkh = DeFiAddress.from(p2pkhFixture.mainnet) - expect(p2pkh.valid).toBeFalsy() - // expect(p2pkh.type).toStrictEqual('P2PKH') // invalid address guessed type is not promising, as p2pkh and p2sh are versy similar - expect(p2pkh.network).toStrictEqual(TestNet) - }) - it('should get the type precisely', () => { const invalid = DeFiAddress.from(p2pkhFixture.invalidChecksum) expect(invalid.valid).toBeFalsy() @@ -95,16 +93,6 @@ describe('P2PKH', () => { }) describe('getScript()', () => { - it('should refuse to build ops code stack for invalid address', () => { - const invalid = DeFiAddress.from(p2pkhFixture.mainnet) - expect(invalid.valid).toBeFalsy() - try { - invalid.getScript() - } catch (e) { - expect(e.message).toStrictEqual('InvalidDefiAddress') - } - }) - it('should be able to build script', async () => { const p2pkh = DeFiAddress.from(p2pkhFixture.mainnet) const scriptStack = p2pkh.getScript() diff --git a/packages/jellyfish-address/__tests__/p2sh.test.ts b/packages/jellyfish-address/__tests__/p2sh.test.ts index 82142ba5e8..6243022ab0 100644 --- a/packages/jellyfish-address/__tests__/p2sh.test.ts +++ b/packages/jellyfish-address/__tests__/p2sh.test.ts @@ -11,7 +11,8 @@ describe('P2SH', () => { regtest: '', invalid: 'FFPENo7FPMJpDV6fUcfo4QfkZrfrV1Uf8', // edited mainnet address, removed prefix - invalidChecksum: 'dFFPENo7FPMJpDV6fUcfo4QfkZrfrV1Uf' // edited mainnet address, trim checksum + invalidChecksum: 'dFFPENo7FPMJpDV6fUcfo4QfkZrfrV1Uf', // edited mainnet address, trim checksum + validBtcAddress: '3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy' // valid btc p2sh } beforeAll(async () => { @@ -44,6 +45,11 @@ describe('P2SH', () => { expect(regtest.constructor.name).toStrictEqual('P2SH') expect(regtest.network).toStrictEqual(RegTest) }) + + it('non DFI (network) address should be invalid', () => { + const btc = DeFiAddress.from(p2shFixture.validBtcAddress) + expect(btc.valid).toBeFalsy() + }) }) describe('from() - invalid address', () => { @@ -52,14 +58,6 @@ describe('P2SH', () => { expect(invalid.valid).toBeFalsy() }) - it('should be able to validate in address prefix with network', () => { - // valid address, used on different network - const p2sh = DeFiAddress.from(p2shFixture.mainnet) - expect(p2sh.valid).toBeFalsy() - // expect(p2sh.type).toStrictEqual('P2SH') // invalid address guessed type is not promising, as p2sh and p2sh are versy similar - expect(p2sh.network).toStrictEqual(TestNet) - }) - it('should get the type precisely', () => { const invalid = DeFiAddress.from(p2shFixture.invalidChecksum) expect(invalid.valid).toBeFalsy() @@ -96,16 +94,6 @@ describe('P2SH', () => { }) describe('getScript()', () => { - it('should refuse to build ops code stack for invalid address', () => { - const invalid = DeFiAddress.from(p2shFixture.mainnet) - expect(invalid.valid).toBeFalsy() - try { - invalid.getScript() - } catch (e) { - expect(e.message).toStrictEqual('InvalidDefiAddress') - } - }) - it('should be able to build script', async () => { const p2sh = DeFiAddress.from(p2shFixture.mainnet) const scriptStack = p2sh.getScript() diff --git a/packages/jellyfish-address/__tests__/p2wpkh.test.ts b/packages/jellyfish-address/__tests__/p2wpkh.test.ts index 31c9e54b48..8e522a5ac9 100644 --- a/packages/jellyfish-address/__tests__/p2wpkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wpkh.test.ts @@ -11,7 +11,8 @@ describe('P2WPKH', () => { regtest: '', trimmedPrefix: 'f1qpe7q4vvtxpdunpazvmwqdh3xlnatfdt2xr8mpv', // edited mainnet address with broken prefix - invalid: 'df1pe7q4vvtxpdunpazvmwqdh3xlnatfdt2ncrpqo' // edited mainnet address, letter 'o' + invalid: 'df1pe7q4vvtxpdunpazvmwqdh3xlnatfdt2ncrpqo', // edited mainnet address, letter 'o' + validBtcAddress: 'bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq' // valid btc p2wph } beforeAll(async () => { @@ -44,6 +45,11 @@ describe('P2WPKH', () => { expect(regtest.constructor.name).toStrictEqual('P2WPKH') expect(regtest.network).toStrictEqual(RegTest) }) + + it('non DFI (network) address should be invalid', () => { + const btc = DeFiAddress.from(p2wpkhFixture.validBtcAddress) + expect(btc.valid).toBeFalsy() + }) }) describe('from() - invalid address', () => { @@ -95,16 +101,6 @@ describe('P2WPKH', () => { }) describe('getScript()', () => { - it('should refuse to build ops code stack for invalid address', () => { - const invalid = DeFiAddress.from(p2wpkhFixture.mainnet) - expect(invalid.valid).toBeFalsy() - try { - invalid.getScript() - } catch (e) { - expect(e.message).toStrictEqual('InvalidDefiAddress') - } - }) - it('should be able to build script', async () => { const p2wpkh = DeFiAddress.from(p2wpkhFixture.mainnet) const scriptStack = p2wpkh.getScript() diff --git a/packages/jellyfish-address/__tests__/p2wsh.test.ts b/packages/jellyfish-address/__tests__/p2wsh.test.ts index c196640c48..1fbf69ecbc 100644 --- a/packages/jellyfish-address/__tests__/p2wsh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wsh.test.ts @@ -9,7 +9,8 @@ describe('P2WSH', () => { regtest: 'bcrt1qncd7qa2cafwv3cpw68vqczg3qj904k2f4lard4wrj50rzkwmagvssfsq3t', trimmedPrefix: 'f1qncd7qa2cafwv3cpw68vqczg3qj904k2f4lard4wrj50rzkwmagvsfkkf88', // edited mainnet valid address, broken prefix - invalid: 'df1qncd7qa2cafwv3cpw68vqczg3qj904k2f4lard4wrj50rzkwmagvsfkkf8o' // edited mainnet address, letter 'o' + invalid: 'df1qncd7qa2cafwv3cpw68vqczg3qj904k2f4lard4wrj50rzkwmagvsfkkf8o', // edited mainnet address, letter 'o' + validBtcAddress: 'bc1qncd7qa2cafwv3cpw68vqczg3qj904k2f4lard4wrj50rzkwmagvsfkkf8o' } describe('from() - valid address', () => { @@ -34,6 +35,11 @@ describe('P2WSH', () => { expect(regtest.constructor.name).toStrictEqual('P2WSH') expect(regtest.network).toStrictEqual(RegTest) }) + + it('non DFI (network) address should be invalid', () => { + const btc = DeFiAddress.from(p2wshFixture.validBtcAddress) + expect(btc.valid).toBeFalsy() + }) }) describe('from() - invalid address', () => { @@ -46,14 +52,6 @@ describe('P2WSH', () => { const invalid = DeFiAddress.from(p2wshFixture.invalid) expect(invalid.valid).toBeFalsy() }) - - it('should be able to validate in address prefix with network', () => { - // valid address, used on different network - const p2sh = DeFiAddress.from(p2wshFixture.mainnet) - expect(p2sh.valid).toBeFalsy() - // expect(p2sh.type).toStrictEqual('P2WSH') // invalid address guessed type is not promising, as p2wsh and p2sh are versy similar - expect(p2sh.network).toStrictEqual(TestNet) - }) }) describe('to()', () => { @@ -85,16 +83,6 @@ describe('P2WSH', () => { }) describe('getScript()', () => { - it('should refuse to build ops code stack for invalid address', () => { - const invalid = DeFiAddress.from(p2wshFixture.mainnet) - expect(invalid.valid).toBeFalsy() - try { - invalid.getScript() - } catch (e) { - expect(e.message).toStrictEqual('InvalidDefiAddress') - } - }) - it('should be able to build script', async () => { const p2wsh = DeFiAddress.from(p2wshFixture.mainnet) const scriptStack = p2wsh.getScript() diff --git a/packages/jellyfish-address/src/p2pkh.ts b/packages/jellyfish-address/src/p2pkh.ts index 9093e40c28..e6469f144d 100644 --- a/packages/jellyfish-address/src/p2pkh.ts +++ b/packages/jellyfish-address/src/p2pkh.ts @@ -1,4 +1,4 @@ -import { Bs58 } from '@defichain/jellyfish-crypto/src' +import { Bs58 } from '@defichain/jellyfish-crypto' import { getNetwork, MainNet, Network, NetworkName, RegTest, TestNet } from '@defichain/jellyfish-network' import { Script, OP_CODES, OP_PUSHDATA } from '@defichain/jellyfish-transaction' import { Address } from './address' @@ -11,7 +11,7 @@ export class P2PKH extends Address { this.pubKeyHash = pubKeyHash // safety precaution - if (valid && (utf8String.length < 26 || utf8String.length > 35 || pubKeyHash?.length !== 20)) { + if (valid && (network === undefined || utf8String.length < 26 || utf8String.length > 35 || pubKeyHash?.length !== 20)) { throw new Error('Invalid P2PKH address marked valid') } } @@ -47,7 +47,7 @@ export class P2PKH extends Address { const decoded = Bs58.toHash160(utf8String) buffer = decoded.buffer network = [MainNet, TestNet, RegTest].find(net => net.pubKeyHashPrefix === decoded.prefix) - valid = true + valid = network !== undefined } catch { // non b58 string, invalid address } diff --git a/packages/jellyfish-address/src/p2sh.ts b/packages/jellyfish-address/src/p2sh.ts index 6d41130fb9..7d3dd402ca 100644 --- a/packages/jellyfish-address/src/p2sh.ts +++ b/packages/jellyfish-address/src/p2sh.ts @@ -46,7 +46,7 @@ export class P2SH extends Address { const decoded = Bs58.toHash160(utf8String) buffer = decoded.buffer network = [MainNet, TestNet, RegTest].find(net => net.scriptHashPrefix === decoded.prefix) - valid = true + valid = network !== undefined } catch { // non b58 string, invalid address } diff --git a/packages/jellyfish-address/src/p2wsh.ts b/packages/jellyfish-address/src/p2wsh.ts index 7f7c97a749..5ed43892a7 100644 --- a/packages/jellyfish-address/src/p2wsh.ts +++ b/packages/jellyfish-address/src/p2wsh.ts @@ -8,7 +8,7 @@ export class P2WSH extends Address { data: string static SCRIPT_HASH_LENGTH = 64 - constructor (network: Network, utf8String: string, data: string, valid: boolean = false) { + constructor (network: Network | undefined, utf8String: string, data: string, valid: boolean = false) { super(network, utf8String, valid, 'P2WSH') this.data = data } @@ -73,10 +73,6 @@ export class P2WSH extends Address { .map(netName => getNetwork(netName)) .find(net => net.bech32.hrp === prefix) - if (network === undefined) { - throw new Error('Unexpected network prefix') - } - return new P2WSH(network, raw, data, valid) } } From 338afae7c72fb508d4795bfb7ca241b63d18973a Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 28 May 2021 09:50:18 +0800 Subject: [PATCH 4/9] remove unnecessary type --- packages/jellyfish-address/src/p2wpkh.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jellyfish-address/src/p2wpkh.ts b/packages/jellyfish-address/src/p2wpkh.ts index ca4929279a..2f6f262f30 100644 --- a/packages/jellyfish-address/src/p2wpkh.ts +++ b/packages/jellyfish-address/src/p2wpkh.ts @@ -33,7 +33,7 @@ export class P2WPKH extends Address { * @throws when h160 input string is not 40 characters long (20 bytes) * @returns */ - static to (net: Network | NetworkName, h160: string, witnessVersion = 0x00): P2WPKH | never { + static to (net: Network | NetworkName, h160: string, witnessVersion = 0x00): P2WPKH { const network: Network = typeof net === 'string' ? getNetwork(net) : net if (h160.length !== P2WPKH.PUB_KEY_HASH_LENGTH) { From 52f7dd270371ccc9132f07e873db6c998cf67dca Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 28 May 2021 10:39:26 +0800 Subject: [PATCH 5/9] clean up + jsdoc --- .../jellyfish-address/__tests__/p2pkh.test.ts | 12 ++-- .../jellyfish-address/__tests__/p2sh.test.ts | 12 ++-- .../__tests__/p2wpkh.test.ts | 14 +---- .../jellyfish-address/__tests__/p2wsh.test.ts | 10 ++-- packages/jellyfish-address/src/address.ts | 6 +- packages/jellyfish-address/src/p2pkh.ts | 25 ++++++-- packages/jellyfish-address/src/p2sh.ts | 24 +++++--- packages/jellyfish-address/src/p2wpkh.ts | 59 +++++++++---------- packages/jellyfish-address/src/p2wsh.ts | 49 ++++++++------- 9 files changed, 111 insertions(+), 100 deletions(-) diff --git a/packages/jellyfish-address/__tests__/p2pkh.test.ts b/packages/jellyfish-address/__tests__/p2pkh.test.ts index 12429054a9..495438e295 100644 --- a/packages/jellyfish-address/__tests__/p2pkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2pkh.test.ts @@ -45,11 +45,6 @@ describe('P2PKH', () => { expect(regtest.constructor.name).toStrictEqual('P2PKH') expect(regtest.network).toStrictEqual(RegTest) }) - - it('non DFI (network) address should be invalid', () => { - const btc = DeFiAddress.from(p2pkhFixture.validBtcAddress) - expect(btc.valid).toBeFalsy() - }) }) describe('from() - invalid address', () => { @@ -58,10 +53,15 @@ describe('P2PKH', () => { expect(invalid.valid).toBeFalsy() }) - it('should get the type precisely', () => { + it('invalid checksum', () => { const invalid = DeFiAddress.from(p2pkhFixture.invalidChecksum) expect(invalid.valid).toBeFalsy() }) + + it('non DFI (network) address should be invalid', () => { + const btc = DeFiAddress.from(p2pkhFixture.validBtcAddress) + expect(btc.valid).toBeFalsy() + }) }) describe('to()', () => { diff --git a/packages/jellyfish-address/__tests__/p2sh.test.ts b/packages/jellyfish-address/__tests__/p2sh.test.ts index 6243022ab0..4de7c7dec6 100644 --- a/packages/jellyfish-address/__tests__/p2sh.test.ts +++ b/packages/jellyfish-address/__tests__/p2sh.test.ts @@ -45,11 +45,6 @@ describe('P2SH', () => { expect(regtest.constructor.name).toStrictEqual('P2SH') expect(regtest.network).toStrictEqual(RegTest) }) - - it('non DFI (network) address should be invalid', () => { - const btc = DeFiAddress.from(p2shFixture.validBtcAddress) - expect(btc.valid).toBeFalsy() - }) }) describe('from() - invalid address', () => { @@ -58,10 +53,15 @@ describe('P2SH', () => { expect(invalid.valid).toBeFalsy() }) - it('should get the type precisely', () => { + it('invalid checksum', () => { const invalid = DeFiAddress.from(p2shFixture.invalidChecksum) expect(invalid.valid).toBeFalsy() }) + + it('non DFI (network) address should be invalid', () => { + const btc = DeFiAddress.from(p2shFixture.validBtcAddress) + expect(btc.valid).toBeFalsy() + }) }) describe('to()', () => { diff --git a/packages/jellyfish-address/__tests__/p2wpkh.test.ts b/packages/jellyfish-address/__tests__/p2wpkh.test.ts index 8e522a5ac9..467f0fea8d 100644 --- a/packages/jellyfish-address/__tests__/p2wpkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wpkh.test.ts @@ -45,11 +45,6 @@ describe('P2WPKH', () => { expect(regtest.constructor.name).toStrictEqual('P2WPKH') expect(regtest.network).toStrictEqual(RegTest) }) - - it('non DFI (network) address should be invalid', () => { - const btc = DeFiAddress.from(p2wpkhFixture.validBtcAddress) - expect(btc.valid).toBeFalsy() - }) }) describe('from() - invalid address', () => { @@ -63,12 +58,9 @@ describe('P2WPKH', () => { expect(invalid.valid).toBeFalsy() }) - it('should be able to validate in address prefix with network', () => { - // valid address, used on different network - const p2wpkh = DeFiAddress.from(p2wpkhFixture.mainnet) - expect(p2wpkh.valid).toBeFalsy() - // expect(p2wpkh.type).toStrictEqual('P2WPKH') // invalid address guessed type is not promising, as p2wpkh and p2wpkh are versy similar - expect(p2wpkh.network).toStrictEqual(TestNet) + it('non DFI (network) address should be invalid', () => { + const btc = DeFiAddress.from(p2wpkhFixture.validBtcAddress) + expect(btc.valid).toBeFalsy() }) }) diff --git a/packages/jellyfish-address/__tests__/p2wsh.test.ts b/packages/jellyfish-address/__tests__/p2wsh.test.ts index 1fbf69ecbc..5dbb5b46d1 100644 --- a/packages/jellyfish-address/__tests__/p2wsh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wsh.test.ts @@ -35,11 +35,6 @@ describe('P2WSH', () => { expect(regtest.constructor.name).toStrictEqual('P2WSH') expect(regtest.network).toStrictEqual(RegTest) }) - - it('non DFI (network) address should be invalid', () => { - const btc = DeFiAddress.from(p2wshFixture.validBtcAddress) - expect(btc.valid).toBeFalsy() - }) }) describe('from() - invalid address', () => { @@ -52,6 +47,11 @@ describe('P2WSH', () => { const invalid = DeFiAddress.from(p2wshFixture.invalid) expect(invalid.valid).toBeFalsy() }) + + it('non DFI (network) address should be invalid', () => { + const btc = DeFiAddress.from(p2wshFixture.validBtcAddress) + expect(btc.valid).toBeFalsy() + }) }) describe('to()', () => { diff --git a/packages/jellyfish-address/src/address.ts b/packages/jellyfish-address/src/address.ts index 9607b7dc38..4dedd21bd2 100644 --- a/packages/jellyfish-address/src/address.ts +++ b/packages/jellyfish-address/src/address.ts @@ -7,18 +7,20 @@ export type Validator = () => boolean export abstract class Address { readonly network?: Network readonly utf8String: string + readonly buffer?: Buffer readonly type: AddressType readonly valid: boolean - constructor (network: Network | undefined, utf8String: string, valid: boolean, type: AddressType) { + constructor (network: Network | undefined, utf8String: string, buffer: Buffer | undefined, valid: boolean, type: AddressType) { this.network = network this.utf8String = utf8String this.valid = valid this.type = type + this.buffer = buffer } /** * should throw if called with address.valid === false */ - abstract getScript (): Script | never + abstract getScript (): Script } diff --git a/packages/jellyfish-address/src/p2pkh.ts b/packages/jellyfish-address/src/p2pkh.ts index e6469f144d..34882ed9f8 100644 --- a/packages/jellyfish-address/src/p2pkh.ts +++ b/packages/jellyfish-address/src/p2pkh.ts @@ -4,14 +4,16 @@ import { Script, OP_CODES, OP_PUSHDATA } from '@defichain/jellyfish-transaction' import { Address } from './address' export class P2PKH extends Address { - readonly pubKeyHash: Buffer | undefined // H160(33 bytes pubKey) - constructor (network: Network | undefined, utf8String: string, pubKeyHash: Buffer | undefined, valid: boolean) { - super(network, utf8String, valid, 'P2PKH') - this.pubKeyHash = pubKeyHash + super(network, utf8String, pubKeyHash, valid, 'P2PKH') // safety precaution - if (valid && (network === undefined || utf8String.length < 26 || utf8String.length > 35 || pubKeyHash?.length !== 20)) { + if (valid && ( + network === undefined || + utf8String.length < 26 || + utf8String.length > 35 || + pubKeyHash?.length !== 20 + )) { throw new Error('Invalid P2PKH address marked valid') } } @@ -25,13 +27,20 @@ export class P2PKH extends Address { stack: [ OP_CODES.OP_DUP, OP_CODES.OP_HASH160, - new OP_PUSHDATA(this.pubKeyHash as Buffer, 'little'), + new OP_PUSHDATA(this.buffer as Buffer, 'little'), OP_CODES.OP_EQUALVERIFY, OP_CODES.OP_CHECKSIG ] } } + /** + * @param {NetworkName|Network} net mainnet | testnet | regtest + * @param {Buffer|string} h160 data, public key hash (20 bytes, 40 characters) + * @param {number} witnessVersion default 0, not more than 1 byte + * @throws when h160 input string is not 40 characters long (20 bytes) + * @returns {P2WSH} + */ static to (net: NetworkName | Network, h160: string | Buffer): P2PKH { const network = typeof net === 'string' ? getNetwork(net) : net const address = Bs58.fromHash160(h160, network.pubKeyHashPrefix) @@ -39,6 +48,10 @@ export class P2PKH extends Address { return new P2PKH(network, address, buffer, true) } + /** + * @param {string} utf8String jellyfish p2pkh (base58 address) string, 26-35 characters + * @returns {P2PKH} + */ static from (utf8String: string): P2PKH { let network: Network | undefined let buffer: Buffer | undefined diff --git a/packages/jellyfish-address/src/p2sh.ts b/packages/jellyfish-address/src/p2sh.ts index 7d3dd402ca..463f5f6edc 100644 --- a/packages/jellyfish-address/src/p2sh.ts +++ b/packages/jellyfish-address/src/p2sh.ts @@ -4,17 +4,17 @@ import { Script, OP_CODES, OP_PUSHDATA } from '@defichain/jellyfish-transaction' import { Address } from './address' export class P2SH extends Address { - readonly scriptHash: Buffer | undefined // dSHA256() - constructor (network: Network | undefined, utf8String: string, scriptHash: Buffer | undefined, valid: boolean = false) { - super(network, utf8String, valid, 'P2SH') + super(network, utf8String, scriptHash, valid, 'P2SH') // safety precaution - if (valid && (utf8String.length < 26 || utf8String.length > 35 || scriptHash?.length !== 20)) { + if (valid && ( + utf8String.length < 26 || + utf8String.length > 35 || + scriptHash?.length !== 20 + )) { throw new Error('Invalid P2SH address marked valid') } - - this.scriptHash = scriptHash } getScript (): Script { @@ -25,12 +25,18 @@ export class P2SH extends Address { return { stack: [ OP_CODES.OP_HASH160, - new OP_PUSHDATA(this.scriptHash as Buffer, 'little'), + new OP_PUSHDATA(this.buffer as Buffer, 'little'), OP_CODES.OP_EQUAL ] } } + /** + * @param {NetworkName|Network} net mainnet | testnet | regtest + * @param {Buffer|string} h160 data, script hash (32 bytes, 64 characters) + * @throws when h160 input string is not 64 characters long (32 bytes) + * @returns {P2SH} + */ static to (net: NetworkName | Network, h160: string | Buffer): P2SH { const network = typeof net === 'string' ? getNetwork(net) : net const address = Bs58.fromHash160(h160, network.scriptHashPrefix) @@ -38,6 +44,10 @@ export class P2SH extends Address { return new P2SH(network, address, buffer, true) } + /** + * @param {string} raw jellyfish p2sh (base58 address) string, 26-35 characters + * @returns {P2SH} + */ static from (utf8String: string): P2SH { let network: Network | undefined let buffer: Buffer | undefined diff --git a/packages/jellyfish-address/src/p2wpkh.ts b/packages/jellyfish-address/src/p2wpkh.ts index 2f6f262f30..19967d3872 100644 --- a/packages/jellyfish-address/src/p2wpkh.ts +++ b/packages/jellyfish-address/src/p2wpkh.ts @@ -1,16 +1,22 @@ import { bech32 } from 'bech32' -import { getNetwork, Network, NetworkName } from '@defichain/jellyfish-network' +import { getNetwork, MainNet, Network, NetworkName, RegTest, TestNet } from '@defichain/jellyfish-network' import { Script, OP_CODES, OP_PUSHDATA } from '@defichain/jellyfish-transaction' import { Address } from './address' export class P2WPKH extends Address { - // 20 bytes, data only, 40 char - pubKeyHash: string - static PUB_KEY_HASH_LENGTH = 40 + static PUB_KEY_HASH_LENGTH = 20 // count in bytes - constructor (network: Network | undefined, utf8String: string, pubKeyHash: string, validated: boolean = false) { - super(network, utf8String, validated, 'P2WPKH') - this.pubKeyHash = pubKeyHash + constructor (network: Network | undefined, utf8String: string, pubKeyHash: Buffer | undefined, valid: boolean = false) { + super(network, utf8String, pubKeyHash, valid, 'P2WPKH') + + // safety precaution + if (valid && ( + network === undefined || + pubKeyHash?.length !== P2WPKH.PUB_KEY_HASH_LENGTH + )) { + console.log(this) + throw new Error('Invalid P2WPKH address marked valid') + } } getScript (): Script { @@ -21,59 +27,50 @@ export class P2WPKH extends Address { return { stack: [ OP_CODES.OP_0, - new OP_PUSHDATA(Buffer.from(this.pubKeyHash, 'hex'), 'little') + new OP_PUSHDATA(this.buffer as Buffer, 'little') ] } } /** - * @param net network - * @param hex data, public key hash (20 bytes, 40 characters) - * @param witnessVersion default 0 + * @param {NetworkName|Network} net mainnet | testnet | regtest + * @param {Buffer|string} h160 data, public key hash (20 bytes, 40 characters) * @throws when h160 input string is not 40 characters long (20 bytes) - * @returns + * @returns {P2WPKH} */ - static to (net: Network | NetworkName, h160: string, witnessVersion = 0x00): P2WPKH { + static to (net: Network | NetworkName, h160: string | Buffer, witnessVersion = 0x00): P2WPKH { const network: Network = typeof net === 'string' ? getNetwork(net) : net + const numbers = typeof h160 === 'string' ? Buffer.from(h160, 'hex') : h160 - if (h160.length !== P2WPKH.PUB_KEY_HASH_LENGTH) { + if (numbers.length !== P2WPKH.PUB_KEY_HASH_LENGTH) { throw new Error('InvalidPubKeyHashLength') } - const numbers = Buffer.from(h160, 'hex') const fiveBitsWords = bech32.toWords(numbers) const includeVersion = [witnessVersion, ...fiveBitsWords] const utf8 = bech32.encode(network.bech32.hrp, includeVersion) - return new P2WPKH(network, utf8, h160, true) + return new P2WPKH(network, utf8, numbers, true) } /** * @param {string} raw jellyfish p2wpkh (bech32 address) string - * @throws when decoded prefix is not found in DeFiChain ecosystem (mainnet / testnet / regtest) * @returns {P2WPKH} */ static from (raw: string): P2WPKH { - let valid: boolean - let prefix: string - let data: string = '' + let network: Network | undefined + let data: Buffer | undefined + let valid: boolean = false try { const decoded = bech32.decode(raw) - valid = true - prefix = decoded.prefix const trimmedVersion = decoded.words.slice(1) - data = Buffer.from(bech32.fromWords(trimmedVersion)).toString('hex') + data = Buffer.from(bech32.fromWords(trimmedVersion)) - if (data.length !== P2WPKH.PUB_KEY_HASH_LENGTH) { - valid = false - } + network = [MainNet, TestNet, RegTest].find(net => net.bech32.hrp === decoded.prefix) + valid = data.length === P2WPKH.PUB_KEY_HASH_LENGTH && network !== undefined } catch (e) { - valid = false + // invalid address, fail to decode bech32 } - const network = (['mainnet', 'testnet', 'regtest'] as NetworkName[]) - .map(netName => getNetwork(netName)) - .find(net => net.bech32.hrp === prefix) - return new P2WPKH(network, raw, data, valid) } } diff --git a/packages/jellyfish-address/src/p2wsh.ts b/packages/jellyfish-address/src/p2wsh.ts index 5ed43892a7..d528b07cfa 100644 --- a/packages/jellyfish-address/src/p2wsh.ts +++ b/packages/jellyfish-address/src/p2wsh.ts @@ -1,16 +1,18 @@ import { bech32 } from 'bech32' -import { getNetwork, Network, NetworkName } from '@defichain/jellyfish-network' +import { getNetwork, MainNet, Network, NetworkName, RegTest, TestNet } from '@defichain/jellyfish-network' import { Script, OP_CODES, OP_PUSHDATA } from '@defichain/jellyfish-transaction' import { Address } from './address' export class P2WSH extends Address { - // 32 bytes, data only, 64 char - data: string - static SCRIPT_HASH_LENGTH = 64 + static SCRIPT_HASH_LENGTH = 32 // count in bytes - constructor (network: Network | undefined, utf8String: string, data: string, valid: boolean = false) { - super(network, utf8String, valid, 'P2WSH') - this.data = data + constructor (network: Network | undefined, utf8String: string, scriptHash: Buffer | undefined, valid: boolean = false) { + super(network, utf8String, scriptHash, valid, 'P2WSH') + + // safety precaution + if (valid && (network === undefined || scriptHash?.length !== P2WSH.SCRIPT_HASH_LENGTH)) { + throw new Error('Invalid P2WPKH address marked valid') + } } getScript (): Script { @@ -21,58 +23,53 @@ export class P2WSH extends Address { return { stack: [ OP_CODES.OP_0, - new OP_PUSHDATA(Buffer.from(this.data, 'hex'), 'little') + new OP_PUSHDATA(this.buffer as Buffer, 'little') ] } } /** - * @param net network - * @param hex data, redeem script (32 bytes, 64 characters) - * @param witnessVersion default 0 - * @returns + * @param {NetworkName|Network} net mainnet | testnet | regtest + * @param {Buffer|string} data data, script hash (32 bytes, 64 characters) + * @throws when data input string is not 64 characters long (32 bytes) + * @returns {P2WSH} */ - static to (net: Network | NetworkName, hex: string, witnessVersion = 0x00): P2WSH { + static to (net: Network | NetworkName, data: string | Buffer, witnessVersion = 0x00): P2WSH { const network: Network = typeof net === 'string' ? getNetwork(net) : net + const numbers = typeof data === 'string' ? Buffer.from(data, 'hex') : data - if (hex.length !== P2WSH.SCRIPT_HASH_LENGTH) { + if (numbers.length !== P2WSH.SCRIPT_HASH_LENGTH) { throw new Error('InvalidScriptHashLength') } - const numbers = Buffer.from(hex, 'hex') const fiveBitsWords = bech32.toWords(numbers) const includeVersion = [witnessVersion, ...fiveBitsWords] const utf8 = bech32.encode(network.bech32.hrp, includeVersion) - return new P2WSH(network, utf8, hex, true) + return new P2WSH(network, utf8, numbers, true) } /** * @param {string} raw jellyfish p2wpkh (bech32 address) string - * @throws when decoded prefix is not found in DeFiChain ecosystem (mainnet / testnet / regtest) * @returns {P2WSH} */ static from (raw: string): P2WSH { let valid: boolean let prefix: string - let data: string = '' + let data: Buffer | undefined + let network: Network | undefined try { const decoded = bech32.decode(raw) valid = true prefix = decoded.prefix const trimmedVersion = decoded.words.slice(1) - data = Buffer.from(bech32.fromWords(trimmedVersion)).toString('hex') + data = Buffer.from(bech32.fromWords(trimmedVersion)) - if (data.length !== P2WSH.SCRIPT_HASH_LENGTH) { - valid = false - } + network = [MainNet, TestNet, RegTest].find(net => net.bech32.hrp === prefix) + valid = data.length === P2WSH.SCRIPT_HASH_LENGTH && network !== undefined } catch (e) { valid = false } - const network = (['mainnet', 'testnet', 'regtest'] as NetworkName[]) - .map(netName => getNetwork(netName)) - .find(net => net.bech32.hrp === prefix) - return new P2WSH(network, raw, data, valid) } } From 8f9acc270311c5346328b0afe581386653d98be0 Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 28 May 2021 11:20:28 +0800 Subject: [PATCH 6/9] add test, correct error message --- .../jellyfish-address/__tests__/p2pkh.test.ts | 31 ++++++++++++++++++- .../jellyfish-address/__tests__/p2sh.test.ts | 31 ++++++++++++++++++- .../__tests__/p2wpkh.test.ts | 24 +++++++++++++- .../jellyfish-address/__tests__/p2wsh.test.ts | 24 +++++++++++++- packages/jellyfish-address/src/p2sh.ts | 1 + packages/jellyfish-address/src/p2wsh.ts | 7 +++-- 6 files changed, 112 insertions(+), 6 deletions(-) diff --git a/packages/jellyfish-address/__tests__/p2pkh.test.ts b/packages/jellyfish-address/__tests__/p2pkh.test.ts index 495438e295..58861a6ead 100644 --- a/packages/jellyfish-address/__tests__/p2pkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2pkh.test.ts @@ -1,7 +1,7 @@ import { MainNet, RegTest, TestNet } from '@defichain/jellyfish-network' import { OP_CODES } from '@defichain/jellyfish-transaction' import { RegTestContainer } from '@defichain/testcontainers' -import { DeFiAddress, P2PKH } from '../src' +import { Address, DeFiAddress, P2PKH } from '../src' describe('P2PKH', () => { const container = new RegTestContainer() @@ -105,4 +105,33 @@ describe('P2PKH', () => { expect(scriptStack.stack[4]).toStrictEqual(OP_CODES.OP_CHECKSIG) }) }) + + describe('constructor()', () => { + let validAddress: Address + + beforeAll(() => { + validAddress = DeFiAddress.from(p2pkhFixture.mainnet) + expect(validAddress.valid).toBeTruthy() + }) + it('should not be able to instantiate `valid` address - without network', () => { + expect(() => { + // eslint-disable-next-line no-new + new P2PKH(undefined, validAddress.utf8String, validAddress.buffer, true) + }).toThrow('Invalid P2PKH address marked valid') + }) + + it('should not be able to instantiate `valid` address - with invalid address length', () => { + expect(() => { + // eslint-disable-next-line no-new + new P2PKH(MainNet, validAddress.utf8String.slice(1), validAddress.buffer, true) + }).toThrow('Invalid P2PKH address marked valid') + }) + + it('should not be able to instantiate `valid` address - with invalid buffer length', () => { + expect(() => { + // eslint-disable-next-line no-new + new P2PKH(MainNet, validAddress.utf8String, (validAddress.buffer as Buffer).slice(1), true) + }).toThrow('Invalid P2PKH address marked valid') + }) + }) }) diff --git a/packages/jellyfish-address/__tests__/p2sh.test.ts b/packages/jellyfish-address/__tests__/p2sh.test.ts index 4de7c7dec6..127286babd 100644 --- a/packages/jellyfish-address/__tests__/p2sh.test.ts +++ b/packages/jellyfish-address/__tests__/p2sh.test.ts @@ -1,7 +1,7 @@ import { MainNet, RegTest, TestNet } from '@defichain/jellyfish-network' import { OP_CODES } from '@defichain/jellyfish-transaction' import { RegTestContainer } from '@defichain/testcontainers' -import { DeFiAddress, P2SH } from '../src' +import { Address, DeFiAddress, P2SH } from '../src' describe('P2SH', () => { const container = new RegTestContainer() @@ -104,4 +104,33 @@ describe('P2SH', () => { expect(scriptStack.stack[2]).toStrictEqual(OP_CODES.OP_EQUAL) }) }) + + describe('constructor()', () => { + let validAddress: Address + + beforeAll(() => { + validAddress = DeFiAddress.from(p2shFixture.mainnet) + expect(validAddress.valid).toBeTruthy() + }) + it('should not be able to instantiate `valid` address - without network', () => { + expect(() => { + // eslint-disable-next-line no-new + new P2SH(undefined, validAddress.utf8String, validAddress.buffer, true) + }).toThrow('Invalid P2SH address marked valid') + }) + + it('should not be able to instantiate `valid` address - with invalid address length', () => { + expect(() => { + // eslint-disable-next-line no-new + new P2SH(MainNet, validAddress.utf8String.slice(10), validAddress.buffer, true) // less than min length + }).toThrow('Invalid P2SH address marked valid') + }) + + it('should not be able to instantiate `valid` address - with invalid buffer length', () => { + expect(() => { + // eslint-disable-next-line no-new + new P2SH(MainNet, validAddress.utf8String, (validAddress.buffer as Buffer).slice(1), true) + }).toThrow('Invalid P2SH address marked valid') + }) + }) }) diff --git a/packages/jellyfish-address/__tests__/p2wpkh.test.ts b/packages/jellyfish-address/__tests__/p2wpkh.test.ts index 467f0fea8d..93cbda9f21 100644 --- a/packages/jellyfish-address/__tests__/p2wpkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wpkh.test.ts @@ -1,7 +1,7 @@ import { MainNet, RegTest, TestNet } from '@defichain/jellyfish-network' import { OP_CODES } from '@defichain/jellyfish-transaction' import { RegTestContainer } from '@defichain/testcontainers' -import { DeFiAddress, P2WPKH } from '../src' +import { Address, DeFiAddress, P2WPKH } from '../src' describe('P2WPKH', () => { const container = new RegTestContainer() @@ -102,4 +102,26 @@ describe('P2WPKH', () => { expect(scriptStack.stack[1].type).toStrictEqual('OP_PUSHDATA') }) }) + + describe('constructor()', () => { + let validAddress: Address + + beforeAll(() => { + validAddress = DeFiAddress.from(p2wpkhFixture.mainnet) + expect(validAddress.valid).toBeTruthy() + }) + it('should not be able to instantiate `valid` address - without network', () => { + expect(() => { + // eslint-disable-next-line no-new + new P2WPKH(undefined, validAddress.utf8String, validAddress.buffer, true) + }).toThrow('Invalid P2WPKH address marked valid') + }) + + it('should not be able to instantiate `valid` address - with invalid buffer length', () => { + expect(() => { + // eslint-disable-next-line no-new + new P2WPKH(MainNet, validAddress.utf8String, (validAddress.buffer as Buffer).slice(1), true) + }).toThrow('Invalid P2WPKH address marked valid') + }) + }) }) diff --git a/packages/jellyfish-address/__tests__/p2wsh.test.ts b/packages/jellyfish-address/__tests__/p2wsh.test.ts index 5dbb5b46d1..830a64b0ba 100644 --- a/packages/jellyfish-address/__tests__/p2wsh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wsh.test.ts @@ -1,6 +1,6 @@ import { MainNet, RegTest, TestNet } from '@defichain/jellyfish-network' import { OP_CODES } from '@defichain/jellyfish-transaction' -import { DeFiAddress, P2WSH } from '../src' +import { Address, DeFiAddress, P2WSH } from '../src' describe('P2WSH', () => { const p2wshFixture = { @@ -92,4 +92,26 @@ describe('P2WSH', () => { expect(scriptStack.stack[1].type).toStrictEqual('OP_PUSHDATA') }) }) + + describe('constructor()', () => { + let validAddress: Address + + beforeAll(() => { + validAddress = DeFiAddress.from(p2wshFixture.mainnet) + expect(validAddress.valid).toBeTruthy() + }) + it('should not be able to instantiate `valid` address - without network', () => { + expect(() => { + // eslint-disable-next-line no-new + new P2WSH(undefined, validAddress.utf8String, validAddress.buffer, true) + }).toThrow('Invalid P2WSH address marked valid') + }) + + it('should not be able to instantiate `valid` address - with invalid buffer length', () => { + expect(() => { + // eslint-disable-next-line no-new + new P2WSH(MainNet, validAddress.utf8String, (validAddress.buffer as Buffer).slice(1), true) + }).toThrow('Invalid P2WSH address marked valid') + }) + }) }) diff --git a/packages/jellyfish-address/src/p2sh.ts b/packages/jellyfish-address/src/p2sh.ts index 463f5f6edc..aa6879825a 100644 --- a/packages/jellyfish-address/src/p2sh.ts +++ b/packages/jellyfish-address/src/p2sh.ts @@ -9,6 +9,7 @@ export class P2SH extends Address { // safety precaution if (valid && ( + network === undefined || utf8String.length < 26 || utf8String.length > 35 || scriptHash?.length !== 20 diff --git a/packages/jellyfish-address/src/p2wsh.ts b/packages/jellyfish-address/src/p2wsh.ts index d528b07cfa..6d3fd5c999 100644 --- a/packages/jellyfish-address/src/p2wsh.ts +++ b/packages/jellyfish-address/src/p2wsh.ts @@ -10,8 +10,11 @@ export class P2WSH extends Address { super(network, utf8String, scriptHash, valid, 'P2WSH') // safety precaution - if (valid && (network === undefined || scriptHash?.length !== P2WSH.SCRIPT_HASH_LENGTH)) { - throw new Error('Invalid P2WPKH address marked valid') + if (valid && ( + network === undefined || + scriptHash?.length !== P2WSH.SCRIPT_HASH_LENGTH + )) { + throw new Error('Invalid P2WSH address marked valid') } } From 2cc0b406a78638ab3592d25381d6452c7dea5a98 Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 28 May 2021 11:53:49 +0800 Subject: [PATCH 7/9] refactor, further enhance test --- .../__tests__/address.test.ts | 57 +++++++++++++++++++ .../jellyfish-address/__tests__/p2pkh.test.ts | 7 --- .../jellyfish-address/__tests__/p2sh.test.ts | 6 -- .../__tests__/p2wpkh.test.ts | 6 -- .../jellyfish-address/__tests__/p2wsh.test.ts | 6 -- packages/jellyfish-address/src/address.ts | 21 +++++++ packages/jellyfish-address/src/p2pkh.ts | 9 +-- packages/jellyfish-address/src/p2sh.ts | 9 +-- packages/jellyfish-address/src/p2wpkh.ts | 14 +---- packages/jellyfish-address/src/p2wsh.ts | 12 +--- 10 files changed, 87 insertions(+), 60 deletions(-) create mode 100644 packages/jellyfish-address/__tests__/address.test.ts diff --git a/packages/jellyfish-address/__tests__/address.test.ts b/packages/jellyfish-address/__tests__/address.test.ts new file mode 100644 index 0000000000..14097f9043 --- /dev/null +++ b/packages/jellyfish-address/__tests__/address.test.ts @@ -0,0 +1,57 @@ +import { RegTest } from '@defichain/jellyfish-network' +import { Script } from '@defichain/jellyfish-transaction' +import { Address } from '../src' + +class DummyAddress extends Address { + getScript (): Script { + return { + stack: [] + } + } +} + +describe('constructor()', () => { + let validAddress: Address + beforeAll(() => { + validAddress = new DummyAddress(RegTest, 'any', Buffer.from('0x1234', 'hex'), true, 'Unknown') + expect(validAddress.valid).toBeTruthy() + }) + + it('should not be able to instantiate `valid` address - without network', () => { + expect(() => { + // eslint-disable-next-line no-new + new DummyAddress(undefined, validAddress.utf8String, validAddress.getBuffer(), true, 'Unknown') + }).toThrow('InvalidDefiAddress') + }) + + it('should not be able to instantiate `valid` address - without buffer (data)', () => { + expect(() => { + // eslint-disable-next-line no-new + new DummyAddress(validAddress.getNetwork(), validAddress.utf8String, undefined, true, 'Unknown') + }).toThrow('InvalidDefiAddress') + }) +}) + +it('getNetwork()', () => { + const validAddress = new DummyAddress(RegTest, 'any', Buffer.from('1234', 'hex'), true, 'Unknown') + expect(validAddress.valid).toBeTruthy() + expect(validAddress.getNetwork()).toStrictEqual(RegTest) + + const invalidAddress = new DummyAddress(RegTest, 'any', Buffer.from('1234', 'hex'), false, 'Unknown') + expect(invalidAddress.valid).toBeFalsy() + expect(() => { + invalidAddress.getNetwork() + }).toThrow('InvalidDefiAddress') +}) + +it('getBuffer()', () => { + const validAddress = new DummyAddress(RegTest, 'any', Buffer.from('1234', 'hex'), true, 'Unknown') + expect(validAddress.valid).toBeTruthy() + expect(validAddress.getBuffer().toString('hex')).toStrictEqual('1234') + + const invalidAddress = new DummyAddress(RegTest, 'any', Buffer.from('1234', 'hex'), false, 'Unknown') + expect(invalidAddress.valid).toBeFalsy() + expect(() => { + invalidAddress.getBuffer() + }).toThrow('InvalidDefiAddress') +}) diff --git a/packages/jellyfish-address/__tests__/p2pkh.test.ts b/packages/jellyfish-address/__tests__/p2pkh.test.ts index 58861a6ead..b26879f43d 100644 --- a/packages/jellyfish-address/__tests__/p2pkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2pkh.test.ts @@ -108,17 +108,10 @@ describe('P2PKH', () => { describe('constructor()', () => { let validAddress: Address - beforeAll(() => { validAddress = DeFiAddress.from(p2pkhFixture.mainnet) expect(validAddress.valid).toBeTruthy() }) - it('should not be able to instantiate `valid` address - without network', () => { - expect(() => { - // eslint-disable-next-line no-new - new P2PKH(undefined, validAddress.utf8String, validAddress.buffer, true) - }).toThrow('Invalid P2PKH address marked valid') - }) it('should not be able to instantiate `valid` address - with invalid address length', () => { expect(() => { diff --git a/packages/jellyfish-address/__tests__/p2sh.test.ts b/packages/jellyfish-address/__tests__/p2sh.test.ts index 127286babd..1b39a45c3b 100644 --- a/packages/jellyfish-address/__tests__/p2sh.test.ts +++ b/packages/jellyfish-address/__tests__/p2sh.test.ts @@ -112,12 +112,6 @@ describe('P2SH', () => { validAddress = DeFiAddress.from(p2shFixture.mainnet) expect(validAddress.valid).toBeTruthy() }) - it('should not be able to instantiate `valid` address - without network', () => { - expect(() => { - // eslint-disable-next-line no-new - new P2SH(undefined, validAddress.utf8String, validAddress.buffer, true) - }).toThrow('Invalid P2SH address marked valid') - }) it('should not be able to instantiate `valid` address - with invalid address length', () => { expect(() => { diff --git a/packages/jellyfish-address/__tests__/p2wpkh.test.ts b/packages/jellyfish-address/__tests__/p2wpkh.test.ts index 93cbda9f21..e2fb5d8c9d 100644 --- a/packages/jellyfish-address/__tests__/p2wpkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wpkh.test.ts @@ -110,12 +110,6 @@ describe('P2WPKH', () => { validAddress = DeFiAddress.from(p2wpkhFixture.mainnet) expect(validAddress.valid).toBeTruthy() }) - it('should not be able to instantiate `valid` address - without network', () => { - expect(() => { - // eslint-disable-next-line no-new - new P2WPKH(undefined, validAddress.utf8String, validAddress.buffer, true) - }).toThrow('Invalid P2WPKH address marked valid') - }) it('should not be able to instantiate `valid` address - with invalid buffer length', () => { expect(() => { diff --git a/packages/jellyfish-address/__tests__/p2wsh.test.ts b/packages/jellyfish-address/__tests__/p2wsh.test.ts index 830a64b0ba..f1154cf615 100644 --- a/packages/jellyfish-address/__tests__/p2wsh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wsh.test.ts @@ -100,12 +100,6 @@ describe('P2WSH', () => { validAddress = DeFiAddress.from(p2wshFixture.mainnet) expect(validAddress.valid).toBeTruthy() }) - it('should not be able to instantiate `valid` address - without network', () => { - expect(() => { - // eslint-disable-next-line no-new - new P2WSH(undefined, validAddress.utf8String, validAddress.buffer, true) - }).toThrow('Invalid P2WSH address marked valid') - }) it('should not be able to instantiate `valid` address - with invalid buffer length', () => { expect(() => { diff --git a/packages/jellyfish-address/src/address.ts b/packages/jellyfish-address/src/address.ts index 4dedd21bd2..9e75710982 100644 --- a/packages/jellyfish-address/src/address.ts +++ b/packages/jellyfish-address/src/address.ts @@ -17,10 +17,31 @@ export abstract class Address { this.valid = valid this.type = type this.buffer = buffer + + if (valid && ( + this.network === undefined || + this.buffer === undefined + )) { + throw new Error('InvalidDefiAddress') + } } /** * should throw if called with address.valid === false */ abstract getScript (): Script + + getNetwork (): Network { + if (!this.valid) { + throw new Error('InvalidDefiAddress') + } + return this.network as Network + } + + getBuffer (): Buffer { + if (!this.valid) { + throw new Error('InvalidDefiAddress') + } + return this.buffer as Buffer + } } diff --git a/packages/jellyfish-address/src/p2pkh.ts b/packages/jellyfish-address/src/p2pkh.ts index 34882ed9f8..f2dbe9df0a 100644 --- a/packages/jellyfish-address/src/p2pkh.ts +++ b/packages/jellyfish-address/src/p2pkh.ts @@ -9,25 +9,20 @@ export class P2PKH extends Address { // safety precaution if (valid && ( - network === undefined || utf8String.length < 26 || utf8String.length > 35 || pubKeyHash?.length !== 20 )) { - throw new Error('Invalid P2PKH address marked valid') + throw new Error('InvalidDefiAddress') } } getScript (): Script { - if (!this.valid) { - throw new Error('InvalidDefiAddress') - } - return { stack: [ OP_CODES.OP_DUP, OP_CODES.OP_HASH160, - new OP_PUSHDATA(this.buffer as Buffer, 'little'), + new OP_PUSHDATA(this.getBuffer(), 'little'), OP_CODES.OP_EQUALVERIFY, OP_CODES.OP_CHECKSIG ] diff --git a/packages/jellyfish-address/src/p2sh.ts b/packages/jellyfish-address/src/p2sh.ts index aa6879825a..4109f43f85 100644 --- a/packages/jellyfish-address/src/p2sh.ts +++ b/packages/jellyfish-address/src/p2sh.ts @@ -9,24 +9,19 @@ export class P2SH extends Address { // safety precaution if (valid && ( - network === undefined || utf8String.length < 26 || utf8String.length > 35 || scriptHash?.length !== 20 )) { - throw new Error('Invalid P2SH address marked valid') + throw new Error('InvalidDefiAddress') } } getScript (): Script { - if (!this.valid) { - throw new Error('InvalidDefiAddress') - } - return { stack: [ OP_CODES.OP_HASH160, - new OP_PUSHDATA(this.buffer as Buffer, 'little'), + new OP_PUSHDATA(this.getBuffer(), 'little'), OP_CODES.OP_EQUAL ] } diff --git a/packages/jellyfish-address/src/p2wpkh.ts b/packages/jellyfish-address/src/p2wpkh.ts index 19967d3872..2c39d9d6ea 100644 --- a/packages/jellyfish-address/src/p2wpkh.ts +++ b/packages/jellyfish-address/src/p2wpkh.ts @@ -10,24 +10,16 @@ export class P2WPKH extends Address { super(network, utf8String, pubKeyHash, valid, 'P2WPKH') // safety precaution - if (valid && ( - network === undefined || - pubKeyHash?.length !== P2WPKH.PUB_KEY_HASH_LENGTH - )) { - console.log(this) - throw new Error('Invalid P2WPKH address marked valid') + if (valid && pubKeyHash?.length !== P2WPKH.PUB_KEY_HASH_LENGTH) { + throw new Error('InvalidDefiAddress') } } getScript (): Script { - if (!this.valid) { - throw new Error('InvalidDefiAddress') - } - return { stack: [ OP_CODES.OP_0, - new OP_PUSHDATA(this.buffer as Buffer, 'little') + new OP_PUSHDATA(this.getBuffer(), 'little') ] } } diff --git a/packages/jellyfish-address/src/p2wsh.ts b/packages/jellyfish-address/src/p2wsh.ts index 6d3fd5c999..bed763ce40 100644 --- a/packages/jellyfish-address/src/p2wsh.ts +++ b/packages/jellyfish-address/src/p2wsh.ts @@ -9,24 +9,16 @@ export class P2WSH extends Address { constructor (network: Network | undefined, utf8String: string, scriptHash: Buffer | undefined, valid: boolean = false) { super(network, utf8String, scriptHash, valid, 'P2WSH') - // safety precaution - if (valid && ( - network === undefined || - scriptHash?.length !== P2WSH.SCRIPT_HASH_LENGTH - )) { + if (valid && scriptHash?.length !== P2WSH.SCRIPT_HASH_LENGTH) { throw new Error('Invalid P2WSH address marked valid') } } getScript (): Script { - if (!this.valid) { - throw new Error('InvalidDefiAddress') - } - return { stack: [ OP_CODES.OP_0, - new OP_PUSHDATA(this.buffer as Buffer, 'little') + new OP_PUSHDATA(this.getBuffer(), 'little') ] } } From 8d6920475b95ab93158eb5b4879b549f2bc6b283 Mon Sep 17 00:00:00 2001 From: ivan Date: Fri, 28 May 2021 11:58:03 +0800 Subject: [PATCH 8/9] fix test, standardize err msg --- packages/jellyfish-address/__tests__/p2pkh.test.ts | 6 +++--- packages/jellyfish-address/__tests__/p2sh.test.ts | 4 ++-- packages/jellyfish-address/__tests__/p2wpkh.test.ts | 2 +- packages/jellyfish-address/__tests__/p2wsh.test.ts | 2 +- packages/jellyfish-address/src/p2wsh.ts | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/jellyfish-address/__tests__/p2pkh.test.ts b/packages/jellyfish-address/__tests__/p2pkh.test.ts index b26879f43d..bebdb0bb45 100644 --- a/packages/jellyfish-address/__tests__/p2pkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2pkh.test.ts @@ -116,15 +116,15 @@ describe('P2PKH', () => { it('should not be able to instantiate `valid` address - with invalid address length', () => { expect(() => { // eslint-disable-next-line no-new - new P2PKH(MainNet, validAddress.utf8String.slice(1), validAddress.buffer, true) - }).toThrow('Invalid P2PKH address marked valid') + new P2PKH(MainNet, validAddress.utf8String.slice(10), validAddress.buffer, true) + }).toThrow('InvalidDefiAddress') }) it('should not be able to instantiate `valid` address - with invalid buffer length', () => { expect(() => { // eslint-disable-next-line no-new new P2PKH(MainNet, validAddress.utf8String, (validAddress.buffer as Buffer).slice(1), true) - }).toThrow('Invalid P2PKH address marked valid') + }).toThrow('InvalidDefiAddress') }) }) }) diff --git a/packages/jellyfish-address/__tests__/p2sh.test.ts b/packages/jellyfish-address/__tests__/p2sh.test.ts index 1b39a45c3b..38d57c787d 100644 --- a/packages/jellyfish-address/__tests__/p2sh.test.ts +++ b/packages/jellyfish-address/__tests__/p2sh.test.ts @@ -117,14 +117,14 @@ describe('P2SH', () => { expect(() => { // eslint-disable-next-line no-new new P2SH(MainNet, validAddress.utf8String.slice(10), validAddress.buffer, true) // less than min length - }).toThrow('Invalid P2SH address marked valid') + }).toThrow('InvalidDefiAddress') }) it('should not be able to instantiate `valid` address - with invalid buffer length', () => { expect(() => { // eslint-disable-next-line no-new new P2SH(MainNet, validAddress.utf8String, (validAddress.buffer as Buffer).slice(1), true) - }).toThrow('Invalid P2SH address marked valid') + }).toThrow('InvalidDefiAddress') }) }) }) diff --git a/packages/jellyfish-address/__tests__/p2wpkh.test.ts b/packages/jellyfish-address/__tests__/p2wpkh.test.ts index e2fb5d8c9d..98cc1924b1 100644 --- a/packages/jellyfish-address/__tests__/p2wpkh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wpkh.test.ts @@ -115,7 +115,7 @@ describe('P2WPKH', () => { expect(() => { // eslint-disable-next-line no-new new P2WPKH(MainNet, validAddress.utf8String, (validAddress.buffer as Buffer).slice(1), true) - }).toThrow('Invalid P2WPKH address marked valid') + }).toThrow('InvalidDefiAddress') }) }) }) diff --git a/packages/jellyfish-address/__tests__/p2wsh.test.ts b/packages/jellyfish-address/__tests__/p2wsh.test.ts index f1154cf615..43a620ccc4 100644 --- a/packages/jellyfish-address/__tests__/p2wsh.test.ts +++ b/packages/jellyfish-address/__tests__/p2wsh.test.ts @@ -105,7 +105,7 @@ describe('P2WSH', () => { expect(() => { // eslint-disable-next-line no-new new P2WSH(MainNet, validAddress.utf8String, (validAddress.buffer as Buffer).slice(1), true) - }).toThrow('Invalid P2WSH address marked valid') + }).toThrow('InvalidDefiAddress') }) }) }) diff --git a/packages/jellyfish-address/src/p2wsh.ts b/packages/jellyfish-address/src/p2wsh.ts index bed763ce40..f79a362430 100644 --- a/packages/jellyfish-address/src/p2wsh.ts +++ b/packages/jellyfish-address/src/p2wsh.ts @@ -10,7 +10,7 @@ export class P2WSH extends Address { super(network, utf8String, scriptHash, valid, 'P2WSH') if (valid && scriptHash?.length !== P2WSH.SCRIPT_HASH_LENGTH) { - throw new Error('Invalid P2WSH address marked valid') + throw new Error('InvalidDefiAddress') } } From c62462486fff4409e23c376b10e72c01f1307ec7 Mon Sep 17 00:00:00 2001 From: ivan Date: Mon, 31 May 2021 09:27:08 +0800 Subject: [PATCH 9/9] jsdoc update --- packages/jellyfish-address/src/index.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/jellyfish-address/src/index.ts b/packages/jellyfish-address/src/index.ts index 25b39967d5..36b02cb059 100644 --- a/packages/jellyfish-address/src/index.ts +++ b/packages/jellyfish-address/src/index.ts @@ -11,9 +11,8 @@ export * from './p2wpkh' export * from './p2wsh' /** - * @param net to be validated against the decoded one from the raw address - * @param address raw human readable address (utf-8) - * @returns DefiAddress or a child class + * @param {string} address raw human readable address (utf-8) + * @returns DefiAddress child class */ function from (address: string): Address { let guess: Address = P2PKH.from(address)