Orbits网络

2019.06 月报 (Orbits网络)

目录

  1. 网络层中节点的设计

  2. Bitcoin P2P

  3. Golang

网络层中节点的设计

2019 Q3

  • 行星钱包(Planet Wallet)上线

  • 开发网络层

网络层在GBT协议项目路线图中的推出时间为2019年第三季度,团队也正以此为目标专注于网络层的开发。我们将通过项目本身分析微小节点和超级节点在连接网络后可能出现的各种情况以及各种优缺点。

Orbits网络系统是由公有链和私有链共同组合而成。通过对Bitcoin P2P、Ethereum P2P的运行方式进行大量分析后,微小节点则设计为通过连接Orbits网络而在公有链上运行。在私有链中运行的超级节点则是在对Hyperledger Fabric P2P, Tendermint P2P, EOS P2P的运行方式进行分析后,而使其在Orbits网络中运行。微小节点和超级节点的运行效率都得到了很大程度的提升。

区块链节点间会持续不断地发送和接收区块数据以及相关事务。接收到数据的节点还会将此数据中继至其他节点。如果某个节点向已知节点发送1MB容量的数据区块,并且已知节点将数据发送到网络中的另一个已知节点,则数据在网络中会不断复制传播,此前接收到数据区块的节点会再次接收到相同的数据。

这种方式使得网络的运转效率低下,因此我们须建立一个不需要节点去做重复工作的新秩序。

我们称呼这个新秩序为“协议”(Protocol,通信协议)。

1. 微小节点

GBT Dapp的使用

微小节点中运行着Dapp开发者开发的去中心化应用程序。使用Dapp时,会伴随着GBT的移动以及生成与SC智能合约相关的事务。

交易的验证

  1. 微小节点对GBT的交易进行验证,以此判断该交易是否有效。

  2. 验证者之间以达成的共识为基础,将更新后的GBT状态进行保存。

2. 超级节点

区块的生成

  1. 按顺序识别并确认从微小节点和宏节点传播过来的区块,传播过来的区块若有异常,此区块# 则会被拒绝。

  2. 超级节点记录着关于微小节点和宏节点生成区块的投票结果。

  3. 超级节点把发生在网络系统内的所有交易存入区块中,将数据以区块的形态保留。

微小节点验证池的生成

  1. 超级节点定期以随机的方式选择对微小节点交易进行验证的节点 。

  2. 超级节点对发生在微小节点的交易进行排序,并将其传播至选定的验证池。

  3. 筛选型超级节点通过检查微小节点是否在线以生成验证池。

Bitcoin P2P

1. 节点管理信息

参与到网络的节点们需要掌握周边节点的状况,包括节点的IP地址以及各个节点所支持的功能,还需要确认周边节点是否一直处在活动当中。为此节点间相互传递着version, verack, addr, ping, pong等信息。

Command "addr"
bool AddAddress(CAddrDB& addrdb, const CAddress& addr)
{
if(!addr.IsRoutable())
return false;
if(addr.ip == addrLocalHost.ip)
return false;
CRITICAL_BLOCK(cs_mapAddresses)
{
map<vector<unsigned char>, CAddress>::iterator it =
mapAddresses.find(addr.GetKey());
if(it == mapAddresses.end())
{
//New address
mapAddresses.insert(make_pair(addr.GetKey(), addr));
addrdb.WriteAddress(addr);
return true;
}
else
{
CAddress& addrFound = (*it).second;
if((addrFound.nServices | addr.nServies) != addrFound.nServices)
{
//Services have been added
addrFound.nServices |= addr.nServices;
addrdb.WriteAddress(addrFound);
return true;
}
}
}
return false;
}

2. 区块数据同步

在完全没有区块数据,或只有部分数据的全节点在接入网络时,需要与其他全节点在区块链网络内完成同步。数据不完整的节点需要向其他全节点申请获得区块数据以保持最新同步状态。只有这样才能对交易进行验证,并将区块连接成链。我们把这一过程称之为区块数据同步。

Command "getdata"
class CNode
{
//.....
void PushInventory(const CInv& inv)
{
CRITICAL_BLOCK(cs_inventory)
if(!setInventoryKnown.count(inv))
vInventoryToSend.push_back(inv);
}
//......
}
inline void RelayInventory(const CInv& inv)
{
//Put on lists to offer to the other nodes
CRITICAL_BLOCK(cs_vNodes)
foreach(CNode* pnode, vNodes)
pnode->PushInventory(inv);
}
template<typename T>
void RelayMessage(const CInv& inv, const T& a)
{
CDataStream ss(SER_NETWORK);
ss.reserve(10000);
ss << a;
RelayMessage(inv, ss);
}
template<>
inline void RelayMessage<>(const CInv& inv, const CDataStream& ss)
{
CRITICAL_BLOCK(cs_mapRelay)
{
//Expire old relay messages
while(!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime())
{
mapRelay.erase(vRelayExpiration.front().second);
vRelayExpiration.pop_fron();
}
//Save original serialized message so newer versions are preserved
mapRelay[inv] = ss;
vRelayExpiration.push_back(make_pair(GetTime() + 15 * 60, inv));
}
RelayInventory(inv)
}

3. 新区块数据中继

4. 交易中继

当有新交易发生时,各节点为了确认该交易,则会将其传播至邻近的节点。 交易数据则是通过inv,getdata和tx进行信息传递。此时,数据的类型为MSG_TX或MSG_WITNESS.

Command "tx"
void AddOrphanTx(const CDataStream& vMsg)
{
CTransaction tx;
CDataStream(vMsg) >> tx;
uint256 hash = tx.GetHash();
if(mapOrphanTransactions.count(hash))
return;
CDataStream* pvMsg = mapOrphanTransactions[hash] = new CDataStream(vMsg);
foreach(const CTxIn& txin, tx.vin)
mapOrphanTransactionsByPrev.insert(make_pair(txin.prevout.hash, pvMsg));
}
void EraseOrhpanTx(uint256 hash)
{
if(!mapOrphanTransactions.count(hash))
return;
const CDataStream* pvMsg = mapOrphanTransactions[hash];
CTransaction tx;
CDataStream(*pvMsg) >> tx;
foreach(const CTxIn& txin, tx.vin)
{
for(multimap<uint256, CDataStream*>::iterator mi =
mapOrphanTransactionsByPrev.lower_bound(txin.prevout.hash);
mi != mapOrphanTransactionsByPrev.upper_bound(txin.prevout.hash);)
{
if((*mi).second == pvMsg)
mapOrphanTransactionsByPrev.erase(mi++);
else
mi++;
}
}
delete pvMsg;
mapOrphanTransactions.erase(hash);
}

Golang

Golang适用于开发大型复杂的应用程序,如Web浏览器,服务器,数据库等。 在这个领域,专注于构建逻辑比花时间在内存管理上更为重要。因此,开发者不用把过多的精力放在内存上,仅通过使用Golang,就可以提高工作效率。

使用Golang时,对于内存的管理可以更加灵活。当需要大量精力去维护且又复杂的任务而言,它更加便利且实用。 而且,它可以提供丰富的网络资源,并足以进行互联网编程

Golang 在区块链中的应用

通过使用Goroutine来练习创建webcrawler

package main
import (
"bufio"
"fmt"
"net/http"
"os"
"strings"
"sync"
"github.com/yhat/scrape"
"golang.org/x/net/html"
"golang.org/x/net/html/atom"
)
const (
urlRoot = "http://"
)
func parseMainNodes(n *html.Node) bool {
if n.DataAtom == atom.A && n.Parent != nil {
return scrape.Attr(n.Parent, "class") == "row"
}
return false
}
func errCheck(err error) {
if err != nil {
panic(err)
}
}
var wg sync.WaitGroup
func scrapContents(url string, fn string) {
defer wg.Done()
resp, err := http.Get(url)
errCheck(err)
defer resp.Body.Close()
root, err := html.Parse(resp.Body)
errCheck(err)
matchNode := func(n *html.Node) bool {
return n.DataAtom == atom.A && scrape.Attr(n, "class") == "deco"
}
file, err := os.OpenFile("c:\\scrape\\"+fn+".txt", os.O_CREATE|os.O_RDWR, os.FileMode(0777))
errCheck(err)
defer file.Close()
w := bufio.NewWriter(file)
for _, g := range scrape.FindAll(root, matchNode) {
fmt.Println("result : ", scrape.Text(g))
w.WriteString(scrape.Text(g) + "\r\n")
}
w.Flush()
}
func main() {
resp, err := http.Get(urlRoot)
errCheck(err)
defer resp.Body.Close()
root, err := html.Parse(resp.Body)
errCheck(err)
urlList := scrape.FindAll(root, parseMainNodes)
for _, link := range urlList {
//fmt.Println("Check Main Link : ", link, idx)
//fmt.Println("TargetUrl : ", scrape.Attr(link, "href"))
fileName := strings.Replace(scrape.Attr(link, "href"), "http://", "", 1)
//fmt.Println(strings.Replace("oink oink oink oink", "k", "ky", 2))
//fmt.Println("fileName : ", fileName)
wg.Add(1)
go scrapContents(scrape.Attr(link, "href"), fileName)
}
wg.Wait()
}

参考资料

  1. 파이썬으로 배우는 블록체인 구조와 이론 / 박미정,박은진 / 위키북스

  2. A Dissection of Bitcoin / Paul Huang

  3. 가장 빨리 만나는 Go언어 / 이재홍 / 길벗