Skip to content

Commit

Permalink
feat(exit): emit signed exit transfer tx details before signing an ex…
Browse files Browse the repository at this point in the history
…it message (#164)

* feat(exit): emit signed exit transfer tx details before signing the exit message.

This way the client has a chance to store the transfer tx, to resume exit process later e.g.
if the user rejected exit signature.

* correct PromiEvent typedef import
  • Loading branch information
troggy authored Dec 16, 2019
1 parent eec71e1 commit d08f4f5
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 43 deletions.
27 changes: 21 additions & 6 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ declare module "leap-core" {
import Web3 from 'web3';
import { Callback } from 'web3/types';
import { Transaction } from 'web3/eth/types';
import PromiEvent from 'web3/promiEvent';
import { BigIntType } from 'jsbi-utils';

export enum Type {
Expand Down Expand Up @@ -317,6 +318,13 @@ declare module "leap-core" {
}>;
}

export type FastSellRequest = {
tx: Transaction;
sigHashBuff: Buffer;
effectiveBlock: number;
signedData?: Array<string>;
};

type SpendCondSimResult = {
error?: string;
outputs: Output[];
Expand Down Expand Up @@ -369,17 +377,24 @@ declare module "leap-core" {
amount: BigIntType | number,
color: number,
plasmaChain: ExtendedWeb3,
rootChain: ExtendedWeb3,
rootChain: Web3,
marketMakerUrl: string,
signer: Signer,
): Promise<any>;
signer?: Signer,
): PromiEvent<any>;

static fastSellUTXO(
utxo: Unspent,
plasmaChain: ExtendedWeb3,
rootChain: ExtendedWeb3,
rootChain: Web3,
marketMakerUrl: string,
signer?: Signer,
): PromiEvent<any>;

static signAndSendFastSellRequest(
fastSellRequest: FastSellRequest,
rootChain: Web3,
marketMakerUrl: string,
signer: Signer,
): Promise<any>;
signer?: Signer,
): PromiEvent<any>;
}
}
86 changes: 50 additions & 36 deletions lib/exit.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import util from 'ethereumjs-util';
import { bi } from 'jsbi-utils';
import fetch from 'node-fetch';
import Web3PromiEvent from 'web3-core-promievent';

import Tx from './transaction';
import Outpoint from './outpoint';
import Output from './output';
import Input from './input';
import { periodBlockRange, sendSignedTransaction } from './helpers';
import Period from './period';
import { sendSignedTransaction } from './helpers';


export default class Exit {
Expand Down Expand Up @@ -79,58 +81,70 @@ export default class Exit {
}

static fastSellUTXO(utxo, plasmaChain, rootChain, marketMakerUrl, signer) {
return plasmaChain.getConfig().then(nodeConfig => {
const promiEvent = Web3PromiEvent();
plasmaChain.getConfig().then(nodeConfig => {
const inputs = [new Input(utxo.outpoint)];
const outputs = [
new Output(utxo.output.value, nodeConfig.exitHandlerAddr, utxo.output.color)
];
const exitingUtxoTransfer = Tx.transfer(inputs, outputs);
return Exit.fastSell(exitingUtxoTransfer, plasmaChain, rootChain, marketMakerUrl, signer);
})
return Exit.fastSell(exitingUtxoTransfer, plasmaChain, rootChain, marketMakerUrl, promiEvent, signer);
});
return promiEvent.eventEmitter;
}

static fastSellAmount(account, amount, color, plasmaChain, rootChain, marketMakerUrl, signer) {
return Promise.all([plasmaChain.getUnspent(account, color), plasmaChain.getConfig()]).then(([unspent, nodeConfig]) => {
const promiEvent = Web3PromiEvent();
Promise.all([plasmaChain.getUnspent(account, color), plasmaChain.getConfig()]).then(([unspent, nodeConfig]) => {
const exitingUtxoTransfer = Tx.transferFromUtxos(
unspent, account, nodeConfig.exitHandlerAddr, amount, color,
);
return Exit.fastSell(exitingUtxoTransfer, plasmaChain, rootChain, marketMakerUrl, signer);
return Exit.fastSell(exitingUtxoTransfer, plasmaChain, rootChain, marketMakerUrl, promiEvent, signer);
});
return promiEvent.eventEmitter;
}

static fastSell(spendTx, plasmaChain, rootChain, marketMakerUrl, signer) {
static fastSell(spendTx, plasmaChain, rootChain, marketMakerUrl, _promiEvent, signer) {
const amount = bi(spendTx.outputs[0].value);
const fastSellRequest = {};
const promiEvent = _promiEvent || Web3PromiEvent();
(signer
? signer.signTx(spendTx)
: spendTx.signWeb3(rootChain)
).then(signedTx => sendSignedTransaction(plasmaChain, signedTx.hex())
).then(transferTx => {
const tx = Tx.fromRaw(transferTx.raw);
const utxoId = (new Outpoint(tx.hash(), 0)).getUtxoId();
const sigHashBuff = Exit.sigHashBuff(utxoId, amount);

return (signer
? signer.signTx(spendTx)
: spendTx.signWeb3(rootChain)
).then(signedTx => sendSignedTransaction(plasmaChain, signedTx.hex())
).then(transferTx => {
const tx = Tx.fromRaw(transferTx.raw);
const utxoId = (new Outpoint(tx.hash(), 0)).getUtxoId();
const sigHashBuff = Exit.sigHashBuff(utxoId, amount);
const sigHash = `0x${sigHashBuff.toString('hex')}`;

return Promise.all([
plasmaChain.eth.getTransaction(
fastSellRequest.tx = transferTx;
fastSellRequest.sigHashBuff = sigHashBuff;
[,fastSellRequest.effectiveBlock] = Period.periodBlockRange(transferTx.blockNumber);

return plasmaChain.eth.getTransaction(
util.bufferToHex(tx.inputs[0].prevout.hash)
),
signer
? signer.signMessage(sigHash)
: Tx.signMessageWithWeb3(rootChain, sigHash)
])
.then(([inputTx, sig]) => {
);
}).then((inputTx) => {
fastSellRequest.inputTx = inputTx;
promiEvent.eventEmitter.emit('transfer', fastSellRequest);
return Exit.signAndSendFastSellRequest(fastSellRequest, rootChain, marketMakerUrl, signer);
}).then(promiEvent.resolve)
.catch(promiEvent.reject);
return promiEvent.eventEmitter;
}

static signAndSendFastSellRequest(fastSellRequest, rootChain, marketMakerUrl, signer) {
const sigHash = `0x${fastSellRequest.sigHashBuff.toString('hex')}`;
return (signer
? signer.signMessage(sigHash)
: Tx.signMessageWithWeb3(rootChain, sigHash))
.then((sig) => {
const vBuff = Buffer.alloc(32);
vBuff.writeInt8(sig.v, 31);
const signedData = Exit.bufferToBytes32Array(
Buffer.concat([sigHashBuff, Buffer.from(sig.r), Buffer.from(sig.s), vBuff])
Buffer.concat([fastSellRequest.sigHashBuff, Buffer.from(sig.r), Buffer.from(sig.s), vBuff])
);
const setup = {
inputTx,
tx: transferTx,
effectiveBlock: periodBlockRange(transferTx.blockNumber)[1],
signedData
};
fastSellRequest.signedData = signedData;

return fetch(
marketMakerUrl,
Expand All @@ -141,10 +155,10 @@ export default class Exit {
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(setup),
body: JSON.stringify(fastSellRequest),
}
).then(response => response.json());
})
});
)
.then(response => response.json());
});
}
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"@types/web3": "^1.0.18",
"ethereumjs-util": "6.0.0",
"jsbi-utils": "^1.0.0",
"node-fetch": "^2.3.0"
"node-fetch": "^2.3.0",
"web3-core-promievent": "^1.2.4"
}
}
18 changes: 18 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1015,6 +1015,11 @@ ansi-to-html@^0.6.4:
dependencies:
entities "^1.1.1"

[email protected]:
version "1.3.0"
resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
integrity sha1-q8av7tzqUugJzcA3au0845Y10X8=

anymatch@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb"
Expand Down Expand Up @@ -2445,6 +2450,11 @@ ethjs-util@^0.1.6:
is-hex-prefixed "1.0.0"
strip-hex-prefix "1.0.0"

[email protected]:
version "3.1.2"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==

events@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/events/-/events-3.0.0.tgz#9a0a0dfaf62893d92b875b8f2698ca4114973e88"
Expand Down Expand Up @@ -5975,6 +5985,14 @@ wcwidth@^1.0.1:
dependencies:
defaults "^1.0.3"

web3-core-promievent@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.2.4.tgz#75e5c0f2940028722cdd21ba503ebd65272df6cb"
integrity sha512-gEUlm27DewUsfUgC3T8AxkKi8Ecx+e+ZCaunB7X4Qk3i9F4C+5PSMGguolrShZ7Zb6717k79Y86f3A00O0VAZw==
dependencies:
any-promise "1.3.0"
eventemitter3 "3.1.2"

webidl-conversions@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
Expand Down

0 comments on commit d08f4f5

Please sign in to comment.