Skip to content

Commit

Permalink
chain-test: update chainstate migration and remove chaindb methods.
Browse files Browse the repository at this point in the history
  • Loading branch information
nodech committed Jul 17, 2024
1 parent 60e9dc8 commit 1ada2d0
Show file tree
Hide file tree
Showing 7 changed files with 598 additions and 106 deletions.
57 changes: 42 additions & 15 deletions lib/blockchain/migrations.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class MigrateChainState extends AbstractMigration {
this.logger = options.logger.context('chain-migration-chainstate');
this.db = options.db;
this.ldb = options.ldb;
this.layout = MigrateChainState.layout();
}

/**
Expand Down Expand Up @@ -185,16 +186,18 @@ class MigrateChainState extends AbstractMigration {
this.logger.info('Migrating chain state.');
this.logger.info('This may take a few minutes...');

const state = await this.db.getState();
const tipHeight = await this.db.getHeight(state.tip);
const pending = state.clone();

pending.coin = 0;
pending.value = 0;
pending.burned = 0;
const rawState = await this.ldb.get(this.layout.R.encode());
const tipHash = rawState.slice(0, 32);
const rawTipHeight = await this.ldb.get(this.layout.h.encode(tipHash));
const tipHeight = rawTipHeight.readUInt32LE(0);
const pending = {
coin: 0,
value: 0,
burned: 0
};

for (let height = 0; height <= tipHeight; height++) {
const hash = await this.db.getHash(height);
const hash = await this.ldb.get(this.layout.H.encode(height));
const block = await this.getBlock(hash);
assert(block);

Expand All @@ -213,7 +216,8 @@ class MigrateChainState extends AbstractMigration {
continue;
}

pending.spend(output);
pending.coin -= 1;
pending.value -= output.value;
}
}

Expand All @@ -223,8 +227,10 @@ class MigrateChainState extends AbstractMigration {
if (output.isUnspendable())
continue;

if (output.covenant.isRegister())
pending.burn(output);
if (output.covenant.isRegister()) {
pending.coin += 1;
pending.burned += output.value;
}

if (output.covenant.type >= rules.types.REGISTER
&& output.covenant.type <= rules.types.REVOKE) {
Expand All @@ -236,12 +242,18 @@ class MigrateChainState extends AbstractMigration {
continue;
}

pending.add(output);
pending.coin += 1;
pending.value += output.value;
}
}
}

b.put(layout.R.encode(), pending.encode());
// prefix hash + tx (8)
// we write coin (8) + value (8) + burned (8)
encoding.writeU64(rawState, pending.coin, 40);
encoding.writeU64(rawState, pending.value, 40 + 8);
encoding.writeU64(rawState, pending.burned, 40 + 16);
b.put(layout.R.encode(), rawState);
}

/**
Expand All @@ -252,7 +264,7 @@ class MigrateChainState extends AbstractMigration {

async getBlock(hash) {
assert(Buffer.isBuffer(hash));
const raw = await this.ldb.get(layout.b.encode(hash));
const raw = await this.ldb.get(this.layout.b.encode(hash));

if (!raw)
return null;
Expand All @@ -269,7 +281,7 @@ class MigrateChainState extends AbstractMigration {
async getBlockView(block) {
const hash = block.hash();
const view = new CoinView();
const raw = await this.ldb.get(layout.u.encode(hash));
const raw = await this.ldb.get(this.layout.u.encode(hash));

if (!raw)
return view;
Expand Down Expand Up @@ -301,6 +313,21 @@ class MigrateChainState extends AbstractMigration {
description: 'Chain state is corrupted.'
};
}

static layout() {
return {
// R -> tip hash
R: bdb.key('R'),
// h[hash] -> height
h: bdb.key('h', ['hash256']),
// H[height] -> hash
H: bdb.key('H', ['uint32']),
// b[hash] -> block
b: bdb.key('b', ['hash256']),
// u[hash] -> undo coins
u: bdb.key('u', ['hash256'])
};
}
}

/**
Expand Down
108 changes: 25 additions & 83 deletions test/chain-migration-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -686,129 +686,71 @@ describe('Chain Migrations', function() {
}
});

describe('Migration ChainState (integration)', function() {
const location = testdir('migrate-chain-state');
describe('Migrate ChainState (integration)', function() {
const location = testdir('migrate-chainstate-data');
const data = require('./data/migrations/chain-1-chainstate.json');
const migrationsBAK = ChainMigrator.migrations;
const Migration = ChainMigrator.MigrateChainState;
const store = BlockStore.create({
memory: true,
network
});

const workers = new WorkerPool({
enabled: true,
size: 2
});

const chainOptions = {
prefix: location,
memory: false,
blocks: store,
network,
workers
logger: Logger.global,
network
};

let chain, miner, cpu;
let chain, ldb;
before(async () => {
ChainMigrator.migrations = {};
await fs.mkdirp(location);
await workers.open();
await store.open();
chain = new Chain(chainOptions);
await chain.open();
ldb = chain.db.db;

await fillEntries(ldb, data.before);

await chain.close();
await store.close();
});

after(async () => {
ChainMigrator.migrations = migrationsBAK;
await rimraf(location);
await workers.close();
});

beforeEach(async () => {
chain = new Chain(chainOptions);
miner = new Miner({ chain });
cpu = miner.cpu;

await miner.open();
await fs.mkdirp(location);
await store.open();
});

afterEach(async () => {
if (chain.opened)
await chain.close();

await store.close();
await miner.close();
});

let correctState;
it('should mine 10 blocks', async () => {
await chain.open();

for (let i = 0; i < 10; i++) {
const block = await cpu.mineBlock();
assert(block);
assert(await chain.add(block));
}
});

it('should move blocks to pre-blockstore state', async () => {
await chain.open();
const chainDB = chain.db;
const ldb = chainDB.db;

const state = await chainDB.getState();
const tipHeight = await chainDB.getHeight(state.tip);

const b = ldb.batch();
for (let i = 0; i <= tipHeight; i++) {
const block = await chainDB.getBlock(i);
const hash = block.hash();
const undo = await chainDB.getUndoCoins(hash);

b.put(chLayout.b.encode(hash), block.encode());
b.put(chLayout.u.encode(hash), undo.encode());
if (chain.opened) {
await chain.close();
}

await b.write();
});

it('should set incorrect chaindb state', async () => {
await chain.open();
const state = chain.db.state.clone();
correctState = state.clone();

state.coin = 0;
state.value = 0;
state.burned = 0;

await chain.db.db.put(chLayout.R.encode(), state.encode());
});

it('should enable chain state migration', () => {
it('should migrate', async () => {
ChainMigrator.migrations = {
0: ChainMigrator.MigrateChainState
0: Migration
};
});

it('should throw error when new migration is available', async () => {
const expected = migrationError(ChainMigrator.migrations, [0],
chainFlagError(0));

let error;
chain.options.chainMigrate = 0;
try {
await chain.open();
} catch (e) {
error = e;
;
}

assert(error, 'Chain must throw an error.');
assert.strictEqual(error.message, expected);
});

it('should migrate chain state', async () => {
chain.options.chainMigrate = 0;

await chain.open();

assert.bufferEqual(chain.db.state.encode(), correctState.encode(),
'Chain State did not properly migrate.');
await checkEntries(ldb, data.after);
await chain.close();
});
});

Expand Down
Loading

0 comments on commit 1ada2d0

Please sign in to comment.