220 lines
6.1 KiB
JavaScript
220 lines
6.1 KiB
JavaScript
const { expect } = require('chai')
|
|
const { BN } = require('../setup')
|
|
|
|
// returns a Promise that resolves with a hex string that is the signature of
|
|
// `data` signed with the key of `address`
|
|
function sign(address, data) {
|
|
return new Promise((resolve, reject) => {
|
|
web3.eth.sign(data, address, (err, result) => {
|
|
if (err !== null) {
|
|
return reject(err)
|
|
}
|
|
return resolve(normalizeSignature(result))
|
|
// return resolve(result);
|
|
})
|
|
})
|
|
}
|
|
module.exports.sign = sign
|
|
|
|
// geth && testrpc has different output of eth_sign than parity
|
|
// https://github.com/ethereumjs/testrpc/issues/243#issuecomment-326750236
|
|
function normalizeSignature(rawSignature) {
|
|
const signature = strip0x(rawSignature)
|
|
|
|
// increase v by 27...
|
|
return `0x${signature.substr(0, 128)}${(parseInt(signature.substr(128), 16) + 27).toString(16)}`
|
|
}
|
|
module.exports.normalizeSignature = normalizeSignature
|
|
|
|
// strips leading "0x" if present
|
|
function strip0x(input) {
|
|
return input.replace(/^0x/, '')
|
|
}
|
|
module.exports.strip0x = strip0x
|
|
|
|
// extracts and returns the `v`, `r` and `s` values from a `signature`.
|
|
function signatureToVRS(rawSignature) {
|
|
assert.equal(rawSignature.length, 2 + 32 * 2 + 32 * 2 + 2)
|
|
const signature = strip0x(rawSignature)
|
|
const v = signature.substr(64 * 2)
|
|
const r = signature.substr(0, 32 * 2)
|
|
const s = signature.substr(32 * 2, 32 * 2)
|
|
return { v, r, s }
|
|
}
|
|
module.exports.signatureToVRS = signatureToVRS
|
|
|
|
function packSignatures(array) {
|
|
const length = strip0x(web3.utils.toHex(array.length))
|
|
const msgLength = length.length === 1 ? `0${length}` : length
|
|
let v = ''
|
|
let r = ''
|
|
let s = ''
|
|
array.forEach(e => {
|
|
v = v.concat(e.v)
|
|
r = r.concat(e.r)
|
|
s = s.concat(e.s)
|
|
})
|
|
return `0x${msgLength}${v}${r}${s}`
|
|
}
|
|
module.exports.packSignatures = packSignatures
|
|
|
|
// returns BigNumber `num` converted to a little endian hex string
|
|
// that is exactly 32 bytes long.
|
|
// `num` must represent an unsigned integer
|
|
function bigNumberToPaddedBytes32(num) {
|
|
let result = strip0x(num.toString(16))
|
|
while (result.length < 64) {
|
|
result = `0${result}`
|
|
}
|
|
return `0x${result}`
|
|
}
|
|
module.exports.bigNumberToPaddedBytes32 = bigNumberToPaddedBytes32
|
|
|
|
// returns an promise that resolves to an object
|
|
// that maps `addresses` to their current balances
|
|
function getBalances(addresses) {
|
|
return Promise.all(
|
|
addresses.map(address => {
|
|
return web3.eth.getBalance(address)
|
|
})
|
|
).then(balancesArray => {
|
|
const addressToBalance = {}
|
|
addresses.forEach((address, index) => {
|
|
addressToBalance[address] = balancesArray[index]
|
|
})
|
|
return addressToBalance
|
|
})
|
|
}
|
|
module.exports.getBalances = getBalances
|
|
|
|
// returns hex string of the bytes of the message
|
|
// composed from `recipient`, `value` and `transactionHash`
|
|
// that is relayed from `foreign` to `home` on withdraw
|
|
function createMessage(rawRecipient, rawValue, rawTransactionHash, rawContractAddress) {
|
|
const recipient = strip0x(rawRecipient)
|
|
assert.equal(recipient.length, 20 * 2)
|
|
|
|
const value = strip0x(bigNumberToPaddedBytes32(rawValue))
|
|
assert.equal(value.length, 64)
|
|
|
|
const transactionHash = strip0x(rawTransactionHash)
|
|
assert.equal(transactionHash.length, 32 * 2)
|
|
|
|
const contractAddress = strip0x(rawContractAddress)
|
|
assert.equal(contractAddress.length, 20 * 2)
|
|
|
|
const message = `0x${recipient}${value}${transactionHash}${contractAddress}`
|
|
const expectedMessageLength = (20 + 32 + 32 + 20) * 2 + 2
|
|
assert.equal(message.length, expectedMessageLength)
|
|
return message
|
|
}
|
|
module.exports.createMessage = createMessage
|
|
|
|
// returns array of integers progressing from `start` up to, but not including, `end`
|
|
function range(start, end) {
|
|
const result = []
|
|
for (let i = start; i < end; i++) {
|
|
result.push(i)
|
|
}
|
|
return result
|
|
}
|
|
module.exports.range = range
|
|
|
|
// just used to signal/document that we're explicitely ignoring/expecting an error
|
|
function ignoreExpectedError() {}
|
|
module.exports.ignoreExpectedError = ignoreExpectedError
|
|
|
|
const getEvents = (truffleInstance, filter, fromBlock = 0, toBlock = 'latest') =>
|
|
truffleInstance.contract.getPastEvents(filter.event, { fromBlock, toBlock })
|
|
|
|
module.exports.getEvents = getEvents
|
|
|
|
function ether(n) {
|
|
return new BN(web3.utils.toWei(n, 'ether'))
|
|
}
|
|
|
|
module.exports.ether = ether
|
|
|
|
function expectEventInLogs(logs, eventName, eventArgs = {}) {
|
|
const events = logs.filter(e => e.event === eventName)
|
|
expect(events.length > 0).to.equal(true, `There is no '${eventName}'`)
|
|
|
|
const exception = []
|
|
const event = events.find(e => {
|
|
for (const [k, v] of Object.entries(eventArgs)) {
|
|
try {
|
|
contains(e.args, k, v)
|
|
} catch (error) {
|
|
exception.push(error)
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
|
|
if (event === undefined) {
|
|
throw exception[0]
|
|
}
|
|
|
|
return event
|
|
}
|
|
|
|
function contains(args, key, value) {
|
|
expect(key in args).to.equal(true, `Unknown event argument '${key}'`)
|
|
|
|
if (value === null) {
|
|
expect(args[key]).to.equal(null)
|
|
} else if (isBN(args[key])) {
|
|
expect(args[key]).to.be.bignumber.equal(value)
|
|
} else {
|
|
expect(args[key]).to.be.equal(value)
|
|
}
|
|
}
|
|
|
|
function isBN(object) {
|
|
return BN.isBN(object) || object instanceof BN
|
|
}
|
|
|
|
module.exports.expectEventInLogs = expectEventInLogs
|
|
|
|
function createAccounts(web3, amount) {
|
|
const array = []
|
|
for (let i = 0; i < amount; i++) {
|
|
array[i] = web3.eth.accounts.create().address
|
|
}
|
|
return array
|
|
}
|
|
|
|
module.exports.createAccounts = createAccounts
|
|
|
|
function createFullAccounts(web3, amount) {
|
|
const array = []
|
|
for (let i = 0; i < amount; i++) {
|
|
array[i] = web3.eth.accounts.create()
|
|
}
|
|
return array
|
|
}
|
|
|
|
module.exports.createFullAccounts = createFullAccounts
|
|
|
|
async function delay(ms) {
|
|
return new Promise(res => setTimeout(res, ms))
|
|
}
|
|
|
|
module.exports.delay = delay
|
|
|
|
async function evalMetrics(target, ...metrics) {
|
|
const before = await Promise.all(metrics.map(metric => metric()))
|
|
await target()
|
|
const after = await Promise.all(metrics.map(metric => metric()))
|
|
return [...before, ...after]
|
|
}
|
|
|
|
module.exports.evalMetrics = evalMetrics
|
|
|
|
function paymasterError(reason) {
|
|
return `paymaster rejected in local view call to 'relayCall()' : ${reason}`
|
|
}
|
|
|
|
module.exports.paymasterError = paymasterError
|