pow(工作证明)是一种非常可怕的方式,可以通过浪费原始的CPU周期进行毫无意义的加密计算来证明您真的很认真。
您可能已经熟悉工作证明,类似技术,或者至少在加密货币方面已经听说过POW。如果您没有
工作证明(POW)是一种机制,您的计算机正在将加密难题求解简单证明,可以做到这一点。好吧,您可能会问什么重点?想象一下一个小偷:他必须在您邻居的前门上挑选15个锁,但在您的前门只有2个锁。您认为他会走哪条路?这也许是一个粗略的例子,但是基本上是POW和类似的技术,旨在通过需要更多的(技术)努力而不是可能的收益来将严重和“真实”的人与其他人区分开。这就是确保区块链的完整性的方式,一些安全和保护库(例如CAPTCHAS)也在使用此类技术。
当然,您无法将以下示例与您喜欢的C | GPU生成的钱提供者的POW技术进行比较,但它简单地显示了它如何工作。
1.在JavaScript中散步
要创建自己的POW函数,我们首先需要一种快速,不可预测的哈希算法,该算法可以在至少更现代的浏览器的JavaScript环境中使用。我敢肯定,有些读者已经想到了NPM上可用的一些加密JS软件包,但是不,我们正在使用SubtleCrypto!
我们的crypto.subtle.digest
函数允许我们从浏览器内部使用SHA-1将SHA-1进行SHA-512加密哈希函数,而无需依赖任何外部依赖。让我们以一个基本示例尝试一下:
function sha512(string) {
return new Promise((resolve, reject) => {
let buffer = (new TextEncoder).encode(string);
crypto.subtle.digest('SHA-512', buffer.buffer).then(result => {
resolve(Array.from(new Uint8Array(result)).map(
c => c.toString(16).padStart(2, '0')
).join(''));
}, reject);
});
}
sha512('somedata').then(result => {
console.log(result); // Output: a053...6f416
}, err => {
console.error(err);
});
sha512
函数将字符串变成UTF8字节数组,我们可以在已经提到的crypto.subtle.digest
函数上使用,这是我们声明所需的哈希算法(SHA-512
)之后的第二个参数。最后,我们将字节阵列转动,持有哈希摘要结果,变成十六进制(you can find a similar example on MDN)。
完美,我们现在能够进行哈希,但是我们如何做?
2.基本的工作证明功能
我能想到的最基本的加密拼图以及那里的许多其他教程和解释文章,正在计算哈希摘要的启动(或结束)的特定零数量(或任何其他十六进制) 。听起来很复杂? na,我们只需要一些(随机)数据和一个nonce以及上面的sha512
函数。
让我们创建一个process
函数,该函数需要2个参数:一些随机数据用于我们的哈希和一个难度值,默认值为5,大多数(通用)计算机的默认值为5个,该数据应已大约10.000-50.000 ms 。
async function process(data, difficulty = 5) {
let hash;
let nonce = 0;
do {
hash = await sha512(data + nonce++);
} while(hash.substr(0, difficulty) !== Array(difficulty + 1).join('0'));
return hash;
}
当然,我们可以通过许多不同的方式进一步改善和扩展此功能。但是,这足以说明,所以让我们用类似的东西执行它:
async function main() {
console.time('pow-test');
let hash = await process('somedata');
console.log(`Result: ${hash}`);
console.timeEnd('pow-test');
}
main();
Here is a working jsfiddle example.
它有效,但是我们在许多浏览器上都有一个主要问题:在工作证明测试运行时,该网站完全不受控制。这是一种可怕的用户体验,尤其是对于较旧的设备,因此我们必须找到一种在后台执行POW功能的方法。
3.使用Webworker API
JavaScript提供了一个简单的解决方案,可以在背景线程中移动长期运行的任务和脚本。通常,此过程需要在外部JavaScript文件中分离用于任务本身的主代码。但是,但是,stackoverflow已经找到了解决方案。
让我们从我们的process()
函数开始,现在应该初始化和执行Webworker,而不是任务本身:
function process(data, difficulty = 5) {
return new Promise((resolve, reject) => {
let webWorkerURL = URL.createObjectURL(new Blob([
'(', processTask(), ')()'
], { type: 'application/javascript' }));
// Create WebWorker
let worker = new Worker(webWorkerURL);
worker.onmessage = (event) => {
worker.terminate();
resolve(event.data);
};
worker.onerror = (event) => {
worker.terminate();
reject();
};
// Execute WebWorker Task
worker.postMessage({
data,
difficulty
});
// Destroy URL Object
URL.revokeObjectURL(webWorkerURL);
});
}
我们首先使用URL.createObjectURL
方法开始“外部化”我们的过程任务,如下所述,该任务已移至processTask()
函数。从技术上讲,我们创建了自己的页面,仅包含所需的JavaScript代码,现在能够传递此页面,而不是Worker
对象构造函数中通常使用的JavaScript文件路径。现在让我们在我们自己的Webworker实例上附加典型的事件侦听器,并发布带有随机数据的消息,并设置难以启动我们的POW进程。
现在,让我们看一下令人毛骨悚然的部分:我们的processTask()
函数返回匿名函数的“串联”主体。那是我们网络工作人员脚本的纯粹内容:
function processTask() {
return function () {
function sha512(text) {
return new Promise((resolve, reject) => {
let buffer = (new TextEncoder).encode(text);
crypto.subtle.digest('SHA-512', buffer.buffer).then(result => {
resolve(Array.from(new Uint8Array(result)).map(
c => c.toString(16).padStart(2, '0')
).join(''));
}, reject);
});
}
addEventListener('message', async (event) => {
let data = event.data.data;
let difficulty = event.data.difficulty;
let hash;
let nonce = 0;
do {
hash = await sha512(data + nonce++);
} while(hash.substr(0, difficulty) !== Array(difficulty + 1).join('0'));
postMessage({
hash,
data,
difficulty
});
});
}.toString();
}
正如您可能已经注意到的那样,我们的sha512()
功能已在网络工程师环境中找到了他的新房屋,然后是一个事件听众,该侦听器执行我们的功率代码,并在完成后通过计算的哈希响应,并在完成时传递了困难。
Here is a working jsfiddle example
4。结论
使用sktlecrypto进行哈希,用于多线程的网络工作者,url blobs,uint8arrays,... JavaScript在过去几年中变成了一种更强大的语言,即使与某些部分相比有些复杂, Python或PHP(考虑到这一事实是愚蠢的,JavaScript被设计为尽可能容易)。
但是,我们使用Webworker API创建了一个简约的工作证明功能,将其作为背景任务运行。虽然您应该明确地扩展和改进上面的代码,但我仍然希望您今天可以阅读或学习一些有用的东西。
感谢您的阅读。