import Fixed64 from '../common/fixed64' ; import { REST_API , TX_MAX_SIG_SIZE } from '../consts' ; import { Address , PrivateKey , SignatureScheme } from '../crypto' ; import { PublicKey } from '../crypto/PublicKey' ; import { ERROR_CODE } from '../error' ; import AbiFunction from '../smartcontract/abi/abiFunction' ; import { Parameter } from '../smartcontract/abi/parameter' ; import { num2hexstring , reverseHex , str2hexstr } from '../utils' ; import { ParameterType } from './../smartcontract/abi/parameter' ; import opcode from './opcode' ; import DeployCode from './payload/deployCode' ; import InvokeCode from './payload/invokeCode' ; import { comparePublicKeys } from './program' ; import { buildWasmContractParam , createCodeParamsScript , serializeAbiFunction , writeVarBytes } from './scriptBuilder' ; import { Transaction , TxType } from './transaction' ; import { makeTransferTx , makeTransferTxV2 } from '../smartcontract/nativevm/ontAssetTxBuilder' ; import { buildGetDDOTx , buildRegisterOntidTx } from '../smartcontract/nativevm/ontidContractTxBuilder' ; import { VmType } from './payload/deployCode' ; import { TxSignature } from './txSignature' ; export const Default_params = { Action : 'sendrawtransaction' , Version : '1.0.0' , Type : '' , Op : 'test' } ; param tx param privateKey param schema export const signTransaction = ( tx , privateKey , schema ? ) => { const signature = TxSignature . create ( tx , privateKey , schema ) ; tx . sigs = [ signature ] ; } ; param tx param privateKey param schema export const signTransactionAsync = async ( tx , privateKey , schema ? ) => { const signature = await TxSignature . createAsync ( tx , privateKey , schema ) ; tx . sigs = [ signature ] ; } ; param tx param privateKey param schema export const addSign = ( tx , privateKey , schema ? ) => { const signature = TxSignature . create ( tx , privateKey , schema ) ; tx . sigs . push ( signature ) ; } ; const equalPks = ( pks1 , pks2 ) : boolean => { if ( pks1 === pks2 ) { return true ; } pks1 . sort ( comparePublicKeys ) ; pks2 . sort ( comparePublicKeys ) ; if ( pks1 . length !== pks2 . length ) { return false ; } for ( let i = 0 ; i < pks1 . length ; i ++ ) { if ( pks1 [ i ] . key !== pks2 [ i ] . key ) { return false ; } } return true ; } ; param tx param M param pubKeys param privateKey param scheme export const signTx = ( tx , M , pubKeys , privateKey , scheme ? ) => { if ( tx . sigs . length === 0 ) { tx . sigs = [ ] ; } else { if ( tx . sigs . length > TX_MAX_SIG_SIZE || M > pubKeys . length || M <= 0 || pubKeys . length === 0 ) { throw ERROR_CODE . INVALID_PARAMS ; } for ( let i = 0 ; i < tx . sigs . length ; i ++ ) { if ( equalPks ( tx . sigs [ i ] . pubKeys , pubKeys ) ) { if ( tx . sigs [ i ] . sigData . length + 1 > pubKeys . length ) { throw new Error ( 'Too many sigData' ) ; } const signData = privateKey . sign ( tx , scheme ) . serializeHex ( ) ; tx . sigs [ i ] . sigData . push ( signData ) ; return ; } } } const sig = new TxSignature ( ) ; sig . M = M ; sig . pubKeys = pubKeys ; sig . sigData = [ privateKey . sign ( tx , scheme ) . serializeHex ( ) ] ; tx . sigs . push ( sig ) ; } ; param funcName param params param contractAddr param gasPrice param gasLimit param payer export const makeInvokeTransaction = ( funcName , params , contractAddr , gasPrice ? , gasLimit ? , payer ? , ledgerCompatible = true ) => { const tx = new Transaction ( ) ; tx . type = TxType . Invoke ; let args = '' ; if ( typeof params === 'string' ) { args = params ; } else { const abiFunc = new AbiFunction ( funcName , '' , params ) ; args = serializeAbiFunction ( abiFunc , ledgerCompatible ) ; } let code = args + num2hexstring ( opcode . APPCALL ) ; code += contractAddr . serialize ( ) ; const payload = new InvokeCode ( ) ; payload . code = code ; tx . payload = payload ; if ( gasLimit ) { tx . gasLimit = new Fixed64 ( gasLimit ) ; } if ( gasPrice ) { tx . gasPrice = new Fixed64 ( gasPrice ) ; } if ( payer ) { tx . payer = payer ; } return tx ; } ; export function buildWasmVmInvokeCode ( contractaddress , params ) { let result = '' ; result += contractaddress . serialize ( ) ; const args = buildWasmContractParam ( params ) ; result += writeVarBytes ( args ) ; return result ; } param funcName param params param contractAddress param gasPrice param gasLimit param payer export function makeWasmVmInvokeTransaction ( funcName , params , contractAddress , gasPrice , gasLimit , payer ? ) { const tx = new Transaction ( ) ; tx . type = TxType . InvokeWasm ; const paramFunc = new Parameter ( 'method' , ParameterType . String , funcName ) ; const paramsAll = [ paramFunc , ... params ] ; const code = buildWasmVmInvokeCode ( contractAddress , paramsAll ) ; const payload = new InvokeCode ( ) ; payload . code = code ; tx . payload = payload ; if ( gasLimit ) { tx . gasLimit = new Fixed64 ( gasLimit ) ; } if ( gasPrice ) { tx . gasPrice = new Fixed64 ( gasPrice ) ; } if ( payer ) { tx . payer = payer ; } return tx ; } param code param name param codeVersion param author param email param desp param vmType param gasPrice param gasLimit param payer export function makeDeployCodeTransaction ( code , name = '' , codeVersion = '1.0' , author = '' , email = '' , desp = '' , vmType , gasPrice , gasLimit , payer ? ) { const dc = new DeployCode ( ) ; dc . author = author ; dc . code = code ; dc . version = codeVersion ; dc . description = desp ; dc . email = email ; dc . name = name ; if ( typeof vmType === 'boolean' ) { dc . vmType = VmType . NEOVM_TYPE ; } else { dc . vmType = vmType ; } const tx = new Transaction ( ) ; tx . version = 0x00 ; tx . payload = dc ; tx . type = TxType . Deploy ; tx . gasLimit = new Fixed64 ( gasLimit ) ; tx . gasPrice = new Fixed64 ( gasPrice ) ; if ( payer ) { tx . payer = payer ; } return tx ; } deprecated param tx param isPreExec export function buildTxParam ( tx , isPreExec = false ) { const op = isPreExec ? { PreExec : '1' } : { } ; const serialized = tx . serialize ( ) ; return JSON . stringify ( Object . assign ( { } , Default_params , { Data : serialized } , op ) ) ; } deprecated param tx param method export function buildRpcParam ( tx , method ? ) { const param = tx . serialize ( ) ; const result = { jsonrpc : '2.0' , method : method || 'sendrawtransaction' , params : [ param ] , id : 10 } ; return result ; } deprecated param tx export function buildRestfulParam ( tx ) { const param = tx . serialize ( ) ; return { Action : 'sendrawtransaction' , Version : '1.0.0' , Data : param } ; } deprecated param url param preExec export function sendRawTxRestfulUrl ( url , preExec = false ) { if ( url . charAt ( url . length - 1 ) === '/' ) { url = url . substring ( 0 , url . length - 1 ) ; } let restUrl = url + REST_API . sendRawTx ; if ( preExec ) { restUrl += '?preExec=1' ; } return restUrl ; } export function transferStringParameter ( value ) { const strs = value . split ( ':' ) ; if ( strs . length < 2 ) { throw new Error ( 'Invalid parameter. ' + value ) ; } const pType = value . substring ( 0 , value . indexOf ( ':' ) ) ; const pVal = value . substring ( value . indexOf ( ':' ) + 1 ) ; const p = new Parameter ( '' , pType as ParameterType , pVal ) ; if ( p . type === ParameterType . Address ) { p . type = ParameterType . ByteArray ; p . value = new Address ( p . value ) . serialize ( ) ; } return p ; } export function transformMapParameter ( value ) { const map = { } ; for ( const k of Object . keys ( value ) ) { const v = value [ k ] ; if ( typeof v === 'number' ) { map [ k ] = new Parameter ( '' , ParameterType . Integer , v ) ; } else if ( typeof v === 'boolean' ) { map [ k ] = new Parameter ( '' , ParameterType . Boolean , v ) ; } else if ( Array . isArray ( v ) ) { map [ k ] = new Parameter ( '' , ParameterType . Array , transformArrayParameter ( v ) ) ; } else if ( typeof v === 'object' ) { map [ k ] = new Parameter ( '' , ParameterType . Map , transformMapParameter ( v ) ) ; } else if ( typeof v === 'string' ) { map [ k ] = transferStringParameter ( v ) ; } } return map ; } export function transformArrayParameter ( val ) { const list = [ ] ; for ( const v of val ) { let p = new Parameter ( '' , ParameterType . ByteArray , v ) ; if ( typeof v === 'number' ) { p . type = ParameterType . Integer ; } else if ( typeof v === 'boolean' ) { p . type = ParameterType . Boolean ; } else if ( Array . isArray ( v ) ) { p . type = ParameterType . Array ; p . value = transformArrayParameter ( v ) ; } else if ( typeof v === 'object' ) { p . type = ParameterType . Map ; p . value = transformMapParameter ( v ) ; } else if ( typeof v === 'string' ) { p = transferStringParameter ( v ) ; } list . push ( p ) ; } return list ; } export function transformParameter ( arg ) { const name = arg . name ; const value = arg . value ; let p = new Parameter ( name , ParameterType . ByteArray , value ) ; if ( typeof value === 'number' ) { p . type = ParameterType . Integer ; p . value = Number ( value ) ; } else if ( typeof value === 'boolean' ) { p . type = ParameterType . Boolean ; p . value = Boolean ( value ) ; } else if ( Array . isArray ( value ) ) { p . type = ParameterType . Array ; p . value = transformArrayParameter ( value ) ; } else if ( typeof value === 'object' ) { p . type = ParameterType . Map ; p . value = transformMapParameter ( value ) ; } else if ( typeof value === 'string' ) { p = transferStringParameter ( value ) ; } return p ; } export function buildParamsByJson ( json ) { const paramsList = [ ] ; const functions = json . functions ; for ( const obj of functions ) { const { operation , args } = obj ; const list = [ ] ; list . push ( str2hexstr ( operation ) ) ; const temp = [ ] ; for ( const arg of args ) { temp . push ( transformParameter ( arg ) ) ; } list . push ( temp ) ; paramsList . push ( list ) ; } return paramsList ; } export function makeTransactionsByJson ( json , ledgerCompatible = true ) { if ( ! json ) { throw new Error ( 'Invalid parameter. Expect JSON object' ) ; } if ( ! json . action || ( json . action !== 'invoke' && json . action !== 'invokeRead' && json . action !== 'invokePasswordFree' ) ) { throw new Error ( 'Invalid parameter. The action type must be "invoke or invokeRead"' ) ; } if ( ! json . params || ! json . params . invokeConfig ) { throw new Error ( 'Invalid parameter. The params can not be empty.' ) ; } const invokeConfig = json . params . invokeConfig ; let { payer , gasPrice , gasLimit , contractHash } = invokeConfig ; if ( ! contractHash ) { throw new Error ( 'Invalid parameter. The contractHash can not be empty.' ) ; } const contractAddr = new Address ( reverseHex ( contractHash ) ) ; payer = payer ? new Address ( payer ) : null ; gasPrice = gasPrice + '' || '500' ; gasLimit = gasLimit + '' || '200000' ; const txList = [ ] ; if ( contractHash . indexOf ( '00000000000000000000000000000000000000' ) > - 1 ) { const tx = buildNativeTxFromJson ( invokeConfig ) ; txList . push ( tx ) ; } else { const parameters = buildParamsByJson ( invokeConfig ) ; for ( const list of parameters ) { const params = createCodeParamsScript ( list , ledgerCompatible ) ; const tx = makeInvokeTransaction ( '' , params , contractAddr , gasPrice , gasLimit , payer , ledgerCompatible ) ; txList . push ( tx ) ; } } return txList ; } export function makeTransactionsByJsonV2 ( json , ledgerCompatible = true ) { if ( ! json ) { throw new Error ( 'Invalid parameter. Expect JSON object' ) ; } if ( ! json . action || ( json . action !== 'invoke' && json . action !== 'invokeRead' && json . action !== 'invokePasswordFree' ) ) { throw new Error ( 'Invalid parameter. The action type must be "invoke or invokeRead"' ) ; } if ( ! json . params || ! json . params . invokeConfig ) { throw new Error ( 'Invalid parameter. The params can not be empty.' ) ; } const invokeConfig = json . params . invokeConfig ; let { payer , gasPrice , gasLimit , contractHash } = invokeConfig ; if ( ! contractHash ) { throw new Error ( 'Invalid parameter. The contractHash can not be empty.' ) ; } const contractAddr = new Address ( reverseHex ( contractHash ) ) ; payer = payer ? new Address ( payer ) : null ; gasPrice = gasPrice + '' || '500' ; gasLimit = gasLimit + '' || '200000' ; const txList = [ ] ; if ( contractHash . indexOf ( '00000000000000000000000000000000000000' ) > - 1 ) { const tx = buildNativeTxFromJsonV2 ( invokeConfig ) ; txList . push ( tx ) ; } else { const parameters = buildParamsByJson ( invokeConfig ) ; for ( const list of parameters ) { const params = createCodeParamsScript ( list , ledgerCompatible ) ; const tx = makeInvokeTransaction ( '' , params , contractAddr , gasPrice , gasLimit , payer , ledgerCompatible ) ; txList . push ( tx ) ; } } return txList ; } export function buildNativeTxFromJson ( json ) { const funcArgs = json . functions [ 0 ] ; const args = funcArgs . args ; if ( json . contractHash . indexOf ( '02' ) > - 1 || json . contractHash . indexOf ( '01' ) > - 1 ) { const tokenType = json . contractHash . indexOf ( '02' ) > - 1 ? 'ONG' : 'ONT' ; if ( funcArgs . operation === 'transfer' ) { const from = new Address ( args [ 0 ] . value . split ( ':' ) [ 1 ] ) ; const to = new Address ( args [ 1 ] . value . split ( ':' ) [ 1 ] ) ; const amount = args [ 2 ] . value . split ( ':' ) [ 1 ] + '' ; const payer = new Address ( json . payer ) ; const tx = makeTransferTx ( tokenType , from , to , amount , json . gasPrice , json . gasLimit , payer ) ; return tx ; } } else if ( json . contractHash . indexOf ( '03' ) > - 1 ) { if ( funcArgs . operation === 'regIDWithPublicKey' ) { const ontid = args [ 0 ] . value . substr ( args [ 0 ] . value . indexOf ( ':' ) + 1 ) ; const pk = new PublicKey ( args [ 1 ] . value . split ( ':' ) [ 1 ] ) ; const payer = new Address ( json . payer ) ; const tx = buildRegisterOntidTx ( ontid , pk , json . gasPrice , json . gasLimit , payer ) ; return tx ; } else if ( funcArgs . operation === 'getDDO' ) { const ontid = args [ 0 ] . value . substr ( args [ 0 ] . value . indexOf ( ':' ) + 1 ) ; const tx = buildGetDDOTx ( ontid ) ; return tx ; } } } export function buildNativeTxFromJsonV2 ( json ) { const funcArgs = json . functions [ 0 ] ; const args = funcArgs . args ; if ( json . contractHash . indexOf ( '02' ) > - 1 || json . contractHash . indexOf ( '01' ) > - 1 ) { const tokenType = json . contractHash . indexOf ( '02' ) > - 1 ? 'ONG' : 'ONT' ; if ( funcArgs . operation === 'transfer' ) { const from = new Address ( args [ 0 ] . value . split ( ':' ) [ 1 ] ) ; const to = new Address ( args [ 1 ] . value . split ( ':' ) [ 1 ] ) ; const amount = args [ 2 ] . value . split ( ':' ) [ 1 ] + '' ; const payer = new Address ( json . payer ) ; const tx = makeTransferTxV2 ( tokenType , from , to , amount , json . gasPrice , json . gasLimit , payer ) ; return tx ; } } else if ( json . contractHash . indexOf ( '03' ) > - 1 ) { if ( funcArgs . operation === 'regIDWithPublicKey' ) { const ontid = args [ 0 ] . value . substr ( args [ 0 ] . value . indexOf ( ':' ) + 1 ) ; const pk = new PublicKey ( args [ 1 ] . value . split ( ':' ) [ 1 ] ) ; const payer = new Address ( json . payer ) ; const tx = buildRegisterOntidTx ( ontid , pk , json . gasPrice , json . gasLimit , payer ) ; return tx ; } else if ( funcArgs . operation === 'getDDO' ) { const ontid = args [ 0 ] . value . substr ( args [ 0 ] . value . indexOf ( ':' ) + 1 ) ; const tx = buildGetDDOTx ( ontid ) ; return tx ; } } }