├── 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 | ![image](yu_flow_chart.png) 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 | --------------------------------------------------------------------------------