天马云--区块链存储--cli客户端

Tallulah ·
更新时间:2024-11-13
· 793 次阅读

前言:

一般性的认为,世界是一个相互独立,又相互连接的空间。比如星系之间是相互独立的个体,但是有相互有引力来达到某种平衡。星系内部也是独类而又连接,地球自转不会带动太阳一起转,太阳的光线照向大地,是风、是云、是海水波动、是万物生长。
−−−−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; } } 查看专栏详情 立即解锁全部专栏
作者:唐一墨



存储 马云 区块链

需要 登录 后方可回复, 如果你还没有账号请 注册新账号