一般性的认为,世界是一个相互独立,又相互连接的空间。比如星系之间是相互独立的个体,但是有相互有引力来达到某种平衡。星系内部也是独类而又连接,地球自转不会带动太阳一起转,太阳的光线照向大地,是风、是云、是海水波动、是万物生长。
−−−−Tangshuncai\color{#222514}{-}\color{#222514}{-}\color{#222514}{-}\color{#222514}{-}\color{#4285f4}{T}\color{#ea4335}{a}\color{#fbbc05}{n}\color{#4285f4}{g}\color{#34a853}{s}\color{#ea4335}{h}\color{#4285f4}{u}\color{#fbbc05}{n}\color{#34a853}{c}\color{#ea4335}{a}\color{#fbbc05}{i}−−−−Tangshuncai
实体经济是以制造符合人们生活、娱乐所需的物品,利用自然资源进行加工整合而出现的结果。本质是物品
虚拟经济是以粘合和促进制造业,提升制造业效率的一个辅助结果。本质是符合所有人公信力的凭证
农业、纺织业、食品加工、化工、医疗器械、汽车等属于制造业实体经济
物流、信息流、支付流(信任机制)属于虚拟经济
最有价值的东西:食物、空气
钱乃天地间致公之物,实现独类的物品,人际关系的粘结流通。区块链也是流通的一个小分支,是虚拟经济的一种。据在下所知,大部分做区块链的项目,是以自己为准,以其他行业为辅助,现象就是强行的把各种产品往区块链上面粘贴,很生硬……。这种情况短期可以,长期必然衰落。 区块链应该摒弃执念,回归到服务与其他行业的身份,这样才能开会结果。
我们认为,价值是能帮助别人更好。而不是从别人那里拿取更多,让别人变得不好。
巴菲特说过一句话,“全世界的黄金如果放在一起,大概可以铸成一个高67英尺的巨大金块。但无论如何,不管你对这个金块做什么,你可以在上面跳舞,或者在上面重点庄稼,它都不会实质生产出任何东西”。
区块链技术阶段: 区块链技术1.0:bitcoin(c++) 区块链技术2.0:ETH(golang) 区块链技术3.0:EOS(c++)区块链的应用场景有三个:
存储 边缘计算 cdn存储领域:
storj、filecoin(基于ipfs)、ppio(pptv技术团队)、
边缘计算:
此领域尚处于技术和市场探索阶段
cdn领域:
框机节点分散性很好,有cpu、有内存、有硬盘、有网络,可以作为传统cdn的延申。迅雷的玩客云、百度曾经搞过一个pcdn项目无疾而终,还有一些小型的创业公司。尚未形成规模。
天马星云项目是一个基于ipfs的区块链存储,设计过程参照了亚马逊s3、ppio、storj、bitcoin、EOS的技术框架。
天马星云存储:系统架构图:
cli设计流程:
获取节点->生成文件元数据->提交文件->同步等待存储节点加密文件、存储数据和元数据存储->支付费用token到公共代理服务器。
存储采用多备份机制,暂时没有采用merkle证明机制(之前的某篇博客有讲merkle存储证明)。证明机制采用节点互相证明的方式。每天证明一次文件存在,获取token。
tianma cloud的链、存储节点还在开发中,这里找出最原始的客户端版本发出来:
命令行客户端源码[root@ cli]# ./tianma
usage:
./tianma create-bucket --bucket=test
./tianma put-object --bucket=test --chiprice=100 --expires=2019-04-01 --body=/home/test.png
./tianma get-object-metadata
./tianma get-object --bucket=test --key=QmTMGFSWdpBgCCfeXzu3sHhxWM6VjghKA1S6VKinvLJUMf --chiprice=100 --outfile=/home/test-poss.png
[root@ cli]
// main.cc
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "HttpClient.h"
using namespace std;
#ifdef DEBUG
#define dout(x) {do{std::cout << x << std::endl;}while(0);}
#else
#define dout(x)
#endif
/**
* #purpose : 字符转十六进制
* #note : 不适用于汉字字符
* #param ch : 要转换成十六进制的字符
* #return : 接收转换后的字符串
*/
std::string chToHex(unsigned char ch)
{
const std::string hex = "0123456789ABCDEF";
std::stringstream ss;
ss <> 4] << hex[ch & 0xf];
return ss.str();
}
/**
* #purpose : 字符串转十六进制字符串
* #note : 可用于汉字字符串
* #param str : 要转换成十六进制的字符串
* #param separator : 十六进制字符串间的分隔符
* #return : 接收转换后的字符串
*/
std::string strToHex(std::string str, std::string separator = "")
{
const std::string hex = "0123456789ABCDEF";
std::stringstream ss;
for (std::string::size_type i = 0; i < str.size(); ++i)
ss <> 4] << hex[(unsigned char)str[i] & 0xf] << separator;
return ss.str();
}
#define MAXDATABUFF 1024
#define MD5LENTH 16
string file_md5(char * filename)
{
string strFilePath = filename;
ifstream ifile(strFilePath.c_str(), ios::in | ios::binary); //打开文件
unsigned char MD5result[MD5LENTH];
string hexstr;
do
{
if (ifile.fail()) //打开失败不做文件MD5
{
cout<<"open file failure!so only display string MD5!"<<endl;
break;
}
MD5_CTX md5_ctx;
MD5_Init(&md5_ctx);
char DataBuff[MAXDATABUFF];
while(!ifile.eof())
{
ifile.read(DataBuff,MAXDATABUFF); //读文件
int length = ifile.gcount();
if(length)
{
MD5_Update(&md5_ctx, DataBuff, length); //将当前文件块加入并更新MD5
}
}
MD5_Final(MD5result, &md5_ctx); //获取MD5
//cout << "file MD5:" << endl;
for(int i = 0; i < MD5LENTH; i++) { //将MD5以16进制输出
//cout << hex << (int)MD5result[i];
hexstr += chToHex(MD5result[i]);
}
//cout << endl;
}
while(false);
#if 0
MD5((const unsigned char*)strFilePath.c_str(), strFilePath.size(), MD5result); //获取字符串MD5
cout<<"string MD5:"<<endl;
for(int i = 0; i < MD5LENTH; i++) {
cout << hex << (int)MD5result[i];
hexstr += chToHex(MD5result[i]);
}
cout<<endl;
#endif
return hexstr;
}
//string 转换为time_t 时间格式为2014/03/28 18:25:26
time_t string2time_t(const string string_time)
{
tm tm1;
memset(&tm1, 0, sizeof(tm1));
time_t time1;
sscanf(string_time.c_str(), "%d-%d-%d- %d:%d:%d",
&(tm1.tm_year),
&(tm1.tm_mon),
&(tm1.tm_mday),
&(tm1.tm_hour),
&(tm1.tm_min),
&(tm1.tm_sec));
tm1.tm_year -= 1900;
tm1.tm_mon -= 1;
time1 = mktime(&tm1);
return time1;
}
/*
*/
void usage(string name)
{
cout << "usage:" << endl << endl;
cout << name << " create-bucket --bucket=test" << endl << endl;
cout << name << " put-object --bucket=test --chiprice=100 --expires=2019-04-01 --body=/home/test.png" << endl << endl;
cout << name << " get-object-metadata" << endl << endl;
cout << name << " get-object --bucket=test --key=QmTMGFSWdpBgCCfeXzu3sHhxWM6VjghKA1S6VKinvLJUMf --chiprice=100 --outfile=/home/test-poss.png" << endl << endl;
}
template
Type stringToNum(const string & str)
{
istringstream iss(str);
Type num;
iss >> num;
return num;
}
template
string NumToString(Type num)
{
stringstream ss;
string str;
ss <> str;
return str;
}
int main(int argc, char **argv)
{
if (argc < 2) {
usage(argv[0]);
return -1;
}
if (argv[1] == string("create-bucket")) {
if (argc != 3) {
usage(argv[0]);
return -1;
}
cout << "create buekct ok" << endl;
return 0;
}
else if (argv[1] == string("put-object")) {
if (argc != 6) {
usage(argv[0]);
cout << "argc != 5" << endl;
return -1;
}
if (string(argv[3]).find("--chiprice=") == string::npos) {
dout("no option --chiprice=");
return -1;
}
if (string(argv[4]).find("--expires=") == string::npos) {
dout("no option --expires=");
return -1;
}
if (string(argv[5]).find("--body=") == string::npos) {
dout("no option --body=");
return -1;
}
string price(string(argv[3]).substr(string(argv[3]).find("=") + 1));
string expires(string(argv[4]).substr(string(argv[4]).find("=") + 1));
string body(string(argv[5]).substr(string(argv[5]).find("=") + 1));
dout("price = "); dout(price);
dout("expires = "); dout(expires);
dout("body = "); dout(body);
// 从计费服务器获取在线存储节点列表
string post_result;
Json::Value content;
string url = "http://86.75.75.20:8888/ipfs_billing?";
content["cmd type"] = "get node";
int ret = HttpClient::PostMessage(url, content.toStyledString(), post_result);
if (ret != 0) {
cout << "获取存储节点列表失败" << endl;
return -1;
}
dout(post_result);
// 解析节点列表
vector vs_nodelist;
Json::Reader reader;
Json::Value root, data;
if (reader.parse(post_result, root) && root.isMember("data")) {
data = root["data"];
if (data.isMember("node_list")) {
//cout << "get node list ok" << endl;
}
for (auto i=0; i<data["node_list"].size(); i++) {
vs_nodelist.push_back(data["node_list"][i]["host"].asString() + ":" + data["node_list"][i]["id"].asString());
}
/*for (auto &it : vs_nodelist) {
cout << it << endl;
}*/
}
vector files; // file list
string targetPath = body;
boost::filesystem::path myPath(targetPath);
if (!boost::filesystem::exists(myPath)) {
cout << "please input one exist file/dir" << endl;
return -1;
}
if (boost::filesystem::is_directory(myPath)) {
// multiple file
boost::filesystem::recursive_directory_iterator endIter;
for (boost::filesystem::recursive_directory_iterator iter(myPath); iter != endIter; iter++) {
if (boost::filesystem::is_directory(*iter)) {
//cout << "is dir" << endl;
//cout <path().string() << endl;
} else {
//cout << "is a file" << endl;
//cout <path().string() << endl <path().string());
}
}
}
else {
// single file
files.push_back(myPath.string());
}
string nodeid(vs_nodelist[0]);
string nodeip = string(nodeid.substr(0, nodeid.find_first_of(":")));
string port = string(nodeid.substr(nodeid.find_first_of(":") + 1, nodeid.find_last_of(":") - nodeid.find_first_of(":") -1));
string id = string(nodeid.substr(nodeid.find_last_of(":") + 1));
string fileurl = "http://" + nodeip + ":8000" + "/upload";
//cout << "nodeip" << nodeip << endl;
//cout << "port" << port << endl;
//cout << "id" << id << endl;
cout << "target node is: " << nodeid << endl;
// upload file to first node
for (auto & it : files) {
//cout << it << endl;
string filename = it;
if (0 != HttpClient::HttpPutUpload(fileurl.c_str(), filename.c_str())) {
cout << "upload file failed: " << filename << endl;
return -1;
}
else {
cout << "upload file succeed: " << filename << endl;
}
}
// get files metadata
vector<tuple> vt_file_meta;
uint64_t files_size = 0;
cout << "file metadata list:" << endl;
for (auto & it : files) {
string path = it;
string md5;
uint64_t size;
boost::filesystem::path filePath(path);
path = filePath.filename().string();
md5 = file_md5((char *)it.c_str());
size = boost::filesystem::file_size(filePath);
files_size += size;
cout << " path=" << path << ", md5=" << md5 << ", size=" << size << endl;
vt_file_meta.push_back(make_tuple(path, md5, size));
}
// upload order to first node
/*
{
"cmd type":"storage file",
"contractid":"DD8877dd882c89f8ad1005b886a6b28a094696a05b1139bcd3a425bf510c3259",
"filecnt":3,
"filelist":[
{"path":"file.txt", "md5":"ea8bbad5abfe4abf5c1c215f4fac61df", "size":158},
{"path":"liuyifei.jpeg", "md5":"300f4bee557dcf01430bfb6a3bf40983", "size":6705},
{"path":"afanda.mp4", "md5":"1cc59dab2e526a0539ffe54a7d649de0", "size":26122769}
],
"contractid size":26129632,
"owner id":"4bae99bac755b620b7802ac5889df324db8d267dfec0b9fd8f9540a1d6e17e4a",
"owner name":"zhangsan",
"contract signing time":"2019-12-8 12:00:00",
"contract cancellation time":"2020-12-20 12:00:00",
"real price":130.001,
"node list":[
"86.75.75.21:7777:QmYy6XwAoh1dUidE44r3dtCGwcqgJDchknU3WL5q5mntsf",
"86.75.75.19:7777:QmQf4XByQisXKQBndUfyWYUXMhf1KfLrgAkQddRG4UhD9Q"
]
}
*/
boost::uuids::uuid a_uuid = boost::uuids::random_generator()();
string uuid_string = boost::uuids::to_string(a_uuid);
//cout << uuid_string << endl;
Json::Value contract;
contract["cmd type"] = "storage file";
contract["contractid"] = uuid_string;
contract["filecnt"] = (int)files.size();
for (auto & it : vt_file_meta) {
Json::Value item;
item["path"] = get(it);
item["md5"] = get(it);
item["size"] = (unsigned long long)get(it);
contract["filelist"].append(item);
}
contract["contractid size"] = (unsigned long long)files_size;
contract["owner id"] = "4bae99bac755b620b7802ac5889df324db8d267dfec0b9fd8f9540a1d6e17e4a";
contract["owner name"] = "zhangsan";
contract["contract signing time"] = "2019-12-8 12:00:00";
contract["contract cancellation time"] = expires + " 12:00:00";
contract["real price"] = stringToNum(price);
for (auto & it : vs_nodelist) {
contract["node list"].append(it);
}
post_result;
url = "http://" + nodeip + ":" + port + "/ipfs_cloud?";
ret = HttpClient::PostMessage(url, contract.toStyledString(), post_result);
if (ret != 0) {
cout << "提交订单失败" << endl;
return -1;
}
cout << "commit contract ok, url is:" << url << endl << endl;
// pay token to billing server
// curl http://91.193.100.41:8008/pay/sendtoaddress/mhof2udmXbq8U2W1hDHbaxSxdgpWDSbgkb/130.001
url = "http://91.193.100.41:8008/pay/sendtoaddress/mhof2udmXbq8U2W1hDHbaxSxdgpWDSbgkb/"
+ NumToString((files_size >> 8) * stringToNum(price) * 1);
/*ret = HttpClient::PostMessage(url, string(), post_result);
//ret = HttpClient::PostMessage(0, url, post_result);
if (ret != 0) {
}*/
string txid;
string response;
if (HttpClient::HttpGet(url, response, 300) != 0) {
cout << "pay token to skynet failed, url: " << url << endl;
return -1;
}
else {
Json::Reader reader;
Json::Value root;
if (reader.parse(response, root)) {
if (root.isMember("code")
&& root.isMember("message")
&& (root["message"].asString().compare("ok") == 0)
&& root.isMember("data")) {
txid = root["data"]["result"].asString();
cout << "pay skynet token ok, txid=" << txid << endl << endl;
}
}
}
// 支付信息提交计费服务器
//curl -X post http://86.75.75.20:8888/ipfs_billing? -d"{\"cmd type\":\"pay order\", \"contractid\":\"DD8877dd882c89f8ad1005b886a6b28a094696a05b1139bcd3a425bf510c3259
Json::Value conten;
url = "http://86.75.75.20:8888/ipfs_billing?";
conten["cmd type"] = "pay order";
conten["contractid"] = txid;
ret = HttpClient::PostMessage(url, conten.toStyledString(), post_result);
if (ret != 0) {
cout << "commit txid info to billing failed" << endl;
return -1;
}
// print ok / fail
cout << "send file ok" << endl;
// file fileid list
}
else if(argv[1] == string("get-object-metadata")) {
//cout << "probe flag" << endl;
//return 0;
// get file metadata
// curl -X post http://86.75.75.20:8888/ipfs_billing? -d"{\"cmd type\":\"get metedata\",\"owner id\":\"4bae99bac755b620b7802ac5889df324db8d267dfec0b9fd8f9540a1d6e17e4a\"}"
string url = "http://86.75.75.20:8888/ipfs_billing?";
string post_result;
Json::Value content;
content["cmd type"] = "get metedata";
content["owner id"] = "4bae99bac755b620b7802ac5889df324db8d267dfec0b9fd8f9540a1d6e17e4a";
int ret = HttpClient::PostMessage(url, content.toStyledString(), post_result);
if (ret != 0) {
cout << "commit txid info to billing failed" << endl;
return -1;
}
//print metadata
//cout << post_result << endl;
Json::Reader reader;
Json::Value root;
if (reader.parse(post_result, root)) {
if (root.isMember("data")) {
cout << "fileid filename filesize" << endl;
Json::Value metadata = root["data"]["metadata"];
//cout << "metadata.size()" << metadata.size() << endl;
for (auto i=0; i<metadata.size(); i++) {
Json::Value filelist = metadata[i]["filelist"];
//cout << filelist.toStyledString() << endl;
for (auto j=0; j<filelist.size(); j++) {
string fileid = filelist[j]["hash"].asString();
string filename = filelist[j]["path"].asString();
uint64_t filesize = filelist[j]["size"].asInt64();
cout << fileid << " " << filename << " " << filesize << endl;
}
}
cout << endl;
}
}
}
else if(argv[1] == string("get-object")) {
if (argc != 6) {
usage(argv[0]);
return -1;
}
if (string(argv[3]).find("--key=") == string::npos) {
dout("no option --key=");
return -1;
}
if (string(argv[5]).find("--outfile=") == string::npos) {
dout("no option --outfile=");
return -1;
}
string fileid(string(argv[3]).substr(string(argv[3]).find("=") + 1));
string filename(string(argv[5]).substr(string(argv[5]).find("=") + 1));
// get file
// curl -X post http://86.75.75.21:7777/ipfs_cloud -d"{\"cmd type\":\"get file\", \"file id\":\"QmTMGFSWdpBgCCfeXzu3sHhxWM6VjghKA1S6VKinvLJUMf\"}"
/*
[root@izwz93atpalb56zydy9bpyz cli]# curl -X post http://86.75.75.21:7777/ipfs_cloud -d"{\"cmd type\":\"get file\", \"file id\":\"QmTVY3kqLuv5S6WBKn6hWej7omN9z26QUMLJdvZ8bNG2YT\"}"
{"data":{"url":"http://86.75.75.21:8000/download/download/QmTVY3kqLuv5S6WBKn6hWej7omN9z26QUMLJdvZ8bNG2YT"},"msg":"","status":"0"}
*/
string url = "http://86.75.75.21:7777/ipfs_cloud";
string post_result;
Json::Value content;
content["cmd type"] = "get file";
content["file id"] = fileid;
int ret = HttpClient::PostMessage(url, content.toStyledString(), post_result);
if (ret != 0) {
cout << "commit txid info to billing failed" << endl;
cout << "url: " << url << endl;
cout << "content: " << content.toStyledString() << endl;
return -1;
}
cout << post_result << endl;
string file_url;
Json::Reader reader;
Json::Value root;
if (!reader.parse(post_result, root)) {
return -1;
}
file_url = root["data"]["url"].asString();
cout << file_url << endl;
ret = HttpClient::HttpDownload(file_url.c_str(), filename.c_str());
if (ret != 0) {
cout << "get file failed" << endl;
cout << "ret=" << ret << endl;
return -1;
}
cout << "get file ok!" << endl;
}
}
查看专栏详情
立即解锁全部专栏