Skip to content

Commit

Permalink
feat: Allows number of ruling options to be defined on creation
Browse files Browse the repository at this point in the history
And formats code to conform to solidity guidelines.
  • Loading branch information
eccentricexit committed Jun 27, 2018
1 parent eade597 commit 6619f2a
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 80 deletions.
40 changes: 28 additions & 12 deletions contracts/standard/arbitration/ArbitrableTransaction.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import "./TwoPartyArbitrable.sol";
* This can be used for buying goods, services and for paying freelancers.
* Party A is the payer. Party B is the payee.
*/
contract ArbitrableTransaction is TwoPartyArbitrable {
contract ArbitrableTransaction is TwoPartyArbitrable {
string constant RULING_OPTIONS = "Reimburse partyA;Pay partyB";

uint public amount; // Amount sent by party A.
Expand All @@ -24,26 +24,44 @@ import "./TwoPartyArbitrable.sol";
* @param _hashContract Keccak hash of the plain English contract.
* @param _timeout Time after which a party automatically loose a dispute.
* @param _partyB The recipient of the transaction.
* @param _amountOfChoices The number of ruling options available.
* @param _arbitratorExtraData Extra data for the arbitrator.
*/
function ArbitrableTransaction(Arbitrator _arbitrator, bytes32 _hashContract, uint _timeout, address _partyB, bytes _arbitratorExtraData) TwoPartyArbitrable(_arbitrator,_hashContract,_timeout,_partyB,_arbitratorExtraData) payable {
amount+=msg.value;
constructor(
Arbitrator _arbitrator,
bytes32 _hashContract,
uint _timeout,
address _partyB,
uint8 _amountOfChoices,
bytes _arbitratorExtraData
)
TwoPartyArbitrable(
_arbitrator,
_hashContract,
_timeout,
_partyB,
_amountOfChoices,
_arbitratorExtraData)
public
payable
{
amount += msg.value;
}

/** @dev Pay the party B. To be called when the good is delivered or the service rendered.
*/
function pay() onlyPartyA {
function pay() public onlyPartyA {
partyB.transfer(amount);
amount=0;
amount = 0;
}

/** @dev Reimburse party A. To be called if the good or service can't be fully provided.
* @param _amountReimbursed Amount to reimburse in wei.
*/
function reimburse(uint _amountReimbursed) onlyPartyB {
function reimburse(uint _amountReimbursed) public onlyPartyB {
require(_amountReimbursed<=amount);
partyA.transfer(_amountReimbursed);
amount-=_amountReimbursed;
amount -= _amountReimbursed;
}

/** @dev Execute a ruling of a dispute. It reimburse the fee to the winning party.
Expand All @@ -58,9 +76,7 @@ import "./TwoPartyArbitrable.sol";
else if (_ruling==PARTY_B_WINS)
partyB.send(amount);

amount=0;
}

amount = 0;
}

}

}
25 changes: 22 additions & 3 deletions contracts/standard/arbitration/Rental.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import "./TwoPartyArbitrable.sol";
* Party A put a deposit. If everything goes well, it will be given back.
* Otherwize parties can claim an amount of damages. If they disagree, the arbitrator will have to solve this dispute.
*/
contract Rental is TwoPartyArbitrable {
contract Rental is TwoPartyArbitrable {
string constant RULING_OPTIONS = "Rule for party A (renter);Rule for Party B (owner)";

uint public amount; // Amount sent by party A.
Expand All @@ -27,10 +27,29 @@ import "./TwoPartyArbitrable.sol";
* @param _hashContract Keccak hash of the plain English contract.
* @param _timeout Time after which a party automatically loose a dispute.
* @param _partyB The owner.
* @param _amountOfChoices The number of ruling options available.
* @param _arbitratorExtraData Extra data for the arbitrator.
*/
function Rental(Arbitrator _arbitrator, bytes32 _hashContract, uint _timeout, address _partyB, bytes _arbitratorExtraData) public TwoPartyArbitrable(_arbitrator,_hashContract,_timeout,_partyB,_arbitratorExtraData) payable {
amount+=msg.value;
constructor(
Arbitrator _arbitrator,
bytes32 _hashContract,
uint _timeout,
address _partyB,
uint8 _amountOfChoices,
bytes _arbitratorExtraData
)
TwoPartyArbitrable(
_arbitrator,
_hashContract,
_timeout,
_partyB,
_amountOfChoices,
_arbitratorExtraData
)
public
payable
{
amount += msg.value;
}

/** @dev Claim an amount of damages.
Expand Down
90 changes: 56 additions & 34 deletions contracts/standard/arbitration/TwoPartyArbitrable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import "./Arbitrable.sol";
*/
contract TwoPartyArbitrable is Arbitrable {
uint public timeout; // Time in second a party can take before being considered unresponding and lose the dispute.
uint8 amountOfChoices;
address public partyA;
address public partyB;
uint public partyAFee; // Total fees paid by the partyA.
Expand All @@ -26,10 +27,10 @@ contract TwoPartyArbitrable is Arbitrable {
enum Status {NoDispute, WaitingPartyA, WaitingPartyB, DisputeCreated, Resolved}
Status public status;

uint8 constant AMOUNT_OF_CHOICES = 2;
// A plain English of what rulings do. Need to be redefined by the child class.
string constant RULING_OPTIONS = "Party A wins;Party B wins";
uint8 constant PARTY_A_WINS = 1;
uint8 constant PARTY_B_WINS = 2;
string constant RULING_OPTIONS = "Party A wins;Party B wins"; // A plain English of what rulings do. Need to be redefined by the child class.
uint8 constant PARTY_B_WINS = 2;

modifier onlyPartyA{ require(msg.sender==partyA); _; }
modifier onlyPartyB{ require(msg.sender==partyB); _; }
Expand All @@ -47,30 +48,46 @@ contract TwoPartyArbitrable is Arbitrable {
* @param _hashContract Keccak hash of the plain English contract.
* @param _timeout Time after which a party automatically loose a dispute.
* @param _partyB The recipient of the transaction.
* @param _amountOfChoices The number of ruling options available.
* @param _arbitratorExtraData Extra data for the arbitrator.
*/
function TwoPartyArbitrable(Arbitrator _arbitrator, bytes32 _hashContract, uint _timeout, address _partyB, bytes _arbitratorExtraData) Arbitrable(_arbitrator,_arbitratorExtraData,_hashContract) {
timeout=_timeout;
partyA=msg.sender;
partyB=_partyB;
constructor(
Arbitrator _arbitrator,
bytes32 _hashContract,
uint _timeout,
address _partyB,
uint8 _amountOfChoices,
bytes _arbitratorExtraData
)
Arbitrable(_arbitrator,_arbitratorExtraData,_hashContract)
public
{
timeout = _timeout;
partyA = msg.sender;
partyB = _partyB;
amountOfChoices = _amountOfChoices;
}


/** @dev Pay the arbitration fee to raise a dispute. To be called by the party A. UNTRUSTED.
* Note that the arbitrator can have createDispute throw, which will make this function throw and therefore lead to a party being timed-out.
* Note that the arbitrator can have createDispute throw, which will make this function
* throw and therefore lead to a party being timed-out.
* This is not a vulnerability as the arbitrator can rule in favor of one party anyway.
*/
function payArbitrationFeeByPartyA() payable onlyPartyA {
uint arbitrationCost=arbitrator.arbitrationCost(arbitratorExtraData);
partyAFee+=msg.value;
function payArbitrationFeeByPartyA() public payable onlyPartyA {
uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);
partyAFee += msg.value;
require(partyAFee == arbitrationCost); // Require that the total pay at least the arbitration cost.
require(status<Status.DisputeCreated); // Make sure a dispute has not been created yet.

lastInteraction=now;
if (partyBFee < arbitrationCost) { // The partyB still has to pay. This can also happens if he has paid, but arbitrationCost has increased.
status=Status.WaitingPartyB;
HasToPayFee(Party.PartyB);
} else { // The partyB has also paid the fee. We create the dispute
lastInteraction = now;
if (partyBFee < arbitrationCost) {
// The partyB still has to pay. This can also happens if he has paid,
// but arbitrationCost has increased.
status = Status.WaitingPartyB;
emit HasToPayFee(Party.PartyB);
} else {
// The partyB has also paid the fee. We create the dispute
raiseDispute(arbitrationCost);
}
}
Expand All @@ -79,16 +96,18 @@ contract TwoPartyArbitrable is Arbitrable {
/** @dev Pay the arbitration fee to raise a dispute. To be called by the party B. UNTRUSTED.
* Note that this function mirror payArbitrationFeeByPartyA.
*/
function payArbitrationFeeByPartyB() payable onlyPartyB {
uint arbitrationCost=arbitrator.arbitrationCost(arbitratorExtraData);
partyBFee+=msg.value;
function payArbitrationFeeByPartyB() public payable onlyPartyB {
uint arbitrationCost = arbitrator.arbitrationCost(arbitratorExtraData);
partyBFee += msg.value;
require(partyBFee == arbitrationCost); // Require that the total pay at least the arbitration cost.
require(status<Status.DisputeCreated); // Make sure a dispute has not been created yet.

lastInteraction=now;
if (partyAFee < arbitrationCost) { // The partyA still has to pay. This can also happens if he has paid, but arbitrationCost has increased.
status=Status.WaitingPartyA;
HasToPayFee(Party.PartyA);
lastInteraction = now;
if (partyAFee < arbitrationCost) {
// The partyA still has to pay. This can also happens if he has paid,
// but arbitrationCost has increased.
status = Status.WaitingPartyA;
emit HasToPayFee(Party.PartyA);
} else { // The partyA has also paid the fee. We create the dispute
raiseDispute(arbitrationCost);
}
Expand All @@ -98,14 +117,14 @@ contract TwoPartyArbitrable is Arbitrable {
* @param _arbitrationCost Amount to pay the arbitrator.
*/
function raiseDispute(uint _arbitrationCost) internal {
status=Status.DisputeCreated;
disputeID=arbitrator.createDispute.value(_arbitrationCost)(AMOUNT_OF_CHOICES,arbitratorExtraData);
Dispute(arbitrator,disputeID,RULING_OPTIONS);
status = Status.DisputeCreated;
disputeID = arbitrator.createDispute.value(_arbitrationCost)(amountOfChoices,arbitratorExtraData);
emit Dispute(arbitrator,disputeID,RULING_OPTIONS);
}

/** @dev Reimburse partyA if partyB fails to pay the fee.
*/
function timeOutByPartyA() onlyPartyA {
function timeOutByPartyA() public onlyPartyA {
require(status==Status.WaitingPartyB);
require(now>=lastInteraction+timeout);

Expand All @@ -114,7 +133,7 @@ contract TwoPartyArbitrable is Arbitrable {

/** @dev Pay partyB if partyA fails to pay the fee.
*/
function timeOutByPartyB() onlyPartyB {
function timeOutByPartyB() public onlyPartyB {
require(status==Status.WaitingPartyA);
require(now>=lastInteraction+timeout);

Expand All @@ -124,17 +143,17 @@ contract TwoPartyArbitrable is Arbitrable {
/** @dev Submit a reference to evidence. EVENT.
* @param _evidence A link to an evidence using its URI.
*/
function submitEvidence(string _evidence) onlyParty {
function submitEvidence(string _evidence) public onlyParty {
require(status>=Status.DisputeCreated);
Evidence(arbitrator,disputeID,msg.sender,_evidence);
emit Evidence(arbitrator,disputeID,msg.sender,_evidence);
}

/** @dev Appeal an appealable ruling.
* Transfer the funds to the arbitrator.
* Note that no checks are required as the checks are done by the arbitrator.
* @param _extraData Extra data for the arbitrator appeal procedure.
*/
function appeal(bytes _extraData) onlyParty payable {
function appeal(bytes _extraData) payable public onlyParty {
arbitrator.appeal.value(msg.value)(disputeID,_extraData);
}

Expand All @@ -145,15 +164,18 @@ contract TwoPartyArbitrable is Arbitrable {
*/
function executeRuling(uint _disputeID, uint _ruling) internal {
require(_disputeID==disputeID);
require(_ruling<=AMOUNT_OF_CHOICES);
require(_ruling<=amountOfChoices);

// Give the arbitration fee back.
// Note that we use send to prevent a party from blocking the execution.
// In both cases sends the highest amount paid to avoid ETH to be stuck in
// the contract if the arbitrator lowers its fee.
if (_ruling==PARTY_A_WINS)
partyA.send(partyAFee > partyBFee ? partyAFee : partyBFee); // In both cases sends the highest amount paid to avoid ETH to be stuck in the contract if the arbitrator lowers its fee.
partyA.send(partyAFee > partyBFee ? partyAFee : partyBFee);
else if (_ruling==PARTY_B_WINS)
partyB.send(partyAFee > partyBFee ? partyAFee : partyBFee);
status=Status.Resolved;

status = Status.Resolved;
}

}
Loading

0 comments on commit 6619f2a

Please sign in to comment.