apla-blockchain-tools
Version:
Module contains a number of tools to work with Apla Blockchain
160 lines (140 loc) • 5.35 kB
JavaScript
;
// MIT License
//
// Copyright (c) 2016-2018 AplaProject
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
/*
* Package: apla-blockchain-tools
* Author: Anton Zuev
* mail: a.zuev@apla.io
* Company: apla.io
*/
let msgpack = require('msgpack-lite');
let { Int64BE } = require('int64-buffer');
let keyring = require("../keyring");
let {getFieldInstanceByType} = require("./fields");
let convert = require('../lib/convert');
let {RequiredParamNotPassedError, RedundantParamPassedError} = require("./errors");
class Contract {
/**
*
* @param contractSchema {object} - schema of the contract received from /api/v2/contract/{contractName}
* @param params {object} - key - name of the parameter, value - its value
* @param options {object}
* @param options.keyId {string} - keyId of the user who executes contract
* @param options.ecosystemId {number} - ecosystemId of the ecosystem that contract belongs to
* @param options.networkId {number} - networkId
* @param options.payOver {number}
* @param options.maxSum
*/
constructor(contractSchema, params, options) {
this._time = Math.floor((new Date()).getTime() / 1000);
this.params = params;
this.schema = contractSchema;
let {keyId, ecosystemId, networkId} = options;
if(!keyId){
throw new Error("keyId was not passed")
}
if(!networkId){
throw new Error("networkId was not passed")
}
if(!ecosystemId){
throw new Error("ecosystemId was not passed")
}
this.prepareParamsForTX();
this._keyID = keyId;
this.id = contractSchema.id;
this._ecosystemId = ecosystemId;
this._networkId = networkId;
}
sign(privateKey) {
const publicKey = keyring.generatePublicKey(privateKey, true);
this._publicKey = convert.toArrayBuffer(publicKey);
const data = this.serialize();
const txHash = keyring.hashData(data).digest();
const resultHash = keyring.hashData(txHash).digest("hex");
const hexHash = resultHash.toString();
const signature = convert.toArrayBuffer(keyring.signHex(hexHash, privateKey));
return {
hash: hexHash,
data: convert.concatBuffer(
new Uint8Array([0x80]),
convert.concatBuffer(
convert.encodeLengthPlusData(data),
convert.encodeLengthPlusData(signature)
)
)
};
}
serialize() {
let codec = msgpack.createCodec({
binarraybuffer: true,
preset: true
});
let txBody = {
Header: {
ID: this.id,
Time: this._time,
EcosystemID: this._ecosystemId,
KeyID: new Int64BE(this._keyID),
NetworkID: this._networkId,
PublicKey: this._publicKey
},
Params: this.params,
};
return msgpack.encode(
txBody,
{
codec: codec
}
);
}
prepareParamsForTX(){
let fields = {};
let obligatory = {};
this.schema.fields.forEach((field) => {
fields[field.name] = field;
if(!field.optional){
obligatory[field.name] = false;
}
});
let preparedParams = {};
Object.keys(this.params).forEach((paramName) => {
let value = this.params[paramName];
if(!fields[paramName]){
throw new RedundantParamPassedError(paramName, this.schema.name)
}
let FieldForParam = getFieldInstanceByType(fields[paramName].type);
preparedParams[paramName] = (new FieldForParam(value)).get();
obligatory[paramName] = true;
});
let paramsNotPassed = [];
Object.keys(obligatory).forEach((paramName) => {
if(!obligatory[paramName]){
paramsNotPassed.push(paramName);
}
});
if(paramsNotPassed.length > 0){
throw new RequiredParamNotPassedError(paramsNotPassed);
}
this.params = preparedParams;
}
}
module.exports = Contract;