-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
State Channels (Payment Channels) (#382)
* Added HashExistenceDecider over a range with ForAllSuchThatDecider * Added SignedByDecider * Added SignedByQuantifier * Added State Channel example and associated tests ** Added Or Decider ** Added ThereExistsSuchThatDecider ** Created StateChannelMessage, StateChannelExitClaim, etc. ** Added MessageDB and StateChannelMessageDB Fixes: * Fixed AND decider to not throw undecided if either left OR right returns false, adding dispute unit tests for State Channels
- Loading branch information
1 parent
b5eaa85
commit dd3c275
Showing
33 changed files
with
2,673 additions
and
29 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,2 @@ | ||
export * from './message-nonce-less-than-decider' | ||
export * from './utils' |
42 changes: 42 additions & 0 deletions
42
packages/core/src/app/ovm/deciders/examples/message-nonce-less-than-decider.ts
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,42 @@ | ||
import { Decider, Decision, ImplicationProofItem } from '../../../../types/ovm' | ||
import { ParsedMessage } from '../../../../types/serialization' | ||
import { BigNumber } from '../../../utils' | ||
|
||
export interface MessageNonceLessThanInput { | ||
messageWithNonce: ParsedMessage | ||
lessThanThis: BigNumber | ||
} | ||
|
||
/** | ||
* Decider that decides true iff the input message has a nonce less than the input nonce. | ||
*/ | ||
export class MessageNonceLessThanDecider implements Decider { | ||
private static _instance: MessageNonceLessThanDecider | ||
public static instance(): MessageNonceLessThanDecider { | ||
if (!MessageNonceLessThanDecider._instance) { | ||
MessageNonceLessThanDecider._instance = new MessageNonceLessThanDecider() | ||
} | ||
return MessageNonceLessThanDecider._instance | ||
} | ||
|
||
public async decide( | ||
input: MessageNonceLessThanInput, | ||
witness: undefined, | ||
noCache?: boolean | ||
): Promise<Decision> { | ||
const justification: ImplicationProofItem[] = [ | ||
{ | ||
implication: { | ||
decider: this, | ||
input, | ||
}, | ||
implicationWitness: witness, | ||
}, | ||
] | ||
|
||
return { | ||
outcome: input.messageWithNonce.message.nonce.lt(input.lessThanThis), | ||
justification, | ||
} | ||
} | ||
} |
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,29 @@ | ||
import { ParsedMessage } from '../../../../types/serialization' | ||
import { objectsEqual } from '../../../utils' | ||
|
||
export class Utils { | ||
/** | ||
* Determines whether or not the provided ParsedMessages conflict. | ||
* Conflicting messages have the same channelID and nonce but different data. | ||
* | ||
* @param message The first message | ||
* @param other The second message | ||
* @returns True if they conflict, false otherwise | ||
*/ | ||
public static stateChannelMessagesConflict( | ||
message: ParsedMessage, | ||
other: ParsedMessage | ||
): boolean { | ||
return ( | ||
!!message && | ||
!!other && | ||
message.message.channelId.equals(other.message.channelId) && | ||
message.message.nonce.equals(other.message.nonce) && | ||
(message.sender.equals(other.sender) || | ||
message.sender.equals(other.recipient)) && | ||
(message.recipient.equals(other.recipient) || | ||
message.recipient.equals(other.sender)) && | ||
!objectsEqual(message.message.data, other.message.data) | ||
) | ||
} | ||
} |
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
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,122 @@ | ||
import { | ||
Decider, | ||
Decision, | ||
ImplicationProofItem, | ||
Property, | ||
} from '../../../types/ovm' | ||
import { CannotDecideError } from './utils' | ||
|
||
export interface OrDeciderInput { | ||
properties: Property[] | ||
witnesses: any[] | ||
} | ||
|
||
/** | ||
* Decider that decides true if any of the provided properties evaluate to true. | ||
*/ | ||
export class OrDecider implements Decider { | ||
private static _instance: OrDecider | ||
|
||
public static instance(): OrDecider { | ||
if (!OrDecider._instance) { | ||
OrDecider._instance = new OrDecider() | ||
} | ||
return OrDecider._instance | ||
} | ||
|
||
public async decide( | ||
input: OrDeciderInput, | ||
witness?: undefined, | ||
noCache?: boolean | ||
): Promise<Decision> { | ||
const decisions: Decision[] = await Promise.all( | ||
input.properties.map((property: Property, index: number) => { | ||
return this.decideWithoutThrowingCannotDecide( | ||
property, | ||
input.witnesses[index], | ||
noCache | ||
) | ||
}) | ||
) | ||
|
||
let trueDecision: Decision | ||
let cannotDecide: boolean = false | ||
const falseJustifications: ImplicationProofItem[] = [] | ||
for (const decision of decisions) { | ||
if (!decision) { | ||
cannotDecide = true | ||
continue | ||
} | ||
if (decision.outcome) { | ||
trueDecision = decision | ||
break | ||
} else { | ||
falseJustifications.push(...decision.justification) | ||
} | ||
} | ||
|
||
if (trueDecision) { | ||
return this.getDecision(input, trueDecision) | ||
} | ||
|
||
if (cannotDecide) { | ||
throw new CannotDecideError( | ||
'At least one of the OR deciders could not decide and none returned true, so this cannot be decided.' | ||
) | ||
} | ||
|
||
return this.getDecision(input, { | ||
outcome: false, | ||
justification: falseJustifications, | ||
}) | ||
} | ||
|
||
/** | ||
* Calls decide on the provided Property's Decider with the appropriate input and catches | ||
* CannotDecideError, returning undefined if it occurs. | ||
* | ||
* @param property the Property with the Decider to decide and the input to pass it | ||
* @param witness the witness for the Decider | ||
* @param noCache whether or not to use the cache if one is available for previous decisions | ||
*/ | ||
private async decideWithoutThrowingCannotDecide( | ||
property: Property, | ||
witness: any, | ||
noCache: boolean | ||
): Promise<Decision> { | ||
try { | ||
return await property.decider.decide(property.input, witness, noCache) | ||
} catch (e) { | ||
if (e instanceof CannotDecideError) { | ||
return undefined | ||
} | ||
throw e | ||
} | ||
} | ||
|
||
/** | ||
* Gets the Decision that results from invocation of the Or decider, which simply | ||
* returns true if any of the sub-Decisions returned true. | ||
* | ||
* @param input The input that led to the Decision | ||
* @param subDecision The decision of the wrapped Property, provided the witness | ||
* @returns The Decision | ||
*/ | ||
private getDecision(input: OrDeciderInput, subDecision: Decision): Decision { | ||
const justification: ImplicationProofItem[] = [ | ||
{ | ||
implication: { | ||
decider: this, | ||
input, | ||
}, | ||
implicationWitness: undefined, | ||
}, | ||
...subDecision.justification, | ||
] | ||
|
||
return { | ||
outcome: subDecision.outcome, | ||
justification, | ||
} | ||
} | ||
} |
Oops, something went wrong.