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

P4 native contracts #2286

Closed
Closed
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
10 changes: 10 additions & 0 deletions packages/neo-one-client-common/src/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,15 @@ const createPrivateKey = (): PrivateKey => common.bufferToPrivateKey(randomBytes

const toScriptHash = hash160;

const getContractHash = (sender: UInt160, script: Buffer) => {
const builder = new ScriptBuilder();
builder.emitOp('ABORT');
builder.emitPushUInt160(sender);
builder.emitPush(script);

return toScriptHash(builder.build());
};

// Takes various formats and converts to standard ECPoint
const toECPoint = (publicKey: Buffer): ECPoint => toECPointFromKeyPair(ec().keyFromPublic(publicKey));

Expand Down Expand Up @@ -891,6 +900,7 @@ export const crypto = {
verify,
privateKeyToPublicKey,
toScriptHash,
getContractHash,
toECPoint,
createKeyPair,
scriptHashToAddress,
Expand Down
1 change: 1 addition & 0 deletions packages/neo-one-client-common/src/models/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,7 @@ export interface ContractParameterDefinitionJSON {

export interface ContractJSON {
readonly id: number;
readonly updatecounter: number;
readonly hash: string;
readonly script: string;
readonly manifest: ContractManifestJSON;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
import { BinaryWriter, createSerializeWire, SerializableWire, SerializeWire } from '@neo-one/client-common';
import { ContractManifestModel } from './manifest';
import { UInt160 } from '@neo-one/client-common';

export interface ContractStateModelAdd<TContractManifest extends ContractManifestModel = ContractManifestModel> {
readonly id: number;
readonly updateCounter: number;
readonly hash: UInt160;
readonly script: Buffer;
readonly manifest: TContractManifest;
}

export class ContractStateModel<TContractManifest extends ContractManifestModel = ContractManifestModel>
implements SerializableWire {
export class ContractStateModel<TContractManifest extends ContractManifestModel = ContractManifestModel> {
public readonly id: number;
public readonly updateCounter: number;
public readonly hash: UInt160;
public readonly script: Buffer;
public readonly manifest: TContractManifest;
public readonly serializeWire: SerializeWire = createSerializeWire(this.serializeWireBase.bind(this));

public constructor({ script, manifest, id }: ContractStateModelAdd<TContractManifest>) {
public constructor({ script, hash, manifest, id, updateCounter }: ContractStateModelAdd<TContractManifest>) {
this.id = id;
this.updateCounter = updateCounter;
this.hash = hash;
this.script = script;
this.manifest = manifest;
}

public serializeWireBase(writer: BinaryWriter): void {
writer.writeUInt32LE(this.id);
writer.writeVarBytesLE(this.script);
this.manifest.serializeWireBase(writer);
}
}
52 changes: 20 additions & 32 deletions packages/neo-one-node-core/src/ContractState.ts
Original file line number Diff line number Diff line change
@@ -1,64 +1,51 @@
import {
common,
ContractJSON,
createSerializeWire,
crypto,
IOHelper,
JSONHelper,
UInt160,
} from '@neo-one/client-common';
import { common, ContractJSON, IOHelper, JSONHelper, UInt160 } from '@neo-one/client-common';
import { ContractStateModel } from '@neo-one/client-full-common';
import { ContractManifest } from './manifest';
import { DeserializeWireBaseOptions, DeserializeWireOptions } from './Serializable';
import { BinaryReader, utils } from './utils';
import { utils } from './utils';
import { StackItem } from './StackItems';
import { assertArrayStackItem } from 'dist/cjs';

export interface ContractStateAdd {
readonly id: number;
readonly updateCounter: number;
readonly hash: UInt160;
readonly script: Buffer;
readonly manifest: ContractManifest;
}

export type ContractKey = UInt160;

export class ContractState extends ContractStateModel<ContractManifest> {
public static deserializeWireBase(options: DeserializeWireBaseOptions): ContractState {
const { reader } = options;
const id = reader.readInt32LE();
const script = reader.readVarBytesLE();
const manifest = ContractManifest.deserializeWireBase(options);
public static fromStackItem(stackItem: StackItem): ContractState {
const { array } = assertArrayStackItem(stackItem);
const id = array[0].getInteger().toNumber();
const updateCounter = array[1].getInteger().toNumber();
const hash = common.bufferToUInt160(array[2].getBuffer());
const script = array[3].getBuffer();
const manifest = ContractManifest.parseBytes(array[4].getBuffer());

return new this({
return new ContractState({
id,
updateCounter,
hash,
script,
manifest,
});
}

public static deserializeWire(options: DeserializeWireOptions): ContractState {
return this.deserializeWireBase({
context: options.context,
reader: new BinaryReader(options.buffer),
});
}

public readonly serializeWire = createSerializeWire(this.serializeWireBase.bind(this));

private readonly scriptHashInternal = utils.lazy(() => common.asUInt160(crypto.hash160(this.script)));
private readonly sizeInternal = utils.lazy(
() => IOHelper.sizeOfUInt32LE + IOHelper.sizeOfVarBytesLE(this.script) + this.manifest.size,
);

public get scriptHash() {
return this.scriptHashInternal();
}

public get size() {
return this.sizeInternal();
}

public clone() {
return new ContractState({
id: this.id,
updateCounter: this.updateCounter,
hash: this.hash,
script: this.script,
manifest: this.manifest,
});
Expand All @@ -67,7 +54,8 @@ export class ContractState extends ContractStateModel<ContractManifest> {
public serializeJSON(): ContractJSON {
return {
id: this.id,
hash: JSONHelper.writeUInt160(this.scriptHash),
updatecounter: this.updateCounter,
hash: JSONHelper.writeUInt160(this.hash),
script: JSONHelper.writeBase64Buffer(this.script),
manifest: this.manifest.serializeJSON(),
};
Expand Down
4 changes: 4 additions & 0 deletions packages/neo-one-node-core/src/DesignationRole.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum DesignationRole {
StateValidator = 4,
Oracle = 8,
}
39 changes: 34 additions & 5 deletions packages/neo-one-node-core/src/Native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { BN } from 'bn.js';
import { ReadFindStorage } from './Storage';
import { StorageItem } from './StorageItem';
import { StorageKey } from './StorageKey';
import { ContractState } from './ContractState';
import { OracleRequest } from './OracleRequest';
import { DesignationRole } from './DesignationRole';

export type OracleRequestResults = ReadonlyArray<readonly [BN, OracleRequest]>;

export interface NativeContractStorageContext {
readonly storages: ReadFindStorage<StorageKey, StorageItem>;
Expand All @@ -13,44 +18,68 @@ export interface NativeContract {
readonly name: string;
}

export interface NEP5NativeContract extends NativeContract {
export interface NEP17NativeContract extends NativeContract {
readonly symbol: string;
readonly decimals: number;

readonly totalSupply: (storage: NativeContractStorageContext) => Promise<BN>;
readonly balanceOf: (storage: NativeContractStorageContext, account: UInt160) => Promise<BN>;
}

export interface GASContract extends NEP5NativeContract {}
export interface GASContract extends NEP17NativeContract {}

export interface PolicyContract extends NativeContract {
readonly getMaxTransactionsPerBlock: (storage: NativeContractStorageContext) => Promise<number>;
readonly getMaxBlockSize: (storage: NativeContractStorageContext) => Promise<number>;
readonly getMaxBlockSystemFee: (storage: NativeContractStorageContext) => Promise<BN>;
readonly getFeePerByte: (storage: NativeContractStorageContext) => Promise<BN>;
readonly getBlockedAccounts: (storage: NativeContractStorageContext) => Promise<readonly UInt160[]>;
readonly getExecFeeFactor: (storage: NativeContractStorageContext) => Promise<number>;
readonly getStoragePrice: (storage: NativeContractStorageContext) => Promise<number>;
readonly isBlocked: (storage: NativeContractStorageContext, account: UInt160) => Promise<boolean>;
}

export interface Candidate {
readonly publicKey: ECPoint;
readonly votes: BN;
}

export interface NEOContract extends NEP5NativeContract {
export interface NEOContract extends NEP17NativeContract {
readonly totalAmount: BN;
readonly effectiveVoterTurnout: number;

readonly totalSupply: () => Promise<BN>;
readonly getCandidates: (storage: NativeContractStorageContext) => Promise<readonly Candidate[]>;
readonly getValidators: (storage: NativeContractStorageContext) => Promise<readonly ECPoint[]>;
readonly getCommittee: (storage: NativeContractStorageContext) => Promise<readonly ECPoint[]>;
readonly getCommitteeAddress: (storage: NativeContractStorageContext) => Promise<UInt160>;
readonly unclaimedGas: (storage: NativeContractStorageContext, account: UInt160, end: number) => Promise<BN>;
readonly getNextBlockValidators: (storage: NativeContractStorageContext) => Promise<readonly ECPoint[]>;
}

export interface ManagementContract extends NativeContract {
readonly getContract: (storage: NativeContractStorageContext, hash: UInt160) => Promise<ContractState>;
readonly listContracts: (storage: NativeContractStorageContext) => Promise<readonly ContractState[]>;
}

export interface DesignationContract extends NativeContract {
readonly getDesignatedByRole: (
storage: NativeContractStorageContext,
role: DesignationRole,
height: number,
index: number,
) => Promise<readonly ECPoint[]>;
}

export interface OracleContract extends NativeContract {
readonly getRequest: (storage: NativeContractStorageContext, id: BN) => Promise<OracleRequest>;
readonly getRequests: (storage: NativeContractStorageContext) => Promise<OracleRequestResults>;
readonly getRequestsByUrl: (storage: NativeContractStorageContext, url: string) => Promise<readonly OracleRequest[]>;
}

export interface NativeContainer {
readonly GAS: GASContract;
readonly NEO: NEOContract;
readonly Policy: PolicyContract;
readonly Management: ManagementContract;
readonly Designation: DesignationContract;
readonly Oracle: OracleContract;
}
62 changes: 62 additions & 0 deletions packages/neo-one-node-core/src/OracleRequest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { UInt256, UInt160, common } from '@neo-one/client-common';
import { BN } from 'bn.js';
import { StackItem, assertArrayStackItem } from './StackItems';

interface OracleRequestAdd {
readonly originalTxid: UInt256;
readonly gasForResponse: BN;
readonly url: string;
readonly filter: string;
readonly callbackContract: UInt160;
readonly callbackMethod: string;
readonly userData: Buffer;
}

export class OracleRequest {
public static fromStackItem(stackItem: StackItem): OracleRequest {
const { array } = assertArrayStackItem(stackItem);
const originalTxid = common.bufferToUInt256(array[0].getBuffer());
const gasForResponse = array[1].getInteger();
const url = array[2].getString();
const filter = array[3].getString();
const callbackContract = common.bufferToUInt160(array[4].getBuffer());
const callbackMethod = array[5].getString();
const userData = array[6].getBuffer();

return new OracleRequest({
originalTxid,
gasForResponse,
url,
filter,
callbackContract,
callbackMethod,
userData,
});
}

public originalTxid: UInt256;
public gasForResponse: BN;
public url: string;
public filter: string;
public callbackContract: UInt160;
public callbackMethod: string;
public userData: Buffer;

public constructor({
originalTxid,
gasForResponse,
url,
filter,
callbackContract,
callbackMethod,
userData,
}: OracleRequestAdd) {
this.originalTxid = originalTxid;
this.gasForResponse = gasForResponse;
this.url = url;
this.filter = filter;
this.callbackContract = callbackContract;
this.callbackMethod = callbackMethod;
this.userData = userData;
}
}
2 changes: 2 additions & 0 deletions packages/neo-one-node-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ export * from './HeaderHashList';
export * from './Native';
export * from './network';
export * from './Notification';
export * from './OracleRequest';
export * from './payload';
export * from './DesignationRole';
export * from './Serializable';
export * from './Settings';
export * from './Signer';
Expand Down
18 changes: 4 additions & 14 deletions packages/neo-one-node-core/src/manifest/ContractManifest.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
import { ContractManifestJSON, IOHelper, JSONHelper, UInt160 } from '@neo-one/client-common';
import { ContractManifestModel, getContractProperties } from '@neo-one/client-full-common';
import { DeserializeWireBaseOptions, DeserializeWireOptions } from '../Serializable';
import { ContractManifestModel } from '@neo-one/client-full-common';
import { BinaryReader, utils } from '../utils';
import { ContractABI } from './ContractABI';
import { ContractGroup } from './ContractGroup';
import { ContractPermission } from './ContractPermission';

export class ContractManifest extends ContractManifestModel<ContractABI, ContractGroup, ContractPermission> {
public static deserializeWireBase(options: DeserializeWireBaseOptions): ContractManifest {
const { reader } = options;
const json = JSON.parse(reader.readVarString(this.maxLength));

return this.deserializeJSON(json);
}

public static deserializeWire(options: DeserializeWireOptions): ContractManifest {
return this.deserializeWireBase({
context: options.context,
reader: new BinaryReader(options.buffer),
});
public static parseBytes(bytes: Buffer) {
const reader = new BinaryReader(bytes);
return this.deserializeJSON(JSON.parse(reader.readVarString(this.maxLength)));
}

private static deserializeJSON(json: ContractManifestJSON) {
Expand Down
31 changes: 31 additions & 0 deletions packages/neo-one-node-native/src/CachedCommittee.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { StackItem, assertArrayStackItem, assertStructStackItem } from '@neo-one/node-core';
import { ECPoint, common } from '@neo-one/client-common';
import { BN } from 'bn.js';

interface CachedCommitteeElement {
readonly publicKey: ECPoint;
readonly votes: BN;
}

export class CachedCommittee {
public static fromStackItem(item: StackItem) {
const arrayItem = assertArrayStackItem(item);

const members = arrayItem.array.map((element) => {
const structItem = assertStructStackItem(element);

return {
publicKey: common.bufferToECPoint(structItem.array[0].getBuffer()),
votes: structItem.array[1].getInteger(),
};
});

return new CachedCommittee(members);
}

public readonly members: readonly CachedCommitteeElement[];

public constructor(members: readonly CachedCommitteeElement[]) {
this.members = members;
}
}
Loading