/* * 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 * as bigInt from 'big-integer'; import * as Long from 'long'; import { Address } from '../../../../common/address'; import * as C from '../../../../common/constants'; import { TracedError } from '../../../../common/error'; import { safeAdd } from '../../../../common/safeMath'; import { bigIntToBytes } from '../../../../common/utils'; import { ST_STORAGE } from '../../../../core/state/dataEntryPrefix'; import { Reader } from '../../../../vm/utils/reader'; import { Writer } from '../../../../vm/utils/writer'; import { contracts, NativeVmService } from '../../../nativeVmService'; import { BYTE_FALSE, BYTE_TRUE, OngContractAddress, OntContractAddress } from '../utils/params'; import { decodeAddress, decodeVarUint } from '../utils/serialization'; import { genUInt32StorageItem, genUInt64StorageItem, getStorageUInt64 } from '../utils/store'; import { calcUnbindOng } from '../utils/unbindOng'; import { deserializeState, deserializeTransferFrom, deserializeTransfers, serializeState, State, TransferFrom } from './states'; import * as U from './utils'; export const TRANSFER_FLAG = 1; export const APPROVE_FLAG = 2; export function initOnt() { contracts.set(OntContractAddress.toHexString(), registerOntContract); } function registerOntContract(native: NativeVmService) { native.register(U.INIT_NAME, ontInit); native.register(U.TRANSFER_NAME, ontTransfer); native.register(U.APPROVE_NAME, ontApprove); native.register(U.TRANSFERFROM_NAME, ontTransferFrom); native.register(U.NAME_NAME, ontName); native.register(U.SYMBOL_NAME, ontSymbol); native.register(U.DECIMALS_NAME, ontDecimals); native.register(U.TOTALSUPPLY_NAME, ontTotalSupply); native.register(U.BALANCEOF_NAME, ontBalanceOf); native.register(U.ALLOWANCE_NAME, ontAllowance); } function ontInit(native: NativeVmService): Buffer { const context = native.contextRef.currentContext()!; const contract = context.contractAddress; const amount = getStorageUInt64(native, U.genTotalSupplyKey(contract)); if (amount.gt(0)) { throw new TracedError('Init ont has been completed!'); } const distribute = new Map(); const source = native.input; let buf: Buffer; try { buf = new Reader(source).readVarBytes(); } catch (e) { throw new TracedError('serialization.ReadVarBytes, contract params deserialize error!', e); } const input = new Reader(buf); const num = decodeVarUint(input); let sum = Long.ZERO; for (let i = 0; i < num.toNumber(); i++) { const addr = decodeAddress(input); const addrStr = addr.toHexString(); const value = decodeVarUint(input); sum = safeAdd(sum, value); const prev = distribute.get(addrStr) || Long.ZERO; distribute.set(addrStr, prev.add(value)); } if (sum.neq(C.ONT_TOTAL_SUPPLY)) { throw new TracedError(`wrong config. total supply ${sum.toString()} != ${C.ONT_TOTAL_SUPPLY.toString()}`); } for (const [addrStr, val] of distribute) { const addr = new Address(addrStr); const balanceKey = U.genBalanceKey(contract, addr); const item = genUInt64StorageItem(val); native.stateStore.add(ST_STORAGE, balanceKey, item); U.addNotifications(native, contract, { from: new Address(), to: addr, value: val }); } native.stateStore.add(ST_STORAGE, U.genTotalSupplyKey(contract), genUInt64StorageItem(C.ONT_TOTAL_SUPPLY)); return BYTE_TRUE; } function ontTransfer(native: NativeVmService): Buffer { const source = new Reader(native.input); const transfers = deserializeTransfers(source); const contract = native.contextRef.currentContext()!.contractAddress; for (const v of transfers.states) { if (v.value.isZero()) { continue; } if (v.value.gt(C.ONT_TOTAL_SUPPLY)) { throw new TracedError(`transfer ont amount:${v.value.toString()} over totalSupply:${C.ONT_TOTAL_SUPPLY}`); } const { fromBalance, toBalance } = U.transfer(native, contract, v); grantOng(native, contract, v.from, fromBalance); grantOng(native, contract, v.to, toBalance); U.addNotifications(native, contract, v); } return BYTE_TRUE; } export function ontTransferFrom(native: NativeVmService): Buffer { const source = new Reader(native.input); const state: TransferFrom = deserializeTransferFrom(source); if (state.value.isZero()) { return BYTE_FALSE; } if (state.value.gt(C.ONT_TOTAL_SUPPLY)) { throw new TracedError(`transferFrom ont amount:${state.value} over totalSupply: ${C.ONT_TOTAL_SUPPLY}`); } const contract = native.contextRef.currentContext()!.contractAddress; const { fromBalance, toBalance } = U.transferedFrom(native, contract, state); grantOng(native, contract, state.from, fromBalance); grantOng(native, contract, state.to, toBalance); U.addNotifications(native, contract, { from: state.from, to: state.to, value: state.value }); return BYTE_TRUE; } export function ontApprove(native: NativeVmService): Buffer { const source = new Reader(native.input); const state: State = deserializeState(source); if (state.value.isZero()) { return BYTE_FALSE; } if (state.value.gt(C.ONT_TOTAL_SUPPLY)) { throw new TracedError(`approve ont amount:${state.value} over totalSupply:${C.ONT_TOTAL_SUPPLY}`); } if (native.contextRef.checkWitness(state.from) === false) { throw new TracedError('authentication failed!'); } const contract = native.contextRef.currentContext()!.contractAddress; native.stateStore.add(ST_STORAGE, U.genApproveKey(contract, state.from, state.to), genUInt64StorageItem(state.value)); return BYTE_TRUE; } export function ontName(native: NativeVmService): Buffer { return new Buffer(C.ONT_NAME); } export function ontDecimals(native: NativeVmService): Buffer { return bigIntToBytes(bigInt(C.ONT_DECIMALS)); } export function ontSymbol(native: NativeVmService): Buffer { return new Buffer(C.ONT_SYMBOL); } export function ontTotalSupply(native: NativeVmService): Buffer { const contract = native.contextRef.currentContext()!.contractAddress; const amount = getStorageUInt64(native, U.genTotalSupplyKey(contract)); return bigIntToBytes(bigInt(amount.toString())); } export function ontBalanceOf(native: NativeVmService): Buffer { return getBalanceValue(native, TRANSFER_FLAG); } export function ontAllowance(native: NativeVmService): Buffer { return getBalanceValue(native, APPROVE_FLAG); } export function getBalanceValue(native: NativeVmService, flag: number): Buffer { const source = new Reader(native.input); const from = decodeAddress(source); const contract = native.contextRef.currentContext()!.contractAddress; let key: Buffer; if (flag === APPROVE_FLAG) { const to = decodeAddress(source); key = U.genApproveKey(contract, from, to); } else if (flag === TRANSFER_FLAG) { key = U.genBalanceKey(contract, from); } else { throw new TracedError('invalid flag'); } const amount = getStorageUInt64(native, key); return bigIntToBytes(bigInt(amount.toString())); } export function grantOng(native: NativeVmService, contract: Address, address: Address, balance: Long) { const startOffset = U.getUnboundOffset(native, contract, address); if (native.time <= C.GENESIS_BLOCK_TIMESTAMP) { return; } const endOffset = native.time - C.GENESIS_BLOCK_TIMESTAMP; if (endOffset < startOffset) { throw new TracedError(`grant Ong error: wrong timestamp endOffset: ${endOffset} < startOffset: ${startOffset}`); } else if (endOffset === startOffset) { return; } if (!balance.isZero()) { const value = calcUnbindOng(balance, startOffset, endOffset); const args = getApproveArgs(native, contract, OngContractAddress, address, value); native.nativeCall(OngContractAddress, 'approve', args); } native.stateStore.add(ST_STORAGE, U.genAddressUnboundOffsetKey(contract, address), genUInt32StorageItem(endOffset)); } export function getApproveArgs( native: NativeVmService, contract: Address, ongContract: Address, address: Address, value: Long ): Buffer { const approve: State = { from: contract, to: address, value }; const stateValue = getStorageUInt64(native, U.genApproveKey(ongContract, approve.from, approve.to)); approve.value = approve.value.add(stateValue); const w = new Writer(); serializeState(w, approve); return w.getBytes(); }