2019-07-10 04:01:15 +00:00
|
|
|
/*
|
|
|
|
* @project: TERA
|
|
|
|
* @version: Development (beta)
|
|
|
|
* @license: MIT (not for evil)
|
|
|
|
* @copyright: Yuriy Ivanov (Vtools) 2017-2019 [progr76@gmail.com]
|
|
|
|
* Web: https://terafoundation.org
|
|
|
|
* Twitter: https://twitter.com/terafoundation
|
|
|
|
* Telegram: https://t.me/terafoundation
|
|
|
|
*/
|
|
|
|
let WebApi2: any = {};
|
|
|
|
import * as crypto from 'crypto';
|
2019-07-12 14:46:26 +00:00
|
|
|
import { TYPE_TRANSACTION } from '../constant/account';
|
2019-07-10 04:01:15 +00:00
|
|
|
WebApi2.GenerateKeys = function(Params) {
|
|
|
|
var KeyPair = crypto.createECDH('secp256k1');
|
|
|
|
var PrivKey = global.sha3(crypto.randomBytes(32));
|
|
|
|
KeyPair.setPrivateKey(Buffer.from(PrivKey));
|
|
|
|
var PubKey = KeyPair.getPublicKey('' as any, 'compressed');
|
|
|
|
return { result: 1, PrivKey: global.GetHexFromArr(PrivKey), PubKey: global.GetHexFromArr(PubKey), Meta: Params ? Params.Meta : undefined };
|
|
|
|
};
|
|
|
|
WebApi2.CreateAccount = function(Params, response) {
|
|
|
|
if (typeof Params === "object" && Params.Name && Params.PrivKey) {
|
|
|
|
var KeyPair = crypto.createECDH('secp256k1');
|
|
|
|
KeyPair.setPrivateKey(Buffer.from(global.GetArrFromHex(Params.PrivKey)));
|
|
|
|
var PubKey = KeyPair.getPublicKey('' as any, 'compressed');
|
2019-07-12 14:46:26 +00:00
|
|
|
var TR: any = { Type: TYPE_TRANSACTION.TYPE_TRANSACTION_CREATE, Currency: Params.Currency, PubKey: PubKey, Name: Params.Name, Smart: Params.Smart, };
|
2019-07-10 04:01:15 +00:00
|
|
|
var Body = global.BufLib.GetBufferFromObject(TR, global.FORMAT_CREATE, 1000, {}, 1);
|
|
|
|
Body = Body.slice(0, Body.len + 12);
|
|
|
|
SendTransaction(Body, TR as any, Params.Wait, function(result, text) {
|
|
|
|
var Result = {
|
2019-07-10 07:59:04 +00:00
|
|
|
result: result, text: text, TxID: global.GetHexFromArr(TR._TxID.slice(0, global.TR_TICKET_HASH_LENGTH + 6)), BlockNum: TR._BlockNum,
|
2019-07-10 04:01:15 +00:00
|
|
|
Meta: Params.Meta,
|
|
|
|
};
|
|
|
|
var Str = JSON.stringify(Result);
|
|
|
|
response.end(Str);
|
|
|
|
});
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return { result: 0, Meta: Params ? Params.Meta : undefined };
|
|
|
|
};
|
|
|
|
var MapSendID = {};
|
|
|
|
WebApi2.Send = function(Params, response, A, bJsonRet) {
|
|
|
|
if (typeof Params !== "object")
|
|
|
|
return { result: 0 };
|
|
|
|
var Coin;
|
|
|
|
if (typeof Params.Amount === "number")
|
2019-07-10 07:59:04 +00:00
|
|
|
Coin = global.COIN_FROM_FLOAT(Params.Amount);
|
2019-07-10 04:01:15 +00:00
|
|
|
else
|
|
|
|
Coin = Params.Amount;
|
2019-07-10 07:59:04 +00:00
|
|
|
var FromNum = global.ParseNum(Params.FromID);
|
2019-07-10 04:01:15 +00:00
|
|
|
if (!Coin)
|
|
|
|
return { result: 0, Meta: Params.Meta, text: "Params.Amount required" };
|
|
|
|
if (!FromNum)
|
|
|
|
return { result: 0, Meta: Params.Meta, text: "Params.FromID required" };
|
|
|
|
if (!Params.ToID)
|
|
|
|
return { result: 0, Meta: Params.Meta, text: "Params.ToID required" };
|
|
|
|
var ToPubKeyArr = [];
|
|
|
|
var ToID = 0;
|
|
|
|
if (typeof Params.ToID === "string" && Params.ToID.length === 66)
|
|
|
|
ToPubKeyArr = global.GetArrFromHex(Params.ToID);
|
|
|
|
else
|
2019-07-10 07:59:04 +00:00
|
|
|
ToID = global.ParseNum(Params.ToID);
|
2019-07-10 04:01:15 +00:00
|
|
|
var DataFrom = global.DApps.Accounts.ReadState(FromNum);
|
|
|
|
if (!DataFrom)
|
|
|
|
return { result: 0, Meta: Params.Meta, text: "Error read account: " + FromNum };
|
|
|
|
var OperationID;
|
|
|
|
if (!MapSendID[FromNum]) {
|
|
|
|
OperationID = DataFrom.Value.OperationID + 10;
|
|
|
|
MapSendID[FromNum] = {};
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
OperationID = MapSendID[FromNum].OperationID;
|
2019-07-10 07:59:04 +00:00
|
|
|
if (((new Date() as any) - MapSendID[FromNum].Date) > 8 * 1000) {
|
2019-07-10 04:01:15 +00:00
|
|
|
OperationID += 20;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
OperationID++;
|
|
|
|
MapSendID[FromNum].OperationID = OperationID;
|
|
|
|
MapSendID[FromNum].Date = Date.now();
|
2019-07-10 07:59:04 +00:00
|
|
|
var TR: any = {
|
2019-07-10 04:01:15 +00:00
|
|
|
Type: 111, Version: 3, Reserve: 0, FromID: FromNum, OperationID: OperationID, To: [{
|
|
|
|
PubKey: ToPubKeyArr, ID: ToID, SumCOIN: Coin.SumCOIN,
|
|
|
|
SumCENT: Coin.SumCENT
|
|
|
|
}], Description: Params.Description, Body: [],
|
|
|
|
};
|
|
|
|
if (bJsonRet)
|
|
|
|
return { result: 1, Tx: TR };
|
|
|
|
if (!Params.FromPrivKey)
|
|
|
|
return { result: 0, Meta: Params.Meta, text: "Params.FromPrivKey required" };
|
|
|
|
TR.Sign = global.DApps.Accounts.GetSignTransferTx(TR, global.GetArrFromHex(Params.FromPrivKey));
|
2019-07-10 07:59:04 +00:00
|
|
|
var Body = global.BufLib.GetBufferFromObject(TR, global.FORMAT_MONEY_TRANSFER3, global.MAX_TRANSACTION_SIZE, {}, 1);
|
2019-07-10 04:01:15 +00:00
|
|
|
Body = Body.slice(0, Body.len + 12);
|
|
|
|
SendTransaction(Body, TR, Params.Wait, function(result, text) {
|
|
|
|
var Result = {
|
2019-07-10 07:59:04 +00:00
|
|
|
result: result, text: text, TxID: global.GetHexFromArr(TR._TxID.slice(0, global.TR_TICKET_HASH_LENGTH + 6)), BlockNum: TR._BlockNum,
|
2019-07-10 04:01:15 +00:00
|
|
|
Meta: Params.Meta,
|
|
|
|
};
|
|
|
|
var Str = JSON.stringify(Result);
|
|
|
|
response.end(Str);
|
|
|
|
});
|
|
|
|
return null;
|
|
|
|
};
|
|
|
|
WebApi2.GetBalance = function(Params, response) {
|
|
|
|
if (typeof Params === "object") {
|
2019-07-10 07:59:04 +00:00
|
|
|
var arr = global.DApps.Accounts.GetRowsAccounts(global.ParseNum(Params.AccountID), 1);
|
2019-07-10 04:01:15 +00:00
|
|
|
if (arr.length) {
|
|
|
|
var Account = arr[0];
|
|
|
|
var Value = Account.Value;
|
|
|
|
var Result = {
|
|
|
|
result: 1, SumCOIN: Value.SumCOIN, SumCENT: Value.SumCENT, Currency: Account.Currency, PubKey: global.GetHexFromArr(Account.PubKey),
|
|
|
|
Meta: Params.Meta,
|
|
|
|
};
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return { result: 0, Meta: Params ? Params.Meta : undefined };
|
|
|
|
};
|
|
|
|
WebApi2.GetTransaction = function(Params) {
|
|
|
|
if (typeof Params === "object" && Params.TxID) {
|
|
|
|
var Arr = global.GetArrFromHex(Params.TxID);
|
2019-07-10 07:59:04 +00:00
|
|
|
var BlockNum = global.ReadUintFromArr(Arr, global.TR_TICKET_HASH_LENGTH);
|
2019-07-10 04:01:15 +00:00
|
|
|
var Block = global.SERVER.ReadBlockDB(BlockNum);
|
|
|
|
if (Block && Block.arrContent) {
|
|
|
|
for (var i = 0; i < Block.arrContent.length; i++) {
|
|
|
|
var Body = Block.arrContent[i];
|
|
|
|
var Arr2 = GetTxID(BlockNum, Body);
|
|
|
|
if (global.CompareArr(Arr2, Arr) === 0) {
|
|
|
|
return GetTransactionFromBody(Params, Block, i, Body);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (typeof Params === "object" && Params.BlockNum && Params.TrNum !== undefined) {
|
|
|
|
var Block = global.SERVER.ReadBlockDB(Params.BlockNum);
|
|
|
|
if (Block && Block.arrContent) {
|
|
|
|
var Body = Block.arrContent[Params.TrNum];
|
|
|
|
if (Body) {
|
|
|
|
return GetTransactionFromBody(Params, Block, Params.TrNum, Body);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return { result: 0, Meta: Params ? Params.Meta : undefined };
|
|
|
|
};
|
|
|
|
WebApi2.GetHistoryTransactions = function(Params) {
|
|
|
|
if (typeof Params === "object" && Params.AccountID) {
|
|
|
|
if (!Params.Count)
|
|
|
|
Params.Count = 100;
|
|
|
|
if (Params.Confirm === undefined)
|
|
|
|
Params.Confirm = 8;
|
|
|
|
var arr = global.DApps.Accounts.GetHistory(Params.AccountID, Params.Count, Params.NextPos, Params.Confirm);
|
|
|
|
if (Params.GetTxID || Params.GetDescription) {
|
|
|
|
for (var i = 0; i < arr.length; i++) {
|
|
|
|
var Item = arr[i];
|
|
|
|
var Block = global.SERVER.ReadBlockDB(Item.BlockNum);
|
|
|
|
if (!Block || (!Block.arrContent))
|
|
|
|
continue;
|
|
|
|
var Body = Block.arrContent[Item.TrNum];
|
|
|
|
if (!Body)
|
|
|
|
continue;
|
|
|
|
if (Params.GetTxID) {
|
|
|
|
Item.TxID = global.GetHexFromArr(GetTxID(Item.BlockNum, Body));
|
|
|
|
}
|
|
|
|
if (Params.GetDescription) {
|
|
|
|
var TR = global.DApps.Accounts.GetObjectTransaction(Body);
|
|
|
|
if (TR) {
|
|
|
|
Item.Description = TR.Description;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var Result = { result: arr.length > 0 ? 1 : 0, History: arr, Tail: global.DApps.Accounts.DBStateHistory.Read(Params.AccountID), Meta: Params ? Params.Meta : undefined };
|
|
|
|
return Result;
|
|
|
|
}
|
|
|
|
return { result: 0, Meta: Params ? Params.Meta : undefined };
|
|
|
|
};
|
|
|
|
WebApi2.CreateRawTransaction = function(Params) {
|
|
|
|
if (typeof Params === "object") {
|
|
|
|
var Ret = WebApi2.Send(Params, undefined, undefined, 1);
|
|
|
|
TxArrToHex(Ret.Tx);
|
|
|
|
Ret.Meta = Params.Meta;
|
|
|
|
return Ret;
|
|
|
|
}
|
|
|
|
return { result: 0, Meta: Params ? Params.Meta : undefined };
|
|
|
|
};
|
|
|
|
WebApi2.SignRawTransaction = function(Params) {
|
|
|
|
if (typeof Params === "object" && Params.Tx) {
|
|
|
|
if (!Params.FromPrivKey)
|
|
|
|
return { result: 0, Meta: Params.Meta, text: "Params.FromPrivKey required" };
|
|
|
|
if (typeof Params.Tx !== "object")
|
|
|
|
return { result: 0, Meta: Params.Meta, text: "Params.Tx required" };
|
|
|
|
if (!Params.Tx.To || !Params.Tx.To.length)
|
|
|
|
return { result: 0, Meta: Params.Meta, text: "Params.Tx.To required" };
|
|
|
|
var TR = Params.Tx;
|
|
|
|
TxHexToArr(TR);
|
|
|
|
TR.Sign = global.DApps.Accounts.GetSignTransferTx(TR, global.GetArrFromHex(Params.FromPrivKey));
|
|
|
|
TxArrToHex(TR);
|
|
|
|
var Ret = { result: 1, Tx: TR, Meta: Params ? Params.Meta : undefined };
|
|
|
|
return Ret;
|
|
|
|
}
|
|
|
|
return { result: 0, Meta: Params ? Params.Meta : undefined };
|
|
|
|
};
|
|
|
|
WebApi2.SendRawTransaction = function(Params, response) {
|
|
|
|
if (typeof Params === "object" && Params.Tx) {
|
|
|
|
if (typeof Params.Tx !== "object")
|
|
|
|
return { result: 0, Meta: Params.Meta, text: "Params.Tx required" };
|
|
|
|
if (!Params.Tx.To || !Params.Tx.To.length)
|
|
|
|
return { result: 0, Meta: Params.Meta, text: "Params.Tx.To required" };
|
|
|
|
if (!Params.Tx.Sign)
|
|
|
|
return { result: 0, Meta: Params.Meta, text: "Params.Tx.Sign required" };
|
|
|
|
var TR = Params.Tx;
|
|
|
|
TxHexToArr(TR);
|
2019-07-10 07:59:04 +00:00
|
|
|
var Body = global.BufLib.GetBufferFromObject(TR, global.FORMAT_MONEY_TRANSFER3, global.MAX_TRANSACTION_SIZE, {}, 1);
|
2019-07-10 04:01:15 +00:00
|
|
|
Body = Body.slice(0, Body.len + 12);
|
|
|
|
SendTransaction(Body, TR, Params.Wait, function(result, text) {
|
|
|
|
var Result = {
|
2019-07-10 07:59:04 +00:00
|
|
|
result: result, text: text, TxID: global.GetHexFromArr(TR._TxID.slice(0, global.TR_TICKET_HASH_LENGTH + 6)), BlockNum: TR._BlockNum,
|
2019-07-10 04:01:15 +00:00
|
|
|
Meta: Params.Meta,
|
|
|
|
};
|
|
|
|
var Str = JSON.stringify(Result);
|
|
|
|
response.end(Str);
|
|
|
|
});
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return { result: 0, Meta: Params ? Params.Meta : undefined };
|
|
|
|
};
|
|
|
|
|
|
|
|
function TxArrToHex(TR) {
|
|
|
|
if (TR && TR.To && TR.To[0].PubKey) {
|
|
|
|
if (TR.To[0].PubKey.length)
|
|
|
|
TR.To[0].PubKey = global.GetHexFromArr(TR.To[0].PubKey);
|
|
|
|
else
|
|
|
|
TR.To[0].PubKey = "";
|
|
|
|
if (TR.Sign && TR.Sign.length)
|
|
|
|
TR.Sign = global.GetHexFromArr(TR.Sign);
|
|
|
|
else
|
|
|
|
TR.Sign = "";
|
|
|
|
TR.Body = undefined;
|
|
|
|
TR.Reserve = undefined;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function TxHexToArr(TR) {
|
|
|
|
TR.Body = [];
|
|
|
|
if (TR.Sign && TR.Sign.length)
|
|
|
|
TR.Sign = global.GetArrFromHex(TR.Sign);
|
|
|
|
else
|
|
|
|
TR.Sign = [];
|
|
|
|
for (var i = 0; i < TR.To.length; i++) {
|
|
|
|
TR.To[i].PubKey = global.GetArrFromHex(TR.To[i].PubKey);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
var DELTA_FOR_TIME_TX = 1;
|
|
|
|
|
|
|
|
function GetTxID(BlockNum, Body) {
|
2019-07-10 07:59:04 +00:00
|
|
|
var Nonce = global.ReadUintFromArr(Body, Body.length - 6);
|
2019-07-10 04:01:15 +00:00
|
|
|
var Arr2 = CreateTxID(Body, BlockNum, Nonce);
|
2019-07-10 07:59:04 +00:00
|
|
|
return Arr2.slice(0, global.TR_TICKET_HASH_LENGTH + 6);
|
2019-07-10 04:01:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
function CreateTxID(body, BlockNum, Nonce) {
|
|
|
|
body.writeUIntLE(BlockNum, body.length - 12, 6);
|
|
|
|
body.writeUIntLE(Nonce, body.length - 6, 6);
|
|
|
|
var HASH = global.sha3(body);
|
|
|
|
var FullHashTicket = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
|
2019-07-10 07:59:04 +00:00
|
|
|
for (var i = 0; i < global.TR_TICKET_HASH_LENGTH; i++)
|
2019-07-10 04:01:15 +00:00
|
|
|
FullHashTicket[i] = HASH[i];
|
2019-07-10 07:59:04 +00:00
|
|
|
global.WriteUintToArrOnPos(FullHashTicket, BlockNum, global.TR_TICKET_HASH_LENGTH);
|
2019-07-10 04:01:15 +00:00
|
|
|
return FullHashTicket;
|
|
|
|
};
|
|
|
|
|
|
|
|
function GetBlockNumTr(arr) {
|
2019-07-10 07:59:04 +00:00
|
|
|
var BlockNum = DELTA_FOR_TIME_TX + global.GetCurrentBlockNumByTime();
|
2019-07-12 14:46:26 +00:00
|
|
|
if (arr[0] === TYPE_TRANSACTION.TYPE_TRANSACTION_CREATE) {
|
2019-07-10 04:01:15 +00:00
|
|
|
var BlockNum2 = Math.floor(BlockNum / 10) * 10;
|
|
|
|
if (BlockNum2 < BlockNum)
|
|
|
|
BlockNum2 = BlockNum2 + 10;
|
|
|
|
BlockNum = BlockNum2;
|
|
|
|
}
|
|
|
|
return BlockNum;
|
|
|
|
};
|
|
|
|
|
|
|
|
function CreateHashBodyPOWInnerMinPower(TR, arr, MinPow, startnonce) {
|
|
|
|
var BlockNum = GetBlockNumTr(arr);
|
|
|
|
if (MinPow === undefined) {
|
2019-07-10 07:59:04 +00:00
|
|
|
MinPow = global.MIN_POWER_POW_TR + Math.log2(arr.length / 128);
|
2019-07-10 04:01:15 +00:00
|
|
|
}
|
|
|
|
var nonce = startnonce;
|
|
|
|
while (1) {
|
|
|
|
var TxID = CreateTxID(arr, BlockNum, nonce);
|
2019-07-10 07:59:04 +00:00
|
|
|
var power = global.GetPowPower(global.sha3(TxID));
|
2019-07-10 04:01:15 +00:00
|
|
|
if (power >= MinPow) {
|
|
|
|
TR._TxID = TxID;
|
|
|
|
TR._BlockNum = BlockNum;
|
|
|
|
global.ToLog("Find: " + power + " for block:" + BlockNum);
|
|
|
|
return nonce;
|
|
|
|
}
|
|
|
|
nonce++;
|
|
|
|
if (nonce % 2000 === 0) {
|
|
|
|
BlockNum = GetBlockNumTr(arr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
function SendTransaction(Body, TR, Wait, F) {
|
|
|
|
if (Body.length > 16000) {
|
|
|
|
TR._result = 0;
|
|
|
|
TR._text = "Error length transaction =" + Body.length + " (max size=16000)";
|
|
|
|
F(1, TR, Body);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
global.GlobalRunID++;
|
|
|
|
let WebID = global.GlobalRunID;
|
|
|
|
CreateNonceAndSend(0, 0);
|
|
|
|
|
|
|
|
function CreateNonceAndSend(startnonce, NumNext) {
|
|
|
|
if (!NumNext)
|
|
|
|
NumNext = 0;
|
|
|
|
if (NumNext > 10) {
|
|
|
|
F(0, TR, Body);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var nonce = CreateHashBodyPOWInnerMinPower(TR, Body, undefined, startnonce);
|
|
|
|
process.RunRPC("AddTransactionFromWeb", { WebID: WebID, HexValue: global.GetHexFromArr(Body) }, function(Err, text) {
|
|
|
|
TR._result = Err ? 0 : 1;
|
|
|
|
TR._text = text;
|
|
|
|
if (text === "Not add" || text === "Bad PoW") {
|
|
|
|
CreateNonceAndSend(nonce + 1, NumNext + 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (text === "Bad time") {
|
|
|
|
if (DELTA_FOR_TIME_TX < 6) {
|
|
|
|
DELTA_FOR_TIME_TX++;
|
|
|
|
global.ToLog("New set Delta time: " + DELTA_FOR_TIME_TX);
|
|
|
|
CreateNonceAndSend(0, NumNext + 1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Wait && TR._result) {
|
|
|
|
global.GlobalRunMap[WebID] = F;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
F(TR._result < 1 ? 0 : 1, text);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
function GetTransactionFromBody(Params, Block, TrNum, Body) {
|
|
|
|
var TR = global.DApps.Accounts.GetObjectTransaction(Body);
|
|
|
|
if (TR) {
|
2019-07-10 07:59:04 +00:00
|
|
|
global.ConvertBufferToStr(TR);
|
2019-07-10 04:01:15 +00:00
|
|
|
TR.result = 1;
|
|
|
|
TR.Meta = Params.Meta;
|
|
|
|
if (Block.VersionBody === 1 && Block.arrContentResult) {
|
|
|
|
TR.result = Block.arrContentResult[TrNum];
|
|
|
|
}
|
|
|
|
TR.BlockNum = Block.BlockNum;
|
|
|
|
TR.TrNum = TrNum;
|
|
|
|
return TR;
|
|
|
|
}
|
|
|
|
return { result: 0, Meta: Params ? Params.Meta : undefined };
|
|
|
|
};
|