├── yu_flow_chart.png
├── example
├── master
│ ├── build.sh
│ └── main.go
└── client
│ └── client.go
├── config
├── state.go
├── storage.go
└── config.go
├── network
├── interface.go
├── protocol.go
└── netsync.go
├── storage
├── storage_type.go
├── sql
│ ├── interface.go
│ ├── mysql.go
│ ├── sqlite.go
│ └── postgre.go
├── queue
│ ├── interface.go
│ └── nats.go
└── kv
│ ├── interface.go
│ ├── bolt.go
│ ├── tikv.go
│ └── badger.go
├── utils
├── time
│ └── time.go
├── ip
│ └── ip.go
├── error_handle
│ └── error_handle.go
├── codec
│ └── codec.go
└── compress
│ └── zip.go
├── consensus
├── hotstuff
│ ├── proposer_election.go
│ ├── pacemaker.go
│ └── safetyrules.go
└── pow
│ └── simple_pow.go
├── apps
├── poa
│ ├── election.go
│ └── poa.go
├── asset
│ ├── types.go
│ └── transfer.go
└── pow
│ └── pow.go
├── chain_env
└── env.go
├── node
├── worker
│ ├── websocket.go
│ ├── http.go
│ └── worker.go
├── node_keeper
│ └── repo.go
├── network.go
├── master
│ ├── resolve_request.go
│ ├── p2p_pubsub.go
│ ├── p2p_protocol.go
│ ├── http.go
│ ├── websocket.go
│ └── run.go
├── types.go
├── url.go
└── execute_txns.go
├── txn
├── interface.go
├── unsigned.go
├── signed_test.go
└── signed.go
├── tripod
├── tripod.go
├── funcs.go
├── default_tripod.go
├── land.go
└── tripod_meta.go
├── yerror
├── tripod_error.go
└── basic_error.go
├── trie
├── mpt
│ ├── db_test.go
│ ├── database.go
│ ├── trie_test.go
│ ├── errors.go
│ ├── encoding.go
│ ├── proof_test.go
│ └── hasher.go
└── merkle_tree.go
├── keypair
├── ed25519_test.go
├── sr25519_test.go
├── interface.go
├── ed25519.go
└── sr25519.go
├── state
├── store.go
├── kv_test.go
└── kv.go
├── result
├── result.go
├── event.go
└── error.go
├── txpool
├── check.go
├── txpool.go
├── local_txpool.go
└── server_txpool.go
├── context
└── context.go
├── subscribe
└── subscribe.go
├── blockchain
├── header.go
├── interfaces.go
├── chain_struct.go
├── blockbase.go
├── blockchain_scheme.go
├── blockbase_scheme.go
└── block.go
├── go.mod
├── startup
└── startup.go
├── common
├── types.go
└── bytes.go
└── README.md
/yu_flow_chart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Lawliet-Chan/yu/HEAD/yu_flow_chart.png
--------------------------------------------------------------------------------
/example/master/build.sh:
--------------------------------------------------------------------------------
1 | go build -o yu-local
2 |
3 | cp yu-local ~/run-yu/node1/
4 |
5 | cp yu-local ~/run-yu/node2/
6 |
7 | mv yu-local ~/run-yu/node3/
8 |
--------------------------------------------------------------------------------
/config/state.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type StateConf struct {
4 | KV StateKvConf `toml:"kv"`
5 | }
6 |
7 | type StateKvConf struct {
8 | IndexDB KVconf `toml:"index_db"`
9 | NodeBase KVconf `toml:"node_base"`
10 | }
11 |
--------------------------------------------------------------------------------
/example/master/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "github.com/Lawliet-Chan/yu/apps/asset"
5 | "github.com/Lawliet-Chan/yu/apps/pow"
6 | "github.com/Lawliet-Chan/yu/startup"
7 | )
8 |
9 | func main() {
10 | startup.StartUp(pow.NewPow(1024), asset.NewAsset("YuCoin"))
11 | }
12 |
--------------------------------------------------------------------------------
/network/interface.go:
--------------------------------------------------------------------------------
1 | package network
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/chain_env"
5 | "io"
6 | )
7 |
8 | type NetSync interface {
9 | ChooseBestNodes()
10 | HandleSyncReq(rw io.ReadWriter, env *ChainEnv) error
11 | SyncHistory(rw io.ReadWriter, env *ChainEnv) error
12 | }
13 |
--------------------------------------------------------------------------------
/storage/storage_type.go:
--------------------------------------------------------------------------------
1 | package storage
2 |
3 | type StorageType interface {
4 | Type() StoreType
5 | Kind() StoreKind
6 | }
7 |
8 | type StoreType int
9 |
10 | const (
11 | Embedded StoreType = iota
12 | Server
13 | )
14 |
15 | type StoreKind int
16 |
17 | const (
18 | KV StoreKind = iota
19 | Queue
20 | SQL
21 | FS
22 | )
23 |
--------------------------------------------------------------------------------
/utils/time/time.go:
--------------------------------------------------------------------------------
1 | package time
2 |
3 | import "time"
4 |
5 | func NowNanoTS() int64 {
6 | return time.Now().UnixNano()
7 | }
8 |
9 | func NowTS() int64 {
10 | return time.Now().Unix()
11 | }
12 |
13 | func NowNanoTsU64() uint64 {
14 | return uint64(NowNanoTS())
15 | }
16 |
17 | func NowTsU64() uint64 {
18 | return uint64(NowTS())
19 | }
20 |
--------------------------------------------------------------------------------
/consensus/hotstuff/proposer_election.go:
--------------------------------------------------------------------------------
1 | // Copyright Xuperchain Authors
2 | // link: https://github.com/xuperchain/xupercore
3 |
4 | package hotstuff
5 |
6 | type IProposerElection interface {
7 | // 获取指定round的主节点Address, 注意, 若存在validators变更, 则需要在此处进行addrToIntAddr的更新操作
8 | GetLeader(round int64) string
9 | // 获取指定round的候选人节点Address
10 | GetValidators(round int64) []string
11 | // 获取候选人地址到网络地址的映射, for unit test
12 | GetIntAddress(string) string
13 | }
14 |
--------------------------------------------------------------------------------
/utils/ip/ip.go:
--------------------------------------------------------------------------------
1 | package ip
2 |
3 | import "strings"
4 |
5 | func MakePort(port string) string {
6 | if strings.HasPrefix(port, ":") {
7 | return port
8 | }
9 | return ":" + port
10 | }
11 |
12 | func MakeLocalIp(port string) string {
13 | if strings.HasPrefix(port, ":") {
14 | return "localhost" + port
15 | }
16 | return "localhost:" + port
17 | }
18 |
19 | func MakeIp(host, port string) string {
20 | if strings.HasPrefix(port, ":") {
21 | return host + port
22 | }
23 | return host + ":" + port
24 | }
25 |
--------------------------------------------------------------------------------
/apps/poa/election.go:
--------------------------------------------------------------------------------
1 | package poa
2 |
3 | type SimpleElection struct {
4 | addrs []string
5 | }
6 |
7 | func NewSimpleElection(addrs []string) *SimpleElection {
8 | return &SimpleElection{addrs: addrs}
9 | }
10 |
11 | func (s *SimpleElection) GetLeader(round int64) string {
12 | pos := (round - 1) % 3
13 | return s.addrs[pos]
14 | }
15 |
16 | func (s *SimpleElection) GetValidators(round int64) []string {
17 | return s.addrs
18 | }
19 |
20 | func (s *SimpleElection) GetIntAddress(str string) string {
21 | return ""
22 | }
23 |
--------------------------------------------------------------------------------
/chain_env/env.go:
--------------------------------------------------------------------------------
1 | package chain_env
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/blockchain"
5 | . "github.com/Lawliet-Chan/yu/common"
6 | . "github.com/Lawliet-Chan/yu/state"
7 | . "github.com/Lawliet-Chan/yu/subscribe"
8 | . "github.com/Lawliet-Chan/yu/txpool"
9 | "github.com/libp2p/go-libp2p-core/peer"
10 | )
11 |
12 | type ChainEnv struct {
13 | *StateStore
14 | RunMode RunMode
15 | Chain IBlockChain
16 | Base IBlockBase
17 | Pool ItxPool
18 |
19 | P2pID peer.ID
20 |
21 | Sub *Subscription
22 | }
23 |
--------------------------------------------------------------------------------
/node/worker/websocket.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "net/http"
6 | . "yu/node"
7 | . "yu/node/handle"
8 | )
9 |
10 | func (w *Worker) HandleWS() {
11 | http.HandleFunc(ExecApiPath, func(rw http.ResponseWriter, req *http.Request) {
12 | PutWsInTxpool(rw, req, w.txPool, w.readyBcTxnsChan)
13 | })
14 | http.HandleFunc(QryApiPath, func(rw http.ResponseWriter, req *http.Request) {
15 | DoWsQryCall(rw, req, w.land)
16 | })
17 | logrus.Panic(http.ListenAndServe(w.wsPort, nil))
18 | }
19 |
--------------------------------------------------------------------------------
/txn/interface.go:
--------------------------------------------------------------------------------
1 | package txn
2 |
3 | //type IunsignedTxn interface {
4 | // ID() Hash
5 | // Caller() Address
6 | // Ecall() *Ecall
7 | // Timestamp() int64
8 | // Hash() (Hash, error)
9 | // ToSignedTxn() (IsignedTxn, error)
10 | // Encode() ([]byte, error)
11 | // Decode(data []byte) (IunsignedTxn, error)
12 | //}
13 | //
14 | //type IsignedTxn interface {
15 | // Size() int
16 | // GetRaw() IunsignedTxn
17 | // GetTxnHash() Hash
18 | // GetPubkey() PubKey
19 | // GetSignature() []byte
20 | // Encode() ([]byte, error)
21 | //}
22 |
--------------------------------------------------------------------------------
/config/storage.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | type KVconf struct {
4 | // "bolt" "badger" "tikv"
5 | KvType string `toml:"kv_type"`
6 | // dbpath, such as boltdb, pebble
7 | Path string `toml:"path"`
8 | // distributed kvdb
9 | Hosts []string `toml:"hosts"`
10 | }
11 |
12 | type QueueConf struct {
13 | QueueType string `toml:"queue_type"`
14 | Url string `toml:"url"`
15 | // toml, gob, default
16 | Encoder string `toml:"encoder"`
17 | }
18 |
19 | type SqlDbConf struct {
20 | SqlDbType string `toml:"sql_db_type"`
21 | Dsn string `toml:"dsn"`
22 | }
23 |
--------------------------------------------------------------------------------
/storage/sql/interface.go:
--------------------------------------------------------------------------------
1 | package sql
2 |
3 | import (
4 | "github.com/Lawliet-Chan/yu/config"
5 | "github.com/Lawliet-Chan/yu/storage"
6 | "github.com/Lawliet-Chan/yu/yerror"
7 | "gorm.io/gorm"
8 | )
9 |
10 | type SqlDB interface {
11 | storage.StorageType
12 | Db() *gorm.DB
13 | CreateIfNotExist(table interface{}) error
14 | }
15 |
16 | func NewSqlDB(cfg *config.SqlDbConf) (SqlDB, error) {
17 | switch cfg.SqlDbType {
18 | case "sqlite":
19 | return NewSqlite(cfg.Dsn)
20 | case "mysql":
21 | return NewMysql(cfg.Dsn)
22 | case "postgre":
23 | return NewPostgreSql(cfg.Dsn)
24 | default:
25 | return nil, yerror.NoSqlDbType
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/tripod/tripod.go:
--------------------------------------------------------------------------------
1 | package tripod
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/blockchain"
5 | . "github.com/Lawliet-Chan/yu/chain_env"
6 | . "github.com/Lawliet-Chan/yu/txn"
7 | )
8 |
9 | type Tripod interface {
10 | GetTripodMeta() *TripodMeta
11 |
12 | Name() string
13 |
14 | CheckTxn(*SignedTxn) error
15 |
16 | VerifyBlock(block IBlock, env *ChainEnv) bool
17 |
18 | InitChain(env *ChainEnv, land *Land) error
19 |
20 | StartBlock(block IBlock, env *ChainEnv, land *Land) (needBroadcast bool, err error)
21 |
22 | EndBlock(block IBlock, env *ChainEnv, land *Land) error
23 |
24 | FinalizeBlock(block IBlock, env *ChainEnv, land *Land) error
25 | }
26 |
--------------------------------------------------------------------------------
/yerror/tripod_error.go:
--------------------------------------------------------------------------------
1 | package yerror
2 |
3 | import (
4 | "github.com/Lawliet-Chan/yu/common"
5 | "github.com/pkg/errors"
6 | )
7 |
8 | var InsufficientFunds = errors.New("Insufficient Funds")
9 |
10 | type ErrAccountNotFound struct {
11 | account string
12 | }
13 |
14 | func (an ErrAccountNotFound) Error() string {
15 | return errors.Errorf("account(%s) not found", an.account).Error()
16 | }
17 |
18 | func AccountNotFound(addr common.Address) ErrAccountNotFound {
19 | return ErrAccountNotFound{account: addr.String()}
20 | }
21 |
22 | // hotstuff errors
23 | var (
24 | NoValidQC = errors.New("Target QC is empty.")
25 | NoValidParentId = errors.New("ParentId is empty.")
26 | )
27 |
--------------------------------------------------------------------------------
/storage/queue/interface.go:
--------------------------------------------------------------------------------
1 | package queue
2 |
3 | import (
4 | "github.com/Lawliet-Chan/yu/config"
5 | "github.com/Lawliet-Chan/yu/storage"
6 | . "github.com/Lawliet-Chan/yu/yerror"
7 | )
8 |
9 | type Queue interface {
10 | storage.StorageType
11 | Push(topic string, msg []byte) error
12 | Pop(topic string) ([]byte, error)
13 |
14 | // The type of msgChan must be chan!
15 | PushAsync(topic string, msgChan interface{}) error
16 | PopAsync(topic string, msgChan interface{}) error
17 | }
18 |
19 | func NewQueue(cfg *config.QueueConf) (Queue, error) {
20 | switch cfg.QueueType {
21 | case "nats":
22 | return NewNatsQueue(cfg.Url, cfg.Encoder)
23 |
24 | default:
25 | return nil, NoQueueType
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/apps/asset/types.go:
--------------------------------------------------------------------------------
1 | package asset
2 |
3 | import (
4 | "github.com/HyperService-Consortium/go-rlp"
5 | "github.com/sirupsen/logrus"
6 | )
7 |
8 | type Amount uint64
9 |
10 | func (a Amount) MustEncode() []byte {
11 | byt, err := a.Encode()
12 | if err != nil {
13 | logrus.Panic("encode amount error")
14 | }
15 | return byt
16 | }
17 |
18 | func (a Amount) Encode() ([]byte, error) {
19 | return rlp.EncodeToBytes(a)
20 | }
21 |
22 | func MustDecodeToAmount(data []byte) Amount {
23 | a, err := DecodeToAmount(data)
24 | if err != nil {
25 | logrus.Panic("decode amount error")
26 | }
27 | return a
28 | }
29 |
30 | func DecodeToAmount(data []byte) (a Amount, err error) {
31 | err = rlp.DecodeBytes(data, &a)
32 | return
33 | }
34 |
--------------------------------------------------------------------------------
/trie/mpt/db_test.go:
--------------------------------------------------------------------------------
1 | package mpt
2 |
3 | import (
4 | "bytes"
5 | "github.com/Lawliet-Chan/yu/config"
6 | "testing"
7 | )
8 |
9 | func TestDataBaseSetPutandGet(t *testing.T) {
10 | cfg := &config.KVconf{
11 | KvType: "badger",
12 | Path: "./testdb",
13 | }
14 | db, err := NewNodeBase(cfg)
15 | if err != nil {
16 | t.Error(err)
17 | return
18 | }
19 | defer db.Close()
20 | var expGet = []byte("value")
21 | db.Put([]byte("key"), expGet)
22 | var toGet []byte
23 | toGet, err = db.Get([]byte("key"))
24 | if err != nil {
25 | t.Error(err)
26 | return
27 | }
28 | if !bytes.Equal(expGet, toGet) {
29 | t.Error("Put value is not equal to Getting value..., expecting", expGet, "but,", toGet)
30 | return
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/tripod/funcs.go:
--------------------------------------------------------------------------------
1 | package tripod
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/blockchain"
5 | . "github.com/Lawliet-Chan/yu/chain_env"
6 | . "github.com/Lawliet-Chan/yu/common"
7 | . "github.com/Lawliet-Chan/yu/context"
8 | )
9 |
10 | type (
11 | // Developers define the 'Execution' in the pod to let clients call.
12 | // Just like transactions in ETH, extrinsic in Substrate
13 | Execution func(ctx *Context, currentBlock IBlock, env *ChainEnv) error
14 | // Developers define the 'Query' in the pod to let clients query the blockchain.
15 | // This operation has no consensus reached in the blockchain network.
16 | // respObj is a json object
17 | Query func(ctx *Context, env *ChainEnv, blockHash Hash) (respObj interface{}, err error)
18 | )
19 |
--------------------------------------------------------------------------------
/keypair/ed25519_test.go:
--------------------------------------------------------------------------------
1 | package keypair
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "testing"
6 | )
7 |
8 | func TestEdKey(t *testing.T) {
9 | pubkey, privkey, err := GenKeyPair(Ed25519)
10 | if err != nil {
11 | panic("generate key error: " + err.Error())
12 | }
13 | ecall := &Ecall{
14 | TripodName: "asset",
15 | ExecName: "Transfer",
16 | Params: JsonString("params"),
17 | }
18 |
19 | signByt, err := privkey.SignData(ecall.Bytes())
20 | if err != nil {
21 | panic("sign data error: " + err.Error())
22 | }
23 |
24 | genPubkey, err := PubKeyFromBytes(pubkey.BytesWithType())
25 | if err != nil {
26 | panic("gen pubkey error: " + err.Error())
27 | }
28 | t.Logf("verify signature result: %v", genPubkey.VerifySignature(ecall.Bytes(), signByt))
29 | }
30 |
--------------------------------------------------------------------------------
/keypair/sr25519_test.go:
--------------------------------------------------------------------------------
1 | package keypair
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "testing"
6 | )
7 |
8 | func TestSrKey(t *testing.T) {
9 | pubkey, privkey, err := GenKeyPair(Sr25519)
10 | if err != nil {
11 | panic("generate key error: " + err.Error())
12 | }
13 | ecall := &Ecall{
14 | TripodName: "asset",
15 | ExecName: "Transfer",
16 | Params: JsonString("params"),
17 | }
18 |
19 | signByt, err := privkey.SignData(ecall.Bytes())
20 | if err != nil {
21 | panic("sign data error: " + err.Error())
22 | }
23 |
24 | genPubkey, err := PubKeyFromBytes(pubkey.BytesWithType())
25 | if err != nil {
26 | panic("gen pubkey error: " + err.Error())
27 | }
28 | t.Logf("verify signature result: %v", genPubkey.VerifySignature(ecall.Bytes(), signByt))
29 | }
30 |
--------------------------------------------------------------------------------
/storage/sql/mysql.go:
--------------------------------------------------------------------------------
1 | package sql
2 |
3 | import (
4 | "github.com/Lawliet-Chan/yu/storage"
5 | "gorm.io/driver/mysql"
6 | "gorm.io/gorm"
7 | )
8 |
9 | type Mysql struct {
10 | *gorm.DB
11 | }
12 |
13 | func NewMysql(dsn string) (*Mysql, error) {
14 | db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
15 | if err != nil {
16 | return nil, err
17 | }
18 | return &Mysql{db}, nil
19 | }
20 |
21 | func (m *Mysql) Db() *gorm.DB {
22 | return m.DB
23 | }
24 |
25 | func (m *Mysql) CreateIfNotExist(table interface{}) error {
26 | if m.Migrator().HasTable(table) {
27 | return nil
28 | }
29 | return m.Migrator().CreateTable(table)
30 | }
31 |
32 | func (*Mysql) Type() storage.StoreType {
33 | return storage.Server
34 | }
35 |
36 | func (*Mysql) Kind() storage.StoreKind {
37 | return storage.SQL
38 | }
39 |
--------------------------------------------------------------------------------
/utils/error_handle/error_handle.go:
--------------------------------------------------------------------------------
1 | package error_handle
2 |
3 | import (
4 | "fmt"
5 | "github.com/sirupsen/logrus"
6 | "net/http"
7 | )
8 |
9 | func LogfIfErr(err error, format string, args ...interface{}) {
10 | if err != nil {
11 | logrus.Errorf(format, err.Error(), args)
12 | }
13 | }
14 |
15 | func FindNoCallStr(tripodName, callName string, err error) string {
16 | return fmt.Sprintf("find Tripod(%s) Call(%s) error: %s", tripodName, callName, err.Error())
17 | }
18 |
19 | func BadReqHttpResp(w http.ResponseWriter, reason string) {
20 | //w.WriteHeader(http.StatusBadRequest)
21 | //w.Write([]byte(reason))
22 | logrus.Panic(reason)
23 | }
24 |
25 | func ServerErrorHttpResp(w http.ResponseWriter, reason string) {
26 | w.WriteHeader(http.StatusInternalServerError)
27 | w.Write([]byte(reason))
28 | }
29 |
--------------------------------------------------------------------------------
/storage/sql/sqlite.go:
--------------------------------------------------------------------------------
1 | package sql
2 |
3 | import (
4 | "github.com/Lawliet-Chan/yu/storage"
5 | "gorm.io/driver/sqlite"
6 | "gorm.io/gorm"
7 | )
8 |
9 | type Sqlite struct {
10 | *gorm.DB
11 | }
12 |
13 | func NewSqlite(dsn string) (*Sqlite, error) {
14 | db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
15 | if err != nil {
16 | return nil, err
17 | }
18 | return &Sqlite{db}, nil
19 | }
20 |
21 | func (s *Sqlite) Db() *gorm.DB {
22 | return s.DB
23 | }
24 |
25 | func (s *Sqlite) CreateIfNotExist(table interface{}) error {
26 | if s.Migrator().HasTable(table) {
27 | return nil
28 | }
29 | return s.Migrator().CreateTable(table)
30 | }
31 |
32 | func (*Sqlite) Type() storage.StoreType {
33 | return storage.Embedded
34 | }
35 |
36 | func (*Sqlite) Kind() storage.StoreKind {
37 | return storage.SQL
38 | }
39 |
--------------------------------------------------------------------------------
/storage/sql/postgre.go:
--------------------------------------------------------------------------------
1 | package sql
2 |
3 | import (
4 | "github.com/Lawliet-Chan/yu/storage"
5 | "gorm.io/driver/postgres"
6 | "gorm.io/gorm"
7 | )
8 |
9 | type PostgreSql struct {
10 | *gorm.DB
11 | }
12 |
13 | func NewPostgreSql(dsn string) (*PostgreSql, error) {
14 | db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
15 | if err != nil {
16 | return nil, err
17 | }
18 | return &PostgreSql{db}, nil
19 | }
20 |
21 | func (p *PostgreSql) Db() *gorm.DB {
22 | return p.DB
23 | }
24 |
25 | func (p *PostgreSql) CreateIfNotExist(table interface{}) error {
26 | if p.Migrator().HasTable(table) {
27 | return nil
28 | }
29 | return p.Migrator().CreateTable(table)
30 | }
31 |
32 | func (*PostgreSql) Type() storage.StoreType {
33 | return storage.Server
34 | }
35 |
36 | func (*PostgreSql) Kind() storage.StoreKind {
37 | return storage.SQL
38 | }
39 |
--------------------------------------------------------------------------------
/state/store.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | . "github.com/Lawliet-Chan/yu/config"
6 | )
7 |
8 | type StateStore struct {
9 | KVDB *StateKV
10 | }
11 |
12 | func NewStateStore(cfg *StateConf) (*StateStore, error) {
13 | stateKV, err := NewStateKV(&cfg.KV)
14 | if err != nil {
15 | return nil, err
16 | }
17 | return &StateStore{KVDB: stateKV}, nil
18 | }
19 |
20 | func (ss *StateStore) StartBlock(blockHash Hash) {
21 | ss.KVDB.StartBlock(blockHash)
22 | }
23 |
24 | func (ss *StateStore) SetCanRead(blockHash Hash) {
25 | ss.KVDB.SetCanRead(blockHash)
26 | }
27 |
28 | func (ss *StateStore) Commit() (Hash, error) {
29 | return ss.KVDB.Commit()
30 | }
31 |
32 | func (ss *StateStore) Discard() {
33 | ss.KVDB.Discard()
34 | }
35 |
36 | func (ss *StateStore) DiscardAll() {
37 | ss.KVDB.DiscardAll()
38 | }
39 |
40 | func (ss *StateStore) NextTxn() {
41 | ss.KVDB.NextTxn()
42 | }
43 |
--------------------------------------------------------------------------------
/result/result.go:
--------------------------------------------------------------------------------
1 | package result
2 |
3 | import (
4 | "errors"
5 | "strconv"
6 | )
7 |
8 | type Result interface {
9 | Type() ResultType
10 | Encode() ([]byte, error)
11 | Decode(data []byte) error
12 | }
13 |
14 | type ResultType int
15 |
16 | const (
17 | EventType ResultType = iota
18 | ErrorType
19 |
20 | ResultTypeBytesLen = 1
21 | )
22 |
23 | var (
24 | EventTypeByt = []byte(strconv.Itoa(int(EventType)))
25 | ErrorTypeByt = []byte(strconv.Itoa(int(ErrorType)))
26 | )
27 |
28 | // this func use for clients
29 | func DecodeResult(data []byte) (Result, error) {
30 | resultTypeByt := data[:ResultTypeBytesLen]
31 |
32 | resultType, err := strconv.Atoi(string(resultTypeByt))
33 | if err != nil {
34 | return nil, err
35 | }
36 |
37 | switch ResultType(resultType) {
38 | case EventType:
39 | event := &Event{}
40 | err := event.Decode(data)
41 | return event, err
42 | case ErrorType:
43 | er := &Error{}
44 | err := er.Decode(data)
45 | return er, err
46 | }
47 | return nil, errors.New("no result type")
48 | }
49 |
--------------------------------------------------------------------------------
/utils/codec/codec.go:
--------------------------------------------------------------------------------
1 | package codec
2 |
3 | import (
4 | "bytes"
5 | "encoding/gob"
6 | "github.com/ethereum/go-ethereum/rlp"
7 | )
8 |
9 | var GlobalCodec Codec
10 |
11 | type Codec interface {
12 | EncodeToBytes(val interface{}) ([]byte, error)
13 | DecodeBytes(data []byte, val interface{}) error
14 | }
15 |
16 | type RlpCodec struct{}
17 |
18 | func (*RlpCodec) EncodeToBytes(val interface{}) ([]byte, error) {
19 | return rlp.EncodeToBytes(val)
20 | }
21 |
22 | func (*RlpCodec) DecodeBytes(data []byte, val interface{}) error {
23 | return rlp.DecodeBytes(data, val)
24 | }
25 |
26 | type GobCodec struct{}
27 |
28 | func (*GobCodec) EncodeToBytes(val interface{}) ([]byte, error) {
29 | var buf bytes.Buffer
30 | encoder := gob.NewEncoder(&buf)
31 | err := encoder.Encode(val)
32 | if err != nil {
33 | return nil, err
34 | }
35 | return buf.Bytes(), nil
36 | }
37 |
38 | func (*GobCodec) DecodeBytes(data []byte, val interface{}) error {
39 | decoder := gob.NewDecoder(bytes.NewReader(data))
40 | return decoder.Decode(val)
41 | }
42 |
--------------------------------------------------------------------------------
/storage/kv/interface.go:
--------------------------------------------------------------------------------
1 | package kv
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/config"
5 | "github.com/Lawliet-Chan/yu/storage"
6 | . "github.com/Lawliet-Chan/yu/yerror"
7 | )
8 |
9 | type KV interface {
10 | storage.StorageType
11 | Get(key []byte) ([]byte, error)
12 | Set(key []byte, value []byte) error
13 | Delete(key []byte) error
14 | Exist(key []byte) bool
15 | Iter(key []byte) (Iterator, error)
16 | NewKvTxn() (KvTxn, error)
17 | }
18 |
19 | func NewKV(cfg *KVconf) (KV, error) {
20 | switch cfg.KvType {
21 | case "badger":
22 | return NewBadger(cfg.Path)
23 | case "bolt":
24 | return NewBolt(cfg.Path)
25 | //case "tikv":
26 | // return NewTiKV(cfg.Path)
27 |
28 | default:
29 | return nil, NoKvdbType
30 | }
31 | }
32 |
33 | type Iterator interface {
34 | Valid() bool
35 | Next() error
36 | Entry() (key, value []byte, err error)
37 | Close()
38 | }
39 |
40 | type KvTxn interface {
41 | Get([]byte) ([]byte, error)
42 | Set(key, value []byte) error
43 | Delete(key []byte) error
44 | Commit() error
45 | Rollback() error
46 | }
47 |
--------------------------------------------------------------------------------
/node/node_keeper/repo.go:
--------------------------------------------------------------------------------
1 | package node_keeper
2 |
3 | import (
4 | "encoding/json"
5 | "io/ioutil"
6 | "os/exec"
7 | "path/filepath"
8 | )
9 |
10 | const CmdFileName = "cmd"
11 |
12 | type Repo struct {
13 | Name string `json:"name"`
14 | StartCmd string `json:"start_cmd"`
15 | Version int `json:"version"`
16 | }
17 |
18 | func NewRepo(name string, files []string, dir string, version int) (*Repo, error) {
19 | repo := &Repo{
20 | Name: name,
21 | Version: version,
22 | }
23 | for _, file := range files {
24 | if file == filepath.Join(dir, CmdFileName) {
25 | byt, err := ioutil.ReadFile(file)
26 | if err != nil {
27 | return nil, err
28 | }
29 | repo.StartCmd = string(byt)
30 | }
31 | }
32 |
33 | return repo, nil
34 | }
35 |
36 | func (r *Repo) Start() error {
37 | cmd := exec.Command(r.StartCmd)
38 | return cmd.Start()
39 | }
40 |
41 | func (r *Repo) encode() ([]byte, error) {
42 | return json.Marshal(r)
43 | }
44 |
45 | func decodeRepo(byt []byte) (r *Repo, err error) {
46 | err = json.Unmarshal(byt, r)
47 | return
48 | }
49 |
--------------------------------------------------------------------------------
/tripod/default_tripod.go:
--------------------------------------------------------------------------------
1 | package tripod
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/blockchain"
5 | . "github.com/Lawliet-Chan/yu/chain_env"
6 | "github.com/Lawliet-Chan/yu/txn"
7 | )
8 |
9 | type DefaultTripod struct {
10 | *TripodMeta
11 | }
12 |
13 | func NewDefaultTripod(name string) *DefaultTripod {
14 | meta := NewTripodMeta(name)
15 | return &DefaultTripod{
16 | TripodMeta: meta,
17 | }
18 | }
19 |
20 | func (dt *DefaultTripod) GetTripodMeta() *TripodMeta {
21 | return dt.TripodMeta
22 | }
23 |
24 | func (*DefaultTripod) CheckTxn(*txn.SignedTxn) error {
25 | return nil
26 | }
27 |
28 | func (*DefaultTripod) VerifyBlock(IBlock, *ChainEnv) bool {
29 | return true
30 | }
31 |
32 | func (*DefaultTripod) InitChain(*ChainEnv, *Land) error {
33 | return nil
34 | }
35 |
36 | func (*DefaultTripod) StartBlock(IBlock, *ChainEnv, *Land) (bool, error) {
37 | return false, nil
38 | }
39 |
40 | func (*DefaultTripod) EndBlock(IBlock, *ChainEnv, *Land) error {
41 | return nil
42 | }
43 |
44 | func (*DefaultTripod) FinalizeBlock(IBlock, *ChainEnv, *Land) error {
45 | return nil
46 | }
47 |
--------------------------------------------------------------------------------
/txpool/check.go:
--------------------------------------------------------------------------------
1 | package txpool
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/txn"
5 | . "github.com/Lawliet-Chan/yu/yerror"
6 | )
7 |
8 | type TxnCheck func(*SignedTxn) error
9 |
10 | func Check(checks []TxnCheck, stxn *SignedTxn) error {
11 | for _, check := range checks {
12 | err := check(stxn)
13 | if err != nil {
14 | return err
15 | }
16 | }
17 | return nil
18 | }
19 |
20 | //func TripodsCheck(land *Land, stxn *SignedTxn) error {
21 | // return land.RangeList(func(tri Tripod) error {
22 | // return tri.CheckTxn(stxn)
23 | // })
24 | //}
25 |
26 | func checkPoolLimit(txnsInPool []*SignedTxn, poolsize uint64) error {
27 | if uint64(len(txnsInPool)) >= poolsize {
28 | return PoolOverflow
29 | }
30 | return nil
31 | }
32 |
33 | func checkSignature(stxn *SignedTxn) error {
34 | sig := stxn.GetSignature()
35 | ecall := stxn.GetRaw().GetEcall()
36 | if !stxn.GetPubkey().VerifySignature(ecall.Bytes(), sig) {
37 | return TxnSignatureErr
38 | }
39 | return nil
40 | }
41 |
42 | func checkTxnSize(txnMaxSize int, stxn *SignedTxn) error {
43 | if stxn.Size() > txnMaxSize {
44 | return TxnTooLarge
45 | }
46 | return nil
47 | }
48 |
--------------------------------------------------------------------------------
/txpool/txpool.go:
--------------------------------------------------------------------------------
1 | package txpool
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | . "github.com/Lawliet-Chan/yu/txn"
6 | )
7 |
8 | type ItxPool interface {
9 | //NewEmptySignedTxn() *SignedTxn
10 | //NewEmptySignedTxns() SignedTxns
11 |
12 | // return pool size of txpool
13 | PoolSize() uint64
14 | // txpool with the check-functions
15 | WithBaseChecks(checkFns []TxnCheck) ItxPool
16 | WithTripodChecks(checkFns []TxnCheck) ItxPool
17 | // base check txn
18 | BaseCheck(*SignedTxn) error
19 | TripodsCheck(stxn *SignedTxn) error
20 | // use for SyncTxns
21 | NecessaryCheck(stxn *SignedTxn) error
22 | // insert into txpool
23 | Insert(txn *SignedTxn) error
24 | // batch insert into txpool
25 | BatchInsert(txns SignedTxns) error
26 | // package some txns to send to tripods
27 | Pack(numLimit uint64) ([]*SignedTxn, error)
28 | // pacakge txns according to specific conditions
29 | PackFor(numLimit uint64, filter func(*SignedTxn) error) ([]*SignedTxn, error)
30 |
31 | GetTxn(hash Hash) (*SignedTxn, error)
32 |
33 | RemoveTxns(hashes []Hash) error
34 | // remove txns after execute all tripods
35 | Flush() error
36 |
37 | Reset()
38 | }
39 |
--------------------------------------------------------------------------------
/trie/mpt/database.go:
--------------------------------------------------------------------------------
1 | package mpt
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/Lawliet-Chan/yu/config"
6 | "github.com/Lawliet-Chan/yu/storage/kv"
7 | "sync"
8 | )
9 |
10 | type NodeBase struct {
11 | db kv.KV
12 | lock sync.RWMutex
13 | }
14 |
15 | func NewNodeBase(cfg *config.KVconf) (*NodeBase, error) {
16 | db, err := kv.NewKV(cfg)
17 | if err != nil {
18 | return nil, err
19 | }
20 | return &NodeBase{db: db}, nil
21 | }
22 |
23 | func (db *NodeBase) node(hash Hash) node {
24 | enc, err := db.db.Get(hash.Bytes())
25 | if err != nil || enc == nil {
26 | return nil
27 | }
28 | // fmt.Println("node", hex.EncodeToString(hash[:]) , "->", hex.EncodeToString(enc))
29 | return mustDecodeNode(hash.Bytes(), enc)
30 | }
31 |
32 | func (db *NodeBase) Get(toGet []byte) ([]byte, error) {
33 | return db.db.Get(toGet)
34 | }
35 |
36 | func (db *NodeBase) Put(key []byte, value []byte) error {
37 | return db.db.Set(key, value)
38 | }
39 |
40 | func (db *NodeBase) Close() error {
41 | return nil
42 | }
43 |
44 | func (db *NodeBase) insert(hash Hash, blob []byte) {
45 | // fmt.Println("inserting", hash, blob)
46 | db.Put(hash.Bytes(), blob)
47 | }
48 |
--------------------------------------------------------------------------------
/consensus/hotstuff/pacemaker.go:
--------------------------------------------------------------------------------
1 | // Copyright Xuperchain Authors
2 | // link: https://github.com/xuperchain/xupercore
3 |
4 | package hotstuff
5 |
6 | // IPacemaker is the interface of Pacemaker. It responsible for generating a new round.
7 | // We assume Pacemaker in all correct replicas will have synchronized leadership after GST.
8 | // Safty is entirely decoupled from liveness by any potential instantiation of Packmaker.
9 | // Different consensus have different pacemaker implement
10 | type IPacemaker interface {
11 | // CurrentView return current view of this node.
12 | GetCurrentView() int64
13 | // 原NextNewProposal,generate new proposal directly.
14 | AdvanceView(qc IQuorumCert) (bool, error)
15 | }
16 |
17 | // DefaultPaceMaker 是一个IPacemaker的默认实现,我们与IPacemaker放置在一起,方便查看
18 | // IPacemaker的新实现直接直接替代DefaultPaceMaker即可
19 | // The Pacemaker keeps track of votes and of time.
20 | // TODO: the Pacemaker broadcasts a TimeoutMsg notification.
21 | type DefaultPaceMaker struct {
22 | CurrentView int64
23 | // timeout int64
24 | }
25 |
26 | func (p *DefaultPaceMaker) AdvanceView(qc IQuorumCert) (bool, error) {
27 | r := qc.GetProposalView()
28 | if r+1 > p.CurrentView {
29 | p.CurrentView = r + 1
30 | }
31 | return true, nil
32 | }
33 |
34 | func (p *DefaultPaceMaker) GetCurrentView() int64 {
35 | return p.CurrentView
36 | }
37 |
--------------------------------------------------------------------------------
/context/context.go:
--------------------------------------------------------------------------------
1 | package context
2 |
3 | import (
4 | "bytes"
5 | "encoding/json"
6 | . "github.com/Lawliet-Chan/yu/common"
7 | . "github.com/Lawliet-Chan/yu/result"
8 | "github.com/Lawliet-Chan/yu/utils/codec"
9 | "github.com/sirupsen/logrus"
10 | )
11 |
12 | type Context struct {
13 | Caller Address
14 | paramsMap map[string]interface{}
15 | paramsStr JsonString
16 | Events []*Event
17 | Error *Error
18 | }
19 |
20 | func NewContext(caller Address, paramsStr JsonString) (*Context, error) {
21 | var i interface{}
22 | d := json.NewDecoder(bytes.NewReader([]byte(paramsStr)))
23 | d.UseNumber()
24 | err := d.Decode(&i)
25 | if err != nil {
26 | return nil, err
27 | }
28 | return &Context{
29 | Caller: caller,
30 | paramsMap: i.(map[string]interface{}),
31 | paramsStr: paramsStr,
32 | Events: make([]*Event, 0),
33 | }, nil
34 | }
35 |
36 | func (c *Context) EmitEvent(value interface{}) error {
37 | byt, err := codec.GlobalCodec.EncodeToBytes(value)
38 | if err != nil {
39 | logrus.Errorf("encode event to bytes error: %s", err.Error())
40 | return err
41 | }
42 | event := &Event{
43 | Value: string(byt),
44 | }
45 | c.Events = append(c.Events, event)
46 | return nil
47 | }
48 |
49 | func (c *Context) EmitError(e error) {
50 | c.Error = &Error{
51 | Err: e.Error(),
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/node/network.go:
--------------------------------------------------------------------------------
1 | package node
2 |
3 | import (
4 | "bytes"
5 | . "github.com/Lawliet-Chan/yu/blockchain"
6 | "github.com/sirupsen/logrus"
7 | "io"
8 | "io/ioutil"
9 | "net/http"
10 | )
11 |
12 | func SendHeartbeats(addrs []string, handleAlive func(addr string) error, handleDead func(addr string) error) {
13 | for _, addr := range addrs {
14 | _, err := http.Get(addr + HeartbeatPath)
15 | if err != nil {
16 | logrus.Errorf("send heartbeat to (%s) error: %s", addr, err.Error())
17 | err = handleDead(addr)
18 | if err != nil {
19 | logrus.Errorf("handle dead node (%s) error: %s", addr, err.Error())
20 | }
21 | } else {
22 | logrus.Debugf("send heartbeat to (%s) succeed!", addr)
23 | err = handleAlive(addr)
24 | if err != nil {
25 | logrus.Errorf("handle alive node (%s) error: %s", addr, err.Error())
26 | }
27 | }
28 | }
29 |
30 | }
31 |
32 | func PostRequest(url string, body []byte) (*http.Response, error) {
33 | req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(body))
34 | if err != nil {
35 | return nil, err
36 | }
37 | cli := &http.Client{}
38 | return cli.Do(req)
39 | }
40 |
41 | func DecodeBlockFromHttp(body io.ReadCloser, chain IBlockChain) (IBlock, error) {
42 | byt, err := ioutil.ReadAll(body)
43 | if err != nil {
44 | return nil, err
45 | }
46 | return chain.NewEmptyBlock().Decode(byt)
47 | }
48 |
--------------------------------------------------------------------------------
/storage/queue/nats.go:
--------------------------------------------------------------------------------
1 | package queue
2 |
3 | import (
4 | "github.com/Lawliet-Chan/yu/storage"
5 | "github.com/nats-io/nats.go"
6 | )
7 |
8 | type NatsQueue struct {
9 | nc *nats.Conn
10 | ec *nats.EncodedConn
11 | }
12 |
13 | func NewNatsQueue(url, encoder string) (*NatsQueue, error) {
14 | nc, err := nats.Connect(url)
15 | if err != nil {
16 | return nil, err
17 | }
18 | ec, err := nats.NewEncodedConn(nc, encoder)
19 | if err != nil {
20 | return nil, err
21 | }
22 | return &NatsQueue{nc: nc, ec: ec}, nil
23 | }
24 |
25 | func (nq *NatsQueue) Type() storage.StoreType {
26 | return storage.Server
27 | }
28 |
29 | func (nq *NatsQueue) Kind() storage.StoreKind {
30 | return storage.Queue
31 | }
32 |
33 | func (nq *NatsQueue) Push(topic string, msg []byte) error {
34 | err := nq.nc.Publish(topic, msg)
35 | if err != nil {
36 | return err
37 | }
38 | return nq.ec.Flush()
39 | }
40 |
41 | func (nq *NatsQueue) Pop(topic string) (data []byte, err error) {
42 | _, err = nq.nc.Subscribe(topic, func(msg *nats.Msg) {
43 | data = msg.Data
44 | })
45 | return
46 | }
47 |
48 | func (nq *NatsQueue) PushAsync(topic string, channel interface{}) error {
49 | return nq.ec.BindSendChan(topic, channel)
50 | }
51 |
52 | func (nq *NatsQueue) PopAsync(topic string, channel interface{}) error {
53 | _, err := nq.ec.BindRecvChan(topic, channel)
54 | return err
55 | }
56 |
--------------------------------------------------------------------------------
/subscribe/subscribe.go:
--------------------------------------------------------------------------------
1 | package subscribe
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/result"
5 | . "github.com/gorilla/websocket"
6 | "github.com/sirupsen/logrus"
7 | "sync"
8 | )
9 |
10 | type Subscription struct {
11 | // key: *websocket.Conn; value: bool
12 | subscribers sync.Map
13 | resultChan chan Result
14 | }
15 |
16 | func NewSubscription() *Subscription {
17 | s := &Subscription{
18 | subscribers: sync.Map{},
19 | resultChan: make(chan Result, 10),
20 | }
21 | go s.emitToClients()
22 | return s
23 | }
24 |
25 | func (s *Subscription) Register(c *Conn) {
26 | c.SetCloseHandler(func(_ int, _ string) error {
27 | s.subscribers.Delete(c)
28 | return nil
29 | })
30 | s.subscribers.Store(c, true)
31 | }
32 |
33 | func (s *Subscription) Push(result Result) {
34 | s.resultChan <- result
35 | }
36 |
37 | func (s *Subscription) emitToClients() {
38 | for {
39 | select {
40 | case r := <-s.resultChan:
41 | byt, err := r.Encode()
42 | if err != nil {
43 | logrus.Errorf("encode Result error: %s", err.Error())
44 | continue
45 | }
46 |
47 | s.subscribers.Range(func(connI, _ interface{}) bool {
48 | conn := connI.(*Conn)
49 |
50 | err = conn.WriteMessage(BinaryMessage, byt)
51 | if err != nil {
52 | logrus.Errorf("emit result to client(%s) error: %s", conn.RemoteAddr().String(), err.Error())
53 | }
54 | return true
55 | })
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/txn/unsigned.go:
--------------------------------------------------------------------------------
1 | package txn
2 |
3 | import (
4 | "crypto/sha256"
5 | . "github.com/Lawliet-Chan/yu/common"
6 | . "github.com/Lawliet-Chan/yu/utils/codec"
7 | ytime "github.com/Lawliet-Chan/yu/utils/time"
8 | )
9 |
10 | type UnsignedTxn struct {
11 | Id Hash
12 | Caller Address
13 | Ecall *Ecall
14 | Timestamp uint64
15 | }
16 |
17 | func NewUnsignedTxn(caller Address, ecall *Ecall) (*UnsignedTxn, error) {
18 | utxn := &UnsignedTxn{
19 | Caller: caller,
20 | Ecall: ecall,
21 | Timestamp: ytime.NowNanoTsU64(),
22 | }
23 | id, err := utxn.Hash()
24 | if err != nil {
25 | return nil, err
26 | }
27 | utxn.Id = id
28 | return utxn, nil
29 | }
30 |
31 | func (ut *UnsignedTxn) ID() Hash {
32 | return ut.Id
33 | }
34 |
35 | func (ut *UnsignedTxn) GetCaller() Address {
36 | return ut.Caller
37 | }
38 |
39 | func (ut *UnsignedTxn) GetEcall() *Ecall {
40 | return ut.Ecall
41 | }
42 |
43 | func (ut *UnsignedTxn) GetTimestamp() uint64 {
44 | return ut.Timestamp
45 | }
46 |
47 | func (ut *UnsignedTxn) Hash() (Hash, error) {
48 | var hash Hash
49 | byt, err := ut.Encode()
50 | if err != nil {
51 | return NullHash, err
52 | }
53 | hash = sha256.Sum256(byt)
54 | return hash, nil
55 | }
56 |
57 | func (ut *UnsignedTxn) Encode() ([]byte, error) {
58 | return GlobalCodec.EncodeToBytes(ut)
59 | }
60 |
61 | func (ut *UnsignedTxn) Decode(data []byte) (*UnsignedTxn, error) {
62 | err := GlobalCodec.DecodeBytes(data, ut)
63 | return ut, err
64 | }
65 |
--------------------------------------------------------------------------------
/trie/mpt/trie_test.go:
--------------------------------------------------------------------------------
1 | package mpt
2 |
3 | import (
4 | "bytes"
5 | . "github.com/Lawliet-Chan/yu/common"
6 | "github.com/Lawliet-Chan/yu/storage/kv"
7 | "testing"
8 | )
9 |
10 | func TestTrieSetPutandGet(t *testing.T) {
11 | cfg := &kv.KVconf{
12 | KVtype: "badger",
13 | Path: "./testdb",
14 | }
15 | db, err := NewNodeBase(cfg)
16 | if err != nil {
17 | t.Error(err)
18 | return
19 | }
20 | defer db.Close()
21 | var tr *Trie
22 | tr, err = NewTrie(HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), db)
23 | if err != nil {
24 | t.Error(err)
25 | return
26 | }
27 |
28 | var expGet = []byte("value")
29 | tr.Update([]byte("key"), expGet)
30 | tr.Update([]byte("kez"), []byte("error"))
31 | tr.Update([]byte("keyyy"), []byte("error"))
32 | tr.Update([]byte("keyyyyy"), []byte("error"))
33 | tr.Update([]byte("ke"), []byte("error"))
34 |
35 | var toGet []byte
36 | toGet = tr.Get([]byte("key"))
37 | if !bytes.Equal(expGet, toGet) {
38 | t.Error("Put value is not equal to Getting value from memory..., expecting", expGet, "but,", toGet)
39 | return
40 | }
41 |
42 | var trHash Hash
43 | trHash, err = tr.Commit(nil)
44 |
45 | tr = nil
46 | tr, err = NewTrie(trHash, db)
47 | if err != nil {
48 | t.Error(err)
49 | return
50 | }
51 |
52 | toGet = tr.Get([]byte("key"))
53 | if !bytes.Equal(expGet, toGet) {
54 | t.Error("Put value is not equal to Getting value from db..., expecting", expGet, "but,", toGet)
55 | return
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/trie/mpt/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright 2015 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package mpt
18 |
19 | import (
20 | "fmt"
21 | . "github.com/Lawliet-Chan/yu/common"
22 | )
23 |
24 | // MissingNodeError is returned by the trie functions (TryGet, Trgithub.com/Lawliet-Chan/yupdate, TryDelete)
25 | // in the case where a trie node is not present in the local database. It contains
26 | // information necessary for retrieving the missing node.
27 | type MissingNodeError struct {
28 | NodeHash Hash // hash of the missing node
29 | Path []byte // hex-encoded path to the missing node
30 | }
31 |
32 | func (err *MissingNodeError) Error() string {
33 | return fmt.Sprintf("missing trie node %x (path %x)", err.NodeHash, err.Path)
34 | }
35 |
--------------------------------------------------------------------------------
/blockchain/header.go:
--------------------------------------------------------------------------------
1 | package blockchain
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/libp2p/go-libp2p-core/peer"
6 | )
7 |
8 | type Header struct {
9 | PrevHash Hash
10 | Hash Hash
11 | Height BlockNum
12 | TxnRoot Hash
13 | StateRoot Hash
14 | Nonce uint64
15 | Timestamp uint64
16 | PeerID peer.ID
17 |
18 | Pubkey []byte
19 | Signature []byte
20 |
21 | LeiLimit uint64
22 | LeiUsed uint64
23 | }
24 |
25 | func (h *Header) GetHeight() BlockNum {
26 | return h.Height
27 | }
28 |
29 | func (h *Header) GetHash() Hash {
30 | return h.Hash
31 | }
32 |
33 | func (h *Header) GetPrevHash() Hash {
34 | return h.PrevHash
35 | }
36 |
37 | func (h *Header) GetTxnRoot() Hash {
38 | return h.TxnRoot
39 | }
40 |
41 | func (h *Header) GetStateRoot() Hash {
42 | return h.StateRoot
43 | }
44 |
45 | func (h *Header) GetTimestamp() uint64 {
46 | return h.Timestamp
47 | }
48 |
49 | func (h *Header) GetPeerID() peer.ID {
50 | return h.PeerID
51 | }
52 |
53 | func (h *Header) GetLeiLimit() uint64 {
54 | return h.LeiLimit
55 | }
56 |
57 | func (h *Header) GetLeiUsed() uint64 {
58 | return h.LeiUsed
59 | }
60 |
61 | //
62 | //func (h *Header) GetSign() []byte {
63 | // return h.Signature
64 | //}
65 | //
66 | //func (h *Header) GetPubkey() PubKey {
67 | // if h.Pubkey == nil {
68 | // return nil
69 | // }
70 | // pubkey, err := PubKeyFromBytes(h.Pubkey)
71 | // if err != nil {
72 | // logrus.Panic("get pubkey from block-header error: ", err.Error())
73 | // }
74 | // return pubkey
75 | //}
76 |
--------------------------------------------------------------------------------
/keypair/interface.go:
--------------------------------------------------------------------------------
1 | package keypair
2 |
3 | import (
4 | "github.com/Lawliet-Chan/yu/common"
5 | . "github.com/Lawliet-Chan/yu/yerror"
6 | )
7 |
8 | const (
9 | Sr25519 = "sr25519"
10 | Ed25519 = "ed25519"
11 |
12 | Sr25519Idx = "1"
13 | Ed25519Idx = "2"
14 | )
15 |
16 | var KeyTypeBytLen = 1
17 |
18 | func GenKeyPair(keyType string) (PubKey, PrivKey, error) {
19 | switch keyType {
20 | case Sr25519:
21 | pub, priv := genSr25519()
22 | return pub, priv, nil
23 | case Ed25519:
24 | pub, priv := genEd25519()
25 | return pub, priv, nil
26 | default:
27 | return nil, nil, NoKeyType
28 | }
29 | }
30 |
31 | // data: (keyTypeBytes + keyBytes)
32 | func PubKeyFromBytes(data []byte) (PubKey, error) {
33 | keyTypeByt := data[:KeyTypeBytLen]
34 | switch string(keyTypeByt) {
35 | case Sr25519Idx:
36 | return SrPubKeyFromBytes(data[KeyTypeBytLen:]), nil
37 | case Ed25519Idx:
38 | return EdPubKeyFromBytes(data[KeyTypeBytLen:]), nil
39 | default:
40 | return nil, NoKeyType
41 | }
42 | }
43 |
44 | func PubkeyFromStr(data string) (PubKey, error) {
45 | byt := common.FromHex(data)
46 | return PubKeyFromBytes(byt)
47 | }
48 |
49 | type Key interface {
50 | Type() string
51 | Equals(key Key) bool
52 | Bytes() []byte
53 | String() string
54 |
55 | BytesWithType() []byte
56 | StringWithType() string
57 | }
58 |
59 | type PubKey interface {
60 | Key
61 | Address() common.Address
62 | VerifySignature(msg, sig []byte) bool
63 | }
64 |
65 | type PrivKey interface {
66 | Key
67 | SignData([]byte) ([]byte, error)
68 | }
69 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/Lawliet-Chan/yu
2 |
3 | go 1.15
4 |
5 | require (
6 | github.com/BurntSushi/toml v0.3.1
7 | github.com/HyperService-Consortium/go-hexutil v1.0.1
8 | github.com/HyperService-Consortium/go-rlp v1.0.2
9 | github.com/cockroachdb/pebble v0.0.0-20201210152317-024096017eda // indirect
10 | github.com/dgraph-io/badger v1.6.2
11 | github.com/ethereum/go-ethereum v1.10.3
12 | github.com/gin-gonic/gin v1.6.3
13 | github.com/gorilla/websocket v1.4.2
14 | github.com/jackc/pgproto3/v2 v2.0.7 // indirect
15 | github.com/jinzhu/now v1.1.2 // indirect
16 | github.com/libp2p/go-libp2p v0.13.0
17 | github.com/libp2p/go-libp2p-core v0.8.0
18 | github.com/libp2p/go-libp2p-pubsub v0.4.1
19 | github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
20 | github.com/multiformats/go-multiaddr v0.3.1
21 | github.com/nats-io/nats.go v1.10.0
22 | github.com/pingcap/tidb v1.1.0-beta.0.20210108095858-c2ee8d37c22f // indirect
23 | github.com/pkg/errors v0.9.1
24 | github.com/sirupsen/logrus v1.7.0
25 | github.com/tendermint/tendermint v0.34.0
26 | github.com/xuperchain/xupercore v0.0.0-20210727025644-b21e3796e679 // indirect
27 | go.etcd.io/bbolt v1.3.5
28 | golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
29 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 // indirect
30 | golang.org/x/text v0.3.5 // indirect
31 | google.golang.org/grpc/examples v0.0.0-20210405205600-8892a7b247c0 // indirect
32 | gorm.io/driver/mysql v1.0.5
33 | gorm.io/driver/postgres v1.0.8
34 | gorm.io/driver/sqlite v1.1.4
35 | gorm.io/gorm v1.21.4
36 | )
37 |
--------------------------------------------------------------------------------
/state/kv_test.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/Lawliet-Chan/yu/config"
6 | "os"
7 | "testing"
8 | )
9 |
10 | var TestStateKvCfg = &config.StateKvConf{
11 | IndexDB: config.KVconf{KvType: "bolt", Path: "./state_index.db", Hosts: nil},
12 | NodeBase: config.KVconf{
13 | KvType: "bolt",
14 | Path: "./state_base.db",
15 | Hosts: nil,
16 | },
17 | }
18 |
19 | type TestTripod struct{}
20 |
21 | func (tt *TestTripod) Name() string {
22 | return "test-tripod"
23 | }
24 |
25 | func TestKvCommit(t *testing.T) {
26 | statekv, err := NewStateKV(TestStateKvCfg)
27 | if err != nil {
28 | panic("new state-kv error: " + err.Error())
29 | }
30 |
31 | tri := &TestTripod{}
32 |
33 | statekv.Set(tri, []byte("dayu-key"), []byte("dayu-value"))
34 |
35 | statekv.NextTxn()
36 |
37 | stateRoot, err := statekv.Commit()
38 | if err != nil {
39 | t.Fatalf("commit state-kv error: %s", err.Error())
40 | }
41 |
42 | statekv.SetCanRead(NullHash)
43 |
44 | t.Logf("state-root is %s", stateRoot.String())
45 |
46 | value, err := statekv.Get(tri, []byte("dayu-key"))
47 | if err != nil {
48 | t.Fatalf("get state-kv error: %s", err.Error())
49 | }
50 | t.Logf("Get value is %s", string(value))
51 |
52 | value, err = statekv.GetByBlockHash(tri, []byte("dayu-key"), NullHash)
53 | if err != nil {
54 | t.Fatalf("get state-kv by blockHash error: %s", err.Error())
55 | }
56 | t.Logf("Get value by blockHash is %s", string(value))
57 |
58 | removeTestDB()
59 | }
60 |
61 | func removeTestDB() {
62 | os.RemoveAll(TestStateKvCfg.NodeBase.Path)
63 | os.RemoveAll(TestStateKvCfg.IndexDB.Path)
64 | }
65 |
--------------------------------------------------------------------------------
/node/master/resolve_request.go:
--------------------------------------------------------------------------------
1 | package master
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | . "github.com/Lawliet-Chan/yu/node"
6 | . "github.com/Lawliet-Chan/yu/txn"
7 | "github.com/gin-gonic/gin"
8 | "net/http"
9 | "net/http/httputil"
10 | )
11 |
12 | func getQryInfoFromReq(req *http.Request, params JsonString) (qcall *Qcall, err error) {
13 | tripodName, qryName := GetTripodCallName(req)
14 | blockHash := GetBlockHash(req)
15 | qcall = &Qcall{
16 | TripodName: tripodName,
17 | QueryName: qryName,
18 | Params: params,
19 | BlockHash: blockHash,
20 | }
21 | return
22 | }
23 |
24 | func getExecInfoFromReq(req *http.Request, params JsonString) (tripodName, execName string, stxn *SignedTxn, err error) {
25 | tripodName, execName = GetTripodCallName(req)
26 | ecall := &Ecall{
27 | TripodName: tripodName,
28 | ExecName: execName,
29 | Params: params,
30 | }
31 | caller := GetAddress(req)
32 | sig := GetSignature(req)
33 | pubkey, err := GetPubkey(req)
34 | if err != nil {
35 | return
36 | }
37 | stxn, err = NewSignedTxn(caller, ecall, pubkey, sig)
38 | return
39 | }
40 |
41 | func getHttpJsonParams(c *gin.Context) (params JsonString, err error) {
42 | if c.Request.Method == http.MethodPost {
43 | params, err = readPostBody(c.Request.Body)
44 | if err != nil {
45 | return
46 | }
47 | } else {
48 | params = c.GetString(PARAMS_KEY)
49 | }
50 | return
51 | }
52 |
53 | func forwardQueryToWorker(ip string, rw http.ResponseWriter, req *http.Request) {
54 | director := func(req *http.Request) {
55 | req.URL.Host = ip
56 | }
57 | proxy := &httputil.ReverseProxy{Director: director}
58 | proxy.ServeHTTP(rw, req)
59 | }
60 |
--------------------------------------------------------------------------------
/trie/merkle_tree.go:
--------------------------------------------------------------------------------
1 | package trie
2 |
3 | import (
4 | "crypto/sha256"
5 | . "github.com/Lawliet-Chan/yu/common"
6 | )
7 |
8 | // MerkleTree represent a Merkle tree
9 | type MerkleTree struct {
10 | RootNode *MerkleNode
11 | }
12 |
13 | // MerkleNode represent a Merkle tree node
14 | type MerkleNode struct {
15 | Left *MerkleNode
16 | Right *MerkleNode
17 | Data Hash
18 | }
19 |
20 | // NewMerkleTree creates a new Merkle tree from a sequence of data
21 | func NewMerkleTree(data []Hash) *MerkleTree {
22 | if len(data) == 0 {
23 | return &MerkleTree{RootNode: &MerkleNode{
24 | Left: nil,
25 | Right: nil,
26 | Data: NullHash,
27 | }}
28 | }
29 |
30 | var nodes []MerkleNode
31 |
32 | if len(data)%2 != 0 {
33 | data = append(data, data[len(data)-1])
34 | }
35 |
36 | for _, datum := range data {
37 | node := newMerkleNode(nil, nil, datum)
38 | nodes = append(nodes, *node)
39 | }
40 |
41 | for i := 0; i < len(data)/2; i++ {
42 | var newLevel []MerkleNode
43 |
44 | for j := 0; j < len(nodes); j += 2 {
45 | node := newMerkleNode(&nodes[j], &nodes[j+1], NullHash)
46 | newLevel = append(newLevel, *node)
47 | }
48 |
49 | nodes = newLevel
50 | }
51 |
52 | mTree := MerkleTree{&nodes[0]}
53 |
54 | return &mTree
55 | }
56 |
57 | // NewMerkleNode creates a new Merkle tree node
58 | func newMerkleNode(left, right *MerkleNode, data Hash) *MerkleNode {
59 | mNode := MerkleNode{}
60 |
61 | if left == nil && right == nil {
62 | mNode.Data = sha256.Sum256(data.Bytes())
63 | } else {
64 | prevHashes := append(left.Data.Bytes(), right.Data.Bytes()...)
65 | mNode.Data = sha256.Sum256(prevHashes)
66 | }
67 |
68 | mNode.Left = left
69 | mNode.Right = right
70 |
71 | return &mNode
72 | }
73 |
--------------------------------------------------------------------------------
/node/types.go:
--------------------------------------------------------------------------------
1 | package node
2 |
3 | import (
4 | "encoding/json"
5 | "reflect"
6 | )
7 |
8 | type WorkerInfo struct {
9 | Name string `json:"name"`
10 | HttpPort string `json:"http_port"`
11 | WsPort string `json:"ws_port"`
12 | NodeKeeperAddr string `json:"node_keeper_addr"`
13 | // Key: Tripod Name
14 | TripodsInfo map[string]TripodInfo `json:"tripods_info"`
15 | Online bool `json:"online"`
16 | }
17 |
18 | type TripodInfo struct {
19 | // Executions Names
20 | ExecNames []string `json:"exec_names"`
21 | // Queries Names
22 | QueryNames []string `json:"query_names"`
23 | }
24 |
25 | func (wi *WorkerInfo) EncodeMasterInfo() ([]byte, error) {
26 | return json.Marshal(wi)
27 | }
28 |
29 | func DecodeWorkerInfo(data []byte) (*WorkerInfo, error) {
30 | var info WorkerInfo
31 | err := json.Unmarshal(data, &info)
32 | if err != nil {
33 | return nil, err
34 | }
35 | return &info, nil
36 | }
37 |
38 | type NodeKeeperInfo struct {
39 | OsArch string `json:"os_arch"`
40 | // key: Worker's Addr
41 | WorkersInfo map[string]WorkerInfo `json:"workers_info"`
42 | ServesPort string `json:"serves_port"`
43 | Online bool `json:"online"`
44 | }
45 |
46 | func (nki NodeKeeperInfo) Equals(other NodeKeeperInfo) bool {
47 | return nki.OsArch == other.OsArch && reflect.DeepEqual(nki.WorkersInfo, other.WorkersInfo)
48 | }
49 |
50 | func (nki *NodeKeeperInfo) EncodeNodeKeeperInfo() ([]byte, error) {
51 | return json.Marshal(nki)
52 | }
53 |
54 | func DecodeNodeKeeperInfo(data []byte) (*NodeKeeperInfo, error) {
55 | var info NodeKeeperInfo
56 | err := json.Unmarshal(data, &info)
57 | if err != nil {
58 | return nil, err
59 | }
60 | return &info, nil
61 | }
62 |
--------------------------------------------------------------------------------
/result/event.go:
--------------------------------------------------------------------------------
1 | package result
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | . "github.com/Lawliet-Chan/yu/common"
7 | )
8 |
9 | type Event struct {
10 | Caller Address `json:"caller"`
11 | BlockStage string `json:"block_stage"`
12 | BlockHash Hash `json:"block_hash"`
13 | Height BlockNum `json:"height"`
14 | TripodName string `json:"tripod_name"`
15 | ExecName string `json:"exec_name"`
16 | Value string `json:"value"`
17 | }
18 |
19 | func (e *Event) Encode() ([]byte, error) {
20 | byt, err := json.Marshal(e)
21 | if err != nil {
22 | return nil, err
23 | }
24 | return append(EventTypeByt, byt...), nil
25 | }
26 |
27 | func (e *Event) Decode(data []byte) error {
28 | return json.Unmarshal(data[ResultTypeBytesLen:], e)
29 | }
30 |
31 | func (e *Event) Type() ResultType {
32 | return EventType
33 | }
34 |
35 | func (e *Event) Sprint() (str string) {
36 | if e.BlockStage == ExecuteTxnsStage {
37 | str = fmt.Sprintf(
38 | "[Event] Caller(%s) call Tripod(%s) Execution(%s) in Block(%s) on Height(%d): %s",
39 | e.Caller.String(),
40 | e.TripodName,
41 | e.ExecName,
42 | e.BlockHash.String(),
43 | e.Height,
44 | e.Value,
45 | )
46 | } else {
47 | str = fmt.Sprintf(
48 | "[Event] %s Block(%s) on Height(%d) in Tripod(%s): %s",
49 | e.BlockStage,
50 | e.BlockHash.String(),
51 | e.Height,
52 | e.TripodName,
53 | e.Value,
54 | )
55 | }
56 | return
57 | }
58 |
59 | //type Events []Event
60 | //
61 | //func ToEvents(events []Event) Events {
62 | // var es Events
63 | // es = append(es, events...)
64 | // return es
65 | //}
66 | //
67 | //func (es Events) ToArray() []Event {
68 | // return es[:]
69 | //}
70 | //
71 | //func (es Events) Encode() ([]byte, error) {
72 | // return GobEncode(es)
73 | //}
74 |
--------------------------------------------------------------------------------
/result/error.go:
--------------------------------------------------------------------------------
1 | package result
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | . "github.com/Lawliet-Chan/yu/common"
7 | )
8 |
9 | type Error struct {
10 | Caller Address `json:"caller"`
11 | BlockStage string `json:"block_stage"`
12 | BlockHash Hash `json:"block_hash"`
13 | Height BlockNum `json:"height"`
14 | TripodName string `json:"tripod_name"`
15 | ExecName string `json:"exec_name"`
16 | Err string `json:"err"`
17 | }
18 |
19 | func (e *Error) Type() ResultType {
20 | return ErrorType
21 | }
22 |
23 | func (e *Error) Error() (str string) {
24 | if e.BlockStage == ExecuteTxnsStage {
25 | str = fmt.Sprintf(
26 | "[Error] Caller(%s) call Tripod(%s) Execution(%s) in Block(%s) on Height(%d): %s",
27 | e.Caller.String(),
28 | e.TripodName,
29 | e.ExecName,
30 | e.BlockHash.String(),
31 | e.Height,
32 | e.Err,
33 | )
34 | } else {
35 | str = fmt.Sprintf(
36 | "[Error] %s Block(%s) on Height(%d) in Tripod(%s): %s",
37 | e.BlockStage,
38 | e.BlockHash.String(),
39 | e.Height,
40 | e.TripodName,
41 | e.Err,
42 | )
43 | }
44 | return
45 | }
46 |
47 | func (e *Error) Encode() ([]byte, error) {
48 | byt, err := json.Marshal(e)
49 | if err != nil {
50 | return nil, err
51 | }
52 | byt = append(ErrorTypeByt, byt...)
53 | return byt, nil
54 | }
55 |
56 | func (e *Error) Decode(data []byte) error {
57 | return json.Unmarshal(data[ResultTypeBytesLen:], e)
58 | }
59 |
60 | //
61 | //type Errors []Error
62 | //
63 | //func ToErrors(errors []Error) Errors {
64 | // var es Errors
65 | // es = append(es, errors...)
66 | // return es
67 | //}
68 | //
69 | //func (es Errors) ToArray() []Error {
70 | // return es[:]
71 | //}
72 | //
73 | //func (es Errors) Encode() ([]byte, error) {
74 | // return GobEncode(es)
75 | //}
76 |
--------------------------------------------------------------------------------
/txn/signed_test.go:
--------------------------------------------------------------------------------
1 | package txn
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | . "github.com/Lawliet-Chan/yu/keypair"
6 | "github.com/Lawliet-Chan/yu/utils/codec"
7 | "strconv"
8 | "testing"
9 | )
10 |
11 | func TestSignedTxns_Remove(t *testing.T) {
12 | codec.GlobalCodec = &codec.RlpCodec{}
13 |
14 | pubkey, privKey, err := GenKeyPair(Sr25519)
15 | if err != nil {
16 | panic("generate key error: " + err.Error())
17 | }
18 |
19 | var (
20 | txns SignedTxns
21 | txns1 = make(SignedTxns, 3)
22 | txns2 = make(SignedTxns, 3)
23 | hashes []Hash
24 | )
25 | for i := 0; i < 3; i++ {
26 | istr := strconv.Itoa(i)
27 | ecall := &Ecall{
28 | TripodName: istr,
29 | ExecName: istr,
30 | Params: JsonString(istr),
31 | }
32 | sig, err := privKey.SignData(ecall.Bytes())
33 | if err != nil {
34 | t.Fatalf("sign data error: %s", err.Error())
35 | }
36 | stxn, err := NewSignedTxn(pubkey.Address(), ecall, pubkey, sig)
37 | if err != nil {
38 | t.Fatalf("new SignedTxn error: %s", err.Error())
39 | }
40 |
41 | txns = append(txns, stxn)
42 |
43 | hashes = append(hashes, stxn.GetTxnHash())
44 | }
45 |
46 | copy(txns1, txns[:])
47 | copy(txns2, txns[:])
48 |
49 | removeIdx, restTxns := txns.Remove(hashes[0])
50 | t.Logf("remove index is %d", removeIdx)
51 | for _, stxn := range restTxns {
52 | t.Logf("After removed 0, txn %s", stxn.GetRaw().Ecall)
53 | }
54 |
55 | removeIdx, restTxns1 := txns1.Remove(hashes[1])
56 | t.Logf("remove index is %d", removeIdx)
57 | for _, stxn := range restTxns1 {
58 | t.Logf("After removed 1, txn %s", stxn.GetRaw().Ecall)
59 | }
60 |
61 | removeIdx, restTxns2 := txns2.Remove(hashes[2])
62 | t.Logf("remove index is %d", removeIdx)
63 | for _, stxn := range restTxns2 {
64 | t.Logf("After removed 2, txn %s", stxn.GetRaw().Ecall)
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/tripod/land.go:
--------------------------------------------------------------------------------
1 | package tripod
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/chain_env"
5 | . "github.com/Lawliet-Chan/yu/common"
6 | . "github.com/Lawliet-Chan/yu/context"
7 | . "github.com/Lawliet-Chan/yu/yerror"
8 | )
9 |
10 | type Land struct {
11 | orderedTripods []Tripod
12 | // Key: the Name of Tripod
13 | tripodsMap map[string]Tripod
14 | }
15 |
16 | func NewLand() *Land {
17 | return &Land{
18 | tripodsMap: make(map[string]Tripod),
19 | orderedTripods: make([]Tripod, 0),
20 | }
21 | }
22 |
23 | func (l *Land) SetTripods(Tripods ...Tripod) {
24 | for _, tri := range Tripods {
25 | triName := tri.GetTripodMeta().Name()
26 | l.tripodsMap[triName] = tri
27 |
28 | l.orderedTripods = append(l.orderedTripods, tri)
29 | }
30 | }
31 |
32 | func (l *Land) GetExecLei(c *Ecall) (Execution, uint64, error) {
33 | tripod, ok := l.tripodsMap[c.TripodName]
34 | if !ok {
35 | return nil, 0, TripodNotFound(c.TripodName)
36 | }
37 | ph := tripod.GetTripodMeta()
38 | fn, lei := ph.GetExec(c.ExecName)
39 | if fn == nil {
40 | return nil, 0, ExecNotFound(c.ExecName)
41 | }
42 | return fn, lei, nil
43 | }
44 |
45 | func (l *Land) Query(c *Qcall, ctx *Context, env *ChainEnv) (interface{}, error) {
46 | Tripod, ok := l.tripodsMap[c.TripodName]
47 | if !ok {
48 | return nil, TripodNotFound(c.TripodName)
49 | }
50 | ph := Tripod.GetTripodMeta()
51 | qry := ph.GetQuery(c.QueryName)
52 | if qry == nil {
53 | return nil, QryNotFound(c.QueryName)
54 | }
55 | return qry(ctx, env, c.BlockHash)
56 | }
57 |
58 | func (l *Land) RangeMap(fn func(string, Tripod) error) error {
59 | for name, tri := range l.tripodsMap {
60 | err := fn(name, tri)
61 | if err != nil {
62 | return err
63 | }
64 | }
65 | return nil
66 | }
67 |
68 | func (l *Land) RangeList(fn func(Tripod) error) error {
69 | for _, tri := range l.orderedTripods {
70 | err := fn(tri)
71 | if err != nil {
72 | return err
73 | }
74 | }
75 | return nil
76 | }
77 |
--------------------------------------------------------------------------------
/apps/poa/poa.go:
--------------------------------------------------------------------------------
1 | package poa
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/blockchain"
5 | . "github.com/Lawliet-Chan/yu/chain_env"
6 | . "github.com/Lawliet-Chan/yu/consensus/hotstuff"
7 | . "github.com/Lawliet-Chan/yu/tripod"
8 | . "github.com/Lawliet-Chan/yu/txn"
9 | )
10 |
11 | type Poa struct {
12 | meta *TripodMeta
13 |
14 | smr *Smr
15 | }
16 |
17 | func NewPoa(addr string, addrs []string) *Poa {
18 | meta := NewTripodMeta("poa")
19 |
20 | q := InitQcTee()
21 | saftyrules := &DefaultSaftyRules{
22 | QcTree: q,
23 | }
24 | elec := NewSimpleElection(addrs)
25 | smr := NewSmr(addr, &DefaultPaceMaker{}, saftyrules, elec, q)
26 |
27 | return &Poa{
28 | meta: meta,
29 | smr: smr,
30 | }
31 | }
32 |
33 | func (p *Poa) GetTripodMeta() *TripodMeta {
34 | return p.meta
35 | }
36 |
37 | func (p *Poa) Name() string {
38 | return p.meta.Name()
39 | }
40 |
41 | func (p *Poa) CheckTxn(txn *SignedTxn) error {
42 | return nil
43 | }
44 |
45 | func (p *Poa) VerifyBlock(block IBlock, env *ChainEnv) bool {
46 | panic("implement me")
47 | }
48 |
49 | func (p *Poa) InitChain(env *ChainEnv, _ *Land) error {
50 | chain := env.Chain
51 | gensisBlock := &Block{
52 | Header: &Header{},
53 | }
54 | return chain.SetGenesis(gensisBlock)
55 | }
56 |
57 | func (p *Poa) StartBlock(block IBlock, env *ChainEnv, land *Land) (needBroadcast bool, err error) {
58 | panic("implement me")
59 | }
60 |
61 | func (p *Poa) EndBlock(block IBlock, env *ChainEnv, land *Land) error {
62 | panic("implement me")
63 | }
64 |
65 | func (p *Poa) FinalizeBlock(block IBlock, env *ChainEnv, land *Land) error {
66 | panic("implement me")
67 | }
68 |
69 | func InitQcTee() *QCPendingTree {
70 | initQC := &QuorumCert{
71 | VoteInfo: &VoteInfo{
72 | ProposalId: []byte{0},
73 | ProposalView: 0,
74 | },
75 | LedgerCommitInfo: &LedgerCommitInfo{
76 | CommitStateId: []byte{0},
77 | },
78 | }
79 | rootNode := &ProposalNode{
80 | In: initQC,
81 | }
82 | return &QCPendingTree{
83 | Genesis: rootNode,
84 | Root: rootNode,
85 | HighQC: rootNode,
86 | CommitQC: rootNode,
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/consensus/pow/simple_pow.go:
--------------------------------------------------------------------------------
1 | package pow
2 |
3 | import (
4 | "bytes"
5 | "crypto/sha256"
6 | "encoding/binary"
7 | . "github.com/Lawliet-Chan/yu/blockchain"
8 | "github.com/Lawliet-Chan/yu/common"
9 | "github.com/sirupsen/logrus"
10 | "math"
11 | "math/big"
12 | )
13 |
14 | func Run(block IBlock, target *big.Int, targetBits int64) (nonce int64, hash common.Hash, err error) {
15 | var hashInt big.Int
16 | nonce = 0
17 |
18 | logrus.Info("[[[Mining a new Block!!!]]]")
19 | for nonce < math.MaxInt64 {
20 | var data []byte
21 | data, err = prepareData(block, nonce, targetBits)
22 | if err != nil {
23 | return
24 | }
25 | hash = sha256.Sum256(data)
26 | if math.Remainder(float64(nonce), 100000) == 0 {
27 | logrus.Infof("Hash is \r%x", hash.Bytes())
28 | }
29 | hashInt.SetBytes(hash.Bytes())
30 |
31 | if hashInt.Cmp(target) == -1 {
32 | break
33 | } else {
34 | nonce++
35 | }
36 | }
37 | return
38 | }
39 |
40 | func Validate(block IBlock, target *big.Int, targetBits int64) bool {
41 | var hashInt big.Int
42 |
43 | var nonce uint64 = block.GetHeader().(*Header).Nonce
44 | data, err := prepareData(block, int64(nonce), targetBits)
45 | if err != nil {
46 | return false
47 | }
48 | hash := sha256.Sum256(data)
49 | hashInt.SetBytes(hash[:])
50 |
51 | return hashInt.Cmp(target) == -1
52 | }
53 |
54 | func prepareData(block IBlock, nonce, targetBits int64) ([]byte, error) {
55 | num := block.GetTimestamp()
56 | hex1, err := intToHex(int64(num))
57 | if err != nil {
58 | return nil, err
59 | }
60 | hex2, err := intToHex(targetBits)
61 | if err != nil {
62 | return nil, err
63 | }
64 | hex3, err := intToHex(nonce)
65 | if err != nil {
66 | return nil, err
67 | }
68 | data := bytes.Join(
69 | [][]byte{
70 | block.GetPrevHash().Bytes(),
71 | block.GetTxnRoot().Bytes(),
72 | hex1,
73 | hex2,
74 | hex3,
75 | },
76 | []byte{},
77 | )
78 |
79 | return data, nil
80 | }
81 |
82 | func intToHex(num int64) ([]byte, error) {
83 | buff := new(bytes.Buffer)
84 | err := binary.Write(buff, binary.BigEndian, num)
85 | if err != nil {
86 | return nil, err
87 | }
88 |
89 | return buff.Bytes(), nil
90 | }
91 |
--------------------------------------------------------------------------------
/node/url.go:
--------------------------------------------------------------------------------
1 | package node
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/Lawliet-Chan/yu/keypair"
6 | "net/http"
7 | "path/filepath"
8 | )
9 |
10 | const (
11 | DownloadUpdatedPath = "/download/upgrade"
12 | RegisterNodeKeepersPath = "/nodekeeper/register"
13 | RegisterWorkersPath = "/worker/register"
14 | HeartbeatPath = "/heartbeat"
15 |
16 | // Worker accept block from p2p network.
17 | // Master forwards this request to Worker.
18 | // Deprecated
19 | BlockFromP2P = "/p2p/block"
20 |
21 | // Worker accept txns from p2p network.
22 | // Master forwards this request to Worker.
23 | // Deprecated
24 | TxnsFromP2P = "/p2p/txns"
25 |
26 | StartBlockPath = "/block/start"
27 | EndBlockPath = "/block/end"
28 | FinalizeBlockPath = "/block/finalize"
29 |
30 | CheckTxnsPath = "/txns/check"
31 | ExecuteTxnsPath = "/txns/execute"
32 |
33 | // For developers, every customized Execution and Query of tripods
34 | // will base on '/api'.
35 | RootApiPath = "/api"
36 |
37 | TripodNameKey = "tripod"
38 | CallNameKey = "call_name"
39 | AddressKey = "address"
40 | BlockHashKey = "block_hash"
41 | PubkeyKey = "pubkey"
42 | SignatureKey = "signature"
43 | )
44 |
45 | var (
46 | ExecApiPath = filepath.Join(RootApiPath, ExecCallType)
47 | QryApiPath = filepath.Join(RootApiPath, QryCallType)
48 | SubResultsPath = "/subscribe/results"
49 | )
50 |
51 | // return (Tripod Name, Execution/Query Name)
52 | func GetTripodCallName(req *http.Request) (string, string) {
53 | query := req.URL.Query()
54 | return query.Get(TripodNameKey), query.Get(CallNameKey)
55 | }
56 |
57 | // return the Address of Txn-Sender
58 | func GetAddress(req *http.Request) Address {
59 | return HexToAddress(req.URL.Query().Get(AddressKey))
60 | }
61 |
62 | func GetBlockHash(req *http.Request) Hash {
63 | return HexToHash(req.URL.Query().Get(BlockHashKey))
64 | }
65 |
66 | func GetPubkey(req *http.Request) (keypair.PubKey, error) {
67 | pubkeyStr := req.URL.Query().Get(PubkeyKey)
68 | return keypair.PubkeyFromStr(pubkeyStr)
69 | }
70 |
71 | func GetSignature(req *http.Request) []byte {
72 | signStr := req.URL.Query().Get(SignatureKey)
73 | return FromHex(signStr)
74 | }
75 |
--------------------------------------------------------------------------------
/tripod/tripod_meta.go:
--------------------------------------------------------------------------------
1 | package tripod
2 |
3 | import (
4 | "github.com/sirupsen/logrus"
5 | "path/filepath"
6 | "reflect"
7 | "runtime"
8 | "strings"
9 | )
10 |
11 | type TripodMeta struct {
12 | name string
13 | // Key: Execution Name
14 | execs map[string]ExecAndLei
15 | // Key: Query Name
16 | queries map[string]Query
17 | }
18 |
19 | func NewTripodMeta(name string) *TripodMeta {
20 | return &TripodMeta{
21 | name: name,
22 | execs: make(map[string]ExecAndLei),
23 | queries: make(map[string]Query),
24 | }
25 | }
26 |
27 | func (t *TripodMeta) Name() string {
28 | return t.name
29 | }
30 |
31 | func (t *TripodMeta) SetExec(fn Execution, lei uint64) *TripodMeta {
32 | name := getFuncName(fn)
33 | t.execs[name] = ExecAndLei{
34 | exec: fn,
35 | lei: lei,
36 | }
37 | logrus.Infof("register Execution(%s) into Tripod(%s) \n", name, t.name)
38 | return t
39 | }
40 |
41 | func (t *TripodMeta) SetQueries(queries ...Query) {
42 | for _, q := range queries {
43 | name := getFuncName(q)
44 | t.queries[name] = q
45 | logrus.Infof("register Query(%s) into Tripod(%s) \n", name, t.name)
46 | }
47 | }
48 |
49 | func getFuncName(i interface{}) string {
50 | ptr := reflect.ValueOf(i).Pointer()
51 | nameFull := runtime.FuncForPC(ptr).Name()
52 | nameEnd := filepath.Ext(nameFull)
53 | funcName := strings.TrimPrefix(nameEnd, ".")
54 | return strings.TrimSuffix(funcName, "-fm")
55 | }
56 |
57 | func (t *TripodMeta) ExistExec(execName string) bool {
58 | _, ok := t.execs[execName]
59 | return ok
60 | }
61 |
62 | func (t *TripodMeta) GetExec(name string) (Execution, uint64) {
63 | execEne, ok := t.execs[name]
64 | if ok {
65 | return execEne.exec, execEne.lei
66 | }
67 | return nil, 0
68 | }
69 |
70 | func (t *TripodMeta) GetQuery(name string) Query {
71 | return t.queries[name]
72 | }
73 |
74 | func (t *TripodMeta) AllQueryNames() []string {
75 | allNames := make([]string, 0)
76 | for name, _ := range t.queries {
77 | allNames = append(allNames, name)
78 | }
79 | return allNames
80 | }
81 |
82 | func (t *TripodMeta) AllExecNames() []string {
83 | allNames := make([]string, 0)
84 | for name, _ := range t.execs {
85 | allNames = append(allNames, name)
86 | }
87 | return allNames
88 | }
89 |
90 | type ExecAndLei struct {
91 | exec Execution
92 | lei uint64
93 | }
94 |
--------------------------------------------------------------------------------
/utils/compress/zip.go:
--------------------------------------------------------------------------------
1 | package compress
2 |
3 | import (
4 | "archive/zip"
5 | "github.com/pkg/errors"
6 | "io"
7 | "os"
8 | "path/filepath"
9 | "strings"
10 | )
11 |
12 | func ZipFiles(files []string, output string) error {
13 | zipFile, err := os.Create(output)
14 | if err != nil {
15 | return err
16 | }
17 | defer zipFile.Close()
18 | zipWriter := zip.NewWriter(zipFile)
19 | defer zipWriter.Close()
20 | for _, file := range files {
21 | err = addFileToZip(zipWriter, file)
22 | if err != nil {
23 | return err
24 | }
25 | }
26 | return nil
27 | }
28 |
29 | func UnzipFile(src, destDir string) ([]string, error) {
30 | fnames := make([]string, 0)
31 |
32 | r, err := zip.OpenReader(src)
33 | if err != nil {
34 | return nil, err
35 | }
36 | defer r.Close()
37 |
38 | for _, f := range r.File {
39 | fpath := filepath.Join(destDir, f.Name)
40 | // Check for ZipSlip.
41 | if !strings.HasPrefix(fpath, filepath.Clean(destDir)+string(os.PathSeparator)) {
42 | return nil, errors.Errorf("Illegal file path: %s", fpath)
43 | }
44 | fnames = append(fnames, fpath)
45 |
46 | if f.FileInfo().IsDir() {
47 | err := os.MkdirAll(fpath, os.ModePerm)
48 | if err != nil {
49 | return nil, err
50 | }
51 | continue
52 | }
53 |
54 | outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
55 | if err != nil {
56 | return nil, err
57 | }
58 |
59 | rc, err := f.Open()
60 | if err != nil {
61 | return nil, err
62 | }
63 |
64 | _, err = io.Copy(outFile, rc)
65 |
66 | outFile.Close()
67 | rc.Close()
68 | if err != nil {
69 | return nil, err
70 | }
71 | }
72 |
73 | return fnames, nil
74 | }
75 |
76 | func addFileToZip(w *zip.Writer, fname string) error {
77 | fileToZip, err := os.Open(fname)
78 | if err != nil {
79 | return err
80 | }
81 | defer fileToZip.Close()
82 |
83 | info, err := fileToZip.Stat()
84 | if err != nil {
85 | return err
86 | }
87 | header, err := zip.FileInfoHeader(info)
88 | if err != nil {
89 | return err
90 | }
91 |
92 | header.Name = fname
93 | header.Method = zip.Deflate
94 |
95 | writer, err := w.CreateHeader(header)
96 | if err != nil {
97 | return err
98 | }
99 | _, err = io.Copy(writer, fileToZip)
100 | return err
101 | }
102 |
--------------------------------------------------------------------------------
/keypair/ed25519.go:
--------------------------------------------------------------------------------
1 | package keypair
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/tendermint/tendermint/crypto/ed25519"
6 | )
7 |
8 | // ----- Public Key ------
9 |
10 | type EdPubkey struct {
11 | pubkey ed25519.PubKey
12 | }
13 |
14 | func EdPubKeyFromBytes(data []byte) *EdPubkey {
15 | return &EdPubkey{pubkey: data}
16 | }
17 |
18 | func (epb *EdPubkey) Address() Address {
19 | addressByt := epb.pubkey.Address().Bytes()
20 | return BytesToAddress(addressByt)
21 | }
22 |
23 | func (epb *EdPubkey) VerifySignature(msg, sig []byte) bool {
24 | return epb.pubkey.VerifySignature(msg, sig)
25 | }
26 |
27 | func (epb *EdPubkey) Type() string {
28 | return epb.pubkey.Type()
29 | }
30 |
31 | func (epb *EdPubkey) Equals(key Key) bool {
32 | edkey, ok := key.(*EdPubkey)
33 | if !ok {
34 | return false
35 | }
36 | return epb.pubkey.Equals(edkey.pubkey)
37 | }
38 |
39 | func (epb *EdPubkey) Bytes() []byte {
40 | return epb.pubkey.Bytes()
41 | }
42 |
43 | func (epb *EdPubkey) String() string {
44 | return ToHex(epb.Bytes())
45 | }
46 |
47 | func (epb *EdPubkey) BytesWithType() []byte {
48 | return append([]byte(Ed25519Idx), epb.pubkey.Bytes()...)
49 | }
50 |
51 | func (epb *EdPubkey) StringWithType() string {
52 | return ToHex(epb.BytesWithType())
53 | }
54 |
55 | // ------ Private Key -------
56 |
57 | type EdPrivkey struct {
58 | privkey ed25519.PrivKey
59 | }
60 |
61 | func (epr *EdPrivkey) SignData(data []byte) ([]byte, error) {
62 | return epr.privkey.Sign(data)
63 | }
64 |
65 | func (epr *EdPrivkey) Type() string {
66 | return epr.privkey.Type()
67 | }
68 |
69 | func (epr *EdPrivkey) Equals(key Key) bool {
70 | edKey, ok := key.(*EdPrivkey)
71 | if !ok {
72 | return false
73 | }
74 | return epr.privkey.Equals(edKey.privkey)
75 | }
76 |
77 | func (epr *EdPrivkey) Bytes() []byte {
78 | return epr.privkey.Bytes()
79 | }
80 |
81 | func (epr *EdPrivkey) String() string {
82 | return ToHex(epr.Bytes())
83 | }
84 |
85 | func (epr *EdPrivkey) BytesWithType() []byte {
86 | return append([]byte(Ed25519Idx), epr.privkey.Bytes()...)
87 | }
88 |
89 | func (epr *EdPrivkey) StringWithType() string {
90 | return ToHex(epr.BytesWithType())
91 | }
92 |
93 | func genEd25519() (*EdPubkey, *EdPrivkey) {
94 | edPrivKey := ed25519.GenPrivKey()
95 | privkey := &EdPrivkey{edPrivKey}
96 | pubkey := &EdPubkey{edPrivKey.PubKey().(ed25519.PubKey)}
97 | return pubkey, privkey
98 | }
99 |
--------------------------------------------------------------------------------
/keypair/sr25519.go:
--------------------------------------------------------------------------------
1 | package keypair
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/tendermint/tendermint/crypto/sr25519"
6 | )
7 |
8 | // ------ Public Key ------
9 |
10 | type SrPubkey struct {
11 | pubkey sr25519.PubKey
12 | }
13 |
14 | func SrPubKeyFromBytes(data []byte) *SrPubkey {
15 | return &SrPubkey{pubkey: data}
16 | }
17 |
18 | func (spb *SrPubkey) Address() Address {
19 | addressByt := spb.pubkey.Address().Bytes()
20 | return BytesToAddress(addressByt)
21 | }
22 |
23 | func (spb *SrPubkey) VerifySignature(msg, sig []byte) bool {
24 | return spb.pubkey.VerifySignature(msg, sig)
25 | }
26 |
27 | func (spb *SrPubkey) Type() string {
28 | return spb.pubkey.Type()
29 | }
30 |
31 | func (spb *SrPubkey) Equals(key Key) bool {
32 | srKey, ok := key.(*SrPubkey)
33 | if !ok {
34 | return false
35 | }
36 | return spb.pubkey.Equals(srKey.pubkey)
37 | }
38 |
39 | func (spb *SrPubkey) Bytes() []byte {
40 | return spb.pubkey.Bytes()
41 | }
42 |
43 | func (spb *SrPubkey) String() string {
44 | return ToHex(spb.Bytes())
45 | }
46 |
47 | func (spb *SrPubkey) BytesWithType() []byte {
48 | return append([]byte(Sr25519Idx), spb.pubkey.Bytes()...)
49 | }
50 |
51 | func (spb *SrPubkey) StringWithType() string {
52 | return ToHex(spb.BytesWithType())
53 | }
54 |
55 | // ----- Private Key ------
56 |
57 | type SrPrivkey struct {
58 | privkey sr25519.PrivKey
59 | }
60 |
61 | func (spr *SrPrivkey) SignData(data []byte) ([]byte, error) {
62 | return spr.privkey.Sign(data)
63 | }
64 |
65 | func (spr *SrPrivkey) Type() string {
66 | return spr.privkey.Type()
67 | }
68 |
69 | func (spr *SrPrivkey) Equals(key Key) bool {
70 | srKey, ok := key.(*SrPrivkey)
71 | if !ok {
72 | return false
73 | }
74 | return spr.privkey.Equals(srKey.privkey)
75 | }
76 |
77 | func (spr *SrPrivkey) Bytes() []byte {
78 | return spr.privkey.Bytes()
79 | }
80 |
81 | func (spr *SrPrivkey) String() string {
82 | return ToHex(spr.Bytes())
83 | }
84 |
85 | func (spr *SrPrivkey) BytesWithType() []byte {
86 | return append([]byte(Sr25519Idx), spr.privkey.Bytes()...)
87 | }
88 |
89 | func (spr *SrPrivkey) StringWithType() string {
90 | return ToHex(spr.BytesWithType())
91 | }
92 |
93 | func genSr25519() (*SrPubkey, *SrPrivkey) {
94 | srPrivkey := sr25519.GenPrivKey()
95 | privkey := &SrPrivkey{srPrivkey}
96 | pubkey := &SrPubkey{srPrivkey.PubKey().(sr25519.PubKey)}
97 | return pubkey, privkey
98 | }
99 |
--------------------------------------------------------------------------------
/node/master/p2p_pubsub.go:
--------------------------------------------------------------------------------
1 | package master
2 |
3 | import (
4 | "context"
5 | . "github.com/Lawliet-Chan/yu/blockchain"
6 | . "github.com/Lawliet-Chan/yu/txn"
7 | pubsub "github.com/libp2p/go-libp2p-pubsub"
8 | )
9 |
10 | const (
11 | BlockTopic = "block"
12 | UnpackedTxnsTopic = "unpacked-txns"
13 | )
14 |
15 | func (m *Master) initTopics() error {
16 | blockTopic, err := m.ps.Join(BlockTopic)
17 | if err != nil {
18 | return err
19 | }
20 | unpkgTxnsTopic, err := m.ps.Join(UnpackedTxnsTopic)
21 | if err != nil {
22 | return err
23 | }
24 | m.blockTopic = blockTopic
25 | m.unpackedTxnsTopic = unpkgTxnsTopic
26 | return nil
27 | }
28 |
29 | func (m *Master) pubBlock(block IBlock) error {
30 | byt, err := block.Encode()
31 | if err != nil {
32 | return err
33 | }
34 | return m.pubToP2P(m.blockTopic, byt)
35 | }
36 |
37 | func (m *Master) subBlock() (IBlock, error) {
38 | byt, err := m.subFromP2P(m.blockTopic)
39 | if err != nil {
40 | return nil, err
41 | }
42 | return m.chain.NewEmptyBlock().Decode(byt)
43 | }
44 |
45 | //func (m *Master) pubPackedTxns(blockHash Hash, txns SignedTxns) error {
46 | // pt, err := NewPackedTxns(blockHash, txns)
47 | // if err != nil {
48 | // return err
49 | // }
50 | // byt, err := json.Marshal(pt)
51 | // if err != nil {
52 | // return err
53 | // }
54 | // return m.pubToP2P(m.packedTxnsTopic, byt)
55 | //}
56 | //
57 | //func (m *Master) subPackedTxns() (Hash, SignedTxns, error) {
58 | // byt, err := m.subFromP2P(m.packedTxnsTopic)
59 | // if err != nil {
60 | // return NullHash, nil, err
61 | // }
62 | // var pt PackedTxns
63 | // err = json.Unmarshal(byt, &pt)
64 | // if err != nil {
65 | // return NullHash, nil, err
66 | // }
67 | //
68 | // return pt.Resolve()
69 | //}
70 |
71 | func (m *Master) pubUnpackedTxns(txns SignedTxns) error {
72 | byt, err := txns.Encode()
73 | if err != nil {
74 | return err
75 | }
76 | return m.pubToP2P(m.unpackedTxnsTopic, byt)
77 | }
78 |
79 | func (m *Master) subUnpackedTxns() (SignedTxns, error) {
80 | byt, err := m.subFromP2P(m.unpackedTxnsTopic)
81 | if err != nil {
82 | return nil, err
83 | }
84 | return DecodeSignedTxns(byt)
85 | }
86 |
87 | func (m *Master) pubToP2P(topic *pubsub.Topic, msg []byte) error {
88 | return topic.Publish(context.Background(), msg)
89 | }
90 |
91 | func (m *Master) subFromP2P(topic *pubsub.Topic) ([]byte, error) {
92 | sub, err := topic.Subscribe()
93 | if err != nil {
94 | return nil, err
95 | }
96 | msg, err := sub.Next(context.Background())
97 | if err != nil {
98 | return nil, err
99 | }
100 | return msg.Data, nil
101 | }
102 |
--------------------------------------------------------------------------------
/node/execute_txns.go:
--------------------------------------------------------------------------------
1 | package node
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/blockchain"
5 | "github.com/Lawliet-Chan/yu/chain_env"
6 | . "github.com/Lawliet-Chan/yu/common"
7 | "github.com/Lawliet-Chan/yu/context"
8 | . "github.com/Lawliet-Chan/yu/subscribe"
9 | . "github.com/Lawliet-Chan/yu/tripod"
10 | "github.com/Lawliet-Chan/yu/txn"
11 | "github.com/Lawliet-Chan/yu/yerror"
12 | "github.com/sirupsen/logrus"
13 | )
14 |
15 | func ExecuteTxns(block IBlock, env *chain_env.ChainEnv, land *Land) error {
16 | base := env.Base
17 | sub := env.Sub
18 |
19 | stxns, err := base.GetTxns(block.GetHash())
20 | if err != nil {
21 | return err
22 | }
23 | for _, stxn := range stxns {
24 | ecall := stxn.GetRaw().GetEcall()
25 | ctx, err := context.NewContext(stxn.GetPubkey().Address(), ecall.Params)
26 | if err != nil {
27 | return err
28 | }
29 |
30 | exec, lei, err := land.GetExecLei(ecall)
31 | if err != nil {
32 | handleError(err, ctx, block, stxn, sub)
33 | continue
34 | }
35 |
36 | if IfLeiOut(lei, block) {
37 | handleError(yerror.OutOfEnergy, ctx, block, stxn, sub)
38 | break
39 | }
40 |
41 | err = exec(ctx, block, env)
42 | if err != nil {
43 | env.Discard()
44 | handleError(err, ctx, block, stxn, sub)
45 | } else {
46 | env.NextTxn()
47 | }
48 |
49 | block.UseLei(lei)
50 |
51 | handleEvent(ctx, block, stxn, sub)
52 |
53 | err = base.SetEvents(ctx.Events)
54 | if err != nil {
55 | return err
56 | }
57 | err = base.SetError(ctx.Error)
58 | if err != nil {
59 | return err
60 | }
61 | }
62 |
63 | stateRoot, err := env.Commit()
64 | if err != nil {
65 | return err
66 | }
67 | block.SetStateRoot(stateRoot)
68 |
69 | return nil
70 | }
71 |
72 | func handleError(err error, ctx *context.Context, block IBlock, stxn *txn.SignedTxn, sub *Subscription) {
73 | ctx.EmitError(err)
74 | ecall := stxn.GetRaw().GetEcall()
75 |
76 | ctx.Error.Caller = stxn.GetRaw().GetCaller()
77 | ctx.Error.BlockStage = ExecuteTxnsStage
78 | ctx.Error.TripodName = ecall.TripodName
79 | ctx.Error.ExecName = ecall.ExecName
80 | ctx.Error.BlockHash = block.GetHash()
81 | ctx.Error.Height = block.GetHeight()
82 |
83 | logrus.Error("push error: ", ctx.Error.Error())
84 | if sub != nil {
85 | sub.Push(ctx.Error)
86 | }
87 |
88 | }
89 |
90 | func handleEvent(ctx *context.Context, block IBlock, stxn *txn.SignedTxn, sub *Subscription) {
91 | for _, event := range ctx.Events {
92 | ecall := stxn.GetRaw().GetEcall()
93 |
94 | event.Height = block.GetHeight()
95 | event.BlockHash = block.GetHash()
96 | event.ExecName = ecall.ExecName
97 | event.TripodName = ecall.TripodName
98 | event.BlockStage = ExecuteTxnsStage
99 | event.Caller = stxn.GetRaw().GetCaller()
100 |
101 | if sub != nil {
102 | sub.Push(event)
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/node/worker/http.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "github.com/gin-gonic/gin"
5 | "github.com/sirupsen/logrus"
6 | "net/http"
7 | . "yu/blockchain"
8 | . "yu/node"
9 | "yu/tripod"
10 | )
11 |
12 | func (w *Worker) HandleHttp() {
13 | r := gin.Default()
14 |
15 | r.GET(HeartbeatPath, func(c *gin.Context) {
16 | c.JSON(http.StatusOK, nil)
17 | logrus.Debugf("accept heartbeat from %s", c.ClientIP())
18 | })
19 |
20 | // ------------ Process Block ---------------
21 | r.POST(StartBlockPath, func(c *gin.Context) {
22 | block, err := DecodeBlockFromHttp(c.Request.Body, w.chain)
23 | if err != nil {
24 | c.AbortWithError(http.StatusBadRequest, err)
25 | return
26 | }
27 | var needBroadcast bool
28 | err = w.land.RangeList(func(tri tripod.Tripod) (err error) {
29 | needBroadcast, err = tri.StartBlock(w.chain, block, w.txPool)
30 | return
31 | })
32 | if err != nil {
33 | c.AbortWithError(http.StatusInternalServerError, err)
34 | return
35 | }
36 |
37 | encodeBlockBackHttp(c, block)
38 | })
39 |
40 | r.POST(EndBlockPath, func(c *gin.Context) {
41 | block, err := DecodeBlockFromHttp(c.Request.Body, w.chain)
42 | if err != nil {
43 | c.AbortWithError(http.StatusBadRequest, err)
44 | return
45 | }
46 | err = w.land.RangeList(func(tri tripod.Tripod) error {
47 | return tri.EndBlock(w.chain, block)
48 | })
49 | if err != nil {
50 | c.AbortWithError(http.StatusInternalServerError, err)
51 | return
52 | }
53 |
54 | encodeBlockBackHttp(c, block)
55 | })
56 |
57 | r.POST(ExecuteTxnsPath, func(c *gin.Context) {
58 | block, err := DecodeBlockFromHttp(c.Request.Body, w.chain)
59 | if err != nil {
60 | c.AbortWithError(http.StatusBadRequest, err)
61 | return
62 | }
63 | err = ExecuteTxns(block, w.base, w.land)
64 | if err != nil {
65 | c.AbortWithError(http.StatusInternalServerError, err)
66 | return
67 | }
68 |
69 | encodeBlockBackHttp(c, block)
70 | })
71 |
72 | r.POST(FinalizeBlockPath, func(c *gin.Context) {
73 | block, err := DecodeBlockFromHttp(c.Request.Body, w.chain)
74 | if err != nil {
75 | c.AbortWithError(http.StatusBadRequest, err)
76 | return
77 | }
78 | err = w.land.RangeList(func(tri tripod.Tripod) error {
79 | return tri.FinalizeBlock(w.chain, block)
80 | })
81 | if err != nil {
82 | c.AbortWithError(http.StatusInternalServerError, err)
83 | return
84 | }
85 |
86 | encodeBlockBackHttp(c, block)
87 | })
88 |
89 | r.Run(w.httpPort)
90 | }
91 |
92 | func encodeBlockBackHttp(c *gin.Context, b IBlock) {
93 | byt, err := b.Encode()
94 | if err != nil {
95 | c.AbortWithError(http.StatusInternalServerError, err)
96 | return
97 | }
98 | _, err = c.Writer.Write(byt)
99 | if err != nil {
100 | logrus.Errorf("write block(%s) back to http error: %s", b.Header().Hash().String(), err.Error())
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/blockchain/interfaces.go:
--------------------------------------------------------------------------------
1 | package blockchain
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | . "github.com/Lawliet-Chan/yu/result"
6 | . "github.com/Lawliet-Chan/yu/txn"
7 | "github.com/libp2p/go-libp2p-core/peer"
8 | )
9 |
10 | type IBlock interface {
11 | IHeader
12 | GetHeader() IHeader
13 |
14 | GetBlockId() BlockId
15 | GetTxnsHashes() []Hash
16 | SetTxnsHashes(hashes []Hash)
17 |
18 | SetHash(hash Hash)
19 | SetPreHash(hash Hash)
20 | SetTxnRoot(hash Hash)
21 | SetStateRoot(hash Hash)
22 | SetHeight(BlockNum)
23 | SetTimestamp(ts uint64)
24 | SetPeerID(peer.ID)
25 |
26 | SetLeiLimit(e uint64)
27 | UseLei(e uint64)
28 |
29 | Encode() ([]byte, error)
30 | Decode(data []byte) (IBlock, error)
31 |
32 | CopyFrom(other IBlock)
33 | }
34 |
35 | type IHeader interface {
36 | GetHeight() BlockNum
37 | GetHash() Hash
38 | GetPrevHash() Hash
39 | GetTxnRoot() Hash
40 | GetStateRoot() Hash
41 | GetTimestamp() uint64
42 | GetPeerID() peer.ID
43 | GetLeiLimit() uint64
44 | GetLeiUsed() uint64
45 | }
46 |
47 | // --------------- blockchain interface ----------------
48 |
49 | type ConvergeType int
50 |
51 | const (
52 | Longest ConvergeType = iota
53 | Heaviest
54 | Finalize
55 | )
56 |
57 | type IBlockChain interface {
58 | ConvergeType() ConvergeType
59 |
60 | NewEmptyBlock() IBlock
61 |
62 | EncodeBlocks(blocks []IBlock) ([]byte, error)
63 | DecodeBlocks(data []byte) ([]IBlock, error)
64 |
65 | // get genesis block
66 | GetGenesis() (IBlock, error)
67 | // set genesis block
68 | SetGenesis(b IBlock) error
69 | // pending a block from other blockchain-node for validating and operating
70 | InsertBlockFromP2P(ib IBlock) error
71 |
72 | TakeP2pBlocksBefore(height BlockNum) (map[BlockNum][]IBlock, error)
73 | TakeP2pBlocks(height BlockNum) ([]IBlock, error)
74 |
75 | AppendBlock(b IBlock) error
76 | GetBlock(blockHash Hash) (IBlock, error)
77 | ExistsBlock(blockHash Hash) bool
78 | UpdateBlock(b IBlock) error
79 |
80 | Children(prevBlockHash Hash) ([]IBlock, error)
81 | Finalize(blockHash Hash) error
82 | LastFinalized() (IBlock, error)
83 | GetEndBlock() (IBlock, error)
84 | GetAllBlocks() ([]IBlock, error)
85 |
86 | GetRangeBlocks(startHeight, endHeight BlockNum) ([]IBlock, error)
87 |
88 | Chain() (IChainStruct, error)
89 | }
90 |
91 | type IChainStruct interface {
92 | Append(block IBlock)
93 | InsertPrev(block IBlock)
94 | First() IBlock
95 | Range(fn func(block IBlock) error) error
96 | Last() IBlock
97 | }
98 |
99 | type IBlockBase interface {
100 | GetTxn(txnHash Hash) (*SignedTxn, error)
101 | SetTxn(stxn *SignedTxn) error
102 |
103 | GetTxns(blockHash Hash) ([]*SignedTxn, error)
104 | SetTxns(blockHash Hash, txns []*SignedTxn) error
105 |
106 | GetEvents(blockHash Hash) ([]*Event, error)
107 | SetEvents(events []*Event) error
108 |
109 | GetErrors(blockHash Hash) ([]*Error, error)
110 | SetError(err *Error) error
111 | }
112 |
--------------------------------------------------------------------------------
/apps/asset/transfer.go:
--------------------------------------------------------------------------------
1 | package asset
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/blockchain"
5 | . "github.com/Lawliet-Chan/yu/chain_env"
6 | . "github.com/Lawliet-Chan/yu/common"
7 | . "github.com/Lawliet-Chan/yu/context"
8 | . "github.com/Lawliet-Chan/yu/tripod"
9 | . "github.com/Lawliet-Chan/yu/yerror"
10 | "github.com/sirupsen/logrus"
11 | )
12 |
13 | type Asset struct {
14 | *DefaultTripod
15 | TokenName string
16 | }
17 |
18 | func NewAsset(tokenName string) *Asset {
19 | df := NewDefaultTripod("asset")
20 |
21 | a := &Asset{df, tokenName}
22 | a.SetExec(a.Transfer, 100).SetExec(a.CreateAccount, 10)
23 | a.SetQueries(a.QueryBalance)
24 |
25 | return a
26 | }
27 |
28 | func (a *Asset) QueryBalance(ctx *Context, env *ChainEnv, _ Hash) (interface{}, error) {
29 | account := ctx.GetAddress("account")
30 | amount := a.getBalance(env, account)
31 | return amount, nil
32 | }
33 |
34 | func (a *Asset) Transfer(ctx *Context, _ IBlock, env *ChainEnv) (err error) {
35 | from := ctx.Caller
36 | to := ctx.GetAddress("to")
37 | amount := Amount(ctx.GetUint64("amount"))
38 |
39 | if !a.exsitAccount(env, from) {
40 | return AccountNotFound(from)
41 | }
42 |
43 | fromBalance := a.getBalance(env, from)
44 | if fromBalance < amount {
45 | return InsufficientFunds
46 | }
47 |
48 | if !a.exsitAccount(env, to) {
49 | a.setBalance(env, to, amount)
50 | } else {
51 | toBalance := a.getBalance(env, to)
52 | toBalance, err = checkAdd(toBalance, amount)
53 | if err != nil {
54 | return
55 | }
56 | a.setBalance(env, to, toBalance)
57 | }
58 |
59 | fromBalance, err = checkSub(fromBalance, amount)
60 | if err != nil {
61 | return
62 | }
63 |
64 | a.setBalance(env, from, fromBalance)
65 |
66 | _ = ctx.EmitEvent("Transfer Completed!")
67 |
68 | return
69 | }
70 |
71 | func (a *Asset) CreateAccount(ctx *Context, _ IBlock, env *ChainEnv) error {
72 | addr := ctx.Caller
73 | amount := ctx.GetUint64("amount")
74 |
75 | if a.exsitAccount(env, addr) {
76 | _ = ctx.EmitEvent("Account Exists!")
77 | return nil
78 | }
79 |
80 | a.setBalance(env, addr, Amount(amount))
81 | _ = ctx.EmitEvent("Account Created Success!")
82 | return nil
83 | }
84 |
85 | func (a *Asset) exsitAccount(env *ChainEnv, addr Address) bool {
86 | return env.KVDB.Exist(a, addr.Bytes())
87 | }
88 |
89 | func (a *Asset) getBalance(env *ChainEnv, addr Address) Amount {
90 | balanceByt, err := env.KVDB.Get(a, addr.Bytes())
91 | if err != nil {
92 | logrus.Panic("get balance error")
93 | }
94 | return MustDecodeToAmount(balanceByt)
95 | }
96 |
97 | func (a *Asset) setBalance(env *ChainEnv, addr Address, amount Amount) {
98 | env.KVDB.Set(a, addr.Bytes(), amount.MustEncode())
99 | }
100 |
101 | func checkAdd(origin, add Amount) (Amount, error) {
102 | result := origin + add
103 | if result < origin && result < add {
104 | return 0, IntegerOverflow
105 | }
106 | return result, nil
107 | }
108 |
109 | func checkSub(origin, sub Amount) (Amount, error) {
110 | if origin < sub {
111 | return 0, IntegerOverflow
112 | }
113 | return origin - sub, nil
114 | }
115 |
--------------------------------------------------------------------------------
/storage/kv/bolt.go:
--------------------------------------------------------------------------------
1 | package kv
2 |
3 | import (
4 | "bytes"
5 | "github.com/Lawliet-Chan/yu/storage"
6 | "go.etcd.io/bbolt"
7 | )
8 |
9 | type boltKV struct {
10 | db *bbolt.DB
11 | }
12 |
13 | var bucket = []byte("github.com/Lawliet-Chan/yu")
14 |
15 | func NewBolt(fpath string) (*boltKV, error) {
16 | db, err := bbolt.Open(fpath, 0666, nil)
17 | tx, err := db.Begin(true)
18 | if err != nil {
19 | return nil, err
20 | }
21 | _, err = tx.CreateBucketIfNotExists(bucket)
22 | if err != nil {
23 | return nil, err
24 | }
25 | err = tx.Commit()
26 | if err != nil {
27 | return nil, err
28 | }
29 | return &boltKV{db: db}, nil
30 | }
31 |
32 | func (*boltKV) Type() storage.StoreType {
33 | return storage.Embedded
34 | }
35 |
36 | func (*boltKV) Kind() storage.StoreKind {
37 | return storage.KV
38 | }
39 |
40 | func (b *boltKV) Get(key []byte) ([]byte, error) {
41 | var value []byte
42 | err := b.db.View(func(tx *bbolt.Tx) error {
43 | bu := tx.Bucket(bucket)
44 | value = bu.Get(key)
45 | return nil
46 | })
47 | return value, err
48 | }
49 |
50 | func (b *boltKV) Set(key []byte, value []byte) error {
51 | return b.db.Update(func(tx *bbolt.Tx) error {
52 | return tx.Bucket(bucket).Put(key, value)
53 | })
54 | }
55 |
56 | func (b *boltKV) Delete(key []byte) error {
57 | return b.db.Update(func(tx *bbolt.Tx) error {
58 | return tx.Bucket(bucket).Delete(key)
59 | })
60 | }
61 |
62 | func (b *boltKV) Exist(key []byte) bool {
63 | value, _ := b.Get(key)
64 | return value != nil
65 | }
66 |
67 | func (b *boltKV) Iter(keyPrefix []byte) (Iterator, error) {
68 | var c *bbolt.Cursor
69 | err := b.db.View(func(tx *bbolt.Tx) error {
70 | c = tx.Bucket(bucket).Cursor()
71 | c.Seek(keyPrefix)
72 | return nil
73 | })
74 | return &boltIterator{
75 | c: c,
76 | keyPrefix: keyPrefix,
77 | }, err
78 | }
79 |
80 | func (b *boltKV) NewKvTxn() (KvTxn, error) {
81 | tx, err := b.db.Begin(true)
82 | if err != nil {
83 | return nil, err
84 | }
85 | return &boltTxn{
86 | tx: tx,
87 | }, nil
88 | }
89 |
90 | type boltIterator struct {
91 | keyPrefix []byte
92 | key []byte
93 | value []byte
94 | c *bbolt.Cursor
95 | }
96 |
97 | func (bi *boltIterator) Valid() bool {
98 | return bi.key != nil && bytes.HasPrefix(bi.key, bi.keyPrefix)
99 | }
100 |
101 | func (bi *boltIterator) Next() error {
102 | bi.key, bi.value = bi.c.Next()
103 | return nil
104 | }
105 |
106 | func (bi *boltIterator) Entry() ([]byte, []byte, error) {
107 | return bi.key, bi.value, nil
108 | }
109 |
110 | func (bi *boltIterator) Close() {
111 |
112 | }
113 |
114 | type boltTxn struct {
115 | tx *bbolt.Tx
116 | }
117 |
118 | func (bot *boltTxn) Get(key []byte) ([]byte, error) {
119 | return bot.tx.Bucket(bucket).Get(key), nil
120 | }
121 |
122 | func (bot *boltTxn) Set(key, value []byte) error {
123 | return bot.tx.Bucket(bucket).Put(key, value)
124 | }
125 |
126 | func (bot *boltTxn) Delete(key []byte) error {
127 | return bot.tx.Bucket(bucket).Delete(key)
128 | }
129 |
130 | func (bot *boltTxn) Commit() error {
131 | return bot.tx.Commit()
132 | }
133 |
134 | func (bot *boltTxn) Rollback() error {
135 | return bot.tx.Rollback()
136 | }
137 |
--------------------------------------------------------------------------------
/storage/kv/tikv.go:
--------------------------------------------------------------------------------
1 | package kv
2 |
3 | //
4 | //import (
5 | // "github.com/pingcap/tidb/kv"
6 | // "github.com/pingcap/tidb/store/tikv"
7 | // goctx "golang.org/x/net/context"
8 | // "yu/storage"
9 | //)
10 | //
11 | //type tiKV struct {
12 | // store kv.Storage
13 | //}
14 | //
15 | //func NewTiKV(path string) (*tiKV, error) {
16 | // driver := tikv.Driver{}
17 | // store, err := driver.Open(path)
18 | // if err != nil {
19 | // return nil, err
20 | // }
21 | // return &tiKV{
22 | // store: store,
23 | // }, nil
24 | //}
25 | //
26 | //func (*tiKV) Type() storage.StoreType {
27 | // return storage.Server
28 | //}
29 | //
30 | //func (*tiKV) Kind() storage.StoreKind {
31 | // return storage.KV
32 | //}
33 | //
34 | //func (t *tiKV) Get(key []byte) ([]byte, error) {
35 | // tx, err := t.store.Begin()
36 | // if err != nil {
37 | // return nil, err
38 | // }
39 | // return tx.Get(goctx.Background(), key)
40 | //}
41 | //
42 | //func (t *tiKV) Set(key, value []byte) error {
43 | // tx, err := t.store.Begin()
44 | // if err != nil {
45 | // return err
46 | // }
47 | // err = tx.Set(key, value)
48 | // if err != nil {
49 | // return err
50 | // }
51 | // return tx.Commit(goctx.Background())
52 | //}
53 | //
54 | //func (t *tiKV) Delete(key []byte) error {
55 | // tx, err := t.store.Begin()
56 | // if err != nil {
57 | // return err
58 | // }
59 | // err = tx.Delete(key)
60 | // if err != nil {
61 | // return err
62 | // }
63 | // return tx.Commit(goctx.Background())
64 | //}
65 | //
66 | //func (t *tiKV) Exist(key []byte) bool {
67 | // value, _ := t.Get(key)
68 | // return value != nil
69 | //}
70 | //
71 | //func (t *tiKV) Iter(key []byte) (Iterator, error) {
72 | // tx, err := t.store.Begin()
73 | // if err != nil {
74 | // return nil, err
75 | // }
76 | // iter, err := tx.Iter(key, nil)
77 | // if err != nil {
78 | // return nil, err
79 | // }
80 | // return &tikvIterator{
81 | // iter: iter,
82 | // }, nil
83 | //}
84 | //
85 | //func (t *tiKV) NewKvTxn() (KvTxn, error) {
86 | // tx, err := t.store.Begin()
87 | // if err != nil {
88 | // return nil, err
89 | // }
90 | // return &tikvTxn{tx: tx}, nil
91 | //}
92 | //
93 | //type tikvIterator struct {
94 | // iter kv.Iterator
95 | //}
96 | //
97 | //func (ti *tikvIterator) Valid() bool {
98 | // return ti.iter.Valid()
99 | //}
100 | //
101 | //func (ti *tikvIterator) Next() error {
102 | // return ti.iter.Next()
103 | //}
104 | //
105 | //func (ti *tikvIterator) Entry() ([]byte, []byte, error) {
106 | // return ti.iter.Key()[:], ti.iter.Value(), nil
107 | //}
108 | //
109 | //func (ti *tikvIterator) Close() {
110 | // ti.iter.Close()
111 | //}
112 | //
113 | //type tikvTxn struct {
114 | // tx kv.Transaction
115 | //}
116 | //
117 | //func (tt *tikvTxn) Get(key []byte) ([]byte, error) {
118 | // return tt.tx.Get(goctx.Background(), key)
119 | //}
120 | //
121 | //func (tt *tikvTxn) Set(key, value []byte) error {
122 | // return tt.tx.Set(key, value)
123 | //}
124 | //
125 | //func (tt *tikvTxn) Delete(key []byte) error {
126 | // return tt.tx.Delete(key)
127 | //}
128 | //
129 | //func (tt *tikvTxn) Commit() error {
130 | // return tt.tx.Commit(goctx.Background())
131 | //}
132 | //
133 | //func (tt *tikvTxn) Rollback() error {
134 | // return tt.tx.Rollback()
135 | //}
136 |
--------------------------------------------------------------------------------
/startup/startup.go:
--------------------------------------------------------------------------------
1 | package startup
2 |
3 | import (
4 | "flag"
5 | "github.com/Lawliet-Chan/yu/blockchain"
6 | "github.com/Lawliet-Chan/yu/config"
7 | "github.com/Lawliet-Chan/yu/node/master"
8 | "github.com/Lawliet-Chan/yu/tripod"
9 | "github.com/Lawliet-Chan/yu/txpool"
10 | "github.com/Lawliet-Chan/yu/utils/codec"
11 | "github.com/gin-gonic/gin"
12 | "github.com/sirupsen/logrus"
13 | )
14 |
15 | var (
16 | masterCfgPath string
17 | masterCfg config.MasterConf
18 | )
19 |
20 | var (
21 | Chain blockchain.IBlockChain
22 | Base blockchain.IBlockBase
23 | TxPool txpool.ItxPool
24 | )
25 |
26 | func StartUp(tripods ...tripod.Tripod) {
27 | initCfgFromFlags()
28 | initLog(masterCfg.LogLevel)
29 |
30 | codec.GlobalCodec = &codec.RlpCodec{}
31 | gin.SetMode(gin.ReleaseMode)
32 |
33 | land := tripod.NewLand()
34 | land.SetTripods(tripods...)
35 |
36 | m, err := master.NewMaster(&masterCfg, Chain, Base, TxPool, land)
37 | if err != nil {
38 | logrus.Panicf("load master error: %s", err.Error())
39 | }
40 |
41 | m.Startup()
42 | }
43 |
44 | func initCfgFromFlags() {
45 | useDefaultCfg := flag.Bool("dc", false, "default config files")
46 |
47 | flag.StringVar(&masterCfgPath, "m", "yu_conf/master.toml", "Master config file path")
48 |
49 | flag.Parse()
50 | if *useDefaultCfg {
51 | initDefaultCfg()
52 | return
53 | }
54 |
55 | config.LoadConf(masterCfgPath, &masterCfg)
56 | }
57 |
58 | func initLog(level string) {
59 | formatter := &logrus.TextFormatter{
60 | FullTimestamp: true,
61 | TimestampFormat: "2006-01-02 15:04:05",
62 | }
63 | logrus.SetFormatter(formatter)
64 | lvl, err := logrus.ParseLevel(level)
65 | if err != nil {
66 | panic("parse log level error: " + err.Error())
67 | }
68 |
69 | logrus.SetLevel(lvl)
70 | }
71 |
72 | func initDefaultCfg() {
73 | masterCfg = config.MasterConf{
74 | RunMode: 0,
75 | HttpPort: "7999",
76 | WsPort: "8999",
77 | LogLevel: "info",
78 | LeiLimit: 50000,
79 | NkDB: config.KVconf{
80 | KvType: "bolt",
81 | Path: "./nk_db.db",
82 | Hosts: nil,
83 | },
84 | Timeout: 60,
85 | P2pListenAddrs: []string{"/ip4/127.0.0.1/tcp/8887"},
86 | Bootnodes: nil,
87 | ProtocolID: "yu",
88 | NodeKeyType: 1,
89 | NodeKeyRandSeed: 1,
90 | NodeKey: "",
91 | NodeKeyBits: 0,
92 | NodeKeyFile: "",
93 | }
94 | masterCfg.BlockChain = config.BlockchainConf{
95 | ChainDB: config.SqlDbConf{
96 | SqlDbType: "sqlite",
97 | Dsn: "chain.db",
98 | },
99 | BlocksFromP2pDB: config.SqlDbConf{
100 | SqlDbType: "sqlite",
101 | Dsn: "blocks_from_p2p.db",
102 | },
103 | }
104 | masterCfg.BlockBase = config.BlockBaseConf{
105 | BaseDB: config.SqlDbConf{
106 | SqlDbType: "sqlite",
107 | Dsn: "blockbase.db",
108 | }}
109 | masterCfg.Txpool = config.TxpoolConf{
110 | PoolSize: 2048,
111 | TxnMaxSize: 1024000,
112 | Timeout: 10,
113 | WorkerIP: "",
114 | }
115 | masterCfg.State = config.StateConf{KV: config.StateKvConf{
116 | IndexDB: config.KVconf{
117 | KvType: "bolt",
118 | Path: "./state_index.db",
119 | Hosts: nil,
120 | },
121 | NodeBase: config.KVconf{
122 | KvType: "bolt",
123 | Path: "./state_base.db",
124 | Hosts: nil,
125 | },
126 | }}
127 | }
128 |
--------------------------------------------------------------------------------
/blockchain/chain_struct.go:
--------------------------------------------------------------------------------
1 | package blockchain
2 |
3 | // todo: consider use array instead of linklist
4 | type ChainStruct struct {
5 | //root *ChainNode
6 | chainArr []IBlock
7 | }
8 |
9 | func NewEmptyChain(block IBlock) *ChainStruct {
10 | return &ChainStruct{
11 | chainArr: []IBlock{block},
12 | }
13 | }
14 |
15 | func MakeFinalizedChain(blocks []IBlock) IChainStruct {
16 | chain := NewEmptyChain(blocks[0])
17 | for i := 1; i < len(blocks); i++ {
18 | chain.Append(blocks[i])
19 | }
20 | return chain
21 | }
22 |
23 | //func MakeLongestChain(blocks []IBlock) []IChainStruct {
24 | // longestChains := make([]IChainStruct, 0)
25 | // allChains := make([][]IBlock, 0)
26 | // for _, block := range blocks {
27 | // h := int(block.GetHeight())
28 | //
29 | // }
30 | //allBlocks := make(map[Hash]IBlock)
31 | //
32 | //highestBlocks := make([]IBlock, 0)
33 | //
34 | //var longestHeight BlockNum = 0
35 | //for _, block := range blocks {
36 | // bh := block.GetHeight()
37 | // if bh > longestHeight {
38 | // longestHeight = bh
39 | // highestBlocks = nil
40 | // }
41 | //
42 | // if bh == longestHeight {
43 | // highestBlocks = append(highestBlocks, block)
44 | // }
45 | //
46 | // allBlocks[block.GetHash()] = block
47 | //}
48 | //
49 | //for _, hblock := range highestBlocks {
50 | // chain := NewEmptyChain(hblock)
51 | // // FIXME: genesis block cannot be returned if its prevHash is Null
52 | // for chain.root.Current.GetPrevHash() != NullHash {
53 | // block, ok := allBlocks[chain.root.Current.GetPrevHash()]
54 | // if ok {
55 | // chain.InsertPrev(block)
56 | // }
57 | // }
58 | //
59 | // longestChains = append(longestChains, chain)
60 | //
61 | //}
62 |
63 | // logrus.Warn("end RANGE highest blocks------------")
64 | //
65 | // return longestChains
66 | //}
67 |
68 | // // deprecated
69 | func MakeHeaviestChain(blocks []IBlock) []IChainStruct {
70 | return nil
71 | }
72 |
73 | func (c *ChainStruct) Append(block IBlock) {
74 | //cursor := c.root
75 | //for cursor.Next != nil {
76 | // cursor = cursor.Next
77 | //}
78 | //cursor.Next = &ChainNode{
79 | // Prev: cursor,
80 | // Current: block,
81 | // Next: nil,
82 | //}
83 | c.chainArr = append(c.chainArr, block)
84 | }
85 |
86 | func (c *ChainStruct) InsertPrev(block IBlock) {
87 | //c.root.Prev = &ChainNode{
88 | // Prev: nil,
89 | // Current: block,
90 | // Next: c.root,
91 | //}
92 | //c.root = c.root.Prev
93 | c.chainArr = append([]IBlock{block}, c.chainArr...)
94 | }
95 |
96 | func (c *ChainStruct) First() IBlock {
97 | return c.chainArr[0]
98 | }
99 |
100 | func (c *ChainStruct) Range(fn func(block IBlock) error) error {
101 | //for cursor := c.root; cursor.Next != nil; cursor = cursor.Next {
102 | // err := fn(cursor.Current)
103 | // if err != nil {
104 | // return err
105 | // }
106 | //}
107 | for _, b := range c.chainArr {
108 | err := fn(b)
109 | if err != nil {
110 | return err
111 | }
112 | }
113 | return nil
114 | }
115 |
116 | func (c *ChainStruct) Last() IBlock {
117 | //cursor := c.root
118 | //for cursor.Next != nil {
119 | // cursor = cursor.Next
120 | //}
121 | //return cursor.Current
122 | return c.chainArr[len(c.chainArr)-1]
123 | }
124 |
125 | //type ChainNode struct {
126 | // Prev *ChainNode
127 | // Current IBlock
128 | // Next *ChainNode
129 | //}
130 |
--------------------------------------------------------------------------------
/network/protocol.go:
--------------------------------------------------------------------------------
1 | package network
2 |
3 | import (
4 | "encoding/json"
5 | . "github.com/Lawliet-Chan/yu/blockchain"
6 | . "github.com/Lawliet-Chan/yu/common"
7 | . "github.com/Lawliet-Chan/yu/txn"
8 | "github.com/Lawliet-Chan/yu/yerror"
9 | )
10 |
11 | type HandShakeRequest struct {
12 | FetchRange *BlocksRange
13 | Info *HandShakeInfo
14 | }
15 |
16 | func NewHsReq(chain IBlockChain, fetchRange *BlocksRange) (*HandShakeRequest, error) {
17 | info, err := NewHsInfo(chain)
18 | if err != nil {
19 | return nil, err
20 | }
21 | return &HandShakeRequest{
22 | FetchRange: fetchRange,
23 | Info: info,
24 | }, nil
25 | }
26 |
27 | func (hs *HandShakeRequest) Encode() ([]byte, error) {
28 | return json.Marshal(hs)
29 | }
30 |
31 | func DecodeHsRequest(data []byte) (*HandShakeRequest, error) {
32 | var hs HandShakeRequest
33 | err := json.Unmarshal(data, &hs)
34 | if err != nil {
35 | return nil, err
36 | }
37 | return &hs, nil
38 | }
39 |
40 | type HandShakeInfo struct {
41 | GenesisBlockHash Hash
42 |
43 | // when chain is finlaized chain, end block is the finalized block
44 | EndHeight BlockNum
45 | EndBlockHash Hash
46 | }
47 |
48 | func NewHsInfo(chain IBlockChain) (*HandShakeInfo, error) {
49 | gBlock, err := chain.GetGenesis()
50 | if err != nil {
51 | return nil, err
52 | }
53 |
54 | eBlock, err := chain.GetEndBlock()
55 | if err != nil {
56 | return nil, err
57 | }
58 |
59 | return &HandShakeInfo{
60 | GenesisBlockHash: gBlock.GetHash(),
61 | EndHeight: eBlock.GetHeight(),
62 | EndBlockHash: eBlock.GetHash(),
63 | }, nil
64 | }
65 |
66 | // return a BlocksRange if other node's height is lower
67 | func (hs *HandShakeInfo) Compare(other *HandShakeInfo) (*BlocksRange, error) {
68 | if hs.GenesisBlockHash != other.GenesisBlockHash {
69 | return nil, yerror.GenesisBlockIllegal
70 | }
71 |
72 | if hs.EndHeight > other.EndHeight {
73 | return &BlocksRange{
74 | StartHeight: other.EndHeight + 1,
75 | EndHeight: hs.EndHeight,
76 | }, nil
77 | }
78 |
79 | return nil, nil
80 | }
81 |
82 | type HandShakeResp struct {
83 | // missing blocks range
84 | MissingRange *BlocksRange
85 | // compressed blocks bytes
86 | BlocksByt []byte
87 | // key: block-hash,
88 | // value: compressed txns bytes
89 | TxnsByt map[Hash][]byte
90 | Err error
91 | }
92 |
93 | func (hs *HandShakeResp) Encode() ([]byte, error) {
94 | return json.Marshal(hs)
95 | }
96 |
97 | func DecodeHsResp(data []byte) (*HandShakeResp, error) {
98 | var hs HandShakeResp
99 | err := json.Unmarshal(data, &hs)
100 | return &hs, err
101 | }
102 |
103 | type BlocksRange struct {
104 | StartHeight BlockNum
105 | EndHeight BlockNum
106 | }
107 |
108 | type PackedTxns struct {
109 | BlockHash string
110 | TxnsBytes []byte
111 | }
112 |
113 | func NewPackedTxns(blockHash Hash, txns SignedTxns) (*PackedTxns, error) {
114 | byt, err := txns.Encode()
115 | if err != nil {
116 | return nil, err
117 | }
118 | return &PackedTxns{
119 | BlockHash: blockHash.String(),
120 | TxnsBytes: byt,
121 | }, nil
122 | }
123 |
124 | func (pt *PackedTxns) Resolve() (Hash, SignedTxns, error) {
125 | stxns, err := DecodeSignedTxns(pt.TxnsBytes)
126 | if err != nil {
127 | return NullHash, nil, err
128 | }
129 | return HexToHash(pt.BlockHash), stxns, nil
130 | }
131 |
--------------------------------------------------------------------------------
/storage/kv/badger.go:
--------------------------------------------------------------------------------
1 | package kv
2 |
3 | import (
4 | "github.com/Lawliet-Chan/yu/storage"
5 | "github.com/dgraph-io/badger"
6 | )
7 |
8 | type badgerKV struct {
9 | db *badger.DB
10 | }
11 |
12 | func NewBadger(path string) (*badgerKV, error) {
13 | db, err := badger.Open(badger.DefaultOptions(path))
14 | if err != nil {
15 | return nil, err
16 | }
17 | return &badgerKV{
18 | db: db,
19 | }, nil
20 | }
21 |
22 | func (*badgerKV) Type() storage.StoreType {
23 | return storage.Embedded
24 | }
25 |
26 | func (*badgerKV) Kind() storage.StoreKind {
27 | return storage.KV
28 | }
29 |
30 | func (bg *badgerKV) Get(key []byte) ([]byte, error) {
31 | var valCopy []byte
32 | err := bg.db.View(func(txn *badger.Txn) error {
33 | item, err := txn.Get(key)
34 | if err != nil {
35 | return err
36 | }
37 | err = item.Value(func(val []byte) error {
38 | valCopy = append(valCopy, val...)
39 | return nil
40 | })
41 | return nil
42 | })
43 | return valCopy, err
44 | }
45 |
46 | func (bg *badgerKV) Set(key, value []byte) error {
47 | return bg.db.Update(func(txn *badger.Txn) error {
48 | return txn.Set(key, value)
49 | })
50 | }
51 |
52 | func (bg *badgerKV) Delete(key []byte) error {
53 | return bg.db.Update(func(txn *badger.Txn) error {
54 | return txn.Delete(key)
55 | })
56 | }
57 |
58 | func (bg *badgerKV) Exist(key []byte) bool {
59 | value, _ := bg.Get(key)
60 | return value != nil
61 | }
62 |
63 | func (bg *badgerKV) Iter(key []byte) (Iterator, error) {
64 | var iter *badger.Iterator
65 | err := bg.db.View(func(txn *badger.Txn) error {
66 | iter = txn.NewIterator(badger.DefaultIteratorOptions)
67 | iter.Seek(key)
68 | return nil
69 | })
70 | return &badgerIterator{
71 | key: key,
72 | iter: iter,
73 | }, err
74 | }
75 |
76 | func (bg *badgerKV) NewKvTxn() (KvTxn, error) {
77 | tx := bg.db.NewTransaction(true)
78 | return &badgerTxn{
79 | tx: tx,
80 | }, nil
81 | }
82 |
83 | type badgerIterator struct {
84 | key []byte
85 | iter *badger.Iterator
86 | }
87 |
88 | func (bgi *badgerIterator) Valid() bool {
89 | return bgi.iter.ValidForPrefix(bgi.key)
90 | }
91 |
92 | func (bgi *badgerIterator) Next() error {
93 | bgi.iter.Next()
94 | return nil
95 | }
96 |
97 | func (bgi *badgerIterator) Entry() ([]byte, []byte, error) {
98 | var value []byte
99 | item := bgi.iter.Item()
100 | key := item.Key()
101 | err := item.Value(func(val []byte) error {
102 | value = append(value, val...)
103 | return nil
104 | })
105 | return key, value, err
106 | }
107 |
108 | func (bgi *badgerIterator) Close() {
109 | bgi.iter.Close()
110 | }
111 |
112 | type badgerTxn struct {
113 | tx *badger.Txn
114 | }
115 |
116 | func (bt *badgerTxn) Get(key []byte) ([]byte, error) {
117 | item, err := bt.tx.Get(key)
118 | if err != nil {
119 | return nil, err
120 | }
121 | var value []byte
122 | err = item.Value(func(val []byte) error {
123 | value = append(value, val...)
124 | return nil
125 | })
126 | return value, err
127 | }
128 |
129 | func (bt *badgerTxn) Set(key, value []byte) error {
130 | return bt.tx.Set(key, value)
131 | }
132 |
133 | func (bt *badgerTxn) Delete(key []byte) error {
134 | return bt.tx.Delete(key)
135 | }
136 |
137 | func (bt *badgerTxn) Commit() error {
138 | return bt.tx.Commit()
139 | }
140 |
141 | func (bt *badgerTxn) Rollback() error {
142 | bt.tx.Discard()
143 | return nil
144 | }
145 |
--------------------------------------------------------------------------------
/blockchain/blockbase.go:
--------------------------------------------------------------------------------
1 | package blockchain
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/Lawliet-Chan/yu/config"
6 | . "github.com/Lawliet-Chan/yu/result"
7 | ysql "github.com/Lawliet-Chan/yu/storage/sql"
8 | . "github.com/Lawliet-Chan/yu/txn"
9 | )
10 |
11 | type BlockBase struct {
12 | db ysql.SqlDB
13 | }
14 |
15 | func NewBlockBase(cfg *config.BlockBaseConf) (*BlockBase, error) {
16 | db, err := ysql.NewSqlDB(&cfg.BaseDB)
17 | if err != nil {
18 | return nil, err
19 | }
20 |
21 | err = db.CreateIfNotExist(&TxnScheme{})
22 | if err != nil {
23 | return nil, err
24 | }
25 |
26 | err = db.CreateIfNotExist(&EventScheme{})
27 | if err != nil {
28 | return nil, err
29 | }
30 |
31 | err = db.CreateIfNotExist(&ErrorScheme{})
32 | if err != nil {
33 | return nil, err
34 | }
35 |
36 | return &BlockBase{
37 | db: db,
38 | }, nil
39 | }
40 |
41 | func (bb *BlockBase) GetTxn(txnHash Hash) (*SignedTxn, error) {
42 | var ts TxnScheme
43 | bb.db.Db().Where(&TxnScheme{TxnHash: txnHash.String()}).First(&ts)
44 | return ts.toTxn()
45 | }
46 |
47 | func (bb *BlockBase) SetTxn(stxn *SignedTxn) error {
48 | txnSm, err := toTxnScheme(stxn)
49 | if err != nil {
50 | return err
51 | }
52 | bb.db.Db().Create(&txnSm)
53 | return nil
54 | }
55 |
56 | func (bb *BlockBase) GetTxns(blockHash Hash) ([]*SignedTxn, error) {
57 | var tss []TxnScheme
58 | bb.db.Db().Where(&TxnScheme{BlockHash: blockHash.String()}).Find(&tss)
59 | itxns := make([]*SignedTxn, 0)
60 | for _, ts := range tss {
61 | stxn, err := ts.toTxn()
62 | if err != nil {
63 | return nil, err
64 | }
65 | itxns = append(itxns, stxn)
66 | }
67 | return itxns, nil
68 | }
69 |
70 | func (bb *BlockBase) SetTxns(blockHash Hash, txns []*SignedTxn) error {
71 | txnSms := make([]TxnScheme, 0)
72 | for _, stxn := range txns {
73 | txnSm, err := newTxnScheme(blockHash, stxn)
74 | if err != nil {
75 | return err
76 | }
77 | txnSms = append(txnSms, txnSm)
78 | }
79 | if len(txnSms) > 0 {
80 | bb.db.Db().Create(&txnSms)
81 | }
82 | return nil
83 | }
84 |
85 | func (bb *BlockBase) GetEvents(blockHash Hash) ([]*Event, error) {
86 | var ess []EventScheme
87 | bb.db.Db().Where(&EventScheme{BlockHash: blockHash.String()}).Find(&ess)
88 | events := make([]*Event, 0)
89 | for _, es := range ess {
90 | e, err := es.toEvent()
91 | if err != nil {
92 | return nil, err
93 | }
94 | events = append(events, e)
95 | }
96 | return events, nil
97 | }
98 |
99 | func (bb *BlockBase) SetEvents(events []*Event) error {
100 | eventSms := make([]EventScheme, 0)
101 | for _, event := range events {
102 | eventSm, err := toEventScheme(event)
103 | if err != nil {
104 | return err
105 | }
106 | eventSms = append(eventSms, eventSm)
107 | }
108 | if len(eventSms) > 0 {
109 | bb.db.Db().Create(&eventSms)
110 | }
111 | return nil
112 | }
113 |
114 | func (bb *BlockBase) GetErrors(blockHash Hash) ([]*Error, error) {
115 | var ess []ErrorScheme
116 | bb.db.Db().Where(&ErrorScheme{BlockHash: blockHash.String()}).Find(&ess)
117 | errs := make([]*Error, 0)
118 | for _, es := range ess {
119 | errs = append(errs, es.toError())
120 | }
121 | return errs, nil
122 | }
123 |
124 | func (bb *BlockBase) SetError(err *Error) error {
125 | if err == nil {
126 | return nil
127 | }
128 | errscm := toErrorScheme(err)
129 | bb.db.Db().Create(&errscm)
130 | return nil
131 | }
132 |
--------------------------------------------------------------------------------
/blockchain/blockchain_scheme.go:
--------------------------------------------------------------------------------
1 | package blockchain
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/libp2p/go-libp2p-core/peer"
6 | )
7 |
8 | type BlocksScheme struct {
9 | Hash string `gorm:"primaryKey"`
10 | PrevHash string
11 | Height BlockNum
12 | TxnRoot string
13 | StateRoot string
14 | Nonce uint64
15 | Timestamp uint64
16 | TxnsHashes string
17 | PeerID string
18 |
19 | LeiLimit uint64
20 | LeiUsed uint64
21 |
22 | Length uint64
23 | Finalize bool
24 | }
25 |
26 | func (BlocksScheme) TableName() string {
27 | return "blockchain"
28 | }
29 |
30 | func toBlocksScheme(b IBlock) (BlocksScheme, error) {
31 | bs := BlocksScheme{
32 | Hash: b.GetHash().String(),
33 | PrevHash: b.GetPrevHash().String(),
34 | Height: b.GetHeight(),
35 | TxnRoot: b.GetTxnRoot().String(),
36 | StateRoot: b.GetStateRoot().String(),
37 | Nonce: b.GetHeader().(*Header).Nonce,
38 | Timestamp: b.GetTimestamp(),
39 | TxnsHashes: HashesToHex(b.GetTxnsHashes()),
40 | PeerID: b.GetPeerID().String(),
41 |
42 | LeiLimit: b.GetLeiLimit(),
43 | LeiUsed: b.GetLeiUsed(),
44 |
45 | Length: b.(*Block).ChainLength,
46 | Finalize: false,
47 | }
48 | return bs, nil
49 | }
50 |
51 | func (b *BlocksScheme) toBlock() (IBlock, error) {
52 | var (
53 | PeerID peer.ID
54 | err error
55 | )
56 | if b.PeerID == "" {
57 | PeerID = peer.ID("")
58 | } else {
59 | PeerID, err = peer.Decode(b.PeerID)
60 | if err != nil {
61 | return nil, err
62 | }
63 | }
64 |
65 | header := &Header{
66 | PrevHash: HexToHash(b.PrevHash),
67 | Hash: HexToHash(b.Hash),
68 | Height: b.Height,
69 | TxnRoot: HexToHash(b.TxnRoot),
70 | StateRoot: HexToHash(b.StateRoot),
71 | Nonce: b.Nonce,
72 | Timestamp: b.Timestamp,
73 | PeerID: PeerID,
74 | LeiLimit: b.LeiLimit,
75 | LeiUsed: b.LeiUsed,
76 | }
77 | block := &Block{
78 | Header: header,
79 | TxnsHashes: HexToHashes(b.TxnsHashes),
80 | ChainLength: b.Length,
81 | }
82 |
83 | return block, nil
84 | }
85 |
86 | type BlocksFromP2pScheme struct {
87 | BlockHash string `gorm:"primaryKey"`
88 | Height BlockNum
89 | BlockContent string
90 | BlockProducer string
91 | }
92 |
93 | func (BlocksFromP2pScheme) TableName() string {
94 | return "blocks_from_p2p"
95 | }
96 |
97 | func toBlocksFromP2pScheme(b IBlock) (BlocksFromP2pScheme, error) {
98 | byt, err := b.Encode()
99 | if err != nil {
100 | return BlocksFromP2pScheme{}, err
101 | }
102 | return BlocksFromP2pScheme{
103 | BlockHash: b.GetHash().String(),
104 | Height: b.GetHeight(),
105 | BlockContent: ToHex(byt),
106 | }, nil
107 | }
108 |
109 | func (bs BlocksFromP2pScheme) toBlock() (IBlock, error) {
110 | byt := FromHex(bs.BlockContent)
111 | b := &Block{}
112 | return b.Decode(byt)
113 | }
114 |
115 | func bssToBlocks(bss []BlocksScheme) []IBlock {
116 | blocks := make([]IBlock, 0)
117 | for _, bs := range bss {
118 | b, err := bs.toBlock()
119 | if err != nil {
120 | return nil
121 | }
122 | blocks = append(blocks, b)
123 | }
124 | return blocks
125 | }
126 |
127 | func bspToBlocks(bsp []BlocksFromP2pScheme) []IBlock {
128 | blocks := make([]IBlock, 0)
129 | for _, bs := range bsp {
130 | b, err := bs.toBlock()
131 | if err != nil {
132 | return nil
133 | }
134 | blocks = append(blocks, b)
135 | }
136 | return blocks
137 | }
138 |
--------------------------------------------------------------------------------
/txn/signed.go:
--------------------------------------------------------------------------------
1 | package txn
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | . "github.com/Lawliet-Chan/yu/keypair"
6 | . "github.com/Lawliet-Chan/yu/utils/codec"
7 | "unsafe"
8 | )
9 |
10 | type SignedTxn struct {
11 | Raw *UnsignedTxn
12 | TxnHash Hash
13 | Pubkey PubKey
14 | Signature []byte
15 | }
16 |
17 | func NewSignedTxn(caller Address, ecall *Ecall, pubkey PubKey, sig []byte) (*SignedTxn, error) {
18 | raw, err := NewUnsignedTxn(caller, ecall)
19 | if err != nil {
20 | return nil, err
21 | }
22 | hash, err := raw.Hash()
23 | if err != nil {
24 | return nil, err
25 | }
26 | return &SignedTxn{
27 | Raw: raw,
28 | TxnHash: hash,
29 | Pubkey: pubkey,
30 | Signature: sig,
31 | }, nil
32 | }
33 |
34 | func (st *SignedTxn) GetRaw() *UnsignedTxn {
35 | return st.Raw
36 | }
37 |
38 | func (st *SignedTxn) GetTxnHash() Hash {
39 | return st.TxnHash
40 | }
41 |
42 | func (st *SignedTxn) GetPubkey() PubKey {
43 | return st.Pubkey
44 | }
45 |
46 | func (st *SignedTxn) GetSignature() []byte {
47 | return st.Signature
48 | }
49 |
50 | func (st *SignedTxn) Encode() ([]byte, error) {
51 | return GlobalCodec.EncodeToBytes(st)
52 | }
53 |
54 | func (st *SignedTxn) Size() int {
55 | return int(unsafe.Sizeof(st))
56 | }
57 |
58 | //func DecodeSignedTxn(data []byte) (st *SignedTxn, err error) {
59 | // decoder := gob.NewDecoder(bytes.NewReader(data))
60 | // err = decoder.Decode(st)
61 | // return
62 | //}
63 |
64 | type SignedTxns []*SignedTxn
65 |
66 | func FromArray(txns ...*SignedTxn) SignedTxns {
67 | var stxns SignedTxns
68 | stxns = append(stxns, txns...)
69 | return stxns
70 | }
71 |
72 | func (sts SignedTxns) ToArray() []*SignedTxn {
73 | return sts[:]
74 | }
75 |
76 | func (sts SignedTxns) Hashes() (hashes []Hash) {
77 | for _, st := range sts {
78 | hashes = append(hashes, st.TxnHash)
79 | }
80 | return
81 | }
82 |
83 | func (sts SignedTxns) Remove(hash Hash) (int, SignedTxns) {
84 | for i, stxn := range sts {
85 | if stxn.GetTxnHash() == hash {
86 | if i == 0 {
87 | sts = sts[1:]
88 | return i, sts
89 | }
90 | if i == len(sts)-1 {
91 | sts = sts[:i]
92 | return i, sts
93 | }
94 |
95 | sts = append(sts[:i], sts[i+1:]...)
96 | return i, sts
97 | }
98 | }
99 | return -1, nil
100 | }
101 |
102 | func (sts SignedTxns) Encode() ([]byte, error) {
103 | var msts extSignedTxns
104 | for _, st := range sts {
105 | msts = append(msts, &extSignedTxn{
106 | Raw: st.Raw,
107 | TxnHash: st.TxnHash,
108 | Pubkey: st.Pubkey.BytesWithType(),
109 | Signature: st.Signature,
110 | })
111 | }
112 | return GlobalCodec.EncodeToBytes(msts)
113 | }
114 |
115 | func DecodeSignedTxns(data []byte) (SignedTxns, error) {
116 | mtxns := extSignedTxns{}
117 | err := GlobalCodec.DecodeBytes(data, &mtxns)
118 | if err != nil {
119 | return nil, err
120 | }
121 | var sts SignedTxns
122 | for _, mtxn := range mtxns {
123 | pubKey, err := PubKeyFromBytes(mtxn.Pubkey)
124 | if err != nil {
125 | return nil, err
126 | }
127 | sts = append(sts, &SignedTxn{
128 | Raw: mtxn.Raw,
129 | TxnHash: mtxn.TxnHash,
130 | Pubkey: pubKey,
131 | Signature: mtxn.Signature,
132 | })
133 | }
134 | return sts, err
135 | }
136 |
137 | type extSignedTxns []*extSignedTxn
138 |
139 | type extSignedTxn struct {
140 | Raw *UnsignedTxn
141 | TxnHash Hash
142 | Pubkey []byte
143 | Signature []byte
144 | }
145 |
--------------------------------------------------------------------------------
/node/worker/worker.go:
--------------------------------------------------------------------------------
1 | package worker
2 |
3 | import (
4 | "bytes"
5 | "github.com/gin-gonic/gin"
6 | "io/ioutil"
7 | "net/http"
8 | . "yu/blockchain"
9 | "yu/config"
10 | . "yu/node"
11 | "yu/storage/kv"
12 | "yu/tripod"
13 | . "yu/txpool"
14 | . "yu/utils/ip"
15 | )
16 |
17 | type Worker struct {
18 | Name string
19 | httpPort string
20 | wsPort string
21 | NodeKeeperAddr string
22 | chain IBlockChain
23 | base IBlockBase
24 | txPool ItxPool
25 | land *tripod.Land
26 |
27 | metadb kv.KV
28 |
29 | //// ready to package a batch of txns to broadcast
30 | //readyBcTxnsChan chan IsignedTxn
31 | //// number of broadcast txns every time
32 | //NumOfBcTxns int
33 | }
34 |
35 | func NewWorker(
36 | cfg *config.WorkerConf,
37 | chain IBlockChain,
38 | base IBlockBase,
39 | txPool ItxPool,
40 | land *tripod.Land,
41 | ) (*Worker, error) {
42 | metadb, err := kv.NewKV(&cfg.DB)
43 | if err != nil {
44 | return nil, err
45 | }
46 | nkAddr := MakeLocalIp(cfg.NodeKeeperPort)
47 | return &Worker{
48 | Name: cfg.Name,
49 | httpPort: MakePort(cfg.HttpPort),
50 | wsPort: MakePort(cfg.WsPort),
51 | NodeKeeperAddr: nkAddr,
52 | chain: chain,
53 | base: base,
54 | txPool: txPool,
55 | land: land,
56 | metadb: metadb,
57 | //readyBcTxnsChan: make(chan IsignedTxn),
58 | //NumOfBcTxns: cfg.NumOfBcTxns,
59 | }, nil
60 |
61 | }
62 |
63 | // Register into NodeKeeper
64 | func (w *Worker) RegisterInNk() error {
65 | infoByt, err := w.Info().EncodeMasterInfo()
66 | if err != nil {
67 | return err
68 | }
69 | _, err = w.postToNk(RegisterWorkersPath, infoByt)
70 | return err
71 | }
72 |
73 | func (w *Worker) Info() *WorkerInfo {
74 | tripodsInfo := make(map[string]TripodInfo)
75 | _ = w.land.RangeMap(func(triName string, tri tripod.Tripod) error {
76 | execNames := tri.TripodMeta().AllExecNames()
77 | queryNames := tri.TripodMeta().AllQueryNames()
78 | tripodsInfo[triName] = TripodInfo{
79 | ExecNames: execNames,
80 | QueryNames: queryNames,
81 | }
82 | return nil
83 | })
84 | return &WorkerInfo{
85 | Name: w.Name,
86 | HttpPort: w.httpPort,
87 | WsPort: w.wsPort,
88 | NodeKeeperAddr: w.NodeKeeperAddr,
89 | TripodsInfo: tripodsInfo,
90 | Online: true,
91 | }
92 | }
93 |
94 | func (w *Worker) postToNk(path string, body []byte) (*http.Response, error) {
95 | url := w.NodeKeeperAddr + path
96 | req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(body))
97 | if err != nil {
98 | return nil, err
99 | }
100 | cli := &http.Client{}
101 | return cli.Do(req)
102 | }
103 |
104 | func (w *Worker) CheckTxnsFromP2P(c *gin.Context) {
105 | byt, err := ioutil.ReadAll(c.Request.Body)
106 | if err != nil {
107 | c.AbortWithError(http.StatusBadRequest, err)
108 | return
109 | }
110 | tbody, err := DecodeTb(byt)
111 | if err != nil {
112 | c.AbortWithError(http.StatusBadRequest, err)
113 | return
114 | }
115 | txns, err := tbody.DecodeTxnsBody()
116 | if err != nil {
117 | c.AbortWithError(http.StatusBadRequest, err)
118 | return
119 | }
120 | for _, txn := range txns {
121 | err = w.txPool.BaseCheck(txn)
122 | if err != nil {
123 | c.AbortWithError(http.StatusBadRequest, err)
124 | return
125 | }
126 | err = w.txPool.TripodsCheck(txn)
127 | if err != nil {
128 | c.AbortWithError(http.StatusBadRequest, err)
129 | return
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/common/types.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "bytes"
5 | "encoding/binary"
6 | "strconv"
7 | "strings"
8 | "unsafe"
9 | )
10 |
11 | type RunMode int
12 |
13 | const (
14 | LocalNode RunMode = iota
15 | MasterWorker
16 | )
17 |
18 | const (
19 | StartBlockStage = "Start Block"
20 | ExecuteTxnsStage = "Execute Txns"
21 | EndBlockStage = "End Block"
22 | FinalizeBlockStage = "Finalize Block"
23 | )
24 |
25 | type (
26 | BlockNum uint32
27 | // Use to be a Key to store into KVDB.
28 | // Add BlockHash to the BlockNum's end.
29 | BlockId [BlockIdLen]byte
30 |
31 | JsonString = string
32 |
33 | // The Call from clients, it is an instance of an 'Execution'.
34 | Ecall struct {
35 | TripodName string
36 | ExecName string
37 | Params JsonString
38 | }
39 |
40 | // The Call from clients, it is an instance of an 'Query'.
41 | Qcall struct {
42 | TripodName string
43 | QueryName string
44 | BlockHash Hash
45 | Params JsonString
46 | }
47 | // Execution or Query
48 | CallType int
49 | )
50 |
51 | func (e *Ecall) Bytes() []byte {
52 | var byt []byte
53 | byt = append(byt, []byte(e.TripodName)...)
54 | byt = append(byt, []byte(e.ExecName)...)
55 | byt = append(byt, []byte(e.Params)...)
56 | return byt
57 | }
58 |
59 | const (
60 | ExecCall CallType = iota
61 | QryCall
62 |
63 | ExecCallType = "execution"
64 | QryCallType = "query"
65 | )
66 |
67 | func (bn BlockNum) len() int {
68 | return int(unsafe.Sizeof(bn))
69 | }
70 |
71 | func (bn BlockNum) Bytes() []byte {
72 | byt := make([]byte, bn.len())
73 | binary.BigEndian.PutUint32(byt, uint32(bn))
74 | return byt
75 | }
76 |
77 | func BytesToBlockNum(byt []byte) BlockNum {
78 | u := binary.BigEndian.Uint32(byt)
79 | return BlockNum(u)
80 | }
81 |
82 | func StrToBlockNum(str string) (BlockNum, error) {
83 | bn, err := strconv.ParseUint(str, 10, 64)
84 | return BlockNum(bn), err
85 | }
86 |
87 | func NewBlockId(bn BlockNum, hash Hash) BlockId {
88 | bnByt := bn.Bytes()
89 | hashByt := hash.Bytes()
90 |
91 | var id BlockId
92 | bnLen := bn.len()
93 | copy(id[:bnLen], bnByt)
94 | copy(id[bnLen:], hashByt)
95 | return id
96 | }
97 |
98 | func DecodeBlockId(byt []byte) (id BlockId) {
99 | copy(id[:], byt)
100 | return
101 | }
102 |
103 | func (bi BlockId) Bytes() []byte {
104 | return bi[:]
105 | }
106 |
107 | func (bi BlockId) Separate() (bn BlockNum, hash Hash) {
108 | byt := bi.Bytes()
109 | bnLen := bn.len()
110 | bn = BytesToBlockNum(byt[:bnLen])
111 | copy(hash[:], byt[bnLen:])
112 | return
113 | }
114 |
115 | const (
116 | // HashLength is the expected length of the hash
117 | HashLen = 32
118 | // AddressLength is the expected length of the address
119 | AddressLen = 20
120 | // len(BlockNum) + HashLen = 40
121 | BlockIdLen = 40
122 | )
123 |
124 | type (
125 | Hash [HashLen]byte
126 | Address [AddressLen]byte
127 | )
128 |
129 | var (
130 | NullHash Hash = [HashLen]byte{}
131 | NullAddress Address = [AddressLen]byte{}
132 | )
133 |
134 | func HashesToHex(hs []Hash) string {
135 | var buffer bytes.Buffer
136 | for _, h := range hs {
137 | buffer.WriteString(ToHex(h.Bytes()))
138 | }
139 | return buffer.String()
140 | }
141 |
142 | func HexToHashes(s string) (hs []Hash) {
143 | arr := strings.SplitN(s, "", HashLen)
144 | for _, hx := range arr {
145 | hs = append(hs, HexToHash(hx))
146 | }
147 | return
148 | }
149 |
150 | func HashesToBytes(hs []Hash) []byte {
151 | return []byte(HashesToHex(hs))
152 | }
153 |
154 | func BytesToHashes(data []byte) []Hash {
155 | return HexToHashes(string(data))
156 | }
157 |
--------------------------------------------------------------------------------
/blockchain/blockbase_scheme.go:
--------------------------------------------------------------------------------
1 | package blockchain
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/Lawliet-Chan/yu/keypair"
6 | . "github.com/Lawliet-Chan/yu/result"
7 | . "github.com/Lawliet-Chan/yu/txn"
8 | "gorm.io/gorm"
9 | )
10 |
11 | type TxnScheme struct {
12 | TxnHash string `gorm:"primaryKey"`
13 | Pubkey string
14 | Signature string
15 | RawTxn string
16 |
17 | BlockHash string
18 | }
19 |
20 | func (TxnScheme) TableName() string {
21 | return "txns"
22 | }
23 |
24 | func newTxnScheme(blockHash Hash, stxn *SignedTxn) (TxnScheme, error) {
25 | txnSm, err := toTxnScheme(stxn)
26 | if err != nil {
27 | return TxnScheme{}, err
28 | }
29 | txnSm.BlockHash = blockHash.String()
30 | return txnSm, nil
31 | }
32 |
33 | func toTxnScheme(stxn *SignedTxn) (TxnScheme, error) {
34 | rawTxnByt, err := stxn.GetRaw().Encode()
35 | if err != nil {
36 | return TxnScheme{}, err
37 | }
38 | return TxnScheme{
39 | TxnHash: stxn.GetTxnHash().String(),
40 | Pubkey: stxn.GetPubkey().StringWithType(),
41 | Signature: ToHex(stxn.GetSignature()),
42 | RawTxn: ToHex(rawTxnByt),
43 | BlockHash: "",
44 | }, nil
45 | }
46 |
47 | func (t TxnScheme) toTxn() (*SignedTxn, error) {
48 | ut := &UnsignedTxn{}
49 | rawTxn, err := ut.Decode(FromHex(t.RawTxn))
50 | if err != nil {
51 | return nil, err
52 | }
53 | pubkey, err := keypair.PubkeyFromStr(t.Pubkey)
54 | if err != nil {
55 | return nil, err
56 | }
57 | return &SignedTxn{
58 | Raw: rawTxn,
59 | TxnHash: HexToHash(t.TxnHash),
60 | Pubkey: pubkey,
61 | Signature: FromHex(t.Signature),
62 | }, nil
63 | }
64 |
65 | type EventScheme struct {
66 | gorm.Model
67 | Caller string
68 | BlockStage string
69 | BlockHash string
70 | Height BlockNum
71 | TripodName string
72 | ExecName string
73 | Value string
74 | }
75 |
76 | func (EventScheme) TableName() string {
77 | return "events"
78 | }
79 |
80 | func toEventScheme(event *Event) (EventScheme, error) {
81 | return EventScheme{
82 | Caller: event.Caller.String(),
83 | BlockStage: event.BlockStage,
84 | BlockHash: event.BlockHash.String(),
85 | Height: event.Height,
86 | TripodName: event.TripodName,
87 | ExecName: event.ExecName,
88 | Value: event.Value,
89 | }, nil
90 | }
91 |
92 | func (e EventScheme) toEvent() (*Event, error) {
93 | return &Event{
94 | Caller: HexToAddress(e.Caller),
95 | BlockStage: e.BlockStage,
96 | BlockHash: HexToHash(e.BlockHash),
97 | Height: e.Height,
98 | TripodName: e.TripodName,
99 | ExecName: e.ExecName,
100 | Value: e.Value,
101 | }, nil
102 |
103 | }
104 |
105 | type ErrorScheme struct {
106 | gorm.Model
107 | Caller string
108 | BlockStage string
109 | BlockHash string
110 | Height BlockNum
111 | TripodName string
112 | ExecName string
113 | Error string
114 | }
115 |
116 | func (ErrorScheme) TableName() string {
117 | return "errors"
118 | }
119 |
120 | func toErrorScheme(err *Error) ErrorScheme {
121 | return ErrorScheme{
122 | Caller: err.Caller.String(),
123 | BlockStage: err.BlockStage,
124 | BlockHash: err.BlockHash.String(),
125 | Height: err.Height,
126 | TripodName: err.TripodName,
127 | ExecName: err.ExecName,
128 | Error: err.Err,
129 | }
130 | }
131 |
132 | func (e ErrorScheme) toError() *Error {
133 | return &Error{
134 | Caller: HexToAddress(e.Caller),
135 | BlockStage: e.BlockStage,
136 | BlockHash: HexToHash(e.BlockHash),
137 | Height: e.Height,
138 | TripodName: e.TripodName,
139 | ExecName: e.ExecName,
140 | Err: e.Error,
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/config/config.go:
--------------------------------------------------------------------------------
1 | package config
2 |
3 | import (
4 | "github.com/BurntSushi/toml"
5 | . "github.com/Lawliet-Chan/yu/common"
6 | "github.com/sirupsen/logrus"
7 | )
8 |
9 | type MasterConf struct {
10 | // 0: local-node
11 | // 1: master-worker
12 | RunMode RunMode `toml:"run_mode"`
13 | // serve http port
14 | HttpPort string `toml:"http_port"`
15 | // serve websocket port
16 | WsPort string `toml:"ws_port"`
17 | // log out level:
18 | // panic, fatal, error, warn, info, debug, trace
19 | LogLevel string `toml:"log_level"`
20 |
21 | LeiLimit uint64 `toml:"lei_limit"`
22 |
23 | NkDB KVconf `toml:"nk_db"`
24 | // when beyond 'Timeout', it means this nodekeeper is down.
25 | // Unit is Second.
26 | Timeout int `toml:"timeout"`
27 |
28 | //---------component config---------
29 | BlockChain BlockchainConf `toml:"block_chain"`
30 | BlockBase BlockBaseConf `toml:"block_base"`
31 | State StateConf `toml:"state"`
32 | Txpool TxpoolConf `toml:"txpool"`
33 |
34 | //---------P2P config--------
35 | // For listening from blockchain network.
36 | P2pListenAddrs []string `toml:"p2p_listen_addrs"`
37 | // To connect other hosts as a p2p network.
38 | Bootnodes []string `toml:"bootnodes"`
39 |
40 | ProtocolID string `toml:"protocol_id"`
41 | // 0: RSA
42 | // 1: Ed25519
43 | // 2: Secp256k1
44 | // 3: ECDSA
45 | NodeKeyType int `toml:"node_key_type"`
46 |
47 | NodeKeyRandSeed int64 `toml:"node_key_rand_seed"`
48 |
49 | NodeKey string `toml:"node_key"`
50 |
51 | // Only RSA has this param.
52 | NodeKeyBits int `toml:"node_key_bits"`
53 | // When use param 'NodeKey', 'NodeKeyFile' will not work.
54 | NodeKeyFile string `toml:"node_key_file"`
55 | }
56 |
57 | //type WorkerConf struct {
58 | // Name string `toml:"name"`
59 | // DB KVconf `toml:"db"`
60 | // NodeKeeperPort string `toml:"node_keeper_port"`
61 | //
62 | // // number of broadcast txns every time
63 | // NumOfBcTxns int `toml:"num_of_bc_txns"`
64 | //
65 | // // serve http port
66 | // HttpPort string `toml:"http_port"`
67 | // // serve websocket port
68 | // WsPort string `toml:"ws_port"`
69 | // // the interval of heartbeat to NodeKeeper,
70 | // // the unit is Second
71 | // Interval int `toml:"interval"`
72 | //
73 | // TxPoolSize int `toml:"tx_pool_size"`
74 | //}
75 | //
76 | //type NodeKeeperConf struct {
77 | // ServesPort string `toml:"serves_port"`
78 | // // Direction used to keep executable file and others.
79 | // Dir string `toml:"dir"`
80 | // // It MUST be {Dir}/xx.db
81 | // // When you use {Dir}/path/to/xx.db, it will be trimmed as {Dir}/xx.db
82 | // RepoDbPath string `toml:"repo_db_path"`
83 | // WorkerDbPath string `toml:"worker_db_path"`
84 | // // specify the os and arch of repo
85 | // // Usually you need not define it, it will get os and arch from local host.
86 | // // such as: linux-amd64, darwin-amd64, windows-amd64, wasm
87 | // OsArch string `toml:"os_arch"`
88 | //
89 | // MasterAddr string `toml:"master_addr"`
90 | // HeartbeatGap int `toml:"heartbeat_gap"`
91 | //}
92 |
93 | type BlockchainConf struct {
94 | ChainDB SqlDbConf `toml:"chain_db"`
95 | BlocksFromP2pDB SqlDbConf `toml:"blocks_from_p2p_db"`
96 | }
97 |
98 | type BlockBaseConf struct {
99 | BaseDB SqlDbConf `toml:"base_db"`
100 | }
101 |
102 | type TxpoolConf struct {
103 | PoolSize uint64 `toml:"pool_size"`
104 | TxnMaxSize int `toml:"txn_max_size"`
105 | Timeout int `toml:"timeout"`
106 | // In local-node mode, this will be null.
107 | WorkerIP string `toml:"worker_ip"`
108 | }
109 |
110 | func LoadConf(fpath string, cfg interface{}) {
111 | _, err := toml.DecodeFile(fpath, cfg)
112 | if err != nil {
113 | logrus.Panicf("load config-file(%s) error: %s ", fpath, err.Error())
114 | }
115 | }
116 |
--------------------------------------------------------------------------------
/common/bytes.go:
--------------------------------------------------------------------------------
1 | // Copyright 2014 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | // Package common contains various helper functions.
18 | package common
19 |
20 | import (
21 | "encoding/hex"
22 | "github.com/HyperService-Consortium/go-hexutil"
23 | )
24 |
25 | // ToHex returns the hex representation of b, prefixed with '0x'.
26 | // For empty slices, the return value is "0x0".
27 | func ToHex(b []byte) string {
28 | return hexutil.Encode(b)
29 | }
30 |
31 | // ToHexArray creates a array of hex-string based on []byte
32 | func ToHexArray(b [][]byte) []string {
33 | r := make([]string, len(b))
34 | for i := range b {
35 | r[i] = ToHex(b[i])
36 | }
37 | return r
38 | }
39 |
40 | // FromHex returns the bytes represented by the hexadecimal string s.
41 | // s may be prefixed with "0x".
42 | func FromHex(s string) []byte {
43 | if len(s) > 1 {
44 | if s[0:2] == "0x" || s[0:2] == "0X" {
45 | s = s[2:]
46 | }
47 | }
48 | if len(s)%2 == 1 {
49 | s = "0" + s
50 | }
51 | return Hex2Bytes(s)
52 | }
53 |
54 | // CopyBytes returns an exact copy of the provided bytes.
55 | func CopyBytes(b []byte) (copiedBytes []byte) {
56 | if b == nil {
57 | return nil
58 | }
59 | copiedBytes = make([]byte, len(b))
60 | copy(copiedBytes, b)
61 |
62 | return
63 | }
64 |
65 | // hasHexPrefix validates str begins with '0x' or '0X'.
66 | func hasHexPrefix(str string) bool {
67 | return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')
68 | }
69 |
70 | // isHexCharacter returns bool of c being a valid hexadecimal.
71 | func isHexCharacter(c byte) bool {
72 | return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
73 | }
74 |
75 | // isHex validates whether each byte is valid hexadecimal string.
76 | func isHex(str string) bool {
77 | if len(str)%2 != 0 {
78 | return false
79 | }
80 | for _, c := range []byte(str) {
81 | if !isHexCharacter(c) {
82 | return false
83 | }
84 | }
85 | return true
86 | }
87 |
88 | // Bytes2Hex returns the hexadecimal encoding of d.
89 | func Bytes2Hex(d []byte) string {
90 | return hex.EncodeToString(d)
91 | }
92 |
93 | // Hex2Bytes returns the bytes represented by the hexadecimal string str.
94 | func Hex2Bytes(str string) []byte {
95 | h, _ := hex.DecodeString(str)
96 | return h
97 | }
98 |
99 | // Hex2BytesFixed returns bytes of a specified fixed length flen.
100 | func Hex2BytesFixed(str string, flen int) []byte {
101 | h, _ := hex.DecodeString(str)
102 | if len(h) == flen {
103 | return h
104 | }
105 | if len(h) > flen {
106 | return h[len(h)-flen:]
107 | }
108 | hh := make([]byte, flen)
109 | copy(hh[flen-len(h):flen], h)
110 | return hh
111 | }
112 |
113 | // RightPadBytes zero-pads slice to the right up to length l.
114 | func RightPadBytes(slice []byte, l int) []byte {
115 | if l <= len(slice) {
116 | return slice
117 | }
118 |
119 | padded := make([]byte, l)
120 | copy(padded, slice)
121 |
122 | return padded
123 | }
124 |
125 | // LeftPadBytes zero-pads slice to the left up to length l.
126 | func LeftPadBytes(slice []byte, l int) []byte {
127 | if l <= len(slice) {
128 | return slice
129 | }
130 |
131 | padded := make([]byte, l)
132 | copy(padded[l-len(slice):], slice)
133 |
134 | return padded
135 | }
136 |
--------------------------------------------------------------------------------
/yerror/basic_error.go:
--------------------------------------------------------------------------------
1 | package yerror
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/pkg/errors"
6 | )
7 |
8 | var TypeErr = errors.New("the type of params error")
9 | var IntegerOverflow = errors.New("integer overflow")
10 |
11 | var NoRunMode = errors.New("no run mode")
12 | var NoKeyType = errors.New("no key type")
13 | var NoConvergeType = errors.New("no converge type")
14 |
15 | var GenesisBlockIllegal = errors.New("genesis block is illegal")
16 |
17 | var NoKvdbType = errors.New("no kvdb type")
18 | var NoQueueType = errors.New("no queue type")
19 | var NoSqlDbType = errors.New("no sqlDB type")
20 |
21 | var (
22 | PoolOverflow error = errors.New("pool size is full")
23 | TxnSignatureErr error = errors.New("the signature of Txn illegal")
24 | TxnTooLarge error = errors.New("the size of txn is too large")
25 | )
26 |
27 | var OutOfEnergy = errors.New("energy out")
28 |
29 | type ErrBlockIllegal struct {
30 | BlockHash string
31 | }
32 |
33 | func BlockIllegal(blockHash Hash) ErrBlockIllegal {
34 | return ErrBlockIllegal{BlockHash: blockHash.String()}
35 | }
36 |
37 | func (b ErrBlockIllegal) Error() string {
38 | return errors.Errorf("block(%s) illegal", b.BlockHash).Error()
39 | }
40 |
41 | type ErrNoTxnInP2P struct {
42 | TxnHash string
43 | }
44 |
45 | func NoTxnInP2P(txnHash Hash) ErrNoTxnInP2P {
46 | return ErrNoTxnInP2P{TxnHash: txnHash.String()}
47 | }
48 |
49 | func (t ErrNoTxnInP2P) Error() string {
50 | return errors.Errorf("no txn(%s) in P2P network", t.TxnHash).Error()
51 | }
52 |
53 | type ErrTripodNotFound struct {
54 | TripodName string
55 | }
56 |
57 | func TripodNotFound(name string) ErrTripodNotFound {
58 | return ErrTripodNotFound{TripodName: name}
59 | }
60 |
61 | func (t ErrTripodNotFound) Error() string {
62 | return errors.Errorf("Tripod (%s) NOT Found", t.TripodName).Error()
63 | }
64 |
65 | type ErrExecNotFound struct {
66 | ExecName string
67 | }
68 |
69 | func ExecNotFound(name string) ErrExecNotFound {
70 | return ErrExecNotFound{ExecName: name}
71 | }
72 |
73 | func (e ErrExecNotFound) Error() string {
74 | return errors.Errorf("Execution(%s) NOT Found", e.ExecName).Error()
75 | }
76 |
77 | type ErrQryNotFound struct {
78 | QryName string
79 | }
80 |
81 | func QryNotFound(name string) ErrQryNotFound {
82 | return ErrQryNotFound{QryName: name}
83 | }
84 |
85 | func (q ErrQryNotFound) Error() string {
86 | return errors.Errorf("Query(%s) NOT Found", q.QryName).Error()
87 | }
88 |
89 | //type ErrOutOfEnergy struct {
90 | // txnsHashes []string
91 | //}
92 | //
93 | //func OutOfEnergy(txnsHashes []Hash) ErrOutOfEnergy {
94 | // hashes := make([]string, 0)
95 | // for _, txnHash := range txnsHashes {
96 | // hashes = append(hashes, txnHash.String())
97 | // }
98 | // return ErrOutOfEnergy{txnsHashes: hashes}
99 | //}
100 | //
101 | //func (oe ErrOutOfEnergy) Error() string {
102 | // return errors.Errorf("Energy out! txns(%v) ")
103 | //}
104 |
105 | type ErrNodeKeeperDead struct {
106 | IP string
107 | }
108 |
109 | func NodeKeeperDead(ip string) ErrNodeKeeperDead {
110 | return ErrNodeKeeperDead{IP: ip}
111 | }
112 |
113 | func (n ErrNodeKeeperDead) Error() string {
114 | return errors.Errorf("NodeKeeper(%s) is dead", n.IP).Error()
115 | }
116 |
117 | type ErrWorkerDead struct {
118 | Name string
119 | }
120 |
121 | func WorkerDead(name string) ErrWorkerDead {
122 | return ErrWorkerDead{Name: name}
123 | }
124 |
125 | func (w ErrWorkerDead) Error() string {
126 | return errors.Errorf("Worker(%s) is dead", w.Name).Error()
127 | }
128 |
129 | type ErrWaitTxnsTimeout struct {
130 | TxnsHash []Hash
131 | }
132 |
133 | func WaitTxnsTimeout(hashesMap map[Hash]bool) ErrWaitTxnsTimeout {
134 | hashes := make([]Hash, 0)
135 | for hash, _ := range hashesMap {
136 | hashes = append(hashes, hash)
137 | }
138 | return ErrWaitTxnsTimeout{TxnsHash: hashes}
139 | }
140 |
141 | func (wt ErrWaitTxnsTimeout) Error() string {
142 | return errors.Errorf("waiting txns-hashes timeout: %v", wt.TxnsHash).Error()
143 | }
144 |
--------------------------------------------------------------------------------
/trie/mpt/encoding.go:
--------------------------------------------------------------------------------
1 | // Copyright 2014 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package mpt
18 |
19 | // Trie keys are dealt with in three distinct encodings:
20 | //
21 | // KEYBYTES encoding contains the actual key and nothing else. This encoding is the
22 | // input to most API functions.
23 | //
24 | // HEX encoding contains one byte for each nibble of the key and an optional trailing
25 | // 'terminator' byte of value 0x10 which indicates whether or not the node at the key
26 | // contains a value. Hex key encoding is used for nodes loaded in memory because it's
27 | // convenient to access.
28 | //
29 | // COMPACT encoding is defined by the Ethereum Yellow Paper (it's called "hex prefix
30 | // encoding" there) and contains the bytes of the key and a flag. The high nibble of the
31 | // first byte contains the flag; the lowest bit encoding the oddness of the length and
32 | // the second-lowest encoding whether the node at the key is a value node. The low nibble
33 | // of the first byte is zero in the case of an even number of nibbles and the first nibble
34 | // in the case of an odd number. All remaining nibbles (now an even number) fit properly
35 | // into the remaining bytes. Compact encoding is used for nodes stored on disk.
36 |
37 | func hexToCompact(hex []byte) []byte {
38 | terminator := byte(0)
39 | if hasTerm(hex) {
40 | terminator = 1
41 | hex = hex[:len(hex)-1]
42 | }
43 | buf := make([]byte, len(hex)/2+1)
44 | buf[0] = terminator << 5 // the flag byte
45 | if len(hex)&1 == 1 {
46 | buf[0] |= 1 << 4 // odd flag
47 | buf[0] |= hex[0] // first nibble is contained in the first byte
48 | hex = hex[1:]
49 | }
50 | decodeNibbles(hex, buf[1:])
51 | return buf
52 | }
53 |
54 | func compactToHex(compact []byte) []byte {
55 | if len(compact) == 0 {
56 | return compact
57 | }
58 | base := keybytesToHex(compact)
59 | // delete terminator flag
60 | if base[0] < 2 {
61 | base = base[:len(base)-1]
62 | }
63 | // apply odd flag
64 | chop := 2 - base[0]&1
65 | return base[chop:]
66 | }
67 |
68 | func keybytesToHex(str []byte) []byte {
69 | l := len(str)*2 + 1
70 | var nibbles = make([]byte, l)
71 | for i, b := range str {
72 | nibbles[i*2] = b / 16
73 | nibbles[i*2+1] = b % 16
74 | }
75 | nibbles[l-1] = 16
76 | return nibbles
77 | }
78 |
79 | // hexToKeybytes turns hex nibbles into key bytes.
80 | // This can only be used for keys of even length.
81 | func hexToKeybytes(hex []byte) []byte {
82 | if hasTerm(hex) {
83 | hex = hex[:len(hex)-1]
84 | }
85 | if len(hex)&1 != 0 {
86 | panic("can't convert hex key of odd length")
87 | }
88 | key := make([]byte, len(hex)/2)
89 | decodeNibbles(hex, key)
90 | return key
91 | }
92 |
93 | func decodeNibbles(nibbles []byte, bytes []byte) {
94 | for bi, ni := 0, 0; ni < len(nibbles); bi, ni = bi+1, ni+2 {
95 | bytes[bi] = nibbles[ni]<<4 | nibbles[ni+1]
96 | }
97 | }
98 |
99 | // prefixLen returns the length of the common prefix of a and b.
100 | func prefixLen(a, b []byte) int {
101 | var i, length = 0, len(a)
102 | if len(b) < length {
103 | length = len(b)
104 | }
105 | for ; i < length; i++ {
106 | if a[i] != b[i] {
107 | break
108 | }
109 | }
110 | return i
111 | }
112 |
113 | // hasTerm returns whether a hex key has the terminator flag.
114 | func hasTerm(s []byte) bool {
115 | return len(s) > 0 && s[len(s)-1] == 16
116 | }
117 |
--------------------------------------------------------------------------------
/blockchain/block.go:
--------------------------------------------------------------------------------
1 | package blockchain
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/Lawliet-Chan/yu/trie"
6 | "github.com/Lawliet-Chan/yu/txn"
7 | . "github.com/Lawliet-Chan/yu/utils/codec"
8 | "github.com/libp2p/go-libp2p-core/peer"
9 | )
10 |
11 | type Block struct {
12 | Header *Header
13 | TxnsHashes []Hash
14 |
15 | ChainLength uint64
16 | }
17 |
18 | func (b *Block) GetHeight() BlockNum {
19 | return b.Header.GetHeight()
20 | }
21 |
22 | func (b *Block) GetHash() Hash {
23 | return b.Header.GetHash()
24 | }
25 |
26 | func (b *Block) GetPrevHash() Hash {
27 | return b.Header.GetPrevHash()
28 | }
29 |
30 | func (b *Block) GetTxnRoot() Hash {
31 | return b.Header.GetTxnRoot()
32 | }
33 |
34 | func (b *Block) GetStateRoot() Hash {
35 | return b.Header.GetStateRoot()
36 | }
37 |
38 | func (b *Block) GetTimestamp() uint64 {
39 | return b.Header.GetTimestamp()
40 | }
41 |
42 | func (b *Block) GetHeader() IHeader {
43 | return b.Header
44 | }
45 |
46 | func (b *Block) GetTxnsHashes() []Hash {
47 | return b.TxnsHashes
48 | }
49 |
50 | func (b *Block) SetTxnsHashes(hashes []Hash) {
51 | b.TxnsHashes = hashes
52 | }
53 |
54 | func (b *Block) SetHash(hash Hash) {
55 | b.Header.Hash = hash
56 | }
57 |
58 | func (b *Block) SetPreHash(preHash Hash) {
59 | b.Header.PrevHash = preHash
60 | }
61 |
62 | func (b *Block) SetTxnRoot(hash Hash) {
63 | b.Header.TxnRoot = hash
64 | }
65 |
66 | func (b *Block) SetHeight(height BlockNum) {
67 | b.Header.Height = height
68 | }
69 |
70 | func (b *Block) GetBlockId() BlockId {
71 | return NewBlockId(b.Header.GetHeight(), b.Header.GetHash())
72 | }
73 |
74 | func (b *Block) SetStateRoot(hash Hash) {
75 | b.Header.StateRoot = hash
76 | }
77 |
78 | func (b *Block) SetTimestamp(ts uint64) {
79 | b.Header.Timestamp = ts
80 | }
81 |
82 | func (b *Block) GetPeerID() peer.ID {
83 | return b.Header.PeerID
84 | }
85 |
86 | func (b *Block) SetPeerID(peerID peer.ID) {
87 | b.Header.PeerID = peerID
88 | }
89 |
90 | func (b *Block) GetLeiLimit() uint64 {
91 | return b.Header.LeiLimit
92 | }
93 |
94 | func (b *Block) SetLeiLimit(e uint64) {
95 | b.Header.LeiLimit = e
96 | }
97 |
98 | func (b *Block) GetLeiUsed() uint64 {
99 | return b.Header.LeiUsed
100 | }
101 |
102 | func (b *Block) UseLei(e uint64) {
103 | b.Header.LeiUsed += e
104 | }
105 |
106 | //
107 | //func (b *Block) SetSign(sign []byte) {
108 | // b.Header.Signature = sign
109 | //}
110 | //
111 | //func (b *Block) GetSign() []byte {
112 | // return b.Header.GetSign()
113 | //}
114 | //
115 | //func (b *Block) SetPubkey(key PubKey) {
116 | // b.Header.Pubkey = key.BytesWithType()
117 | //}
118 | //
119 | //func (b *Block) GetPubkey() PubKey {
120 | // return b.Header.GetPubkey()
121 | //}
122 |
123 | func (b *Block) SetNonce(nonce uint64) {
124 | b.Header.Nonce = nonce
125 | }
126 |
127 | func (b *Block) SetChainLength(len uint64) {
128 | b.ChainLength = len
129 | }
130 |
131 | func (b *Block) Encode() ([]byte, error) {
132 | return GlobalCodec.EncodeToBytes(b)
133 | }
134 |
135 | func (b *Block) Decode(data []byte) (IBlock, error) {
136 | var block Block
137 | err := GlobalCodec.DecodeBytes(data, &block)
138 | return &block, err
139 | }
140 |
141 | func (b *Block) CopyFrom(other IBlock) {
142 | otherBlock := other.(*Block)
143 | *b = *otherBlock
144 | }
145 |
146 | func IfLeiOut(Lei uint64, block IBlock) bool {
147 | return Lei+block.GetLeiUsed() > block.GetLeiLimit()
148 | }
149 |
150 | func MakeTxnRoot(txns []*txn.SignedTxn) (Hash, error) {
151 | txnsBytes := make([]Hash, 0)
152 | for _, tx := range txns {
153 | hash := tx.GetTxnHash()
154 | txnsBytes = append(txnsBytes, hash)
155 | }
156 | mTree := trie.NewMerkleTree(txnsBytes)
157 | return mTree.RootNode.Data, nil
158 | }
159 |
160 | //func getPubkeyStrOrNil(b IBlock) (pubkeyStr string) {
161 | // pubkey := b.GetPubkey()
162 | // if pubkey == nil {
163 | // return
164 | // }
165 | // return pubkey.StringWithType()
166 | //}
167 |
168 | //
169 | //func (b *Block) getSignStr() (signStr string) {
170 | // sign := b.GetSign()
171 | // if sign == nil {
172 | // return
173 | // }
174 | // return ToHex(sign)
175 | //}
176 |
--------------------------------------------------------------------------------
/node/master/p2p_protocol.go:
--------------------------------------------------------------------------------
1 | package master
2 |
3 | import (
4 | "encoding/json"
5 | . "github.com/Lawliet-Chan/yu/common"
6 | "github.com/Lawliet-Chan/yu/yerror"
7 | "github.com/libp2p/go-libp2p-core/peer"
8 | "strconv"
9 | )
10 |
11 | const (
12 | HandshakeType int = iota
13 | SyncTxnsType
14 |
15 | RequestTypeBytesLen = 1
16 | )
17 |
18 | var (
19 | HandshakeReqByt = []byte(strconv.Itoa(HandshakeType))
20 | SyncTxnsReqByt = []byte(strconv.Itoa(SyncTxnsType))
21 | )
22 |
23 | type HandShakeRequest struct {
24 | FetchRange *BlocksRange
25 | Info *HandShakeInfo
26 | }
27 |
28 | func (m *Master) NewHsReq(fetchRange *BlocksRange) (*HandShakeRequest, error) {
29 | info, err := m.NewHsInfo()
30 | if err != nil {
31 | return nil, err
32 | }
33 | return &HandShakeRequest{
34 | FetchRange: fetchRange,
35 | Info: info,
36 | }, nil
37 | }
38 |
39 | func (hs *HandShakeRequest) Encode() ([]byte, error) {
40 | byt, err := json.Marshal(hs)
41 | if err != nil {
42 | return nil, err
43 | }
44 | return append(HandshakeReqByt, byt...), nil
45 | }
46 |
47 | func DecodeHsRequest(data []byte) (*HandShakeRequest, error) {
48 | var hs HandShakeRequest
49 | err := json.Unmarshal(data[RequestTypeBytesLen:], &hs)
50 | if err != nil {
51 | return nil, err
52 | }
53 | return &hs, nil
54 | }
55 |
56 | type HandShakeInfo struct {
57 | GenesisBlockHash Hash
58 |
59 | // when chain is finlaized chain, end block is the finalized block
60 | EndHeight BlockNum
61 | EndBlockHash Hash
62 | }
63 |
64 | func (m *Master) NewHsInfo() (*HandShakeInfo, error) {
65 | gBlock, err := m.chain.GetGenesis()
66 | if err != nil {
67 | return nil, err
68 | }
69 |
70 | eBlock, err := m.chain.GetEndBlock()
71 | if err != nil {
72 | return nil, err
73 | }
74 |
75 | return &HandShakeInfo{
76 | GenesisBlockHash: gBlock.GetHash(),
77 | EndHeight: eBlock.GetHeight(),
78 | EndBlockHash: eBlock.GetHash(),
79 | }, nil
80 | }
81 |
82 | // return a BlocksRange if other node's height is lower
83 | func (hs *HandShakeInfo) Compare(other *HandShakeInfo) (*BlocksRange, error) {
84 | if hs.GenesisBlockHash != other.GenesisBlockHash {
85 | return nil, yerror.GenesisBlockIllegal
86 | }
87 |
88 | if hs.EndHeight > other.EndHeight {
89 | return &BlocksRange{
90 | StartHeight: other.EndHeight + 1,
91 | EndHeight: hs.EndHeight,
92 | }, nil
93 | }
94 |
95 | return nil, nil
96 | }
97 |
98 | type HandShakeResp struct {
99 | // missing blocks range
100 | MissingRange *BlocksRange
101 | // compressed blocks bytes
102 | BlocksByt []byte
103 | // key: block-hash,
104 | // value: compressed txns bytes
105 | TxnsByt map[Hash][]byte
106 | Err error
107 | }
108 |
109 | func (hs *HandShakeResp) Encode() ([]byte, error) {
110 | return json.Marshal(hs)
111 | }
112 |
113 | func DecodeHsResp(data []byte) (*HandShakeResp, error) {
114 | var hs HandShakeResp
115 | err := json.Unmarshal(data, &hs)
116 | return &hs, err
117 | }
118 |
119 | type BlocksRange struct {
120 | StartHeight BlockNum
121 | EndHeight BlockNum
122 | }
123 |
124 | //type PackedTxns struct {
125 | // BlockHash string
126 | // TxnsBytes []byte
127 | //}
128 | //
129 | //func NewPackedTxns(blockHash Hash, txns SignedTxns) (*PackedTxns, error) {
130 | // byt, err := txns.Encode()
131 | // if err != nil {
132 | // return nil, err
133 | // }
134 | // return &PackedTxns{
135 | // BlockHash: blockHash.String(),
136 | // TxnsBytes: byt,
137 | // }, nil
138 | //}
139 | //
140 | //func (pt *PackedTxns) Resolve() (Hash, SignedTxns, error) {
141 | // stxns, err := DecodeSignedTxns(pt.TxnsBytes)
142 | // if err != nil {
143 | // return NullHash, nil, err
144 | // }
145 | // return HexToHash(pt.BlockHash), stxns, nil
146 | //}
147 |
148 | type TxnsRequest struct {
149 | Hashes []Hash
150 | BlockProducer peer.ID
151 | }
152 |
153 | func (tr TxnsRequest) Encode() ([]byte, error) {
154 | byt, err := json.Marshal(tr)
155 | if err != nil {
156 | return nil, err
157 | }
158 | return append(SyncTxnsReqByt, byt...), nil
159 | }
160 |
161 | func DecodeTxnsRequest(data []byte) (tr TxnsRequest, err error) {
162 | err = json.Unmarshal(data[RequestTypeBytesLen:], &tr)
163 | return
164 | }
165 |
--------------------------------------------------------------------------------
/apps/pow/pow.go:
--------------------------------------------------------------------------------
1 | package pow
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/blockchain"
5 | . "github.com/Lawliet-Chan/yu/chain_env"
6 | spow "github.com/Lawliet-Chan/yu/consensus/pow"
7 | . "github.com/Lawliet-Chan/yu/keypair"
8 | "github.com/Lawliet-Chan/yu/node"
9 | . "github.com/Lawliet-Chan/yu/tripod"
10 | "github.com/Lawliet-Chan/yu/txn"
11 | "github.com/sirupsen/logrus"
12 | "math/big"
13 | "time"
14 | )
15 |
16 | type Pow struct {
17 | meta *TripodMeta
18 | target *big.Int
19 | targetBits int64
20 |
21 | myPrivKey PrivKey
22 | myPubkey PubKey
23 |
24 | pkgTxnsLimit uint64
25 | }
26 |
27 | func NewPow(pkgTxnsLimit uint64) *Pow {
28 | meta := NewTripodMeta("pow")
29 | var targetBits int64 = 16
30 | target := big.NewInt(1)
31 | target.Lsh(target, uint(256-targetBits))
32 |
33 | pubkey, privkey, err := GenKeyPair(Sr25519)
34 | if err != nil {
35 | logrus.Fatalf("generate my keypair error: %s", err.Error())
36 | }
37 |
38 | return &Pow{
39 | meta: meta,
40 | target: target,
41 | targetBits: targetBits,
42 | myPrivKey: privkey,
43 | myPubkey: pubkey,
44 |
45 | pkgTxnsLimit: pkgTxnsLimit,
46 | }
47 | }
48 |
49 | func (p *Pow) GetTripodMeta() *TripodMeta {
50 | return p.meta
51 | }
52 |
53 | func (p *Pow) Name() string {
54 | return p.meta.Name()
55 | }
56 |
57 | func (*Pow) CheckTxn(*txn.SignedTxn) error {
58 | return nil
59 | }
60 |
61 | func (p *Pow) VerifyBlock(block IBlock, _ *ChainEnv) bool {
62 | return spow.Validate(block, p.target, p.targetBits)
63 | }
64 |
65 | func (p *Pow) InitChain(env *ChainEnv, _ *Land) error {
66 | chain := env.Chain
67 | gensisBlock := &Block{
68 | Header: &Header{},
69 | }
70 | return chain.SetGenesis(gensisBlock)
71 | }
72 |
73 | func (p *Pow) StartBlock(block IBlock, env *ChainEnv, _ *Land) (needBroadcast bool, err error) {
74 | time.Sleep(2 * time.Second)
75 |
76 | chain := env.Chain
77 | pool := env.Pool
78 |
79 | logrus.Info("start block...................")
80 |
81 | prevBlock, err := chain.GetEndBlock()
82 | if err != nil {
83 | return
84 | }
85 |
86 | logrus.Infof("prev-block hash is (%s), height is (%d)", block.GetPrevHash().String(), block.GetHeight()-1)
87 |
88 | block.(*Block).SetChainLength(prevBlock.(*Block).ChainLength + 1)
89 |
90 | pbMap, err := chain.TakeP2pBlocksBefore(block.GetHeight())
91 | if err != nil {
92 | logrus.Errorf("get p2p-blocks before error: %s", err.Error())
93 | }
94 |
95 | for _, pbs := range pbMap {
96 | for _, pb := range pbs {
97 | err = chain.AppendBlock(pb)
98 | if err != nil {
99 | return
100 | }
101 | }
102 | }
103 |
104 | pbsht, err := chain.TakeP2pBlocks(block.GetHeight())
105 | if err != nil {
106 | logrus.Errorf("get p2p-blocks error: %s", err.Error())
107 | }
108 | if len(pbsht) > 0 {
109 | block.CopyFrom(pbsht[0])
110 | logrus.Infof("USE P2P block(%s)", block.GetHash().String())
111 | env.StartBlock(block.GetHash())
112 | return
113 | }
114 |
115 | needBroadcast = true
116 |
117 | txns, err := pool.Pack(p.pkgTxnsLimit)
118 | if err != nil {
119 | return
120 | }
121 |
122 | hashes := txn.FromArray(txns...).Hashes()
123 | block.SetTxnsHashes(hashes)
124 |
125 | txnRoot, err := MakeTxnRoot(txns)
126 | if err != nil {
127 | return
128 | }
129 | block.SetTxnRoot(txnRoot)
130 |
131 | nonce, hash, err := spow.Run(block, p.target, p.targetBits)
132 | if err != nil {
133 | return
134 | }
135 |
136 | env.Pool.Reset()
137 |
138 | block.(*Block).SetNonce(uint64(nonce))
139 | block.SetHash(hash)
140 |
141 | block.SetPeerID(env.P2pID)
142 |
143 | env.StartBlock(hash)
144 | err = env.Base.SetTxns(block.GetHash(), txns)
145 | return
146 | }
147 |
148 | func (*Pow) EndBlock(block IBlock, env *ChainEnv, land *Land) error {
149 | chain := env.Chain
150 | pool := env.Pool
151 |
152 | err := node.ExecuteTxns(block, env, land)
153 | if err != nil {
154 | return err
155 | }
156 |
157 | err = chain.AppendBlock(block)
158 | if err != nil {
159 | return err
160 | }
161 |
162 | logrus.Infof("append block(%d)", block.GetHeight())
163 |
164 | env.SetCanRead(block.GetHash())
165 |
166 | return pool.Flush()
167 | }
168 |
169 | func (*Pow) FinalizeBlock(_ IBlock, _ *ChainEnv, _ *Land) error {
170 | return nil
171 | }
172 |
--------------------------------------------------------------------------------
/node/master/http.go:
--------------------------------------------------------------------------------
1 | package master
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/Lawliet-Chan/yu/context"
6 | . "github.com/Lawliet-Chan/yu/node"
7 | . "github.com/Lawliet-Chan/yu/txn"
8 | . "github.com/Lawliet-Chan/yu/utils/error_handle"
9 | "github.com/gin-gonic/gin"
10 | "io"
11 | "io/ioutil"
12 | "net/http"
13 | )
14 |
15 | const PARAMS_KEY = "params"
16 |
17 | func (m *Master) HandleHttp() {
18 | r := gin.Default()
19 |
20 | if m.RunMode == MasterWorker {
21 | r.POST(RegisterNodeKeepersPath, func(c *gin.Context) {
22 | m.registerNodeKeepers(c)
23 | })
24 | }
25 |
26 | // GET request
27 | r.GET(ExecApiPath, func(c *gin.Context) {
28 | m.handleHttpExec(c)
29 | })
30 | r.GET(QryApiPath, func(c *gin.Context) {
31 | m.handleHttpQry(c)
32 | })
33 |
34 | // POST request
35 | r.POST(ExecApiPath, func(c *gin.Context) {
36 | m.handleHttpExec(c)
37 | })
38 | r.POST(QryApiPath, func(c *gin.Context) {
39 | m.handleHttpQry(c)
40 | })
41 |
42 | // PUT request
43 | r.PUT(ExecApiPath, func(c *gin.Context) {
44 | m.handleHttpExec(c)
45 | })
46 | r.PUT(QryApiPath, func(c *gin.Context) {
47 | m.handleHttpQry(c)
48 | })
49 |
50 | // DELETE request
51 | r.DELETE(ExecApiPath, func(c *gin.Context) {
52 | m.handleHttpExec(c)
53 | })
54 | r.DELETE(QryApiPath, func(c *gin.Context) {
55 | m.handleHttpQry(c)
56 | })
57 |
58 | r.Run(m.httpPort)
59 | }
60 |
61 | func (m *Master) handleHttpExec(c *gin.Context) {
62 | params, err := getHttpJsonParams(c)
63 | if err != nil {
64 | c.AbortWithError(http.StatusBadRequest, err)
65 | return
66 | }
67 |
68 | _, _, stxn, err := getExecInfoFromReq(c.Request, params)
69 | if err != nil {
70 | c.AbortWithError(http.StatusBadRequest, err)
71 | return
72 | }
73 |
74 | switch m.RunMode {
75 | case MasterWorker:
76 | //ip, name, err := m.findWorkerIpAndName(tripodName, callName, ExecCall)
77 | //if err != nil {
78 | // c.String(
79 | // http.StatusBadRequest,
80 | // FindNoCallStr(tripodName, callName, err),
81 | // )
82 | // return
83 | //}
84 | //
85 | //fmap := make(map[string]*TxnsAndWorkerName)
86 | //fmap[ip] = &TxnsAndWorkerName{
87 | // Txns: FromArray(stxn),
88 | // WorkerName: name,
89 | //}
90 | //err = m.forwardTxnsForCheck(fmap)
91 | //if err != nil {
92 | // c.AbortWithError(http.StatusInternalServerError, err)
93 | // return
94 | //}
95 | //
96 | //err = m.txPool.Insert(name, stxn)
97 | //if err != nil {
98 | // c.AbortWithError(http.StatusInternalServerError, err)
99 | // return
100 | //}
101 | case LocalNode:
102 | err = m.txPool.Insert(stxn)
103 | if err != nil {
104 | c.AbortWithError(http.StatusInternalServerError, err)
105 | return
106 | }
107 | }
108 |
109 | err = m.pubUnpackedTxns(FromArray(stxn))
110 | if err != nil {
111 | c.AbortWithError(http.StatusInternalServerError, err)
112 | }
113 | }
114 |
115 | func (m *Master) handleHttpQry(c *gin.Context) {
116 | params, err := getHttpJsonParams(c)
117 | if err != nil {
118 | c.AbortWithError(http.StatusBadRequest, err)
119 | return
120 | }
121 | qcall, err := getQryInfoFromReq(c.Request, params)
122 | if err != nil {
123 | c.AbortWithError(http.StatusBadRequest, err)
124 | return
125 | }
126 |
127 | switch m.RunMode {
128 | case MasterWorker:
129 | var ip string
130 | ip, err = m.findWorkerIP(qcall.TripodName, qcall.QueryName, QryCall)
131 | if err != nil {
132 | c.String(
133 | http.StatusBadRequest,
134 | FindNoCallStr(qcall.TripodName, qcall.QueryName, err),
135 | )
136 | return
137 | }
138 | forwardQueryToWorker(ip, c.Writer, c.Request)
139 | case LocalNode:
140 | pubkey, err := GetPubkey(c.Request)
141 | if err != nil {
142 |
143 | return
144 | }
145 | ctx, err := context.NewContext(pubkey.Address(), qcall.Params)
146 | if err != nil {
147 | c.String(http.StatusBadRequest, err.Error())
148 | return
149 | }
150 |
151 | respObj, err := m.land.Query(qcall, ctx, m.GetEnv())
152 | if err != nil {
153 | c.String(
154 | http.StatusBadRequest,
155 | FindNoCallStr(qcall.TripodName, qcall.QueryName, err),
156 | )
157 | return
158 | }
159 | c.JSON(http.StatusOK, respObj)
160 | }
161 |
162 | }
163 |
164 | func readPostBody(body io.ReadCloser) (JsonString, error) {
165 | byt, err := ioutil.ReadAll(body)
166 | return JsonString(byt), err
167 | }
168 |
--------------------------------------------------------------------------------
/node/master/websocket.go:
--------------------------------------------------------------------------------
1 | package master
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | . "github.com/Lawliet-Chan/yu/common"
7 | "github.com/Lawliet-Chan/yu/context"
8 | . "github.com/Lawliet-Chan/yu/node"
9 | . "github.com/Lawliet-Chan/yu/txn"
10 | . "github.com/Lawliet-Chan/yu/utils/error_handle"
11 | "github.com/gorilla/websocket"
12 | "github.com/sirupsen/logrus"
13 | "net/http"
14 | )
15 |
16 | func (m *Master) HandleWS() {
17 | http.HandleFunc(ExecApiPath, func(w http.ResponseWriter, req *http.Request) {
18 | m.handleWS(w, req, execution)
19 | })
20 |
21 | http.HandleFunc(QryApiPath, func(w http.ResponseWriter, req *http.Request) {
22 | m.handleWS(w, req, query)
23 | })
24 |
25 | http.HandleFunc(SubResultsPath, func(w http.ResponseWriter, req *http.Request) {
26 | m.handleWS(w, req, subscription)
27 | })
28 |
29 | logrus.Panic(http.ListenAndServe(m.wsPort, nil))
30 | }
31 |
32 | const (
33 | query = iota
34 | execution
35 | subscription
36 | )
37 |
38 | func (m *Master) handleWS(w http.ResponseWriter, req *http.Request, typ int) {
39 | upgrade := websocket.Upgrader{}
40 | c, err := upgrade.Upgrade(w, req, nil)
41 | if err != nil {
42 | ServerErrorHttpResp(w, err.Error())
43 | return
44 | }
45 | if typ == subscription {
46 | logrus.Info("!!!!!!!! register a sub")
47 | m.sub.Register(c)
48 | return
49 | }
50 |
51 | _, params, err := c.ReadMessage()
52 | if err != nil {
53 | BadReqHttpResp(w, fmt.Sprintf("read websocket message from client error: %v", err))
54 | return
55 | }
56 | switch typ {
57 | case execution:
58 | m.handleWsExec(w, req, JsonString(params))
59 | case query:
60 | m.handleWsQry(c, w, req, JsonString(params))
61 | }
62 |
63 | }
64 |
65 | func (m *Master) handleWsExec(w http.ResponseWriter, req *http.Request, params JsonString) {
66 | _, _, stxn, err := getExecInfoFromReq(req, params)
67 | if err != nil {
68 | BadReqHttpResp(w, fmt.Sprintf("get Execution info from websocket error: %v", err))
69 | return
70 | }
71 |
72 | switch m.RunMode {
73 | case MasterWorker:
74 | //ip, name, err := m.findWorkerIpAndName(tripodName, callName, ExecCall)
75 | //if err != nil {
76 | // BadReqHttpResp(w, FindNoCallStr(tripodName, callName, err))
77 | // return
78 | //}
79 | //
80 | //fmap := make(map[string]*TxnsAndWorkerName)
81 | //fmap[ip] = &TxnsAndWorkerName{
82 | // Txns: FromArray(stxn),
83 | // WorkerName: name,
84 | //}
85 | //err = m.forwardTxnsForCheck(fmap)
86 | //if err != nil {
87 | // BadReqHttpResp(w, FindNoCallStr(tripodName, callName, err))
88 | // return
89 | //}
90 | //
91 | //err = m.txPool.Insert(name, stxn)
92 | //if err != nil {
93 | // ServerErrorHttpResp(w, err.Error())
94 | // return
95 | //}
96 | case LocalNode:
97 | _, _, err = m.land.GetExecLei(stxn.GetRaw().GetEcall())
98 | if err != nil {
99 | return
100 | }
101 | err = m.txPool.Insert(stxn)
102 | if err != nil {
103 | ServerErrorHttpResp(w, err.Error())
104 | return
105 | }
106 | }
107 |
108 | err = m.pubUnpackedTxns(FromArray(stxn))
109 | if err != nil {
110 | BadReqHttpResp(w, fmt.Sprintf("publish Unpacked txn(%s) error: %v", stxn.GetTxnHash().String(), err))
111 | }
112 | logrus.Info("publish unpacked txns to P2P")
113 | }
114 |
115 | func (m *Master) handleWsQry(c *websocket.Conn, w http.ResponseWriter, req *http.Request, params JsonString) {
116 | qcall, err := getQryInfoFromReq(req, params)
117 | if err != nil {
118 | BadReqHttpResp(w, fmt.Sprintf("get Query info from websocket error: %v", err))
119 | return
120 | }
121 |
122 | switch m.RunMode {
123 | case MasterWorker:
124 | ip, err := m.findWorkerIP(qcall.TripodName, qcall.QueryName, QryCall)
125 | if err != nil {
126 | BadReqHttpResp(w, FindNoCallStr(qcall.TripodName, qcall.QueryName, err))
127 | return
128 | }
129 | forwardQueryToWorker(ip, w, req)
130 | case LocalNode:
131 | ctx, err := context.NewContext(NullAddress, qcall.Params)
132 | if err != nil {
133 | BadReqHttpResp(w, fmt.Sprintf("new context error: %s", err.Error()))
134 | return
135 | }
136 |
137 | respObj, err := m.land.Query(qcall, ctx, m.GetEnv())
138 | if err != nil {
139 | ServerErrorHttpResp(w, FindNoCallStr(qcall.TripodName, qcall.QueryName, err))
140 | return
141 | }
142 | respByt, err := json.Marshal(respObj)
143 | if err != nil {
144 | ServerErrorHttpResp(w, err.Error())
145 | return
146 | }
147 | err = c.WriteMessage(websocket.BinaryMessage, respByt)
148 | if err != nil {
149 | logrus.Errorf("response Query result error: %s", err.Error())
150 | }
151 | }
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/node/master/run.go:
--------------------------------------------------------------------------------
1 | package master
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/blockchain"
5 | . "github.com/Lawliet-Chan/yu/common"
6 | . "github.com/Lawliet-Chan/yu/node"
7 | . "github.com/Lawliet-Chan/yu/tripod"
8 | ytime "github.com/Lawliet-Chan/yu/utils/time"
9 | . "github.com/Lawliet-Chan/yu/yerror"
10 | "github.com/sirupsen/logrus"
11 | )
12 |
13 | func (m *Master) Run() {
14 |
15 | switch m.RunMode {
16 | case LocalNode:
17 | for {
18 | err := m.LocalRun()
19 | if err != nil {
20 | logrus.Errorf("local-run blockchain error: %s", err.Error())
21 | }
22 | }
23 | case MasterWorker:
24 | for {
25 | err := m.MasterWokrerRun()
26 | logrus.Errorf("master-worker-run blockchain error: %s", err.Error())
27 | }
28 |
29 | default:
30 | logrus.Panic(NoRunMode)
31 | }
32 |
33 | }
34 |
35 | func (m *Master) LocalRun() error {
36 |
37 | needBcBlock := false
38 |
39 | newBlock, err := m.makeNewBasicBlock()
40 | if err != nil {
41 | return err
42 | }
43 |
44 | // start a new block
45 | err = m.land.RangeList(func(tri Tripod) error {
46 | var (
47 | need bool
48 | err error
49 | )
50 | need, err = tri.StartBlock(newBlock, m.GetEnv(), m.land)
51 | if err != nil {
52 | return err
53 | }
54 | if need {
55 | needBcBlock = true
56 | }
57 | return nil
58 | })
59 | if err != nil {
60 | return err
61 | }
62 |
63 | if needBcBlock {
64 | go func() {
65 | err := m.pubBlock(newBlock)
66 | if err != nil {
67 | logrus.Errorf("broadcast block(%s) and txns error: %s", newBlock.GetHash().String(), err.Error())
68 | }
69 | }()
70 | } else {
71 | err = m.SyncTxns(newBlock)
72 | if err != nil {
73 | return err
74 | }
75 | err = m.txPool.RemoveTxns(newBlock.GetTxnsHashes())
76 | if err != nil {
77 | return err
78 | }
79 | }
80 |
81 | // end block and append to chain
82 | err = m.land.RangeList(func(tri Tripod) error {
83 | return tri.EndBlock(newBlock, m.GetEnv(), m.land)
84 | })
85 | if err != nil {
86 | return err
87 | }
88 |
89 | // finalize this block
90 | return m.land.RangeList(func(tri Tripod) error {
91 | return tri.FinalizeBlock(newBlock, m.GetEnv(), m.land)
92 | })
93 | }
94 |
95 | func (m *Master) makeNewBasicBlock() (IBlock, error) {
96 | var newBlock IBlock = m.chain.NewEmptyBlock()
97 |
98 | newBlock.SetTimestamp(ytime.NowNanoTsU64())
99 | prevBlock, err := m.chain.GetEndBlock()
100 | if err != nil {
101 | return nil, err
102 | }
103 | newBlock.SetPreHash(prevBlock.GetHash())
104 | newBlock.SetHeight(prevBlock.GetHeight() + 1)
105 | newBlock.SetLeiLimit(m.leiLimit)
106 | return newBlock, nil
107 | }
108 |
109 | func (m *Master) MasterWokrerRun() error {
110 | //workersIps, err := m.allWorkersIP()
111 | //if err != nil {
112 | // return err
113 | //}
114 | //
115 | //newBlock := m.chain.NewDefaultBlock()
116 | //
117 | //err = m.nortifyWorker(workersIps, StartBlockPath, newBlock)
118 | //if err != nil {
119 | // return err
120 | //}
121 | //
122 | //// todo: if need broadcast block,
123 | //// m.readyBroadcastBlock(newBlock)
124 | //
125 | //err = m.SyncTxns(newBlock)
126 | //if err != nil {
127 | // return err
128 | //}
129 | //
130 | //err = m.nortifyWorker(workersIps, EndBlockPath, newBlock)
131 | //if err != nil {
132 | // return err
133 | //}
134 | //
135 | //go func() {
136 | // err := m.nortifyWorker(workersIps, ExecuteTxnsPath, newBlock)
137 | // if err != nil {
138 | // logrus.Errorf("nortify worker executing txns error: %s", err.Error())
139 | // }
140 | //}()
141 | //
142 | //return m.nortifyWorker(workersIps, FinalizeBlockPath, newBlock)
143 | return nil
144 | }
145 |
146 | func (m *Master) nortifyWorker(workersIps []string, path string, newBlock IBlock) error {
147 | blockByt, err := newBlock.Encode()
148 | if err != nil {
149 | return err
150 | }
151 |
152 | for _, ip := range workersIps {
153 | resp, err := PostRequest(ip+path, blockByt)
154 | if err != nil {
155 | return err
156 | }
157 | respBlock, err := DecodeBlockFromHttp(resp.Body, m.chain)
158 | if err != nil {
159 | return err
160 | }
161 | newBlock = respBlock
162 | }
163 | return nil
164 | }
165 |
166 | //func (m *Master) broadcastBlockAndTxns(b IBlock) error {
167 | // err := m.pubBlock(b)
168 | // if err != nil {
169 | // return err
170 | // }
171 | //
172 | // blockHash := b.GetHash()
173 | // txns, err := m.base.GetTxns(blockHash)
174 | // if err != nil {
175 | // return err
176 | // }
177 | //
178 | // if len(txns) == 0 {
179 | // return nil
180 | // }
181 | //
182 | // logrus.Warnf("=== pub block(%s) to P2P ===", blockHash.String())
183 | // for _, stxn := range txns {
184 | // logrus.Warnf("============== pub stxn(%s) to P2P ============", stxn.TxnHash.String())
185 | // }
186 | //
187 | // return m.pubPackedTxns(blockHash, txns)
188 | //}
189 |
--------------------------------------------------------------------------------
/trie/mpt/proof_test.go:
--------------------------------------------------------------------------------
1 | package mpt
2 |
3 | import (
4 | "bytes"
5 | "encoding/hex"
6 | "encoding/json"
7 | "fmt"
8 | . "github.com/Lawliet-Chan/yu/common"
9 | "github.com/Lawliet-Chan/yu/storage/kv"
10 | "io"
11 | "testing"
12 | "unicode/utf8"
13 | )
14 |
15 | func MustUnmarshal(data []byte, load interface{}) {
16 | err := json.Unmarshal(data, &load)
17 | if err != nil {
18 | panic(err)
19 | }
20 | }
21 |
22 | type MPTMerkleProof struct {
23 | RootHash []byte `json:"r"`
24 | HashChain [][]byte `json:"h"`
25 | }
26 |
27 | func validateMerklePatriciaTrie(
28 | Proof []byte,
29 | Key []byte,
30 | Value []byte,
31 | hfType uint8,
32 | ) bool {
33 | var jsonProof MPTMerkleProof
34 | MustUnmarshal(Proof, &jsonProof)
35 |
36 | var hf = Keccak256
37 | keybuf := bytes.NewReader(Key)
38 |
39 | var keyrune rune
40 | var keybyte byte
41 | // var rsize int
42 | var err error
43 | var hashChain = jsonProof.HashChain
44 | var curNode node
45 | var curHash []byte = jsonProof.RootHash
46 | // TODO: export node decoder
47 | for {
48 |
49 | if len(hashChain) == 0 {
50 | return false
51 | }
52 | if !bytes.Equal(curHash, hf(hashChain[0])) {
53 | return false
54 | }
55 |
56 | curNode, err = DecodeNode(curHash, hashChain[0])
57 | if err != nil {
58 | return false
59 | }
60 | hashChain = hashChain[1:]
61 |
62 | switch n := curNode.(type) {
63 | case *FullNode:
64 | keyrune, _, err = keybuf.ReadRune()
65 | if err == io.EOF {
66 | if len(hashChain) != 0 {
67 | return false
68 | }
69 | cld, ok := n.Children[16].(ValueNode)
70 | if !ok {
71 | return false
72 | }
73 | if !bytes.Equal(cld[:], Value) {
74 | return false
75 | }
76 | // else:
77 | goto CheckKeyValueOK
78 | } else if err != nil {
79 | return false
80 | }
81 | if keyrune == utf8.RuneError {
82 | return false
83 | }
84 | cld, ok := n.Children[int(keyrune)].(HashNode)
85 | if !ok {
86 | return false
87 | }
88 | curHash = cld[:]
89 | case *ShortNode:
90 | for idx := 0; idx < len(n.Key); idx++ {
91 | keybyte, err = keybuf.ReadByte()
92 | if err == io.EOF {
93 | if idx != len(n.Key)-1 {
94 | if Value != nil {
95 | return false
96 | } else {
97 | goto CheckKeyValueOK
98 | }
99 | } else {
100 | if len(hashChain) != 0 {
101 | return false
102 | }
103 | cld, ok := n.Val.(ValueNode)
104 | if !ok {
105 | return false
106 | }
107 | if !bytes.Equal(cld[:], Value) {
108 | return false
109 | }
110 | // else:
111 | goto CheckKeyValueOK
112 | }
113 | } else if err != nil {
114 | return false
115 | }
116 | if keybyte != n.Key[idx] {
117 | return Value == nil
118 | }
119 | }
120 | cld, ok := n.Val.(ValueNode)
121 | if !ok {
122 | return false
123 | }
124 | curHash = cld[:]
125 | }
126 | }
127 | CheckKeyValueOK:
128 |
129 | return true
130 | }
131 |
132 | func TestGenerateProof(t *testing.T) {
133 | cfg := &kv.KVconf{
134 | KVtype: "badger",
135 | Path: "./testdb",
136 | }
137 | db, err := NewNodeBase(cfg)
138 | if err != nil {
139 | t.Error(err)
140 | return
141 | }
142 | defer db.Close()
143 | var tr *Trie
144 | tr, err = NewTrie(HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), db)
145 | if err != nil {
146 | t.Error(err)
147 | return
148 | }
149 |
150 | tr.Update([]byte("key"), []byte("..."))
151 | tr.Update([]byte("keyy"), []byte("..."))
152 | tr.Update([]byte("keyyyy"), []byte("..."))
153 |
154 | var trHash Hash
155 | trHash, err = tr.Commit(nil)
156 | if err != nil {
157 | t.Error(err)
158 | return
159 | }
160 | fmt.Println(trHash)
161 | tr, err = NewTrie(trHash, db)
162 | var proof [][]byte
163 | fmt.Println("--------------------------------")
164 | proof, err = tr.TryProve([]byte("keyy"))
165 | for _, bt := range proof {
166 | fmt.Println(hex.EncodeToString(bt))
167 | }
168 | }
169 |
170 | func TestGenerateLongProof(t *testing.T) {
171 | cfg := &kv.KVconf{
172 | KVtype: "badger",
173 | Path: "./testdb",
174 | }
175 | db, err := NewNodeBase(cfg)
176 | if err != nil {
177 | t.Error(err)
178 | return
179 | }
180 | defer db.Close()
181 | var tr *Trie
182 | tr, err = NewTrie(HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"), db)
183 | if err != nil {
184 | t.Error(err)
185 | return
186 | }
187 |
188 | tr.Update([]byte("\x20\x20\x20\x20\x20\x20\x20\x20"), []byte("..."))
189 | tr.Update([]byte("\x20\x20\x20\x20"), []byte("..."))
190 | tr.Update([]byte("\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x21"), []byte("..."))
191 |
192 | var trHash Hash
193 | trHash, err = tr.Commit(nil)
194 | if err != nil {
195 | t.Error(err)
196 | return
197 | }
198 | fmt.Println(trHash)
199 | tr, err = NewTrie(trHash, db)
200 | var proof [][]byte
201 | fmt.Println("--------------------------------")
202 | proof, err = tr.TryProve([]byte("keyy"))
203 | for _, bt := range proof {
204 | fmt.Println(hex.EncodeToString(bt))
205 | }
206 | fmt.Println("--------------------------------")
207 | proof, err = tr.TryProve([]byte("\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x21"))
208 | for _, bt := range proof {
209 | fmt.Println(hex.EncodeToString(bt))
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/state/kv.go:
--------------------------------------------------------------------------------
1 | package state
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | . "github.com/Lawliet-Chan/yu/config"
6 | . "github.com/Lawliet-Chan/yu/storage/kv"
7 | . "github.com/Lawliet-Chan/yu/trie/mpt"
8 | "github.com/sirupsen/logrus"
9 | )
10 |
11 | // Merkle Patricia Trie
12 | // / | \
13 | // / | \
14 | // blockHash->stateRoot blockHash->stateRoot blockHash->stateRoot
15 | // / | \ / | \ / | \
16 | // / | \ / | \ / | \
17 | // kv kv kv kv kv kv kv kv kv
18 |
19 | type StateKV struct {
20 | // blockHash -> stateRoot
21 | indexDB KV
22 |
23 | nodeBase *NodeBase
24 |
25 | nowBlock Hash
26 | canReadBlock Hash
27 |
28 | nowStashes []*KvStash
29 | stashes []*KvStash
30 | }
31 |
32 | func NewStateKV(cfg *StateKvConf) (*StateKV, error) {
33 | indexDB, err := NewKV(&cfg.IndexDB)
34 | if err != nil {
35 | return nil, err
36 | }
37 |
38 | nodeBase, err := NewNodeBase(&cfg.NodeBase)
39 | if err != nil {
40 | return nil, err
41 | }
42 |
43 | return &StateKV{
44 | indexDB: indexDB,
45 | nodeBase: nodeBase,
46 | nowBlock: NullHash,
47 | nowStashes: make([]*KvStash, 0),
48 | stashes: make([]*KvStash, 0),
49 | }, nil
50 | }
51 |
52 | func (skv *StateKV) NextTxn() {
53 | for _, stash := range skv.nowStashes {
54 | skv.stashes = append(skv.stashes, stash)
55 | }
56 | skv.nowStashes = make([]*KvStash, 0)
57 | }
58 |
59 | func (skv *StateKV) Set(triName NameString, key, value []byte) {
60 | skv.nowStashes = append(skv.nowStashes, &KvStash{
61 | ops: SetOp,
62 | Key: makeKey(triName, key),
63 | Value: value,
64 | })
65 | }
66 |
67 | func (skv *StateKV) Delete(triName NameString, key []byte) {
68 | skv.nowStashes = append(skv.nowStashes, &KvStash{
69 | ops: DeleteOp,
70 | Key: makeKey(triName, key),
71 | Value: nil,
72 | })
73 | }
74 |
75 | func (skv *StateKV) Get(triName NameString, key []byte) ([]byte, error) {
76 | return skv.GetByBlockHash(triName, key, skv.canReadBlock)
77 | }
78 |
79 | func (skv *StateKV) Exist(triName NameString, key []byte) bool {
80 | value, _ := skv.Get(triName, key)
81 | return value != nil
82 | }
83 |
84 | func (skv *StateKV) GetByBlockHash(triName NameString, key []byte, blockHash Hash) ([]byte, error) {
85 | stateRoot, err := skv.getIndexDB(blockHash)
86 | if err != nil {
87 | return nil, err
88 | }
89 | mpt, err := NewTrie(stateRoot, skv.nodeBase)
90 | if err != nil {
91 | return nil, err
92 | }
93 | return mpt.TryGet(makeKey(triName, key))
94 | }
95 |
96 | // return StateRoot or error
97 | func (skv *StateKV) Commit() (Hash, error) {
98 | lastStateRoot, err := skv.getIndexDB(skv.canReadBlock)
99 | if err != nil {
100 | return NullHash, err
101 | }
102 | if lastStateRoot == NullHash {
103 | lastStateRoot = EmptyRoot
104 | }
105 | mpt, err := NewTrie(lastStateRoot, skv.nodeBase)
106 | if err != nil {
107 | skv.DiscardAll()
108 | return NullHash, err
109 | }
110 | for _, stash := range skv.stashes {
111 | switch stash.ops {
112 | case SetOp:
113 | err := mpt.TryUpdate(stash.Key, stash.Value)
114 | if err != nil {
115 | skv.DiscardAll()
116 | return NullHash, err
117 | }
118 | case DeleteOp:
119 | err := mpt.TryDelete(stash.Key)
120 | if err != nil {
121 | skv.DiscardAll()
122 | return NullHash, err
123 | }
124 | }
125 | }
126 |
127 | stateRoot, err := mpt.Commit(nil)
128 | if err != nil {
129 | skv.DiscardAll()
130 | return NullHash, err
131 | }
132 |
133 | err = skv.setIndexDB(skv.nowBlock, stateRoot)
134 | if err != nil {
135 | skv.DiscardAll()
136 | return NullHash, err
137 | }
138 |
139 | skv.stashes = nil
140 | return stateRoot, nil
141 | }
142 |
143 | func (skv *StateKV) Discard() {
144 | skv.nowStashes = nil
145 | }
146 |
147 | func (skv *StateKV) DiscardAll() {
148 | stateRoot, err := skv.getIndexDB(skv.canReadBlock)
149 | if err != nil {
150 | logrus.Panicf("DiscardAll: get stateRoot error: %s", err.Error())
151 | }
152 | err = skv.setIndexDB(skv.nowBlock, stateRoot)
153 | if err != nil {
154 | logrus.Panicf("DiscardAll: set stateRoot error: %s", err.Error())
155 | }
156 |
157 | skv.stashes = nil
158 | }
159 |
160 | func (skv *StateKV) StartBlock(blockHash Hash) {
161 | skv.nowBlock = blockHash
162 | }
163 |
164 | func (skv *StateKV) SetCanRead(blockHash Hash) {
165 | skv.canReadBlock = blockHash
166 | }
167 |
168 | func (skv *StateKV) setIndexDB(blockHash, stateRoot Hash) error {
169 | return skv.indexDB.Set(blockHash.Bytes(), stateRoot.Bytes())
170 | }
171 |
172 | func (skv *StateKV) getIndexDB(blockHash Hash) (Hash, error) {
173 | stateRoot, err := skv.indexDB.Get(blockHash.Bytes())
174 | if err != nil {
175 | return NullHash, err
176 | }
177 | return BytesToHash(stateRoot), nil
178 | }
179 |
180 | func makeKey(triName NameString, key []byte) []byte {
181 | tripodName := []byte(triName.Name())
182 | return append(tripodName, key...)
183 | }
184 |
185 | type Ops int
186 |
187 | const (
188 | SetOp = iota
189 | DeleteOp
190 | )
191 |
192 | type KvStash struct {
193 | ops Ops
194 | Key []byte
195 | Value []byte
196 | }
197 |
198 | type NameString interface {
199 | Name() string
200 | }
201 |
--------------------------------------------------------------------------------
/example/client/client.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "github.com/Lawliet-Chan/yu/apps/asset"
7 | . "github.com/Lawliet-Chan/yu/common"
8 | . "github.com/Lawliet-Chan/yu/keypair"
9 | . "github.com/Lawliet-Chan/yu/node"
10 | . "github.com/Lawliet-Chan/yu/result"
11 | "github.com/gorilla/websocket"
12 | "github.com/sirupsen/logrus"
13 | "net/url"
14 | "time"
15 | )
16 |
17 | func main() {
18 |
19 | pubkey, privkey, err := GenKeyPair(Sr25519)
20 | if err != nil {
21 | panic("generate key error: " + err.Error())
22 | }
23 |
24 | toPubkey, _, err := GenKeyPair(Sr25519)
25 | if err != nil {
26 | panic("generate To Address key error: " + err.Error())
27 | }
28 |
29 | go subEvent()
30 |
31 | createAccount(privkey, pubkey)
32 | time.Sleep(4 * time.Second)
33 | transfer(privkey, pubkey, toPubkey.Address())
34 | time.Sleep(4 * time.Second)
35 |
36 | transfer(privkey, pubkey, toPubkey.Address())
37 | time.Sleep(6 * time.Second)
38 |
39 | queryAccount(pubkey)
40 | queryAccount(toPubkey)
41 |
42 | select {}
43 | }
44 |
45 | type QryAccount struct {
46 | Account string `json:"account"`
47 | }
48 |
49 | func queryAccount(pubkey PubKey) {
50 | qa := &QryAccount{Account: pubkey.Address().String()}
51 | paramByt, err := json.Marshal(qa)
52 | if err != nil {
53 | panic("json encode qryAccount error: " + err.Error())
54 | }
55 | qcall := &Qcall{
56 | TripodName: "asset",
57 | QueryName: "QueryBalance",
58 | BlockHash: Hash{},
59 | Params: JsonString(paramByt),
60 | }
61 | callChainByQry(pubkey.Address(), qcall)
62 | }
63 |
64 | type CreateAccountInfo struct {
65 | Amount uint64 `json:"amount"`
66 | }
67 |
68 | func createAccount(privkey PrivKey, pubkey PubKey) {
69 | paramsByt, err := json.Marshal(CreateAccountInfo{
70 | Amount: 500,
71 | })
72 | if err != nil {
73 | panic("create-account params marshal error: " + err.Error())
74 | }
75 | ecall := &Ecall{
76 | TripodName: "asset",
77 | ExecName: "CreateAccount",
78 | Params: JsonString(paramsByt),
79 | }
80 | callChainByExec(privkey, pubkey, ecall)
81 | }
82 |
83 | type TransferInfo struct {
84 | To string `json:"to"`
85 | Amount uint64 `json:"amount"`
86 | }
87 |
88 | func transfer(privkey PrivKey, pubkey PubKey, to Address) {
89 | params := TransferInfo{
90 | To: to.String(),
91 | Amount: 100,
92 | }
93 | paramsByt, err := json.Marshal(params)
94 | if err != nil {
95 | panic("transfer params marshal error: " + err.Error())
96 | }
97 | ecall := &Ecall{
98 | TripodName: "asset",
99 | ExecName: "Transfer",
100 | Params: JsonString(paramsByt),
101 | }
102 | callChainByExec(privkey, pubkey, ecall)
103 | }
104 |
105 | func callChainByQry(addr Address, qcall *Qcall) {
106 | u := url.URL{Scheme: "ws", Host: "localhost:8999", Path: QryApiPath}
107 | q := u.Query()
108 | q.Set(TripodNameKey, qcall.TripodName)
109 | q.Set(CallNameKey, qcall.QueryName)
110 | q.Set(BlockHashKey, qcall.BlockHash.String())
111 |
112 | u.RawQuery = q.Encode()
113 |
114 | //logrus.Info("qcall: ", u.String())
115 | c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
116 | if err != nil {
117 | panic("qcall dial chain error: " + err.Error())
118 | }
119 | err = c.WriteMessage(websocket.TextMessage, []byte(qcall.Params))
120 | if err != nil {
121 | panic("write qcall message to chain error: " + err.Error())
122 | }
123 | _, resp, err := c.ReadMessage()
124 | if err != nil {
125 | fmt.Println("get qcall response error: " + err.Error())
126 | }
127 | var amount asset.Amount
128 | err = json.Unmarshal(resp, &amount)
129 | if err != nil {
130 | panic("json decode response error: " + err.Error())
131 | }
132 | logrus.Infof("get account(%s) balance(%d)", addr.String(), amount)
133 | }
134 |
135 | func callChainByExec(privkey PrivKey, pubkey PubKey, ecall *Ecall) {
136 | signByt, err := privkey.SignData(ecall.Bytes())
137 | if err != nil {
138 | panic("sign data error: " + err.Error())
139 | }
140 |
141 | u := url.URL{Scheme: "ws", Host: "localhost:8999", Path: ExecApiPath}
142 | q := u.Query()
143 | q.Set(TripodNameKey, ecall.TripodName)
144 | q.Set(CallNameKey, ecall.ExecName)
145 | q.Set(AddressKey, pubkey.Address().String())
146 | q.Set(SignatureKey, ToHex(signByt))
147 | q.Set(PubkeyKey, pubkey.StringWithType())
148 |
149 | u.RawQuery = q.Encode()
150 |
151 | // logrus.Info("ecall: ", u.String())
152 |
153 | c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
154 | if err != nil {
155 | panic("ecall dial chain error: " + err.Error())
156 | }
157 |
158 | err = c.WriteMessage(websocket.TextMessage, []byte(ecall.Params))
159 | if err != nil {
160 | panic("write ecall message to chain error: " + err.Error())
161 | }
162 | }
163 |
164 | func subEvent() {
165 | u := url.URL{Scheme: "ws", Host: "localhost:8999", Path: SubResultsPath}
166 | c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
167 | if err != nil {
168 | panic("dial chain error: " + err.Error())
169 | }
170 |
171 | for {
172 | _, msg, err := c.ReadMessage()
173 | if err != nil {
174 | panic("sub event msg from chain error: " + err.Error())
175 | }
176 | result, err := DecodeResult(msg)
177 | if err != nil {
178 | logrus.Panicf("decode result error: %s", err.Error())
179 | }
180 | switch result.Type() {
181 | case EventType:
182 | logrus.Info(result.(*Event).Sprint())
183 | case ErrorType:
184 | logrus.Error(result.(*Error).Error())
185 | }
186 | }
187 | }
188 |
--------------------------------------------------------------------------------
/network/netsync.go:
--------------------------------------------------------------------------------
1 | package network
2 |
3 | import (
4 | "bufio"
5 | . "github.com/Lawliet-Chan/yu/blockchain"
6 | . "github.com/Lawliet-Chan/yu/chain_env"
7 | . "github.com/Lawliet-Chan/yu/common"
8 | "github.com/Lawliet-Chan/yu/node"
9 | . "github.com/Lawliet-Chan/yu/tripod"
10 | . "github.com/Lawliet-Chan/yu/txn"
11 | . "github.com/Lawliet-Chan/yu/yerror"
12 | "io"
13 | )
14 |
15 | type DefaultNetSync struct {
16 | land *Land
17 | }
18 |
19 | func NewDefaultNetSync(land *Land) *DefaultNetSync {
20 | return &DefaultNetSync{land: land}
21 | }
22 |
23 | func (d *DefaultNetSync) ChooseBestNodes() {
24 | panic("implement me")
25 | }
26 |
27 | func (d *DefaultNetSync) SyncHistory(rw io.ReadWriter, env *ChainEnv) error {
28 | resp, err := d.pushFetchReq(rw, env.Chain, nil)
29 | if err != nil {
30 | return err
31 | }
32 | if resp.Err != nil {
33 | return resp.Err
34 | }
35 |
36 | for resp.MissingRange != nil {
37 | // todo: the missing range maybe very huge and we need fetch them multiple times
38 | // the remote node will return new Missing blocks-range in this response.
39 | resp, err = d.pushFetchReq(rw, env.Chain, resp.MissingRange)
40 | if err != nil {
41 | return err
42 | }
43 | if resp.Err != nil {
44 | return resp.Err
45 | }
46 |
47 | blocks, err := env.Chain.DecodeBlocks(resp.BlocksByt)
48 | if err != nil {
49 | return err
50 | }
51 |
52 | err = d.syncHistoryBlocks(env, blocks)
53 | if err != nil {
54 | return err
55 | }
56 |
57 | resp.MissingRange = nil
58 |
59 | for blockHash, byt := range resp.TxnsByt {
60 | txns, err := DecodeSignedTxns(byt)
61 | if err != nil {
62 | return err
63 | }
64 | err = env.Base.SetTxns(blockHash, txns)
65 | if err != nil {
66 | return err
67 | }
68 | }
69 | }
70 |
71 | return nil
72 | }
73 |
74 | func (d *DefaultNetSync) HandleSyncReq(rw io.ReadWriter, env *ChainEnv) error {
75 | byt, err := ReadFrom(rw)
76 | if err != nil {
77 | return err
78 | }
79 |
80 | remoteReq, err := DecodeHsRequest(byt)
81 | if err != nil {
82 | return err
83 | }
84 |
85 | var (
86 | blocksByt []byte
87 | txnsByt map[Hash][]byte
88 | )
89 | if remoteReq.FetchRange != nil {
90 | blocksByt, txnsByt, err = getMissingBlocksTxns(env, remoteReq)
91 | if err != nil {
92 | return err
93 | }
94 | }
95 |
96 | missingRange, err := compareMissingRange(env.Chain, remoteReq.Info)
97 |
98 | hsResp := &HandShakeResp{
99 | MissingRange: missingRange,
100 | BlocksByt: blocksByt,
101 | TxnsByt: txnsByt,
102 | Err: err,
103 | }
104 | byt, err = hsResp.Encode()
105 | if err != nil {
106 | return err
107 | }
108 |
109 | return WriteTo(byt, rw)
110 | }
111 |
112 | func (d *DefaultNetSync) pushFetchReq(rw io.ReadWriter, chain IBlockChain, fetchRange *BlocksRange) (*HandShakeResp, error) {
113 | hs, err := NewHsReq(chain, fetchRange)
114 | if err != nil {
115 | return nil, err
116 | }
117 |
118 | byt, err := hs.Encode()
119 | if err != nil {
120 | return nil, err
121 | }
122 | err = WriteTo(byt, rw)
123 | if err != nil {
124 | return nil, err
125 | }
126 | respByt, err := ReadFrom(rw)
127 | if err != nil {
128 | return nil, err
129 | }
130 | return DecodeHsResp(respByt)
131 | }
132 |
133 | // get the missing range of remote node
134 | func getMissingBlocksTxns(env *ChainEnv, remoteReq *HandShakeRequest) ([]byte, map[Hash][]byte, error) {
135 | fetchRange := remoteReq.FetchRange
136 | blocks, err := env.Chain.GetRangeBlocks(fetchRange.StartHeight, fetchRange.EndHeight)
137 | if err != nil {
138 | return nil, nil, err
139 | }
140 | blocksByt, err := env.Chain.EncodeBlocks(blocks)
141 | if err != nil {
142 | return nil, nil, err
143 | }
144 |
145 | txnsByt := make(map[Hash][]byte)
146 | for _, block := range blocks {
147 | blockHash := block.GetHash()
148 | txns, err := env.Base.GetTxns(blockHash)
149 | if err != nil {
150 | return nil, nil, err
151 | }
152 | byt, err := FromArray(txns...).Encode()
153 | if err != nil {
154 | return nil, nil, err
155 | }
156 | txnsByt[blockHash] = byt
157 | }
158 |
159 | return blocksByt, txnsByt, nil
160 | }
161 |
162 | func (d *DefaultNetSync) syncHistoryBlocks(env *ChainEnv, blocks []IBlock) error {
163 | switch env.RunMode {
164 | case LocalNode:
165 | for _, block := range blocks {
166 | err := d.land.RangeList(func(tri Tripod) error {
167 | if tri.ValidateBlock(block, env) {
168 | return env.Chain.AppendBlock(block)
169 | }
170 | return BlockIllegal(block.GetHash())
171 | })
172 | if err != nil {
173 | return err
174 | }
175 | }
176 |
177 | return d.executeChainTxns(env)
178 |
179 | case MasterWorker:
180 | // todo
181 | return nil
182 | default:
183 | return NoRunMode
184 | }
185 | }
186 |
187 | func (d *DefaultNetSync) executeChainTxns(env *ChainEnv) error {
188 | chain, err := env.Chain.Chain()
189 | if err != nil {
190 | return err
191 | }
192 | return chain.Range(func(block IBlock) error {
193 | return node.ExecuteTxns(block, env, d.land)
194 | })
195 | }
196 |
197 | func compareMissingRange(chain IBlockChain, remoteInfo *HandShakeInfo) (*BlocksRange, error) {
198 | localInfo, err := NewHsInfo(chain)
199 | if err != nil {
200 | return nil, err
201 | }
202 | return localInfo.Compare(remoteInfo)
203 | }
204 |
205 | func ReadFrom(r io.Reader) ([]byte, error) {
206 | return bufio.NewReader(r).ReadBytes('\n')
207 | }
208 |
209 | func WriteTo(data []byte, w io.Writer) error {
210 | data = append(data, '\n')
211 | _, err := w.Write(data)
212 | return err
213 | }
214 |
--------------------------------------------------------------------------------
/txpool/local_txpool.go:
--------------------------------------------------------------------------------
1 | package txpool
2 |
3 | import (
4 | . "github.com/Lawliet-Chan/yu/common"
5 | "github.com/Lawliet-Chan/yu/config"
6 | . "github.com/Lawliet-Chan/yu/txn"
7 | ytime "github.com/Lawliet-Chan/yu/utils/time"
8 | . "github.com/Lawliet-Chan/yu/yerror"
9 | "github.com/sirupsen/logrus"
10 | "sync"
11 | "time"
12 | )
13 |
14 | // This implementation only use for Local-Node mode.
15 | type LocalTxPool struct {
16 | sync.RWMutex
17 |
18 | poolSize uint64
19 | TxnMaxSize int
20 |
21 | txnsMap map[Hash]*SignedTxn
22 | Txns SignedTxns
23 | startPackIdx int
24 |
25 | blockTime uint64
26 | timeout time.Duration
27 |
28 | baseChecks []TxnCheck
29 | tripodChecks []TxnCheck
30 | }
31 |
32 | func NewLocalTxPool(cfg *config.TxpoolConf) *LocalTxPool {
33 | return &LocalTxPool{
34 | poolSize: cfg.PoolSize,
35 | TxnMaxSize: cfg.TxnMaxSize,
36 | txnsMap: make(map[Hash]*SignedTxn),
37 | Txns: make([]*SignedTxn, 0),
38 | startPackIdx: 0,
39 | timeout: time.Duration(cfg.Timeout),
40 | baseChecks: make([]TxnCheck, 0),
41 | tripodChecks: make([]TxnCheck, 0),
42 | }
43 | }
44 |
45 | func LocalWithDefaultChecks(cfg *config.TxpoolConf) *LocalTxPool {
46 | tp := NewLocalTxPool(cfg)
47 | return tp.withDefaultBaseChecks()
48 | }
49 |
50 | func (tp *LocalTxPool) withDefaultBaseChecks() *LocalTxPool {
51 | tp.baseChecks = []TxnCheck{
52 | tp.checkPoolLimit,
53 | tp.checkTxnSize,
54 | tp.checkSignature,
55 | }
56 | return tp
57 | }
58 |
59 | func (tp *LocalTxPool) NewEmptySignedTxn() *SignedTxn {
60 | return &SignedTxn{}
61 | }
62 |
63 | func (tp *LocalTxPool) NewEmptySignedTxns() SignedTxns {
64 | return make([]*SignedTxn, 0)
65 | }
66 |
67 | func (tp *LocalTxPool) PoolSize() uint64 {
68 | return tp.poolSize
69 | }
70 |
71 | func (tp *LocalTxPool) WithBaseChecks(checkFns []TxnCheck) ItxPool {
72 | tp.baseChecks = append(tp.baseChecks, checkFns...)
73 | return tp
74 | }
75 |
76 | func (tp *LocalTxPool) WithTripodChecks(checkFns []TxnCheck) ItxPool {
77 | tp.tripodChecks = append(tp.tripodChecks, checkFns...)
78 | return tp
79 | }
80 |
81 | // insert into txpool
82 | func (tp *LocalTxPool) Insert(stxn *SignedTxn) error {
83 | return tp.BatchInsert(FromArray(stxn))
84 | }
85 |
86 | // batch insert into txpool
87 | func (tp *LocalTxPool) BatchInsert(txns SignedTxns) (err error) {
88 | tp.Lock()
89 | defer tp.Unlock()
90 | for _, stxn := range txns {
91 | if _, ok := tp.txnsMap[stxn.TxnHash]; ok {
92 | return
93 | }
94 | err = tp.BaseCheck(stxn)
95 | if err != nil {
96 | return
97 | }
98 | err = tp.TripodsCheck(stxn)
99 | if err != nil {
100 | return
101 | }
102 |
103 | tp.Txns = append(tp.Txns, stxn)
104 | tp.txnsMap[stxn.TxnHash] = stxn
105 | }
106 | return
107 | }
108 |
109 | // package some txns to send to tripods
110 | func (tp *LocalTxPool) Pack(numLimit uint64) ([]*SignedTxn, error) {
111 | return tp.PackFor(numLimit, func(*SignedTxn) error {
112 | return nil
113 | })
114 | }
115 |
116 | func (tp *LocalTxPool) PackFor(numLimit uint64, filter func(*SignedTxn) error) ([]*SignedTxn, error) {
117 | tp.Lock()
118 | defer tp.Unlock()
119 | stxns := make([]*SignedTxn, 0)
120 | for i := 0; i < int(numLimit); i++ {
121 | if i >= len(tp.Txns) {
122 | break
123 | }
124 | logrus.Info("********************** pack txn: ", tp.Txns[i].GetTxnHash().String())
125 | err := filter(tp.Txns[i])
126 | if err != nil {
127 | return nil, err
128 | }
129 | stxns = append(stxns, tp.Txns[i])
130 | tp.startPackIdx++
131 | }
132 | return stxns, nil
133 | }
134 |
135 | func (tp *LocalTxPool) GetTxn(hash Hash) (*SignedTxn, error) {
136 | tp.RLock()
137 | defer tp.RUnlock()
138 | return tp.txnsMap[hash], nil
139 | }
140 |
141 | func (tp *LocalTxPool) RemoveTxns(hashes []Hash) error {
142 | tp.Lock()
143 | for _, hash := range hashes {
144 | var idx int
145 | idx, tp.Txns = tp.Txns.Remove(hash)
146 | if idx == -1 {
147 | continue
148 | }
149 | delete(tp.txnsMap, hash)
150 | if idx < tp.startPackIdx {
151 | tp.startPackIdx--
152 | }
153 | }
154 | tp.Unlock()
155 | return nil
156 | }
157 |
158 | // remove txns after execute all tripods
159 | func (tp *LocalTxPool) Flush() error {
160 | tp.Lock()
161 | for _, stxn := range tp.Txns[:tp.startPackIdx] {
162 | delete(tp.txnsMap, stxn.GetTxnHash())
163 | }
164 | tp.Txns = tp.Txns[tp.startPackIdx:]
165 | tp.startPackIdx = 0
166 | tp.Unlock()
167 | return nil
168 | }
169 |
170 | func (tp *LocalTxPool) Reset() {
171 | tp.blockTime = ytime.NowNanoTsU64()
172 | }
173 |
174 | // --------- check txn ------
175 |
176 | func (tp *LocalTxPool) BaseCheck(stxn *SignedTxn) error {
177 | return Check(tp.baseChecks, stxn)
178 | }
179 |
180 | func (tp *LocalTxPool) TripodsCheck(stxn *SignedTxn) error {
181 | return Check(tp.tripodChecks, stxn)
182 | }
183 |
184 | func (tp *LocalTxPool) NecessaryCheck(stxn *SignedTxn) (err error) {
185 | err = tp.checkTxnSize(stxn)
186 | if err != nil {
187 | return
188 | }
189 | err = tp.checkSignature(stxn)
190 | if err != nil {
191 | return
192 | }
193 |
194 | return tp.TripodsCheck(stxn)
195 | }
196 |
197 | func (tp *LocalTxPool) checkPoolLimit(*SignedTxn) error {
198 | return checkPoolLimit(tp.Txns, tp.poolSize)
199 | }
200 |
201 | func (tp *LocalTxPool) checkSignature(stxn *SignedTxn) error {
202 | return checkSignature(stxn)
203 | }
204 |
205 | func (tp *LocalTxPool) checkTxnSize(stxn *SignedTxn) error {
206 | if stxn.Size() > tp.TxnMaxSize {
207 | return TxnTooLarge
208 | }
209 | return checkTxnSize(tp.TxnMaxSize, stxn)
210 | }
211 |
--------------------------------------------------------------------------------
/consensus/hotstuff/safetyrules.go:
--------------------------------------------------------------------------------
1 | // Copyright Xuperchain Authors
2 | // link: https://github.com/xuperchain/xupercore
3 |
4 | package hotstuff
5 |
6 | import (
7 | "errors"
8 |
9 | cCrypto "github.com/xuperchain/xupercore/kernel/consensus/base/driver/chained-bft/crypto"
10 | "github.com/xuperchain/xupercore/lib/logs"
11 | )
12 |
13 | var (
14 | EmptyVoteSignErr = errors.New("No signature in vote.")
15 | InvalidVoteAddr = errors.New("Vote address is not a validator in the target validators.")
16 | InvalidVoteSign = errors.New("Vote sign is invalid compared with its publicKey")
17 | TooLowVoteView = errors.New("Vote received is lower than local lastVoteRound.")
18 | TooLowVParentView = errors.New("Vote's parent received is lower than local preferredRound.")
19 | TooLowProposalView = errors.New("Proposal received is lower than local lastVoteRound.")
20 | EmptyParentQC = errors.New("Parent qc is empty.")
21 | NoEnoughVotes = errors.New("Parent qc doesn't have enough votes.")
22 | EmptyParentNode = errors.New("Parent's node is empty.")
23 | EmptyValidators = errors.New("Justify validators are empty.")
24 | )
25 |
26 | type iSaftyRules interface {
27 | UpdatePreferredRound(round int64) bool
28 | VoteProposal(proposalId []byte, proposalRound int64, parentQc IQuorumCert) bool
29 | CheckVote(qc IQuorumCert, validators []string) error
30 | CalVotesThreshold(input, sum int) bool
31 | CheckProposal(proposal, parent IQuorumCert, justifyValidators []string) error
32 | CheckPacemaker(pending, local int64) bool
33 | }
34 |
35 | type DefaultSaftyRules struct {
36 | // lastVoteRound 存储着本地最近一次投票的轮数
37 | lastVoteRound int64
38 | // preferredRound 存储着本地PendingTree
39 | // 即有[两个子孙节点的节点]
40 | // 若本地有相同高度的节点,则自然排序后选出preferredRound
41 | preferredRound int64
42 | Crypto *cCrypto.CBFTCrypto
43 | QcTree *QCPendingTree
44 |
45 | Log logs.Logger
46 | }
47 |
48 | func (s *DefaultSaftyRules) UpdatePreferredRound(round int64) bool {
49 | if round-1 > s.preferredRound {
50 | s.preferredRound = round - 1
51 | }
52 | // TODO: 检查LedgerInfo是否一致
53 | return true
54 | }
55 |
56 | // VoteProposal 返回是否需要发送voteMsg给下一个Leader
57 | // DefaultSaftyRules 并没有严格比对proposalRound和parentRound的相邻自增关系
58 | // 但需要注意的是,在上层bcs的实现中,由于共识操纵了账本回滚。因此实际上safetyrules需要proposalRound和parentRound严格相邻的
59 | // 因此由于账本的可回滚性,因此lastVoteRound和preferredRound比对时,仅需比对新来的数据是否小于local数据-3即可
60 | // 此处-3代表数据已经落盘
61 | func (s *DefaultSaftyRules) VoteProposal(proposalId []byte, proposalRound int64, parentQc IQuorumCert) bool {
62 | if proposalRound < s.lastVoteRound-3 {
63 | return false
64 | }
65 | if parentQc.GetProposalView() < s.preferredRound-3 {
66 | return false
67 | }
68 | s.increaseLastVoteRound(proposalRound)
69 | return true
70 | }
71 |
72 | // CheckVote 检查logid、voteInfoHash是否正确
73 | func (s *DefaultSaftyRules) CheckVote(qc IQuorumCert, validators []string) error {
74 | // 检查签名, vote目前为单个签名,因此只需要验证第一个即可,验证的内容为签名信息是否在合法的validators里面
75 | signs := qc.GetSignsInfo()
76 | if len(signs) == 0 {
77 | return EmptyVoteSignErr
78 | }
79 | // 是否是来自有效的候选人
80 | if !isInSlice(signs[0].GetAddress(), validators) {
81 | s.Log.Error("DefaultSaftyRules::CheckVote error", "validators", validators, "from", signs[0].GetAddress())
82 | return InvalidVoteAddr
83 | }
84 | // 签名和公钥是否匹配
85 | if ok, err := s.Crypto.VerifyVoteMsgSign(signs[0], qc.GetProposalId()); !ok {
86 | return err
87 | }
88 | // 检查voteinfo信息, proposalView小于lastVoteRound,parentView不小于preferredRound
89 | if qc.GetProposalView() < s.lastVoteRound-3 {
90 | return TooLowVoteView
91 | }
92 | if qc.GetParentView() < s.preferredRound-3 {
93 | return TooLowVParentView
94 | }
95 | // TODO: 检查commit消息
96 | return nil
97 | }
98 |
99 | func (s *DefaultSaftyRules) increaseLastVoteRound(round int64) {
100 | if round > s.lastVoteRound {
101 | s.lastVoteRound = round
102 | }
103 | }
104 |
105 | func (s *DefaultSaftyRules) CalVotesThreshold(input, sum int) bool {
106 | // 计算最大恶意节点数, input+1表示去除自己的签名
107 | f := (sum - 1) / 3
108 | if f < 0 {
109 | return false
110 | }
111 | if f == 0 {
112 | return input+1 >= sum
113 | }
114 | return input+1 >= sum-f
115 | }
116 |
117 | // CheckProposalMsg 原IsQuorumCertValidate 判断justify,即需check的block的parentQC是否合法
118 | // 需要注意的是,在上层bcs的实现中,由于共识操纵了账本回滚。因此实际上safetyrules需要proposalRound和parentRound严格相邻的
119 | // 因此在此proposal和parent的QC稍微宽松检查
120 | func (s *DefaultSaftyRules) CheckProposal(proposal, parent IQuorumCert, justifyValidators []string) error {
121 | if proposal.GetProposalView() < s.lastVoteRound-3 {
122 | return TooLowProposalView
123 | }
124 | if justifyValidators == nil {
125 | return EmptyValidators
126 | }
127 | // step2: verify justify's votes
128 |
129 | // verify justify sign number
130 | if parent.GetProposalId() == nil {
131 | return EmptyParentQC
132 | }
133 |
134 | // 新qc至少要在本地qcTree挂上, 那么justify的节点需要在本地
135 | // 或者新qc目前为孤儿节点,有可能未来切换成HighQC,此时仅需要proposal在[root+1, root+6]
136 | // 是+6不是+3的原因是考虑到重起的时候的情况,重起时,root为tipId-3,而外界状态最多到tipId+3,此处简化处理
137 | if parentNode := s.QcTree.DFSQueryNode(parent.GetProposalId()); parentNode == nil {
138 | if proposal.GetProposalView() <= s.QcTree.Root.In.GetParentView() || proposal.GetProposalView() > s.QcTree.Root.In.GetProposalView()+6 {
139 | return EmptyParentNode
140 | }
141 | }
142 |
143 | // 检查justify的所有vote签名
144 | justifySigns := parent.GetSignsInfo()
145 | validCnt := 0
146 | for _, v := range justifySigns {
147 | if !isInSlice(v.GetAddress(), justifyValidators) {
148 | continue
149 | }
150 | // 签名和公钥是否匹配
151 | if ok, _ := s.Crypto.VerifyVoteMsgSign(v, parent.GetProposalId()); !ok {
152 | return InvalidVoteSign
153 | }
154 | validCnt++
155 | }
156 | if !s.CalVotesThreshold(validCnt, len(justifyValidators)) {
157 | return NoEnoughVotes
158 | }
159 | return nil
160 | }
161 |
162 | // CheckPacemaker
163 | // 注意: 由于本smr支持不同节点产生同一round, 因此下述round比较和leader比较与原文(验证Proposal的Round是否和pacemaker的Round相等)并不同。
164 | // 仅需proposal round不超过范围即可
165 | func (s *DefaultSaftyRules) CheckPacemaker(pending int64, local int64) bool {
166 | if pending <= local-3 {
167 | return false
168 | }
169 | return true
170 | }
171 |
172 | func isInSlice(target string, s []string) bool {
173 | for _, v := range s {
174 | if target == v {
175 | return true
176 | }
177 | }
178 | return false
179 | }
180 |
--------------------------------------------------------------------------------
/txpool/server_txpool.go:
--------------------------------------------------------------------------------
1 | package txpool
2 |
3 | //
4 | //import (
5 | // . "github.com/Lawliet-Chan/yu/common"
6 | // "github.com/Lawliet-Chan/yu/config"
7 | // . "github.com/Lawliet-Chan/yu/storage/kv"
8 | // . "github.com/Lawliet-Chan/yu/txn"
9 | // . "github.com/Lawliet-Chan/yu/yerror"
10 | // "github.com/sirupsen/logrus"
11 | // "sync"
12 | // "time"
13 | //)
14 | //
15 | //// This implementation only use for Master-Worker mode.
16 | //type ServerTxPool struct {
17 | // sync.RWMutex
18 | //
19 | // poolSize uint64
20 | // TxnMaxSize int
21 | // pendingTxns []*SignedTxn
22 | // Txns []*SignedTxn
23 | // packagedIdx int
24 | // db KV
25 | //
26 | // // need to sync txns from p2p
27 | // ToSyncTxnsChan chan Hash
28 | // // accept the txn-content of txn-hash from p2p
29 | // WaitSyncTxnsChan chan *SignedTxn
30 | // // wait sync txns timeout
31 | // WaitTxnsTimeout time.Duration
32 | //
33 | // baseChecks []TxnCheck
34 | // tripodChecks []TxnCheck
35 | //}
36 | //
37 | //func NewServerTxPool(cfg *config.TxpoolConf) *ServerTxPool {
38 | // db, err := NewKV(&cfg.DB)
39 | // if err != nil {
40 | // logrus.Panicf("load server txpool error: %s", err.Error())
41 | // }
42 | // WaitTxnsTimeout := time.Duration(cfg.WaitTxnsTimeout)
43 | // return &ServerTxPool{
44 | // poolSize: cfg.PoolSize,
45 | // TxnMaxSize: cfg.TxnMaxSize,
46 | // Txns: make([]*SignedTxn, 0),
47 | // packagedIdx: 0,
48 | // db: db,
49 | // ToSyncTxnsChan: make(chan Hash, 1024),
50 | // WaitSyncTxnsChan: make(chan *SignedTxn, 1024),
51 | // WaitTxnsTimeout: WaitTxnsTimeout,
52 | // baseChecks: make([]TxnCheck, 0),
53 | // tripodChecks: make([]TxnCheck, 0),
54 | // }
55 | //}
56 | //
57 | //func ServerWithDefaultChecks(cfg *config.TxpoolConf) *ServerTxPool {
58 | // tp := NewServerTxPool(cfg)
59 | // return tp.withDefaultBaseChecks()
60 | //}
61 | //
62 | //func (tp *ServerTxPool) withDefaultBaseChecks() *ServerTxPool {
63 | // tp.baseChecks = []TxnCheck{
64 | // tp.checkPoolLimit,
65 | // tp.checkTxnSize,
66 | // tp.checkSignature,
67 | // }
68 | // return tp
69 | //}
70 | //
71 | //func (tp *ServerTxPool) NewEmptySignedTxn() *SignedTxn {
72 | // return &SignedTxn{}
73 | //}
74 | //
75 | //func (tp *ServerTxPool) NewEmptySignedTxns() SignedTxns {
76 | // return make([]*SignedTxn, 0)
77 | //}
78 | //
79 | //func (tp *ServerTxPool) PoolSize() uint64 {
80 | // return tp.poolSize
81 | //}
82 | //
83 | //func (tp *ServerTxPool) WithBaseChecks(checkFns []TxnCheck) ItxPool {
84 | // tp.baseChecks = append(tp.baseChecks, checkFns...)
85 | // return tp
86 | //}
87 | //
88 | //func (tp *ServerTxPool) WithTripodChecks(checkFns []TxnCheck) ItxPool {
89 | // tp.tripodChecks = append(tp.tripodChecks, checkFns...)
90 | // return tp
91 | //}
92 | //
93 | //// insert into txpool
94 | //func (tp *ServerTxPool) Insert(workerName string, stxn *SignedTxn) (err error) {
95 | // tp.pendingTxns = append(tp.pendingTxns, stxn)
96 | // return
97 | //}
98 | //
99 | //// batch insert into txpool
100 | //func (tp *ServerTxPool) BatchInsert(workerName string, txns SignedTxns) error {
101 | // for _, txn := range txns {
102 | // err := tp.Insert(workerName, txn)
103 | // if err != nil {
104 | // return err
105 | // }
106 | // }
107 | // return nil
108 | //}
109 | //
110 | //// package some txns to send to tripods
111 | //func (tp *ServerTxPool) Package(workerName string, numLimit uint64) ([]*SignedTxn, error) {
112 | // return tp.PackageFor(workerName, numLimit, func(*SignedTxn) error {
113 | // return nil
114 | // })
115 | //}
116 | //
117 | //func (tp *ServerTxPool) PackageFor(workerName string, numLimit uint64, filter func(*SignedTxn) error) ([]*SignedTxn, error) {
118 | // tp.Lock()
119 | // defer tp.Unlock()
120 | // stxns := make([]*SignedTxn, 0)
121 | // for i := 0; i < int(numLimit); i++ {
122 | // err := filter(tp.Txns[i])
123 | // if err != nil {
124 | // return nil, err
125 | // }
126 | // stxns = append(stxns, tp.Txns[i])
127 | // tp.packagedIdx++
128 | // }
129 | // return stxns, nil
130 | //}
131 | //
132 | //// get txn content of txn-hash from p2p network
133 | ////func (tp *ServerTxPool) SyncTxns(hashes []Hash) error {
134 | ////
135 | //// hashesMap := make(map[Hash]bool)
136 | //// tp.RLock()
137 | //// for _, txnHash := range hashes {
138 | //// if !existTxn(txnHash, tp.Txns) {
139 | //// tp.ToSyncTxnsChan <- txnHash
140 | //// hashesMap[txnHash] = true
141 | //// }
142 | //// }
143 | //// tp.RUnlock()
144 | ////
145 | //// ticker := time.NewTicker(tp.WaitTxnsTimeout)
146 | ////
147 | //// for len(hashesMap) > 0 {
148 | //// select {
149 | //// case stxn := <-tp.WaitSyncTxnsChan:
150 | //// txnHash := stxn.GetRaw().ID()
151 | //// delete(hashesMap, txnHash)
152 | //// err := tp.Insert(workerName, stxn)
153 | //// if err != nil {
154 | //// return err
155 | //// }
156 | //// case <-ticker.C:
157 | //// return WaitTxnsTimeout(hashesMap)
158 | //// }
159 | //// }
160 | ////
161 | //// return nil
162 | ////}
163 | //
164 | //// remove txns after execute all tripods
165 | //func (tp *ServerTxPool) Flush() error {
166 | // tp.Lock()
167 | // tp.Txns = tp.Txns[tp.packagedIdx:]
168 | // tp.packagedIdx = 0
169 | // tp.Unlock()
170 | // return nil
171 | //}
172 | //
173 | //// --------- check txn ------
174 | //
175 | //func (tp *ServerTxPool) BaseCheck(stxn *SignedTxn) error {
176 | // return Check(tp.baseChecks, stxn)
177 | //}
178 | //
179 | //func (tp *ServerTxPool) TripodsCheck(stxn *SignedTxn) error {
180 | // return Check(tp.tripodChecks, stxn)
181 | //}
182 | //
183 | //func (tp *ServerTxPool) NecessaryCheck(stxn *SignedTxn) (err error) {
184 | // err = tp.checkTxnSize(stxn)
185 | // if err != nil {
186 | // return
187 | // }
188 | // err = tp.checkSignature(stxn)
189 | // if err != nil {
190 | // return
191 | // }
192 | //
193 | // return tp.TripodsCheck(stxn)
194 | //}
195 | //
196 | //func (tp *ServerTxPool) checkPoolLimit(*SignedTxn) error {
197 | // return checkPoolLimit(tp.Txns, tp.poolSize)
198 | //}
199 | //
200 | //func (tp *ServerTxPool) checkSignature(stxn *SignedTxn) error {
201 | // return checkSignature(stxn)
202 | //}
203 | //
204 | //func (tp *ServerTxPool) checkTxnSize(stxn *SignedTxn) error {
205 | // if stxn.Size() > tp.TxnMaxSize {
206 | // return TxnTooLarge
207 | // }
208 | // return checkTxnSize(tp.TxnMaxSize, stxn)
209 | //}
210 | //
211 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 禹
2 |
3 | Yu is a highly customizable blockchain framework.
4 |
5 | [中文文档](https://lawliet-chan.github.io/yu.github.io/zh/)
6 |
7 | ### Overall Structure
8 | 
9 |
10 | ## Introduction
11 | By using Yu, you can customize three levels to develop your own blockchain. The `Tripod` is for developers to
12 | customize their own bussiness.
13 | First level is define `Exection` and `Query` on chain.
14 | Second level is define `blockchain lifecycle`. ( including customizable Consensus Algorithm )
15 | Third level is define `basic components`, such as `block data structures`, `blockchain`, `blockbase`, `txpool`.
16 | - Define your `Exection` and `Query` on chain.
17 | `Execution` is like `Transaction` in Ethereum but not only for transfer of Token, it changes the state on the chain and must be consensus on all nodes.
18 | `Query` is like `query` in Ethereum, it doesn't change state, just query some data from the chain.
19 |
20 | ```go
21 | type (
22 | Execution func(*context.Context, currentBlock IBlock, *chain_env.ChainEnv) error
23 |
24 | Query func(ctx *context.Context, env *chain_env.ChainEnv, blockHash common.Hash) (respObj interface{}, err error)
25 | )
26 | ```
27 | - Define Your `blockchain lifecycle`, this function is in `Tripod` interface.
28 | `CheckTxn` defines the rules for checking transactions(Executions) before inserting txpool.
29 | `VerifyBlock` defines the rules for verifying blocks.
30 | `InitChain` defines bussiness when the blockchain starts up. You should use it to define `Genesis Block`.
31 | `StartBlock` defines bussiness when a new block starts. In this func, you can set some attributes( including pack txns from txpool, mining ) in the block,
32 | then you should tell the framework whether broadcast the block to other nodes or not.
33 | `EndBlock` defines bussiness when all nodes accept the new block, usually we execute the txns of new block and append block into the chain.
34 | `FinalizeBlock` defines bussiness when the block is finalized in the chain by all nodes.
35 |
36 | ```go
37 | type Tripod interface {
38 |
39 | ......
40 |
41 | CheckTxn(*txn.SignedTxn)
42 |
43 | VerifyBlock(block IBlock, env *ChainEnv) bool
44 |
45 | InitChain(env *ChainEnv, land *Land) error
46 |
47 | StartBlock(block IBlock, env *ChainEnv, land *Land) (needBroadcast bool, err error)
48 |
49 | EndBlock(block IBlock, env *ChainEnv, land *Land) error
50 |
51 | FinalizeBlock(block IBlock, env *ChainEnv, land *Land) error
52 | }
53 | ```
54 |
55 | #### Examples
56 |
57 | [Asset Tripod](https://github.com/Lawliet-Chan/yu/blob/master/apps/asset)
58 | `Asset Tripod` imitates an Asset function, it has `transfer accounts`, `create accounts`.
59 | `QueryBalance` queries someone's account balance. It implements type func `Query`.
60 | ```go
61 | func (a *Asset) QueryBalance(ctx *context.Context, env *ChainEnv, _ Hash) (interface{}, error) {
62 | account := ctx.GetAddress("account")
63 | amount := a.getBalance(env, account)
64 | return amount, nil
65 | }
66 | ```
67 | `CreateAccount` creates an account. It implements type func `Execution`.
68 | `EmitEvent` will emit an event out of the chain.
69 | The error returned will emit out of the chain.
70 | ```go
71 | func (a *Asset) CreateAccount(ctx *context.Context, _ IBlock, env *ChainEnv) error {
72 | addr := ctx.Caller
73 | amount := ctx.GetUint64("amount")
74 |
75 | if a.exsitAccount(env, addr) {
76 | _ = ctx.EmitEvent("Account Exists!")
77 | return nil
78 | }
79 |
80 | a.setBalance(env, addr, Amount(amount))
81 | _ = ctx.EmitEvent("Account Created Success!")
82 | return nil
83 | }
84 | ```
85 |
86 | We need use `SetExec` and `SetQueries` to set `Execution` and `Query` into `Asset Tripod`.
87 | When we set a `Execution`, we need declare how much `Lei`(耜) it consumes. (`Lei` is the same as `gas` in `ethereum` )
88 | ```go
89 | func NewAsset(tokenName string) *Asset {
90 | df := NewDefaultTripod("asset")
91 |
92 | a := &Asset{df, tokenName}
93 | a.SetExec(a.Transfer, 100).SetExec(a.CreateAccount, 10)
94 | a.SetQueries(a.QueryBalance)
95 |
96 | return a
97 | }
98 | ```
99 | Finally set `Asset Tripod` into `land` in `main func`.
100 | ```go
101 | func main() {
102 | startup.StartUp(pow.NewPow(1024), asset.NewAsset("YuCoin"))
103 | }
104 | ```
105 |
106 | [Pow Tripod](https://github.com/Lawliet-Chan/yu/blob/master/apps/pow/pow.go)
107 | `Pow Tripod` imitates a Consensus algorithm for proof of work. It customizes the lower-level code.
108 | - Start a new block
109 | If there are no verified blocks from P2P network, we pack some txns, mine a new block and broadcast it to P2P network.
110 | ```go
111 | func (p *Pow) StartBlock(block IBlock, env *ChainEnv, _ *Land) (needBroadcast bool, err error) {
112 | ......
113 |
114 | // get validated blocks from P2P network, if exists, use it and return.
115 | pbsht, err := chain.TakeP2pBlocks(height)
116 | if err != nil {
117 | logrus.Errorf("get p2p-blocks error: %s", err.Error())
118 | }
119 | if len(pbsht) > 0 {
120 | block.CopyFrom(pbsht[0])
121 | logrus.Infof("USE P2P block(%s)", block.GetHash().String())
122 | env.StartBlock(block.GetHash())
123 | return
124 | }
125 |
126 | // if there are no validated blocks from P2P network, we need to mine a block.
127 | needBroadcast = true
128 |
129 | ......
130 |
131 | // pack transactions(Executions) from txpool
132 | txns, err := pool.Pack(p.pkgTxnsLimit)
133 | if err != nil {
134 | return
135 | }
136 |
137 | ......
138 |
139 | // mine a hash for the new block
140 | nonce, hash, err := spow.Run(block, p.target, p.targetBits)
141 | if err != nil {
142 | return
143 | }
144 |
145 | block.(*Block).SetNonce(uint64(nonce))
146 | block.SetHash(hash)
147 |
148 | ......
149 |
150 | return
151 | }
152 | ```
153 | - End the block
154 | We execute the txns of the block and append the block into the chain.
155 | ```go
156 | func (*Pow) EndBlock(block IBlock, env *ChainEnv, land *Land) error {
157 | ......
158 |
159 | // execute the txns of the block
160 | err := node.ExecuteTxns(block, env, land)
161 | if err != nil {
162 | return err
163 | }
164 |
165 | // append the block into chain
166 | err = chain.AppendBlock(block)
167 | if err != nil {
168 | return err
169 | }
170 |
171 | ......
172 | // flush the txpool to prepare for new txns of the next block
173 | return pool.Flush()
174 | }
175 | ```
176 |
177 | - Finalize the block
178 | poW does not need `finalize` stage, so the `FinalizeBlock` has no implements.
179 |
180 |
181 | Same as `Asset Tripod` , finally set `Pow Tripod` into `land` in `main function`.
182 | ```go
183 | func main() {
184 | startup.StartUp(pow.NewPow(1024), asset.NewAsset("YuCoin"))
185 | }
186 | ```
187 |
188 |
--------------------------------------------------------------------------------
/trie/mpt/hasher.go:
--------------------------------------------------------------------------------
1 | // Copyright 2016 The go-ethereum Authors
2 | // This file is part of the go-ethereum library.
3 | //
4 | // The go-ethereum library is free software: you can redistribute it and/or modify
5 | // it under the terms of the GNU Lesser General Public License as published by
6 | // the Free Software Foundation, either version 3 of the License, or
7 | // (at your option) any later version.
8 | //
9 | // The go-ethereum library is distributed in the hope that it will be useful,
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | // GNU Lesser General Public License for more details.
13 | //
14 | // You should have received a copy of the GNU Lesser General Public License
15 | // along with the go-ethereum library. If not, see .
16 |
17 | package mpt
18 |
19 | import (
20 | "github.com/HyperService-Consortium/go-rlp"
21 | . "github.com/Lawliet-Chan/yu/common"
22 | "golang.org/x/crypto/sha3"
23 | "hash"
24 | "sync"
25 | )
26 |
27 | type hasher struct {
28 | tmp sliceBuffer
29 | sha keccakState
30 | onleaf LeafCallback
31 | }
32 |
33 | // keccakState wraps sha3.state. In addition to the usual hash methods, it also supports
34 | // Read to get a variable amount of data from the hash state. Read is faster than Sum
35 | // because it doesn't copy the internal state, but also modifies the internal state.
36 | type keccakState interface {
37 | hash.Hash
38 | Read([]byte) (int, error)
39 | }
40 |
41 | type sliceBuffer []byte
42 |
43 | func (b *sliceBuffer) Write(data []byte) (n int, err error) {
44 | *b = append(*b, data...)
45 | return len(data), nil
46 | }
47 |
48 | func (b *sliceBuffer) Reset() {
49 | *b = (*b)[:0]
50 | }
51 |
52 | // hashers live in a global db.
53 | var hasherPool = sync.Pool{
54 | New: func() interface{} {
55 | return &hasher{
56 | tmp: make(sliceBuffer, 0, 550), // cap is as large as a full FullNode.
57 | sha: sha3.NewLegacyKeccak256().(keccakState),
58 | }
59 | },
60 | }
61 |
62 | func newHasher(onleaf LeafCallback) *hasher {
63 | h := hasherPool.Get().(*hasher)
64 | h.onleaf = onleaf
65 | return h
66 | }
67 |
68 | func returnHasherToPool(h *hasher) {
69 | hasherPool.Put(h)
70 | }
71 |
72 | // hash collapses a node down into a hash node, also returning a copy of the
73 | // original node initialized with the computed hash to replace the original one.
74 | func (h *hasher) hash(n node, db *NodeBase, force bool) (node, node, error) {
75 | // If we're not storing the node, just hashing, use available cached data
76 | if hash, dirty := n.cache(); hash != nil {
77 | if db == nil {
78 | return hash, n, nil
79 | }
80 | if !dirty {
81 | switch n.(type) {
82 | case *FullNode, *ShortNode:
83 | return hash, hash, nil
84 | default:
85 | return hash, n, nil
86 | }
87 | }
88 | }
89 | // Trie not processed yet or needs storage, walk the children
90 | collapsed, cached, err := h.hashChildren(n, db)
91 | if err != nil {
92 | return HashNode{}, n, err
93 | }
94 | hashed, err := h.store(collapsed, db, force)
95 | if err != nil {
96 | return HashNode{}, n, err
97 | }
98 | // Cache the hash of the node for later reuse and remove
99 | // the dirty flag in commit mode. It's fine to assign these values directly
100 | // without copying the node first because hashChildren copies it.
101 | cachedHash, _ := hashed.(HashNode)
102 | switch cn := cached.(type) {
103 | case *ShortNode:
104 | cn.flags.hash = cachedHash
105 | if db != nil {
106 | cn.flags.dirty = false
107 | }
108 | case *FullNode:
109 | cn.flags.hash = cachedHash
110 | if db != nil {
111 | cn.flags.dirty = false
112 | }
113 | }
114 | return hashed, cached, nil
115 | }
116 |
117 | // hashChildren replaces the children of a node with their hashes if the encoded
118 | // size of the child is larger than a hash, returning the collapsed node as well
119 | // as a replacement for the original node with the child hashes cached in.
120 | func (h *hasher) hashChildren(original node, db *NodeBase) (node, node, error) {
121 | var err error
122 |
123 | switch n := original.(type) {
124 | case *ShortNode:
125 | // Hash the short node's child, caching the newly hashed subtree
126 | collapsed, cached := n.copy(), n.copy()
127 | collapsed.Key = hexToCompact(n.Key)
128 | cached.Key = CopyBytes(n.Key)
129 |
130 | if _, ok := n.Val.(ValueNode); !ok {
131 | collapsed.Val, cached.Val, err = h.hash(n.Val, db, false)
132 | if err != nil {
133 | return original, original, err
134 | }
135 | }
136 | return collapsed, cached, nil
137 |
138 | case *FullNode:
139 | // Hash the full node's children, caching the newly hashed subtrees
140 | collapsed, cached := n.copy(), n.copy()
141 |
142 | for i := 0; i < 16; i++ {
143 | if n.Children[i] != nil {
144 | collapsed.Children[i], cached.Children[i], err = h.hash(n.Children[i], db, false)
145 | if err != nil {
146 | return original, original, err
147 | }
148 | }
149 | }
150 | cached.Children[16] = n.Children[16]
151 | return collapsed, cached, nil
152 |
153 | default:
154 | // Value and hash nodes don't have children so they're left as were
155 | return n, original, nil
156 | }
157 | }
158 |
159 | // store hashes the node n and if we have a storage layer specified, it writes
160 | // the key/value pair to it and tracks any node->child references as well as any
161 | // node->external trie references.
162 | func (h *hasher) store(n node, db *NodeBase, force bool) (node, error) {
163 | // Don't store hashes or empty nodes.
164 | if _, isHash := n.(HashNode); n == nil || isHash {
165 | return n, nil
166 | }
167 | // Generate the RLP encoding of the node
168 | h.tmp.Reset()
169 | if err := rlp.Encode(&h.tmp, n); err != nil {
170 | panic("encode error: " + err.Error())
171 | }
172 | if len(h.tmp) < 32 && !force {
173 | return n, nil // Nodes smaller than 32 bytes are stored inside their parent
174 | }
175 | // Larger nodes are replaced by their hash and stored in the database.
176 | hash, _ := n.cache()
177 | if hash == nil {
178 | hash = h.makeHashNode(h.tmp)
179 | }
180 |
181 | if db != nil {
182 | // We are pooling the trie nodes into an intermediate memory cache
183 | hash := BytesToHash(hash)
184 |
185 | db.lock.Lock()
186 | db.insert(hash, h.tmp)
187 | db.lock.Unlock()
188 |
189 | // Track external references from account->storage trie
190 | if h.onleaf != nil {
191 | switch n := n.(type) {
192 | case *ShortNode:
193 | if child, ok := n.Val.(ValueNode); ok {
194 | h.onleaf(child, hash)
195 | }
196 | case *FullNode:
197 | for i := 0; i < 16; i++ {
198 | if child, ok := n.Children[i].(ValueNode); ok {
199 | h.onleaf(child, hash)
200 | }
201 | }
202 | }
203 | }
204 | }
205 | return hash, nil
206 | }
207 |
208 | func (h *hasher) makeHashNode(data []byte) HashNode {
209 | n := make(HashNode, h.sha.Size())
210 | h.sha.Reset()
211 | h.sha.Write(data)
212 | h.sha.Read(n)
213 | return n
214 | }
215 |
--------------------------------------------------------------------------------