区块链的一个显著特点是,数据一旦写入链中,就不可篡改重写。
在墨客区块链中,每一笔交易(transaction),都有一个保存数据的data空间,本文主要讲解如何将自定义数据(可以是一句话、一篇文章等)写入区块链的交易中,并读取出来。
当然,保存数据到区块链是会消耗gas费的,且gas费跟数据量是正相关的。
本文内容不适用于有强逻辑性和关系型的大数据存储。
1.做一笔交易,将数据写入区块链
该笔交易没有发送mc,或者其他token,仅将数据写到区块链。
代码文档sendData.js如下:
var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://localhost:8545'));
var address = "0x745c57ca5318093115d61bbca368XXXXXXXXXXXX";
var account = {address:"0x745c57ca5318093115d61bbca368XXXXXXXXXXXX",secret:"bb673026deda3c3cd0c63f6ccddfb02a7aXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"};
send(chain3, account.address, account.secret, txCount = -1);
function send(chain3, fromAddress, fromSecret, txCount = -1){
var mc = chain3.mc;
var txcount = txCount >= 0 ? txCount : chain3.mc.getTransactionCount(fromAddress);
console.log("Get tx account", txcount);
var gasPrice = 25000000000;
var gasLimit = 100000;
var gasTotal = gasPrice * gasLimit;
console.log(gasPrice, gasLimit, chain3.fromSha(gasTotal, 'mc'));
//以下为写入数据log
let log = {
time:(new Date).getTime(),
type:"info",
msg:"Hello MOAC!!!"
};
//转换log数据格式
let str = JSON.stringify(log);
console.log(str);
let data = Buffer.from(str).toString('hex');
data = '0x'+data;
console.log(data);
var rawTx = {
from: fromAddress,
nonce: chain3.intToHex(txcount),
gasPrice: chain3.intToHex(gasPrice),
gasLimit: chain3.intToHex(gasLimit),
data: data ,
shardingFlag: 0, //default is global contract
chainId: chain3.version.network
};
var signedTx = chain3.signTransaction(rawTx, fromSecret);
mc.sendRawTransaction(signedTx, function(err, hash) {
if (!err){
console.log("succeed: ", hash);
return hash;
}else{
console.log("error:", err);
console.log('raw tx:', rawTx);
}
});
}
代码使用sendRawTransaction时需要私钥签名,得到本地节点账号的私钥方法见:
《第八篇 墨客区块链(MOAC BlockChain) 程序猿怎么部署和调用智能合约》的第三节“3.部署智能合约”。
直接node,运行结果如下:
返回信息中包含:
本次交易写入数据“Hello MOAC!!!”及其十六进制表示。
本次交易的hash值:0x7834667df3890d0a4bc2fc949d45206fec8fe4b63853181dd9cc20c1b6c009dc。
到浏览器查询hash:
其中的Input Data就是本次交易写入的数据。与node sendData.js时显示的hex格式数据内容一致。
2.查看区块链里的数据字段
当前,每笔交易里的数据在浏览器还显示为hex。因此需要自己写代码解读出其中的内容。
读数据代码callData.js如下:
var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://localhost:8545')); //设置API访问moac节点方式
//获取交易信息
hash = "0x7834667df3890d0a4bc2fc949d45206fec8fe4b63853181dd9cc20c1b6c009dc";
var receipt = chain3.mc.getTransaction(hash);
console.log('get transaction from hash :'+ JSON.stringify(receipt));
console.log();
//获取交易内保存的数据data,需要提前写入
hash = "0x7834667df3890d0a4bc2fc949d45206fec8fe4b63853181dd9cc20c1b6c009dc";
chain3.mc.getTransaction(hash,function(error, result){
//console.log(result);
inputData = result.input;
res_str = Buffer.from(inputData.replace('0x',''),'hex').toString();
res_json = JSON.parse(res_str);
console.log('your data :',res_json);
console.log('your msg :',res_json.msg);
});
直接node,运行结果如下:
通过交易hash得到交易的所有信息,并分析出data数据。
3.将数据批量写入区块链
结合对excel表格的读写操作,可以简单地将批量数据写入到区块链中去。
首先设计一个excel文件,保存要写到区块链的数据。命名为testMessage.xlsx。
本实例用到message。id和name不在代码里使用,仅用于文档记录。time用于返回时间,在成功后会新建一个文件,返回每次信息发送的时间。
代码文件goMessage.js。node执行即可。
注意:1. 将数据上链的交易会收取gas费,且收取的费用跟数据大小正相关;
2. 该代码里包含手动设置nounce完成tx的实例。
var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://localhost:8545'));
//通过node-xlsx读取的excel文件就是一个json数据
var xlsx = require('node-xlsx'); //定义xlsx,自行npm install node-xlsx
var fs = require('fs'); //定义fs,自行npm install fs
var address = "0x745c57ca5318093115d61bbca36XXXXXXXXXXXX";
var account = {
address: "0x745c57ca5318093115d61bbca36XXXXXXXXXXXX",
secret: "bb673026deda3c3cd0c63f6ccddfb02a7ae320078aa8XXXXXXXXXXXXXXXXXXXX"
};
//主要参数
var thisTxCount = -1; //nounce值,本例手动设置,初始为-1,读取实际值后,每次自增1
var interval = 2; //发送一次信息的间隔时间,秒
var message = "Hello MOAC!!!"; //定义要发送的信息,从cxcel文件读取
var obj = xlsx.parse(__dirname + '/testMessage.xlsx');//配置excel文件的路径
var excelObj = obj[0].data; //obj[i].data表示excel文件第i+1个sheet文档的全部内容
var data = [];
var promiseList = []; //用于异步返回
for (var i in excelObj) {
var arr = [];
if (i > 0) { //数据从第二行开始,通常在excel中第一行(i=0)就是每一列的title
sleep(interval * 1000); //每隔2秒,发送一次信息
message = excelObj[i][2]; //得到要发送的信息内容
excelObj[i][3] = (new Date).toLocaleString(); //记录发送交易的时间
promiseList.push(send(chain3, account.address, account.secret, message, thisTxCount, i));
}
}
Promise.all(promiseList).then(function (objList) { //所有异步完成后回调
//遍历objList
objList.forEach(item=>{
excelObj[item.index][4] = item.hash; //记录发送交易的哈希
})
var buffer = xlsx.build([
{
name: 'sheet1',
data: excelObj
}
]);
//创建新文件,将发送时间等记录到文件中
fs.writeFileSync((new Date).toLocaleDateString() + '-' + (new Date).getTime() + '-' + 'hadSendMessage.xlsx', buffer, {'flag': 'w'});
});
//自己写的休眠函数
function sleep(numberMillis) {
var now = new Date();
var exitTime = now.getTime() + numberMillis;
while (true) {
now = new Date();
if (now.getTime() > exitTime)
return;
}
}
function send(chain3, fromAddress, fromSecret, message, txCount = -1, index) {
var mc = chain3.mc;
var txcount = txCount >= 0 ? txCount : chain3.mc.getTransactionCount(fromAddress);//获取nounce值
//console.log("Get tx account", txcount);
thisTxCount = txcount + 1; //手动设置nounce,每次自增1
var gasPrice = 25000000000;
var gasLimit = 100000; //数据量越大,gas费应该设置得越高
var gasTotal = gasPrice * gasLimit;
//console.log(gasPrice, gasLimit, chain3.fromSha(gasTotal, 'mc'));
//以下为写入数据log
let log = {
time: (new Date).getTime(), //获取当前时间(从1970.1.1开始的毫秒数)
type: "info",
msg: message
};
//转换log数据格式,将数据转换为16进制字符串
let str = JSON.stringify(log);
console.log(str);
let data = Buffer.from(str).toString('hex');
data = '0x' + data;
console.log(data);
var rawTx = {
from: fromAddress,
nonce: chain3.intToHex(txcount),
gasPrice: chain3.intToHex(gasPrice),
gasLimit: chain3.intToHex(gasLimit),
data: data,
shardingFlag: 0, //default is global contract
chainId: chain3.version.network
};
var signedTx = chain3.signTransaction(rawTx, fromSecret);
return new Promise(function (resolve, reject) { //异步调用方法
mc.sendRawTransaction(signedTx, function (err, hash) {
if (!err) {
console.log('i=', index);
console.log("succeed: ", hash);
let objBack = { //index及对应的hash
hash,
index
}
resolve(objBack);
} else {
console.log("error:", err);
console.log('raw tx:', rawTx);
reject(err);
}
});
})
}
执行结果:返回所有交易的hash值。
并且建立一个新的文件2018-8-12-1534085632480-hadSendMessage.xlsx。
4.在交易里发送 mc + data
发送一笔交易,包含发送mc,同时把数据写到data字段。
var Chain3 = require('chain3');
var chain3 = new Chain3(new Chain3.providers.HttpProvider('http://localhost:8545'));
var address = "0x745c57ca5318093115d61bbca368XXXXXXXXXXXX";
var account = {address:"0x745c57ca5318093115d61bbca368XXXXXXXXXXXX",secret:"bb673026deda3c3cd0c63f6ccddfb02a7ae320078aa8XXXXXXXXXXXXXXXXXXXX"};
var toAddress = "0x68986c1bcd54ae5dae69310fc64eXXXXXXXXXXXX";
var amount = 0.002;
send(chain3, account.address, account.secret, toAddress, amount, txCount = -1)
function send(chain3, fromAddress, fromSecret, toAddress, amount, txCount = -1){
var mc = chain3.mc;
var txcount = txCount >= 0 ? txCount : chain3.mc.getTransactionCount(fromAddress);
console.log("Get tx account", txcount);
var gasPrice = 25000000000;
var gasLimit = 100000;
var value = chain3.toSha(amount, 'mc');
var gasTotal = gasPrice * gasLimit + Number(value);
console.log(gasPrice, gasLimit, value, chain3.fromSha(gasTotal, 'mc'));
//以下为写入数据log
let log = {
time:(new Date).getTime(),
type:"info",
msg:"MOAC GO!!!"
};
let str = JSON.stringify(log);
console.log(str);
let data = Buffer.from(str).toString('hex');
data = '0x'+data;
console.log(data);
var rawTx = {
from: fromAddress,
to: toAddress,
nonce: chain3.intToHex(txcount),
gasPrice: chain3.intToHex(gasPrice),
gasLimit: chain3.intToHex(gasLimit),
value: chain3.intToHex(value),
data: data ,
shardingFlag: 0, //default is global contract
chainId: chain3.version.network
};
var signedTx = chain3.signTransaction(rawTx, fromSecret);
mc.sendRawTransaction(signedTx, function(err, hash) {
if (!err){
console.log("succeed: ", hash);
return hash;
}else{
console.log("error:", err);
console.log('raw tx:', rawTx);
}
});
}