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

Preview4 blockchain changes #2284

Merged
Merged
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
2 changes: 2 additions & 0 deletions packages/neo-one-client-common/src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ const isWildcard = (input: unknown): input is Wildcard => input === '*';

const NEGATIVE_SATOSHI_FIXED8 = new BN(-1);
const TEN_FIXED8 = fixed8FromDecimal(10);
const TWENTY_FIXED8 = fixed8FromDecimal(20);
const ONE_HUNDRED_FIXED8 = fixed8FromDecimal(100);
const FOUR_HUNDRED_FIXED8 = fixed8FromDecimal(400);
const FIVE_HUNDRED_FIXED8 = fixed8FromDecimal(500);
Expand Down Expand Up @@ -249,6 +250,7 @@ export const common = {
MAX_UINT256,
NEGATIVE_SATOSHI_FIXED8,
TEN_FIXED8,
TWENTY_FIXED8,
ONE_HUNDRED_FIXED8,
FOUR_HUNDRED_FIXED8,
FIVE_HUNDRED_FIXED8,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface FeelessTransactionModelAdd<

export const MAX_TRANSACTION_ATTRIBUTES = 16;
export const MAX_TRANSACTION_SIZE = 102400;
export const MAX_VALID_UNTIL_BLOCK_INCREMENT = 2102400;
export const MAX_VALID_UNTIL_BLOCK_INCREMENT = 5760; // 24 hours
export const DEFAULT_VERSION = 0;

export class FeelessTransactionModel<
Expand Down
5 changes: 4 additions & 1 deletion packages/neo-one-client-common/src/models/trigger.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import { InvalidFormatError } from '../common';

export enum TriggerType {
OnPersist = 0x01,
PostPersist = 0x02,
Verification = 0x20,
System = 0x01,
Application = 0x40,
System = OnPersist | PostPersist,
All = OnPersist | PostPersist | Verification | Application,
}

export type TriggerTypeJSON = keyof typeof TriggerType;
Expand Down
9 changes: 3 additions & 6 deletions packages/neo-one-client-common/src/models/vm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,18 +224,17 @@ export enum SysCall {
'System.Blockchain.GetTransaction' = 'System.Blockchain.GetTransaction',
'System.Blockchain.GetTransactionHeight' = 'System.Blockchain.GetTransactionHeight',
'System.Blockchain.GetTransactionFromBlock' = 'System.Blockchain.GetTransactionFromBlock',
'System.Blockchain.GetContract' = 'System.Blockchain.GetContract',
'System.Callback.Create' = 'System.Callback.Create',
'System.Callback.CreateFromMethod' = 'System.Callback.CreateFromMethod',
'System.Callback.CreateFromSyscall' = 'System.Callback.CreateFromSyscall',
'System.Callback.Invoke' = 'System.Callback.Invoke',
'System.Contract.Create' = 'System.Contract.Create',
'System.Contract.Update' = 'System.Contract.Update',
'System.Contract.Destroy' = 'System.Contract.Destroy',
'System.Contract.Call' = 'System.Contract.Call',
'System.Contract.CallEx' = 'System.Contract.CallEx',
'System.Contract.CallNative' = 'System.Contract.CallNative',
'System.Contract.IsStandard' = 'System.Contract.IsStandard',
'System.Contract.GetCallFlags' = 'System.Contract.GetCallFlags',
'System.Contract.NativeOnPersist' = 'System.Contract.NativeOnPersist',
'System.Contract.NativePostPersist' = 'System.Contract.NativePostPersist',
'System.Contract.CreateStandardAccount' = 'System.Contract.CreateStandardAccount',
'Neo.Crypto.RIPEMD160' = 'Neo.Crypto.RIPEMD160',
'Neo.Crypto.SHA256' = 'Neo.Crypto.SHA256',
Expand All @@ -254,8 +253,6 @@ export enum SysCall {
'System.Iterator.Concat' = 'System.Iterator.Concat',
'System.Json.Serialize' = 'System.Json.Serialize',
'System.Json.Deserialize' = 'System.Json.Deserialize',
'Neo.Native.Deploy' = 'Neo.Native.Deploy',
'Neo.Native.Call' = 'Neo.Native.Call',
'System.Runtime.Platform' = 'System.Runtime.Platform',
'System.Runtime.GetTrigger' = 'System.Runtime.GetTrigger',
'System.Runtime.GetTime' = 'System.Runtime.GetTime',
Expand Down
26 changes: 22 additions & 4 deletions packages/neo-one-client-core/src/user/LocalUserAccountProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,13 +177,27 @@ export class LocalUserAccountProvider<TKeyStore extends KeyStore = KeyStore, TPr
// tslint:disable-next-line
for (const hash of hashes) {
try {
const witnessScript = this.getUserAccount({
let witnessScript: Buffer | undefined;
witnessScript = this.tryGetUserAccount({
network,
address: crypto.scriptHashToAddress({
addressVersion: common.NEO_ADDRESS_VERSION,
scriptHash: hash,
}),
}).contract.script;
})?.contract.script;

if (witnessScript === undefined && transaction.witnesses.length !== 0) {
transaction.witnesses.forEach((witness) => {
if (crypto.toScriptHash(witness.verification).equals(hash)) {
witnessScript = witness.verification;
}
});
}

// TODO: Implemented looking up the witness verification script by contract hash: https://github.com/neo-project/neo/blob/master/src/neo/Wallets/Wallet.cs#L390
if (witnessScript === undefined) {
throw new Error('Contract witness script not yet implemented');
}

const multiSig = crypto.isMultiSigContractWithResult(witnessScript);
if (multiSig.result) {
Expand Down Expand Up @@ -533,10 +547,14 @@ export class LocalUserAccountProvider<TKeyStore extends KeyStore = KeyStore, TPr
}
}

private getUserAccount(id: UserAccountID): UserAccount {
const userAccount = this.keystore
private tryGetUserAccount(id: UserAccountID): UserAccount | undefined {
return this.keystore
.getUserAccounts()
.find((account) => account.id.network === id.network && account.id.address === id.address);
}

private getUserAccount(id: UserAccountID): UserAccount {
const userAccount = this.tryGetUserAccount(id);
if (userAccount === undefined) {
throw new UnknownAccountError(id.address);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
export * from './ContractABIModel';
export * from './ContractEventDescriptorModel';
export * from './ContractFeaturesModel';
export * from './ContractGroupModel';
export * from './ContractManifestModel';
export * from './ContractMethodDescriptorModel';
Expand Down
17 changes: 9 additions & 8 deletions packages/neo-one-node-blockchain/src/Blockchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const logger = createChild(nodeLogger, { service: 'blockchain' });

export interface CreateBlockchainOptions {
readonly onPersistNativeContractScript?: Buffer;
readonly postPersistNativeContractScript?: Buffer;
readonly settings: BlockchainSettings;
readonly storage: Storage;
readonly native: NativeContainer;
Expand Down Expand Up @@ -167,6 +168,7 @@ export class Blockchain {
public readonly verifyWitnesses = verifyWitnesses;
public readonly settings: BlockchainSettings;
public readonly onPersistNativeContractScript: Buffer;
public readonly postPersistNativeContractScript: Buffer;

// tslint:disable-next-line: readonly-array
private readonly headerIndexCache: HeaderIndexCache;
Expand Down Expand Up @@ -195,12 +197,15 @@ export class Blockchain {
this.vm = options.vm;
this.onPersistNativeContractScript =
options.onPersistNativeContractScript ?? utils.getOnPersistNativeContractScript();
this.postPersistNativeContractScript =
options.postPersistNativeContractScript ?? utils.getPostPersistNativeContractScript();
this.deserializeWireContext = {
messageMagic: this.settings.messageMagic,
validatorsCount: this.settings.validatorsCount,
};
this.mutableCurrentBlock = options.currentBlock;
this.onPersist = options.onPersist === undefined ? this.vm.updateSnapshots : options.onPersist;
this.onPersist =
options.onPersist === undefined ? () => Promise.resolve(this.vm.updateSnapshots()) : options.onPersist;
this.start();
}

Expand Down Expand Up @@ -507,7 +512,6 @@ export class Blockchain {
snapshot: 'clone',
container: transaction,
gas: common.ONE_HUNDRED_FIXED8,
testMode: true,
});
}

Expand All @@ -516,7 +520,6 @@ export class Blockchain {
script,
snapshot: 'main',
container: signers,
gas: common.TEN_FIXED8,
});
}

Expand All @@ -526,8 +529,7 @@ export class Blockchain {
container,
persistingBlock,
offset = 0,
testMode = false,
gas = new BN(0),
gas = common.TEN_FIXED8,
}: RunEngineOptions): CallReceipt {
return this.vm.withSnapshots(({ main, clone }) => {
const handler = snapshot === 'main' ? main : clone;
Expand All @@ -543,11 +545,9 @@ export class Blockchain {
container,
snapshot,
gas,
testMode,
},
(engine) => {
engine.loadScript(script);
engine.setInstructionPointer(offset);
engine.loadScript({ script: script, initialPosition: offset });
engine.execute();

return utils.getCallReceipt(engine, container);
Expand Down Expand Up @@ -801,6 +801,7 @@ export class Blockchain {
private createPersistingBlockchain(): PersistingBlockchain {
return new PersistingBlockchain({
onPersistNativeContractScript: this.onPersistNativeContractScript,
postPersistNativeContractScript: this.postPersistNativeContractScript,
vm: this.vm,
});
}
Expand Down
82 changes: 54 additions & 28 deletions packages/neo-one-node-blockchain/src/PersistingBlockchain.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
// tslint:disable no-array-mutation no-object-mutation
import { TriggerType, VMState } from '@neo-one/client-common';
import { TriggerType, VMState, common } from '@neo-one/client-common';
import { ApplicationExecuted, Block, SnapshotHandler, Transaction, VM } from '@neo-one/node-core';
import { PersistNativeContractsError } from './errors';
import { utils, utils as blockchainUtils } from './utils';
import { PersistNativeContractsError, PostPersistError } from './errors';
import { utils } from './utils';

interface PersistingBlockchainOptions {
readonly vm: VM;
readonly onPersistNativeContractScript: Buffer;
readonly postPersistNativeContractScript: Buffer;
}

export class PersistingBlockchain {
private readonly vm: VM;
private readonly onPersistNativeContractScript: Buffer;
private readonly postPersistNativeContractScript: Buffer;

public constructor(options: PersistingBlockchainOptions) {
this.vm = options.vm;
this.onPersistNativeContractScript = options.onPersistNativeContractScript;
this.postPersistNativeContractScript = options.postPersistNativeContractScript;
}

public persistBlock(
Expand All @@ -31,34 +34,38 @@ export class PersistingBlockchain {
const appsExecuted: ApplicationExecuted[] = [];

main.setPersistingBlock(block);
if (block.index > 0) {
const executed = this.vm.withApplicationEngine<ApplicationExecuted>(
{
trigger: TriggerType.System,
snapshot: 'main',
gas: utils.ZERO,
testMode: true,
},
(engine) => {
engine.loadScript(this.onPersistNativeContractScript);
const result = engine.execute();
if (result !== VMState.HALT) {
throw new PersistNativeContractsError();
}

return blockchainUtils.getApplicationExecuted(engine);
},
);

appsExecuted.push(executed);
}
const executed = this.vm.withApplicationEngine<ApplicationExecuted>(
{
trigger: TriggerType.OnPersist,
snapshot: 'main',
gas: common.TWENTY_FIXED8,
},
(engine) => {
engine.loadScript({ script: this.onPersistNativeContractScript });
const result = engine.execute();
if (result !== VMState.HALT) {
throw new PersistNativeContractsError();
}

return utils.getApplicationExecuted(engine);
},
);

appsExecuted.push(executed);

main.addBlock(block);
main.clone();

const executedTransactions = this.persistTransactions(block, main, clone);

return { changeBatch: main.getChangeSet(), applicationsExecuted: appsExecuted.concat(executedTransactions) };
main.changeBlockHashIndex(block.index, block.hash);

const postPersistExecuted = this.postPersist();

return {
changeBatch: main.getChangeSet(),
applicationsExecuted: appsExecuted.concat(executedTransactions).concat(postPersistExecuted),
};
});
}

Expand Down Expand Up @@ -89,10 +96,9 @@ export class PersistingBlockchain {
container: transaction,
snapshot: 'clone',
gas: transaction.systemFee,
testMode: false,
},
(engine) => {
engine.loadScript(transaction.script);
engine.loadScript({ script: transaction.script });
const state = engine.execute();
if (state === VMState.HALT) {
clone.deleteTransaction(transaction.hash);
Expand All @@ -102,7 +108,27 @@ export class PersistingBlockchain {
main.clone();
}

return blockchainUtils.getApplicationExecuted(engine, transaction);
return utils.getApplicationExecuted(engine, transaction);
},
);
}

private postPersist(): ApplicationExecuted {
return this.vm.withApplicationEngine(
{
trigger: TriggerType.PostPersist,
container: undefined,
gas: common.TWENTY_FIXED8,
snapshot: 'main',
},
(engine) => {
engine.loadScript({ script: this.postPersistNativeContractScript });
const result = engine.execute();
if (result !== VMState.HALT) {
throw new PostPersistError();
}

return utils.getApplicationExecuted(engine);
},
);
}
Expand Down
4 changes: 4 additions & 0 deletions packages/neo-one-node-blockchain/src/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ export const PersistNativeContractsError = makeErrorWithCode(
'PERSIST_NATIVE_CONTRACTS_FAILED',
() => 'Engine state !== HALT when persisting native contract scripts',
);
export const PostPersistError = makeErrorWithCode(
'POST_PERSIST_SCRIPT_FAILED',
() => 'Engine state !== HALT when running post persist scripts',
);
export const ContractStateFetchError = makeErrorWithCode(
'FETCH_CONTRACT_FAILED',
(hash: string) => `failed to fetch contract state with hash: ${hash} from storage.`,
Expand Down
Loading