azimuth-js
Version:
Functions for interacting with Azimuth
659 lines (489 loc) • 21.9 kB
JavaScript
'use strict';
const assert = require('chai').assert;
const bip39 = require('bip39');
const hdkey = require('hdkey');
const Web3 = require('web3');
const ethUtil = require('ethereumjs-util');
const ajs = require('..');
const check = ajs.check;
const ecliptic = ajs.ecliptic;
const azimuth = ajs.azimuth;
const delsend = ajs.delegatedSending;
const details = ajs.chainDetails;
const txn = ajs.txn;
const claims = ajs.claims;
const reasons = require('../resources/reasons.json');
// accounts
const mnemonic = 'benefit crew supreme gesture quantum web media hazard theory mercy wing kitten';
const seed = bip39.mnemonicToSeedSync(mnemonic);
const hd = hdkey.fromMasterSeed(seed);
const path = "m/44'/60'/0'/0";
const pair0 = ajs.getKeyPair(hd, path, 0);
const pair1 = ajs.getKeyPair(hd, path, 1);
const pair2 = ajs.getKeyPair(hd, path, 2);
const ac0 = ethUtil.addHexPrefix(pair0.address.toString('hex'));
const ac1 = ethUtil.addHexPrefix(pair1.address.toString('hex'));
const ac2 = ethUtil.addHexPrefix(pair2.address.toString('hex'));
const pk0 = pair0.privateKey;
const pk1 = pair1.privateKey;
const pk2 = pair2.privateKey;
const zaddr = ethUtil.zeroAddress();
// contract addresses
const contractAddresses = {
ecliptic: '0x56db68f29203ff44a803faa2404a44ecbb7a7480',
azimuth: '0x863d9c2e5c4c133596cfac29d55255f0d0f86381',
polls: '0x935452c45eda2958976a429c9733c40302995efd',
claims: '0xe0834579269eac6beca2882a6a21f6fb0b1d7196',
delegatedSending: '0xb71c0b6cee1bcae56dfe95cd9d3e41ddd7eafc43'
}
// helpers
function can(res) {
return assert.isTrue(res.result, res.reason);
}
function cant(res, reason) {
assert.isFalse(res.result);
return assert.equal(res.reason, reason);
}
function renderAsHex(value) {
return ethUtil.addHexPrefix(value.toString('hex'));
}
async function firstUnownedGalaxy(contracts) {
let galaxy = 0;
while (await check.hasOwner(contracts, galaxy)) galaxy++;
return galaxy;
}
async function sendTransaction(web3, tx, privateKey) {
if (!ethUtil.isValidPrivate(privateKey)) {
throw "Invalid key";
}
let addr = ethUtil.privateToAddress(privateKey);
// NB (jtobin):
//
// Explicitly set the tx.from field to whoever owns the supplied private
// key. We don't want to depend on the state of web3.eth.defaultAccount,
// implicitly or otherwise, ever.
tx.from = renderAsHex(addr);
let stx = await txn.signTransaction(web3, tx, privateKey);
return txn.sendSignedTransaction(web3, stx);
}
// tests
function main() {
let provider = new Web3.providers.HttpProvider('http://localhost:8545');
let web3 = new Web3(provider);
let contracts = ajs.initContracts(web3, contractAddresses);
const someBytes32 = web3.utils.asciiToHex('whatever');
let galaxy = 0;
let galaxyPlanet = 65536;
let star1 = 256;
let star2 = 512;
let star3 = 768;
let planet1a = 65792;
let planet1b = 131328;
let planet1c = 196864;
let planet1d = 262400;
it('prepare the environment', async function() {
this.timeout(20000)
// NB (jtobin):
//
// The polls tests require that a sufficient number of galaxies have
// been spawned, so we explicitly create ~zod and ~nec below.
let tx = ecliptic.createGalaxy(contracts, 0, ac0);
await sendTransaction(web3, tx, pk0);
tx = ecliptic.createGalaxy(contracts, 1, ac0);
await sendTransaction(web3, tx, pk0);
galaxy = await firstUnownedGalaxy(contracts);
star1 = star1 + galaxy;
star2 = star2 + galaxy;
planet1a = planet1a + galaxy;
planet1b = planet1b + galaxy;
planet1c = planet1c + galaxy;
planet1d = planet1d + galaxy;
});
describe('#createGalaxy', async function() {
it('can only be done by contract owner', async function() {
can(await check.canCreateGalaxy(contracts, galaxy, ac0));
cant(await check.canCreateGalaxy(contracts, galaxy, ac1),
reasons.permission);
});
it('prevents targeting the zero address', async function() {
cant(await check.canCreateGalaxy(contracts, galaxy, zaddr), reasons.zero);
});
it('generates usable transaction', async function() {
assert.isFalse(await azimuth.isOwner(contracts, galaxy, ac0));
let tx = ecliptic.createGalaxy(contracts, galaxy, ac0);
await sendTransaction(web3, tx, pk0);
assert.isTrue(await azimuth.isOwner(contracts, galaxy, ac0));
});
it('prevents creating existing galaxies', async function() {
cant(await check.canCreateGalaxy(contracts, galaxy, ac0), reasons.spawned);
});
});
describe('#setManagementProxy', async function() {
it('can only be done by owner', async function() {
assert.isFalse(await azimuth.canManage(contracts, galaxy, ac2));
cant(await check.canSetManagementProxy(contracts, galaxy, ac1),
reasons.permission);
can(await check.canSetManagementProxy(contracts, galaxy, ac0));
});
it('generates usable transaction', async function() {
let tx = ecliptic.setManagementProxy(contracts, galaxy, ac2);
await sendTransaction(web3, tx, pk0);
assert.isTrue(await azimuth.canManage(contracts, galaxy, ac2));
});
});
describe('#setVotingProxy', async function() {
it('can only be done by owner of galaxy', async function() {
assert.isFalse(await azimuth.canVoteAs(contracts, galaxy, ac2));
cant(await check.canSetVotingProxy(contracts, galaxy, ac1),
reasons.permission);
cant(await check.canSetVotingProxy(contracts, star1, ac0),
reasons.notGalaxy);
can(await check.canSetVotingProxy(contracts, galaxy, ac0));
});
it('generates usable transaction', async function() {
let tx = ecliptic.setVotingProxy(contracts, galaxy, ac2);
await sendTransaction(web3, tx, pk0);
assert.isTrue(await azimuth.canVoteAs(contracts, galaxy, ac2));
tx = ecliptic.setVotingProxy(contracts, galaxy, ac0);
await sendTransaction(web3, tx, pk0);
});
});
describe('#spawn', async function() {
it('cannot spawn from unbooted point', async function() {
cant(await check.canSpawn(contracts, star1, ac0), reasons.spawnPrefix);
});
it('cannot spawn if not prefix owner (or spawn proxy)', async function() {
cant(await check.canSpawn(contracts, star1, ac1), reasons.permission);
});
it('cannot spawn galaxy planets', async function() {
cant(await check.canSpawn(contracts, galaxyPlanet, ac0),
reasons.spawnSize);
});
it('can spawn child to self, directly', async function() {
let tx = ecliptic.configureKeys(
contracts, galaxy, someBytes32, someBytes32, 1, false);
await sendTransaction(web3, tx, pk0);
can(await check.canSpawn(contracts, star1, ac0));
});
it('generates usable transaction', async function() {
this.timeout(10000) // this one can take awhile
assert.isFalse(await azimuth.isOwner(contracts, star1, ac0));
assert.isFalse(await azimuth.isActive(contracts, star1));
assert.equal((await azimuth.getUnspawnedChildren(contracts, galaxy)).length, 255);
let tx = ecliptic.spawn(contracts, star1, ac0);
await sendTransaction(web3, tx, pk0);
tx = ecliptic.configureKeys(
contracts, star1, someBytes32, someBytes32, 1, false);
await sendTransaction(web3, tx, pk0);
assert.isTrue(await azimuth.isOwner(contracts, star1, ac0));
assert.isTrue(await azimuth.isActive(contracts, star1));
assert.isFalse(await azimuth.isOwner(contracts, star2, ac0));
assert.isFalse(await azimuth.isActive(contracts, star2));
tx = ecliptic.spawn(contracts, star2, ac1);
await sendTransaction(web3, tx, pk0);
assert.isTrue(await azimuth.isOwner(contracts, star2, ac0));
assert.isFalse(await azimuth.isActive(contracts, star2));
assert.isTrue(await azimuth.isTransferProxy(contracts, star2, ac1));
assert.equal((await azimuth.getUnspawnedChildren(contracts, galaxy)).length, 253);
});
it('prevents spawning spawned points', async function() {
cant(await check.canSpawn(contracts, star2, ac0), reasons.spawned);
});
it('prevents targeting the zero address', async function() {
cant(await check.canSpawn(contracts, star2, zaddr), reasons.zero);
});
});
describe('#setSpawnProxy', async function() {
it('can only be done by owner or operator', async function() {
cant(await check.canSetSpawnProxy(contracts, galaxy, ac1),
reasons.permission);
can(await check.canSetSpawnProxy(contracts, galaxy, ac0));
});
it('generates usable transaction', async function() {
assert.isFalse(await azimuth.isSpawnProxy(contracts, galaxy, ac1));
let tx = ecliptic.setSpawnProxy(contracts, galaxy, ac1);
await sendTransaction(web3, tx, pk0);
assert.isTrue(await azimuth.isSpawnProxy(contracts, galaxy, ac1));
});
});
describe('#transferPoint', async function() {
it('can only be done by owner/operator/transfer proxy', async function(){
cant(await check.canTransferPoint(contracts, star2, ac2, ac1),
reasons.permission);
can(await check.canTransferPoint(contracts, star2, ac1, ac1));
can(await check.canTransferPoint(contracts, star2, ac0, ac1));
});
it('prevents targeting the zero address', async function() {
cant(await check.canTransferPoint(contracts, star2, ac0, zaddr),
reasons.zero);
});
it('generates usable transaction', async function() {
assert.isFalse(await azimuth.isOwner(contracts, star2, ac1));
let tx = ecliptic.transferPoint(contracts, star2, ac1);
await sendTransaction(web3, tx, pk0);
assert.isTrue(await azimuth.isOwner(contracts, star2, ac1));
});
});
describe('#setTransferProxy', async function() {
it('can only be done by owner', async function() {
cant(await check.canSetTransferProxy(contracts, galaxy, ac1),
reasons.permission);
can(await check.canSetTransferProxy(contracts, galaxy, ac0));
});
it('generates usable transaction', async function() {
assert.isFalse(await azimuth.isTransferProxy(contracts, galaxy, ac1));
let tx = ecliptic.setTransferProxy(contracts, galaxy, ac1);
await sendTransaction(web3, tx, pk0);
assert.isTrue(await azimuth.isTransferProxy(contracts, galaxy, ac1));
});
});
describe('#setManagementProxy', async function() {
it('generates usable transaction, also configureKeys', async function() {
cant(await check.canConfigureKeys(contracts, galaxy, ac1),
reasons.permission);
let tx = ecliptic.setManagementProxy(contracts, galaxy, ac1);
await sendTransaction(web3, tx, pk0);
can(await check.canConfigureKeys(contracts, galaxy, ac1));
assert.isTrue(await azimuth.isManagementProxy(contracts, galaxy, ac1));
});
});
describe('#escape', async function() {
it('prevents invalid sponsors', async function() {
cant(await check.canEscape(contracts, planet1a, star1, ac0),
reasons.permission);
cant(await check.canEscape(contracts, star1, planet1a, ac0),
reasons.sponsor);
cant(await check.canEscape(contracts, star1, galaxy + 1, ac0),
reasons.sponsorBoot);
can(await check.canEscape(contracts, star1, galaxy, ac0));
});
it('generates usable transaction', async function() {
assert.isFalse(await azimuth.isEscaping(contracts, star2));
can(await check.canEscape(contracts, star2, star1, ac1));
let tx = ecliptic.escape(contracts, star2, star1);
await sendTransaction(web3, tx, pk1);
assert.isTrue(await azimuth.isEscaping(contracts, star2));
});
});
describe('#cancelEscape', async function() {
it('can only be done by active point manager', async function() {
cant(await check.checkActivePointManager(contracts, planet1a, ac1),
reasons.permission);
can(await check.checkActivePointManager(contracts, star2, ac1));
});
it('generates usable transaction', async function() {
let tx = ecliptic.cancelEscape(contracts, star2);
await sendTransaction(web3, tx, pk1);
assert.isFalse(await azimuth.isEscaping(contracts, star2));
});
});
describe('#adopt', async function() {
it('can only be done for actual escapees', async function() {
cant(await check.canAdopt(contracts, star2, ac0),
reasons.notEscape);
let tx = ecliptic.escape(contracts, star2, star1);
await sendTransaction(web3, tx, pk1);
can(await check.canAdopt(contracts, star2, ac0));
});
it('generates usable transaction', async function() {
let sponsor = (await azimuth.getPoint(contracts, star2)).sponsor;
assert.notEqual(sponsor, star1);
let tx = ecliptic.adopt(contracts, star2);
await sendTransaction(web3, tx, pk0);
sponsor = (await azimuth.getPoint(contracts, star2)).sponsor;
assert.equal(sponsor, star1);
});
});
describe('#reject', async function() {
it('can only be done for actual escapees', async function() {
cant(await check.canReject(contracts, star2, ac1),
reasons.notEscape);
let tx = ecliptic.escape(contracts, star2, galaxy);
await sendTransaction(web3, tx, pk1);
can(await check.canReject(contracts, star2, ac1));
});
it('generates usable transaction', async function() {
assert.isTrue(await azimuth.isEscaping(contracts, star2));
let tx = ecliptic.reject(contracts, star2);
await sendTransaction(web3, tx, pk1);
assert.isFalse(await azimuth.isEscaping(contracts, star2));
});
});
describe('#detach', async function() {
it('can only be done by the sponsor', async function() {
cant(await check.canDetach(contracts, star2, ac1),
reasons.permission);
can(await check.canDetach(contracts, star2, ac0));
});
it('generates usable transaction', async function() {
assert.isTrue((await azimuth.getPoint(contracts, star2)).hasSponsor);
let tx = ecliptic.detach(contracts, star2);
await sendTransaction(web3, tx, pk0);
assert.isFalse((await azimuth.getPoint(contracts, star2)).hasSponsor);
cant(await check.canDetach(contracts, star2, ac1), reasons.sponsorless);
});
});
describe('#polls', async function() {
it('cannot be done by non-voters', async function() {
cant(await check.canStartUpgradePoll(web3, contracts, star1),
reasons.notGalaxy);
cant(await check.canStartUpgradePoll(web3, contracts, galaxy, zaddr, ac1),
reasons.permission);
cant(await check.canStartDocumentPoll(contracts, star1),
reasons.notGalaxy);
cant(await check.canStartDocumentPoll(contracts, galaxy, zaddr, ac1),
reasons.permission);
cant(await check.canCastUpgradeVote(contracts, star1),
reasons.notGalaxy);
cant(await check.canCastUpgradeVote(contracts, galaxy, zaddr, ac1),
reasons.permission);
cant(await check.canCastDocumentVote(contracts, star1),
reasons.notGalaxy);
cant(await check.canCastDocumentVote(contracts, galaxy, zaddr, ac1),
reasons.permission);
});
it('checks for proposal correctness', async function() {
cant(await check.canStartUpgradePoll(web3, contracts, galaxy, ac2, ac0),
reasons.upgradePath);
});
it('generates usable transactions', async function() {
this.timeout(10000) // this one can take awhile
let fakeHash = web3.utils.asciiToHex('poll hash ' + galaxy);
cant(await check.canCastDocumentVote(contracts, galaxy, fakeHash, ac0),
reasons.pollInactive);
await sendTransaction(
web3,
ecliptic.startDocumentPoll(contracts, galaxy, fakeHash),
pk0);
can(await check.canCastDocumentVote(contracts, galaxy, fakeHash, ac0));
await sendTransaction(
web3,
ecliptic.castDocumentVote(contracts, galaxy, fakeHash, true),
pk0);
await sendTransaction(
web3,
ecliptic.updateDocumentPoll(contracts, fakeHash),
pk0);
cant(await check.canCastDocumentVote(contracts, galaxy, fakeHash, ac0),
reasons.pollVoted);
});
});
describe('#delegatedSending', async function() {
it('sets up for tests', async function() {
let prep = ecliptic.spawn(contracts, planet1c, ac0);
await sendTransaction(web3, prep, pk0);
prep = ecliptic.setSpawnProxy(contracts, star1, contracts.delegatedSending._address);
await sendTransaction(web3, prep, pk0);
prep = ecliptic.setSpawnProxy(contracts, star2, contracts.delegatedSending._address);
await sendTransaction(web3, prep, pk1);
// ac0 now owns planet1c (in addition to star1)
});
it('checks for star ownership', async function() {
cant(await check.canSetPoolSize(contracts, planet1c, ac1),
reasons.permission);
can(await check.canSetPoolSize(contracts, planet1c, ac0));
});
it('generates usable transaction', async function() {
assert.equal(await delsend.pools(contracts, planet1c, star1), 0);
let tx = delsend.setPoolSize(contracts, star1, planet1c, 9);
await sendTransaction(web3, tx, pk0);
tx = delsend.setPoolSize(contracts, star2, planet1c, 1);
await sendTransaction(web3, tx, pk1);
assert.equal(await delsend.pools(contracts, planet1c, star1), 9);
assert.equal(await delsend.pools(contracts, planet1c, star2), 1);
});
it('checks invite send ability', async function() {
cant(await check.canSendInvitePoint(contracts, planet1c, planet1d, ac0, ac0),
reasons.cantReceive);
cant(await check.canSendInvitePoint(contracts, planet1c, planet1d, ac1, ac0),
reasons.cantReceive);
can(await check.canSendInvitePoint(contracts, planet1c, planet1d, ac2, ac0));
});
it('generates usable transaction', async function() {
let tx = delsend.sendPoint(contracts, planet1c, planet1d, ac2);
await sendTransaction(web3, tx, pk0);
assert.isTrue(await azimuth.isTransferProxy(contracts, planet1d, ac2));
assert.equal(await delsend.pools(contracts, planet1c, star1), 8);
assert.equal(await delsend.getTotalUsableInvites(contracts, planet1c), 8);
assert.equal(await delsend.invitedBy(contracts, planet1d), planet1c);
assert.equal(await delsend.getPool(contracts, planet1d), planet1c);
let invited = await delsend.getInvited(contracts, planet1c);
assert.equal(invited.length, 1);
assert.equal(invited[0], planet1d);
});
it('counts & generates planets to send', async function() {
this.timeout(10000) // this one can take awhile
let shortList = await delsend.getPlanetsToSend(contracts, planet1c, 3);
let longList = await delsend.getPlanetsToSend(contracts, planet1c, 15);
let count = await delsend.getTotalUsableInvites(contracts, planet1c);
assert.equal(shortList.length, 3);
assert.equal(longList.length, 8);
assert.equal(count, 8);
let tx = ecliptic.configureKeys(
contracts, star2, someBytes32, someBytes32, 1, false
);
await sendTransaction(web3, tx, pk1);
longList = await delsend.getPlanetsToSend(contracts, planet1c, 15);
count = await delsend.getTotalUsableInvites(contracts, planet1c);
assert.equal(longList.length, 9);
assert.equal(count, 9);
tx = ecliptic.setSpawnProxy(contracts, star2, zaddr);
await sendTransaction(web3, tx, pk1);
longList = await delsend.getPlanetsToSend(contracts, planet1c, 15);
count = await delsend.getTotalUsableInvites(contracts, planet1c);
assert.equal(longList.length, 8);
assert.equal(count, 8);
});
});
describe('#eventLog', async function() {
it('can find activated ship block', async function() {
const latest = await web3.eth.getBlockNumber();
const res = await azimuth.getActivationBlock(
contracts,
star1,
details.local.azimuth.genesis,
latest
);
assert.notEqual(res, 0);
});
it('cannot find unactivated ship block', async function() {
const latest = await web3.eth.getBlockNumber();
const res = await azimuth.getActivationBlock(
contracts,
star3,
details.local.azimuth.genesis,
latest
);
assert.equal(res, 0);
});
});
describe('#claims', async function() {
const btcAddress = '1Hz96kJKF2HLPGY15JWLB5m9qGNxvt8tHJ';
const protocol = 'BTC'
const dossier = '0x00';
it('can write a claim', async function() {
const newClaimTx = await claims.addClaim(contracts, galaxy, protocol, btcAddress, dossier);
await sendTransaction(web3, newClaimTx, pk0);
const claim = await claims.getClaim(contracts, galaxy, 0);
assert.equal(claim.claim, btcAddress);
assert.equal(claim.protocol, protocol);
assert.equal(claim.dossier, dossier);
});
it('can find all claims', async function() {
const ethAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';
const ethProtocol = 'ETH'
const ethDossier = '0x01';
const newClaimTx = await claims.addClaim(contracts, galaxy, ethProtocol, ethAddress, ethDossier);
await sendTransaction(web3, newClaimTx, pk0);
const allClaims = await claims.getAllClaims(contracts, galaxy);
assert.equal(allClaims.length, 2);
assert.equal(allClaims[0].claim, btcAddress);
assert.equal(allClaims[0].protocol, protocol);
assert.equal(allClaims[0].dossier, dossier);
assert.equal(allClaims[1].claim, ethAddress);
assert.equal(allClaims[1].protocol, ethProtocol);
assert.equal(allClaims[1].dossier, ethDossier);
});
});
}
main();