Navigation Menu

Skip to content
This repository has been archived by the owner on Jun 30, 2022. It is now read-only.

Commit

Permalink
feat: fetch evidence from the chain
Browse files Browse the repository at this point in the history
  • Loading branch information
satello committed Jul 25, 2018
1 parent fef04aa commit c3f410f
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 81 deletions.
34 changes: 0 additions & 34 deletions src/contracts/abstractions/Arbitrable.js
Expand Up @@ -117,40 +117,6 @@ class ArbitrableContract extends AbstractContract {
return userProfile.contracts
}

/**
* Get evidence for contract.
* @param {string} contractAddress - Address of arbitrable contract.
* @returns {object[]} - Array of evidence objects.
*/
getEvidenceForArbitrableContract = async () => {
const arbitrableContractData = await this._contractImplementation.getData()
const partyAContractData = await this._StoreProvider.getContractByAddress(
arbitrableContractData.partyA,
this._contractImplementation.contractAddress
)
const partyBContractData = await this._StoreProvider.getContractByAddress(
arbitrableContractData.partyB,
this._contractImplementation.contractAddress
)

const partyAEvidence = (partyAContractData
? partyAContractData.evidence
: []
).map(evidence => {
evidence.submitter = arbitrableContractData.partyA
return evidence
})
const partyBEvidence = (partyBContractData
? partyBContractData.evidence
: []
).map(evidence => {
evidence.submitter = arbitrableContractData.partyB
return evidence
})

return partyAEvidence.concat(partyBEvidence)
}

/**
* Fetch all data from the store on the current contract.
* @returns {object} - Store data for contract.
Expand Down
75 changes: 75 additions & 0 deletions src/contracts/implementations/arbitrable/ArbitrableTransaction.js
Expand Up @@ -6,6 +6,8 @@ import * as contractConstants from '../../../constants/contract'
import * as errorConstants from '../../../constants/error'
import ContractImplementation from '../../ContractImplementation'
import deployContractAsync from '../../../utils/deployContractAsync'
import EventListener from '../../../utils/EventListener'
import httpRequest from '../../../utils/httpRequest'

/**
* Provides interaction with an Arbitrable Transaction contract deployed on the blockchain.
Expand Down Expand Up @@ -57,6 +59,79 @@ class ArbitrableTransaction extends ContractImplementation {
return contractDeployed
}

/**
* Get the meta evidence for a contract dispute. Look up meta-evidence events
* and make an http request to the resource.
* @param {string} arbitratorAddress - The arbitrators address.
* @param {number} disputeId - The index of the dispute.
*/
getMetaEvidenceForDispute = async (arbitratorAddress, disputeId) => {
const metaEvidenceLinkLog = await EventListener.getEventLogs(
this,
'LinkMetaEvidence',
0,
'latest',
{ _disputeID: disputeId, _arbitrator: arbitratorAddress }
)

// No meta-evidence for dispute
if (!metaEvidenceLinkLog)
return {}

// Always use the first log
const metaEvidenceId = metaEvidenceLinkLog[0].args._metaEvidenceID
const metaEvidenceLog = await EventListener.getEventLogs(
this,
'MetaEvidence',
0,
'latest',
{ _metaEvidenceID: metaEvidenceId }
)

if (!metaEvidenceLog)
return {} // NOTE better to throw errors for missing meta-evidence?

const metaEvidenceUri = metaEvidenceLog[0].args._evidence
const metaEvidenceResponse = await httpRequest(
'GET',
metaEvidenceUri
)

if (metaEvidenceResponse.status !== 200)
throw new Error(`Unable to fetch meta-evidence at ${metaEvidenceUri}`)
return metaEvidenceResponse.body || {}
}

/**
* Get the evidence submitted in a dispute.
* @param {string} arbitratorAddress - The arbitrators address.
* @param {number} disputeId - The index of the dispute.
*/
getEvidenceForDispute = async (arbitratorAddress, disputeId) => {
const evidenceLogs = await EventListener.getEventLogs(
this,
'Evidence',
0,
'latest',
{ _disputeID: disputeId, _arbitrator: arbitratorAddress }
)

return evidenceLogs.map(async evidenceLog => {
const evidenceURI = evidenceLog.args._evidence
const evidence = await httpRequest(
'GET',
metaEvidenceUri
)
const submittedAt = (
await this._Web3Wrapper.getBlock(evidenceLog.blockNumber)
).timestamp
return {
...evidence.body,
...{ submittedBy: evidenceLog.args._party, submittedAt }
}
})
}

/**
* Pay the party B. To be called when the good is delivered or the service rendered.
* @param {string} account - Ethereum account (default account[0]).
Expand Down
2 changes: 1 addition & 1 deletion src/contracts/implementations/arbitrator/KlerosPOC.js
Expand Up @@ -698,7 +698,7 @@ class KlerosPOC extends ContractImplementation {
* @returns {number} timestamp
*/
_getTimestampForBlock = async blockNumber =>
(await this.getBlock(blockNumber)).timestamp
(await this._Web3Wrapper.getBlock(blockNumber)).timestamp

/**
* Get event NewPeriod event logs a period in a session.
Expand Down
14 changes: 10 additions & 4 deletions src/resources/Disputes.js
Expand Up @@ -312,10 +312,15 @@ class Disputes {
await this._ArbitrableInstance.setContractInstance(
arbitrableContractAddress
)
const [arbitrableContractData, evidence] = await Promise.all([
const [arbitrableContractData, evidence, metaEvidence] = await Promise.all([
this._ArbitrableInstance.getData(account),
this._ArbitrableInstance.getEvidenceForArbitrableContract(
arbitrableContractAddress
this._ArbitrableInstance.getEvidenceForDispute(
arbitratorAddress,
dispute.disputeId
),
this._ArbitrableInstance.getMetaEvidenceForDispute(
arbitratorAddress,
dispute.disputeId
)
])
const contractStoreData = await this._StoreProviderInstance.getContractByAddress(
Expand Down Expand Up @@ -435,7 +440,8 @@ class Disputes {
? contractStoreData.description
: undefined,
email: contractStoreData ? contractStoreData.email : undefined,
evidence
evidence,
metaEvidence
}
}
}
Expand Down
47 changes: 5 additions & 42 deletions src/utils/StoreProviderWrapper.js
Expand Up @@ -3,6 +3,7 @@ import _ from 'lodash'
import * as errorConstants from '../constants/error'

import PromiseQueue from './PromiseQueue'
import httpRequest from './httpRequest'

/**
* A wrapper for interacting with Kleros Store.
Expand All @@ -17,44 +18,6 @@ class StoreProviderWrapper {
this._storeQueue = new PromiseQueue()
}

/**
* Helper method for sending an http request to kleros store.
* @param {string} verb - HTTP verb to be used in request. E.g. GET, POST, PUT.
* @param {string} uri - The uri to send the request to.
* @param {string} body - json string of the body.
* @returns {Promise} request promise that resolves to the HTTP response.
*/
_makeRequest = (verb, uri, body = null) => {
const httpRequest = new XMLHttpRequest()
return new Promise((resolve, reject) => {
try {
httpRequest.open(verb, uri, true)
if (body) {
httpRequest.setRequestHeader(
'Content-Type',
'application/json;charset=UTF-8'
)
}
httpRequest.onreadystatechange = () => {
if (httpRequest.readyState === 4) {
let body = null
try {
body = JSON.parse(httpRequest.responseText)
// eslint-disable-next-line no-unused-vars
} catch (err) {}
resolve({
body: body,
status: httpRequest.status
})
}
}
httpRequest.send(body)
} catch (err) {
reject(errorConstants.REQUEST_FAILED(err))
}
})
}

/**
* use the queue for write request. this allows a function to be passed so we can read immediately before we write
* @param {fn} getBodyFn async function to call before we write. Should to reads and return JSON to be used as body.
Expand All @@ -64,7 +27,7 @@ class StoreProviderWrapper {
*/
queueWriteRequest = (getBodyFn, verb, uri = null) =>
this._storeQueue.fetch(() =>
getBodyFn().then(result => this._makeRequest(verb, uri, result))
getBodyFn().then(result => httpRequest(verb, uri, result))
)

/**
Expand All @@ -73,7 +36,7 @@ class StoreProviderWrapper {
* @returns {Promise} promise of the result function
*/
queueReadRequest = uri =>
this._storeQueue.fetch(() => this._makeRequest('GET', uri))
this._storeQueue.fetch(() => httpRequest('GET', uri))


getMetaEvidenceUri = address => (
Expand All @@ -90,7 +53,7 @@ class StoreProviderWrapper {
* @returns {object} - a response object.
*/
getUserProfile = async userAddress => {
const httpResponse = await this._makeRequest(
const httpResponse = await httpRequest(
'GET',
`${this._storeUri}/${userAddress}`
)
Expand Down Expand Up @@ -177,7 +140,7 @@ class StoreProviderWrapper {
let userProfile = await this.getUserProfile(userAddress)
if (_.isNull(userProfile)) {
// we can safely make request without queuing because all other writes for profile will fail if it hasn't been created.
const response = await this._makeRequest(
const response = await httpRequest(
'POST',
`${this._storeUri}/${userAddress}`
)
Expand Down
39 changes: 39 additions & 0 deletions src/utils/httpRequest.js
@@ -0,0 +1,39 @@
/**
* Helper method for sending an http requests.
* @param {string} verb - HTTP verb to be used in request. E.g. GET, POST, PUT.
* @param {string} uri - The uri to send the request to.
* @param {string} body - json string of the body.
* @returns {Promise} request promise that resolves to the HTTP response.
*/
const httpRequest = (verb, uri, body = null) => {
const httpRequest = new XMLHttpRequest()
return new Promise((resolve, reject) => {
try {
httpRequest.open(verb, uri, true)
if (body) {
httpRequest.setRequestHeader(
'Content-Type',
'application/json;charset=UTF-8'
)
}
httpRequest.onreadystatechange = () => {
if (httpRequest.readyState === 4) {
let body = null
try {
body = JSON.parse(httpRequest.responseText)
// eslint-disable-next-line no-unused-vars
} catch (err) {}
resolve({
body: body,
status: httpRequest.status
})
}
}
httpRequest.send(body)
} catch (err) {
reject(errorConstants.REQUEST_FAILED(err))
}
})
}

export default httpRequest

0 comments on commit c3f410f

Please sign in to comment.