/* * Copyright (C) 2018 Matus Zamborsky & The ontology Authors * This file is part of The ontology library. * * The ontology is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The ontology is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with The ontology. If not, see . */ import { Address } from '../common/address'; import { TracedError } from '../common/error'; import { StateStore } from '../core/state/stateStore'; import { Transaction } from '../core/transaction'; import { LogEventInfo, NotificationCallback, NotifyEventInfo } from '../event/notifyEvents'; import { newStackItem } from '../vm/func/common'; import { StackItem } from '../vm/types/stackItem'; import { Reader } from '../vm/utils/reader'; import { Writer } from '../vm/utils/writer'; import { ContextRef } from './context'; import { Contract } from './states/contract'; type RegisterService = (native: NativeVmService) => void; type Handler = (native: NativeVmService) => Buffer; export const contracts: Map = new Map(); interface NativeVmServiceOptions { stateStore: StateStore; contextRef: ContextRef; code: Buffer; tx: Transaction; time: number; // height: number; - unused serviceMap: Map; notificationCallback?: NotificationCallback; } export class NativeVmService { stateStore: StateStore; contextRef: ContextRef; code: Buffer; input: Buffer; tx: Transaction; time: number; // height: number; - unused private notifications: NotifyEventInfo[]; private logs: LogEventInfo[]; private serviceMap: Map; private notificationCallback?: NotificationCallback; constructor(options: NativeVmServiceOptions) { this.stateStore = options.stateStore; this.contextRef = options.contextRef; this.code = options.code; this.tx = options.tx; this.time = options.time; this.serviceMap = options.serviceMap; // this.height = options.height; this.notifications = []; this.logs = []; this.notificationCallback = options.notificationCallback; } invoke(): StackItem | undefined { const contract = Contract.deserialize(new Reader(this.code)); const addr = contract.address.toHexString(); const services = contracts.get(addr); if (services === undefined) { throw new TracedError(`Native contract address ${addr} haven't been registered.`); } services(this); const service = this.serviceMap.get(contract.method); if (service === undefined) { throw new TracedError(`Native contract ${addr} doesn't support this function ${contract.method}.`); } const args = this.input; this.input = contract.args; this.contextRef.pushContext({ contractAddress: contract.address, code: new Buffer('') }); const notifications = this.notifications; this.notifications = []; try { const result = service(this); this.contextRef.popContext(); this.contextRef.pushNotifications(this.notifications); this.contextRef.pushLogs(this.logs); this.notifications = notifications; this.input = args; return newStackItem(result); } catch (e) { throw new TracedError('[Invoke] Native serivce function execute error!', e); } } register(methodName: string, handler: Handler) { this.serviceMap.set(methodName, handler); } addNotification(event: NotifyEventInfo) { this.notifications.push(event); if (this.notificationCallback !== undefined) { this.notificationCallback(event); } } nativeCall(address: Address, method: string, args: Buffer): StackItem | undefined { const c = new Contract({ version: 0, address, method, args }); const w = new Writer(); c.serialize(w); this.code = w.getBytes(); return this.invoke(); } }