Exemple #1
0
def conditions_for_solution(
    puzzle_reveal: Program,
    solution: Program,
) -> Tuple[Optional[Err], Optional[List[ConditionVarPair]], uint64]:
    # get the standard script for a puzzle hash and feed in the solution
    try:
        cost, r = puzzle_reveal.run_with_cost(solution)
        error, result = parse_sexp_to_conditions(r)
        return error, result, uint64(cost)
    except Program.EvalError:
        return Err.SEXP_ERROR, None, uint64(0)
Exemple #2
0
def conditions_dict_for_solution(
    puzzle_reveal: Program,
    solution: Program,
) -> Tuple[Optional[Err], Optional[Dict[ConditionOpcode,
                                        List[ConditionVarPair]]], uint64]:
    error, result, cost = conditions_for_solution(puzzle_reveal, solution)
    if error or result is None:
        return error, None, uint64(0)
    return None, conditions_by_opcode(result), cost
Exemple #3
0
def calculate_cost_of_program(program: SerializedProgram,
                              clvm_cost_ratio_constant: int,
                              strict_mode: bool = False) -> CostResult:
    """
    This function calculates the total cost of either a block or a spendbundle
    """
    total_clvm_cost = 0
    error, npc_list, cost = get_name_puzzle_conditions(program, strict_mode)
    if error:
        raise Exception("get_name_puzzle_conditions raised error:" +
                        str(error))
    total_clvm_cost += cost

    # Add cost of conditions
    npc: NPC
    total_vbyte_cost = 0
    for npc in npc_list:
        for condition, cvp_list in npc.condition_dict.items():
            if condition is ConditionOpcode.AGG_SIG or condition is ConditionOpcode.AGG_SIG_ME:
                total_vbyte_cost += len(cvp_list) * ConditionCost.AGG_SIG.value
            elif condition is ConditionOpcode.CREATE_COIN:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.CREATE_COIN.value
            elif condition is ConditionOpcode.ASSERT_SECONDS_NOW_EXCEEDS:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_SECONDS_NOW_EXCEEDS.value
            elif condition is ConditionOpcode.ASSERT_HEIGHT_AGE_EXCEEDS:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_HEIGHT_AGE_EXCEEDS.value
            elif condition is ConditionOpcode.ASSERT_HEIGHT_NOW_EXCEEDS:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_HEIGHT_NOW_EXCEEDS.value
            elif condition is ConditionOpcode.ASSERT_MY_COIN_ID:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_MY_COIN_ID.value
            elif condition is ConditionOpcode.RESERVE_FEE:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.RESERVE_FEE.value
            elif condition is ConditionOpcode.CREATE_ANNOUNCEMENT:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.CREATE_ANNOUNCEMENT.value
            elif condition is ConditionOpcode.ASSERT_ANNOUNCEMENT:
                total_vbyte_cost += len(
                    cvp_list) * ConditionCost.ASSERT_ANNOUNCEMENT.value
            else:
                # We ignore unknown conditions in order to allow for future soft forks
                pass

    # Add raw size of the program
    total_vbyte_cost += len(bytes(program))

    total_clvm_cost += total_vbyte_cost * clvm_cost_ratio_constant

    return CostResult(error, npc_list, uint64(total_clvm_cost))
def mempool_assert_relative_time_exceeds(condition: ConditionVarPair, unspent: CoinRecord):
    """
    Check if the current time in millis exceeds the time specified by condition
    """
    try:
        expected_mili_time = int_from_bytes(condition.vars[0])
    except ValueError:
        return Err.INVALID_CONDITION

    current_time = uint64(int(time.time() * 1000))
    if current_time <= expected_mili_time + unspent.timestamp:
        return Err.ASSERT_SECONDS_NOW_EXCEEDS_FAILED
    return None
def get_name_puzzle_conditions(block_program: SerializedProgram, safe_mode: bool):
    # TODO: allow generator mod to take something (future)
    # TODO: write more tests
    block_program_args = SerializedProgram.from_bytes(b"\x80")

    try:
        if safe_mode:
            cost, result = GENERATOR_MOD.run_safe_with_cost(block_program, block_program_args)
        else:
            cost, result = GENERATOR_MOD.run_with_cost(block_program, block_program_args)
        npc_list = []
        opcodes = set(item.value for item in ConditionOpcode)
        for res in result.as_iter():
            conditions_list = []
            name = std_hash(
                bytes(
                    res.first().first().as_atom()
                    + res.first().rest().first().as_atom()
                    + res.first().rest().rest().first().as_atom()
                )
            )
            puzzle_hash = bytes32(res.first().rest().first().as_atom())
            for cond in res.rest().first().as_iter():
                if cond.first().as_atom() in opcodes:
                    opcode = ConditionOpcode(cond.first().as_atom())
                elif not safe_mode:
                    opcode = ConditionOpcode.UNKNOWN
                else:
                    return "Unknown operator in safe mode.", None, None
                if len(list(cond.as_iter())) > 1:
                    cond_var_list = []
                    for cond_1 in cond.rest().as_iter():
                        cond_var_list.append(cond_1.as_atom())
                    cvl = ConditionVarPair(opcode, cond_var_list)
                else:
                    cvl = ConditionVarPair(opcode, [])
                conditions_list.append(cvl)
            conditions_dict = conditions_by_opcode(conditions_list)
            if conditions_dict is None:
                conditions_dict = {}
            npc_list.append(NPC(name, puzzle_hash, [(a, b) for a, b in conditions_dict.items()]))
        return None, npc_list, uint64(cost)
    except Exception:
        tb = traceback.format_exc()
        return tb, None, None
Exemple #6
0
def calculate_pool_reward(height: uint32) -> uint64:
    """
    Returns the pool reward at a certain block height. The pool earns 7/8 of the reward in each block. If the farmer
    is solo farming, they act as the pool, and therefore earn the entire block reward.
    These halving events will not be hit at the exact times
    (3 years, etc), due to fluctuations in difficulty. They will likely come early, if the network space and VDF
    rates increase continuously.
    """

    if height == 0:
        return uint64(int((7 / 8) * 21000000 * _mojo_per_chia))
    elif height < 3 * _blocks_per_year:
        return uint64(int((7 / 8) * 2 * _mojo_per_chia))
    elif height < 6 * _blocks_per_year:
        return uint64(int((7 / 8) * 1 * _mojo_per_chia))
    elif height < 9 * _blocks_per_year:
        return uint64(int((7 / 8) * 0.5 * _mojo_per_chia))
    elif height < 12 * _blocks_per_year:
        return uint64(int((7 / 8) * 0.25 * _mojo_per_chia))
    else:
        return uint64(int((7 / 8) * 0.125 * _mojo_per_chia))
Exemple #7
0
def calculate_base_farmer_reward(height: uint32) -> uint64:
    """
    Returns the base farmer reward at a certain block height.
    The base fee reward is 1/8 of total block reward

    Returns the coinbase reward at a certain block height. These halving events will not be hit at the exact times
    (3 years, etc), due to fluctuations in difficulty. They will likely come early, if the network space and VDF
    rates increase continuously.
    """
    if height == 0:
        return uint64(int((1 / 8) * 21000000 * _mojo_per_chia))
    elif height < 3 * _blocks_per_year:
        return uint64(int((1 / 8) * 2 * _mojo_per_chia))
    elif height < 6 * _blocks_per_year:
        return uint64(int((1 / 8) * 1 * _mojo_per_chia))
    elif height < 9 * _blocks_per_year:
        return uint64(int((1 / 8) * 0.5 * _mojo_per_chia))
    elif height < 12 * _blocks_per_year:
        return uint64(int((1 / 8) * 0.25 * _mojo_per_chia))
    else:
        return uint64(int((1 / 8) * 0.125 * _mojo_per_chia))
Exemple #8
0
def float_to_timestamp(time: float) -> uint64:
    return uint64(int(time))
Exemple #9
0
 # Used as the initial cc rc challenges, as well as first block back pointers, and first SES back pointer
 # We override this value based on the chain being run (testnet0, testnet1, mainnet, etc)
 "GENESIS_CHALLENGE": bytes32([0x00] * 32),
 "GENESIS_PRE_FARM_POOL_PUZZLE_HASH": bytes.fromhex(
     "bc4fd6c394fe90c6097afbfa6ab5927743e2210ecf689dad477be8eb408745d5"
 ),
 "GENESIS_PRE_FARM_FARMER_PUZZLE_HASH": bytes.fromhex(
     "4ad98e66a019f11b60a8279514d13105b65588865e2a8e794b5b73d5646174f6"
 ),
 "MAX_VDF_WITNESS_SIZE": 64,
 # Target tx count per sec
 "TX_PER_SEC": 20,
 # Size of mempool = 10x the size of block
 "MEMPOOL_BLOCK_BUFFER": 10,
 # Max coin amount, fits into 64 bits
 "MAX_COIN_AMOUNT": uint64((1 << 64) - 1),
 # Targeting twice bitcoin's block size of 1.3MB per block
 # Raw size per block target = 1,300,000 * 600 / 47 = approx 100 KB
 # Rax TX (single in, single out) = 219 bytes (not compressed)
 # TX = 457 vBytes
 # floor(100 * 1024 / 219) * 457 = 213684 (size in vBytes)
 # Max block cost in virtual bytes
 "MAX_BLOCK_COST": 213684,
 # MAX block cost in clvm cost units = MAX_BLOCK_COST * CLVM_COST_RATIO_CONSTANT
 # 1 vByte = 108 clvm cost units
 "CLVM_COST_RATIO_CONSTANT": 108,
 # Max block cost in clvm cost units (MAX_BLOCK_COST * CLVM_COST_RATIO_CONSTANT)
 # "MAX_BLOCK_COST_CLVM": 23077872,
 "MAX_BLOCK_COST_CLVM": 40000000,  # Based on arvid analysis
 "WEIGHT_PROOF_THRESHOLD": 2,
 "BLOCKS_CACHE_SIZE": 4608 + (128 * 4),
Exemple #10
0
 def from_bytes(cls, blob):
     parent_coin_info = blob[:32]
     puzzle_hash = blob[32:64]
     amount = int_from_bytes(blob[64:])
     return Coin(parent_coin_info, puzzle_hash, uint64(amount))
Exemple #11
0
def validate_spendbundle(
    new_spend: SpendBundle,
    mempool_removals: List[Coin],
    current_coin_records: List[CoinRecord],
    block_height: uint32,
    validate_signature=True
) -> Tuple[Optional[uint64], MempoolInclusionStatus, Optional[Err]]:
    spend_name = new_spend.name()
    cost_result = CostResult.from_bytes(validate_transaction(bytes(new_spend)))

    npc_list = cost_result.npc_list
    cost = cost_result.cost

    if cost > DEFAULT_CONSTANTS.MAX_BLOCK_COST_CLVM:
        return None, MempoolInclusionStatus.FAILED, Err.BLOCK_COST_EXCEEDS_MAX

    if cost_result.error is not None:
        return None, MempoolInclusionStatus.FAILED, Err(cost_result.error)

    removal_names: List[bytes32] = new_spend.removal_names()
    additions = additions_for_npc(npc_list)

    additions_dict: Dict[bytes32, Coin] = {}
    for add in additions:
        additions_dict[add.name()] = add

    addition_amount = uint64(0)
    # Check additions for max coin amount
    for coin in additions:
        if coin.amount > DEFAULT_CONSTANTS.MAX_COIN_AMOUNT:
            return (
                None,
                MempoolInclusionStatus.FAILED,
                Err.COIN_AMOUNT_EXCEEDS_MAXIMUM,
            )
        addition_amount = uint64(addition_amount + coin.amount)

    # Check for duplicate outputs
    addition_counter = collections.Counter(_.name() for _ in additions)
    for k, v in addition_counter.items():
        if v > 1:
            return None, MempoolInclusionStatus.FAILED, Err.DUPLICATE_OUTPUT

    # Check for duplicate inputs
    removal_counter = collections.Counter(name for name in removal_names)
    for k, v in removal_counter.items():
        if v > 1:
            return None, MempoolInclusionStatus.FAILED, Err.DOUBLE_SPEND

    removal_record_dict: Dict[bytes32, CoinRecord] = {}
    removal_coin_dict: Dict[bytes32, Coin] = {}
    unknown_unspent_error: bool = False
    removal_amount = uint64(0)
    for name in removal_names:
        removal_record = list(
            filter(lambda e: e.coin.name() == name, current_coin_records))
        if len(removal_record) == 0:
            removal_record = None
        else:
            removal_record = removal_record[0]
        if removal_record is None and name not in additions_dict:
            unknown_unspent_error = True
            break
        elif name in additions_dict:
            removal_coin = additions_dict[name]
            # TODO(straya): what timestamp to use here?
            removal_record = CoinRecord(
                removal_coin,
                uint32(self.peak.height +
                       1),  # In mempool, so will be included in next height
                uint32(0),
                False,
                False,
                uint64(int(time.time())),
            )

        assert removal_record is not None
        removal_amount = uint64(removal_amount + removal_record.coin.amount)
        removal_record_dict[name] = removal_record
        removal_coin_dict[name] = removal_record.coin
    if unknown_unspent_error:
        return None, MempoolInclusionStatus.FAILED, Err.UNKNOWN_UNSPENT

    if addition_amount > removal_amount:
        return None, MempoolInclusionStatus.FAILED, Err.MINTING_COIN

    fees = removal_amount - addition_amount
    assert_fee_sum: uint64 = uint64(0)

    for npc in npc_list:
        if ConditionOpcode.RESERVE_FEE in npc.condition_dict:
            fee_list: List[ConditionVarPair] = npc.condition_dict[
                ConditionOpcode.RESERVE_FEE]
            for cvp in fee_list:
                fee = int_from_bytes(cvp.vars[0])
                assert_fee_sum = assert_fee_sum + fee
    if fees < assert_fee_sum:
        return (
            None,
            MempoolInclusionStatus.FAILED,
            Err.RESERVE_FEE_CONDITION_FAILED,
        )

    if cost == 0:
        return None, MempoolInclusionStatus.FAILED, Err.UNKNOWN

    # Use this information later when constructing a block
    fail_reason, conflicts = check_removals(mempool_removals,
                                            removal_record_dict)
    # If there is a mempool conflict check if this spendbundle has a higher fee per cost than all others
    tmp_error: Optional[Err] = None
    conflicting_pool_items: Dict[bytes32, Coin] = {}
    if fail_reason is Err.MEMPOOL_CONFLICT:
        for conflicting in conflicts:
            # sb: Coin = mempool_removals[conflicting.name()]
            conflicting_pool_items[conflicting.name()] = conflicting

        # for item in conflicting_pool_items.values():
        #     if item.fee_per_cost >= fees_per_cost:
        #         self.add_to_potential_tx_set(new_spend, spend_name, cost_result)
        print("The following items conflict with current mempool items: " +
              conflicting_pool_items)
        print(
            "This fails in the simulation, but the bigger fee_per_cost likely wins on the network"
        )
        return (
            uint64(cost),
            MempoolInclusionStatus.FAILED,
            Err.MEMPOOL_CONFLICT,
        )

    elif fail_reason:
        return None, MempoolInclusionStatus.FAILED, fail_reason

    if tmp_error:
        return None, MempoolInclusionStatus.FAILED, tmp_error

    # Verify conditions, create hash_key list for aggsig check
    pks: List[G1Element] = []
    msgs: List[bytes32] = []
    error: Optional[Err] = None
    for npc in npc_list:
        coin_record: CoinRecord = removal_record_dict[npc.coin_name]
        # Check that the revealed removal puzzles actually match the puzzle hash
        if npc.puzzle_hash != coin_record.coin.puzzle_hash:
            return None, MempoolInclusionStatus.FAILED, Err.WRONG_PUZZLE_HASH

        chialisp_height = block_height - 1
        error = mempool_check_conditions_dict(coin_record, new_spend,
                                              npc.condition_dict,
                                              uint32(chialisp_height))

        if error:
            if error is Err.ASSERT_HEIGHT_NOW_EXCEEDS_FAILED or error is Err.ASSERT_HEIGHT_AGE_EXCEEDS_FAILED:
                return uint64(cost), MempoolInclusionStatus.PENDING, error
            break

        if validate_signature:
            for pk, message in pkm_pairs_for_conditions_dict(
                    npc.condition_dict, npc.coin_name):
                pks.append(pk)
                msgs.append(message)

    if error:
        return None, MempoolInclusionStatus.FAILED, error

    if validate_signature:
        # Verify aggregated signature
        if not AugSchemeMPL.aggregate_verify(pks, msgs,
                                             new_spend.aggregated_signature):
            return None, MempoolInclusionStatus.FAILED, Err.BAD_AGGREGATE_SIGNATURE

    removals: List[Coin] = [coin for coin in removal_coin_dict.values()]
    # new_item = MempoolItem(new_spend, uint64(fees), cost_result, spend_name, additions, removals)
    # self.mempool.add_to_pool(new_item, additions, removal_coin_dict)
    # log.info(f"add_spendbundle took {time.time() - start_time} seconds")
    return uint64(cost), MempoolInclusionStatus.SUCCESS, None