JavaScript中的简单工作证明
#javascript #网络开发人员 #crypto

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创建了一个简约的工作证明功能,将其作为背景任务运行。虽然您应该明确地扩展和改进上面的代码,但我仍然希望您今天可以阅读或学习一些有用的东西。

感谢您的阅读。