项目中需要直接对接以太坊预言机(oracle)的需求。因此,我创建了一个:直接从以太坊的Solidity调用JavaScript的项目。
如何操作
以太坊预言机(oracle)谕称为CaptainJS。它负责JavaScript请求并在NodeJS容器中执行它们。如果你的合同是使用CaptainJS,那么你可以直接使用回调和JavaScript执行功能。
Captain需要一些预算来转移您的结果或回复您的合同。该预算(gas*gas price)将转入CaptainJS核心合同,并将用于调用您的合同。
简单回调{use.case.1}
在某些情况下,有必要建立一种机制去调用合同。例如每年都会向特定人员提取预算。但是Solidity不能调用自己。
因此从usingCaptainJS扩展并开始编码......
使用唯一的整数jobId来标识您的回调。使用足够的gas调用RingShipsBell并在方法RingRing中接收回调。
为了确保RingRing只由Captain而不是盗版者调用,只需将CaptainsOrdersAllowed添加到其声明中就可以。
pragma solidity ^0.4.25;
import "https://github.com/CaptainJavaScript/Solidity/usingCaptainJS.sol";
contract SimpleCallback is usingCaptainJS {
uint constant CALLBACK_ID = 3;
function CallbackExample() public {
RingShipsBell(
CALLBACK_ID, /* give the job a unique ID */
60 * 24, /* 24 hours */
45000, /* gas budget for callback */
DEFAULT_GAS_PRICE /* use default gas price */
);
}
function RingRing(uint UniqueIdentifier) external onlyCaptainsOrdersAllowed {
if(UniqueIdentifier == CALLBACK_ID) {
/* this will be executed 24 hours after CallbackExample
* was invoked
*/
}
}
}
使用Mathjs{use.case.2}
现在让我们看一个简单的JavaScript编程工作。 JavaScript的mathjs库有许多有用的功能,例如厘米到英寸的转换。
要使用此库,您需要调用Run函数并移交将厘米转换为英寸所需的JavaScript代码。
您要提交的JavaScript代码必须按以下方式编写:
module.exports = function(CaptainJSIn) { /* here goes your code */ }
CaptainJS将通过调用默认函数来调用容器中的代码,CaptainJSIn将包含您对JavaScript函数的输入, 然后CaptainJS将返回代码的结果, 如果您的JavaScript代码成功:将调用CaptainsResult, 返回结果始终是一个字符串。
如果你的JavaScript代码不成功或者它的结果无法发回(失败有可能是没有足够的gas),那么将调用CaptainsError。
为了确保CaptainsResult和CaptainsError都只由Captain而不是盗版者调用,只需将CaptainsOrdersAllowed添加到其声明中。
运行时片的持续时间为10秒,它包括所有必需的npm模块的下载和安装。 目前6个运行时切片是最大值。
这是完整的代码段:
contract SeamansExamples is usingCaptainJS {
...
uint constant CENTIMETER_JOB = 1;
function CentimeterToInchExample(string Centimeter) public {
Run(
CENTIMETER_JOB, /* give the job a unique ID */
/* JavaScript code I want to execute: */
"module.exports = function(CaptainJSIn) { var math = require('mathjs'); return math.eval(CaptainJSIn + ' cm to inch'); }",
Centimeter, /* Input parameter which will result in CaptainJSIn (see above) */
"mathjs", /* Nodejs libraries we need */
3, /* we need a maximum of 3 runtime slices */
DEFAULT_GAS_UNITS, /* use default gas units */
DEFAULT_GAS_PRICE /* we will transfer the default gas price for return */
);
}
function CaptainsResult(uint UniqueJobIdentifier, string Result) external onlyCaptainsOrdersAllowed {
if(UniqueJobIdentifier == CENTIMETER_JOB) {
// OK. It worked and we got a result
...
}
}
function CaptainsError(uint UniqueJobIdentifier, string Error) external onlyCaptainsOrdersAllowed {
if(UniqueJobIdentifier == CENTIMETER_JOB) {
// OK. It didn't work :-/
}
}
}
调用WolframAlpha{use.case.3}外部资源
如果你想调用WolframAlpha这样的外部资源也很容易, 让我们问WolframAlpha它是否知道法国国家的一切。 因此,您可以像在第二个用例中一样设计代码。
要查询WolframAlpha,请使用JavaScript的axios库。 默认函数必须是异步的,以便在调用axios.get(...)时等待结果。 axios将返回一个JSON对象,但我们需要将其展平为一个字符串:
module.exports = async function(CaptainJSIn) {
const axios = require('axios');
const WAlpha = await axios.get('http://www.wolframalpha.com/queryrecognizer/query.jsp?appid=DEMO&mode=Default&i=' + CaptainJSIn + '&output=json');
return JSON.stringify(WAlpha.data);
}
同样,CaptainJS将通过调用此默认函数来调用容器中的代码。 CaptainJSIn将包含您的输入,例如“France”。
同样,如果您的JavaScript代码成功:将调用CaptainsResult。 否则将调用CaptainsError。
而且由于Solidity有时候是一种糟糕的编程语言,你将使用非常昂贵的concat函数来使你的JavaScript代码更具可读性。
这是完整的代码段:
contract SeamansExamples is usingCaptainJS {
...
uint constant WOLFRAMALPHA_JOB = 2;
function WolframAlphaExample(string Country) public {
Run(
WOLFRAMALPHA_JOB, /* give the job a unique ID */
concat ( /* JavaScript code I want to execute: */
"module.exports = async function(CaptainJSIn) { ",
" const axios = require('axios'); ",
" const WAlpha = await axios.get('http://www.wolframalpha.com/queryrecognizer/query.jsp?appid=DEMO&mode=Default&i=' + CaptainJSIn + '&output=json'); ",
" return JSON.stringify(WAlpha.data); ",
"}"
),
Country, /* Input parameter which will result in CaptainJSIn (see above) */
"axios", /* Nodejs libraries we need */
3, /* we need a maximum of 3 runtime slices */
200000, /* use 200,000 gas units */
DEFAULT_GAS_PRICE /* use default gas price */
);
}
...
}
JSON,XML / XPath或HTML / jQuery {use.case.4}请求
经典的oracle请求是一个简单的JSON,XML / XPath或HTML / jQuery请求。 而不是编写一个完整的JavaScript代码为您执行查询,而不是最新版本具有预定义的查询。
要调用简单查询,只需使用Run方法,就像在前两个用例中一样。 但是,不是提交JavaScript代码而是发送具有html:,xml:或json:前缀的URL。 Run方法的输入参数是JSON,XPath或jQuery表达式。 通常1个运行时切片就足够了。
如果您的JavaScript代码成功:将调用CaptainsResult。 否则将调用CaptainsError。
(客户端库现在包含一个测试模块,用于查看代码是否有效 - 在您提交到区块链之前)
function HTMLqueryExample() public {
Run(
HTML_QUERY_EXAMPLE, /* give the job a unique ID */
/* url needs to start with html: */
"html:http://www.amazon.co.uk/gp/product/1118531647",
/* Input parameter is the jQuery. Result will be stored in QUERY_RESULT variable */
"$('span.inlineBlock-display span.a-color-price').each(function(i, element) {var el = $(this); QUERY_RESULT = el.text(); })",
"", /* no modules required */
1, /* queries are fast */
DEFAULT_GAS_UNITS, /* use default gas units */
DEFAULT_GAS_PRICE /* we will transfer the default gas price for return */
);
}
function JSONqueryExample() public {
Run(
JSON_QUERY_EXAMPLE, /* give the job a unique ID */
/* url needs to start with json: */
"json:https://api.kraken.com/0/public/Ticker?pair=ETHUSD",
/* Input parameter is the JSON path */
"result.XETHZUSD.a[0]",
"", /* no modules required */
1, /* queries are fast */
DEFAULT_GAS_UNITS, /* use default gas units */
DEFAULT_GAS_PRICE /* we will transfer the default gas price for return */
);
数据加密{use.case.5}
对于那些喜欢加密存储在区块链中的数据的海员,我已经为最新版本添加了一个加密模块。 这允许您将加密数据发送到Captain的NodeJS容器。 在那里它将被解密和执行。 (字符串结果不会被加密回发送)
例如,您希望在事务发生后发送邮件,然后您可以使用nodemailer,登录到您的邮件帐户并发送邮件。 在这种情况下,您的代码看起来与此类似:
module.exports = async function(CaptainJSIn) {
var nodemailer = require("nodemailer");
var transport = nodemailer.createTransport({
host: "smtp-mail.outlook.com", // hostname
secureConnection: false, // TLS requires secureConnection to be false
port: 587, // port for secure SMTP
tls: {
ciphers:'SSLv3'
},
auth: {
user: "myaddress@outlook.com",
pass: "mysecretpassword"
}
});
var mailOptions = {
to: 'donald.trum@whitehouse.gov', // list of receivers
subject: "you're fired", // Subject line
text: "Dear Donald, ..."
};
// send mail with defined transport object
transport.sendMail(mailOptions, function(error, info){});
}
您的帐户详细信息将存储在您的代码中。 这些帐户详细信息将永久存储在以太坊的区块链中。只需将模块添加到MailSample.js等文件中即可;
使用CaptainJS-Encryption.js中的EncryptFile加密文件;
将加密代码添加到合同中
加密代码:
var ENC = require("./CaptainJS-Encryption.js");
async function RUN() {
await ENC.EncryptFile(false, "MailSample.js", "EncryptedMailSample.txt",
() => { console.log("Success!"); },
(ERROR) => { console.log(ERROR); }
);
}
RUN();
并将加密代码添加到基于Solidity的合同中:
function HTMLqueryExample() public {
Run(
ENCRYPTED_MAIL_EXAMPLE,
"crypt:8366268bd167a9f8318f99c71d0f489d0372b545735c2e10303c47bad2507e933171f72f...",
"",
"",
1,
DEFAULT_GAS_UNITS,
DEFAULT_GAS_PRICE
);
}
Captain永远不会读你的代码! 它只是在一个新的容器中执行。 换句话说:如果您加密数据,那么它将保持加密状态,直到它在容器中执行(并且容器将在执行后消失)。 加密使用Captain合同的公钥/私钥。 尽管Captain可以出于私人目的阅读您的加密数据,但它不会这样做,这是一个承诺和信任的证据。
本文转载:区块链研究实验室-以太坊预言机(oracle)直接从Solidity执行JavaScriptmp.weixin.qq.com