Skip to content

Commit

Permalink
feat(node): initial consensus block generation
Browse files Browse the repository at this point in the history
  • Loading branch information
danwbyrne committed Nov 20, 2020
1 parent f61dd90 commit fc94328
Show file tree
Hide file tree
Showing 60 changed files with 3,170 additions and 1,608 deletions.
10 changes: 7 additions & 3 deletions packages/neo-one-client-common/src/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,12 +621,15 @@ const isMultiSigContract = (script: Buffer) => {
};
// tslint:enable

type MultiSigResult = { readonly result: true; readonly m: number; readonly n: number } | { readonly result: false };
type MultiSigResult =
| { readonly result: true; readonly m: number; readonly n: number; readonly points: readonly ECPoint[] }
| { readonly result: false };
// tslint:disable
const isMultiSigContractWithResult = (script: Buffer): MultiSigResult => {
let m = 0;
let n = 0;
let i = 0;
let points = [];
if (script.length < 43) return { result: false };
if (script[i] > Op.PUSH16) return { result: false };
if (script[i] < Op.PUSH1 && script[i] !== 1 && script[i] !== 2) return { result: false };
Expand All @@ -652,7 +655,8 @@ const isMultiSigContractWithResult = (script: Buffer): MultiSigResult => {
while (script[i] == Op.PUSHDATA1) {
if (script.length <= i + 35) return { result: false };
if (script[++i] !== 33) return { result: false };
// points?.push(script.slice(i + 1, i + 1 + 33)); // TODO: add "points" List from C#
// TODO: this will take some work to verify but look at what the C# calls
points.push(common.bufferToECPoint(script.slice(i + 1, i + 1 + 33)));

i += 34;
++n;
Expand Down Expand Up @@ -680,7 +684,7 @@ const isMultiSigContractWithResult = (script: Buffer): MultiSigResult => {
if (script[i++] !== Op.SYSCALL) return { result: false };
if (script.length !== i + 4) return { result: false };
if (script.slice(i) !== checkMultiSigUint) return { result: false };
return { result: true, m, n };
return { result: true, m, n, points };
};
// tslint:enable

Expand Down
4 changes: 2 additions & 2 deletions packages/neo-one-client-common/src/models/Serializable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ export interface SerializableJSON<TJSON> {
readonly serializeJSON: () => TJSON;
}

export const createGetHashData = (serializeWire: () => Buffer, magic: number) => () => {
export const getHashData = (wire: Buffer, magic: number) => {
const writer = new BinaryWriter();
writer.writeUInt32LE(magic);
writer.writeBytes(serializeWire());
writer.writeBytes(wire);

return writer.toBuffer();
};
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { BinaryWriter } from '../../BinaryWriter';
import { common, ECPoint, InvalidFormatError, PrivateKey, UInt160, UInt256, UInt256Hex } from '../../common';
import { crypto } from '../../crypto';
import { utils } from '../../utils';
import { createGetHashData, createSerializeWire, SerializableWire, SerializeWire } from '../Serializable';
import { createSerializeWire, getHashData, SerializableWire, SerializeWire } from '../Serializable';
import { SignerModel } from '../SignerModel';
import { WitnessModel } from '../WitnessModel';
import { AttributeModel } from './attribute';
Expand Down Expand Up @@ -66,7 +66,7 @@ export class FeelessTransactionModel<
public readonly serializeUnsigned: SerializeWire = createSerializeWire(this.serializeUnsignedBase.bind(this));
private readonly hashInternal: () => UInt256;
private readonly hashHexInternal = utils.lazy(() => common.uInt256ToHex(this.hash));
private readonly messageInternal = utils.lazy(() => createGetHashData(this.serializeUnsigned, this.messageMagic)());
private readonly messageInternal = utils.lazy(() => getHashData(this.serializeUnsigned(), this.messageMagic));

public constructor({
version,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from '@neo-one/client-common';
import { constants } from '@neo-one/utils';
import BN from 'bn.js';
import { Hash160 } from '../../Hash160';
import { LocalKeyStore, LocalMemoryStore } from '../../user';

describe('RPC Call sendrawtransaction', () => {
Expand Down Expand Up @@ -80,8 +81,8 @@ describe('RPC Call sendrawtransaction', () => {

const txUnsigned = new TransactionModel({
script: new ScriptBuilder().emitOp('PUSHNULL').build(),
systemFee: new BN(0),
networkFee: new BN(1590000),
systemFee: new BN(10),
networkFee: new BN(10),
validUntilBlock: 800000,
signers: [signer],
messageMagic,
Expand Down
36 changes: 34 additions & 2 deletions packages/neo-one-node-blockchain/src/Blockchain.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { common, ScriptBuilder, TriggerType, UInt256, VerifyResultModel } from '@neo-one/client-common';
import {
common,
crypto,
ECPoint,
ScriptBuilder,
TriggerType,
UInt256,
VerifyResultModel,
} from '@neo-one/client-common';
import { createChild, nodeLogger } from '@neo-one/logger';
import {
ApplicationExecuted,
Expand Down Expand Up @@ -276,6 +284,10 @@ export class Blockchain {
return this.storage.contractID;
}

public get consensusState() {
return this.storage.consensusState;
}

public get serializeJSONContext() {
return {
addressVersion: this.settings.addressVersion,
Expand Down Expand Up @@ -414,6 +426,26 @@ export class Blockchain {
return this.getBlockHash(header.index + 1);
}

public async getValidators(): Promise<readonly ECPoint[]> {
return this.native.NEO.getValidators(this.storage);
}

public async getNextBlockValidators(): Promise<readonly ECPoint[]> {
return this.native.NEO.getNextBlockValidators(this.storage);
}

public async getMaxBlockSize(): Promise<number> {
return this.native.Policy.getMaxBlockSize(this.storage);
}

public async getMaxBlockSystemFee(): Promise<BN> {
return this.native.Policy.getMaxBlockSystemFee(this.storage);
}

public async getMaxTransactionsPerBlock(): Promise<number> {
return this.native.Policy.getMaxTransactionsPerBlock(this.storage);
}

public async persistBlock({
block,
verify = false,
Expand Down Expand Up @@ -630,7 +662,7 @@ export class Blockchain {

const nep5BalancePairs = assetKeys.map((key) => {
const script = new ScriptBuilder().emitAppCall(key.assetScriptHash, 'balanceOf', key.userScriptHash).build();
const callReceipt = this.runEngineWrapper({ script, gas: 1, snapshot: 'main' });
const callReceipt = this.invokeScript(script);
const balanceBuffer = callReceipt.stack[0].getInteger().toBuffer();

return { key, value: new Nep5Balance({ balanceBuffer, lastUpdatedBlock: this.currentBlockIndex }) };
Expand Down
49 changes: 27 additions & 22 deletions packages/neo-one-node-consensus/src/Consensus.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
/// <reference types="@reactivex/ix-es2015-cjs" />
import { common, crypto, ECPoint, PrivateKey, UInt160 } from '@neo-one/client-common';
import { common, crypto, ECPoint, PrivateKey, UInt160, UInt256Hex } from '@neo-one/client-common';
import { createChild, nodeLogger } from '@neo-one/logger';
import { ConsensusPayload, Node, Transaction } from '@neo-one/node-core';
import { ConsensusContext, ConsensusPayload, Node, Transaction } from '@neo-one/node-core';
import { composeDisposables, Disposable, noopDisposable, utils as commonUtils } from '@neo-one/utils';
import { AsyncIterableX } from '@reactivex/ix-es2015-cjs/asynciterable/asynciterablex';
import { scan } from '@reactivex/ix-es2015-cjs/asynciterable/pipe/scan';
import { initializeNewConsensus } from './common';
import { ConsensusContext } from './ConsensusContext';
import { ConsensusQueue } from './ConsensusQueue';
import { Context } from './context';
import { handleConsensusPayload } from './handleConsensusPayload';
import { handlePersistBlock } from './handlePersistBlock';
import { handleTransactionReceived } from './handleTransactionReceived';
import { runConsensus } from './runConsensus';
import { TimerContext } from './TimerContext';
import { Event, Result } from './types';

const logger = createChild(nodeLogger, { component: 'consensus' });
Expand All @@ -35,8 +34,9 @@ export class Consensus {
private mutableTimer: number | undefined;
private readonly options: InternalOptions;
private readonly node: Node;
private mutableConsensusContext: ConsensusContext;
private mutableTimerContext: TimerContext;
private mutableStartPromise: Promise<void> | undefined;
private readonly knownHashes = new Set<UInt256Hex>();

public constructor({ options, node }: { readonly options: Options; readonly node: Node }) {
this.mutableQueue = new ConsensusQueue();
Expand All @@ -52,7 +52,7 @@ export class Consensus {
};

this.node = node;
this.mutableConsensusContext = new ConsensusContext();
this.mutableTimerContext = new TimerContext();
}

public async start(): Promise<Disposable> {
Expand Down Expand Up @@ -102,20 +102,20 @@ export class Consensus {
}

public nowSeconds(): number {
return this.mutableConsensusContext.nowSeconds();
return this.mutableTimerContext.nowSeconds();
}

public async fastForwardOffset(seconds: number): Promise<void> {
if (this.options.privateNet) {
this.mutableConsensusContext.fastForwardOffset(seconds);
this.mutableTimerContext.fastForwardOffset(seconds);
} else {
throw new Error('Can only fast forward on a private network.');
}
}

public async fastForwardToTime(seconds: number): Promise<void> {
if (this.options.privateNet) {
this.mutableConsensusContext.fastForwardToTime(seconds);
this.mutableTimerContext.fastForwardToTime(seconds);
} else {
throw new Error('Can only fast forward on a private network.');
}
Expand All @@ -131,7 +131,7 @@ export class Consensus {
}

public async reset(): Promise<void> {
this.mutableConsensusContext = new ConsensusContext();
this.mutableTimerContext = new TimerContext();
}

public async resume(): Promise<void> {
Expand All @@ -156,29 +156,34 @@ export class Consensus {
const initialResult = await initializeNewConsensus({
blockchain: this.node.blockchain,
publicKey: options.publicKey,
consensusContext: this.mutableConsensusContext,
privateKey: options.privateKey,
timerContext: this.mutableTimerContext,
verificationContext: this.node.getNewVerificationContext(),
});

await AsyncIterableX.from(this.mutableQueue)
.pipe(
scan(async (context: Context, event: Event) => {
scan(async (context: ConsensusContext, event: Event) => {
let result;
switch (event.type) {
case 'handlePersistBlock':
result = await handlePersistBlock({
context,
blockchain: this.node.blockchain,
publicKey: options.publicKey,
consensusContext: this.mutableConsensusContext,
privateKey: options.privateKey,
timerContext: this.mutableTimerContext,
});
this.knownHashes.clear();

break;
case 'handleConsensusPayload':
result = await handleConsensusPayload({
context,
node: this.node,
knownHashes: this.knownHashes,
privateKey: options.privateKey,
payload: event.payload,
consensusContext: this.mutableConsensusContext,
timerContext: this.mutableTimerContext,
});

break;
Expand All @@ -188,7 +193,7 @@ export class Consensus {
node: this.node,
privateKey: options.privateKey,
transaction: event.transaction,
consensusContext: this.mutableConsensusContext,
timerContext: this.mutableTimerContext,
});

break;
Expand All @@ -197,7 +202,7 @@ export class Consensus {
context,
node: this.node,
options,
consensusContext: this.mutableConsensusContext,
timerContext: this.mutableTimerContext,
}).catch((err) => {
if (event.promise !== undefined) {
event.promise.reject(err);
Expand All @@ -223,19 +228,19 @@ export class Consensus {
logger.info({ name: 'neo_consensus_stop' }, 'Consensus stopped.');
}

private handleResult(result: Result<Context>): Context {
if (result.timerSeconds !== undefined) {
this.handleTimer(result.timerSeconds);
private handleResult(result: Result): ConsensusContext {
if (result.timerMS !== undefined) {
this.handleTimer(result.timerMS);
}

return result.context;
}

private handleTimer(mutableTimerSeconds: number): void {
private handleTimer(mutableTimerMS: number): void {
this.clearTimer();
this.mutableTimer = setTimeout(
() => this.mutableQueue.write({ type: 'timer' }),
mutableTimerSeconds * MS_IN_SECOND,
mutableTimerMS,
// tslint:disable-next-line no-any no-useless-cast
) as any;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { utils } from '@neo-one/utils';

export class ConsensusContext {
export class TimerContext {
private mutableFastForwardSeconds: number;

public constructor() {
Expand All @@ -11,6 +11,10 @@ export class ConsensusContext {
return utils.nowSeconds() + this.mutableFastForwardSeconds;
}

public nowMilliseconds(): number {
return Date.now() + this.mutableFastForwardSeconds * 1000;
}

public fastForwardOffset(seconds: number) {
if (seconds >= 0) {
this.mutableFastForwardSeconds += seconds;
Expand Down
Loading

0 comments on commit fc94328

Please sign in to comment.