-
Notifications
You must be signed in to change notification settings - Fork 37
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
guess P2SH / P2PKH / P2WSH / P2WPKH type (#189)
Co-authored-by: Fuxing Loh <[email protected]> Co-authored-by: Fuxing Loh <[email protected]>
- Loading branch information
1 parent
7f906b1
commit 1bfeab9
Showing
26 changed files
with
1,251 additions
and
1 deletion.
There are no files selected for viewing
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,4 +28,7 @@ node_modules | |
dist | ||
*.tgz | ||
|
||
# debug log | ||
lerna-debug.log | ||
|
||
coverage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
[![npm](https://img.shields.io/npm/v/@defichain/jellyfish-address)](https://www.npmjs.com/package/@defichain/jellyfish-address/v/latest) | ||
[![npm@next](https://img.shields.io/npm/v/@defichain/jellyfish-address/next)](https://www.npmjs.com/package/@defichain/jellyfish-address/v/next) | ||
|
||
# @defichain/jellyfish-address | ||
|
||
DeFi blockchain address type builder, parser, validator library. |
57 changes: 57 additions & 0 deletions
57
packages/jellyfish-address/__tests__/base58_address.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import { Script } from '@defichain/jellyfish-transaction/src/tx' | ||
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' | ||
} | ||
|
||
describe('extensible, should work for any defined network protocol', () => { | ||
it('fromAddress() - valid', () => { | ||
const valid = Base58Address.fromAddress<DummyB58Address>(dummyNetwork, b58Fixture.p2pkh, DummyB58Address) | ||
expect(valid.validate()).toBeTruthy() | ||
}) | ||
|
||
it('fromAddress() - invalid character set', () => { | ||
const invalid = Base58Address.fromAddress<DummyB58Address>(dummyNetwork, 'invalid b58 address', DummyB58Address) | ||
expect(invalid.validate()).toBeFalsy() | ||
}) | ||
|
||
it('fromAddress() - invalid prefix', () => { | ||
const invalid = Base58Address.fromAddress<DummyB58Address>(dummyNetwork, b58Fixture.p2sh, DummyB58Address) | ||
expect(invalid.validate()).toBeFalsy() | ||
|
||
const valid = Base58Address.fromAddress<DummyB58Address>(dummyNetwork, b58Fixture.p2pkh, DummyB58Address) | ||
expect(valid.validate()).toBeTruthy() | ||
}) | ||
}) | ||
}) |
51 changes: 51 additions & 0 deletions
51
packages/jellyfish-address/__tests__/bech32_address.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { Script } from '@defichain/jellyfish-transaction/src/tx' | ||
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' | ||
} | ||
|
||
describe('extensible, should work for any defined network protocol', () => { | ||
it('fromAddress() - valid', () => { | ||
const valid = Bech32Address.fromAddress<DummyBech32Address>(dummyNetwork, bech32Fixture.p2wpkh, DummyBech32Address) | ||
expect(valid.validate()).toBeTruthy() | ||
}) | ||
|
||
it('fromAddress() - invalid character set', () => { | ||
const invalid = Bech32Address.fromAddress<DummyBech32Address>(dummyNetwork, bech32Fixture.invalidCharset, DummyBech32Address) | ||
expect(invalid.validate()).toBeFalsy() | ||
}) | ||
|
||
it('fromAddress() - invalid prefix', () => { | ||
const invalid = Bech32Address.fromAddress<DummyBech32Address>(dummyNetwork, bech32Fixture.invalidPrefix, DummyBech32Address) | ||
expect(invalid.validate()).toBeFalsy() | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
import bs58 from 'bs58' | ||
import { MainNet, RegTest, TestNet } from '@defichain/jellyfish-network' | ||
import { OP_CODES } from '@defichain/jellyfish-transaction/src/script' | ||
import { RegTestContainer } from '@defichain/testcontainers' | ||
import * as DeFiAddress from '../src' | ||
import { P2PKH } from '../src' | ||
|
||
describe('P2PKH', () => { | ||
const container = new RegTestContainer() | ||
const p2pkhFixture = { | ||
mainnet: '8JBuS81VT8ouPrT6YS55qoS74D13Cw7h1Y', | ||
testnet: '7LMorkhKTDjbES6DfRxX2RiNMbeemUkxmp', | ||
regtest: '', | ||
|
||
invalid: 'JBuS81VT8ouPrT6YS55qoS74D13Cw7h1Y', // edited, removed prefix | ||
invalidChecksum: '8JBuS81VT8ouPrT6YS55qoS74D13Cw7h1X' // edited checksum (last char) | ||
} | ||
|
||
beforeAll(async () => { | ||
await container.start() | ||
await container.waitForReady() | ||
p2pkhFixture.regtest = await container.getNewAddress('', 'legacy') | ||
}) | ||
|
||
afterAll(async () => await container.stop()) | ||
|
||
describe('from() - valid address', () => { | ||
it('should get the type precisely', () => { | ||
const p2pkh = DeFiAddress.from('mainnet', p2pkhFixture.mainnet) | ||
expect(p2pkh.valid).toBeTruthy() | ||
expect(p2pkh.type).toBe('P2PKH') | ||
expect(p2pkh.constructor.name).toBe('P2PKH') | ||
expect(p2pkh.network).toBe(MainNet) | ||
}) | ||
|
||
it('should work for all recognized network type', () => { | ||
const testnet = DeFiAddress.from('testnet', p2pkhFixture.testnet) | ||
expect(testnet.valid).toBeTruthy() | ||
expect(testnet.type).toBe('P2PKH') | ||
expect(testnet.constructor.name).toBe('P2PKH') | ||
expect(testnet.network).toBe(TestNet) | ||
|
||
const regtest = DeFiAddress.from('regtest', p2pkhFixture.regtest) | ||
expect(regtest.valid).toBeTruthy() | ||
expect(regtest.type).toBe('P2PKH') | ||
expect(regtest.constructor.name).toBe('P2PKH') | ||
expect(regtest.network).toBe(RegTest) | ||
}) | ||
}) | ||
|
||
describe('from() - invalid address', () => { | ||
it('should be able to validate in address prefix with network', () => { | ||
const invalid = DeFiAddress.from('mainnet', 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) | ||
expect(p2pkh.valid).toBeFalsy() | ||
// expect(p2pkh.type).toBe('P2PKH') // invalid address guessed type is not promising, as p2pkh and p2sh are versy similar | ||
expect(p2pkh.network).toBe(TestNet) | ||
}) | ||
|
||
it('should get the type precisely', () => { | ||
const invalid = DeFiAddress.from('mainnet', p2pkhFixture.invalidChecksum) | ||
expect(invalid.valid).toBeFalsy() | ||
}) | ||
}) | ||
|
||
describe('to()', () => { | ||
it('should be able to build a new address using a public key hash (20 bytes, 40 char hex string)', () => { | ||
const pubKeyHash = '134b0749882c225e8647df3a3417507c6f5b2797' | ||
expect(pubKeyHash.length).toEqual(40) | ||
|
||
const p2pkh = P2PKH.to('regtest', pubKeyHash) | ||
expect(p2pkh.type).toEqual('P2PKH') | ||
expect(p2pkh.valid).toBeTruthy() | ||
|
||
const scriptStack = p2pkh.getScript() | ||
expect(scriptStack.stack.length).toEqual(5) | ||
expect(scriptStack.stack[0]).toEqual(OP_CODES.OP_DUP) | ||
expect(scriptStack.stack[1]).toEqual(OP_CODES.OP_HASH160) | ||
expect(scriptStack.stack[2]).toEqual(OP_CODES.OP_PUSHDATA_HEX_LE(pubKeyHash)) | ||
expect(scriptStack.stack[3]).toEqual(OP_CODES.OP_EQUALVERIFY) | ||
expect(scriptStack.stack[4]).toEqual(OP_CODES.OP_CHECKSIG) | ||
}) | ||
|
||
it('should reject invalid data - not 20 bytes data', () => { | ||
const pubKeyHash = '134b0749882c225e8647df3a3417507c6f5b27' | ||
expect(pubKeyHash.length).toEqual(38) | ||
|
||
expect(() => { | ||
P2PKH.to('regtest', pubKeyHash) | ||
}).toThrow('InvalidDataLength') | ||
}) | ||
}) | ||
|
||
describe('getScript()', () => { | ||
it('should refuse to build ops code stack for invalid address', () => { | ||
const invalid = DeFiAddress.from('testnet', p2pkhFixture.mainnet) | ||
expect(invalid.valid).toBeFalsy() | ||
try { | ||
invalid.getScript() | ||
} catch (e) { | ||
expect(e.message).toBe('InvalidDefiAddress') | ||
} | ||
}) | ||
|
||
it('should be able to build script', async () => { | ||
const p2pkh = DeFiAddress.from('mainnet', p2pkhFixture.mainnet) | ||
const scriptStack = p2pkh.getScript() | ||
|
||
expect(scriptStack.stack.length).toEqual(5) | ||
expect(scriptStack.stack[0]).toEqual(OP_CODES.OP_DUP) | ||
expect(scriptStack.stack[1]).toEqual(OP_CODES.OP_HASH160) | ||
expect(scriptStack.stack[2].type).toEqual('OP_PUSHDATA') // tested in `to()` | ||
expect(scriptStack.stack[3]).toEqual(OP_CODES.OP_EQUALVERIFY) | ||
expect(scriptStack.stack[4]).toEqual(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).toEqual(0) | ||
expect(p2pkh.valid).toBeFalsy() | ||
|
||
const isValid = p2pkh.validate() | ||
expect(p2pkh.validatorPassed).toEqual(5) | ||
expect(isValid).toBeTruthy() | ||
}) | ||
|
||
it('guess()', () => { | ||
const p2pkh = DeFiAddress.guess(p2pkhFixture.mainnet) | ||
expect(p2pkh.valid).toBeTruthy() | ||
expect(p2pkh.type).toBe('P2PKH') | ||
expect(p2pkh.constructor.name).toBe('P2PKH') | ||
expect(p2pkh.network).toBe(MainNet) | ||
}) | ||
}) |
Oops, something went wrong.