Skip to content
This repository has been archived by the owner on Jun 11, 2024. It is now read-only.

Implement genesis block creation and validation function in lisk-genesis - Closes #5277, #5386 #5421

Merged
merged 30 commits into from
Jun 17, 2020
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fb881e5
Update lisk-chain to export schema objects
nazarhussain Jun 10, 2020
46b26e8
Update lisk-genesis to use lisk-chain and lisk-validator
nazarhussain Jun 10, 2020
92293ed
Define types and constants for lisk-genesis
nazarhussain Jun 10, 2020
2eb802c
Add createGenesisBlock to lisk-genesis
nazarhussain Jun 10, 2020
7fb97ef
Add utility functions to be used in lisk-genesis
nazarhussain Jun 10, 2020
10be84a
Update validation for lisk genesis block
nazarhussain Jun 10, 2020
332d9f5
Add lisk-cryptography and lisk-codec as dependency for lisk-genesis
nazarhussain Jun 10, 2020
920d382
Update createGenesisBlock to generate actual block id
nazarhussain Jun 10, 2020
ea372c2
Merge branch 'development' into 5277-create-genesis-block
nazarhussain Jun 10, 2020
d304c16
Update create genesis to use constant values
nazarhussain Jun 11, 2020
e7a4d7d
Add default account asset schema for genesis
nazarhussain Jun 11, 2020
d803148
Update create and validate function to use default account asset schema
nazarhussain Jun 11, 2020
75dd063
Add test case for valid input of genesis block to match snapshot
nazarhussain Jun 11, 2020
abddaa2
Merge branch 'development' into 5277-create-genesis-block
nazarhussain Jun 12, 2020
2e8c130
Add test skeleton for genesis block creation and validation
nazarhussain Jun 12, 2020
67095b6
Add validation tests for genesis block
nazarhussain Jun 15, 2020
46a1fb1
Add account keys validation for genesis block
nazarhussain Jun 15, 2020
5ff76ef
Add validtion for initDelegates of genesis block
nazarhussain Jun 15, 2020
8fd6ebf
Add unit tests for genesis block creation
nazarhussain Jun 15, 2020
1056c30
Remove a left over console statement
nazarhussain Jun 15, 2020
ddd3f1e
Merge branch 'development' into 5277-create-genesis-block
nazarhussain Jun 15, 2020
1a11968
Update some spec comments
nazarhussain Jun 15, 2020
6078f57
Update code with provided feedback
nazarhussain Jun 16, 2020
d06e52e
Merge branch 'development' into 5277-create-genesis-block
nazarhussain Jun 16, 2020
2ca9b85
Remove unncessary file from branch
nazarhussain Jun 16, 2020
c976906
Add test comments
nazarhussain Jun 16, 2020
a7532e4
Update genesis block type to use generic Block type
nazarhussain Jun 16, 2020
f37b818
Merge branch 'development' into 5277-create-genesis-block
nazarhussain Jun 16, 2020
aff7e4e
Revert "Update genesis block type to use generic Block type"
nazarhussain Jun 16, 2020
b81809b
Update validation for buffer values
nazarhussain Jun 17, 2020
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
1 change: 1 addition & 0 deletions elements/lisk-chain/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const events = { EVENT_DELETE_BLOCK, EVENT_NEW_BLOCK };
export { Account } from './account';
export * from './block_reward';
export { Chain, events };
export * from './schema';
export { StateStore } from './state_store';
export { Slots } from './slots';
export * from './types';
Expand Down
31 changes: 28 additions & 3 deletions elements/lisk-chain/src/utils/buffer_set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@
import { keyString } from './buffer_string';

export class BufferSet {
private _data: { [key: string]: Buffer | undefined } = {};
private _data: { [key: string]: Buffer } = {};

public constructor(data?: { [key: string]: Buffer | undefined }) {
this._data = data ?? {};
public constructor(data?: Buffer[]) {
this._data = {};
if (data) {
for (const d of data) {
this.add(d);
}
}
}

public delete(key: Buffer): void {
Expand All @@ -31,4 +36,24 @@ export class BufferSet {
public has(value: Buffer): boolean {
return this._data[keyString(value)] !== undefined;
}

public get size(): number {
return Object.keys(this._data).length;
}

public [Symbol.iterator](): { next: () => { value: Buffer; done: boolean } } {
let index = -1;
const data = Object.values<Buffer>(this._data);

return {
next: (): { value: Buffer; done: boolean } => {
index += 1;

return {
value: data[index],
done: !(index in data),
};
},
};
}
}
8 changes: 7 additions & 1 deletion elements/lisk-genesis/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,12 @@
"build:check": "node -e \"require('./dist-node')\"",
"prepublishOnly": "npm run lint && npm test && npm run build && npm run build:check"
},
"dependencies": {},
"dependencies": {
"@liskhq/lisk-cryptography": "2.5.0-alpha.0",
"@liskhq/lisk-chain": "0.1.0-alpha.0",
"@liskhq/lisk-validator": "0.4.0-alpha.0",
"@liskhq/lisk-codec": "0.1.0"
},
"devDependencies": {
"@types/node": "12.12.11",
"@types/jest": "25.1.3",
Expand All @@ -48,6 +53,7 @@
"jest": "25.1.0",
"jest-extended": "0.11.5",
"jest-when": "2.7.0",
"lodash.clonedeep": "4.5.0",
"prettier": "1.19.1",
"source-map-support": "0.5.16",
"ts-jest": "25.2.1",
Expand Down
26 changes: 26 additions & 0 deletions elements/lisk-genesis/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright © 2020 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*/

import { hash } from '@liskhq/lisk-cryptography';

export const EMPTY_BUFFER = Buffer.alloc(0);
export const EMPTY_HASH = hash(EMPTY_BUFFER);

export const GENESIS_BLOCK_VERSION = 0;
export const GENESIS_BLOCK_GENERATOR_PUBLIC_KEY = EMPTY_BUFFER;
export const GENESIS_BLOCK_REWARD = BigInt(0);
export const GENESIS_BLOCK_PAYLOAD: Buffer[] = [];
export const GENESIS_BLOCK_SIGNATURE = EMPTY_BUFFER;
export const GENESIS_BLOCK_TRANSACTION_ROOT = EMPTY_HASH;
export const GENESIS_BLOCK_MAX_BALANCE = BigInt(2) ** BigInt(63) - BigInt(1);
137 changes: 112 additions & 25 deletions elements/lisk-genesis/src/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,117 @@
* Removal or modification of this copyright notice is prohibited.
*/

interface GeneisAccountState {
readonly publicKey?: Buffer;
readonly address: Buffer;
readonly balance: bigint;
readonly keys: ReadonlyArray<Buffer>;
readonly delegate?: {
readonly username: string;
readonly lastForgedHeight: bigint;
import { codec, Schema } from '@liskhq/lisk-codec';
import { Account } from '@liskhq/lisk-chain';
import { hash } from '@liskhq/lisk-cryptography';
import { LiskValidationError } from '@liskhq/lisk-validator';
import {
EMPTY_BUFFER,
GENESIS_BLOCK_GENERATOR_PUBLIC_KEY,
GENESIS_BLOCK_PAYLOAD,
GENESIS_BLOCK_REWARD,
GENESIS_BLOCK_SIGNATURE,
GENESIS_BLOCK_TRANSACTION_ROOT,
GENESIS_BLOCK_VERSION,
} from './constants';
import {
GenesisAccountState,
GenesisBlock,
GenesisBlockHeaderWithoutId,
GenesisBlockParams,
} from './types';
import { validateGenesisBlock } from './validate';
import {
defaultAccountAssetSchema,
genesisBlockHeaderAssetSchema,
genesisBlockHeaderSchema,
} from './schema';
import { getHeaderAssetSchemaWithAccountAsset } from './utils/schema';

const getBlockId = (
header: GenesisBlockHeaderWithoutId,
accountAssetSchema: Schema,
): Buffer => {
// eslint-disable-next-line
const genesisBlockAssetBuffer = codec.encode(
getHeaderAssetSchemaWithAccountAsset(
genesisBlockHeaderAssetSchema,
accountAssetSchema,
),
header.asset,
);

const genesisBlockHeaderBuffer = codec.encode(genesisBlockHeaderSchema, {
...header,
asset: genesisBlockAssetBuffer,
});

return hash(genesisBlockHeaderBuffer);
};

export const createGenesisBlock = (
params: GenesisBlockParams,
): GenesisBlock => {
// Default values
const initRounds = params.initRounds ?? 3;
const height = params.height ?? 0;
const timestamp = params.timestamp ?? Math.floor(Date.now() / 1000);
const previousBlockID = params.previousBlockID ?? Buffer.from(EMPTY_BUFFER);
const accountAssetSchema =
params.accountAssetSchema ?? defaultAccountAssetSchema;

// Constant values
const version = GENESIS_BLOCK_VERSION;
const generatorPublicKey = GENESIS_BLOCK_GENERATOR_PUBLIC_KEY;
const reward = GENESIS_BLOCK_REWARD;
const payload = GENESIS_BLOCK_PAYLOAD;
const signature = GENESIS_BLOCK_SIGNATURE;
const transactionRoot = GENESIS_BLOCK_TRANSACTION_ROOT;

const accounts: ReadonlyArray<GenesisAccountState> = params.accounts
.map(acc => new Account(acc))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you need to change this to instance here?

Copy link
Contributor Author

@nazarhussain nazarhussain Jun 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise, we have to specify full account type and also duplicate code to convert the partial account to a full account attributes. Then we have to add another dependency of lisk-transactions

.sort((a, b): number => a.address.compare(b.address));

for (const account of accounts) {
account.keys.mandatoryKeys.sort((a, b) => a.compare(b));
account.keys.optionalKeys.sort((a, b) => a.compare(b));
}

const initDelegates: ReadonlyArray<Buffer> = [
...params.initDelegates,
].sort((a, b): number => a.compare(b));

const header: GenesisBlockHeaderWithoutId = {
generatorPublicKey,
height,
previousBlockID,
reward,
signature,
timestamp,
transactionRoot,
version,
asset: {
initRounds,
initDelegates,
accounts,
},
};
}

interface GenesisBlockParams {
// List of accounts in the genesis
readonly accounts: ReadonlyArray<GeneisAccountState>;
// List fo initial delegate addresses used during the bootstrap period to forge blocks
readonly initDelegates: ReadonlyArray<Buffer>;
// Number of rounds for bootstrap period, default is 3
readonly initRounds?: number;
readonly height?: number;
readonly timestamp?: number;
readonly previousBlockId?: Buffer;
}

// eslint-disable-next-line
export const createGenesisBlock = (_params: GenesisBlockParams) => {
return {};

const errors = validateGenesisBlock(
{ header, payload },
{ accountAssetSchema, roundLength: params.roundLength },
);
if (errors.length) {
throw new LiskValidationError(errors);
}

const genesisBlock: GenesisBlock = {
header: {
...header,
id: getBlockId(header, accountAssetSchema),
},
payload,
};

return genesisBlock;
};
7 changes: 6 additions & 1 deletion elements/lisk-genesis/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,9 @@
* Removal or modification of this copyright notice is prohibited.
*/

export * from './create';
import { createGenesisBlock } from './create';
import { validateGenesisBlock } from './validate';

export * from './types';
export * from './schema';
export { createGenesisBlock, validateGenesisBlock };
138 changes: 138 additions & 0 deletions elements/lisk-genesis/src/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
* Copyright © 2020 Lisk Foundation
*
* See the LICENSE file at the top-level directory of this distribution
* for licensing information.
*
* Unless otherwise agreed in a custom licensing agreement with the Lisk Foundation,
* no part of this software, including this file, may be copied, modified,
* propagated, or distributed except according to the terms contained in the
* LICENSE file.
*
* Removal or modification of this copyright notice is prohibited.
*/

import { Schema } from '@liskhq/lisk-codec';

import {
blockSchema,
baseAccountSchema,
blockHeaderSchema,
} from '@liskhq/lisk-chain';
import { mergeDeep } from './utils';

export const genesisAccountSchema = mergeDeep({}, baseAccountSchema) as Schema;
export const genesisBlockSchema = mergeDeep({}, blockSchema, {
properties: {
payload: {
const: [],
},
},
}) as Schema;

export const defaultAccountAssetSchema = {
$id: '/genesis_block/account/default',
type: 'object',
properties: {
delegate: {
type: 'object',
fieldNumber: 1,
properties: {
username: { dataType: 'string', fieldNumber: 1 },
pomHeights: {
type: 'array',
items: { dataType: 'uint32' },
fieldNumber: 2,
},
consecutiveMissedBlocks: { dataType: 'uint32', fieldNumber: 3 },
lastForgedHeight: { dataType: 'uint32', fieldNumber: 4 },
isBanned: { dataType: 'boolean', fieldNumber: 5 },
totalVotesReceived: { dataType: 'uint64', fieldNumber: 6 },
},
required: [
'username',
'pomHeights',
'consecutiveMissedBlocks',
'lastForgedHeight',
'isBanned',
'totalVotesReceived',
],
},
sentVotes: {
type: 'array',
fieldNumber: 2,
items: {
type: 'object',
properties: {
delegateAddress: {
dataType: 'bytes',
fieldNumber: 1,
},
amount: {
dataType: 'uint64',
fieldNumber: 2,
},
},
required: ['delegateAddress', 'amount'],
},
},
unlocking: {
type: 'array',
fieldNumber: 3,
items: {
type: 'object',
properties: {
delegateAddress: {
dataType: 'bytes',
fieldNumber: 1,
},
amount: {
dataType: 'uint64',
fieldNumber: 2,
},
unvoteHeight: {
dataType: 'uint32',
fieldNumber: 3,
},
},
required: ['delegateAddress', 'amount', 'unvoteHeight'],
},
},
},
};

export const genesisBlockHeaderSchema = mergeDeep({}, blockHeaderSchema, {
properties: {
version: {
const: 0,
},
},
}) as Schema;

export const genesisBlockHeaderAssetSchema = {
$id: '/genesis_block/header/asset',
type: 'object',
required: ['accounts', 'initDelegates', 'initRounds'],
properties: {
accounts: {
type: 'array',
items: {
...genesisAccountSchema,
nazarhussain marked this conversation as resolved.
Show resolved Hide resolved
},
fieldNumber: 1,
},
initDelegates: {
type: 'array',
items: {
dataType: 'bytes',
},
fieldNumber: 2,
minItems: 1,
},
initRounds: {
dataType: 'uint32',
fieldNumber: 3,
minimum: 3,
},
},
} as Schema;
Loading