├── src ├── plasma.js ├── random.js ├── index.js ├── MerkleTree.js ├── index.d.ts ├── set-payment-channels.js ├── utils.js └── general-state-channel.js ├── .gitignore ├── .babelrc ├── .gitmodules ├── README.md ├── package.json ├── test ├── randomTest.js ├── Layer2LibSetTest.js ├── Layer2LibSetTest-fb.js ├── layer2libTest.js └── Layer2LibVCTest.js ├── contracts └── package-lock.json └── LICENSE /src/plasma.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // TODO -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .DS_Store 4 | /coverage 5 | /contracts/general-state-channels/build/contracts/* 6 | /dist 7 | data.json 8 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "latest", 4 | "stage-0" 5 | ], 6 | "plugins": [ 7 | "babel-plugin-transform-async-to-generator" 8 | ] 9 | } -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "contracts/general-state-channels"] 2 | path = contracts/general-state-channels 3 | url = https://github.com/finalitylabs/general-state-channels 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # js-layer2lib 2 | A javascript library for building state channel applications 3 | 4 | To run tests on rinkeby: 5 | ``` 6 | npm install 7 | npm run test 8 | ``` 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "js-layer2lib", 3 | "version": "0.0.6", 4 | "description": "An ethereum layer 2 scalability js library", 5 | "main": "dist/index.js", 6 | "types": "./src/index.d.ts", 7 | "scripts": { 8 | "test": "GUN_ENV=false node test/layer2libTest.js", 9 | "testset": "GUN_ENV=false node test/Layer2LibSetTest.js", 10 | "testset:fb": "FIREBASE_KEY_PATH='/home/lex/Certs/layer2hub-firebase-adminsdk-4apv0-0c4f82578f.json' node test/Layer2LibSetTest-fb.js", 11 | "testvc": "GUN_ENV=false node test/Layer2LibVCTest.js", 12 | "test-random": "GUN_ENV=false node test/randomTest.js", 13 | "build": "babel src -d dist", 14 | "deploy": "npm run build && npm publish" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/finalitylabs/js-layer2lib.git" 19 | }, 20 | "author": "nathan@finalitylabs.io", 21 | "license": "ISC", 22 | "bugs": { 23 | "url": "https://github.com/layer2lib/js-layer2lib/issues" 24 | }, 25 | "homepage": "https://github.com/layer2lib/js-layer2lib#readme", 26 | "dependencies": { 27 | "axios": "^0.18.0", 28 | "babel-polyfill": "^6.26.0", 29 | "bignumber.js": "^7.1.0", 30 | "buffer": "^5.0.7", 31 | "crypto": "^1.0.1", 32 | "crypto-js": "^3.1.9-1", 33 | "ethereumjs-tx": "^1.3.4", 34 | "ethereumjs-util": "^5.1.5", 35 | "web3": "1.0.0-beta.34" 36 | }, 37 | "devDependencies": { 38 | "babel-cli": "^6.26.0", 39 | "babel-plugin-transform-async-to-generator": "^6.24.1", 40 | "babel-preset-latest": "^6.24.1", 41 | "babel-preset-stage-0": "^6.24.1", 42 | "firebase-admin": "^5.12.1", 43 | "gun": "^0.9.9993", 44 | "layer2storage": "github:layer2lib/layer2storage#firebase" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/randomTest.js: -------------------------------------------------------------------------------- 1 | const { RandomPieceGenerator, MergedRandomGenerator } = require('../src/random.js'); 2 | 3 | function testPieceGenerator() { 4 | console.log('Test Piece Generator\n') 5 | const length = 5; 6 | const aliceSeed = 'alice_seed'; 7 | const aliceRandom = new RandomPieceGenerator(aliceSeed, length); 8 | console.log(`Alice Hashes: ${aliceRandom.hashes}\n`); 9 | 10 | for(let i = length; i >= 0; i--) { 11 | const alicePreimage = aliceRandom.getNextRandom(); 12 | console.log(`Alice's Next Random: ${alicePreimage}`); 13 | } 14 | 15 | console.log(); 16 | } 17 | 18 | function testMergedGenerator() { 19 | console.log('Test Merged Generator\n'); 20 | const length = 5; 21 | const aliceSeed = 'alice_seed'; 22 | const bobSeed = 'bob_seed'; 23 | 24 | const aliceRandom = new RandomPieceGenerator(aliceSeed, length); 25 | const bobRandom = new RandomPieceGenerator(bobSeed, length); 26 | 27 | console.log(`Alice Hashes: ${aliceRandom.hashes}\n`); 28 | console.log(`Bob Hashes: ${bobRandom.hashes}\n`); 29 | 30 | const randomGenerator = new MergedRandomGenerator(aliceRandom.getNextRandom(), bobRandom.getNextRandom()); 31 | const startingRandom = randomGenerator.getCurrentRandom(); 32 | console.log(`Starting Random: ${startingRandom}\n`); 33 | 34 | for(let i = length - 1; i >= 0; i--) { 35 | console.log(`Round ${length - i}`); 36 | const alicePreimage = aliceRandom.getNextRandom(); 37 | const bobPreimage = bobRandom.getNextRandom(); 38 | console.log(`Alice's Next Preimage: ${alicePreimage}`); 39 | console.log(`Bob's Next Preimage: ${bobPreimage}`); 40 | 41 | const nextRandom = randomGenerator.getNextRandom(alicePreimage, bobPreimage); 42 | 43 | console.log(`Next Random: ${nextRandom.modulo(100)}\n`); 44 | } 45 | 46 | } 47 | 48 | testPieceGenerator(); 49 | testMergedGenerator(); 50 | //TODO: Test failure/malicious scenarios 51 | -------------------------------------------------------------------------------- /src/random.js: -------------------------------------------------------------------------------- 1 | const web3 = require('web3'); 2 | var BigNumber = require('bignumber.js'); 3 | 4 | class RandomPieceGenerator { 5 | 6 | constructor(seed, length) { 7 | this.hashes = []; 8 | this.currentRound = 0; 9 | this.seed = seed; 10 | for(let i = 0; i < length; i++) { 11 | const lastHash = this.hashes[this.hashes.length - 1] || seed; 12 | this.hashes.push(web3.utils.soliditySha3(lastHash)); 13 | } 14 | } 15 | 16 | getRandom(roundNumber) { 17 | return this.hashes[this.hashes.length - roundNumber]; 18 | } 19 | 20 | getNextRandom() { 21 | this.currentRound++; 22 | const hash = this.hashes[this.hashes.length - this.currentRound] || this.seed; 23 | return hash; 24 | } 25 | 26 | } 27 | 28 | class MergedRandomGenerator { 29 | 30 | constructor(initialHashA, initialHashB) { 31 | this.lastHashA = initialHashA; 32 | this.lastHashB = initialHashB; 33 | this.lastHash = web3.utils.soliditySha3(this.lastHashA, this.lastHashB); 34 | } 35 | 36 | getCurrentRandom() { 37 | return this.lastHash; 38 | } 39 | 40 | getNextRandom(preimageA, preimageB) { 41 | const hashedPreimageA = web3.utils.soliditySha3(preimageA); 42 | const hashedPreimageB = web3.utils.soliditySha3(preimageB); 43 | const aMatch = hashedPreimageA === this.lastHashA; 44 | const bMatch = hashedPreimageB === this.lastHashB; 45 | if (!aMatch) { 46 | throw JSON.stringify({ 47 | message: 'Preimage from A does not cycle into correct value', 48 | preimageA, 49 | hashedPreimageA, 50 | lastHashA: this.lastHashA 51 | }); 52 | } 53 | if (!bMatch) { 54 | throw 'Preimage from B does not cycle into correct value'; 55 | } 56 | this.lastHashA = preimageA; 57 | this.lastHashB = preimageB; 58 | this.lastHash = web3.utils.soliditySha3(this.lastHashA, this.lastHashB); 59 | const number = new BigNumber(this.lastHash); 60 | return number; 61 | } 62 | 63 | } 64 | 65 | exports.RandomPieceGenerator = RandomPieceGenerator; 66 | exports.MergedRandomGenerator = MergedRandomGenerator; 67 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | require('babel-core/register') 3 | require('babel-polyfill') 4 | 5 | const Web3 = require('web3') 6 | const web3 = new Web3() 7 | const GSC = require('./general-state-channel') 8 | const SET = require('./set-payment-channels') 9 | const Merkle = require('./MerkleTree') 10 | 11 | const utils = require('./utils') 12 | 13 | // const config = require('./config') 14 | // replaced by repo-browser when running in browser 15 | // const defaultRepo = require('./runtime/repo-nodejs') 16 | 17 | exports = module.exports 18 | 19 | class Layer2lib { 20 | constructor(providerUrl, options) { 21 | if (!providerUrl) throw new Error('No provider URL provided') 22 | web3.setProvider(new web3.providers.HttpProvider(providerUrl)) 23 | this.web3 = web3 24 | 25 | this.merkleTree = Merkle 26 | this.utils = utils(this) 27 | 28 | if (!options.db) throw new Error('Require DB object'); 29 | if (!options.db.set) 30 | throw new Error('Not a valid DB object'); 31 | 32 | this.storage = options.db 33 | this.gsc = GSC(this) 34 | this.setPayment = SET(this) 35 | 36 | 37 | // TODO: store encrypted private key, require password to unlock and sign 38 | this.privateKey = options.privateKey 39 | } 40 | 41 | async getMainnetBalance(address) { 42 | const balance = await web3.eth.getBalance(address) 43 | return web3.utils.fromWei(balance, 'ether') 44 | } 45 | 46 | async initGSC(options) { 47 | await this.gsc.init(options) 48 | } 49 | 50 | async createGSCAgreement(options) { 51 | await this.gsc.createAgreement(options) 52 | } 53 | 54 | async getGSCAgreement(ID) { 55 | let res = await this.gsc.getAgreement(ID) 56 | return res 57 | } 58 | 59 | async joinGSCAgreement(agreement, state) { 60 | await this.gsc.joinAgreement(agreement, state) 61 | } 62 | 63 | 64 | async startGSCSettleAgreement(agreementID) { 65 | await this.gsc.startSettleAgreement(agreementID) 66 | } 67 | 68 | async challengeGSCAgreement(agreementID) { 69 | await this.gsc.challengeAgreement(agreementID) 70 | } 71 | 72 | async closeByzantineGSCAgreement(agreementID) { 73 | await this.gsc.closeByzantineAgreement(agreementID) 74 | } 75 | 76 | async openGSCChannel(options) { 77 | await this.gsc.openChannel(options) 78 | } 79 | } 80 | 81 | module.exports = Layer2lib 82 | -------------------------------------------------------------------------------- /src/MerkleTree.js: -------------------------------------------------------------------------------- 1 | const Buffer = require('buffer').Buffer 2 | const util = require('ethereumjs-util') 3 | const localUtils = require('./utils') 4 | 5 | function combinedHash(first, second) { 6 | if(!second) { 7 | return first 8 | } 9 | if (!first) { 10 | return second 11 | } 12 | let sorted = Buffer.concat([first, second].sort(Buffer.compare)) 13 | 14 | return util.sha3(sorted) 15 | } 16 | 17 | function deduplicate (buffers) { 18 | return buffers.filter((buffer, i) => { 19 | return buffers.findIndex(e => e.equals(buffer)) === i 20 | }) 21 | } 22 | 23 | function getPair (index, layer) { 24 | let pairIndex = index % 2 ? index - 1 : index + 1 25 | if (pairIndex < layer.length) { 26 | return layer[pairIndex] 27 | } else { 28 | return null 29 | } 30 | } 31 | 32 | function getLayers (elements) { 33 | if (elements.length === 0) { 34 | return [[Buffer.from('')]] 35 | } 36 | let layers = [] 37 | layers.push(elements) 38 | while (layers[layers.length - 1].length > 1) { 39 | layers.push(getNextLayer(layers[layers.length - 1])) 40 | } 41 | return layers 42 | } 43 | 44 | function getNextLayer (elements) { 45 | return elements.reduce((layer, element, index, arr) => { 46 | if (index % 2 === 0) { 47 | layer.push(combinedHash(element, arr[index + 1])) 48 | } 49 | return layer 50 | }, []) 51 | } 52 | 53 | function isHash(buffer) { 54 | return buffer.length === 32 && Buffer.isBuffer(buffer) 55 | } 56 | 57 | module.exports = class MerkleTree { 58 | 59 | constructor(_elements) { 60 | if(!_elements.every(isHash)){ 61 | throw new Error('elements must be 32 byte buffers') 62 | } 63 | const e = { elements: deduplicate(_elements) } 64 | Object.assign(this, e) 65 | 66 | this.elements.sort(Buffer.compare) 67 | 68 | const l = { layers: getLayers(this.elements) } 69 | Object.assign(this, l) 70 | } 71 | 72 | getRoot() { 73 | if(!this.root) { 74 | let r = { root:this.layers[this.layers.length - 1][0] } 75 | Object.assign(this, r) 76 | } 77 | return this.root 78 | } 79 | 80 | verify(proof, element) { 81 | return this.root.equals(proof.reduce((hash, pair) => combinedHash(hash, pair), element)) 82 | } 83 | 84 | proof(element) { 85 | let index = this.elements.findIndex(e => e.equals(element)) 86 | if (index === -1) { 87 | throw new Error('element not found in merkle tree') 88 | } 89 | 90 | return this.layers.reduce((proof, layer) => { 91 | let pair = getPair(index, layer) 92 | if (pair) { 93 | proof.push(pair) 94 | } 95 | index = Math.floor(index / 2) 96 | return proof 97 | }, []) 98 | } 99 | 100 | } -------------------------------------------------------------------------------- /src/index.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'js-layer2lib' { 2 | export default class Layer2lib { 3 | web3: any; 4 | merkleTree: any; 5 | utils: any; 6 | gsc: any; 7 | db: L2Database; 8 | 9 | /** 10 | * Initializes a web3 connection to the provider url 11 | * 12 | * @param str String to store in buffer. 13 | * @param encoding encoding to use, optional. Default is 'utf8' 14 | */ 15 | constructor(url: string, options: L2Options); 16 | 17 | /** 18 | * Returns the current balance as a string 19 | * 20 | * @param address Wallet address 21 | */ 22 | getMainnetBalance(address: string): string; 23 | initGSC(options: any): void; 24 | storeGSCAgreement(options: any): void; 25 | getGSCAgreement(ID: string): Promise; //agreement 26 | joinGSCAgreement(agreement: Agreement): Promise; 27 | } 28 | 29 | export interface L2Options { 30 | db: L2Database; 31 | privateKey?: string; 32 | } 33 | export interface Agreement { 34 | ID: string; 35 | partyA: string; 36 | partyB: string; 37 | balanceA: string; 38 | balanceB: string; 39 | openPending?: boolean; 40 | inDispute?: false; 41 | stateRaw?: any[]; 42 | stateSerialized?: string; 43 | signatures?: any[]; 44 | subChannels?: any; 45 | } 46 | 47 | // ============== START HERE ===================== 48 | type StrObject = { [key: string]: string }; 49 | type BigNumber = any; 50 | 51 | type VCID = string; 52 | type LCID = string; 53 | type Address = string; 54 | 55 | /* 56 | interface PartyKey { 57 | id: Address 58 | pubkey: string 59 | } 60 | */ 61 | 62 | export interface State { 63 | id: string; 64 | nonce: string; 65 | isClosed?: boolean; 66 | party: Address; 67 | counterparty: Address; 68 | sig: string; 69 | sig_counterpary?: string; 70 | } 71 | /* 72 | interface Balances { 73 | balanceA: BigNumber 74 | balanceB: BigNumber 75 | } 76 | */ 77 | export interface LCState extends State { 78 | openVCs: number; 79 | vcRootHash: string; 80 | balanceA: BigNumber; 81 | balanceB: BigNumber; 82 | } 83 | 84 | export interface VCState extends State { 85 | lcId: string; 86 | balanceA: BigNumber; 87 | balanceB: BigNumber; 88 | appState: StrObject | null; // challenger?: address; 89 | } 90 | 91 | /* 92 | interface PaymentState extends LCState { 93 | sender: Address 94 | balance: string 95 | } 96 | */ 97 | 98 | export interface L2Database { 99 | logdriver(): void; 100 | set(k: string, v: any): void; // for misc data 101 | get(k: string): any; 102 | 103 | storeLC(data: LCState): Promise; 104 | updateLC(data: LCState): Promise; // replace if same nonce 105 | getLC(ledgerID: LCID): Promise; // latest by nonce 106 | getLCElder(id: LCID): Promise; 107 | 108 | getLCbyNonce(id: LCID, seq: number): Promise; 109 | getLCs(cb: (lc: LCState) => void): void; // TODO replace above 110 | getLCsList(): Promise; 111 | 112 | delLC(id: LCID): Promise; 113 | 114 | storeVChannel(data: VCState): Promise; 115 | delVChannel(chan: VCID): Promise; 116 | // replace if same nonce 117 | updateVChannel(data: VCState): Promise; 118 | getVChannel(id: VCID): Promise; // latest by nonce 119 | getVChannelElder(id: VCID): Promise; // latest by nonce 120 | getVChannelbyNonce(id: VCID, seq: number): Promise; 121 | 122 | getVChannels(ledger: LCID, cb: (lc: VCState) => void): void; // latest by nonce 123 | getVChannelsList(ledger: LCID): Promise; 124 | getAllVChannels(cb: (lc: VCState) => void): void; 125 | getAllVChannelsList(): Promise; 126 | 127 | getVChannelStateCount(id: string): Promise; 128 | getLCStateCount(id: string): Promise; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /test/Layer2LibSetTest.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const Web3 = require('web3') 3 | const web3 = new Web3() 4 | const Gun = require('gun') 5 | require('gun/lib/then.js') 6 | require('gun/lib/unset.js') 7 | require('gun/lib/open.js') 8 | require('gun/lib/load.js') 9 | require('gun/lib/not.js') 10 | require('gun/lib/path.js') 11 | 12 | const Layer2lib = require('../src/index.js') 13 | const GunProxy = require('layer2storage').GunStorageProxy 14 | 15 | const gun = new Gun({ radisk: false, localStorage: true }) 16 | 17 | async function test(_db) { 18 | let _partyA = '0xd4EA3b21C312D7C6a1c744927a6F80Fe226A8416' 19 | let _partyB = '0xb1dd709d7eb8138f25b71103d41d50ba8708e816' 20 | let _partyI = '0x1e8524370b7caf8dc62e3effbca04ccc8e493ffe' 21 | 22 | // ALICE 23 | const proxyAlice = new GunProxy(_db, `layer2/Alice`); 24 | let optionsAlice = { 25 | db: proxyAlice, 26 | privateKey: '0x9eb0e84b7cadfcbbec8d49ae7112b25e0c1cb158ecd2160c301afa1f4a1029c8' 27 | } 28 | 29 | let lAlice = new Layer2lib('https://rinkeby.infura.io', optionsAlice) 30 | 31 | const lcS0 = { 32 | partyA: _partyA, 33 | partyI: _partyI, 34 | balanceA: '0.00001', 35 | balanceI: '0.00002' 36 | } 37 | 38 | 39 | await lAlice.setPayment.init() 40 | const id = await lAlice.setPayment.createLC(lcS0) 41 | let lc0Stored = await lAlice.setPayment.getLC(id) 42 | //console.log(lc0Stored) 43 | 44 | 45 | // Bob 46 | const proxyBob = new GunProxy(_db, `layer2/Bob`); 47 | let optionsBob = { 48 | db: proxyBob, 49 | privateKey: '0x42b83487fcc52252abd33f7c1d32006545388d0036e1ed3ae75c86a62a5c85d1' 50 | } 51 | 52 | let lBob = new Layer2lib('https://rinkeby.infura.io', optionsBob) 53 | 54 | const lcS0_b = { 55 | partyA: _partyB, 56 | partyI: _partyI, 57 | balanceA: '0.00004', 58 | balanceI: '0.00002' 59 | } 60 | 61 | 62 | await lBob.setPayment.init() 63 | const id_b = await lBob.setPayment.createLC(lcS0_b) 64 | let lc0Stored_b = await lBob.setPayment.getLC(id_b) 65 | console.log(lc0Stored_b) 66 | 67 | 68 | // Ingrid 69 | const proxyIngrid = new GunProxy(_db, `layer2/Ingrid`); 70 | let optionsIngrid = { 71 | db: proxyIngrid, 72 | privateKey: '0x2c339e1afdbfd0b724a4793bf73ec3a4c235cceb131dcd60824a06cefbef9875' 73 | } 74 | 75 | let lIngrid = new Layer2lib('https://rinkeby.infura.io', optionsIngrid) 76 | await lIngrid.setPayment.init() 77 | // todo: await lIngrid.setPayment.validateLCState(lc0Stored) 78 | // cannot get alice / bob state by id until we have a system that watches for updates 79 | // await lIngrid.setPayment.joinLC(id) 80 | // await lIngrid.setPayment.joinLC(id_b) 81 | await lIngrid.setPayment.joinLC(lc0Stored) 82 | await lIngrid.setPayment.joinLC(lc0Stored_b) 83 | 84 | // todo get sig from some update mechanism, then update database for alice 85 | let lc0Stored2 = await lBob.setPayment.getLC(id_b) // this should not have ingrids sig on it 86 | let lc0Stored2_b = await lIngrid.setPayment.getLC(id_b) 87 | console.log(lc0Stored2) 88 | 89 | // generate new lc state with vc state in it 90 | const vcS0 = { 91 | lcid: id, 92 | partyA: _partyA, 93 | partyB: _partyB, 94 | balanceA: '0.000005', 95 | balanceB: '0.000004', 96 | bond: '0.000009' 97 | } 98 | 99 | let vcid = await lAlice.setPayment.openVC(vcS0) 100 | let vc0Stored = await lAlice.setPayment.getVC(vcid) 101 | //console.log(vc0Stored) 102 | 103 | // bob joins when they see a request with given vcid 104 | // find some way to get the state from alice to bob's db 105 | vc0Stored.lcId = id_b 106 | await lBob.setPayment.joinVC(vc0Stored) 107 | 108 | 109 | 110 | // both alice and bob update request to hub db for their lc channels 111 | 112 | 113 | 114 | // Close channel 115 | 116 | const lcS2 = { 117 | id: id, 118 | partyA: _partyA, 119 | partyI: _partyI, 120 | balanceA: '0.000001', 121 | balanceI: '0.00002' 122 | } 123 | 124 | await lAlice.setPayment.initiateCloseLC(lcS2) 125 | let lc1Stored = await lAlice.setPayment.getLC(id) 126 | //console.log(lc1Stored) 127 | 128 | //await lIngrid.setPayment.confirmCloseLC(id) 129 | } 130 | 131 | test(gun) -------------------------------------------------------------------------------- /test/Layer2LibSetTest-fb.js: -------------------------------------------------------------------------------- 1 | if (!process.env.FIREBASE_KEY_PATH) { 2 | console.error('FIREBASE_KEY_PATH missing!') 3 | process.exit(0) 4 | } 5 | 6 | const admin = require('firebase-admin'); 7 | const serviceAccountJson = require(process.env.FIREBASE_KEY_PATH) 8 | 9 | 10 | admin.initializeApp({ 11 | credential: admin.credential.cert(serviceAccountJson) 12 | }); 13 | 14 | const Layer2lib = require('../src/index.js') 15 | const FirebaseProxy = require('layer2storage').FirebaseStorageProxy 16 | 17 | var db = admin.firestore(); 18 | 19 | async function test(_db) { 20 | let _partyA = '0xd4EA3b21C312D7C6a1c744927a6F80Fe226A8416' 21 | let _partyB = '0xb1dd709d7eb8138f25b71103d41d50ba8708e816' 22 | let _partyI = '0x1e8524370b7caf8dc62e3effbca04ccc8e493ffe' 23 | 24 | // ALICE 25 | const proxyAlice = new FirebaseProxy(_db, `layer2/Alice`); 26 | let optionsAlice = { 27 | db: proxyAlice, 28 | privateKey: '0x9eb0e84b7cadfcbbec8d49ae7112b25e0c1cb158ecd2160c301afa1f4a1029c8', 29 | } 30 | 31 | let lAlice = new Layer2lib('https://rinkeby.infura.io', optionsAlice) 32 | 33 | const lcS0 = { 34 | partyA: _partyA, 35 | partyI: _partyI, 36 | balanceA: '0.00001', 37 | balanceI: '0.00002' 38 | } 39 | 40 | 41 | await lAlice.setPayment.init() 42 | const id = await lAlice.setPayment.createLC(lcS0) 43 | let lc0Stored = await lAlice.setPayment.getLC(id) 44 | console.log(lc0Stored) 45 | 46 | 47 | // Bob 48 | const proxyBob = new FirebaseProxy(_db, `layer2/Bob`); 49 | let optionsBob = { 50 | db: proxyBob, 51 | privateKey: '0x42b83487fcc52252abd33f7c1d32006545388d0036e1ed3ae75c86a62a5c85d1' 52 | } 53 | 54 | let lBob = new Layer2lib('https://rinkeby.infura.io', optionsBob) 55 | 56 | const lcS0_b = { 57 | partyA: _partyB, 58 | partyI: _partyI, 59 | balanceA: '0.00004', 60 | balanceI: '0.00002' 61 | } 62 | 63 | 64 | await lBob.setPayment.init() 65 | const id_b = await lBob.setPayment.createLC(lcS0_b) 66 | let lc0Stored_b = await lBob.setPayment.getLC(id_b) 67 | console.log(lc0Stored_b) 68 | 69 | 70 | // // Ingrid 71 | // const proxyIngrid = new GunProxy(_db, `layer2/Ingrid`); 72 | // let optionsIngrid = { 73 | // db: proxyIngrid, 74 | // privateKey: '0x2c339e1afdbfd0b724a4793bf73ec3a4c235cceb131dcd60824a06cefbef9875' 75 | // } 76 | 77 | // let lIngrid = new Layer2lib('https://rinkeby.infura.io', optionsIngrid) 78 | // await lIngrid.setPayment.init() 79 | // // todo: await lIngrid.setPayment.validateLCState(lc0Stored) 80 | // // cannot get alice / bob state by id until we have a system that watches for updates 81 | // // await lIngrid.setPayment.joinLC(id) 82 | // // await lIngrid.setPayment.joinLC(id_b) 83 | // await lIngrid.setPayment.joinLC(lc0Stored) 84 | // await lIngrid.setPayment.joinLC(lc0Stored_b) 85 | 86 | // // todo get sig from some update mechanism, then update database for alice 87 | // let lc0Stored2 = await lBob.setPayment.getLC(id_b) // this should not have ingrids sig on it 88 | // let lc0Stored2_b = await lIngrid.setPayment.getLC(id_b) 89 | // console.log(lc0Stored2) 90 | 91 | // // generate new lc state with vc state in it 92 | // const vcS0 = { 93 | // lcid: id, 94 | // partyA: _partyA, 95 | // partyB: _partyB, 96 | // balanceA: '0.000005', 97 | // balanceB: '0.000004', 98 | // bond: '0.000009' 99 | // } 100 | 101 | // let vcid = await lAlice.setPayment.openVC(vcS0) 102 | // let vc0Stored = await lAlice.setPayment.getVC(vcid) 103 | // //console.log(vc0Stored) 104 | 105 | // // bob joins when they see a request with given vcid 106 | // // find some way to get the state from alice to bob's db 107 | // vc0Stored.lcId = id_b 108 | // await lBob.setPayment.joinVC(vc0Stored) 109 | 110 | 111 | 112 | // // both alice and bob update request to hub db for their lc channels 113 | 114 | 115 | 116 | // // Close channel 117 | 118 | // const lcS2 = { 119 | // id: id, 120 | // partyA: _partyA, 121 | // partyI: _partyI, 122 | // balanceA: '0.000001', 123 | // balanceI: '0.00002' 124 | // } 125 | 126 | // await lAlice.setPayment.initiateCloseLC(lcS2) 127 | // let lc1Stored = await lAlice.setPayment.getLC(id) 128 | // //console.log(lc1Stored) 129 | 130 | //await lIngrid.setPayment.confirmCloseLC(id) 131 | } 132 | 133 | test(db) -------------------------------------------------------------------------------- /test/layer2libTest.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const Web3 = require('web3') 3 | const web3 = new Web3() 4 | const Layer2lib = require('../src/index.js') 5 | 6 | const GunProxy = require('layer2storage').GunStorageProxy; 7 | const Gun = require("gun"); 8 | 9 | const gun = new Gun() 10 | test(gun); 11 | 12 | let etherPaymentIntAddress = '0x' 13 | let etherPaymentExtAddress = '0x' 14 | let CTFregistryAddress = '0x' 15 | 16 | async function test(gun) { 17 | // alice + bob keys from rinkeby 18 | const partyA = '0x1e8524370B7cAf8dC62E3eFfBcA04cCc8e493FfE' 19 | const partyAPrivate = '0x2c339e1afdbfd0b724a4793bf73ec3a4c235cceb131dcd60824a06cefbef9875' 20 | const partyB = '0x4c88305c5f9e4feb390e6ba73aaef4c64284b7bc' 21 | const partyBPrivate = '0xaee55c1744171b2d3fedbbc885a615b190d3dd7e79d56e520a917a95f8a26579' 22 | const aliceProxy = new GunProxy(gun, `layer2/${partyA}`); 23 | const bobProxy = new GunProxy(gun, `layer2/${partyB}`); 24 | 25 | // ALICE 26 | let optionsAlice = { 27 | db: aliceProxy, 28 | privateKey: partyAPrivate 29 | } 30 | 31 | let lAlice = new Layer2lib('https://rinkeby.infura.io', optionsAlice) 32 | 33 | web3.setProvider(new web3.providers.HttpProvider('https://rinkeby.infura.io')) 34 | 35 | try { 36 | const balA = await lAlice.getMainnetBalance(partyA) 37 | const balB = await lAlice.getMainnetBalance(partyB) 38 | console.log('alice starting balance:', balA) 39 | console.log('bob starting balance:', balB) 40 | } catch (e) { 41 | console.log(e) 42 | } 43 | 44 | lAlice.initGSC() 45 | 46 | // clear database 47 | await lAlice.gsc.clearStorage() 48 | 49 | let agreement = { 50 | ID: 'agreementId', 51 | types: ['Ether'], 52 | partyA, // Viewer or performer public key 53 | partyB, // Spank Hub public key 54 | balanceA: web3.utils.toWei('0.1', 'ether'), 55 | balanceB: web3.utils.toWei('0.2', 'ether') 56 | } 57 | 58 | await lAlice.createGSCAgreement(agreement) 59 | 60 | let Alice_agreement = await lAlice.getGSCAgreement('agreementId') 61 | //console.log(col) 62 | let Alice_tx = await lAlice.gsc.getTransactions('agreementId') 63 | //console.log(Alice_tx) 64 | let AliceAgreementState = await lAlice.gsc.getStates('agreementId') 65 | //Grab the latest (currently only state in list) 66 | AliceAgreementState = AliceAgreementState[0] 67 | //console.log(AliceAgreementState) 68 | 69 | console.log('Alice agreement created and stored.. initiating Bob') 70 | // -------------------------------------------------- 71 | 72 | // BOB 73 | let optionsBob = { 74 | db: bobProxy, 75 | privateKey: partyBPrivate 76 | } 77 | 78 | let lBob = new Layer2lib('https://rinkeby.infura.io', optionsBob) 79 | lBob.initGSC() 80 | 81 | console.log('Bob initialized, receive agreement from Alice and joins') 82 | 83 | let bobAgreement = JSON.parse(JSON.stringify(agreement)) 84 | 85 | await lBob.joinGSCAgreement(bobAgreement, AliceAgreementState) 86 | 87 | let Bob_agreement = await lBob.getGSCAgreement('agreementId') 88 | //console.log(Bob_agreement) 89 | let Bob_tx = await lBob.gsc.getTransactions('agreementId') 90 | //console.log(Bob_tx) 91 | let BobAgreementState = await lBob.gsc.getStates('agreementId') 92 | //console.log(BobAgreementState) 93 | 94 | console.log('Bob now sends openchannel ack to Alice') 95 | 96 | let isOpenAlice = await lAlice.gsc.isAgreementOpen('agreementId') 97 | console.log('Alice state is agreement open: ' + isOpenAlice) 98 | let isOpenBob = await lBob.gsc.isAgreementOpen('agreementId') 99 | console.log('Bob state is agreement open: ' + isOpenBob) 100 | // alice updates agreement with ack 101 | 102 | // Load Bob's ack into Alice db 103 | await lAlice.gsc.updateAgreement(bobAgreement) 104 | 105 | isOpenAlice = await lAlice.gsc.isAgreementOpen('agreementId') 106 | console.log('Alice state is agreement open: ' + isOpenAlice) 107 | 108 | 109 | 110 | // -------------------------------------------------- 111 | 112 | 113 | // Open a channel 114 | 115 | let channel = { 116 | ID: 'channelId', 117 | agreementID: 'agreementId', 118 | type: 'ether', 119 | balanceA: web3.utils.toWei('0.03', 'ether'), 120 | balanceB: web3.utils.toWei('0.05', 'ether') 121 | } 122 | 123 | await lAlice.openGSCChannel(channel) 124 | 125 | 126 | let Alice_chan = await lAlice.gsc.getChannel('channelId') 127 | //console.log(Alice_chan) 128 | Alice_agreement = await lAlice.getGSCAgreement('agreementId') 129 | //console.log(Alice_agreement) 130 | let AliceChanState = await lAlice.gsc.getStates('channelId') 131 | //console.log(AliceChanState) 132 | AliceAgreementState = await lAlice.gsc.getStates('agreementId') 133 | //console.log(AliceAgreementState) 134 | 135 | let chanBob = JSON.parse(JSON.stringify(Alice_chan)) 136 | Bob_agreement = JSON.parse(JSON.stringify(Alice_agreement)) 137 | await lBob.gsc.joinChannel(chanBob, Bob_agreement, chanBob.stateRaw) 138 | 139 | let Bob_chan = await lBob.gsc.getChannel('channelId') 140 | //console.log(Bob_chan) 141 | Bob_agreement = await lBob.getGSCAgreement('agreementId') 142 | //console.log(Bob_agreement) 143 | let BobChanState = await lBob.gsc.getStates('channelId') 144 | //console.log(BobChanState) 145 | BobAgreementState = await lBob.gsc.getStates('agreementId') 146 | //console.log(BobAgreementState) 147 | 148 | let txs_agreement = await lBob.gsc.getTransactions('agreementId') 149 | let txs_channel = await lBob.gsc.getTransactions('channelId') 150 | //console.log(txs_agreement) 151 | //console.log(txs_channel) 152 | 153 | console.log('Bob sends join channel ack to Alice') 154 | await lAlice.gsc.updateAgreement(Bob_agreement) 155 | 156 | 157 | // -------------------------------------------------- 158 | 159 | // Send ether in channel 160 | 161 | 162 | 163 | Alice_agreement = await lAlice.getGSCAgreement('agreementId') 164 | //console.log(Alice_agreement) 165 | 166 | console.log('ether channel now open') 167 | console.log('Bob is initiating ether payment') 168 | 169 | let updateState = { 170 | isClose: 0, 171 | balanceA: web3.utils.toWei('0.06', 'ether'), 172 | balanceB: web3.utils.toWei('0.02', 'ether') 173 | } 174 | 175 | await lBob.gsc.initiateUpdateChannelState('channelId', updateState, false) 176 | 177 | Bob_chan = await lBob.gsc.getChannel('channelId') 178 | //console.log(Bob_chan) 179 | Bob_agreement = await lBob.getGSCAgreement('agreementId') 180 | //console.log(Bob_agreement) 181 | BobChanState = await lBob.gsc.getStates('channelId') 182 | //console.log(BobChanState) 183 | BobAgreementState = await lBob.gsc.getStates('agreementId') 184 | //console.log(BobAgreementState) 185 | 186 | console.log('Bob sends channel state update to Alice') 187 | 188 | let chanAlice = JSON.parse(JSON.stringify(Bob_chan)) 189 | let agreeAlice = JSON.parse(JSON.stringify(Bob_agreement)) 190 | //console.log(agreeAlice) 191 | 192 | 193 | await lAlice.gsc.confirmUpdateChannelState(chanAlice, agreeAlice, updateState) 194 | 195 | Alice_chan = await lAlice.gsc.getChannel('channelId') 196 | //console.log(Alice_chan) 197 | Alice_agreement = await lAlice.getGSCAgreement('agreementId') 198 | //console.log(Alice_agreement) 199 | // console.log(Alice_agreement.stateSignatures) 200 | AliceChanState = await lAlice.gsc.getStates('channelId') 201 | //console.log(AliceChanState) 202 | AliceAgreementState = await lAlice.gsc.getStates('agreementId') 203 | //console.log(AliceAgreementState) 204 | 205 | console.log('Alice confirmed channel state update, sends ack to Bob') 206 | 207 | await lBob.gsc.updateAgreement(Alice_agreement) 208 | 209 | txs_channel = await lBob.gsc.getTransactions('channelId') 210 | txs_agreement = await lBob.gsc.getTransactions('agreementId') 211 | Alice_tx = await lAlice.gsc.getTransactions('agreementId') 212 | let Alice_tx_chan = await lAlice.gsc.getTransactions('channelId') 213 | //console.log(txs_agreement) 214 | 215 | 216 | // -------------------------------------------------- 217 | 218 | // Send ether in channel and close channel 219 | 220 | // Close Channel Consensus 221 | 222 | // updateState = { 223 | // isClose: 1, 224 | // balanceA: web3.utils.toWei(0.07, 'ether'), 225 | // balanceB: web3.utils.toWei(0.01, 'ether') 226 | // } 227 | // Bob_agreement = await lBob.getGSCAgreement('spankHub1337Bob') 228 | // //console.log(Bob_agreement) 229 | // await lBob.gsc.initiateUpdateChannelState('respekBob', updateState, false) 230 | // Bob_chan = await lBob.gsc.getChannel('respekBob') 231 | // Bob_agreement = await lBob.getGSCAgreement('spankHub1337Bob') 232 | // //console.log(Bob_agreement) 233 | // agreeAlice = JSON.parse(JSON.stringify(Bob_agreement)) 234 | // chanAlice = JSON.parse(JSON.stringify(Bob_chan)) 235 | // chanAlice.dbSalt = 'Alice' 236 | // agreeAlice.dbSalt = 'Alice' 237 | 238 | // await lAlice.gsc.confirmUpdateChannelState(chanAlice, agreeAlice, updateState) 239 | 240 | // let allAgreements = await lAlice.gsc.getAllAgreements() 241 | // //console.log(allAgreements) 242 | 243 | // let allChannels = await lAlice.gsc.getAllChannels() 244 | // //console.log(allChannels) 245 | 246 | // let alltxs = await lAlice.gsc.getAllTransactions() 247 | // //console.log(alltxs) 248 | 249 | // let allRawStates = await lAlice.gsc.getAllRawStates() 250 | // //console.log(allRawStates) 251 | 252 | // Alice_agreement = await lAlice.getGSCAgreement('spankHub1337Alice') 253 | 254 | // Alice_agreement.dbSalt = 'Bob' 255 | // await lBob.gsc.updateAgreement(Alice_agreement) 256 | // Alice_agreement.dbSalt = 'Alice' 257 | 258 | // // Close agreement 259 | // await lAlice.gsc.initiateCloseAgreement('spankHub1337Alice') 260 | 261 | // Alice_chan = await lAlice.gsc.getChannel('respekAlice') 262 | // //console.log(Alice_chan) 263 | // Alice_agreement = await lAlice.getGSCAgreement('spankHub1337Alice') 264 | // //console.log(Alice_agreement) 265 | // //console.log(Alice_agreement.stateSignatures) 266 | // AliceChanState = await lAlice.gsc.getStates('respekAlice') 267 | // //console.log(AliceChanState) 268 | // AliceAgreementState = await lAlice.gsc.getStates('spankHub1337Alice') 269 | // //console.log(AliceAgreementState) 270 | 271 | // Alice_agreement.dbSalt = 'Bob' 272 | // await lBob.gsc.confirmCloseAgreement(Alice_agreement, AliceAgreementState) 273 | // Bob_chan = await lBob.gsc.getChannel('respekBob') 274 | // //console.log(Bob_chan) 275 | // Bob_agreement = await lBob.getGSCAgreement('spankHub1337Bob') 276 | // //console.log(Bob_agreement) 277 | // BobChanState = await lBob.gsc.getStates('respekBob') 278 | // //console.log(BobChanState) 279 | // BobAgreementState = await lBob.gsc.getStates('spankHub1337Bob') 280 | // //console.log(BobAgreementState) 281 | 282 | // Bob_agreement = await lBob.getGSCAgreement('spankHub1337Bob') 283 | 284 | // Bob_agreement.dbSalt = 'Alice' 285 | // await lAlice.gsc.updateAgreement(Bob_agreement) 286 | // Bob_agreement.dbSalt = 'Bob' 287 | 288 | // // Note: Either party may call this now to move final state 289 | // await lBob.gsc.finalizeAgreement('spankHub1337Bob') 290 | 291 | 292 | // Close Channel Byzantine 293 | 294 | await lBob.gsc.startSettleChannel('channelId') 295 | 296 | console.log('Settlement period started on channel, calling close after') 297 | 298 | await lBob.gsc.closeByzantineChannel('channelId') 299 | 300 | console.log('Agreement finalized, quiting...') 301 | 302 | try { 303 | const balA = await lAlice.getMainnetBalance(partyA) 304 | const balB = await lAlice.getMainnetBalance(partyB) 305 | console.log('alice ending balance:', balA) 306 | console.log('bob ending balance:', balB) 307 | } catch (e) { 308 | console.log(e) 309 | } 310 | 311 | } 312 | -------------------------------------------------------------------------------- /src/set-payment-channels.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const axios = require('axios') 4 | const BigNumber = require('bignumber.js') 5 | const LC = require('../contracts/LedgerChannel.json') 6 | const EC = require('../contracts/ECTools.json') 7 | 8 | module.exports = function setPayment (self) { 9 | return { 10 | init: async function(options) { 11 | // TODO: Check against counterfactual registry and see if any 12 | // of the channels are being challenged when online 13 | self.ledgerAddress = '0x009d610444387202fec65eb056b0a7c141e4b9ed' 14 | self.abi = LC.abi 15 | self.api = axios.create({ 16 | baseURL: 'http://localhost:8080/', 17 | timeout: 5000, 18 | // TODO: hub api auth 19 | // headers: { 'X-Custom-Header': 'foobar' } 20 | }); 21 | }, 22 | 23 | createLC: async function(options) { 24 | 25 | const _id = self.utils.getNewChannelId() 26 | 27 | let raw_lcS0 = { 28 | isClosed: false, 29 | nonce: '0', 30 | numOpenVC: '0', 31 | rootHash: '0x0', 32 | partyA: options.partyA, 33 | partyI: options.partyI, 34 | balanceA: options.balanceA, 35 | balanceI: options.balanceI 36 | } 37 | 38 | const _state = await self.utils.createLCStateUpdate(raw_lcS0) 39 | const _sig = await self.utils.signState(_state) 40 | 41 | const lcS0 = { 42 | id: _id, 43 | isClosed: false, 44 | nonce: '0', 45 | numOpenVC: '0', 46 | rootHash: '0x0', 47 | partyA: options.partyA, 48 | partyI: options.partyI, 49 | balanceA: options.balanceA, 50 | balanceI: options.balanceI, 51 | stateHash: _state, 52 | sig: _sig 53 | } 54 | 55 | //console.log(self.utils.bufferToHex(self.utils.ecrecover(_state, _sig.v, _sig.r, _sig.s))) 56 | 57 | // contact hub with lcS0, wait for signature response 58 | const sigResponse = await self.api.post('/v1/lc/open', lcS0) 59 | 60 | // store the res signature 61 | await self.storage.storeLC(lcS0) 62 | 63 | let tx_receipt = await self.utils.createLCHandler(lcS0) 64 | 65 | //await self.utils.testLC() 66 | 67 | return _id 68 | }, 69 | 70 | // TODO: Replace agreement with just the state sig from counterparty 71 | joinLC: async function(lc) { 72 | //TODO: verify format of lc 73 | // need lc data updated to database before just id will work 74 | //let lc = await this.getLC(id) 75 | let raw_lcS0 = { 76 | isClosed: lc.isClosed, 77 | nonce: lc.nonce, 78 | numOpenVC: lc.numOpenVC, 79 | rootHash: lc.rootHash, 80 | partyA: lc.partyA, 81 | partyI: lc.partyI, 82 | balanceA: lc.balanceA, 83 | balanceI: lc.balanceI 84 | } 85 | 86 | const _state = await self.utils.createLCStateUpdate(raw_lcS0) 87 | const _sig = await self.utils.signState(_state) 88 | 89 | lc.sig_counterparty = _sig 90 | await self.storage.storeLC(lc) 91 | 92 | //todo send sig to alice 93 | 94 | let tx_receipt = await self.utils.joinLCHandler(lc) 95 | 96 | return lc.id 97 | }, 98 | 99 | refundOpenLC: async function(lc_id) { 100 | 101 | }, 102 | 103 | updateLCsigs: async function(lc_id, sig) { 104 | let lcState = await this.getLC(lc.id) 105 | lcState.sig_counterparty = sig 106 | await self.storage.updateLC(lcState) 107 | }, 108 | 109 | // just allowed to update the balance of an lc 110 | updateLC: async function(lc) { 111 | let oldState = await this.getLC(lc.id) 112 | 113 | // console.log(oldState) 114 | // todo state update validation 115 | 116 | let _nonce = parseInt(oldState.nonce, 10) 117 | _nonce = _nonce+1 118 | 119 | // todo network send to hub 120 | }, 121 | 122 | confirmUpdateLC: async function(lc) { 123 | let oldState = await this.getLC(lc.id) 124 | 125 | // console.log(oldState) 126 | // todo state update validation 127 | 128 | }, 129 | 130 | initiateCloseLC: async function(lc) { 131 | // todo call consensus close with double signed close state 132 | let oldState = await this.getLC(lc.id) 133 | // todo: verify latest known lc state from db doesn't have open vc on it 134 | 135 | let _nonce = parseInt(oldState.nonce, 10) 136 | _nonce = _nonce+1 137 | 138 | let raw_lcS = { 139 | isClosed: true, 140 | nonce: _nonce, 141 | numOpenVC: '0', 142 | rootHash: '0x0', 143 | partyA: oldState.partyA, 144 | partyI: oldState.partyI, 145 | balanceA: lc.balanceA, 146 | balanceI: lc.balanceI 147 | } 148 | 149 | const _state = await self.utils.createLCStateUpdate(raw_lcS) 150 | const _sig = await self.utils.signState(_state) 151 | 152 | let lcS = raw_lcS 153 | lcS.stateHash = _state 154 | lcS.sig = _sig 155 | lcS.id = lc.id 156 | 157 | // contact hub message node 158 | 159 | await self.storage.updateLC(lcS) 160 | 161 | }, 162 | 163 | confirmCloseLC: async function(lc) { 164 | let oldState = await this.getLC(lc.id) 165 | 166 | // todo state update validation 167 | let raw_lcS = { 168 | isClosed: true, 169 | nonce: _nonce, 170 | numOpenVC: '0', 171 | rootHash: '0x0', 172 | partyA: oldState.partyA, 173 | partyI: oldState.partyI, 174 | balanceA: lc.balanceA, 175 | balanceI: lc.balanceI 176 | } 177 | 178 | const _state = await self.utils.createLCStateUpdate(raw_lcS) 179 | const _sig = await self.utils.signState(_state) 180 | 181 | await self.storage.updateLC(lcS) 182 | 183 | // todo: call close on contract (consensusCloseLC) 184 | 185 | }, 186 | 187 | consensusCloseLC: async function(lc) { 188 | 189 | 190 | }, 191 | 192 | // channel functions 193 | 194 | openVC: async function(options) { 195 | let lcState = await this.getLC(options.lcid) 196 | 197 | // generate init vc state 198 | const _id = self.utils.getNewChannelId() 199 | 200 | let raw_vcS0 = { 201 | nonce: '0', 202 | partyA: options.partyA, 203 | partyB: options.partyB, 204 | balanceA: options.balanceA, 205 | balanceB: options.balanceB, 206 | hubBond: options.bond 207 | } 208 | 209 | const _state = await self.utils.createVCStateUpdate(raw_vcS0) 210 | const _sig = await self.utils.signState(_state) 211 | 212 | const vcS0 = raw_vcS0 213 | vcS0.lcId = options.lcid 214 | vcS0.id = _id 215 | vcS0.stateHash = _state 216 | vcS0.sig = _sig 217 | 218 | // pass vc state to counterparty 219 | 220 | // generate merkle root 221 | // todo: dont assume two channels 222 | let buf = self.utils.hexToBuffer(_state) 223 | let elems = [] 224 | elems.push(buf) 225 | elems.push(self.utils.hexToBuffer('0x0000000000000000000000000000000000000000000000000000000000000000')) 226 | 227 | let merkle = new self.merkleTree(elems) 228 | 229 | let vcRootHash = self.utils.bufferToHex(merkle.getRoot()) 230 | 231 | // generate new lc state 232 | // initiator should always be balance A, joiner is balance B 233 | let _nonce = parseInt(lcState.nonce, 10) 234 | _nonce = _nonce+1 235 | let _numVC = parseInt(lcState.numOpenVC, 10) 236 | _numVC = _numVC+1 237 | let _newBalA = parseFloat(lcState.balanceA, 10) 238 | _newBalA = _newBalA - parseFloat(options.balanceA, 10) 239 | _newBalA = _newBalA.toFixed(18) 240 | let _newBalI = parseFloat(lcState.balanceI, 10) 241 | _newBalI = _newBalI - parseFloat(options.balanceB, 10) 242 | // fix javascript precision on floating point math 243 | _newBalI = _newBalI.toFixed(18) 244 | 245 | let raw_lcS = { 246 | isClosed: false, 247 | nonce: _nonce, 248 | numOpenVC: _numVC, 249 | rootHash: vcRootHash, 250 | partyA: lcState.partyA, 251 | partyI: lcState.partyI, 252 | balanceA: _newBalA.toString(), 253 | balanceI: _newBalI.toString() 254 | } 255 | 256 | const _lcstate = await self.utils.createLCStateUpdate(raw_lcS) 257 | const _lcsig = await self.utils.signState(_lcstate) 258 | 259 | let lcS = raw_lcS 260 | lcS.id = lcState.id 261 | lcS.stateHash = _lcstate, 262 | lcS.sig = _lcsig 263 | 264 | // pass lc state to hub 265 | 266 | // store vc state 267 | let s = await self.storage.storeVChannel(vcS0) 268 | //console.log(s) 269 | // store lc state 270 | await self.storage.updateLC(lcS) 271 | // listen for responses 272 | return _id 273 | }, 274 | 275 | // Called byt hub and counterparty 276 | // you must respond to any request before updating any other state (everything pulls from latest) 277 | joinVC: async function(vc) { 278 | let lcState = await this.getLC(vc.lcId) 279 | 280 | let raw_vcS0 = { 281 | nonce: '0', 282 | partyA: vc.partyA, 283 | partyB: vc.partyB, 284 | balanceA: vc.balanceA, 285 | balanceB: vc.balanceB, 286 | hubBond: vc.bond 287 | } 288 | 289 | const _sig = await self.utils.signState(vc.stateHash) 290 | 291 | const vcS0 = raw_vcS0 292 | vcS0.lcId = vc.lcId 293 | vcS0.id = vc.id 294 | vcS0.stateHash = vc.stateHash 295 | vcS0.sig = _sig 296 | 297 | // pass vc state to counterparty 298 | 299 | // generate merkle root 300 | // todo: dont assume two channels 301 | let buf = self.utils.hexToBuffer(vc.stateHash) 302 | let elems = [] 303 | elems.push(buf) 304 | elems.push(self.utils.hexToBuffer('0x0000000000000000000000000000000000000000000000000000000000000000')) 305 | 306 | let merkle = new self.merkleTree(elems) 307 | 308 | let vcRootHash = self.utils.bufferToHex(merkle.getRoot()) 309 | 310 | // generate new lc state 311 | 312 | let _nonce = parseInt(lcState.nonce, 10) 313 | _nonce = _nonce+1 314 | let _numVC = parseInt(lcState.numOpenVC, 10) 315 | _numVC = _numVC+1 316 | let _newBalA = parseFloat(lcState.balanceA, 10) 317 | _newBalA = _newBalA - parseFloat(vc.balanceB, 10) 318 | _newBalA = _newBalA.toFixed(18) 319 | let _newBalI = parseFloat(lcState.balanceI, 10) 320 | _newBalI = _newBalI - parseFloat(vc.balanceA, 10) 321 | // fix javascript precision on floating point math 322 | _newBalI = _newBalI.toFixed(18) 323 | 324 | let raw_lcS = { 325 | isClosed: false, 326 | nonce: _nonce, 327 | numOpenVC: _numVC, 328 | rootHash: vcRootHash, 329 | partyA: lcState.partyA, 330 | partyI: lcState.partyI, 331 | balanceA: _newBalA.toString(), 332 | balanceI: _newBalI.toString() 333 | } 334 | 335 | const _lcstate = await self.utils.createLCStateUpdate(raw_lcS) 336 | const _lcsig = await self.utils.signState(_lcstate) 337 | 338 | let lcS = raw_lcS 339 | lcS.id = lcState.id 340 | lcS.stateHash = _lcstate, 341 | lcS.sig = _lcsig 342 | 343 | // pass lc state to hub 344 | 345 | // store vc state 346 | let s = await self.storage.storeVChannel(vcS0) 347 | //console.log(s) 348 | // store lc state 349 | await self.storage.updateLC(lcS) 350 | // listen for responses 351 | return vc.id 352 | 353 | }, 354 | 355 | // When Ingrid receives both agreements 356 | hubConfirmVC: async function(lcid_A, lcid_B, vc_id) { 357 | 358 | }, 359 | 360 | // TODO: before updating any channel state we should check that the channel 361 | // is not closed. Check previous channel state for close flag 362 | updateVCState: async function(id, updateState) { 363 | 364 | }, 365 | 366 | confirmVCState: async function(id, updateState) { 367 | 368 | }, 369 | 370 | isVCOpen: async function(agreementID) { 371 | 372 | 373 | }, 374 | 375 | closeVC: async function(vc_id) { 376 | 377 | }, 378 | // Byzantine functions 379 | 380 | startSettleLC: async function(channelID) { 381 | 382 | }, 383 | 384 | challengeSettleLC: async function(channelID) { 385 | // TODO: call challengeSettle on metachannel 386 | }, 387 | 388 | 389 | startSettleVC: async function(agreementID) { 390 | 391 | }, 392 | 393 | challengeSettleVC: async function(agreementID) { 394 | 395 | }, 396 | 397 | closeByzantineVC: async function(agreementID) { 398 | 399 | 400 | }, 401 | 402 | 403 | closeByzantineLC: async function(channelID) { 404 | 405 | }, 406 | 407 | _verifyUpdate: function(updateAgreement, currentAgreement, currentChannel, updateState) { 408 | 409 | }, 410 | 411 | // DB helpers 412 | 413 | getLC: async function(id) { 414 | let lc = await self.storage.getLC(id) 415 | return lc 416 | }, 417 | 418 | getVC: async function(id) { 419 | let vc = await self.storage.getVChannel(id) 420 | return vc 421 | }, 422 | 423 | getAllLCs: async function() { 424 | 425 | }, 426 | 427 | getAllVCs: async function() { 428 | 429 | }, 430 | 431 | getAllTransactions: async function() { 432 | 433 | }, 434 | 435 | getTransaction: async function(agreementID) { 436 | 437 | }, 438 | 439 | syncDatabase: async function(agreement) { 440 | 441 | } 442 | } 443 | } 444 | -------------------------------------------------------------------------------- /test/Layer2LibVCTest.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | const Web3 = require('web3') 3 | const web3 = new Web3() 4 | const Layer2lib = require('../src/index.js') 5 | const rand = require('../src/random.js').RandomPieceGenerator 6 | const mergeRand = require('../src/random.js').MergedRandomGenerator 7 | const GunProxy = require('layer2storage').GunStorageProxy; 8 | const Gun = require("gun"); 9 | 10 | const gun = new Gun() 11 | test(gun); 12 | 13 | let battleEthIntAddress = '0x' 14 | let etherPaymentExtAddress = '0x' 15 | let CTFregistryAddress = '0x' 16 | 17 | let attackTable = ['12', '6', '25'] 18 | 19 | async function test(gun) { 20 | // ALICE ---------------------- 21 | const proxyAlice = new GunProxy(gun, `layer2/Alice`); 22 | let optionsAlice = { 23 | db: proxyAlice, 24 | privateKey: '0x2c339e1afdbfd0b724a4793bf73ec3a4c235cceb131dcd60824a06cefbef9875' 25 | } 26 | 27 | let lAlice = new Layer2lib("https://rinkeby.infura.io", optionsAlice) 28 | 29 | web3.setProvider(new web3.providers.HttpProvider('https://rinkeby.infura.io')) 30 | 31 | lAlice.initGSC() 32 | 33 | // clear database hack. Clears eveyones database if testing multiple parties on one system 34 | await lAlice.gsc.clearStorage() 35 | 36 | let agreementAlice = { 37 | dbSalt: 'Alice', // for testing multiple layer2 instances on same db 38 | ID: 'battleHub1337', 39 | types: ['Battle-Ether'], 40 | partyA: '0x1e8524370b7caf8dc62e3effbca04ccc8e493ffe', // Player Alice public Key 41 | partyB: '0xd4EA3b21C312D7C6a1c744927a6F80Fe226A8416', // Game Hub public key 42 | balanceA: web3.utils.toWei('0.1', 'ether'), 43 | balanceB: web3.utils.toWei('0.2', 'ether') 44 | } 45 | 46 | let entryID = agreementAlice.ID + agreementAlice.dbSalt 47 | 48 | await lAlice.createGSCAgreement(agreementAlice) 49 | 50 | let Alice_agreement = await lAlice.getGSCAgreement(entryID) 51 | //console.log(col) 52 | let Alice_tx = await lAlice.gsc.getTransactions(entryID) 53 | //console.log(Alice_tx) 54 | let AliceAgreementState = await lAlice.gsc.getStates('battleHub1337Alice') 55 | //Grab the latest (currently only state in list) 56 | AliceAgreementState = AliceAgreementState[0] 57 | //console.log(AliceAgreementState) 58 | 59 | console.log('Alice agreement created and stored.. initiating Ingrid') 60 | 61 | 62 | // -------------------------------------------------- 63 | 64 | 65 | 66 | // Ingrid ------------------ 67 | const proxyIngrid = new GunProxy(gun, 'layer2/Ingrid'); 68 | let optionsIngrid = { 69 | db: proxyIngrid, 70 | privateKey: '0x9eb0e84b7cadfcbbec8d49ae7112b25e0c1cb158ecd2160c301afa1f4a1029c8' 71 | } 72 | 73 | let lIngrid = new Layer2lib('https://rinkeby.infura.io', optionsIngrid) 74 | lIngrid.initGSC() 75 | 76 | console.log('Ingrid initialized, receive agreement from Alice and joins') 77 | 78 | let agreementIngrid = JSON.parse(JSON.stringify(agreementAlice)) 79 | agreementIngrid.dbSalt = 'Ingrid' 80 | 81 | await lIngrid.joinGSCAgreement(agreementIngrid, AliceAgreementState) 82 | 83 | let Ingrid_agreement = await lIngrid.getGSCAgreement('battleHub1337Ingrid') 84 | //console.log(Ingrid_agreement) 85 | let Ingrid_tx = await lIngrid.gsc.getTransactions('battleHub1337Ingrid') 86 | //console.log(Ingrid_tx) 87 | let IngridAgreementState = await lIngrid.gsc.getStates('battleHub1337Ingrid') 88 | //console.log(IngridAgreementState) 89 | 90 | console.log('Ingrid now sends openchannel ack to Alice') 91 | 92 | let isOpenAlice = await lAlice.gsc.isAgreementOpen('battleHub1337Alice') 93 | console.log('Alice state is agreement open: ' + isOpenAlice) 94 | let isOpenIngrid = await lIngrid.gsc.isAgreementOpen('battleHub1337Ingrid') 95 | console.log('Ingrid state is agreement open: ' + isOpenIngrid) 96 | 97 | // Load Bob's ack into Alice db 98 | agreementIngrid.dbSalt = 'Alice' 99 | await lAlice.gsc.updateAgreement(agreementIngrid) 100 | agreementIngrid.dbSalt = 'Ingrid' 101 | 102 | isOpenAlice = await lAlice.gsc.isAgreementOpen('battleHub1337Alice') 103 | console.log('Alice state is agreement open: ' + isOpenAlice) 104 | 105 | 106 | // Initiate Bob 107 | 108 | // BOB ----------------------- 109 | const proxyBob = new GunProxy(gun, 'layer2/bob'); 110 | let optionsBob = { 111 | db: proxyBob, 112 | privateKey: '0xaee55c1744171b2d3fedbbc885a615b190d3dd7e79d56e520a917a95f8a26579' 113 | } 114 | 115 | let lBob = new Layer2lib("https://rinkeby.infura.io", optionsBob) 116 | 117 | web3.setProvider(new web3.providers.HttpProvider('http://localhost:7545')) 118 | 119 | lBob.initGSC() 120 | 121 | let agreementBob = { 122 | dbSalt: 'Bob', // for testing multiple layer2 instances on same db 123 | ID: 'battleHub420', 124 | types: ['Battle-Ether'], 125 | partyA: '0x4c88305c5f9e4feb390e6ba73aaef4c64284b7bc', // Viewer or performer public key 126 | partyB: '0xd4EA3b21C312D7C6a1c744927a6F80Fe226A8416', // Spank Hub public key 127 | balanceA: web3.utils.toWei('0.1', 'ether'), 128 | balanceB: web3.utils.toWei('0.2', 'ether') 129 | } 130 | 131 | let entryID_b = agreementBob.ID + agreementBob.dbSalt 132 | 133 | await lBob.createGSCAgreement(agreementBob) 134 | 135 | let Bob_agreement = await lBob.getGSCAgreement(entryID_b) 136 | //console.log(col) 137 | let Bob_tx = await lBob.gsc.getTransactions(entryID_b) 138 | //console.log(Alice_tx) 139 | let BobAgreementState = await lBob.gsc.getStates('battleHub420Bob') 140 | //Grab the latest (currently only state in list) 141 | BobAgreementState = BobAgreementState[0] 142 | //console.log(AliceAgreementState) 143 | 144 | console.log('Bob agreement created and stored.. initiating with Ingrid') 145 | 146 | // Ingrid Join Bob--------------------- 147 | 148 | let agreementIngrid_b = JSON.parse(JSON.stringify(agreementBob)) 149 | agreementIngrid_b.dbSalt = 'Ingrid' 150 | 151 | await lIngrid.joinGSCAgreement(agreementIngrid_b, BobAgreementState) 152 | 153 | Ingrid_agreement = await lIngrid.getGSCAgreement('battleHub420Ingrid') 154 | //console.log(Ingrid_agreement) 155 | Ingrid_tx = await lIngrid.gsc.getTransactions('battleHub420Ingrid') 156 | //console.log(Ingrid_tx) 157 | IngridAgreementState = await lIngrid.gsc.getStates('battleHub420Ingrid') 158 | //console.log(IngridAgreementState) 159 | 160 | console.log('Ingrid now sends openchannel ack to Bob') 161 | 162 | let isOpenBob = await lBob.gsc.isAgreementOpen('battleHub420Bob') 163 | console.log('Bob state is agreement open: ' + isOpenBob) 164 | isOpenIngrid = await lIngrid.gsc.isAgreementOpen('battleHub420Ingrid') 165 | console.log('Ingrid state is agreement open: ' + isOpenIngrid) 166 | 167 | // Load Ingrids's ack into Bobs db 168 | Ingrid_agreement.dbSalt = 'Bob' 169 | await lBob.gsc.updateAgreement(Ingrid_agreement) 170 | Ingrid_agreement.dbSalt = 'Ingrid' 171 | 172 | isOpenBob = await lBob.gsc.isAgreementOpen('battleHub420Bob') 173 | console.log('Bob state is agreement open: ' + isOpenBob) 174 | 175 | // -------------------------------------------------- 176 | 177 | 178 | // Open a virtual channel 179 | 180 | let channelAlice = { 181 | dbSalt: 'Alice', // for testing multiple layer2 instances on same db 182 | ID: 'respek', 183 | agreementID: 'battleHub1337', 184 | type: 'battleEther', 185 | counterparty: '0x4c88305c5f9e4feb390e6ba73aaef4c64284b7bc', 186 | balanceA: web3.utils.toWei('0.03', 'ether'), 187 | balanceB: web3.utils.toWei('0.05', 'ether'), 188 | bond: web3.utils.toWei('0.03', 'ether') 189 | } 190 | 191 | await lAlice.openGSCChannel(channelAlice) 192 | 193 | 194 | let Alice_chan = await lAlice.gsc.getChannel('respekAlice') 195 | //console.log(Alice_chan) 196 | Alice_agreement = await lAlice.getGSCAgreement('battleHub1337Alice') 197 | //console.log(Alice_agreement) 198 | let AliceChanState = await lAlice.gsc.getStates('respekAlice') 199 | //console.log(AliceChanState) 200 | AliceAgreementState = await lAlice.gsc.getStates('battleHub1337Alice') 201 | //console.log(AliceAgreementState) 202 | 203 | console.log('Alice sends her agreement with ingrid to ingrid and bob') 204 | 205 | console.log('Bob generating same agreement with ingrid') 206 | 207 | let channelBob = { 208 | dbSalt: 'Bob', // for testing multiple layer2 instances on same db 209 | ID: 'respek', 210 | agreementID: 'battleHub420', 211 | type: 'battleEther', 212 | counterparty: '0x1e8524370b7caf8dc62e3effbca04ccc8e493ffe', 213 | balanceA: web3.utils.toWei('0.03', 'ether'), 214 | balanceB: web3.utils.toWei('0.05', 'ether'), 215 | bond: web3.utils.toWei('0.05', 'ether') 216 | } 217 | 218 | 219 | await lBob.openGSCChannel(channelBob) 220 | 221 | 222 | let Bob_chan = await lBob.gsc.getChannel('respekBob') 223 | //console.log(Bob_chan) 224 | Bob_agreement = await lBob.getGSCAgreement('battleHub420Bob') 225 | //console.log(Bob_agreement) 226 | let BobChanState = await lBob.gsc.getStates('respekBob') 227 | //console.log(BobChanState) 228 | BobAgreementState = await lBob.gsc.getStates('battleHub420Bob') 229 | //console.log(BobAgreementState) 230 | 231 | console.log('Ingrid receives update agreement states from both alice and bob') 232 | 233 | 234 | // Ingrid calls join channel on Alice------------- 235 | 236 | let chanIngrid = JSON.parse(JSON.stringify(Alice_chan)) 237 | chanIngrid.dbSalt = 'Ingrid-a' 238 | Ingrid_agreement = JSON.parse(JSON.stringify(Alice_agreement)) 239 | Ingrid_agreement.dbSalt = 'Ingrid-a' 240 | await lIngrid.gsc.joinChannel(chanIngrid, Ingrid_agreement, chanIngrid.stateRaw) 241 | 242 | let Ingrid_chan_1 = await lIngrid.gsc.getChannel('respekIngrid-a') 243 | //console.log(Ingrid_chan) 244 | let Ingrid_agreement_1 = await lIngrid.getGSCAgreement('battleHub1337Ingrid') 245 | //console.log(Bob_agreement) 246 | let IngridChanState_1 = await lIngrid.gsc.getStates('respekIngrid-a') 247 | //console.log(BobChanState) 248 | let IngridAgreementState_1 = await lIngrid.gsc.getStates('battleHub1337Ingrid') 249 | //console.log(BobAgreementState) 250 | 251 | let txs_agreement = await lAlice.gsc.getTransactions('battleHub1337Alice') 252 | let txs_channel = await lAlice.gsc.getTransactions('respekIngrid-a') 253 | //console.log(txs_agreement) 254 | //console.log(txs_channel) 255 | 256 | console.log('Ingrid sends join channel ack to Alice') 257 | 258 | await lAlice.gsc.updateAgreement(Ingrid_agreement_1) 259 | 260 | 261 | // Ingrid calls join channel on Bob------------- 262 | 263 | let chanIngrid_2 = JSON.parse(JSON.stringify(Bob_chan)) 264 | chanIngrid_2.dbSalt = 'Ingrid-b' 265 | let Ingrid_agreement_2 = JSON.parse(JSON.stringify(Bob_agreement)) 266 | Ingrid_agreement_2.dbSalt = 'Ingrid-b' 267 | await lIngrid.gsc.joinChannel(chanIngrid_2, Ingrid_agreement_2, chanIngrid_2.stateRaw) 268 | 269 | let Ingrid_chan_2 = await lIngrid.gsc.getChannel('respekIngrid-b') 270 | //console.log(Ingrid_chan) 271 | Ingrid_agreement_2 = await lIngrid.getGSCAgreement('battleHub420Ingrid') 272 | //console.log(Bob_agreement) 273 | let IngridChanState_2 = await lIngrid.gsc.getStates('respekIngrid-b') 274 | //console.log(BobChanState) 275 | let IngridAgreementState_2 = await lIngrid.gsc.getStates('battleHub420Ingrid') 276 | //console.log(BobAgreementState) 277 | 278 | let txs_agreement_2 = await lIngrid.gsc.getTransactions('battleHub420Ingrid') 279 | let txs_channel_2 = await lIngrid.gsc.getTransactions('respekIngrid-b') 280 | //console.log(txs_agreement) 281 | //console.log(txs_channel) 282 | 283 | console.log('Ingrid sends join channel ack to Bob') 284 | 285 | await lBob.gsc.updateAgreement(Ingrid_agreement_2) 286 | 287 | console.log('Virtual Channel ready for updates') 288 | 289 | console.log('Negotiate the random half before creating update state on VC') 290 | 291 | // // -------------------------------------------------- 292 | 293 | // TODO: Get Bob's random half and calculate the damage done 294 | 295 | let Alice_rands = new rand('test', 100) 296 | let Bob_rands = new rand('test2', 100) 297 | 298 | // Get user attack index 299 | let dmg = attackTable[2] 300 | 301 | let random = new mergeRand(Alice_rands.hashes[Alice_rands.hashes.length-1], Bob_rands.hashes[Bob_rands.hashes.length-1]) 302 | console.log('RANDOM NUMBER = '+ random.getCurrentRandom()) 303 | 304 | let dmgModifier = (1/100) * (random.getCurrentRandom()%100) 305 | console.log('DMG Modifier: '+dmgModifier) 306 | 307 | let newDmg = Math.floor(dmg-(dmg*dmgModifier)) 308 | console.log('New DMG: '+newDmg) 309 | 310 | let partyBHealth = 100 - newDmg 311 | 312 | //console.log(Alice_rands) 313 | //console.log(Bob_rands) 314 | 315 | let updateState = { 316 | isClose: 0, 317 | nonce: 1, 318 | dbSalt: 'Alice', // for testing multiple layer2 instances on same db 319 | agreementID: 'battleHub420', 320 | channelID: 'respek', 321 | type: 'battleEther', 322 | partyA: '0x1e8524370b7caf8dc62e3effbca04ccc8e493ffe', 323 | partyB: '0x4c88305c5f9e4feb390e6ba73aaef4c64284b7bc', 324 | balanceA: web3.utils.toWei('0.03', 'ether'), 325 | balanceB: web3.utils.toWei('0.05', 'ether'), 326 | hpA: 100, 327 | hpB: partyBHealth, 328 | attack: 2, 329 | ultimateNonceA: 1, 330 | ultimateNonceB: 0, 331 | turn: '0x1e8524370b7caf8dc62e3effbca04ccc8e493ffe', 332 | randomA: Alice_rands.hashes[Alice_rands.hashes.length-1], 333 | randomB: Bob_rands.hashes[Bob_rands.hashes.length-1] 334 | } 335 | 336 | // // Send VC update state 337 | 338 | await lAlice.gsc.initiateUpdateVCstate('respekAlice', updateState, false) 339 | 340 | let Alice_Virtuals = await lAlice.gsc.getVirtuals('respekAlice') 341 | console.log(Alice_Virtuals) 342 | 343 | let Alice_Vstate = await lAlice.gsc.getStates('respekAliceV') 344 | //console.log(Alice_Vstate) 345 | 346 | Alice_Virtuals.dbSalt = 'Bob' 347 | await lAlice.gsc.confirmVCUpdate(Alice_Virtuals, updateState) 348 | Alice_Virtuals.dbSalt = 'Alice' 349 | 350 | // Get user attack index 351 | dmg = attackTable[1] 352 | 353 | random = new mergeRand(Alice_rands.hashes[Alice_rands.hashes.length-2], Bob_rands.hashes[Bob_rands.hashes.length-2]) 354 | console.log('RANDOM NUMBER = '+ random.getCurrentRandom()) 355 | 356 | dmgModifier = (1/100) * (random.getCurrentRandom()%100) 357 | console.log('DMG Modifier: '+dmgModifier) 358 | 359 | newDmg = Math.floor(dmg-(dmg*dmgModifier)) 360 | console.log('New DMG: '+newDmg) 361 | 362 | let partyAHealth = 100 - newDmg 363 | 364 | let updateState2 = { 365 | isClose: 0, 366 | nonce: 2, 367 | dbSalt: 'Bob', // for testing multiple layer2 instances on same db 368 | agreementID: 'battleHub1337', 369 | channelID: 'respek', 370 | type: 'battleEther', 371 | partyA: '0x1e8524370b7caf8dc62e3effbca04ccc8e493ffe', 372 | partyB: '0x4c88305c5f9e4feb390e6ba73aaef4c64284b7bc', 373 | balanceA: web3.utils.toWei('0.03', 'ether'), 374 | balanceB: web3.utils.toWei('0.05', 'ether'), 375 | hpA: partyAHealth, 376 | hpB: partyBHealth, 377 | attack: 1, 378 | ultimateNonceA: 1, 379 | ultimateNonceB: 0, 380 | turn: '0x1e8524370b7caf8dc62e3effbca04ccc8e493ffe', 381 | randomA: Alice_rands.hashes[Alice_rands.hashes.length-2], 382 | randomB: Bob_rands.hashes[Bob_rands.hashes.length-2] 383 | } 384 | 385 | await lBob.gsc.initiateUpdateVCstate('respekBob', updateState2, false) 386 | 387 | let Bob_Virtuals = await lBob.gsc.getVirtuals('respekBob') 388 | console.log(Bob_Virtuals) 389 | 390 | // Generate a state that pushed HP of a party to 0 391 | // Signal this as a close state 392 | // Get both parties sigs 393 | // Send to Ingrid 394 | // Have Ingrid call update channel state on channelAlice with the adjusted wager winnings 395 | 396 | // assume Alice wins 397 | // Ingrid constructs update channel to adjust bobs balance ledger, this 398 | updateState = { 399 | isClose: 1, 400 | balanceA: web3.utils.toWei('0.08', 'ether'), 401 | balanceB: web3.utils.toWei('0', 'ether'), 402 | bond: web3.utils.toWei('0', 'ether') 403 | } 404 | Ingrid_agreement = await lIngrid.getGSCAgreement('battleHub420Ingrid') 405 | //console.log(Ingrid_agreement) 406 | await lIngrid.gsc.initiateUpdateChannelState('respekIngrid-b', updateState, false) 407 | 408 | Ingrid_chan_2 = await lIngrid.gsc.getChannel('respekIngrid-b') 409 | console.log(Ingrid_chan_2) 410 | 411 | // TODO fix db keys 412 | // build proper handling of updage state with close flag 413 | 414 | // Bob_chan = await lBob.gsc.getChannel('respekBob') 415 | // //console.log(Bob_chan) 416 | // Bob_agreement = await lBob.getGSCAgreement('spankHub1337Bob') 417 | // //console.log(Bob_agreement) 418 | // BobChanState = await lBob.gsc.getStates('respekBob') 419 | // //console.log(BobChanState) 420 | // BobAgreementState = await lBob.gsc.getStates('spankHub1337Bob') 421 | // //console.log(BobAgreementState) 422 | 423 | // console.log('Bob sends channel state update to Alice') 424 | 425 | // let chanAlice = JSON.parse(JSON.stringify(Bob_chan)) 426 | // let agreeAlice = JSON.parse(JSON.stringify(Bob_agreement)) 427 | // //console.log(agreeAlice) 428 | // chanAlice.dbSalt = 'Alice' 429 | // agreeAlice.dbSalt = 'Alice' 430 | 431 | // await lAlice.gsc.confirmUpdateChannelState(chanAlice, agreeAlice, updateState) 432 | 433 | // Alice_chan = await lAlice.gsc.getChannel('respekAlice') 434 | // //console.log(Alice_chan) 435 | // Alice_agreement = await lAlice.getGSCAgreement('spankHub1337Alice') 436 | // //console.log(Alice_agreement) 437 | // // console.log(Alice_agreement.stateSignatures) 438 | // AliceChanState = await lAlice.gsc.getStates('respekAlice') 439 | // //console.log(AliceChanState) 440 | // AliceAgreementState = await lAlice.gsc.getStates('spankHub1337Alice') 441 | // //console.log(AliceAgreementState) 442 | 443 | // console.log('Alice confirmed channel state update, sends ack to Bob') 444 | 445 | // Alice_agreement.dbSalt = 'Bob' 446 | // await lBob.gsc.updateAgreement(Alice_agreement) 447 | // Alice_agreement.dbSalt = 'Alice' 448 | 449 | // txs_channel = await lBob.gsc.getTransactions('respekBob') 450 | // txs_agreement = await lBob.gsc.getTransactions('spankHub1337Bob') 451 | // Alice_tx = await lAlice.gsc.getTransactions('spankHub1337Alice') 452 | // let Alice_tx_chan = await lAlice.gsc.getTransactions('respekAlice') 453 | // //console.log(txs_agreement) 454 | 455 | 456 | // // -------------------------------------------------- 457 | 458 | // // Send ether in channel and close channel 459 | 460 | // // Close Channel Consensus 461 | 462 | // // updateState = { 463 | // // isClose: 1, 464 | // // balanceA: web3.utils.toWei('0.07', 'ether'), 465 | // // balanceB: web3.utils.toWei('0.01', 'ether') 466 | // // } 467 | // // Bob_agreement = await lBob.getGSCAgreement('spankHub1337Bob') 468 | // // //console.log(Bob_agreement) 469 | // // await lBob.gsc.initiateUpdateChannelState('respekBob', updateState, false) 470 | // // Bob_chan = await lBob.gsc.getChannel('respekBob') 471 | // // Bob_agreement = await lBob.getGSCAgreement('spankHub1337Bob') 472 | // // //console.log(Bob_agreement) 473 | // // agreeAlice = JSON.parse(JSON.stringify(Bob_agreement)) 474 | // // chanAlice = JSON.parse(JSON.stringify(Bob_chan)) 475 | // // chanAlice.dbSalt = 'Alice' 476 | // // agreeAlice.dbSalt = 'Alice' 477 | 478 | // // await lAlice.gsc.confirmUpdateChannelState(chanAlice, agreeAlice, updateState) 479 | 480 | // // let allAgreements = await lAlice.gsc.getAllAgreements() 481 | // // //console.log(allAgreements) 482 | 483 | // // let allChannels = await lAlice.gsc.getAllChannels() 484 | // // //console.log(allChannels) 485 | 486 | // // let alltxs = await lAlice.gsc.getAllTransactions() 487 | // // //console.log(alltxs) 488 | 489 | // // let allRawStates = await lAlice.gsc.getAllRawStates() 490 | // // //console.log(allRawStates) 491 | 492 | // // Alice_agreement = await lAlice.getGSCAgreement('spankHub1337Alice') 493 | 494 | // // Alice_agreement.dbSalt = 'Bob' 495 | // // await lBob.gsc.updateAgreement(Alice_agreement) 496 | // // Alice_agreement.dbSalt = 'Alice' 497 | 498 | // // // Close agreement 499 | // // await lAlice.gsc.initiateCloseAgreement('spankHub1337Alice') 500 | 501 | // // Alice_chan = await lAlice.gsc.getChannel('respekAlice') 502 | // // //console.log(Alice_chan) 503 | // // Alice_agreement = await lAlice.getGSCAgreement('spankHub1337Alice') 504 | // // //console.log(Alice_agreement) 505 | // // //console.log(Alice_agreement.stateSignatures) 506 | // // AliceChanState = await lAlice.gsc.getStates('respekAlice') 507 | // // //console.log(AliceChanState) 508 | // // AliceAgreementState = await lAlice.gsc.getStates('spankHub1337Alice') 509 | // // //console.log(AliceAgreementState) 510 | 511 | // // Alice_agreement.dbSalt = 'Bob' 512 | // // await lBob.gsc.confirmCloseAgreement(Alice_agreement, AliceAgreementState) 513 | // // Bob_chan = await lBob.gsc.getChannel('respekBob') 514 | // // //console.log(Bob_chan) 515 | // // Bob_agreement = await lBob.getGSCAgreement('spankHub1337Bob') 516 | // // //console.log(Bob_agreement) 517 | // // BobChanState = await lBob.gsc.getStates('respekBob') 518 | // // //console.log(BobChanState) 519 | // // BobAgreementState = await lBob.gsc.getStates('spankHub1337Bob') 520 | // // //console.log(BobAgreementState) 521 | 522 | // // Bob_agreement = await lBob.getGSCAgreement('spankHub1337Bob') 523 | 524 | // // Bob_agreement.dbSalt = 'Alice' 525 | // // await lAlice.gsc.updateAgreement(Bob_agreement) 526 | // // Bob_agreement.dbSalt = 'Bob' 527 | 528 | // // // Note: Either party may call this now to move final state 529 | // // await lBob.gsc.finalizeAgreement('spankHub1337Bob') 530 | 531 | 532 | // // Close Channel Byzantine 533 | 534 | // await lBob.gsc.startSettleChannel('respekBob') 535 | 536 | // console.log('Settlement period started on channel, calling close after') 537 | 538 | // await lBob.gsc.closeByzantineChannel('respekBob') 539 | 540 | console.log('Agreement finalized, quiting...') 541 | } -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | const Buffer = require('buffer').Buffer 2 | const ethutil = require('ethereumjs-util') 3 | const TX = require('ethereumjs-tx') 4 | const BigNumber = require('bignumber.js') 5 | const crypto = require('crypto') 6 | 7 | module.exports = function(self) { 8 | return { 9 | latestTime: function latestTime() { 10 | return self.web3.eth.getBlock('latest').timestamp 11 | }, 12 | 13 | recoverSigner: function recoverSigner(state, sig) { 14 | const pubkey = ethutil.ecrecover(state, sig.v, sig.r, sig.s) 15 | const addrBuf = ethutil.pubToAddress(pubkey) 16 | return this.bufferToHex(addrBuf) 17 | }, 18 | 19 | signState: function signState(state) { 20 | // TODO: deal with the protocol this uses to hash and sign... 21 | return self.web3.eth.accounts.sign(state, self.privateKey) 22 | }, 23 | 24 | deployContract: async function deployContract(bytes, signer) { 25 | const gas = await self.web3.eth.getGasPrice() 26 | 27 | // TODO: get use public key of private key to generate account 28 | const nonce = await self.web3.eth.getTransactionCount(signer) 29 | 30 | const rawTx = { 31 | nonce: await self.web3.utils.toHex(nonce), 32 | gasPrice: await self.web3.utils.toHex(gas), 33 | gasLimit: await self.web3.utils.toHex(4612388), 34 | data: bytes, 35 | from: signer 36 | } 37 | 38 | const tx = new TX(rawTx, 3) 39 | tx.sign(this.hexToBuffer(self.privateKey)) 40 | const serialized = tx.serialize() 41 | 42 | //let txHash = await self.web3.eth.sendSignedTransaction(this.bufferToHex(serialized)) 43 | let txHash = '0xff0b70a7210b8c70a3d0dc9eb33144d308cce763fdcc10d4f836022f20e03d22' 44 | await this.waitForConfirm(txHash) 45 | let receipt = await self.web3.eth.getTransactionReceipt(txHash.transactionHash) 46 | let contract_address = receipt.contractAddress 47 | 48 | return contract_address 49 | //return '0x213c5c4a205fa2ca5833befd0fa34b2f5cb64c8f' 50 | }, 51 | 52 | // SET-PAYMENT HELPERS 53 | 54 | createLCStateUpdate: async function createLCStateUpdate(state) { 55 | // generate state update to sign 56 | const hash = self.web3.utils.soliditySha3( 57 | { type: 'bool', value: state.isClose }, // isclose 58 | //{ type: 'bytes32', value: web3.sha3('lc2', {encoding: 'hex'}) }, // lcid 59 | { type: 'uint256', value: state.nonce }, // sequence 60 | { type: 'uint256', value: state.numOpenVC }, // open VCs 61 | { type: 'bytes32', value: state.rootHash }, // VC root hash 62 | { type: 'address', value: state.partyA }, // partyA 63 | { type: 'address', value: state.partyI }, // hub 64 | { type: 'uint256', value: self.web3.utils.toWei(state.balanceA) }, 65 | { type: 'uint256', value: self.web3.utils.toWei(state.balanceI) } 66 | ) 67 | 68 | return hash 69 | }, 70 | 71 | createVCStateUpdate: async function createVCStateUpdate(state) { 72 | // generate state update to sign 73 | const hash = self.web3.utils.soliditySha3( 74 | //{ type: 'bytes32', value: state.channelId }, 75 | { type: 'uint256', value: state.nonce }, 76 | { type: 'address', value: state.partyA }, 77 | { type: 'address', value: state.partyB }, 78 | { type: 'uint256', value: state.hubBond }, 79 | { type: 'uint256', value: self.web3.utils.toWei(state.balanceA) }, 80 | { type: 'uint256', value: self.web3.utils.toWei(state.balanceB) } 81 | ) 82 | 83 | return hash 84 | }, 85 | 86 | testLC: async function testLC() { 87 | //console.log(contractABI) 88 | var newContract = new self.web3.eth.Contract(self.abi, self.ledgerAddress) 89 | //var contractInstance = newContract.at(address) 90 | //let c = new self.web3.eth.Contract(contractABI, address) 91 | let name = await newContract.methods.NAME().call() 92 | let ver = await newContract.methods.VERSION().call() 93 | let numChan = await newContract.methods.numChannels().call() 94 | console.log(name) 95 | console.log(ver) 96 | console.log(numChan) 97 | return 98 | }, 99 | 100 | createLCHandler: async function createLCHandler(state) { 101 | var lc = new self.web3.eth.Contract(self.abi, self.ledgerAddress) 102 | 103 | const callData = lc.methods.createChannel(state.id, state.partyI, '0').encodeABI() 104 | let gas = await self.web3.eth.getGasPrice() 105 | const nonce = await self.web3.eth.getTransactionCount(state.partyA) 106 | 107 | const rawTx = { 108 | nonce: await self.web3.utils.toHex(nonce), 109 | gasPrice: await self.web3.utils.toHex(gas), 110 | gasLimit: await self.web3.utils.toHex(250000), 111 | to: self.ledgerAddress, 112 | value: await self.web3.utils.numberToHex(self.web3.utils.toWei(state.balanceA)), 113 | data: callData, 114 | from: state.partyA 115 | } 116 | 117 | const tx = new TX(rawTx, 3) 118 | tx.sign(this.hexToBuffer(self.privateKey)) 119 | const serialized = tx.serialize() 120 | 121 | //let txHash = await self.web3.eth.sendSignedTransaction(this.bufferToHex(serialized)) 122 | let txHash = '0xba0c15d2c36a7f848a699b3a767abccee471de54ceb81d03bd3d7687c4c48141' 123 | await this.waitForConfirm({transactionHash:txHash}) 124 | //await this.waitForConfirm(txHash) 125 | return txHash 126 | }, 127 | 128 | joinLCHandler: async function createLCHandler(state) { 129 | var lc = new self.web3.eth.Contract(self.abi, self.ledgerAddress) 130 | 131 | const callData = lc.methods.joinChannel(state.id).encodeABI() 132 | let gas = await self.web3.eth.getGasPrice() 133 | const nonce = await self.web3.eth.getTransactionCount(state.partyI) 134 | 135 | const rawTx = { 136 | nonce: await self.web3.utils.toHex(nonce), 137 | gasPrice: await self.web3.utils.toHex(gas), 138 | gasLimit: await self.web3.utils.toHex(250000), 139 | to: self.ledgerAddress, 140 | value: await self.web3.utils.numberToHex(self.web3.utils.toWei(state.balanceI)), 141 | data: callData, 142 | from: state.partyI 143 | } 144 | 145 | const tx = new TX(rawTx, 3) 146 | tx.sign(this.hexToBuffer(self.privateKey)) 147 | const serialized = tx.serialize() 148 | 149 | //let txHash = await self.web3.eth.sendSignedTransaction(this.bufferToHex(serialized)) 150 | let txHash = '0xffaca6f4a3eec5b17a58cfdaac7676be46f8deef34ee6e6ad9a5550a3bf8685a' 151 | await this.waitForConfirm({transactionHash:txHash}) 152 | //await this.waitForConfirm(txHash) 153 | return txHash 154 | }, 155 | 156 | consensusCloseLCHandler: async function consensusCloseLCHandler(state) { 157 | // var lc = new self.web3.eth.Contract(self.abi, self.ledgerAddress) 158 | 159 | // const callData = lc.methods.joinChannel(state.id).encodeABI() 160 | // let gas = await self.web3.eth.getGasPrice() 161 | // const nonce = await self.web3.eth.getTransactionCount(state.partyI) 162 | 163 | // const rawTx = { 164 | // nonce: await self.web3.utils.toHex(nonce), 165 | // gasPrice: await self.web3.utils.toHex(gas), 166 | // gasLimit: await self.web3.utils.toHex(250000), 167 | // to: self.ledgerAddress, 168 | // value: await self.web3.utils.numberToHex(self.web3.utils.toWei(state.balanceI)), 169 | // data: callData, 170 | // from: state.partyI 171 | // } 172 | 173 | // const tx = new TX(rawTx, 3) 174 | // tx.sign(this.hexToBuffer(self.privateKey)) 175 | // const serialized = tx.serialize() 176 | 177 | // //let txHash = await self.web3.eth.sendSignedTransaction(this.bufferToHex(serialized)) 178 | // let txHash = '0xffaca6f4a3eec5b17a58cfdaac7676be46f8deef34ee6e6ad9a5550a3bf8685a' 179 | // await this.waitForConfirm({transactionHash:txHash}) 180 | // //await this.waitForConfirm(txHash) 181 | // return txHash 182 | }, 183 | 184 | 185 | // OLD GSC HELPERS 186 | 187 | // TODO: combine open and join agreement function to executeAgreement. 188 | // this will just require swaping the method sig 189 | executeOpenAgreement: async function executeOpenAgreement( 190 | contractABI, 191 | address, 192 | state, 193 | extension, 194 | sig, 195 | balA, 196 | signer 197 | ) { 198 | 199 | // TODO: Replace .getData with our serialization of the call inputs, just get the 4 byte method sig and serialize() 200 | let c = new self.web3.eth.Contract(contractABI, address) 201 | let r = sig[0] 202 | let s = sig[1] 203 | let v = sig[2] 204 | let callData = c.methods.openAgreement(state, extension, v, r, s).encodeABI() 205 | 206 | let gas = await self.web3.eth.getGasPrice() 207 | //gas+=2000000000 208 | 209 | // TODO: get use public key of private key to generate account 210 | const nonce = await self.web3.eth.getTransactionCount(signer) 211 | 212 | const rawTx = { 213 | nonce: await self.web3.utils.toHex(nonce), 214 | gasPrice: await self.web3.utils.toHex(gas), 215 | gasLimit: await self.web3.utils.toHex(250000), 216 | to: address, 217 | value: await self.web3.utils.toHex(balA), 218 | data: callData, 219 | from: signer 220 | } 221 | 222 | const tx = new TX(rawTx, 3) 223 | tx.sign(this.hexToBuffer(self.privateKey)) 224 | const serialized = tx.serialize() 225 | 226 | //let txHash = await self.web3.eth.sendSignedTransaction(this.bufferToHex(serialized)) 227 | let txHash = '0xff0b70a7210b8c70a3d0dc9eb33144d308cce763fdcc10d4f836022f20e03d22' 228 | await this.waitForConfirm(txHash) 229 | return txHash 230 | }, 231 | 232 | executeJoinAgreement: async function executeJoinAgreement( 233 | contractABI, 234 | address, 235 | state, 236 | extension, 237 | sig, 238 | balB, 239 | signer 240 | ) { 241 | // TODO: Replace .getData with our serialization of the call inputs, just get the 4 byte method sig and serialize() 242 | let c = new self.web3.eth.Contract(contractABI, address) 243 | let r = sig[0] 244 | let s = sig[1] 245 | let v = sig[2] 246 | let callData = c.methods.joinAgreement(state, extension, v, r, s).encodeABI() 247 | 248 | let gas = await self.web3.eth.getGasPrice() 249 | 250 | const nonce = await self.web3.eth.getTransactionCount(signer) 251 | 252 | //gas+=2000000000 253 | 254 | const rawTx = { 255 | nonce: await self.web3.utils.toHex(nonce), 256 | gasPrice: await self.web3.utils.toHex(gas), 257 | gasLimit: await self.web3.utils.toHex(250000), 258 | to: address, 259 | value: await self.web3.utils.toHex(balB), 260 | data: callData, 261 | from: signer 262 | } 263 | 264 | const tx = new TX(rawTx, 3) 265 | tx.sign(this.hexToBuffer(self.privateKey)) 266 | const serialized = tx.serialize() 267 | 268 | //let txHash = await self.web3.eth.sendSignedTransaction(this.bufferToHex(serialized)) 269 | let txHash = '0x8d470165aa3cee1f5d6e927b90d20a59a14318964fa67846a230535443b83f07' 270 | await this.waitForConfirm(txHash) 271 | return txHash 272 | }, 273 | 274 | executeCloseAgreement: async function executeCloseAgreement( 275 | contractABI, 276 | address, 277 | state, 278 | sigs, 279 | signer 280 | ) { 281 | // TODO: Replace .getData with our serialization of the call inputs, just get the 4 byte method sig and serialize() 282 | let c = new self.web3.eth.Contract(contractABI, address) 283 | 284 | let r = sigs[0][0] 285 | let s = sigs[0][1] 286 | let v = sigs[0][2] 287 | let r2 = sigs[1][0] 288 | let s2 = sigs[1][1] 289 | let v2 = sigs[1][2] 290 | 291 | let sigV = [] 292 | let sigR = [] 293 | let sigS = [] 294 | 295 | sigV.push(v) 296 | sigV.push(v2) 297 | sigR.push(r) 298 | sigR.push(r2) 299 | sigS.push(s) 300 | sigS.push(s2) 301 | 302 | let callData = c.methods.closeAgreement(state, sigV, sigR, sigS).encodeABI() 303 | 304 | 305 | let gas = await self.web3.eth.getGasPrice() 306 | 307 | const nonce = await self.web3.eth.getTransactionCount(signer) 308 | 309 | //gas+=2000000000 310 | 311 | const rawTx = { 312 | nonce: await self.web3.utils.toHex(nonce), 313 | gasPrice: await self.web3.utils.toHex(gas), 314 | gasLimit: await self.web3.utils.toHex(250000), 315 | to: address, 316 | data: callData, 317 | from: signer 318 | } 319 | 320 | 321 | const tx = new TX(rawTx, 3) 322 | tx.sign(this.hexToBuffer(self.privateKey)) 323 | const serialized = tx.serialize() 324 | 325 | //let txHash = await self.web3.eth.sendSignedTransaction(this.bufferToHex(serialized)) 326 | let txHash = '0x8d470165aa3cee1f5d6e927b90d20a59a14318964fa67846a230535443b83f07' 327 | await this.waitForConfirm(txHash) 328 | return txHash 329 | }, 330 | 331 | executeDeployCTF: async function executeDeployCTF( 332 | contractABI, // registry abi 333 | address, // registry address 334 | state, 335 | sigs, 336 | signer, 337 | metaCTF 338 | ) { 339 | let c = new self.web3.eth.Contract(contractABI, address) 340 | 341 | let r = sigs[0][0] 342 | let s = sigs[0][1] 343 | let v = sigs[0][2] 344 | let r2 = sigs[1][0] 345 | let s2 = sigs[1][1] 346 | let v2 = sigs[1][2] 347 | 348 | let sigV = [] 349 | let sigR = [] 350 | let sigS = [] 351 | 352 | sigV.push(v) 353 | sigV.push(v2) 354 | sigR.push(r) 355 | sigR.push(r2) 356 | sigS.push(s) 357 | sigS.push(s2) 358 | 359 | let callData = c.methods.deployCTF(state, sigV, sigR, sigS).encodeABI() 360 | 361 | 362 | let gas = await self.web3.eth.getGasPrice() 363 | 364 | const nonce = await self.web3.eth.getTransactionCount(signer) 365 | 366 | //gas+=2000000000 367 | 368 | const rawTx = { 369 | nonce: await self.web3.utils.toHex(nonce), 370 | gasPrice: await self.web3.utils.toHex(gas), 371 | gasLimit: await self.web3.utils.toHex(6500000), 372 | to: address, 373 | data: callData, 374 | from: signer 375 | } 376 | 377 | 378 | const tx = new TX(rawTx, 3) 379 | tx.sign(this.hexToBuffer(self.privateKey)) 380 | const serialized = tx.serialize() 381 | 382 | //let txHash = await self.web3.eth.sendSignedTransaction(this.bufferToHex(serialized)) 383 | let txHash = '0x0' 384 | await this.waitForConfirm(txHash) 385 | let metaDeployed = await c.methods.resolveAddress(metaCTF).call() 386 | //let metaDeployed = '0x69d374647049341aa74f2216434fe2d0715546b4' 387 | console.log(metaDeployed) 388 | return metaDeployed 389 | }, 390 | 391 | executeSettleChannel: async function executeSettleChannel( 392 | contractABI, // metachan abi 393 | address, // metachan address 394 | chanState, 395 | agreeState, 396 | sigs, 397 | signer, 398 | channels 399 | ) { 400 | let c = new self.web3.eth.Contract(contractABI, address) 401 | 402 | let elems = [] 403 | for(var i=0; i setTimeout(resolve, ms)) 544 | }, 545 | 546 | getBytes: function getBytes(input) { 547 | if(Buffer.isBuffer(input)) input = '0x' + input.toString('hex') 548 | if(66-input.length <= 0) return self.web3.utils.toHex(input) 549 | return this.padBytes32(self.web3.utils.toHex(input)) 550 | }, 551 | 552 | sha3: function sha3(input) { 553 | return self.web3.utils.sha3(input, {encoding: 'hex'}) 554 | }, 555 | 556 | sign: function sign(message, key) { 557 | // TODO, web3 1.0.0 has a method for this but 558 | // is not stable yet 559 | let msg = this.hexToBuffer(message) 560 | let msgHash = ethutil.hashPersonalMessage(msg) 561 | return ethutil.ecsign(msgHash, this.hexToBuffer(key)) 562 | }, 563 | 564 | importPublic: function importPublic(key) { 565 | // TODO, web3 1.0.0 has a method for this but 566 | // is not stable yet 567 | return ethutil.importPublic(this.hexToBuffer(key)) 568 | }, 569 | 570 | ecrecover: function ecrecover(message, v, r, s) { 571 | // TODO, web3 1.0.0 has a method for this but 572 | // is not stable yet 573 | return ethutil.ecrecover(this.hexToBuffer(message), v, r, s) 574 | }, 575 | 576 | privateToPublic: function privateToPublic(key) { 577 | // TODO, web3 1.0.0 has a method for this but 578 | // is not stable yet 579 | return ethutil.privateToPublic(this.hexToBuffer(key)) 580 | }, 581 | 582 | pubToAddress: function pubToAddress(key) { 583 | // TODO, web3 1.0.0 has a method for this but 584 | // is not stable yet 585 | return ethutil.pubToAddress(this.hexToBuffer(key)) 586 | }, 587 | 588 | serializeState: function serializeState(inputs) { 589 | var m = this.getBytes(inputs[0]) 590 | 591 | for(var i=1; i 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | -------------------------------------------------------------------------------- /src/general-state-channel.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const metachannel = require('../contracts/MetaChannel.json') 4 | const msig = require('../contracts/MultiSig.json') 5 | const reg = require('../contracts/CTFRegistry.json') 6 | const BigNumber = require('bignumber.js') 7 | 8 | module.exports = function gsc (self) { 9 | return { 10 | init: async function(options) { 11 | // TODO: Check against counterfactual registry and see if any 12 | // of the channels are being challenged when online 13 | self.etherExtension = '0x32c1d681fe917170573aed0671d21317f14219fd' 14 | self.bidirectEtherInterpreter = '0x74926af30d35337e45225666bbf49e156fd08016' 15 | self.battleEtherInterpreter = '0x0' 16 | self.registryAddress = '0x72be812074e5618786f1953662b8af1ec344231c' 17 | }, 18 | 19 | createAgreement: async function(agreement) { 20 | let entryID = agreement.ID 21 | let agreements = await self.storage.get('agreements') || {} 22 | if(!agreements.hasOwnProperty(entryID)) agreements[entryID] = {} 23 | 24 | // OVERWRITE OLD DATABASE TX ENTRIES IF THEY EXIST FOR THIS AGREEMENT 25 | let txs = await self.storage.get('transactions') || {} 26 | if(!txs.hasOwnProperty(entryID)) txs[entryID] = [] 27 | 28 | let rawStates = await self.storage.get('states') || {} 29 | if(!rawStates.hasOwnProperty(entryID)) rawStates[entryID] = [] 30 | 31 | agreement.openPending = true 32 | agreement.closed = false 33 | agreement.inDispute = false 34 | agreement.metaSignatures = [] 35 | agreement.channels = [] 36 | agreement.channelRootHash = '0x0' 37 | 38 | const metaByteCode = metachannel.bytecode 39 | 40 | let args = ['test42042', agreement.partyA, agreement.partyB] 41 | let signers = [agreement.partyA, agreement.partyB] 42 | let metaCTFbytes = self.utils.getCTFstate(metaByteCode, signers, args) 43 | let metachannelCTFaddress = self.utils.getCTFaddress(metaCTFbytes) 44 | //let metachannelCTFaddress = '1337' 45 | 46 | rawStates[metachannelCTFaddress] = metaCTFbytes 47 | 48 | agreement.metachannelCTFaddress = metachannelCTFaddress 49 | agreement.metachannelCTFbytes = metaCTFbytes 50 | let metaSig = self.utils.sign(agreement.metachannelCTFaddress, self.privateKey) 51 | let mr = self.utils.bufferToHex(metaSig.r) 52 | let ms = self.utils.bufferToHex(metaSig.s) 53 | let mv = metaSig.v 54 | let msigs = [[mr,ms,mv]] 55 | 56 | agreement.metaSignatures = msigs 57 | 58 | 59 | let initialState = [] 60 | initialState.push(0) // is close 61 | initialState.push(0) // sequence 62 | initialState.push(agreement.partyA) // partyA address 63 | initialState.push(agreement.partyB) // partyB address 64 | initialState.push(metachannelCTFaddress) // counterfactual metachannel address 65 | initialState.push('0x0') // sub-channel root hash 66 | initialState.push(agreement.balanceA) // balance in ether partyA 67 | initialState.push(agreement.balanceB) // balance in ether partyB 68 | 69 | rawStates[entryID].push(initialState) 70 | 71 | agreement.stateSerialized = self.utils.serializeState(initialState) 72 | 73 | let stateHash = self.web3.utils.sha3(agreement.stateSerialized, {encoding: 'hex'}) 74 | agreement.stateSignatures = [] 75 | let state0sig = self.utils.sign(stateHash, self.privateKey) 76 | 77 | let r = self.utils.bufferToHex(state0sig.r) 78 | let s = self.utils.bufferToHex(state0sig.s) 79 | let v = state0sig.v 80 | let sigs = [[r,s,v]] 81 | 82 | agreement.stateSignatures = [] 83 | agreement.stateSignatures[0] = sigs 84 | 85 | 86 | // TODO deploy and call openAgreement on msig wallet 87 | // save msig deploy address to agreement object 88 | const msigBytecode = msig.bytecode 89 | let msigArgs = [msigBytecode, metachannelCTFaddress, self.registryAddress] 90 | let msigDeployBytes = self.utils.serializeState(msigArgs) 91 | let msigAddress = await self.utils.deployContract(msigDeployBytes, agreement.partyA) 92 | //let msigAddress = msig_tx_hash 93 | agreement.address = msigAddress 94 | 95 | // TODO: call deployed msig 96 | let openTxHash = await self.utils.executeOpenAgreement( 97 | msig.abi, 98 | msigAddress, 99 | agreement.stateSerialized, 100 | self.etherExtension, 101 | sigs[0], 102 | agreement.balanceA, 103 | agreement.partyA 104 | ) 105 | 106 | 107 | let tx = { 108 | agreement: agreement.ID, 109 | channel: 'master', 110 | nonce: 0, 111 | timestamp: Date.now(), 112 | data: 'Open Agreement', 113 | txHash: openTxHash 114 | } 115 | txs[entryID].push(tx) 116 | 117 | 118 | self.publicKey = self.utils.bufferToHex(self.utils.ecrecover(stateHash, state0sig.v, state0sig.r, state0sig.s)) 119 | 120 | Object.assign(agreements[entryID], agreement) 121 | // Object.assign(txs[entryID], txList) 122 | // Object.assign(rawStates[entryID], rawStatesList) 123 | 124 | await self.storage.set('agreements', agreements) 125 | await self.storage.set('transactions', txs) 126 | await self.storage.set('states', rawStates) 127 | console.log('Agreement and tx stored in db, deploying contract') 128 | }, 129 | 130 | // TODO: Replace agreement with just the state sig from counterparty 131 | joinAgreement: async function(agreement, state) { 132 | let entryID = agreement.ID 133 | let agreements = await self.storage.get('agreements') || {} 134 | if(!agreements.hasOwnProperty(entryID)) agreements[entryID] = {} 135 | 136 | let txs = await self.storage.get('transactions') || {} 137 | if(!txs.hasOwnProperty(entryID)) txs[entryID] = [] 138 | 139 | let rawStates = await self.storage.get('states') || {} 140 | if(!rawStates.hasOwnProperty(entryID)) rawStates[entryID] = [] 141 | 142 | rawStates[entryID].push(state) 143 | rawStates[agreement.metachannelCTFaddress] = agreement.metachannelCTFbytes 144 | 145 | let stateHash = self.web3.utils.sha3(agreement.stateSerialized, {encoding: 'hex'}) 146 | let sig = self.utils.sign(stateHash, self.privateKey) 147 | let r = self.utils.bufferToHex(sig.r) 148 | let s = self.utils.bufferToHex(sig.s) 149 | let v = sig.v 150 | let sigs = [r,s,v] 151 | 152 | let metaSig = self.utils.sign(agreement.metachannelCTFaddress, self.privateKey) 153 | let mr = self.utils.bufferToHex(metaSig.r) 154 | let ms = self.utils.bufferToHex(metaSig.s) 155 | let mv = metaSig.v 156 | let msigs = [mr,ms,mv] 157 | 158 | agreement.metaSignatures.push(msigs) 159 | agreement.stateSignatures[0].push(sigs) 160 | agreement.openPending = false; 161 | 162 | self.publicKey = self.utils.bufferToHex( 163 | self.utils.ecrecover( 164 | stateHash, 165 | sig.v, 166 | sig.r, 167 | sig.s 168 | ) 169 | ) 170 | 171 | let joinTxHash = await self.utils.executeJoinAgreement( 172 | msig.abi, 173 | agreement.address, 174 | agreement.stateSerialized, 175 | self.etherExtension, 176 | agreement.stateSignatures[0][1], 177 | agreement.balanceB, 178 | agreement.partyB 179 | ) 180 | 181 | let tx = { 182 | agreement: agreement.ID, 183 | channel: 'master', 184 | nonce: 0, 185 | timestamp: Date.now(), 186 | data: 'Join Agreement', 187 | txHash: joinTxHash 188 | } 189 | txs[entryID].push(tx) 190 | 191 | Object.assign(agreements[entryID], agreement) 192 | // Object.assign(txs[entryID], txList) 193 | // Object.assign(rawStates[entryID], rawStatesList) 194 | 195 | await self.storage.set('agreements', agreements) 196 | await self.storage.set('transactions', txs) 197 | await self.storage.set('states', rawStates) 198 | 199 | console.log('Agreement stored in db, responding to deployed contract') 200 | }, 201 | 202 | updateAgreement: async function(agreement) { 203 | let entryID = agreement.ID 204 | let agreements = await self.storage.get('agreements') || {} 205 | if(!agreements.hasOwnProperty(entryID)) agreements[entryID] = {} 206 | 207 | Object.assign(agreements[entryID], agreement) 208 | 209 | await self.storage.set('agreements', agreements) 210 | 211 | console.log('Agreement Sigs updated in db') 212 | }, 213 | 214 | updateChannel: async function(channel) { 215 | let entryID = channel.ID 216 | let channels = await self.storage.get('channels') || {} 217 | if(!channels.hasOwnProperty(entryID)) channels[entryID] = {} 218 | 219 | Object.assign(channels[entryID], channel) 220 | 221 | await self.storage.set('channels', channels) 222 | 223 | console.log('Channel Sigs updated in db') 224 | }, 225 | 226 | updateChannelSigs: async function(channel) { 227 | let entryID = channel.ID 228 | let channels = await self.storage.get('channels') || {} 229 | if(!channels.hasOwnProperty(entryID)) return 230 | 231 | Object.assign(channels[entryID], channel) 232 | 233 | await self.storage.set('channels', channel) 234 | 235 | console.log('Channel Sigs updated in db') 236 | }, 237 | 238 | initiateCloseAgreement: async function(agreementID) { 239 | let agreements = await self.storage.get('agreements') || {} 240 | if(!agreements.hasOwnProperty(agreementID)) return 241 | 242 | let txs = await self.storage.get('transactions') || {} 243 | if(!txs.hasOwnProperty(agreementID)) return 244 | 245 | let rawStates = await self.storage.get('states') || {} 246 | if(!rawStates.hasOwnProperty(agreementID)) return 247 | 248 | let agreement = agreements[agreementID] 249 | agreement.closed = true 250 | const stateLength = rawStates[agreementID].length-1 251 | 252 | let oldState = JSON.parse(JSON.stringify(rawStates[agreementID][stateLength])) 253 | 254 | oldState[0] = 1 255 | oldState[1] = oldState[1] + 1 256 | 257 | rawStates[agreementID].push(oldState) 258 | agreement.stateSerialized = self.utils.serializeState(oldState) 259 | 260 | let stateHash = self.web3.utils.sha3(agreement.stateSerialized, {encoding: 'hex'}) 261 | 262 | let sig = self.utils.sign(stateHash, self.privateKey) 263 | let r = self.utils.bufferToHex(sig.r) 264 | let s = self.utils.bufferToHex(sig.s) 265 | let v = sig.v 266 | let sigs = [[r,s,v]] 267 | 268 | agreement.stateSignatures[agreement.stateSignatures.length] = sigs 269 | 270 | let tx = { 271 | agreement: agreement.ID, 272 | channel: 'master', 273 | nonce: oldState[1], 274 | timestamp: Date.now(), 275 | data: 'close Agreement', 276 | txHash: '0x0' 277 | } 278 | txs[agreementID].push(tx) 279 | 280 | Object.assign(agreements[agreementID], agreement) 281 | // Object.assign(txs[entryID], txList) 282 | // Object.assign(rawStates[entryID], rawStatesList) 283 | 284 | await self.storage.set('agreements', agreements) 285 | await self.storage.set('transactions', txs) 286 | await self.storage.set('states', rawStates) 287 | }, 288 | 289 | confirmCloseAgreement: async function(agreement, state) { 290 | let agreementID = agreement.ID 291 | let agreements = await self.storage.get('agreements') || {} 292 | if(!agreements.hasOwnProperty(agreementID)) return 293 | 294 | let txs = await self.storage.get('transactions') || {} 295 | if(!txs.hasOwnProperty(agreementID)) return 296 | 297 | let rawStates = await self.storage.get('states') || {} 298 | if(!rawStates.hasOwnProperty(agreementID)) return 299 | 300 | rawStates[agreementID].push(state[state.length-1]) 301 | 302 | let stateHash = self.web3.utils.sha3(agreement.stateSerialized, {encoding: 'hex'}) 303 | let sig = self.utils.sign(stateHash, self.privateKey) 304 | let r = self.utils.bufferToHex(sig.r) 305 | let s = self.utils.bufferToHex(sig.s) 306 | let v = sig.v 307 | let sigs = [r,s,v] 308 | 309 | agreement.stateSignatures[agreement.stateSignatures.length-1].push(sigs) 310 | 311 | let tx = { 312 | agreement: agreement.ID, 313 | channel: 'master', 314 | nonce: '99', 315 | timestamp: Date.now(), 316 | data: 'close Agreement', 317 | txHash: '0x0' 318 | } 319 | txs[agreementID].push(tx) 320 | 321 | Object.assign(agreements[agreementID], agreement) 322 | // Object.assign(txs[entryID], txList) 323 | // Object.assign(rawStates[entryID], rawStatesList) 324 | 325 | await self.storage.set('agreements', agreements) 326 | await self.storage.set('transactions', txs) 327 | await self.storage.set('states', rawStates) 328 | }, 329 | 330 | finalizeAgreement: async function(agreementID) { 331 | let agreements = await self.storage.get('agreements') || {} 332 | if(!agreements.hasOwnProperty(agreementID)) return 333 | 334 | let agreement = agreements[agreementID] 335 | 336 | let finalTxHash = await self.utils.executeCloseAgreement( 337 | msig.abi, 338 | agreement.address, 339 | agreement.stateSerialized, 340 | agreement.stateSignatures[agreement.stateSignatures.length-1], 341 | agreement.partyB // TODO: dont assume which party is calling this, it may be neither 342 | ) 343 | 344 | 345 | }, 346 | 347 | startSettleAgreement: async function(agreementID) { 348 | // Require that there are no open channels! 349 | 350 | // TODO: instantiate metachannel, call startSettle 351 | }, 352 | 353 | challengeAgreement: async function(agreementID) { 354 | // TODO: call challengeSettle on metachannel 355 | }, 356 | 357 | closeByzantineAgreement: async function(agreementID) { 358 | // TODO: call msig closeWithMetachannel 359 | 360 | }, 361 | 362 | isAgreementOpen: async function(agreementID) { 363 | let agreements = await self.storage.get('agreements') || {} 364 | if(!agreements.hasOwnProperty(agreementID)) return false 365 | let agreement = agreements[agreementID] 366 | if(agreement.openPending == true) return false 367 | if(agreement.stateSignatures[0].length != 2) return false 368 | 369 | // TODO: Check blockchain for confirmed open status 370 | return true 371 | 372 | }, 373 | 374 | // channel functions 375 | 376 | // When Ingrid receives both agreements 377 | hubConfirmVC: async function(channelA, channelB, agreementA, agreementB, channelState) { 378 | 379 | }, 380 | 381 | // IF battle eth channel 382 | // Alice creates channel agreement for Bob with Ingrid 383 | // Bob confirms and creates agreement for Alice with Ingrid 384 | openChannel: async function(channel) { 385 | let AgreeEntryID = channel.agreementID 386 | let agreements = await self.storage.get('agreements') || {} 387 | if(!agreements.hasOwnProperty(AgreeEntryID)) return 388 | let agreement = agreements[AgreeEntryID] 389 | 390 | let ChanEntryID = channel.ID 391 | let channels = await self.storage.get('channels') || {} 392 | if(!channels.hasOwnProperty(ChanEntryID)) channels[ChanEntryID] = {} 393 | 394 | let rawStates = await self.storage.get('states') || {} 395 | if(!rawStates.hasOwnProperty(ChanEntryID)) rawStates[ChanEntryID] = [] 396 | if(!rawStates.hasOwnProperty(AgreeEntryID)) return 397 | 398 | let txs = await self.storage.get('transactions') || {} 399 | if(!txs.hasOwnProperty(ChanEntryID)) txs[ChanEntryID] = [] 400 | 401 | 402 | channel.openPending = true 403 | channel.inDispute = false 404 | 405 | var channelInputs = [] 406 | 407 | if(channel.type == 'ether') { 408 | channelInputs.push(0) // is close 409 | channelInputs.push(0) // is force push channel 410 | channelInputs.push(0) // channel sequence 411 | channelInputs.push(0) // timeout length ms 412 | channelInputs.push(self.bidirectEtherInterpreter) // ether payment interpreter library address 413 | channelInputs.push(channel.ID) // ID of channel 414 | channelInputs.push(agreement.metachannelCTFaddress) // counterfactual metachannel address 415 | channelInputs.push(self.registryAddress) // CTF registry address 416 | channelInputs.push('0x0') // channel tx roothash 417 | channelInputs.push(agreement.partyA) // partyA in the channel 418 | channelInputs.push(agreement.partyB) // partyB in the channel 419 | channelInputs.push(channel.balanceA) // balance of party A in channel (ether) 420 | channelInputs.push(channel.balanceB) // balance of party B in channel (ether) 421 | } 422 | 423 | if(channel.type == 'battleEther') { 424 | channelInputs.push(0) // is close 425 | channelInputs.push(1) // is force push channel 426 | channelInputs.push(0) // channel sequence 427 | channelInputs.push(0) // timeout length ms 428 | channelInputs.push(self.battleEtherInterpreter) // ether payment interpreter library address 429 | channelInputs.push(channel.ID) // ID of channel 430 | channelInputs.push(agreement.metachannelCTFaddress) // counterfactual metachannel address 431 | channelInputs.push(self.registryAddress) // CTF registry address 432 | channelInputs.push('0x0') // channel tx roothash 433 | channelInputs.push(agreement.partyA) // partyA in the channel 434 | channelInputs.push(channel.counterparty) // partyB in the channel 435 | channelInputs.push(agreement.partyB) // partyI in the channel 436 | 437 | channelInputs.push(channel.balanceA) // balance of party A in channel (ether) 438 | channelInputs.push(channel.balanceB) // balance of party B in channel (ether) 439 | channelInputs.push(channel.bond) // how much of a bond does ingrid put in 440 | } 441 | 442 | rawStates[ChanEntryID].push(channelInputs) 443 | channel.stateSerialized = self.utils.serializeState(channelInputs) 444 | channel.stateRaw = channelInputs 445 | 446 | // calculate channel root hash 447 | let elem = self.utils.sha3(channel.stateSerialized, {encoding: 'hex'}) 448 | 449 | let elems = [] 450 | agreement.channels = agreement.channels || []; 451 | for(var i=0; i