-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from ConsenSys/generateSwap
Implement generate swap for Bitcoin and Ethereum
- Loading branch information
Showing
9 changed files
with
250 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import Provider from '../../Provider' | ||
import crypto from './BitcoinCrypto' | ||
const { addressToPubKeyHash } = crypto | ||
|
||
export default class BitcoinSwapProvider extends Provider { | ||
generateSwap (recipientAddress, refundAddress, secretHash, expiration) { | ||
let expirationHex = expiration.toString(16) | ||
if (expirationHex.length % 2 === 1) { | ||
expirationHex = '0' + expirationHex | ||
} | ||
expirationHex = expirationHex.match(/.{2}/g).reverse() | ||
expirationHex.length = Math.min(expirationHex.length, 5) | ||
expirationHex[expirationHex.length - 1] = '00' | ||
|
||
const recipientPubKeyHash = addressToPubKeyHash(recipientAddress) | ||
const refundPubKeyHash = addressToPubKeyHash(refundAddress) | ||
const expirationPushDataOpcode = expirationHex.length.toString(16).padStart(2, '0') | ||
const expirationHexEncoded = expirationHex.join('') | ||
|
||
return [ | ||
'76', 'a9', // OP_DUP OP_HASH160 | ||
'72', // OP_2SWAP | ||
'63', // OP_IF | ||
'a8', // OP_SHA256 | ||
'20', secretHash, // OP_PUSHDATA20 {secretHash} | ||
'88', // OP_EQUALVERIFY | ||
'14', recipientPubKeyHash, // OP_PUSHDATA20 {recipientPubKeyHash} | ||
'67', // OP_ELSE | ||
expirationPushDataOpcode, // OP_PUSHDATA{expirationHexLength} | ||
expirationHexEncoded, // {expirationHexEncoded} | ||
'b1', // OP_CHECKLOCKTIMEVERIFY | ||
'6d', // OP_2DROP | ||
'14', refundPubKeyHash, // OP_PUSHDATA20 {refundPubKeyHash} | ||
'68', // OP_ENDIF | ||
'88', 'ac' // OP_EQUALVERIFY OP_CHECKSIG | ||
].join('') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import BitcoinRPCProvider from './BitcoinRPCProvider' | ||
import BitcoinLedgerProvider from './BitcoinLedgerProvider' | ||
import BitcoinCrypto from './BitcoinCrypto' | ||
import BitcoinSwapProvider from './BitcoinSwapProvider' | ||
|
||
export default { | ||
BitcoinRPCProvider, | ||
BitcoinLedgerProvider, | ||
crypto: BitcoinCrypto, | ||
BitcoinSwapProvider | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import Provider from '../../Provider' | ||
|
||
export default class EthereumSwapProvider extends Provider { | ||
generateSwap (recipientAddress, refundAddress, secretHash, expiration) { | ||
const dataSizeBase = 112 | ||
const redeemDestinationBase = 66 | ||
const refundDestinationBase = 89 | ||
const expirationHex = expiration.toString(16) | ||
const expirationEncoded = expirationHex.length % 2 ? '0' + expirationHex : expirationHex // Pad with 0 | ||
const expirationSize = expirationEncoded.length / 2 | ||
const expirationPushOpcode = (0x60 - 1 + expirationSize).toString(16) | ||
const redeemDestinationEncoded = (redeemDestinationBase + expirationSize).toString(16) | ||
const refundDestinationEncoded = (refundDestinationBase + expirationSize).toString(16) | ||
const dataSizeEncoded = (dataSizeBase + expirationSize).toString(16) | ||
const recipientAddressEncoded = recipientAddress.replace('0x', '') // Remove 0x if exists | ||
const refundAddressEncoded = refundAddress.replace('0x', '') // Remove 0x if exists | ||
const secretHashEncoded = secretHash.replace('0x', '') // Remove 0x if exists | ||
|
||
return [ | ||
// Constructor | ||
'60', dataSizeEncoded, // PUSH1 {dataSizeEncoded} | ||
'80', // DUP1 | ||
'60', '0b', // PUSH1 0b | ||
'60', '00', // PUSH1 00 | ||
'39', // CODECOPY | ||
'60', '00', // PUSH1 00 | ||
'f3', // RETURN | ||
|
||
// Contract | ||
'60', '20', // PUSH1 20 | ||
|
||
// Get secret | ||
'80', // DUP1 | ||
'60', '00', // PUSH1 00 | ||
'80', // DUP1 | ||
'37', // CALLDATACOPY | ||
|
||
// SHA256 | ||
'60', '21', // PUSH1 21 | ||
'81', // DUP2 | ||
'60', '00', // PUSH1 00 | ||
'80', // DUP1 | ||
'60', '02', // PUSH1 02 | ||
'60', '48', // PUSH1 48 | ||
'f1', // CALL | ||
|
||
// Validate with secretHash | ||
'7f', secretHashEncoded, // PUSH32 {secretHashEncoded} | ||
'60', '21', // PUSH1 21 | ||
'51', // MLOAD | ||
'14', // EQ | ||
'16', // AND (to make sure CALL succeeded) | ||
// Redeem if secret is valid | ||
'60', redeemDestinationEncoded, // PUSH1 {redeemDestinationEncoded} | ||
'57', // JUMPI | ||
|
||
// Check time lock | ||
expirationPushOpcode, // PUSH{expirationSize} | ||
expirationEncoded, // {expirationEncoded} | ||
'42', // TIMESTAMP | ||
'11', // GT | ||
// Refund if timelock passed | ||
'60', refundDestinationEncoded, // PUSH1 {refundDestinationEncoded} | ||
'57', | ||
|
||
'00', // STOP | ||
|
||
'5b', // JUMPDEST | ||
'73', recipientAddressEncoded, // PUSH20 {recipientAddressEncoded} | ||
'ff', // SUICIDE | ||
|
||
'5b', // JUMPDEST | ||
'73', refundAddressEncoded, // PUSH20 {refundAddressEncoded} | ||
'ff' // SUICIDE | ||
].join('') | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import EthereumRPCProvider from './EthereumRPCProvider' | ||
import EthereumLedgerProvider from './EthereumLedgerProvider' | ||
import EthereumMetaMaskProvider from './EthereumMetaMaskProvider' | ||
import EthereumSwapProvider from './EthereumSwapProvider' | ||
|
||
export default { | ||
EthereumRPCProvider, | ||
EthereumLedgerProvider, | ||
EthereumMetaMaskProvider, | ||
EthereumSwapProvider | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,20 +1,7 @@ | ||
import BitcoinRPCProvider from './bitcoin/BitcoinRPCProvider' | ||
import BitcoinLedgerProvider from './bitcoin/BitcoinLedgerProvider' | ||
import BitcoinCrypto from './bitcoin/BitcoinCrypto' | ||
|
||
import EthereumRPCProvider from './ethereum/EthereumRPCProvider' | ||
import EthereumLedgerProvider from './ethereum/EthereumLedgerProvider' | ||
import EthereumMetaMaskProvider from './ethereum/EthereumMetaMaskProvider' | ||
import bitcoin from './bitcoin/' | ||
import ethereum from './ethereum/' | ||
|
||
export default { | ||
bitcoin: { | ||
BitcoinRPCProvider, | ||
BitcoinLedgerProvider, | ||
crypto: BitcoinCrypto | ||
}, | ||
ethereum: { | ||
EthereumRPCProvider, | ||
EthereumLedgerProvider, | ||
EthereumMetaMaskProvider | ||
} | ||
bitcoin, | ||
ethereum | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* eslint-env mocha */ | ||
|
||
const chai = require('chai') | ||
const chaiAsPromised = require('chai-as-promised') | ||
chai.use(chaiAsPromised) | ||
const { expect } = chai | ||
|
||
const ChainAbstractionLayer = require('../') | ||
|
||
const lib = new ChainAbstractionLayer.providers.bitcoin.BitcoinSwapProvider() | ||
|
||
describe('Bitcoin Swap provider', () => { | ||
describe('Generate swap', () => { | ||
it('should generate correct bytecode', () => { | ||
return expect(lib.generateSwap('1J7eFp9p48g3U3yCREyhd6LJzhnkywhi5s', | ||
'1GZQKjsC97yasxRj1wtYf5rC61AxpR1zmr', | ||
'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', | ||
1532622116403)) | ||
.to.equal('76a97263a820ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8814bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb6705339665d700b16d14aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa6888ac') | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* eslint-env mocha */ | ||
|
||
const chai = require('chai') | ||
const chaiAsPromised = require('chai-as-promised') | ||
chai.use(chaiAsPromised) | ||
const { expect } = chai | ||
|
||
const ChainAbstractionLayer = require('../') | ||
|
||
const lib = new ChainAbstractionLayer.providers.ethereum.EthereumSwapProvider() | ||
|
||
describe('Ethereum Swap provider', () => { | ||
describe('Generate swap', () => { | ||
it('should generate correct bytecode', () => { | ||
return expect(lib.generateSwap('0x5acbf79d0cf4139a6c3eca85b41ce2bd23ced04f', | ||
'0x0a81e8be41b21f651a71aab1a85c6813b8bbccf8', | ||
'0x91d6a24697ed31932537ae598d3de3131e1fcd0641b9ac4be7afcb376386d71e', | ||
255)) | ||
.to.equal('607180600b6000396000f36020806000803760218160008060026048f17f91d6a24697ed31932537ae598d3de3131e1fcd0641b9ac4be7afcb376386d71e602151141660435760ff4211605a57005b735acbf79d0cf4139a6c3eca85b41ce2bd23ced04fff5b730a81e8be41b21f651a71aab1a85c6813b8bbccf8ff') | ||
}) | ||
|
||
it('should generate correct bytecode with different expiration length', () => { | ||
return expect(lib.generateSwap('0x5acbf79d0cf4139a6c3eca85b41ce2bd23ced04f', | ||
'0x0a81e8be41b21f651a71aab1a85c6813b8bbccf8', | ||
'0x91d6a24697ed31932537ae598d3de3131e1fcd0641b9ac4be7afcb376386d71e', | ||
6016519)) | ||
.to.equal('607380600b6000396000f36020806000803760218160008060026048f17f91d6a24697ed31932537ae598d3de3131e1fcd0641b9ac4be7afcb376386d71e6021511416604557625bce074211605c57005b735acbf79d0cf4139a6c3eca85b41ce2bd23ced04fff5b730a81e8be41b21f651a71aab1a85c6813b8bbccf8ff') | ||
}) | ||
}) | ||
}) |
and this?