[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/javascriptit/PacketCrypt/develop/src/Validate.c [Back]  [Original]

/**
 * (C) Copyright 2019
 * Caleb James DeLisle
 *
 * SPDX-License-Identifier: (LGPL-2.1-only OR LGPL-3.0-only)
 */
#include "packetcrypt/Validate.h"
#include "Buf.h"
#include "Announce.h"
#include "packetcrypt/PacketCrypt.h"
#include "PacketCryptProof.h"
#include "CryptoCycle.h"
#include "Conf.h"
#include "Difficulty.h"
#include "Work.h"
#include "Util.h"
#include "ContentMerkle.h"
#include "config.h"

#include "sodium/crypto_sign_ed25519.h"

int Validate_checkAnn(
    uint8_t annHashOut[32],
    const PacketCrypt_Announce_t* pcAnn,
    const uint8_t* parentBlockHash,
    PacketCrypt_ValidateCtx_t* vctx)
{
    Announce_t _ann;
    Announce_t* ann = (Announce_t*) pcAnn;
    Buf_OBJCPY(&_ann.hdr, &ann->hdr);
    memcpy(&_ann.merkleProof.thirtytwos[0], parentBlockHash, 32);
    Buf_OBJSET(&_ann.merkleProof.thirtytwos[1], 0);
    Buf_OBJSET(_ann.hdr.softNonce, 0);

    Buf64_t annHash0;
    Hash_compress64(annHash0.bytes, (uint8_t*)&_ann,
        (sizeof _ann.hdr + sizeof _ann.merkleProof.sixtyfours[0]));

    Buf_OBJCPY(&_ann.merkleProof.sixtyfours[0], &ann->merkleProof.sixtyfours[13]);

    Buf64_t annHash1;
    Hash_compress64(annHash1.bytes, (uint8_t*)&_ann,
        (sizeof _ann.hdr + sizeof _ann.merkleProof.sixtyfours[0]));

    CryptoCycle_Item_t item;
    CryptoCycle_State_t state;
    uint32_t softNonce = PacketCrypt_AnnounceHdr_softNonce(&ann->hdr);

    Buf64_t v1Seed[2];
    int randHashCycles = Conf_AnnHash_RANDHASH_CYCLES;
    if (ann->hdr.version > 0) {
        randHashCycles = 0;
        uint32_t softNonceMax = Util_annSoftNonceMax(ann->hdr.workBits);
        if (softNonce > softNonceMax) {
            return Validate_checkAnn_SOFT_NONCE_HIGH;
        }
        Buf_OBJCPY(&v1Seed[0], &ann->merkleProof.sixtyfours[Announce_MERKLE_DEPTH]);
        Buf_OBJCPY(&v1Seed[1], &annHash0);
        Hash_COMPRESS64_OBJ(&v1Seed[0], &v1Seed);
        Announce_createProg(vctx, &v1Seed[0].thirtytwos[0]);
    }

    CryptoCycle_init(&state, &annHash1.thirtytwos[0], softNonce);
    int itemNo = -1;
    for (int i = 0; i < 4; i++) {
        itemNo = (CryptoCycle_getItemNo(&state) % Announce_TABLE_SZ);
        if (ann->hdr.version > 0) {
            Announce_mkitem2(itemNo, &item, &v1Seed[0].thirtytwos[1], vctx);
        } else {
            // only 32 bytes of the seed is used
            Announce_mkitem(itemNo, &item, &annHash0.thirtytwos[0]);
        }
        if (!CryptoCycle_update(&state, &item, NULL, randHashCycles, vctx)) {
            return Validate_checkAnn_INVAL;
        }
    }

    CryptoCycle_final(&state);

    if (ann->hdr.version > 0) {
        Buf_OBJCPY(&_ann, ann);
        ann = &_ann;
        Announce_crypt(ann, &state);
        if (!Buf_IS_ZERO(ann->lastAnnPfx)) {
            return Validate_checkAnn_INVAL_ITEM4;
        }
    } else {
        _Static_assert(sizeof ann->lastAnnPfx == Announce_lastAnnPfx_SZ, "");
        if (memcmp(&item, ann->lastAnnPfx, sizeof ann->lastAnnPfx)) {
            return Validate_checkAnn_INVAL_ITEM4;
        }
    }

    if (ann->hdr.version > 0) {
        // Need to re-compute the item because we are proving the original value
        Announce_createProg(vctx, &annHash0.thirtytwos[0]);
        Announce_mkitem2(itemNo, &item, &annHash0.thirtytwos[1], vctx);
    }

    Buf64_t itemHash; Hash_COMPRESS64_OBJ(&itemHash, &item);
    if (!Announce_Merkle_isItemValid(&ann->merkleProof, &itemHash, itemNo)) {
        return Validate_checkAnn_INVAL;
    }

    if (annHashOut) { memcpy(annHashOut, state.bytes, 32); }
    if (!Work_check(state.bytes, ann->hdr.workBits)) { return Validate_checkAnn_INSUF_POW; }

    return Validate_checkAnn_OK;
}

static bool isWorkOk(const CryptoCycle_State_t* ccState,
                     const PacketCrypt_Coinbase_t* cb,
                     uint32_t target)
{
    uint32_t effectiveTarget = Difficulty_getEffectiveTarget(
        target, cb->annLeastWorkTarget, cb->numAnns);
    return Work_check(ccState->bytes, effectiveTarget);
}

// returns:
// Validate_checkBlock_OK
// Validate_checkBlock_SHARE_OK or
// Validate_checkBlock_INSUF_POW
static int checkPcHash(uint64_t indexesOut[PacketCrypt_NUM_ANNS],
                       const PacketCrypt_HeaderAndProof_t* hap,
                       const PacketCrypt_Coinbase_t* cb,
                       uint32_t shareTarget,
                       const uint8_t** contents,
                       uint8_t workHashOut[static 32])
{
    CryptoCycle_State_t pcState;
    _Static_assert(sizeof(PacketCrypt_Announce_t) == sizeof(CryptoCycle_Item_t), "");

    Buf32_t hdrHash;
    Hash_COMPRESS32_OBJ(&hdrHash, &hap->blockHeader);
    CryptoCycle_init(&pcState, &hdrHash, hap->nonce2);

#ifndef PCP2
    uint32_t proofIdx = hap->nonce2 ^ hdrHash.ints[0];
#endif

    for (int j = 0; j < 4; j++) {
        const uint8_t* contentProof = NULL;
#ifndef PCP2
        Buf32_t contentProofBuf;
        contentProof = ContentMerkle_getProofBlock(
            proofIdx, &contentProofBuf, contents[j], hap->announcements[j].hdr.contentLength);
#endif
        // This gets modded over the total anns in PacketCryptProof_hashProof()
        indexesOut[j] = CryptoCycle_getItemNo(&pcState);
        CryptoCycle_Item_t* it = (CryptoCycle_Item_t*) &hap->announcements[j];
        Hash_eprintHex((uint8_t*)contentProof, 32);
        if (Util_unlikely(!CryptoCycle_update(&pcState, it, contentProof, 0, NULL))) { return -1; }
    }
    CryptoCycle_smul(&pcState);
    CryptoCycle_final(&pcState);

    memcpy(workHashOut, pcState.bytes, 32);

    if (isWorkOk(&pcState, cb, hap->blockHeader.workBits)) {
        return Validate_checkBlock_OK;
    }

    if (shareTarget && isWorkOk(&pcState, cb, shareTarget)) {
        return Validate_checkBlock_SHARE_OK;
    }

    return Validate_checkBlock_INSUF_POW;
}

int Validate_checkBlock(const PacketCrypt_HeaderAndProof_t* hap,
                        uint32_t hapLen,
                        uint32_t blockHeight,
                        uint32_t shareTarget,
                        const PacketCrypt_Coinbase_t* coinbaseCommitment,
                        const uint8_t blockHashes[static PacketCrypt_NUM_ANNS * 32],
                        const uint8_t** annContents,
                        uint8_t workHashOut[static 32],
                        PacketCrypt_ValidateCtx_t* vctx)
{
#ifdef PCP2
    annContents = NULL;
#endif
    if (hapLen < PacketCrypt_HeaderAndProof_SIZEOF(0)) {
        return Validate_checkBlock_PCP_INVAL;
    }
    if (coinbaseCommitment->magic != PacketCrypt_Coinbase_MAGIC) {
        return Validate_checkBlock_BAD_COINBASE;
    }
    if (!Difficulty_isMinAnnDiffOk(coinbaseCommitment->annLeastWorkTarget)) {
        return Validate_checkBlock_BAD_COINBASE;
    }

    // Check that final work result meets difficulty requirement
    uint64_t annIndexes[PacketCrypt_NUM_ANNS] = {0};
    int chk = checkPcHash(annIndexes, hap, coinbaseCommitment, shareTarget, annContents, workHashOut);

    Buf32_t annHashes[PacketCrypt_NUM_ANNS];

    // Validate announcements
    for (int i = 0; i < PacketCrypt_NUM_ANNS; i++) {
        const PacketCrypt_Announce_t* ann = &hap->announcements[i];
        if (Validate_checkAnn(NULL, ann, &blockHashes[i * 32], vctx)) {
            return Validate_checkBlock_ANN_INVALID(i);
        }
        uint32_t effectiveAnnTarget =
            Difficulty_degradeAnnouncementTarget(ann->hdr.workBits,
                (blockHeight - ann->hdr.parentBlockHeight));
        if (blockHeight < 3) { effectiveAnnTarget = ann->hdr.workBits; }
        if (effectiveAnnTarget > coinbaseCommitment->annLeastWorkTarget) {
            return Validate_checkBlock_ANN_INSUF_POW(i);
        }
        Hash_COMPRESS32_OBJ(&annHashes[i], ann);
    }

    uint32_t proofLen = hapLen - PacketCrypt_HeaderAndProof_SIZEOF(0);

    // hash PacketCryptProof
    Buf32_t pcpHash;
    if (PacketCryptProof_hashProof(
        &pcpHash, annHashes, coinbaseCommitment->numAnns, annIndexes, hap->proof, proofLen))
    {
        return Validate_checkBlock_PCP_INVAL;
    }

    // compare PacketCryptProof root hash to CoinbaseCommitment
    if (Buf_OBJCMP(&pcpHash, &coinbaseCommitment->merkleRoot)) {
        return Validate_checkBlock_PCP_MISMATCH;
    }

    return chk;
}

#define XX(x) case x: return #x;

char* Validate_checkAnn_outToString(int code) {
    switch (code) {
        case Validate_checkAnn_OK: return NULL;
        XX(Validate_checkAnn_INVAL)
        XX(Validate_checkAnn_INVAL_ITEM4)
        XX(Validate_checkAnn_INSUF_POW)
        XX(Validate_checkAnn_SOFT_NONCE_HIGH)
        default:;
    }
    return "Validate_checkAnn_UNKNOWN_ERROR";
}

// Return a string form of the result of Validate_checkBlock()
// if the result is Validate_checkBlock_OK (0) then NULL is returned.
// any unrecognized code returns "Validate_checkBlock_UNKNOWN_ERROR"
char* Validate_checkBlock_outToString(int code) {

    #define XXX(x) case x ## _: { \
        switch ((code >> 8) & 0xff) { \
            case 0: return #x "(0)"; \
            case 1: return #x "(1)"; \
            case 2: return #x "(2)"; \
            case 3: return #x "(3)"; \
            default: return #x "(unknown)"; \
        } \
    }
    switch (code) {
        case Validate_checkBlock_OK: return NULL;
        XX(Validate_checkBlock_SHARE_OK)

        XXX(Validate_checkBlock_ANN_INVALID)
        XXX(Validate_checkBlock_ANN_INSUF_POW)
        XXX(Validate_checkBlock_ANN_SIG_INVALID)
        XXX(Validate_checkBlock_ANN_CONTENT_INVALID)

        XX(Validate_checkBlock_PCP_INVAL)
        XX(Validate_checkBlock_PCP_MISMATCH)
        XX(Validate_checkBlock_INSUF_POW)
        XX(Validate_checkBlock_BAD_COINBASE)
        default:;
    }
    #undef XXX
    return "Validate_checkBlock_UNKNOWN_ERROR";
}
#undef XX

Web Proxy Viewer  |  New URL  |  Original Page