Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use monotonic SOA serial numbers, enforced by 12-confirmation "safe height" for tree roots #766

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 23 additions & 10 deletions lib/blockchain/chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ class Chain extends AsyncEmitter {
this.height = -1;
this.synced = false;

// Sufficiently confirmed tree root.
// Remains null until chain is synced.
this.safeEntry = null;

this.orphanMap = new BufferMap();
this.orphanPrev = new BufferMap();
}
Expand Down Expand Up @@ -113,6 +117,7 @@ class Chain extends AsyncEmitter {
this.emit('tip', tip);

this.maybeSync();
await this.setSafeEntry();
}

/**
Expand Down Expand Up @@ -2035,6 +2040,7 @@ class Chain extends AsyncEmitter {
this.purgeOrphans();

this.maybeSync();
await this.setSafeEntry();
}

/**
Expand Down Expand Up @@ -2414,6 +2420,7 @@ class Chain extends AsyncEmitter {

// Check sync state.
this.maybeSync();
await this.setSafeEntry();

return entry;
}
Expand Down Expand Up @@ -3714,11 +3721,16 @@ class Chain extends AsyncEmitter {
}

/**
* Get safe tree root.
* @returns {Hash}
* Get safe ChainEntry (for sufficiently confirmed tree root).
* @returns {Promise}
*/

async getSafeRoot() {
async setSafeEntry() {
if (!this.synced) {
this.safeEntry = null;
return;
}

// The tree is committed on an interval.
// Mainnet is 36 blocks, meaning at height 36,
// the name set of the past 36 blocks are
Expand All @@ -3732,17 +3744,18 @@ class Chain extends AsyncEmitter {

let mod = this.height % interval;

// If there's enough proof-of-work
// If there's not enough proof-of-work
// on top of the most recent root,
// it should be safe to use it.
if (mod >= 12)
mod = 0;
// go back one tree interval
if (mod < this.network.names.safeRoot)
mod += interval;

const height = this.height - mod + 1;

const height = this.height - mod;
// Could be null if not enough blocks
const entry = await this.getEntryByHeight(height);
assert(entry);

return entry.treeRoot;
this.safeEntry = entry;
}
}

Expand Down
39 changes: 26 additions & 13 deletions lib/dns/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class RootServer extends DNSServer {
this.noSig0 = false;
this.icann = new RootResolver(RES_OPT);

this.chain = null;
this.logger = Logger.global;
this.key = secp256k1.privateKeyGenerate();
this.host = '127.0.0.1';
Expand All @@ -133,8 +134,7 @@ class RootServer extends DNSServer {

this.cache = new RootCache(3000);

if (options)
this.initOptions(options);
this.initOptions(options);

// Create SYNTH record to use for root zone NS
let ip = IP.toBuffer(this.publicHost);
Expand All @@ -146,7 +146,11 @@ class RootServer extends DNSServer {
}

initOptions(options) {
assert(options);
assert(options, 'DNS root server requires options.');
assert(options.chain && typeof options.chain === 'object',
'DNS root server requires a blockchain.');

this.chain = options.chain;

this.parseOptions(options);

Expand Down Expand Up @@ -234,8 +238,12 @@ class RootServer extends DNSServer {
if (!this.lookup)
throw new Error('Tree not available.');

if (!this.chain.safeEntry)
throw new Error('Chain is not safe for name resolution.');

const {treeRoot} = this.chain.safeEntry;
const hash = rules.hashName(name);
const data = await this.lookup(hash);
const data = await this.lookup(hash, treeRoot);

if (!data)
return null;
Expand Down Expand Up @@ -380,11 +388,8 @@ class RootServer extends DNSServer {
// useless proofs for invalid TLDs
// (These requests are most
// likely bad anyways)
if (!rules.verifyName(tld)) {
const res = new Message();
res.code = codes.REFUSED;
return res;
}
if (!rules.verifyName(tld))
throw new Error('Invalid name.');

// Ask the urkel tree for the name data.
const data = !this.blacklist.has(tld)
Expand Down Expand Up @@ -530,10 +535,17 @@ class RootServer extends DNSServer {
if (cache)
return cache;

const res = await this.response(req, rinfo);
let res;
try {
res = await this.response(req, rinfo);

if (!util.equal(tld, '_synth.'))
this.cache.set(name, type, res);
if (!util.equal(tld, '_synth.'))
this.cache.set(name, type, res);
} catch (e) {
this.logger.error(e);
res = new Message();
res.code = codes.REFUSED;
}

return res;
}
Expand All @@ -558,7 +570,8 @@ class RootServer extends DNSServer {
}

serial() {
const date = new Date();
const time = this.chain.safeEntry ? this.chain.safeEntry.time : 0;
const date = new Date(time * 1000);
const y = date.getUTCFullYear() * 1e6;
const m = (date.getUTCMonth() + 1) * 1e4;
const d = date.getUTCDate() * 1e2;
Expand Down
6 changes: 4 additions & 2 deletions lib/net/pool.js
Original file line number Diff line number Diff line change
Expand Up @@ -4284,8 +4284,10 @@ class Pool extends EventEmitter {
*/

async resolve(nameHash) {
const root = await this.chain.getSafeRoot();
return this.resolveAtRoot(nameHash, root);
const {treeRoot} = this.chain.safeEntry;
if (!treeRoot)
return null;
return this.resolveAtRoot(nameHash, treeRoot);
}

/**
Expand Down
3 changes: 2 additions & 1 deletion lib/node/fullnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,11 +163,12 @@ class FullNode extends Node {

if (!this.config.bool('no-dns')) {
this.ns = new RootServer({
chain: this.chain,
logger: this.logger,
key: this.identityKey,
host: this.config.str('ns-host'),
port: this.config.uint('ns-port', this.network.nsPort),
lookup: key => this.chain.db.tree.get(key),
lookup: (key, root) => this.chain.db.lookup(root, key),
publicHost: this.config.str('public-host'),
noSig0: this.config.bool('no-sig0')
});
Expand Down
9 changes: 6 additions & 3 deletions lib/node/rpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3112,12 +3112,15 @@ class RPC extends RPCBase {

// Safe roots are the last Urkel tree commitment
// with more than 12 confirmations.
const root = await this.chain.getSafeRoot();
const {treeRoot} = this.chain.safeEntry;
if (!treeRoot)
return null;

let data;
if (this.chain.options.spv)
data = await this.pool.resolveAtRoot(nameHash, root);
data = await this.pool.resolveAtRoot(nameHash, treeRoot);
else
data = await this.chain.db.lookup(root, nameHash);
data = await this.chain.db.lookup(treeRoot, nameHash);

if (!data)
return null;
Expand Down
3 changes: 2 additions & 1 deletion lib/node/spvnode.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,12 @@ class SPVNode extends Node {

if (!this.config.bool('no-dns')) {
this.ns = new RootServer({
chain: this.chain,
logger: this.logger,
key: this.identityKey,
host: this.config.str('ns-host'),
port: this.config.uint('ns-port', this.network.nsPort),
lookup: key => this.pool.resolve(key),
lookup: (key, root) => this.pool.resolveAtRoot(key, root),
publicHost: this.config.str('public-host'),
noSig0: this.config.bool('no-sig0')
});
Expand Down
10 changes: 10 additions & 0 deletions lib/protocol/networks.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,13 @@ main.names = {

treeInterval: main.pow.blocksPerDay >>> 2,

/**
* Number of confirmations to require before proving from tree root
* @const {Number}
*/

safeRoot: 12,

/**
* Amount of time transfers are locked up for.
* @const {Number}
Expand Down Expand Up @@ -670,6 +677,7 @@ testnet.names = {
biddingPeriod: 1 * testnet.pow.blocksPerDay,
revealPeriod: 2 * testnet.pow.blocksPerDay,
treeInterval: testnet.pow.blocksPerDay >>> 2,
safeRoot: 12,
transferLockup: 2 * testnet.pow.blocksPerDay,
revocationDelay: 4 * testnet.pow.blocksPerDay,
auctionMaturity: (1 + 2 + 4) * testnet.pow.blocksPerDay,
Expand Down Expand Up @@ -813,6 +821,7 @@ regtest.names = {
biddingPeriod: 5,
revealPeriod: 10,
treeInterval: 5,
safeRoot: 3,
transferLockup: 10,
revocationDelay: 50,
auctionMaturity: 5 + 10 + 50,
Expand Down Expand Up @@ -960,6 +969,7 @@ simnet.names = {
biddingPeriod: 25,
revealPeriod: 50,
treeInterval: 2,
safeRoot: 1,
transferLockup: 5,
revocationDelay: 25,
auctionMaturity: 25 + 50 + 25,
Expand Down
6 changes: 5 additions & 1 deletion test/chain-tree-compaction-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,11 @@ describe('Tree Compacting', function() {
// Update name and attempt to confirm
send(update, mempool);
// Will "crash" node before completing operation
await mineBlocks(1, mempool);
try {
await mineBlocks(1, mempool);
} catch (e) {
;
}
assert(!chain.opened);

// Restore proper batch-write function
Expand Down
Loading