/** * (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