aladinnetwork-blockstack
Version:
The Aladin Javascript library for authentication, identity, and storage.
389 lines (359 loc) • 16 kB
text/typescript
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)
})
})
}