@reality.eth/contracts 3.0.48 → 4.0.0-rc.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/abi/solc-0.8.20/RealityETH-4.0.abi.json +1 -1
- package/abi/solc-0.8.20/RealityETHReopenable-4.0.abi.json +1 -0
- package/abi/solc-0.8.20/RealityETHReopenable_ERC20-4.0.abi.json +1 -0
- package/abi/solc-0.8.20/RealityETH_ERC20-4.0.abi.json +1 -1
- package/bytecode/RealityETH-4.0.bin +1 -1
- package/bytecode/RealityETHReopenable-4.0.bin +1 -0
- package/bytecode/RealityETHReopenable_ERC20-4.0.bin +1 -0
- package/bytecode/RealityETH_ERC20-4.0.bin +1 -1
- package/chains/supported.json +1 -1
- package/development/contracts/IRealityETHCore.sol +0 -15
- package/development/contracts/IRealityETHCore_ERC20.sol +0 -16
- package/development/contracts/IRealityETHHistoryVerification.sol +8 -0
- package/development/contracts/IRealityETHReopenable.sol +22 -0
- package/development/contracts/IRealityETHReopenable_ERC20.sol +23 -0
- package/development/contracts/RealityETH-4.0.sol +50 -105
- package/development/contracts/RealityETHReopenable-4.0.sol +119 -0
- package/development/contracts/RealityETHReopenable_ERC20-4.0.sol +122 -0
- package/development/contracts/RealityETH_ERC20-4.0.sol +50 -108
- package/generated/chains.json +1 -1
- package/package.json +2 -2
- package/tests/python/test.py +81 -5
|
@@ -4,10 +4,11 @@ pragma solidity ^0.8.20;
|
|
|
4
4
|
|
|
5
5
|
import {IERC20} from "./IERC20.sol";
|
|
6
6
|
import {IRealityETHCore_ERC20} from "./IRealityETHCore_ERC20.sol";
|
|
7
|
+
import {IRealityETHHistoryVerification} from "./IRealityETHHistoryVerification.sol";
|
|
7
8
|
import {BalanceHolder_ERC20} from "./BalanceHolder_ERC20.sol";
|
|
8
9
|
|
|
9
10
|
// solhint-disable-next-line contract-name-camelcase
|
|
10
|
-
contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
|
|
11
|
+
contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20, IRealityETHHistoryVerification {
|
|
11
12
|
address private constant NULL_ADDRESS = address(0);
|
|
12
13
|
|
|
13
14
|
// History hash when no history is created, or history has been cleared
|
|
@@ -19,18 +20,12 @@ contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
|
|
|
19
20
|
// Proportion withheld when you claim an earlier bond.
|
|
20
21
|
uint256 private constant BOND_CLAIM_FEE_PROPORTION = 40; // One 40th ie 2.5%
|
|
21
22
|
|
|
22
|
-
// Special value representing a question that was answered too soon.
|
|
23
|
-
// bytes32(-2). By convention we use bytes32(-1) for "invalid", although the contract does not handle this.
|
|
24
|
-
bytes32 private constant UNRESOLVED_ANSWER = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe;
|
|
25
|
-
|
|
26
23
|
uint256 private nextTemplateID = 0;
|
|
27
24
|
mapping(uint256 => uint256) public templates;
|
|
28
25
|
mapping(uint256 => bytes32) public template_hashes;
|
|
29
26
|
mapping(bytes32 => Question) public questions;
|
|
30
27
|
mapping(bytes32 => Claim) public question_claims;
|
|
31
28
|
mapping(address => uint256) public arbitrator_question_fees;
|
|
32
|
-
mapping(bytes32 => bytes32) public reopened_questions;
|
|
33
|
-
mapping(bytes32 => bool) public reopener_questions;
|
|
34
29
|
|
|
35
30
|
modifier onlyArbitrator(bytes32 question_id) {
|
|
36
31
|
if (msg.sender != questions[question_id].arbitrator) revert MsgSenderMustBeArbitrator();
|
|
@@ -457,7 +452,9 @@ contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
|
|
|
457
452
|
/// @param last_answer The last answer given
|
|
458
453
|
/// @param last_answerer The address that supplied the last answer
|
|
459
454
|
function assignWinnerAndSubmitAnswerByArbitrator(bytes32 question_id, bytes32 answer, address payee_if_wrong, bytes32 last_history_hash, bytes32 last_answer, address last_answerer) external {
|
|
460
|
-
|
|
455
|
+
if (!_isHistoryInputValidForHash(questions[question_id].history_hash, last_history_hash, last_answer, questions[question_id].bond, last_answerer)) {
|
|
456
|
+
revert HistoryInputProvidedDidNotMatchTheExpectedHash();
|
|
457
|
+
}
|
|
461
458
|
|
|
462
459
|
address payee = (questions[question_id].best_answer == answer) ? last_answerer : payee_if_wrong;
|
|
463
460
|
submitAnswerByArbitrator(question_id, answer, payee);
|
|
@@ -485,100 +482,6 @@ contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
|
|
|
485
482
|
return questions[question_id].best_answer;
|
|
486
483
|
}
|
|
487
484
|
|
|
488
|
-
/// @notice Returns whether the question was answered before it had an answer, ie resolved to UNRESOLVED_ANSWER
|
|
489
|
-
/// @param question_id The ID of the question
|
|
490
|
-
function isSettledTooSoon(bytes32 question_id) public view returns (bool) {
|
|
491
|
-
return (resultFor(question_id) == UNRESOLVED_ANSWER);
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
/// @notice Like resultFor(), but errors out if settled too soon, or returns the result of a replacement if it was reopened at the right time and settled
|
|
495
|
-
/// @param question_id The ID of the question
|
|
496
|
-
function resultForOnceSettled(bytes32 question_id) external view returns (bytes32) {
|
|
497
|
-
bytes32 result = resultFor(question_id);
|
|
498
|
-
if (result == UNRESOLVED_ANSWER) {
|
|
499
|
-
// Try the replacement
|
|
500
|
-
bytes32 replacement_id = reopened_questions[question_id];
|
|
501
|
-
if (replacement_id == bytes32(0x0)) revert QuestionWasSettledTooSoonAndHasNotBeenReopened();
|
|
502
|
-
// We only try one layer down rather than recursing to keep the gas costs predictable
|
|
503
|
-
result = resultFor(replacement_id);
|
|
504
|
-
if (result == UNRESOLVED_ANSWER) revert QuestionReplacementWasSettledTooSoonAndHasNotBeenReopened();
|
|
505
|
-
}
|
|
506
|
-
return result;
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
/// @notice Asks a new question reopening a previously-asked question that was settled too soon
|
|
510
|
-
/// @dev A special version of askQuestion() that replaces a previous question that was settled too soon
|
|
511
|
-
/// @param template_id The ID number of the template the question will use
|
|
512
|
-
/// @param question A string containing the parameters that will be passed into the template to make the question
|
|
513
|
-
/// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute
|
|
514
|
-
/// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer
|
|
515
|
-
/// @param opening_ts If set, the earliest time it should be possible to answer the question.
|
|
516
|
-
/// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question.
|
|
517
|
-
/// @param min_bond The minimum bond that can be used to provide the first answer.
|
|
518
|
-
/// @param reopens_question_id The ID of the question this reopens
|
|
519
|
-
/// @param tokens The number of tokens you want to use as an additional question reward for the reopened question.
|
|
520
|
-
/// @return The ID of the newly-created question, created deterministically.
|
|
521
|
-
function reopenQuestionERC20(
|
|
522
|
-
uint256 template_id,
|
|
523
|
-
string memory question,
|
|
524
|
-
address arbitrator,
|
|
525
|
-
uint32 timeout,
|
|
526
|
-
uint32 opening_ts,
|
|
527
|
-
uint256 nonce,
|
|
528
|
-
uint256 min_bond,
|
|
529
|
-
bytes32 reopens_question_id,
|
|
530
|
-
uint256 tokens
|
|
531
|
-
)
|
|
532
|
-
public
|
|
533
|
-
returns (
|
|
534
|
-
// stateNotCreated is enforced by the internal _askQuestion
|
|
535
|
-
bytes32
|
|
536
|
-
)
|
|
537
|
-
{
|
|
538
|
-
// _deductTokensOrRevert will be called when we call askQuestionWithMinBondERC20
|
|
539
|
-
|
|
540
|
-
if (!isSettledTooSoon(reopens_question_id)) revert YouCanOnlyReopenQuestionsThatResolvedAsSettledTooSoon();
|
|
541
|
-
|
|
542
|
-
bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question));
|
|
543
|
-
|
|
544
|
-
// A reopening must exactly match the original question, except for the nonce and the creator
|
|
545
|
-
if (content_hash != questions[reopens_question_id].content_hash) revert ContentHashMismatch();
|
|
546
|
-
if (arbitrator != questions[reopens_question_id].arbitrator) revert ArbitratorMismatch();
|
|
547
|
-
if (timeout != questions[reopens_question_id].timeout) revert TimeoutMismatch();
|
|
548
|
-
if (opening_ts != questions[reopens_question_id].opening_ts) revert Opening_TsMismatch();
|
|
549
|
-
if (min_bond != questions[reopens_question_id].min_bond) revert Min_BondMismatch();
|
|
550
|
-
|
|
551
|
-
// If the the question was itself reopening some previous question, you'll have to re-reopen the previous question first.
|
|
552
|
-
// This ensures the bounty can be passed on to the next attempt of the original question.
|
|
553
|
-
if (reopener_questions[reopens_question_id]) revert QuestionIsAlreadyReopeningAPreviousQuestion();
|
|
554
|
-
|
|
555
|
-
// A question can only be reopened once, unless the reopening was also settled too soon in which case it can be replaced
|
|
556
|
-
bytes32 existing_reopen_question_id = reopened_questions[reopens_question_id];
|
|
557
|
-
|
|
558
|
-
// Normally when we reopen a question we will take its bounty and pass it on to the reopened version.
|
|
559
|
-
bytes32 take_bounty_from_question_id = reopens_question_id;
|
|
560
|
-
// If the question has already been reopened but was again settled too soon, we can transfer its bounty to the next attempt.
|
|
561
|
-
if (existing_reopen_question_id != bytes32(0)) {
|
|
562
|
-
if (!isSettledTooSoon(existing_reopen_question_id)) revert QuestionHasAlreadyBeenReopened();
|
|
563
|
-
// We'll overwrite the reopening with our new question and move the bounty.
|
|
564
|
-
// Once that's done we'll detach the failed reopener and you'll be able to reopen that too if you really want, but without the bounty.
|
|
565
|
-
reopener_questions[existing_reopen_question_id] = false;
|
|
566
|
-
take_bounty_from_question_id = existing_reopen_question_id;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
bytes32 question_id = askQuestionWithMinBondERC20(template_id, question, arbitrator, timeout, opening_ts, nonce, min_bond, tokens);
|
|
570
|
-
|
|
571
|
-
reopened_questions[reopens_question_id] = question_id;
|
|
572
|
-
reopener_questions[question_id] = true;
|
|
573
|
-
|
|
574
|
-
questions[question_id].bounty = questions[take_bounty_from_question_id].bounty + questions[question_id].bounty;
|
|
575
|
-
questions[take_bounty_from_question_id].bounty = 0;
|
|
576
|
-
|
|
577
|
-
emit LogReopenQuestion(question_id, reopens_question_id);
|
|
578
|
-
|
|
579
|
-
return question_id;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
485
|
/// @notice Return the final answer to the specified question, provided it matches the specified criteria.
|
|
583
486
|
/// @dev Reverts if the question is not finalized, or if it does not match the specified criteria.
|
|
584
487
|
/// @param question_id The ID of the question
|
|
@@ -625,7 +528,9 @@ contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
|
|
|
625
528
|
|
|
626
529
|
uint256 i;
|
|
627
530
|
for (i = 0; i < history_hashes.length; i++) {
|
|
628
|
-
|
|
531
|
+
if (!_isHistoryInputValidForHash(last_history_hash, history_hashes[i], answers[i], bonds[i], addrs[i])) {
|
|
532
|
+
revert HistoryInputProvidedDidNotMatchTheExpectedHash();
|
|
533
|
+
}
|
|
629
534
|
|
|
630
535
|
queued_funds = queued_funds + last_bond;
|
|
631
536
|
(queued_funds, payee) = _processHistoryItem(question_id, best_answer, queued_funds, payee, addrs[i], bonds[i], answers[i]);
|
|
@@ -665,10 +570,13 @@ contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
|
|
|
665
570
|
emit LogClaim(question_id, payee, value);
|
|
666
571
|
}
|
|
667
572
|
|
|
668
|
-
function
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
573
|
+
function _isHistoryInputValidForHash(bytes32 last_history_hash, bytes32 history_hash, bytes32 answer, uint256 bond, address addr) internal pure returns (bool) {
|
|
574
|
+
return (last_history_hash == keccak256(abi.encodePacked(history_hash, answer, bond, addr, false)));
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// The answered-too-soon version will override this with (answer != UNRESOLVED_ANSWER)
|
|
578
|
+
function _isBountyPayableOnAnswer(bytes32) internal pure virtual returns (bool) {
|
|
579
|
+
return true;
|
|
672
580
|
}
|
|
673
581
|
|
|
674
582
|
function _processHistoryItem(bytes32 question_id, bytes32 best_answer, uint256 queued_funds, address payee, address addr, uint256 bond, bytes32 answer) internal returns (uint256, address) {
|
|
@@ -678,7 +586,7 @@ contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
|
|
|
678
586
|
// They get the question bounty.
|
|
679
587
|
payee = addr;
|
|
680
588
|
|
|
681
|
-
if (
|
|
589
|
+
if (questions[question_id].bounty > 0 && _isBountyPayableOnAnswer(best_answer)) {
|
|
682
590
|
_payPayee(question_id, payee, questions[question_id].bounty);
|
|
683
591
|
questions[question_id].bounty = 0;
|
|
684
592
|
}
|
|
@@ -746,6 +654,40 @@ contract RealityETH_ERC20_v4_0 is BalanceHolder_ERC20, IRealityETHCore_ERC20 {
|
|
|
746
654
|
withdraw();
|
|
747
655
|
}
|
|
748
656
|
|
|
657
|
+
/// @notice Returns true if the supplied history is valid
|
|
658
|
+
/// @dev Caller must provide the answer history, in reverse order back to the item they want to check
|
|
659
|
+
/// @dev Not necessarily the entire history
|
|
660
|
+
/// @dev Useful for freezing an action once a bond is paid for a particular answer, without waiting for resolution
|
|
661
|
+
/// @dev Cannot be used after the question is finalized
|
|
662
|
+
/// @param question_id The ID of the question
|
|
663
|
+
/// @param history_hashes Second-last-to-first, the hash of each history entry. (Final one should be empty).
|
|
664
|
+
/// @param addrs Last-to-first, the address of each answerer
|
|
665
|
+
/// @param bonds Last-to-first, the bond supplied with each answer
|
|
666
|
+
/// @param answers Last-to-first, each answer supplied
|
|
667
|
+
function isHistoryOfUnfinalizedQuestionValid(
|
|
668
|
+
bytes32 question_id,
|
|
669
|
+
bytes32[] memory history_hashes,
|
|
670
|
+
address[] memory addrs,
|
|
671
|
+
uint256[] memory bonds,
|
|
672
|
+
bytes32[] memory answers
|
|
673
|
+
) external view stateOpenOrPendingArbitration(question_id) returns (bool) {
|
|
674
|
+
bytes32 last_history_hash = questions[question_id].history_hash;
|
|
675
|
+
|
|
676
|
+
uint256 hist_len = history_hashes.length;
|
|
677
|
+
// Check for uneven length entries to make sure we validate all the inputs
|
|
678
|
+
if (addrs.length != hist_len || bonds.length != hist_len || answers.length != hist_len) {
|
|
679
|
+
return false;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
for (uint256 i = 0; i < hist_len; i++) {
|
|
683
|
+
if (!_isHistoryInputValidForHash(last_history_hash, history_hashes[i], answers[i], bonds[i], addrs[i])) {
|
|
684
|
+
return false;
|
|
685
|
+
}
|
|
686
|
+
last_history_hash = history_hashes[i];
|
|
687
|
+
}
|
|
688
|
+
return true;
|
|
689
|
+
}
|
|
690
|
+
|
|
749
691
|
/// @notice Returns the questions's content hash, identifying the question content
|
|
750
692
|
/// @param question_id The ID of the question
|
|
751
693
|
function getContentHash(bytes32 question_id) public view returns (bytes32) {
|
package/generated/chains.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reality.eth/contracts",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0-rc.0",
|
|
4
4
|
"description": "Collection of smart contracts for the Realitio fact verification platform",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"generate-chains": "node scripts/generate_chains_json.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"url": "https://github.com/RealityETH/monorepo/issues"
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://reality.eth.link",
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "03e4a48bc6ffcb5d327c5503fa1afb2eba3f97fb",
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"ethers": "^5.6.8"
|
|
40
40
|
}
|
package/tests/python/test.py
CHANGED
|
@@ -43,12 +43,27 @@ if VERNUM >= 2.1:
|
|
|
43
43
|
else:
|
|
44
44
|
CLAIM_FEE = 0
|
|
45
45
|
|
|
46
|
+
if VERNUM == 3.0:
|
|
47
|
+
IS_ANSWERED_TOO_SOON_SUPPORTED = True
|
|
48
|
+
elif VERNUM >= 4.0:
|
|
49
|
+
if "Reopenable" in REALITYETH_CONTRACT:
|
|
50
|
+
IS_ANSWERED_TOO_SOON_SUPPORTED = True
|
|
51
|
+
else:
|
|
52
|
+
IS_ANSWERED_TOO_SOON_SUPPORTED = False
|
|
53
|
+
else:
|
|
54
|
+
# TODO: Add special version
|
|
55
|
+
IS_ANSWERED_TOO_SOON_SUPPORTED = False
|
|
56
|
+
|
|
46
57
|
if VERNUM >= 4.0:
|
|
47
58
|
IS_COMMIT_REVEAL_SUPPORTED = False
|
|
59
|
+
IS_PROVE_HISTORY_SUPPORTED = True
|
|
48
60
|
else:
|
|
49
61
|
IS_COMMIT_REVEAL_SUPPORTED = True
|
|
62
|
+
IS_PROVE_HISTORY_SUPPORTED = False
|
|
50
63
|
|
|
51
64
|
print("IS_COMMIT_REVEAL_SUPPORTED is "+str(IS_COMMIT_REVEAL_SUPPORTED))
|
|
65
|
+
print("IS_ANSWERED_TOO_SOON_SUPPORTED is "+str(IS_ANSWERED_TOO_SOON_SUPPORTED))
|
|
66
|
+
print("CLAIM_FEE is "+str(CLAIM_FEE))
|
|
52
67
|
|
|
53
68
|
DEPLOY_GAS = 8000000
|
|
54
69
|
|
|
@@ -2406,7 +2421,7 @@ class TestRealitio(TestCase):
|
|
|
2406
2421
|
@unittest.skipIf(WORKING_ONLY, "Not under construction")
|
|
2407
2422
|
def test_reopen_question(self):
|
|
2408
2423
|
|
|
2409
|
-
if
|
|
2424
|
+
if not IS_ANSWERED_TOO_SOON_SUPPORTED:
|
|
2410
2425
|
print("Skipping test_reopen_question, not a feature of this contract")
|
|
2411
2426
|
return
|
|
2412
2427
|
|
|
@@ -2552,7 +2567,7 @@ class TestRealitio(TestCase):
|
|
|
2552
2567
|
@unittest.skipIf(WORKING_ONLY, "Not under construction")
|
|
2553
2568
|
def test_too_soon_bounty(self):
|
|
2554
2569
|
|
|
2555
|
-
if
|
|
2570
|
+
if not IS_ANSWERED_TOO_SOON_SUPPORTED:
|
|
2556
2571
|
print("Skipping test_reopen_question, not a feature of this contract")
|
|
2557
2572
|
return
|
|
2558
2573
|
|
|
@@ -2590,8 +2605,8 @@ class TestRealitio(TestCase):
|
|
|
2590
2605
|
print("Skipping test_too_soon_bonds_under_unrevealed_commit, contract does not support commit-reveal")
|
|
2591
2606
|
return
|
|
2592
2607
|
|
|
2593
|
-
if
|
|
2594
|
-
print("Skipping
|
|
2608
|
+
if not IS_ANSWERED_TOO_SOON_SUPPORTED:
|
|
2609
|
+
print("Skipping test_too_soon_bonds_under_unrevealed_commit , not a feature of this contract")
|
|
2595
2610
|
return
|
|
2596
2611
|
|
|
2597
2612
|
k0 = self.web3.eth.accounts[0]
|
|
@@ -2624,13 +2639,74 @@ class TestRealitio(TestCase):
|
|
|
2624
2639
|
|
|
2625
2640
|
self.assertEqual(self.rc0.functions.balanceOf(k3).call(), claimable)
|
|
2626
2641
|
|
|
2642
|
+
#@unittest.skipIf(WORKING_ONLY, "Not under construction")
|
|
2643
|
+
def test_is_history_of_unfinalized_question_valid(self):
|
|
2644
|
+
|
|
2645
|
+
if not IS_PROVE_HISTORY_SUPPORTED:
|
|
2646
|
+
print("Skipping test_is_history_of_unfinalized_question_valid, not supported by this version")
|
|
2647
|
+
return
|
|
2648
|
+
|
|
2649
|
+
k2 = self.web3.eth.accounts[2]
|
|
2650
|
+
k3 = self.web3.eth.accounts[3]
|
|
2651
|
+
k4 = self.web3.eth.accounts[4]
|
|
2652
|
+
|
|
2653
|
+
if ERC20:
|
|
2654
|
+
self._issueTokens(k3, 100000, 50000)
|
|
2655
|
+
self._issueTokens(k4, 100000, 50000)
|
|
2656
|
+
|
|
2657
|
+
st = None
|
|
2658
|
+
st = self.submitAnswerReturnUpdatedState( st, self.question_id, 1001, 0, 20, k3)
|
|
2659
|
+
st = self.submitAnswerReturnUpdatedState( st, self.question_id, 1002, 20, 40, k3)
|
|
2660
|
+
st = self.submitAnswerReturnUpdatedState( st, self.question_id, 1001, 40, 80, k4)
|
|
2661
|
+
st = self.submitAnswerReturnUpdatedState( st, self.question_id, 1004, 80, 160, k3)
|
|
2662
|
+
st = self.submitAnswerReturnUpdatedState( st, self.question_id, 1003, 160, 320, k4)
|
|
2663
|
+
st = self.submitAnswerReturnUpdatedState( st, self.question_id, 1001, 320, 640, k4)
|
|
2664
|
+
|
|
2665
|
+
hashes = st['hash']
|
|
2666
|
+
addrs = st['addr']
|
|
2667
|
+
bonds = st['bond']
|
|
2668
|
+
answers = st['answer']
|
|
2627
2669
|
|
|
2628
|
-
|
|
2670
|
+
self.assertTrue(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes, addrs, bonds, answers).call())
|
|
2629
2671
|
|
|
2672
|
+
bad_hashes = hashes.copy()
|
|
2673
|
+
bad_hashes[2] = bad_hashes[1]
|
|
2674
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, bad_hashes, addrs, bonds, answers).call())
|
|
2630
2675
|
|
|
2676
|
+
bad_addrs = addrs.copy()
|
|
2677
|
+
bad_addrs[2] = k2
|
|
2678
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes, bad_addrs, bonds, answers).call())
|
|
2631
2679
|
|
|
2680
|
+
bad_bonds = bonds.copy()
|
|
2681
|
+
bad_bonds[1] = 987
|
|
2682
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes, addrs, bad_bonds, answers).call())
|
|
2632
2683
|
|
|
2684
|
+
bad_answers = answers.copy()
|
|
2685
|
+
bad_answers[1] = to_answer_for_contract(987)
|
|
2686
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes, addrs, bonds, bad_answers).call())
|
|
2633
2687
|
|
|
2688
|
+
# Supplying just the end of the list (lowest-bond answers) will fail
|
|
2689
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes[3:], addrs[3:], bonds[3:], answers[3:]).call())
|
|
2690
|
+
|
|
2691
|
+
# Supplying just the start of the list (highest-bond answers) will work
|
|
2692
|
+
self.assertTrue(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes[:3], addrs[:3], bonds[:3], answers[:3]).call())
|
|
2693
|
+
|
|
2694
|
+
# Any unevenness in the length of parameters passed will fail
|
|
2695
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes[:4], addrs[:3], bonds[:3], answers[:3]).call())
|
|
2696
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes[:3], addrs[:4], bonds[:3], answers[:3]).call())
|
|
2697
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes[:3], addrs[:3], bonds[:4], answers[:3]).call())
|
|
2698
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes[:3], addrs[:3], bonds[:3], answers[:4]).call())
|
|
2699
|
+
|
|
2700
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes[:2], addrs[:3], bonds[:3], answers[:3]).call())
|
|
2701
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes[:3], addrs[:2], bonds[:3], answers[:3]).call())
|
|
2702
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes[:3], addrs[:3], bonds[:2], answers[:3]).call())
|
|
2703
|
+
self.assertFalse(self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, hashes[:3], addrs[:3], bonds[:3], answers[:2]).call())
|
|
2704
|
+
|
|
2705
|
+
self._advance_clock(33)
|
|
2706
|
+
|
|
2707
|
+
# Once we finalize this will revert
|
|
2708
|
+
with self.assertRaises(TransactionFailed):
|
|
2709
|
+
self.rc0.functions.isHistoryOfUnfinalizedQuestionValid(self.question_id, st['hash'], st['addr'], st['bond'], st['answer']).transact()
|
|
2634
2710
|
|
|
2635
2711
|
if __name__ == '__main__':
|
|
2636
2712
|
main()
|