ProgPoW是一种PoW算法,旨在缩小与专用ASIC的效率差距。它利用了标准硬件(GPU)几乎所有的性能,提前调整优化了以太坊网络中最常见的硬件。
自第一个比特币挖矿ASIC问世以来,出现了许多新的PoW算法,目的是保持“ASIC抗力”。所谓“ASIC抵抗”,就是抵抗PoW挖掘计算能力的集中化,防止使用这种算法的货币被少数参与者操纵。
本文将首先介绍新算法ProgPoW及其对“ASIC电阻”的影响。此外,本文还将分析和比较不同PoW算法在硬件中的使用情况。最后,我们通过分析代码来谈谈ProgPoW的部署。
Prog power的设计目标是将该算法的要求与显卡相匹配:如果要将算法部署在特定的ASIC上,其效率与显卡相比不会有太大的提高。
Prog pow的主要特点是:
-将keccak_f1600(64位字)改为keccak_f800(32位字)以减少对最终力的影响
-增加主循环中的混合态
-将DRAM(动态随机存取存储器)读取从128字节增加到256字节
虽然ASIC也可以部署这个代码,但在效率方面几乎没有帮助。大部分显卡都需要支持以上功能。唯一可以优化的部分是:去除图形管道(显示、几何引擎、表面纹理等。)和浮点数学。
这样会提高1.1-1.2倍左右的效率,比Ethash(2倍)或者Cryptonight(50倍)要少很多。
标准硬件PoW应用概述
随着大型矿池的不断发展,形成了少数矿池控制大量计算能力的局面,因为只有加入这些矿池,小矿商才能获得更稳定的经济利益。虽然有人认为大型集中式矿池违反了“ASIC阻力”,但重要的是,基于ASIC的货币实际上更加集中,原因如下:
2。没有后援机构:当价格波动较大,容易被操纵时,没有后援硬件或利益相关者可以进入。
3。进入门槛更高:早进入的矿工已经很有钱了,可以用不知名的新货币投入资金和生态资源。因此,通过挖掘进行的初始令牌分配具有局限性,可能导致集权的“经济偏差”。
4。委托集中化vs .部署集中化:矿池集中化虽然是用户委托造成的,但硬件简单化不是:只有这类硬件的特定购买者才能参与挖矿,所以短时间内不可能剥夺矿池控制权。
5。即使采用分散挖矿的方式,也很难做到分散控制:一旦大型ASIC厂商介入,设计后门硬件就变得毫无意义。保证市场的透明尚力财经小编2022 和公平,对ASIC厂商并无益处。
虽然维持“ASIC抵抗”的目标很有价值,但是“ASIC抵抗”的概念却是谬误的。CPU(中央处理器)和GPU(显卡)本身就是ASIC。理论上,任何可以在商用ASIC(CPU或GPU)上运行的算法,都可以部署特定的ASIC。一些算法甚至被故意做成“ASIC友好的”。用ASIC实现——的挖掘效率远高于运行普通硬件的同类算法。这种现象对于专业ASIC矿机厂商来说是很有吸引力的。
所以,ASIC阻力是指专业化硬件与普及度和应用性更高的硬件之间的效率差距。定制硬件和通用硬件的效率差异越小,抵抗能力越强,算法越好。这种低效率是衡量PoW算法好坏的合理标准。意味着效率的绝对表现,——的性能/功率比或者性价比是高度相关的。 如果一个公司生产和控制的ASIC效率极高,那么他们就可以控制51%的网络计算能力,很有可能发动攻击。
PoW算法一目了然
SHA 256(ASIC部署后挖掘效率提升约1000倍)
处理CPU或GPU的单点操作需要获取并解码一条指令,从寄存器堆中获取数据,执行这条指令,然后将结果写入寄存器堆。这个过程需要大量的时间和资源。
在ASIC中实现的单点操作需要少量的晶体管和导线。这意味着每个单独的操作只需要消耗非常少的电力、空间或时间。可以通过列出所需操作的顺序来构建散列核心。
这个hash核可以在很短的时间内执行操作的序列,而且比CPU和GPU消耗更少的功耗和空间。比特币ASIC由许多相同的哈希内核和一些最小的片外通信组成。
Scrypt和NeoCrypt(ASIC部署后挖掘效率提升约1000倍)
从算法和位操作的角度来看,Scrypt和neo crypt与SHA相似。不幸的是,对于使用这种算法的货币,如Litecoin,他们的PoW mining算法只使用容量在32kb至128kb之间的临时存储器。该寄存器的容量很小,非常适合ASIC实现。所以这种算法的ASIC部署类似于SHA,会大大提高效率。
X11和X16R(ASIC部署后挖掘效率提升约1000倍)
X11(以及类似的X系列)需要11个唯一的哈希核以固定顺序排列。每个hash核的效率和单个SHA核的效率差不多,所以从整体设计来看,两者的效率增长还是差不多的。
X16R需要多个哈希核心通过一个简单的排序状态机进行交互。每个内核都将有类似的效率提升,时序逻辑将消耗最少的功率、空间和时间。
Baikal BK-X是一款ASIC,包含多个哈希内核和一个可编程序列器。它已经升级到支持具有不同散列顺序的新算法。
Equihash(部署ASIC后挖掘效率提升约100倍)
150 MB的状态很大,但通过ASIC实现是可能的。ASIC可以快速完成位串的宁滨、排序和比较。
布谷鸟循环(部署ASIC后挖掘效率提升约100倍)
一旦发生时间/内存权衡攻击,芯片所需状态量不明确。一个专业图遍历核的效率增长和SHA计算核差不多。
CryptoNight(部署ASIC后挖掘效率提升约50倍)
与Scrypt相比,CryptoNight的计算量要小得多,需要2mb的临时内存(无已知的时间/内存权衡攻击)。这种大容量寄存器将主导ASIC部署,限制哈希核的数量和ASIC的绝对性能。ASIC几乎全部由片上SRAM(静态随机存取存储器)组成。
Ethash(部署ASIC后挖掘效率提升约2倍)
Ethash由于大容量DAG(数据库可用性组)的存在,需要外部存储。但是,它只需要这个——。很少计算内存加载结果。所以ASIC可以在一定程度上消除GPU的复杂性和能耗,只需要形成一个连接小型计算引擎的内存接口。
ProgPoW算法drill
这里生成的DAG和Ethash中的完全一样。唯一的区别是生成了具有progpower _ size _ cache值的附加数据,这些数据将驻留在L1缓存中,而不是framebuffer中。
- PROGPOW_LANES:为计算一个散列实例而协调的平行行数;默认值为32
- PROGPOW_REGS:寄存器文件的使用大小;默认值是16
- PROGPOW_CACHE_BYTES:缓存器容量;默认值是16*1024
- PROGPOW_CNT_MEM:帧缓冲器访问的次数,定义为算法的外部循环;默认值是64(和Ethash一样)
- PROGPOW_CNT_CACHE:每个循环的缓存访问次数;默认值是8
- PROGPOW_CNT_MATH:每个循环的数学运算次数;默认值是8
ProgPoW用FNV1a来合并数据Ethash用FNV1来合并数据,但FNV1a能够实现更好的分布特性ProgPoW用KISS99生成随机数。这是最简单的(最少指令)随机数生成器,通过了TestU01统计测试。像梅森图斯特这样更复杂的随机数生成器可以在专用专用集成电路实现有效部署,从而提高挖矿效率。uint32_t fnv1a(uint32_t h,uint 32 _ t d){ return h=(h ^ d)*0x 1000193;} typedef struct { uint32_t z,w,jsr,jcong } kiss 99 _ t;//KISS99简单,快速,通过testu 01套房//https://en。维基百科。org/wiki/KISS _(算法)//http://www . CSE . yorku . ca/~ oz/marsa glia-RNG . html uint 32 _ t KISS 99(KISS 99 _ t ST){ uint 32 _ t znew=(ST . z=36969 *(ST . z 65535)(ST . z 16));uint 32 _ tnew=(ST . w=18000 *(ST . w 65535)(ST . w 16));uint 32 _ t MWC=((znew 16)wnew);uint32_t SHR3=(圣jsr ^=(圣jsr
13),圣jsr ^=(圣JSR 5));uint 32 _ t CONG=(ST . jcong=69069 * ST . jcong 1234567);return((mwc^cong)shr 3);}混合数据的车道*规则是从散列的种子进行初始化的。void fill_mix( uint64_t hash_seed,uint32_t lane_id,uint32_t mix[PROGPOW_REGS] ) { //使用FNV将每经种子扩展为每通道//使用吻将每通道种子扩展为填充混合uint 32 _ t fnv _ hash=0x 811 c 9 DC 5;kiss99 _ t stst.z=fnv1a(fnv_hash,seed);st.w=fnv1a(fnv_hash,种子32);st.jsr=fnv1a(fnv_hash,lane _ id);st.jcong=fnv1a(fnv_hash,lane _ id);for(int I=0;I Prog pow _ REGSI)mix[I]=kiss 99(ST);}
主要的搜索算法采用了凯克克海绵函数(宽度为800位,448比特率和352的容量)来生成一个种子,扩展种子,并在混合数据过程中加载序列和进行随机运算,然后压缩结果到最终的凯克克排列(参数相同)进行目标比较。bool Prog pow _ search(const uint 64 _ t Prog _ seed,const uint64_t nonce,const hash32_t header,const uint64 _ t target,const uint64_t *g_dag,//千兆字节十克位于帧缓冲区常量64 _ t * c _ DAG//千字节十克位于腰神经2缓存){ uint 32 _ t mix[Prog pow _ LANES][Prog pow _ REGS];uint32_t结果[8];for(int I=0;I 8;我)结果[I]=0;//keccak(表头.nonce)uint 64 _ t seed=keccak _ f800(header,nonce,result);//初始化所有通道的mix for(int l=0;l PROGPOW _ LANESl ) fill_mix(seed,l,mix);//执行随机生成的for(int I=0;我Prog pow _ CNT _ MEM;i ) progPowLoop(prog_seed,I,mix,g_dag,c _ Dag);//将混合数据减少到单个每通道结果uint 32 _ t lane _ hash[Prog pow _ LANES];for(int l=0;l Prog pow _ LANESl){ lane _ hash[l]=0x 811 C9 DC 5 for(int I=0;I Prog pow _ REGSi)fnv1a(lane _ hash[l],mix[l][I]);} //对于(int i=0,将所有通道缩减为单个128位结果;I 8;我)结果[I]=0x 811 C9 DC 5;for(int l=0;l Prog pow _ LANESl)fnv1a(result[l % 8],lane_hash[l]) //keccak(header.凯克(标题.nonce).结果);return (keccak_f800(header,seed,result)=target);}内循环采用FNV和KISS99从食物_seed生成随机序列。这个随机序列决定访问哪个混合状态以及执行什么随机运算。由于对prog_seed的更改相对较少,因此可以预料到的是,在挖矿过程中将会编译progPowLoop,而不是进行动态执行。
kiss99_t progPowInit(uint64_t prog_seed, int mix_seq[PROGPOW_REGS]) { kiss99_t prog_rnd; uint32_t fnv_hash = 0x811c9dc5; prog_rnd.z = fnv1a(fnv_hash, prog_seed); prog_rnd.w = fnv1a(fnv_hash, prog_seed >> 32); prog_rnd.jsr = fnv1a(fnv_hash, prog_seed); prog_rnd.jcong = fnv1a(fnv_hash, prog_seed >> 32); // Create a random sequence of mix destinations for merge() // guaranteeing every location is touched once // Uses Fisher–Yates shuffle for (int i = 0; i 0; i--) { int j = kiss99(prog_rnd) % (i + 1); swap(mix_seq[i], mix_seq[j]); } return prog_rnd; }将数值合并到混合数据中的数学运算是为了保持熵。
// Merge new data from b into the value in a // Assuming A has high entropy only do ops that retain entropy // even if B is low entropy // (IE don't do A&B) void merge(uint32_t &a, uint32_t b, uint32_t r) { switch (r % 4) { case 0: a = (a * 33) + b; break; case 1: a = (a ^ b) * 33; break; case 2: a = ROTL32(a, ((r >> 16) % 32)) ^ b; break; case 3: a = ROTR32(a, ((r >> 16) % 32)) ^ b; break; } }<>为随机数学选择的数学运算在CUDA和OpenCL(通用GOU的两种主要编程语言)中易于实现。// Random math between two input values uint32_t math(uint32_t a, uint32_t b, uint32_t r) { switch (r % 11) { case 0: return a + b; case 1: return a * b; case 2: return mul_hi(a, b); case 3: return min(a, b); case 4: return ROTL32(a, b); case 5: return ROTR32(a, b); case 6: return a & b; case 7: return a | b; case 8: return a ^ b; case 9: return clz(a) + clz(b); case 10: return popcount(a) + popcount(b); } }
主循环:// Helper to get the next value in the per-program random sequence #define rnd() (kiss99(prog_rnd)) // Helper to pick a random mix location #define mix_src() (rnd() % PROGPOW_REGS) // Helper to access the sequence of mix destinations #define mix_dst() (mix_seq[(mix_seq_cnt++)%PROGPOW_REGS]) void progPowLoop( const uint64_t prog_seed, const uint32_t loop, uint32_t mix[PROGPOW_LANES][PROGPOW_REGS], const uint64_t *g_dag, const uint32_t *c_dag) { // All lanes share a base address for the global load // Global offset uses mix[0] to guarantee it depends on the load result uint32_t offset_g = mix[loop%PROGPOW_LANES][0] % DAG_SIZE; // Lanes can execute in parallel and will be convergent for (int l = 0; l < 尚力财经小编2022PROGPOW_LANES; l++) { // global load to sequential locations uint64_t data64 = g_dag[offset_g + l]; // initialize the seed and mix destination sequence int mix_seq[PROGPOW_REGS]; int mix_seq_cnt = 0; kiss99_t prog_rnd = progPowInit(prog_seed, mix_seq); uint32_t offset, data32; int max_i = max(PROGPOW_CNT_CACHE, PROGPOW_CNT_MATH); for (int i = 0; i < max_i; i++) { if (i
32, rnd()); } }标签: do