UNPKG

aladinnetwork-blockstack

Version:

The Aladin Javascript library for authentication, identity, and storage.

389 lines (359 loc) 16 kB
import { exec } from 'child_process' import { promisify } from 'util' import test from 'tape' import { transactions, config, network, hexStringToECPair, ecPairToAddress } from '../../../src' import { hash160 } from '../../../src/operations/utils' const ALADIN_TEST = !!process.env.ALADIN_TEST async function pExec(cmd: string) { try { const { stdout } = await promisify(exec)(cmd) return stdout } catch (error) { console.error(`Failed to run "${cmd}": ${error}`) throw error } } function initializeAladinCore(): Promise<any> { if (ALADIN_TEST) { // running with an external test suite return Promise.resolve() } else { return pExec('docker pull quay.io/blockstack/integrationtests:master') .then(() => { console.log('Pulled latest docker image') return pExec(`docker stop test-bsk-core ; docker rm test-bsk-core ; rm -rf /tmp/.aladin_int_test`) .catch(() => true) }) .then(() => pExec('docker run --name test-bsk-core -dt ' + '-p 16268:16268 -p 18332:18332 -p 30001:30001 ' + '-e BLOCKSTACK_TEST_CLIENT_RPC_PORT=16268 ' + '-e BLOCKSTACK_TEST_CLIENT_BIND=0.0.0.0 ' + '-e BLOCKSTACK_TEST_BITCOIND_ALLOWIP=172.17.0.0/16 ' + '-e BLOCKSTACK_WEB_TEST_BIND=0.0.0.0 ' + 'quay.io/blockstack/integrationtests:master ' + 'blockstack-test-scenario --interactive 2 ' + 'blockstack_integration_tests.scenarios.portal_test_env')) .then(() => { console.log('Started regtest container, waiting until initialized') return pExec('docker logs -f test-bsk-core | grep -q \'Test finished\'') }) .then(() => { // try to avoid race with nextBlock() console.log('Wait 10 seconds for test server to bind') return new Promise(resolve => setTimeout(resolve, 10000)) }) } } function nextBlock(numBlocks?: number): Promise<any> { if (ALADIN_TEST) { const options: {method: string, body?: string} = { method: 'POST' } if (!!numBlocks) { options.body = `numblocks=${numBlocks}` } const url = 'http://localhost:30001/nextblock' return fetch(url, options) .then((resp) => { if (resp.status >= 400) { throw new Error(`Bad test framework status: ${resp.status}`) } else { return true } }) } else { return new Promise(resolve => setTimeout(resolve, 30000)) } } function shutdownAladinCore(): Promise<any> { if (ALADIN_TEST) { return Promise.resolve() } else { return pExec('docker stop test-bsk-core') } } export function runIntegrationTests() { test('registerIdName', (t) => { t.plan(8) config.network = network.defaults.LOCAL_REGTEST const myNet = config.network const dest = '19238846ac60fa62f8f8bb8898b03df79bc6112600181f36061835ad8934086001' const destAddress = ecPairToAddress(hexStringToECPair(dest)) const btcDest = '897f1b92041b798580f96b8be379053f6276f04eb7590a9042a62059d46d6fc301' const btcDestAddress = ecPairToAddress(hexStringToECPair(btcDest)) const payer = 'bb68eda988e768132bc6c7ca73a87fb9b0918e9a38d3618b74099be25f7cab7d01' const secondOwner = '54164693e3803223f7fa9a004997bfbf1475f5c44f65593fa45c6783086dafec01' const transferDestination = ecPairToAddress(hexStringToECPair(secondOwner)) const renewalDestination = 'myPgwEX2ddQxPPqWBRkXNqL3TwuWbY29DJ' const zfTest = '$ORIGIN aaron.id\n$TTL 3600\n_http._tcp URI 10 1 ' + `"https://gaia.blockstacktest.org/hub/${destAddress}/0/profile.json"` const zfTest2 = '$ORIGIN aaron.id\n$TTL 3600\n_http._tcp URI 10 1 ' + `"https://gaia.blockstacktest.org/hub/${destAddress}/3/profile.json"` const renewalZF = '$ORIGIN aaron.id\n$TTL 3600\n_http._tcp URI 10 1 ' + `"https://gaia.blockstacktest.org/hub/${destAddress}/4/profile.json"` initializeAladinCore() .then(() => { console.log('Aladin Core initialized.') return transactions.makePreorder('aaron.id', destAddress, payer) }) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('PREORDER broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => transactions.makeRegister('aaron.id', destAddress, payer, zfTest)) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('REGISTER broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => myNet.broadcastZoneFile(zfTest)) .then(() => fetch(`${myNet.aladinAPIUrl}/v1/names/aaron.id`)) .then(resp => resp.json()) .then((nameInfo) => { t.equal(myNet.coerceAddress(nameInfo.address), destAddress, `aaron.id should be owned by ${destAddress}`) t.equal(nameInfo.zonefile, zfTest, 'zonefile should be properly set') }) .then(() => transactions.makeUpdate('aaron.id', dest, payer, zfTest2)) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('UPDATE broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => myNet.broadcastZoneFile(zfTest2)) .then(() => fetch(`${myNet.aladinAPIUrl}/v1/names/aaron.id`)) .then(resp => resp.json()) .then((nameInfo) => { t.equal(nameInfo.zonefile, zfTest2, 'zonefile should be updated') }) .then(() => transactions.makeTransfer('aaron.id', transferDestination, dest, payer)) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('TRANSFER broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => fetch(`${myNet.aladinAPIUrl}/v1/names/aaron.id`)) .then(resp => resp.json()) .then((nameInfo) => { t.equal(myNet.coerceAddress(nameInfo.address), transferDestination, `aaron.id should be owned by ${transferDestination}`) }) .then(() => transactions.makeRenewal('aaron.id', renewalDestination, secondOwner, payer, renewalZF)) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('RENEWAL broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => myNet.broadcastZoneFile(renewalZF)) .then(() => fetch(`${myNet.aladinAPIUrl}/v1/names/aaron.id`)) .then(resp => resp.json()) .then((nameInfo) => { t.equal(nameInfo.zonefile, renewalZF, 'zonefile should be updated') t.equal(myNet.coerceAddress(nameInfo.address), renewalDestination, `aaron.id should be owned by ${renewalDestination}`) }) .then(() => transactions.makeBitcoinSpend(btcDestAddress, payer, 500000)) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('broadcasted SPEND, waiting 10 seconds.') return nextBlock() }) .then(() => myNet.getUTXOs(btcDestAddress)) .then((utxos) => { t.equal(utxos.length, 1, `Destination address ${btcDestAddress} should have 1 UTXO`) const satoshis = utxos.reduce((agg, utxo) => agg + utxo.value, 0) console.log(`${btcDestAddress} has ${satoshis} satoshis`) }) .then(() => shutdownAladinCore()) .then(() => t.pass('Finished test')) }) test('helloNamespace', (t) => { t.plan(11) config.network = network.defaults.LOCAL_REGTEST const myNet = config.network const nsPay = '6e50431b955fe73f079469b24f06480aee44e4519282686433195b3c4b5336ef01' const nsReveal = 'c244642ce0b4eb68da8e098facfcad889e3063c36a68b7951fb4c085de49df1b01' const nsRevealAddress = ecPairToAddress(hexStringToECPair(nsReveal)) const dest = '19238846ac60fa62f8f8bb8898b03df79bc6112600181f36061835ad8934086001' const destAddress = ecPairToAddress(hexStringToECPair(dest)) const btcDest = '3ad9f690cc7694572fe7574526ad260ff2e711d608d3224895efd932b1d47c7201' const btcDestAddress = ecPairToAddress(hexStringToECPair(btcDest)) const payer = 'bb68eda988e768132bc6c7ca73a87fb9b0918e9a38d3618b74099be25f7cab7d01' const secondOwner = '54164693e3803223f7fa9a004997bfbf1475f5c44f65593fa45c6783086dafec01' const transferDestination = ecPairToAddress(hexStringToECPair(secondOwner)) const renewalKey = 'bb68eda988e768132bc6c7ca73a87fb9b0918e9a38d3618b74099be25f7cab7d' const renewalDestination = ecPairToAddress(hexStringToECPair(renewalKey)) const zfTest = '$ORIGIN aaron.hello\n$TTL 3600\n_http._tcp URI 10 1 ' + `"https://gaia.blockstacktest.org/hub/${destAddress}/0/profile.json"` const zfTest2 = '$ORIGIN aaron.hello\n$TTL 3600\n_http._tcp URI 10 1 ' + `"https://gaia.blockstacktest.org/hub/${destAddress}/3/profile.json"` const renewalZF = '$ORIGIN aaron.hello\n$TTL 3600\n_http._tcp URI 10 1 ' + `"https://gaia.blockstacktest.org/hub/${destAddress}/4/profile.json"` const importZF = '$ORIGIN import.hello\n$TTL 3600\n_http._tcp URI 10 1 ' + `"https://gaia.blockstacktest.org/hub/${destAddress}/0/profile.json"` initializeAladinCore() .then(() => { console.log('Aladin Core initialized.') console.log(`Preorder namespace "hello" to ${nsRevealAddress}`) return transactions.makeNamespacePreorder('hello', nsRevealAddress, nsPay) }) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('NAMESPACE_PREORDER broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => { const ns = new transactions.AladinNamespace('hello') ns.setVersion(1) ns.setLifetime(52595) ns.setCoeff(4) ns.setBase(4) ns.setBuckets([6, 5, 4, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) ns.setNonalphaDiscount(10) ns.setNoVowelDiscount(10) console.log(`Reveal namespace "hello" to ${nsRevealAddress}`) return transactions.makeNamespaceReveal(ns, nsRevealAddress, nsPay) }) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('NAMESPACE_REVEAL broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => { console.log('NAME_IMPORT import.hello') const zfHash = hash160(Buffer.from(importZF)).toString('hex') return transactions.makeNameImport( 'import.hello', renewalDestination, zfHash, nsReveal ) }) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('NAME_IMPORT broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => myNet.broadcastZoneFile(importZF)) .then(() => fetch(`${myNet.aladinAPIUrl}/v1/names/import.hello`)) .then(resp => resp.json()) .then((nameInfo) => { t.equal(myNet.coerceAddress(nameInfo.address), renewalDestination, `import.hello should be owned by ${renewalDestination}`) t.equal(nameInfo.zonefile, importZF, 'zonefile should be properly set for import.hello') }) .then(() => { console.log('Launch namespace "hello"') return transactions.makeNamespaceReady('hello', nsReveal) }) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('NAMESPACE_READY broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => { console.log('Namespace initialized. Preordering aaron.hello') return transactions.makePreorder('aaron.hello', destAddress, payer) }) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('PREORDER broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => transactions.makeRegister('aaron.hello', destAddress, payer, zfTest)) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('REGISTER broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => myNet.broadcastZoneFile(zfTest)) .then(() => fetch(`${myNet.aladinAPIUrl}/v1/names/aaron.hello`)) .then(resp => resp.json()) .then((nameInfo) => { t.equal(myNet.coerceAddress(nameInfo.address), destAddress, `aaron.hello should be owned by ${destAddress}`) t.equal(nameInfo.zonefile, zfTest, 'zonefile should be properly set') }) .then(() => nextBlock()) .then(() => nextBlock()) .then(() => nextBlock()) .then(() => transactions.makeUpdate('aaron.hello', dest, payer, zfTest2)) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('UPDATE broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => myNet.broadcastZoneFile(zfTest2)) .then(() => fetch(`${myNet.aladinAPIUrl}/v1/names/aaron.hello`)) .then(resp => resp.json()) .then((nameInfo) => { t.equal(nameInfo.zonefile, zfTest2, 'zonefile should be updated') }) .then(() => transactions.makeTransfer('aaron.hello', transferDestination, dest, payer)) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('TRANSFER broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => fetch(`${myNet.aladinAPIUrl}/v1/names/aaron.hello`)) .then(resp => resp.json()) .then((nameInfo) => { t.equal(myNet.coerceAddress(nameInfo.address), transferDestination, `aaron.hello should be owned by ${transferDestination}`) }) .then(() => nextBlock()) .then(() => nextBlock()) .then(() => transactions.makeRenewal('aaron.hello', renewalDestination, secondOwner, payer, renewalZF)) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('RENEWAL broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => myNet.broadcastZoneFile(renewalZF)) .then(() => fetch(`${myNet.aladinAPIUrl}/v1/names/aaron.hello`)) .then(resp => resp.json()) .then((nameInfo) => { t.equal(nameInfo.zonefile, renewalZF, 'zonefile should be updated') t.equal(myNet.coerceAddress(nameInfo.address), renewalDestination, `aaron.hello should be owned by ${renewalDestination}`) }) .then(() => nextBlock()) .then(() => nextBlock()) .then(() => transactions.makeRevoke('aaron.hello', renewalKey, payer)) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('REVOKE broadcasted, waiting 30 seconds.') return nextBlock() }) .then(() => fetch(`${myNet.aladinAPIUrl}/v1/names/aaron.hello`)) .then(resp => resp.json()) .then((nameInfo) => { t.equal(nameInfo.status, 'revoked', 'Name should be revoked') }) .then(() => nextBlock()) .then(() => nextBlock()) .then(() => transactions.makeBitcoinSpend(btcDestAddress, payer, 500000)) .then(rawtx => myNet.broadcastTransaction(rawtx)) .then(() => { console.log('broadcasted SPEND, waiting 10 seconds.') return nextBlock(6) }) .then(() => nextBlock()) .then(() => myNet.getUTXOs(btcDestAddress)) .then((utxos) => { t.equal(utxos.length, 1, `Destination address ${btcDestAddress} should have 1 UTXO`) const satoshis = utxos.reduce((agg, utxo) => agg + utxo.value, 0) console.log(`${btcDestAddress} has ${satoshis} satoshis`) }) .then(() => shutdownAladinCore()) .then(() => t.pass('Finished test')) .catch((err) => { console.log(err.stack) console.log(err) }) }) }