Source: engine.js

  1. /*
  2. * @Author: amitshah
  3. * @Date: 2018-04-17 00:55:47
  4. * @Last Modified by: amitshah
  5. * @Last Modified time: 2018-04-28 23:43:00
  6. */
  7. const messageLib = require('./message');
  8. const channelLib = require('./channel');
  9. const channelStateLib = require('./channelState');
  10. const stateMachineLib = require('./stateMachine/stateMachine');
  11. const util = require('ethereumjs-util');
  12. const events = require('events');
  13. /**
  14. * @class GoNetworks Engine encapsualtes off chain interactions between clients and propogation onto the blockchain.
  15. * The Engine is platform agnostic and adaptble to different blockchains by providing appropriate blockchainService adapters
  16. * Overriding the send callback also allows for custom transport layers and methods (not neccessarily IP network based)
  17. * @extends events.EventEmitter
  18. * @property {BN} msgID=0
  19. * @property {BN} currentBlock=0 - the current block which is synchronized to the onchain mined block value via blockchainService handler callbacks
  20. * @property {Buffer} publicKey - Future Use: ElGamal Elliptic Curve Asymmetric Encryption public key to be sent to channel partners
  21. * @property {InitiatorFactory} initiatorStateMachine=stateMachine.IntiatorFactory - creates a new state machine for mediated transfers you initiate
  22. * @property {TargetFactory} targeStateMachine=stateMachine.TargetFactory - creates a new state machine for mediated transfers that are intended for you
  23. * @property {object} messageState={} - tracks the state of mediated transfer messages using msgID as the key i.e. this.messageState[msgID] = stateMachine.*
  24. * @property {object} channelByPeer={} - channels by peers ethereum address as hex string no 0x prefix
  25. * @property {object} channels={} - channels by on-chain contract address hex string no 0x prefix
  26. * @property {object} pendingChannels={} - used to track newChannel requests initiated by the engine
  27. * @property {function} signatureService
  28. * @property {object} blockchain
  29. */
  30. class Engine extends events.EventEmitter {
  31. /**
  32. * @constructror.
  33. * @param {Buffer} address - your ethereum address; ETH Address is merely the last 20 bytes of the keccak256 hash of the public key given the public private key pair.
  34. * @param {Function} signatureService - the callback that requests the privatekey for signing of messages. This allows the user to store the private key in a secure store or other means
  35. * @param {BlockchainService} blockchainService - a class extending the BlockchainService class to monitor and propogate transactions on chain. Override for different Blockchains
  36. */
  37. constructor(address,signatureService,blockchainService){
  38. super();
  39. //dictionary of channels[peerAddress] that are pending mining
  40. this.pendingChannels = {};
  41. this.channels = {};
  42. //dictionary of channels[peerState.address.toString('hex')];
  43. this.channelByPeer = {};
  44. //dictionary of messages[msgID] = statemachine.*
  45. this.messageState = {};
  46. this.currentBlock = new util.BN(0);
  47. this.msgID = new util.BN(0);
  48. this.publicKey;
  49. this.address = address;
  50. this.initiatorStateMachine = stateMachineLib.InitiatorFactory();
  51. this.targetStateMachine = stateMachineLib.TargetFactory();
  52. var self = this;
  53. this.initiatorStateMachine.on("*",function(event,state){
  54. self.handleEvent(event,state);
  55. });
  56. this.targetStateMachine.on("*",function(event,state){
  57. self.handleEvent(event,state);
  58. });
  59. this.signature = signatureService;
  60. this.blockchain = blockchainService;
  61. //sanity check
  62. if(!channelLib.SETTLE_TIMEOUT.gt(channelLib.REVEAL_TIMEOUT)){
  63. throw new Error("SETTLE_TIMEOUT must be strictly and much larger then REVEAL_TIMEOUT");
  64. }
  65. this.currentBlock = new util.BN(0);
  66. }
  67. /**
  68. * Handle an incoming message after it has been deserialized
  69. * @param {message.SignedMessage} message
  70. * @returns {message.Ack}
  71. * @throws "Invalid Message: no signature found"
  72. * @throws "Invalid Message: uknown message received"
  73. */
  74. onMessage(message){
  75. //TODO: all messages must be signed here?
  76. if(!message.isSigned()){
  77. throw new Error("Invalid Message: no signature found");
  78. }
  79. if(message instanceof messageLib.RequestSecret){
  80. this.onRequestSecret(message);
  81. }else if(message instanceof messageLib.RevealSecret){
  82. this.onRevealSecret(message);
  83. }else if(message instanceof messageLib.MediatedTransfer){
  84. this.onMediatedTransfer(message);
  85. }else if(message instanceof messageLib.DirectTransfer){
  86. this.onDirectTransfer(message);
  87. }else if(message instanceof messageLib.SecretToProof){
  88. this.onSecretToProof(message);
  89. }else{
  90. throw new Error("Invalid Message: uknown message received");
  91. }
  92. return new messageLib.Ack({msgID:message.msgID, messageHash:message.getHash(), to:message.from});
  93. }
  94. onRequestSecret(requestSecret){
  95. if(this.messageState.hasOwnProperty(requestSecret.msgID)){
  96. this.messageState[requestSecret.msgID].applyMessage('receiveRequestSecret',requestSecret);
  97. }
  98. }
  99. onRevealSecret(revealSecret){
  100. //handle reveal secret for all channels that have a lock created by it
  101. //we dont care where it came from unless we want to progress our state machine
  102. var errors = [];
  103. Object.values(this.channelByPeer).map(function (channel) {
  104. try{
  105. channel.handleRevealSecret(revealSecret);
  106. }catch(err){
  107. errors.push(err)
  108. }
  109. });
  110. //update all state machines that are in awaitRevealSecret state
  111. Object.values(this.messageState).map(function (messageState) {
  112. try{
  113. //the state machines will take care of echoing RevealSecrets
  114. //to channel peerStates
  115. messageState.applyMessage('receiveRevealSecret',revealSecret);
  116. }catch(err){
  117. errors.push(err)
  118. }
  119. });
  120. errors.map(function (error) {
  121. console.log(error);
  122. });
  123. }
  124. onSecretToProof(secretToProof){
  125. //handle reveal secret for all channels that have a lock created by it.
  126. //this is in the case where for some reason we get a SecretToProof before
  127. //a reveal secret
  128. //encapsulate in message.RevealSecret type of message, we dont have to sign it
  129. //it is not required
  130. var tempRevealSecret = new messageLib.RevealSecret({secret:secretToProof.secret})
  131. this.signature(tempRevealSecret);
  132. Object.values(this.channelByPeer).map(function (channel) {
  133. try{
  134. channel.handleRevealSecret(tempRevealSecret);
  135. }catch(err){
  136. console.log(err);
  137. }
  138. });
  139. Object.values(this.messageState).map(function (messageState) {
  140. try{
  141. //the state machines will take care of echoing RevealSecrets
  142. //to channel peerStates
  143. messageState.applyMessage('receiveRevealSecret',tempRevealSecret);
  144. }catch(err){
  145. console.log(err);
  146. }
  147. });
  148. if(!this.channelByPeer.hasOwnProperty(secretToProof.from.toString('hex'))){
  149. throw new Error("Invalid SecretToProof: unknown sender");
  150. }
  151. var channel = this.channelByPeer[secretToProof.from.toString('hex')];
  152. channel.handleTransfer(secretToProof,this.currentBlock);
  153. if(this.messageState.hasOwnProperty(secretToProof.msgID)){
  154. this.messageState[secretToProof.msgID].applyMessage('receiveSecretToProof',secretToProof);
  155. }else{
  156. //Something went wrong with the statemachine :(
  157. }
  158. }
  159. onDirectTransfer(directTransfer){
  160. if(!this.channelByPeer.hasOwnProperty(directTransfer.from.toString('hex'))){
  161. throw new Error('Invalid DirectTransfer: channel does not exist');
  162. }
  163. var channel = this.channelByPeer[directTransfer.from.toString('hex')];
  164. if(!channel.isOpen()){
  165. throw new Error('Invalid Channel State:state channel is not open');
  166. }
  167. console.log("EMIT TO UI: transferred:"+directTransfer.transferredAmount.sub(channel.peerState.transferredAmount));
  168. channel.handleTransfer(directTransfer,this.currentBlock);
  169. }
  170. onMediatedTransfer(mediatedTransfer){
  171. if(!this.channelByPeer.hasOwnProperty(mediatedTransfer.from.toString('hex'))){
  172. throw new Error('Invalid MediatedTransfer: channel does not exist');
  173. }
  174. var channel = this.channelByPeer[mediatedTransfer.from.toString('hex')];
  175. if(!channel.isOpen()){
  176. throw new Error('Invalid MediatedTransfer Received:state channel is not open');
  177. }
  178. //register the mediated transfer
  179. channel.handleTransfer(mediatedTransfer,this.currentBlock);
  180. if(mediatedTransfer.target.compare(this.address)===0){
  181. console.log("Start targetStateMachine");
  182. this.messageState[mediatedTransfer.msgID] = new stateMachineLib.MessageState(mediatedTransfer,this.targetStateMachine);
  183. this.messageState[mediatedTransfer.msgID].applyMessage('init',this.currentBlock);
  184. }
  185. }
  186. /**
  187. * Send a locked transfer to your channel partner. This method intiatlizes a initator state machine which will queue the message for send via handleEvent
  188. * @param {Buffer} to - eth address who this message will be sent to. Only differs from target if mediating a transfer
  189. * @param {Buffer} target - eth address of the target.
  190. * @param {BN} amount - amount to lock and send.
  191. * @param {BN} expiration - the absolute block number this locked transfer expires at.
  192. * @param {Buffer} secret - Bytes32 cryptographic secret
  193. * @param {Buffer} hashLock - Bytes32 keccak256(secret) value.
  194. * @throws "Invalid MediatedTransfer: channel does not exist"
  195. * @throws 'Invalid Channel State:state channel is not open'
  196. */
  197. sendMediatedTransfer(to,target,amount,expiration,secret,hashLock){
  198. if(!this.channelByPeer.hasOwnProperty(to.toString('hex'))){
  199. throw new Error("Invalid MediatedTransfer: channel does not exist");
  200. }
  201. var channel = this.channelByPeer[to.toString('hex')];
  202. if(!channel.isOpen()){
  203. throw new Error('Invalid Channel State:state channel is not open');
  204. }
  205. //var expiration = this.currentBlock.add(channel.SETTLE_TIMEOUT);
  206. var msgID = this.incrementedMsgID();
  207. var mediatedTransferState = ({msgID:msgID,
  208. "lock":{
  209. hashLock:hashLock,
  210. amount:amount,
  211. expiration:expiration,
  212. },
  213. target:to,
  214. initiator:this.address,
  215. currentBlock:this.currentBlock,
  216. secret:secret,
  217. to:channel.peerState.address});
  218. this.messageState[msgID] = new stateMachineLib.MessageState(mediatedTransferState,this.initiatorStateMachine);
  219. this.messageState[msgID].applyMessage('init');
  220. }
  221. /**
  222. * Send a direct transfer to your channel partner. This method calls send(directTransfer) and applies the directTransfer to the local channel state.
  223. * @param {Buffer} to - eth address who this message will be sent to. Only differs from target if mediating a transfer
  224. * @param {Buffer} transferredAmount - the monotonically increasing amount to send. This value is set by taking the previous transferredAmount + amount you want to transfer.
  225. * @throws "Invalid MediatedTransfer: unknown to address"
  226. * @throws 'Invalid DirectTransfer:state channel is not open'
  227. */
  228. sendDirectTransfer(to,transferredAmount){
  229. if(!this.channelByPeer.hasOwnProperty(to.toString('hex'))){
  230. throw new Error("Invalid MediatedTransfer: unknown to address");
  231. }
  232. var channel = this.channelByPeer[to.toString('hex')];
  233. if(!channel.isOpen()){
  234. throw new Error('Invalid DirectTransfer:state channel is not open');
  235. }
  236. var msgID = this.incrementedMsgID();
  237. var directTransfer = channel.createDirectTransfer(msgID,transferredAmount);
  238. this.signature(directTransfer);
  239. this.send(directTransfer);
  240. channel.handleTransfer(directTransfer);
  241. }
  242. incrementedMsgID(){
  243. this.msgID = this.msgID.add(new util.BN(1));
  244. return this.msgID;
  245. }
  246. /**Send the message. Override this function to define different transport channels
  247. * e.g integrate this with TELEGRAMS Api and securely transfer funds between users on telegram.
  248. * Generate qrcodes for revelSecret message
  249. * or implement webRTC p2p protocol for transport etc.
  250. * @param {message} msg - A message implementation in the message namespace
  251. */
  252. send(msg){
  253. console.log("SENDING:"+messageLib.SERIALIZE(msg));
  254. }
  255. /** Internal event handlers triggered by state-machine workflows and blockchain events
  256. * @param {string} event - the GOT.* namespaced event triggered asynchronously by external engine components i.e. stateMachine, on-chain event handlers,etc.
  257. * @param {object} state - the accompanying object state
  258. */
  259. handleEvent(event, state){
  260. try{
  261. if(event.startsWith('GOT.')){
  262. switch(event){
  263. case 'GOT.sendMediatedTransfer':
  264. var channel = this.channelByPeer[state.to.toString('hex')];
  265. if(!channel.isOpen()){
  266. throw new Error("Channel is not open");
  267. }
  268. //msgID,hashLock,amount,expiration,target,initiator,currentBlock
  269. var mediatedTransfer = channel.createMediatedTransfer(state.msgID,
  270. state.lock.hashLock,
  271. state.lock.amount,
  272. state.lock.expiration,
  273. state.target,
  274. state.initiator,
  275. state.currentBlock);
  276. this.signature(mediatedTransfer);
  277. this.send(mediatedTransfer);
  278. channel.handleTransfer(mediatedTransfer);
  279. break;
  280. case 'GOT.sendRequestSecret':
  281. var channel = this.channelByPeer[state.to.toString('hex')];
  282. var requestSecret = new messageLib.RequestSecret({msgID:state.msgID,to:state.from,
  283. hashLock:state.lock.hashLock,amount:state.lock.amount});
  284. this.signature(requestSecret);
  285. this.send(requestSecret);
  286. break;
  287. case 'GOT.sendRevealSecret':
  288. var channel = this.channelByPeer[state.to.toString('hex')];
  289. //technically, this workflow only works when target == to. In mediated transfers
  290. //we need to act more generally and have the state machine tell us where we should
  291. //send this secret (backwards and forwards maybe)
  292. var revealSecret = new messageLib.RevealSecret({to:state.revealTo, secret:state.secret});
  293. this.signature(revealSecret);
  294. this.send(revealSecret);
  295. //we dont register the secret, we wait for the echo Reveal
  296. break;
  297. case 'GOT.sendSecretToProof':
  298. var channel = this.channelByPeer[state.to.toString('hex')];
  299. //OPTIMIZE:technically we can still send sec2proof,
  300. //it would beneficial to our partner saving $$ for lock withdrawal
  301. //but for now we act in no interest of the peer endpoint :( meanie
  302. if(!channel.isOpen()){
  303. throw new Error("Channel is not open");
  304. }
  305. var secretToProof = channel.createSecretToProof(state.msgID,state.secret);
  306. this.signature(secretToProof)
  307. channel.handleTransfer(secretToProof);
  308. this.send(secretToProof);
  309. //TODO: in the future, wait to apply secret to proof locally. We basically locked the state up now
  310. //It makes sense, in a sense. With this implementation, when a lock secret is revealed and echoed back
  311. // the peer MUST accept a valid SecretToProof or no more new transfers can take place as the states are unsynced
  312. //By having the peer echo back, we dont really do much difference, the state is simplex
  313. break;
  314. case 'GOT.closeChannel':
  315. var channel = this.channelByPeer[state.from.toString('hex')];
  316. //TODO emit closing
  317. return this.closeChannel(channel.channelAddress);
  318. //channel.handleClose(this.currentBlock);
  319. break;
  320. case 'GOT.issueSettle':
  321. //TODO emit "IssueSettle to ui + channel";
  322. var channelAddress = state;
  323. console.log("CAN ISSUE SETTLE:"+channelAddress.toString('hex'));
  324. break;
  325. }
  326. return;
  327. }
  328. }
  329. catch(err){
  330. this.handleError(err);
  331. }
  332. }
  333. /*** Internal error handler triggered by errors encountered by handleEvent
  334. * @param {Error} err - the error caught during handleEvent execution
  335. */
  336. handleError(err){
  337. console.error(err);
  338. }
  339. /*** Blockchain callback when a new block is mined and blockNumber increases.
  340. *This informs the engine of time progression via block number increments crucial for lockedtransfer
  341. * and channel lifecycle management
  342. * @param {BN} block - the latest mined block
  343. */
  344. onBlock(block){
  345. if(block.lt(this.currentBlock)){
  346. throw new Error("Block Error: block count must be monotonically increasing");
  347. }
  348. this.currentBlock = block;
  349. //handleBlock by all the in-flight messages
  350. //timeout or take action as needed
  351. var self = this;
  352. Object.values(this.messageState).map(function (messageState) {
  353. try{
  354. console.debug("CALL HANDLE BLOCK ON MESSAGE");
  355. messageState.applyMessage('handleBlock',self.currentBlock);
  356. }catch(err){
  357. console.log(err);
  358. }
  359. });
  360. //handleBlock for each of the channels, perhaps SETTLE_TIMEOUT has passed
  361. Object.values(this.channels).map(function(channel){
  362. console.debug("CALL HANDLE BLOCK ON CHANNEL");
  363. var events = channel.onBlock(self.currentBlock);
  364. for(var i=0; i < events.length; i++){
  365. self.handleEvent.apply(events[i]);
  366. }
  367. });
  368. }
  369. /** Create a new channel given the peer ethereum address
  370. * @param {Buffer} peerAddress - eth address
  371. * @returns {Promise} - the promise is settled when the channel is mined. If there is an error during any point of execution in the mining
  372. * the onChannelNewError(peerAddress) is called
  373. */
  374. newChannel(peerAddress){
  375. //is this a blocking call?
  376. if(!this.pendingChannels.hasOwnProperty(peerAddress.toString('hex')) &&
  377. this.channelByPeer.hasOwnProperty(peerAddress.toString('hex'))
  378. && this.channelByPeer[peerAddress.toString('hex')].state !== channelLib.CHANNEL_STATE_SETTLED){
  379. throw new Error("Invalid Channel: cannot create new channel as channel already exists with peer and is unsettled");
  380. }
  381. this.pendingChannels[peerAddress.toString('hex')] = true;
  382. var self = this;
  383. var _peerAddress = peerAddress;
  384. return this.blockchain.newChannel(peerAddress,channelLib.SETTLE_TIMEOUT).then(function(vals) {
  385. // ChannelNew(address netting_channel,address participant1,address participant2,uint settle_timeout);
  386. // var channelAddress = vals[0];
  387. // var addressOne = vals[1];
  388. // var addressTwo = vals[2];
  389. // var timeout = vals[3];
  390. //self.onChannelNew(channelAddress,addressOne,addressTwo,timeout);
  391. }).catch(function (err) {
  392. self.onChannelNewError(_peerAddress);
  393. });
  394. };
  395. /** close a channel given the peer ethereum address. The close proof in the state is transferred during the call to close.
  396. * @param {Buffer} channelAddress - the on-chain nettingchannel address of the channel
  397. * @returns {Promise} - the promise is settled when the channel close request is mined. If there is an error during any point of execution in the mining
  398. * the onChannelCloseError(channelAddress) is called
  399. */
  400. closeChannel(channelAddress){
  401. if(!this.channels.hasOwnProperty(channelAddress.toString('hex'))){
  402. throw new Error("Invalid Close: unknown channel");
  403. }
  404. var channel = this.channels[channelAddress.toString('hex')];
  405. if(!channel.isOpen()){
  406. throw new Error("Invalid Close: Cannot reissue Closed");
  407. }
  408. var proof = channel.issueClose(this.currentBlock);
  409. var self = this;
  410. var _channelAddress = channelAddress;
  411. return this.blockchain.closeChannel(channelAddress,proof).then(function(closingAddress){
  412. //channelAddress,closingAddress,block
  413. //TODO: @Artur, only call this after the transaction is mined i.e. txMulitplexer
  414. // return self.onChannelClose(_channelAddress,closingAddress);
  415. }).catch(function(error){
  416. return self.onChannelCloseError(_channelAddress);
  417. });
  418. }
  419. /** Update the proof after you learn a channel has been closed by the channel counter party
  420. * @param {Buffer} channelAddress - the on-chain nettingchannel address of the channel
  421. * @returns {Promise} - the promise is settled when the channel close request is mined. If there is an error during any point of execution in the mining
  422. * the onTransferUpdatedError(channelAddress) is called
  423. */
  424. transferUpdate(channelAddress){
  425. if(!this.channels.hasOwnProperty(channelAddress.toString('hex'))){
  426. throw new Error("Invalid TransferUpdate: unknown channel");
  427. }
  428. var channel = this.channels[channelAddress.toString('hex')];
  429. if(channel.isOpen()){
  430. throw new Error("Invalid TransferUpdate: Cannot issue update on open channel");
  431. }
  432. var proof = channel.issueTransferUpdate(this.currentBlock);
  433. var self = this;
  434. var _channelAddress = channelAddress;
  435. return this.blockchain.updateTransfer(channelAddress, proof).then(function(nodeAddress){
  436. //self.onTransferUpdated(nodeAddress)
  437. }).catch(function(err) {
  438. self.onTransferUpdatedError(_channelAddress);
  439. })
  440. }
  441. /** Issue withdraw proofs on-chain for locks that have had their corresponding secret revealed. Locks can be settled on chain once a proof has been sent on-chain.
  442. * Locks can only be withdrawn once.
  443. * @param {Buffer} channelAddress - the on-chain nettingchannel address of the channel
  444. * @returns {Promise} - the promise is settled when the channel close request is mined. If there is an error during any point of execution in the mining
  445. * the onChannelSecretRevealedError(channelAddress) is called for each lock that was not successfully withdrawn on-chain and must be reissued
  446. */
  447. withdrawPeerOpenLocks(channelAddress){
  448. if(!this.channels.hasOwnProperty(channelAddress.toString('hex'))){
  449. throw new Error("Invalid Withdraw: unknown channel");
  450. }
  451. var channel = this.channels[channelAddress.toString('hex')];
  452. if(channel.isOpen()){
  453. throw new Error("Invalid Withdraw: Cannot issue withdraw on open channel");
  454. }
  455. var openLockProofs = channel.issueWithdrawPeerOpenLocks(this.currentBlock);
  456. var withdraws = [];
  457. for(var i=0; i< openLockProofs.length; i++){
  458. var p = openLockProofs[i];
  459. //nonce,gasPrice,nettingChannelAddress, encodedOpenLock, merkleProof,secret)
  460. var _secret = p.openLock.secret;
  461. var _channelAddress = channelAddress;
  462. var self = this;
  463. var promise = this.blockchain.withdrawLock(channelAddress,p.encodeLock(),p.merkleProof,_secret)
  464. .then(function(vals){
  465. // var secret = vals[0];
  466. // var receiverAddress = vals[1];
  467. // //channelAddress, secret, receiverAddress,block
  468. // return self.onChannelSecretRevealed(_channelAddress,secret,receiverAddress)
  469. })
  470. .catch(function(err){
  471. return self.onChannelSecretRevealedError(_channelAddress,_secret);
  472. })
  473. withdraws.push(promise);
  474. }
  475. return Promise.all(withdraws);
  476. }
  477. /** Settle the channel on-chain after settle_timeout time has passed since closing, unlocking the on-chain collateral and distributing the funds
  478. * according to the proofs and lock withdrawals on chain.
  479. * @param {Buffer} channelAddress - the on-chain nettingchannel address of the channel
  480. * @returns {Promise} - the promise is settled when the channel close request is mined. If there is an error during any point of execution in the mining
  481. * the onChannelSettledError(channelAddress) is called
  482. */
  483. settleChannel(channelAddress){
  484. if(!this.channels.hasOwnProperty(channelAddress)){
  485. throw new Error("Invalid Settle: unknown channel");
  486. }
  487. var channel = this.channels[channelAddress.toString('hex')];
  488. if(channel.isOpen()){
  489. throw new Error("Invalid Settle: cannot issue settle on open channel");
  490. }
  491. var _channelAddress = channelAddress;
  492. var self = this;
  493. channel.issueSettle(this.currentBlock);
  494. var _channelAddress = channelAddress;
  495. return self.blockChain.settle(_channelAddress).then(function () {
  496. //return self.onChannelSettled(_channelAddress);
  497. }).catch(function(err){
  498. return self.onChannelSettledError(_channelAddress);
  499. });
  500. }
  501. /** Deposit an amount of the ERC20 token into the channel on-chain. After the transaction is mined successfully,
  502. * that amount will be available for net transfer in the channel. This is effectively the collateral locked up during the
  503. * channel lifetime and cannot be freed until the channel is closed and settled.
  504. * @param {Buffer} channelAddress - the on-chain nettingchannel address of the channel
  505. * @param {BN} amount - the amount of the ERC20 token to deposit. The maxium amount of the cumulative deposits is determined by the allowance setup for the channel.
  506. * @see Engine.approveChannel
  507. * @returns {Promise} - the promise is settled when the channel close request is mined. If there is an error during any point of execution in the mining
  508. * the onChannelNewBalanceError(channelAddress) is called
  509. */
  510. depositChannel(channelAddress,amount){
  511. if(!this.channels.hasOwnProperty(channelAddress)){
  512. throw new Error("Invalid Settle: unknown channel");
  513. }
  514. var channel = this.channels[channelAddress.toString('hex')];
  515. if(!channel.isOpen()){
  516. throw new Error("Invalid Deposite: cannot issue settle on open channel");
  517. }
  518. var _channelAddress = channelAddress;
  519. var self = this;
  520. return self.blockChain.depoist(_channelAddress,amount).then(function (vals) {
  521. // event ChannelNewBalance(address token_address, address participant, uint balance);
  522. // var tokenAddress = vals[0];
  523. // var nodeAddress = vals[1];
  524. // var balance = vals[2];
  525. // return self.onChannelNewBalance(_channelAddress,nodeAddress,balance);
  526. }).catch(function(err){
  527. return self.onChannelNewBalanceError(_channelAddress);
  528. });
  529. }
  530. /** approve the channel to take ERC20 deposits. This must be called before a deposit can be made successfully. This utlimately creates and allowance
  531. * for the channel on the ERC20 contract.
  532. * @param {Buffer} channelAddress - the on-chain nettingchannel address of the channel
  533. * @param {BN} amount - the maximum amount of the ERC20 token to allow the channel to transfer when making a deposit.
  534. * @returns {Promise} - the promise is settled when the channel close request is mined. If there is an error during any point of execution in the mining
  535. * the onApprovalError(channelAddress) is called
  536. */
  537. approveChannel(channelAddress,amount){
  538. if(!this.channels.hasOwnProperty(channelAddress)){
  539. throw new Error("Invalid approve Channel: unknown channel");
  540. }
  541. var channel = this.channels[channelAddress.toString('hex')];
  542. var _channelAddress = channel.channelAddress;
  543. return self.blockChain.approve(self.blockchain.tokenAddress,channel.channelAddress,amount)
  544. .then(function (vals) {
  545. //event Approval(address indexed _owner, address indexed _spender, uint256 _value);
  546. // var owner = vals[0];
  547. // var spender = vals[1];
  548. // var value = vals[2];
  549. // return self.onApproval(owner,spender,value);
  550. }).catch(function(err){
  551. return self.onApprovalError(_channelAddress);
  552. });
  553. }
  554. /** Approve the channelManager to take the flat fee in GOT ERC20 tokens when a channel is created. This only needs to be called once when the engine is initialized
  555. * @param {BN} amount - the maximum allowance of GOT ERC20 tokens to allow the channelManager to transfer.
  556. * @returns {Promise} - the promise is settled when the channel close request is mined. If there is an error during any point of execution in the mining
  557. * the onApprovalError() is called
  558. */
  559. approveChannelManager(amount){
  560. return self.blockChain.approve(self.blockchain.gotokenAddress,
  561. self.blockchain.chanelManagerAddress,
  562. amount)
  563. .then(function (vals) {
  564. //event Approval(address indexed _owner, address indexed _spender, uint256 _value);
  565. // var owner = vals[0];
  566. // var spender = vals[1];
  567. // var value = vals[2];
  568. // return self.onApproval(owner,spender,value);
  569. }).catch(function(err){
  570. return self.onApprovalError();
  571. });
  572. }
  573. /** Callback when a ERC20 token approves someone for an allowance
  574. * @param {String} owner - ethereum address hexString
  575. * @param {String} spender - ethereum address hexString
  576. * @param {BN} value - the allowance that was set
  577. */
  578. onApproval(owner,spender,value){
  579. return true;
  580. };
  581. onApprovalError(address){
  582. return true;
  583. }
  584. /** Callback when a new channel is created by the channel manager
  585. * @param {String} channelAddress - ethereum address hexString
  586. * @param {String} addressOne - ethereum address hexString
  587. * @param {String} addressTwo - ethereum address hexString
  588. * @param {BN} settleTimeout- the settle_timeout for the channel
  589. */
  590. onChannelNew(channelAddress,addressOne,addressTwo,settleTimeout){
  591. var peerAddress = null;
  592. if(addressOne.compare(this.address)===0){
  593. peerAddress = addressTwo;
  594. }else if(addressTwo.compare(this.address)===0){
  595. peerAddress = addressOne;
  596. }else{
  597. //something very wrong
  598. throw new Error("Invalid Channel Event:unknown new channel");
  599. }
  600. var existingChannel = this.channelByPeer[peerAddress.toString('hex')];
  601. if(existingChannel && existingChannel.state !== channelLib.CHANNEL_STATE_SETTLED){
  602. throw new Error("Invalid Channel: cannot add new channel as it already exists");
  603. }
  604. var stateOne = new channelStateLib.ChannelState({
  605. address:this.address
  606. });
  607. var stateTwo = new channelStateLib.ChannelState({
  608. address:peerAddress
  609. });
  610. //constructor(peerState,myState,channelAddress,settleTimeout,revealTimeout,currentBlock){
  611. var channel = new channelLib.Channel(stateTwo,stateOne,channelAddress,
  612. this.currentBlock);
  613. this.channels[channel.channelAddress.toString('hex')] = channel;
  614. this.channelByPeer[channel.peerState.address.toString('hex')] = channel;
  615. if(this.pendingChannels.hasOwnProperty(peerAddress.toString('hex'))){
  616. delete this.pendingChannels[peerAddress.toString('hex')] ;
  617. }
  618. return true;
  619. }
  620. onChannelNewError(peerAddress){
  621. if(this.pendingChannels.hasOwnProperty(peerAddress.toString('hex'))){
  622. delete this.pendingChannels[peerAddress.toString('hex')] ;
  623. }
  624. return;
  625. //TODO: emit UnableToCreate Channel with Peer
  626. }
  627. /** Callback when a channel has tokens deposited into it on-chain
  628. * @param {String} channelAddress - ethereum address hexString
  629. * @param {String} address - the particpants ethereum address in hexString who deposited the funds
  630. * @param {String} balance - the new deposited balance for the participant in the channel
  631. */
  632. onChannelNewBalance(channelAddress,address,balance){
  633. this.channels[channelAddress.toString('hex')].onChannelNewBalance(address,balance);
  634. return true;
  635. }
  636. onChannelNewBalanceError(){
  637. return false;
  638. }
  639. /** Callback when a channel is closed on chain identifying which of the partners initiated the close
  640. * @param {String} channelAddress - ethereum address hexString
  641. * @param {String} closingAddress - ethereum address hexString
  642. */
  643. onChannelClose(channelAddress,closingAddress){
  644. var channel = this.channels[channelAddress.toString('hex')];
  645. channel.onChannelClose(closingAddress, this.currentBlock)
  646. if(closingAddress.compare(this.address) !==0){
  647. return this.transferUpdate(channelAddress)
  648. }
  649. return true;
  650. }
  651. onChannelCloseError(channelAddress,proof){
  652. var channel = this.channels[channelAddress.toString('hex')];
  653. return channel.onChannelCloseError();
  654. }
  655. /** Callback when a the counterpary has updated their transfer proof on-chain
  656. * @param {String} channelAddress - ethereum address hexString
  657. * @param {String} nodeAddress - the party who submitted the proof
  658. */
  659. onTransferUpdated(channelAddress,nodeAddress){
  660. return this.channels[channelAddress.toString('hex')].onTransferUpdated(nodeAddress,this.currentBlock);
  661. }
  662. onTransferUpdatedError(channelAddress){
  663. return this.channels[channelAddress.toString('hex')].onTransferUpdatedError();
  664. }
  665. /** Callback when a channel is settled on-chain
  666. * @param {String} channelAddress - ethereum address hexString
  667. */
  668. onChannelSettled(channelAddress){
  669. return this.channels[channelAddress.toString('hex')].onChannelSettled(this.currentBlock);
  670. }
  671. onChannelSettledError(channelAddress){
  672. return this.channels[channelAddress.toString('hex')].onChannelSettledError();;
  673. }
  674. /** Callback when a lock has been withdrawn on-chain. If a user was withholding the secret in a mediate transfer,
  675. * the party can now unlock the pending locks in the other channels. This is why it is essential in a mediated transfer setting
  676. * that each hop decrements the expiration by a safe margin such that they may claim a lock off chain in case of byzantine faults
  677. * @param {String} channelAddress - ethereum address hexString
  678. * @param {String} secret - the 32 byte secret in hexString
  679. * @param {String} receiverAddress - ethereum address hexString which unlocked the lock on-chain
  680. */
  681. onChannelSecretRevealed(channelAddress, secret, receiverAddress){
  682. return this.channels[channelAddress.toString('hex')].onChannelSecretRevealed(secret,receiverAddress,this.currentBlock);
  683. };
  684. onChannelSecretRevealedError(channelAddress, secret){
  685. return this.channels[channelAddress.toString('hex')].onChannelSecretRevealedError(secret);
  686. };
  687. /** Callback when a channel has been closed and the channel lifetime exceeds the refund interval.
  688. * i.e. channel.closedBlock - channel.openedBlock > refundInterval. This is in hopes to incentives longer lived state channels
  689. * by reducing the cost of their deployment for longer periods.
  690. * @param {String} channelAddress - ethereum address hexString
  691. * @param {String} receiverAddress - ethereum address hexString of the party that received the refund
  692. * @param {BN} amount- the amount of GOT refunded
  693. */
  694. onRefund(channelAddress,receiverAddress,amount){
  695. return true;
  696. }
  697. }
  698. module.exports = {
  699. Engine
  700. }