diff --git a/ling/ActTransfer.js b/ling/ActTransfer.js index b26b409..369d173 100644 --- a/ling/ActTransfer.js +++ b/ling/ActTransfer.js @@ -1,40 +1,28 @@ -const Action = require('./Action.js') -const Ticrypto = wo&&wo.Crypto?wo.Crypto:require('tic.crypto') - -/******************** Public of instance ********************/ - -const DAD=module.exports=function ActTransfer(prop) { - this._class='ActTransfer' - this.setProp(prop) // 没有定义 ActTransfer.prototype._model,因此继承了上级Action.prototype._model,因此通过this.setProp,继承了上级Action定义的实例自有数据。另一个方案是,调用 Action.call(this, prop) - this.type='ActTransfer' -} -DAD.__proto__= Action -// DAD._table=DAD.name // 注释掉,从而继承父类Action的数据库表格名 -const MOM=DAD.prototype -MOM.__proto__=Action.prototype - -/******************** Shared by instances ********************/ - -MOM.validate=function(){ - // return Ticrypto.isAddress(this.toAddress) - return Ticrypto.isAddress(this.toAddress) && this.fee>=wo.Config.MIN_FEE_ActTransfer - // && wo.Account.accountPool[this.actorAddress].balance>this.amount+this.fee //Todo:引入缓存账户 - &&this.toAddress != this.actorAddress -} - -MOM.execute=async function(){ - let sender= await wo.Account.getOne({Account: { address: this.actorAddress }}) - if (sender && sender.type !== 'multisig' && this.toAddress != this.actorAddress && sender.balance >= this.amount + this.fee){ - await sender.setMe({Account:{ balance: sender.balance-this.amount-this.fee }, cond:{ address:sender.address}}) - let getter= await wo.Account.getOne({Account: { address: this.toAddress }}) || await wo.Account.addOne({Account: { address: this.toAddress }}) - await getter.setMe({Account:{ balance: getter.balance+this.amount }, cond:{ address:getter.address}}) - // mylog.info('Excecuted action='+JSON.stringify(this)) - return this - } - // mylog.info('balance('+sender.address+')='+sender.balance+' is less than '+this.amount+', 无法转账') - return null -} - -/******************** Public of class ********************/ - -DAD.api={} +const Action = require('./Action.js') + +const DAD = module.exports = function ActTransfer (prop) { + this._class = 'ActTransfer' + this.setProp(prop) // 没有定义 ActTransfer.prototype._model,因此继承了上级Action.prototype._model,因此通过this.setProp,继承了上级Action定义的实例自有数据。另一个方案是,调用 Action.call(this, prop) + this.type = 'ActTransfer' +} +DAD.__proto__ = Action +const MOM = DAD.prototype +MOM.__proto__ = Action.prototype + +DAD.validate = async function (action) { + // if (sender && sender.type !== 'multisig' && action.toAddress != action.actorAddress && sender.balance >= action.amount + action.fee){ + let sender = await wo.Store.getBalance(action.actorAddress) + return action.actorAddress && action.toAddress && action.toAddress != action.actorAddress && action.amount && action.amount > 0 && sender >= action.amount + action.fee && action.fee >= wo.Config.MIN_FEE_ActTransfer +} + +DAD.execute = async function (action) { + let sender = await wo.Store.getBalance(action.actorAddress) + if (sender >= action.amount + action.fee) { + await wo.Store.decrease(action.actorAddress, action.amount + action.fee) + await wo.Store.increase(action.toAddress, action.amount) + return true + } + return null +} + +DAD.api = {} diff --git a/ling/Action.js b/ling/Action.js index 1952e3a..6d5b717 100644 --- a/ling/Action.js +++ b/ling/Action.js @@ -1,123 +1,144 @@ -const Ling = wo&&wo.Ling?wo.Ling:require('fon.ling') -const Ticrypto = wo&&wo.Crypto?wo.Crypto:require('tic.crypto') +var Ling = require('fon.ling') +var Ticrypto = require('tic.crypto') -/******************** Public of instance ********************/ +/** ****************** Public of instance ********************/ -const DAD=module.exports=function Action(prop) { - this._class=this.constructor.name +const DAD = module.exports = function Action (prop) { + this._class = this.constructor.name this.setProp(prop) } -DAD.__proto__=Ling -DAD._table=DAD.name -const MOM=DAD.prototype -MOM.__proto__=Ling.prototype +DAD.__proto__ = Ling +DAD._table = DAD.name +const MOM = DAD.prototype +MOM.__proto__ = Ling.prototype -/******************** Shared by instances ********************/ -MOM._tablekey='hash' -MOM._model= { - hash: { default:undefined, sqlite:'TEXT UNIQUE', mysql:'VARCHAR(64) PRIMARY KEY' }, // 不纳入签名和哈希 - version: { default:0, sqlite:'INTEGER' }, - type: { default:'Action', sqlite:'TEXT', mysql:'VARCHAR(100)' }, // 是否放在 assets里更好?这里该放action自己的version - blockHash: { default:undefined, sqlite:'TEXT', mysql:'VARCHAR(64)' }, // 不纳入签名和哈希。只为了方便查找 - timestamp: { default:undefined, sqlite:'TEXT', mysql:'CHAR(24)' }, - actorPubkey: { default:undefined, sqlite:'TEXT', mysql:'BINARY(32)' }, - actorAddress: { default:undefined, sqlite:'TEXT', mysql:'VARCHAR(50)' }, - actorSignature:{ default:undefined, sqlite:'TEXT', mysql:'BINARY(64)' }, // 不纳入签名,纳入哈希 - toAddress: { default:undefined, sqlite:'TEXT', mysql:'VARCHAR(50)' }, - amount: { default:0, sqlite:'NUMERIC', mysql:'BIGINT' }, - fee: { default:0, sqlite:'NUMERIC', mysql:'BIGINT' }, -// signSignature: { default:undefined, sqlite:'TEXT', mysql:'BINARY(64)' }, // 不纳入签名,纳入哈希 -// requesterPubkey:{ default:undefined, sqlite:'TEXT', mysql:'BINARY(32)' }, -// signatures: { default:undefined, sqlite:'TEXT', mysql:'TEXT' }, -// option: { default:undefined, sqlite:'TEXT', mysql:'VARCHAR(4096)' }, -// act: { default:null, sqlite:'TEXT' }, // 相当于 asch/lisk里的asset - message: { default:undefined, sqlite:'TEXT', mysql:'VARCHAR(256)' }, - dataIndex: { default:undefined, sqlite:'TEXT', mysql:'VARCHAR(50)'}, //用于索引json中存储数据, - json: { default:{}, sqlite:'TEXT' } // 开发者自定义字段,可以用json格式添加任意数据,而不破坏整体结构 +/** ****************** Shared by instances ********************/ +MOM._tablekey = 'hash' +MOM._model = { + hash: { default: undefined, sqlite: 'TEXT UNIQUE', mysql: 'VARCHAR(64) PRIMARY KEY' }, // 不纳入签名和哈希 + version: { default: 0, sqlite: 'INTEGER' }, + type: { default: 'Action', sqlite: 'TEXT', mysql: 'VARCHAR(100)' }, // 是否放在 assets里更好?这里该放action自己的version + blockHash: { default: undefined, sqlite: 'TEXT', mysql: 'VARCHAR(64)' }, // 不纳入签名和哈希。只为了方便查找 + timestamp: { default: undefined, sqlite: 'TEXT', mysql: 'CHAR(24)' }, + actorPubkey: { default: undefined, sqlite: 'TEXT', mysql: 'BINARY(32)' }, + actorAddress: { default: undefined, sqlite: 'TEXT', mysql: 'VARCHAR(50)' }, + actorSignature: { default: undefined, sqlite: 'TEXT', mysql: 'BINARY(64)' }, // 不纳入签名,纳入哈希 + toAddress: { default: undefined, sqlite: 'TEXT', mysql: 'VARCHAR(50)' }, + amount: { default: 0, sqlite: 'NUMERIC', mysql: 'BIGINT' }, + fee: { default: 0, sqlite: 'NUMERIC', mysql: 'BIGINT' }, + message: { default: undefined, sqlite: 'TEXT', mysql: 'VARCHAR(256)' }, + dataIndex: { default: undefined, sqlite: 'TEXT', mysql: 'VARCHAR(50)' }, // 用于索引json中存储数据, + method: { default: undefined, sqlite: 'TEXT' }, + data: { default: undefined, sqlite: 'TEXT' } } -MOM.packMe = function(keypair){ // 由前端调用,后台不创建 - this.actorPubkey=keypair.pubkey - this.actorAddress=Ticrypto.pubkey2address(keypair.pubkey) - this.timestamp=new Date() - +MOM.packMe = function (keypair) { // 由前端调用,后台不创建 + this.actorPubkey = keypair.pubkey + this.actorAddress = Ticrypto.pubkey2address(keypair.pubkey) + this.timestamp = new Date() + this.signMe(keypair.seckey) this.hashMe() return this } -MOM.signMe = function(seckey){ // 由前端调用,后台不该进行签名 - let json=this.getJson({exclude:['hash','blockHash','actorSignature']}) // 是前端用户发起事务时签字,这时候还不知道进入哪个区块,所以不能计入blockHash - this.actorSignature=Ticrypto.sign(json, seckey) +MOM.signMe = function (seckey) { // 由前端调用,后台不该进行签名 + let json = this.getJson({ exclude: ['hash', 'blockHash', 'actorSignature'] }) // 是前端用户发起事务时签字,这时候还不知道进入哪个区块,所以不能计入blockHash + this.actorSignature = Ticrypto.sign(json, seckey) return this } -MOM.verifySig = function(){ - let json=this.getJson({exclude:['hash','blockHash','actorSignature']}) - let res=Ticrypto.verify(json, this.actorSignature, this.actorPubkey) +MOM.hashMe = function () { + this.hash = Ticrypto.hash(this.getJson({ exclude: ['hash', 'blockHash'] })) // block.hash 受到所包含的actionList影响,所以action不能受blockHash影响,否则循环了 + return this +} + +DAD.getJson = function (action, option = {}) { + let data = {} + let sortedKey = Object.keys(DAD.prototype._model).sort() + for (let exkey of option.exclude) { sortedKey.splice(sortedKey.indexOf(exkey), 1) } + for (let key of sortedKey) { // 忽略一些不需要签名的属性 + data[key] = action[key] + } + let json = JSON.stringify(data) + return json +} + +DAD.verifySig = function (action) { + let json = DAD.getJson(action, { exclude: ['hash', 'blockHash', 'actorSignature'] }) + let res = Ticrypto.verify(json, action.actorSignature, action.actorPubkey) return res } -MOM.verifyAddress = function(){ - return this.actorAddress===Ticrypto.pubkey2address(this.actorPubkey) +DAD.verifyAddress = function (action) { + return action.actorAddress === Ticrypto.pubkey2address(action.actorPubkey) } -MOM.hashMe = function(){ - this.hash=Ticrypto.hash(this.getJson({exclude:['hash', 'blockHash']})) // block.hash 受到所包含的actionList影响,所以action不能受blockHash影响,否则循环了 - return this +DAD.verifyHash = function (action) { + return action.hash === Ticrypto.hash(DAD.getJson(action, { exclude: ['hash', 'blockHash'] })) } -MOM.verifyHash = function(){ - return this.hash===Ticrypto.hash(this.getJson({exclude:['hash', 'blockHash']})) -} - -MOM.execute=function(){ // 子类应当覆盖本方法。把action的影响,汇总登记到其他表格(用于辅助的、索引的表格),方便快速索引、处理。每种事务类型都要重定义这个方法。 +DAD.execute = function () { // 子类应当覆盖本方法。把action的影响,汇总登记到其他表格(用于辅助的、索引的表格),方便快速索引、处理。每种事务类型都要重定义这个方法。 // save to account or other tables return this } -MOM.validate=function(){ // 子类应当覆盖本方法 - return true -} - -MOM.calculateFee = function(){ +DAD.calculateFee = function () { return 1000 } +/** + * 获取一批交易,在出块时调用。调用actionPool的内容被深拷贝到currentActionPool后自动清空。 + * 所以在一次出块期间只能调用一次 + */ +DAD.getActionBatch = function () { + let actionBatch = { + actionPool: JSON.parse(JSON.stringify(DAD.actionPool)), // deep copy + totalAmount: DAD.actionPoolInfo.totalAmount, + totalFee: DAD.actionPoolInfo.totalFee + } + DAD.actionPool = {} + DAD.actionPoolInfo = { + totalAmount: 0, + totalFee: 0 + } + return actionBatch +} -// DAD._init=async function(){ -// await DAD.__proto__._init() // create database table at first -// await DAD.actionLoop() -// return this -// } +/** ********************* Public of class *******************/ +DAD.api = {} -/*********************** Public of class *******************/ -DAD.api={} - -DAD.api.getAction=async function(option){ +DAD.api.getAction = async function (option) { return await DAD.getOne(option) } -DAD.api.getActionList=async function(option){ +DAD.api.getActionList = async function (option) { return await DAD.getAll(option) } -DAD.api.prepare=async function(option){ // 前端发来action数据,进行初步检查(不检查是否可执行--这和事务类型、执行顺序有关,只检查格式是否有效--这是所有事务通用的规范)后放入缓冲池。 +DAD.api.prepare = async function (option) { + // 前端发来action数据,进行初步检查(不检查是否可执行--这和事务类型、执行顺序有关,只检查格式是否有效--这是所有事务通用的规范)后放入缓冲池。 if (option && option.Action && option.Action.type && option.Action.hash && !DAD.actionPool[option.Action.hash]) { - let action=new wo[option.Action.type](option.Action) // 一次性把option.Action里送来的参数导入新建的action - if (action.verifyAddress() && action.verifySig() && action.verifyHash() // 对所有Action类型都通用的验证 - && action.validate()) { // 各子类特有的验证 - // mylog.info('Received action='+JSON.stringify(action)) - DAD.actionPool[action.hash]=action + if (DAD.verifyAddress(option.Action) && + DAD.verifySig(option.Action) && + DAD.verifyHash(option.Action) && + !DAD.actionPool[option.Action.hash] && + (await wo[option.Action.type].validate(option.Action)) + ) { + DAD.actionPool[option.Action.hash] = option.Action + DAD.actionPoolInfo.totalAmount += option.Action.amount || 0 + DAD.actionPoolInfo.totalFee += option.Action.fee || 0 wo.Peer.broadcast('/Action/prepare', option) - return action + return option.Action } } - return null // 非法的交易数据 + return null // 非法的交易数据 } -/********************** Private in class *******************/ +/** ******************** Private in class *******************/ -DAD.actionPool={} // 随时不断接收新的交易请求 - -const my = { +DAD.actionPool = {} // 交易池,在执行getActionBatch时被清空 +// DAD.currentActionPool = {} // 仅包含0~40秒的交易,40~59秒的交易将被堆积到actionPool。 +DAD.actionPoolInfo = { + totalAmount: 0, + totalFee: 0 }