From f1d85fc772826393fd6e770705e87c6bb9ccf119 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Mon, 3 Dec 2018 16:59:31 +0100 Subject: [PATCH 01/13] fix crash when checking account --- REST/EPAccount.go | 18 ++++-------------- client/account.go | 4 ++-- network/requests.go | 6 +++--- network/responses.go | 5 ++++- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/REST/EPAccount.go b/REST/EPAccount.go index 27080b4..49acb8f 100644 --- a/REST/EPAccount.go +++ b/REST/EPAccount.go @@ -1,9 +1,8 @@ package REST import ( + "fmt" "github.com/bazo-blockchain/bazo-client/client" - "github.com/bazo-blockchain/bazo-client/network" - "github.com/bazo-blockchain/bazo-miner/protocol" "github.com/gorilla/mux" "math/big" "net/http" @@ -16,22 +15,13 @@ func GetAccountEndpoint(w http.ResponseWriter, req *http.Request) { logger.Printf("Incoming acc request for id: %v", param) var address [64]byte - var addressHash [32]byte pubKeyInt, _ := new(big.Int).SetString(param, 16) - if len(param) == 64 { - copy(addressHash[:], pubKeyInt.Bytes()) - - network.AccReq(false, addressHash) - - accI, _ := network.Fetch(network.AccChan) - acc := accI.(*protocol.Account) - - address = acc.Address - } else if len(param) == 128 { + if len(param) == 128 { copy(address[:], pubKeyInt.Bytes()) - addressHash = protocol.SerializeHashContent(address) + } else { + logger.Fatal(fmt.Sprintf("provided invalid address %x\n", param)) } acc, lastTenTx, err := client.GetAccount(address) diff --git a/client/account.go b/client/account.go index b4e21f8..584b69f 100644 --- a/client/account.go +++ b/client/account.go @@ -31,13 +31,13 @@ func GetAccount(address [64]byte) (*Account, []*FundsTxJson, error) { //Set default params activeParameters = miner.NewDefaultParameters() - network.AccReq(false, protocol.SerializeHashContent(account.Address)) + network.AccReq(false, account.Address) if accI, _ := network.Fetch(network.AccChan); accI != nil { if acc := accI.(*protocol.Account); acc != nil { account.IsCreated = true account.IsStaking = acc.IsStaking - network.AccReq(true, protocol.SerializeHashContent(account.Address)) + network.AccReq(true, account.Address) if rootAccI, _ := network.Fetch(network.AccChan); rootAccI != nil { if rootAcc := rootAccI.(*protocol.Account); rootAcc != nil { account.IsRoot = true diff --git a/network/requests.go b/network/requests.go index f848118..706b700 100644 --- a/network/requests.go +++ b/network/requests.go @@ -43,7 +43,7 @@ func TxReq(txType uint8, txHash [32]byte) error { return nil } -func AccReq(root bool, addressHash [32]byte) error { +func AccReq(root bool, address [64]byte) error { p := peers.getRandomPeer() if p == nil { return errors.New("Couldn't get a connection, request not transmitted.") @@ -51,9 +51,9 @@ func AccReq(root bool, addressHash [32]byte) error { var packet []byte if root { - packet = p2p.BuildPacket(p2p.ROOTACC_REQ, addressHash[:]) + packet = p2p.BuildPacket(p2p.ROOTACC_REQ, address[:]) } else { - packet = p2p.BuildPacket(p2p.ACC_REQ, addressHash[:]) + packet = p2p.BuildPacket(p2p.ACC_REQ, address[:]) } sendData(p, packet) diff --git a/network/responses.go b/network/responses.go index dd11ba1..b3d46c6 100644 --- a/network/responses.go +++ b/network/responses.go @@ -64,7 +64,10 @@ func txRes(p *peer, payload []byte, txType uint8) { func accRes(p *peer, payload []byte) { var acc *protocol.Account - acc = acc.Decode(payload) + acc, err := acc.Decode(payload) + if err != nil { + logger.Fatal(err) + } AccChan <- acc } From 6d241b6ab3c5f0211f0e40a38dc31bbb055bc127 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Mon, 3 Dec 2018 17:02:19 +0100 Subject: [PATCH 02/13] refactor client storage --- cli/rest.go | 6 +++++- client/state.go | 48 +++++++++++++++++++++++++++++++-------------- client/util.go | 10 ++++++++-- cstorage/delete.go | 10 ++++------ cstorage/read.go | 32 ++++++++++++++++++------------ cstorage/storage.go | 42 +++++++++++++++++---------------------- cstorage/write.go | 34 ++++++++++++-------------------- 7 files changed, 101 insertions(+), 81 deletions(-) diff --git a/cli/rest.go b/cli/rest.go index 612cc12..6c01107 100644 --- a/cli/rest.go +++ b/cli/rest.go @@ -11,7 +11,11 @@ func GetRestCommand() cli.Command { Name: "rest", Usage: "start the REST service", Action: func(c *cli.Context) error { - client.Sync() + err := client.Sync() + if err != nil { + return err + } + REST.Init() return nil }, diff --git a/client/state.go b/client/state.go index 05e046a..b2610ec 100644 --- a/client/state.go +++ b/client/state.go @@ -21,23 +21,36 @@ var ( ) //Update allBlockHeaders to the latest header. Start listening to broadcasted headers after. -func Sync() { - loadBlockHeaders() +func Sync() error { + err := loadBlockHeaders() + if err != nil { + return err + } + go incomingBlockHeaders() + + return nil } -func loadBlockHeaders() { - var last *protocol.Block +func loadBlockHeaders() error { //youngest = fetchBlockHeader(nil) - if last = cstorage.ReadLastBlockHeader(); last != nil { - var loaded []*protocol.Block - loaded = loadDB(last, [32]byte{}, loaded) - blockHeaders = append(blockHeaders, loaded...) + last, err := cstorage.ReadLastBlockHeader() + if err != nil || last == nil { + return err + } + + loaded, err := loadDB(last, [32]byte{}, []*protocol.Block{}) + if err != nil { + return err } + blockHeaders = append(blockHeaders, loaded...) + //The client is up to date with the network and can start listening for incoming headers. network.Uptodate = true + + return nil } func incomingBlockHeaders() { @@ -114,15 +127,17 @@ func fetchBlockHeader(blockHash []byte) (blockHeader *protocol.Block) { return blockHeader } -func loadDB(last *protocol.Block, abort [32]byte, loaded []*protocol.Block) []*protocol.Block { - var ancestor *protocol.Block - +func loadDB(last *protocol.Block, abort [32]byte, loaded []*protocol.Block) ([]*protocol.Block, error) { if last.PrevHash != abort { - if ancestor = cstorage.ReadBlockHeader(last.PrevHash); ancestor == nil { - logger.Fatal() + ancestor, err := cstorage.ReadBlockHeader(last.PrevHash) + if err != nil { + return nil, err } - loaded = loadDB(ancestor, abort, loaded) + loaded, err = loadDB(ancestor, abort, loaded) + if err != nil { + return nil, err + } } logger.Printf("Header %x with height %v loaded from DB\n", @@ -131,7 +146,7 @@ func loadDB(last *protocol.Block, abort [32]byte, loaded []*protocol.Block) []*p loaded = append(loaded, last) - return loaded + return loaded, nil } func loadNetwork(last *protocol.Block, abort [32]byte, loaded []*protocol.Block) []*protocol.Block { @@ -196,6 +211,7 @@ func getState(acc *Account, lastTenTx []*FundsTxJson) (err error) { return err } + // Check if account is sender of a transaction if fundsTx.From == acc.Address { //If Acc is no root, balance funds if !acc.IsRoot { @@ -206,12 +222,14 @@ func getState(acc *Account, lastTenTx []*FundsTxJson) (err error) { acc.TxCnt += 1 } + // Check if account is recipient of a transaction if fundsTx.To == acc.Address { acc.Balance += fundsTx.Amount put(lastTenTx, ConvertFundsTx(fundsTx, "verified")) } + // Check if account is beneficiary of a block if block.Beneficiary == acc.Address { acc.Balance += fundsTx.Fee } diff --git a/client/util.go b/client/util.go index 33a0330..e27d6a9 100644 --- a/client/util.go +++ b/client/util.go @@ -12,14 +12,20 @@ var ( logger *log.Logger ) -func Init() { +func Init() error { p2p.InitLogging() logger = util.InitLogger() util.Config = util.LoadConfiguration() network.Init() - cstorage.Init("client.db") + err := cstorage.Init("client.db") + + if err != nil { + return err + } + + return nil } func put(slice []*FundsTxJson, tx *FundsTxJson) { diff --git a/cstorage/delete.go b/cstorage/delete.go index 8f60b97..2947e62 100644 --- a/cstorage/delete.go +++ b/cstorage/delete.go @@ -2,11 +2,9 @@ package cstorage import "github.com/boltdb/bolt" -func DeleteBlockHeader(hash [32]byte) { - db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("blockheaders")) - err := b.Delete(hash[:]) - - return err +func DeleteBlockHeader(hash [32]byte) error { + return db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(BLOCKHEADERS_BUCKET)) + return b.Delete(hash[:]) }) } diff --git a/cstorage/read.go b/cstorage/read.go index 76eb28d..9422cf1 100644 --- a/cstorage/read.go +++ b/cstorage/read.go @@ -1,39 +1,47 @@ package cstorage import ( + "errors" + "fmt" "github.com/bazo-blockchain/bazo-miner/protocol" "github.com/boltdb/bolt" ) -func ReadBlockHeader(hash [32]byte) (header *protocol.Block) { - db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("blockheaders")) +func ReadBlockHeader(hash [32]byte) (header *protocol.Block, err error) { + err = db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(BLOCKHEADERS_BUCKET)) encodedHeader := b.Get(hash[:]) header = header.Decode(encodedHeader) - return nil }) + if err != nil { + return nil, err + } + if header == nil { - return nil + return nil, errors.New(fmt.Sprintf("header not found for hash %x\n", hash)) } - return header + return header, nil } -func ReadLastBlockHeader() (header *protocol.Block) { - db.View(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("lastblockheader")) +func ReadLastBlockHeader() (header *protocol.Block, err error) { + err = db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(LASTBLOCKHEADER_BUCKET)) cb := b.Cursor() _, encodedHeader := cb.First() header = header.Decode(encodedHeader) - return nil }) + if err != nil { + return nil, err + } + if header == nil { - return nil + return nil, errors.New("last block header not found") } - return header + return header, nil } diff --git a/cstorage/storage.go b/cstorage/storage.go index 93967f7..03989be 100644 --- a/cstorage/storage.go +++ b/cstorage/storage.go @@ -1,51 +1,45 @@ package cstorage import ( - "fmt" "github.com/bazo-blockchain/bazo-client/util" + "github.com/bazo-blockchain/bazo-miner/storage" "github.com/boltdb/bolt" "log" "time" ) var ( - db *bolt.DB - logger *log.Logger + db *bolt.DB + logger *log.Logger + Buckets []string ) const ( ERROR_MSG = "Initiate storage aborted: " + BLOCKHEADERS_BUCKET = "blockheaders" + LASTBLOCKHEADER_BUCKET = "lastblockheader" ) //Entry function for the storage package -func Init(dbname string) { +func Init(dbname string) (err error) { logger = util.InitLogger() - var err error + Buckets = []string { + BLOCKHEADERS_BUCKET, + LASTBLOCKHEADER_BUCKET, + } + db, err = bolt.Open(dbname, 0600, &bolt.Options{Timeout: 5 * time.Second}) if err != nil { logger.Fatal(ERROR_MSG, err) } - db.Update(func(tx *bolt.Tx) error { - _, err = tx.CreateBucket([]byte("blockheaders")) - if err != nil { - return fmt.Errorf(ERROR_MSG+"Create bucket: %s", err) - } - - return nil - }) - - db.Update(func(tx *bolt.Tx) error { - _, err = tx.CreateBucket([]byte("lastblockheader")) + for _, bucket := range Buckets { + err = storage.CreateBucket(bucket, db) if err != nil { - return fmt.Errorf(ERROR_MSG+"Create bucket: %s", err) + return err } + } - return nil - }) -} - -func TearDown() { - db.Close() -} + return nil +} \ No newline at end of file diff --git a/cstorage/write.go b/cstorage/write.go index f434f0e..444d602 100644 --- a/cstorage/write.go +++ b/cstorage/write.go @@ -6,35 +6,27 @@ import ( ) func WriteBlockHeader(header *protocol.Block) (err error) { - err = db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("blockheaders")) - err := b.Put(header.Hash[:], header.EncodeHeader()) - - return err + return db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(BLOCKHEADERS_BUCKET)) + return b.Put(header.Hash[:], header.EncodeHeader()) }) - - return err } //Before saving the last block header, delete all existing entries. func WriteLastBlockHeader(header *protocol.Block) (err error) { - db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("lastblockheader")) - b.ForEach(func(k, v []byte) error { - b.Delete(k) - - return nil + err = db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(LASTBLOCKHEADER_BUCKET)) + return b.ForEach(func(k, v []byte) error { + return b.Delete(k) }) - - return nil }) - err = db.Update(func(tx *bolt.Tx) error { - b := tx.Bucket([]byte("lastblockheader")) - err := b.Put(header.Hash[:], header.EncodeHeader()) - + if err != nil { return err - }) + } - return err + return db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(LASTBLOCKHEADER_BUCKET)) + return b.Put(header.Hash[:], header.EncodeHeader()) + }) } From f9e7c83e6b289b111f37fd4d10ba1da41a685d90 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Tue, 4 Dec 2018 09:41:05 +0100 Subject: [PATCH 03/13] WIP: store MerkleProofs locally --- client/state.go | 23 +++++++++++++++++++++++ cstorage/read.go | 33 +++++++++++++++++++++++++++++++++ cstorage/storage.go | 2 ++ cstorage/write.go | 7 +++++++ 4 files changed, 65 insertions(+) diff --git a/client/state.go b/client/state.go index b2610ec..2174f00 100644 --- a/client/state.go +++ b/client/state.go @@ -190,6 +190,8 @@ func getState(acc *Account, lastTenTx []*FundsTxJson) (err error) { relevantBlocks, err := getRelevantBlocks(relevantHeadersConfigBF) for _, block := range relevantBlocks { if block != nil { + merkleTree := protocol.BuildMerkleTree(block) + //Balance funds and collect fee for _, txHash := range block.FundsTxData { err := network.TxReq(p2p.FUNDSTX_REQ, txHash) @@ -211,6 +213,27 @@ func getState(acc *Account, lastTenTx []*FundsTxJson) (err error) { return err } + mhashes, err := merkleTree.MerkleProof(fundsTx.Hash()) + if err != nil { + return err + } + + proof := protocol.NewMerkleProof( + block.Height, + mhashes, + fundsTx.Header, + fundsTx.Amount, + fundsTx.Fee, + fundsTx.TxCnt, + fundsTx.From, + fundsTx.To, + fundsTx.Data) + + err = cstorage.WriteMerkleProof(&proof) + if err != nil { + return err + } + // Check if account is sender of a transaction if fundsTx.From == acc.Address { //If Acc is no root, balance funds diff --git a/cstorage/read.go b/cstorage/read.go index 9422cf1..0bada7d 100644 --- a/cstorage/read.go +++ b/cstorage/read.go @@ -45,3 +45,36 @@ func ReadLastBlockHeader() (header *protocol.Block, err error) { return header, nil } + +func ReadMerkleProof(hash [32]byte) (proof *protocol.MerkleProof, err error) { + err = db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(MERKLEPROOF_BUCKET)) + encdoedProof := b.Get(hash[:]) + proof = proof.Decode(encdoedProof) + return nil + }) + + if err != nil { + return nil, err + } + + return proof, nil +} + +func ReadMerkleProofs() (proofs []*protocol.MerkleProof, err error) { + err = db.View(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(MERKLEPROOF_BUCKET)) + + return b.ForEach(func(k, v []byte) error { + var proof *protocol.MerkleProof + proofs = append(proofs, proof.Decode(v)) + return nil + }) + }) + + if err != nil { + return nil, err + } + + return proofs, nil +} \ No newline at end of file diff --git a/cstorage/storage.go b/cstorage/storage.go index 03989be..14d8d8b 100644 --- a/cstorage/storage.go +++ b/cstorage/storage.go @@ -18,6 +18,7 @@ const ( ERROR_MSG = "Initiate storage aborted: " BLOCKHEADERS_BUCKET = "blockheaders" LASTBLOCKHEADER_BUCKET = "lastblockheader" + MERKLEPROOF_BUCKET = "merkleproofs" ) //Entry function for the storage package @@ -27,6 +28,7 @@ func Init(dbname string) (err error) { Buckets = []string { BLOCKHEADERS_BUCKET, LASTBLOCKHEADER_BUCKET, + MERKLEPROOF_BUCKET, } db, err = bolt.Open(dbname, 0600, &bolt.Options{Timeout: 5 * time.Second}) diff --git a/cstorage/write.go b/cstorage/write.go index 444d602..c6177cc 100644 --- a/cstorage/write.go +++ b/cstorage/write.go @@ -30,3 +30,10 @@ func WriteLastBlockHeader(header *protocol.Block) (err error) { return b.Put(header.Hash[:], header.EncodeHeader()) }) } + +func WriteMerkleProof(proof *protocol.MerkleProof) (err error) { + return db.Update(func(tx *bolt.Tx) error { + b := tx.Bucket([]byte(MERKLEPROOF_BUCKET)) + return b.Put(proof.Hash()[:], proof.Encode()) + }) +} \ No newline at end of file From e0308ba6cd7f235b8580354ecbfa944167a91e6c Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Tue, 4 Dec 2018 09:41:17 +0100 Subject: [PATCH 04/13] WIP: create SCP when sending a FundsTx --- cli/funds.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/cli/funds.go b/cli/funds.go index 7940057..2d7fded 100644 --- a/cli/funds.go +++ b/cli/funds.go @@ -5,6 +5,7 @@ import ( "errors" "fmt" "github.com/bazo-blockchain/bazo-client/client" + "github.com/bazo-blockchain/bazo-client/cstorage" "github.com/bazo-blockchain/bazo-client/network" "github.com/bazo-blockchain/bazo-client/util" "github.com/bazo-blockchain/bazo-miner/crypto" @@ -12,6 +13,7 @@ import ( "github.com/bazo-blockchain/bazo-miner/protocol" "github.com/urfave/cli" "log" + "sort" ) type fundsArgs struct { @@ -30,6 +32,10 @@ func GetFundsCommand(logger *log.Logger) cli.Command { Usage: "send funds from one account to another", Action: func(c *cli.Context) error { client.Init() + err := client.Sync() + if err != nil { + return err + } args := &fundsArgs{ header: c.Int("header"), @@ -117,6 +123,12 @@ func sendFunds(args *fundsArgs, logger *log.Logger) error { fromAddress := crypto.GetAddressFromPubKey(&fromPrivKey.PublicKey) toAddress := crypto.GetAddressFromPubKey(toPubKey) + scp := protocol.NewSCP() + proofs, err := cstorage.ReadMerkleProofs() + sort.Slice(proofs, func(i, j int) bool { + return proofs[i].Height > proofs[j].Height + }) + tx, err := protocol.ConstrFundsTx( byte(args.header), uint64(args.amount), @@ -132,6 +144,8 @@ func sendFunds(args *fundsArgs, logger *log.Logger) error { return err } + tx.Proof = &scp + if err := network.SendTx(util.Config.BootstrapIpport, tx, p2p.FUNDSTX_BRDCST); err != nil { logger.Printf("%v\n", err) return err From cb4af6249f8241ed4cb5411370a611ca1270c887 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Tue, 4 Dec 2018 10:36:21 +0100 Subject: [PATCH 05/13] fix "invalid operation proof.Hash()[:] (slice of unaddressable value)" --- cstorage/write.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cstorage/write.go b/cstorage/write.go index c6177cc..52dd6d1 100644 --- a/cstorage/write.go +++ b/cstorage/write.go @@ -34,6 +34,7 @@ func WriteLastBlockHeader(header *protocol.Block) (err error) { func WriteMerkleProof(proof *protocol.MerkleProof) (err error) { return db.Update(func(tx *bolt.Tx) error { b := tx.Bucket([]byte(MERKLEPROOF_BUCKET)) - return b.Put(proof.Hash()[:], proof.Encode()) + key := proof.Hash() + return b.Put(key[:], proof.Encode()) }) } \ No newline at end of file From cb53b0b1cf76f403713d85aa35ae452505cfcc17 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Tue, 4 Dec 2018 11:55:09 +0100 Subject: [PATCH 06/13] Partly revert 6d241b6ab3c5f0211f0e40a38dc31bbb055bc127 --- cli/account.go | 2 -- cli/funds.go | 5 +---- cli/rest.go | 6 +----- client/state.go | 47 ++++++++++++++------------------------------ client/util.go | 10 ++-------- network/responses.go | 5 +---- 6 files changed, 20 insertions(+), 55 deletions(-) diff --git a/cli/account.go b/cli/account.go index 1fbaf75..bccb8f7 100644 --- a/cli/account.go +++ b/cli/account.go @@ -62,8 +62,6 @@ func checkAccount(args *accountArgs, logger *log.Logger) error { address = crypto.GetAddressFromPubKey(&privKey.PublicKey) } - logger.Printf("My address: %x\n", address) - acc, _, err := client.CheckAccount(address) if err != nil { logger.Println(err) diff --git a/cli/funds.go b/cli/funds.go index 2d7fded..1453dd2 100644 --- a/cli/funds.go +++ b/cli/funds.go @@ -32,10 +32,7 @@ func GetFundsCommand(logger *log.Logger) cli.Command { Usage: "send funds from one account to another", Action: func(c *cli.Context) error { client.Init() - err := client.Sync() - if err != nil { - return err - } + client.Sync() args := &fundsArgs{ header: c.Int("header"), diff --git a/cli/rest.go b/cli/rest.go index 6c01107..612cc12 100644 --- a/cli/rest.go +++ b/cli/rest.go @@ -11,11 +11,7 @@ func GetRestCommand() cli.Command { Name: "rest", Usage: "start the REST service", Action: func(c *cli.Context) error { - err := client.Sync() - if err != nil { - return err - } - + client.Sync() REST.Init() return nil }, diff --git a/client/state.go b/client/state.go index 2174f00..dfe6958 100644 --- a/client/state.go +++ b/client/state.go @@ -21,36 +21,23 @@ var ( ) //Update allBlockHeaders to the latest header. Start listening to broadcasted headers after. -func Sync() error { - err := loadBlockHeaders() - if err != nil { - return err - } - +func Sync() { + loadBlockHeaders() go incomingBlockHeaders() - - return nil } -func loadBlockHeaders() error { +func loadBlockHeaders() { + var last *protocol.Block //youngest = fetchBlockHeader(nil) - last, err := cstorage.ReadLastBlockHeader() - if err != nil || last == nil { - return err - } - - loaded, err := loadDB(last, [32]byte{}, []*protocol.Block{}) - if err != nil { - return err + if last, _ = cstorage.ReadLastBlockHeader(); last != nil { + var loaded []*protocol.Block + loaded = loadDB(last, [32]byte{}, loaded) + blockHeaders = append(blockHeaders, loaded...) } - blockHeaders = append(blockHeaders, loaded...) - //The client is up to date with the network and can start listening for incoming headers. network.Uptodate = true - - return nil } func incomingBlockHeaders() { @@ -127,17 +114,15 @@ func fetchBlockHeader(blockHash []byte) (blockHeader *protocol.Block) { return blockHeader } -func loadDB(last *protocol.Block, abort [32]byte, loaded []*protocol.Block) ([]*protocol.Block, error) { +func loadDB(last *protocol.Block, abort [32]byte, loaded []*protocol.Block) []*protocol.Block { + var ancestor *protocol.Block + if last.PrevHash != abort { - ancestor, err := cstorage.ReadBlockHeader(last.PrevHash) - if err != nil { - return nil, err + if ancestor, _ = cstorage.ReadBlockHeader(last.PrevHash); ancestor == nil { + logger.Fatal() } - loaded, err = loadDB(ancestor, abort, loaded) - if err != nil { - return nil, err - } + loaded = loadDB(ancestor, abort, loaded) } logger.Printf("Header %x with height %v loaded from DB\n", @@ -146,7 +131,7 @@ func loadDB(last *protocol.Block, abort [32]byte, loaded []*protocol.Block) ([]* loaded = append(loaded, last) - return loaded, nil + return loaded } func loadNetwork(last *protocol.Block, abort [32]byte, loaded []*protocol.Block) []*protocol.Block { @@ -245,14 +230,12 @@ func getState(acc *Account, lastTenTx []*FundsTxJson) (err error) { acc.TxCnt += 1 } - // Check if account is recipient of a transaction if fundsTx.To == acc.Address { acc.Balance += fundsTx.Amount put(lastTenTx, ConvertFundsTx(fundsTx, "verified")) } - // Check if account is beneficiary of a block if block.Beneficiary == acc.Address { acc.Balance += fundsTx.Fee } diff --git a/client/util.go b/client/util.go index e27d6a9..33a0330 100644 --- a/client/util.go +++ b/client/util.go @@ -12,20 +12,14 @@ var ( logger *log.Logger ) -func Init() error { +func Init() { p2p.InitLogging() logger = util.InitLogger() util.Config = util.LoadConfiguration() network.Init() - err := cstorage.Init("client.db") - - if err != nil { - return err - } - - return nil + cstorage.Init("client.db") } func put(slice []*FundsTxJson, tx *FundsTxJson) { diff --git a/network/responses.go b/network/responses.go index b3d46c6..bc9306d 100644 --- a/network/responses.go +++ b/network/responses.go @@ -64,10 +64,7 @@ func txRes(p *peer, payload []byte, txType uint8) { func accRes(p *peer, payload []byte) { var acc *protocol.Account - acc, err := acc.Decode(payload) - if err != nil { - logger.Fatal(err) - } + acc, _ = acc.Decode(payload) AccChan <- acc } From e29601287c2287a0db748a97a452717c1e616d3f Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Tue, 4 Dec 2018 15:07:05 +0100 Subject: [PATCH 07/13] fix: init client before sync --- cli/rest.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/rest.go b/cli/rest.go index 612cc12..cc5803a 100644 --- a/cli/rest.go +++ b/cli/rest.go @@ -11,6 +11,7 @@ func GetRestCommand() cli.Command { Name: "rest", Usage: "start the REST service", Action: func(c *cli.Context) error { + client.Init() client.Sync() REST.Init() return nil From bd90f141f9797efc2479f05263e03976fd282725 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Wed, 5 Dec 2018 15:26:44 +0100 Subject: [PATCH 08/13] remove SCP data structure --- cli/funds.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/funds.go b/cli/funds.go index 1453dd2..9a55297 100644 --- a/cli/funds.go +++ b/cli/funds.go @@ -120,7 +120,6 @@ func sendFunds(args *fundsArgs, logger *log.Logger) error { fromAddress := crypto.GetAddressFromPubKey(&fromPrivKey.PublicKey) toAddress := crypto.GetAddressFromPubKey(toPubKey) - scp := protocol.NewSCP() proofs, err := cstorage.ReadMerkleProofs() sort.Slice(proofs, func(i, j int) bool { return proofs[i].Height > proofs[j].Height @@ -141,7 +140,7 @@ func sendFunds(args *fundsArgs, logger *log.Logger) error { return err } - tx.Proof = &scp + tx.Proofs = proofs if err := network.SendTx(util.Config.BootstrapIpport, tx, p2p.FUNDSTX_BRDCST); err != nil { logger.Printf("%v\n", err) From c56d8b584766f551e939d3601d60818b8c2d0f6f Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Wed, 5 Dec 2018 22:44:43 +0100 Subject: [PATCH 09/13] :100: first successful end-to-end SCP --- cli/funds.go | 1 - client/state.go | 2 ++ client/validate.go | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cli/funds.go b/cli/funds.go index 9a55297..3e6ba52 100644 --- a/cli/funds.go +++ b/cli/funds.go @@ -32,7 +32,6 @@ func GetFundsCommand(logger *log.Logger) cli.Command { Usage: "send funds from one account to another", Action: func(c *cli.Context) error { client.Init() - client.Sync() args := &fundsArgs{ header: c.Int("header"), diff --git a/client/state.go b/client/state.go index dfe6958..09e364f 100644 --- a/client/state.go +++ b/client/state.go @@ -219,6 +219,8 @@ func getState(acc *Account, lastTenTx []*FundsTxJson) (err error) { return err } + logger.Printf("Merkle proof written to client storage for tx at block height %v", block.Height) + // Check if account is sender of a transaction if fundsTx.From == acc.Address { //If Acc is no root, balance funds diff --git a/client/validate.go b/client/validate.go index 7a09f7a..457fd0c 100644 --- a/client/validate.go +++ b/client/validate.go @@ -32,9 +32,9 @@ func validateTx(block *protocol.Block, tx protocol.Transaction, txHash [32]byte) for i := 0; i < len(nodes); i += 2 { var parentHash [32]byte concatHash := append(leafHash[:], nodes[i][:]...) - if parentHash = protocol.SerializeHashContent(concatHash); parentHash != nodes[i+1] { + if parentHash = protocol.MTHash(concatHash); parentHash != nodes[i+1] { concatHash = append(nodes[i][:], leafHash[:]...) - if parentHash = protocol.SerializeHashContent(concatHash); parentHash != nodes[i+1] { + if parentHash = protocol.MTHash(concatHash); parentHash != nodes[i+1] { valid = false } } From 3a2ea233bb5bfa876808bfb663c95c5cf81cd28d Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Thu, 6 Dec 2018 13:02:42 +0100 Subject: [PATCH 10/13] WIP: refactor the getState function into multiple subfunctions --- client/state.go | 207 +++++++++++++++++++++++++++--------------------- 1 file changed, 118 insertions(+), 89 deletions(-) diff --git a/client/state.go b/client/state.go index 09e364f..153f1b2 100644 --- a/client/state.go +++ b/client/state.go @@ -174,109 +174,138 @@ func getState(acc *Account, lastTenTx []*FundsTxJson) (err error) { relevantBlocks, err := getRelevantBlocks(relevantHeadersConfigBF) for _, block := range relevantBlocks { - if block != nil { - merkleTree := protocol.BuildMerkleTree(block) - - //Balance funds and collect fee - for _, txHash := range block.FundsTxData { - err := network.TxReq(p2p.FUNDSTX_REQ, txHash) - if err != nil { - return err - } + if block == nil { + continue + } - txI, err := network.Fetch(network.FundsTxChan) - if err != nil { - return err - } + err = updateConfigParameters(block) + if err != nil { + return err + } - tx := txI.(protocol.Transaction) - fundsTx := txI.(*protocol.FundsTx) - - if fundsTx.From == acc.Address || fundsTx.To == acc.Address || block.Beneficiary == acc.Address { - //Validate tx - if err := validateTx(block, tx, txHash); err != nil { - return err - } - - mhashes, err := merkleTree.MerkleProof(fundsTx.Hash()) - if err != nil { - return err - } - - proof := protocol.NewMerkleProof( - block.Height, - mhashes, - fundsTx.Header, - fundsTx.Amount, - fundsTx.Fee, - fundsTx.TxCnt, - fundsTx.From, - fundsTx.To, - fundsTx.Data) - - err = cstorage.WriteMerkleProof(&proof) - if err != nil { - return err - } - - logger.Printf("Merkle proof written to client storage for tx at block height %v", block.Height) - - // Check if account is sender of a transaction - if fundsTx.From == acc.Address { - //If Acc is no root, balance funds - if !acc.IsRoot { - acc.Balance -= fundsTx.Amount - acc.Balance -= fundsTx.Fee - } - - acc.TxCnt += 1 - } - - if fundsTx.To == acc.Address { - acc.Balance += fundsTx.Amount - - put(lastTenTx, ConvertFundsTx(fundsTx, "verified")) - } - - if block.Beneficiary == acc.Address { - acc.Balance += fundsTx.Fee - } - } - } + // Check if bloomfilter returns false, if yes, the block has nothing related to account's address + if !block.BloomFilter.Test(acc.Address[:]) { + continue + } - //Update config parameters and collect fee - for _, txHash := range block.ConfigTxData { - err := network.TxReq(p2p.CONFIGTX_REQ, txHash) - if err != nil { - return err - } + fundsTxs, err := requestFundsTx(block) - txI, err := network.Fetch(network.ConfigTxChan) - if err != nil { - return err - } + // Check if it's a block with a Bloomfilter that returns false positive + if len(fundsTxs) == 0 && block.Beneficiary != acc.Address { + + } - tx := txI.(protocol.Transaction) - configTx := txI.(*protocol.ConfigTx) + err = balanceFunds(fundsTxs, block, acc, lastTenTx) + if err != nil { + return err + } - configTxSlice := []*protocol.ConfigTx{configTx} + if block.Beneficiary == acc.Address { + acc.Balance += block.TotalFees + // TODO @rmnblm create merkle proof for beneficiary, but how? + } + } - if block.Beneficiary == acc.Address { - //Validate tx - if err := validateTx(block, tx, txHash); err != nil { - return err - } + return nil +} - acc.Balance += configTx.Fee - } +func requestFundsTx(block *protocol.Block) (fundsTxs []*protocol.FundsTx, err error) { + for _, txHash := range block.FundsTxData { + err := network.TxReq(p2p.FUNDSTX_REQ, txHash) + if err != nil { + return nil, err + } + + txI, err := network.Fetch(network.FundsTxChan) + if err != nil { + return nil, err + } + + fundsTx := txI.(*protocol.FundsTx) + fundsTxs = append(fundsTxs, fundsTx) + } - miner.CheckAndChangeParameters(&activeParameters, &configTxSlice) + return fundsTxs, nil +} + +func balanceFunds(fundsTxs []*protocol.FundsTx, block *protocol.Block, acc *Account, lastTenTx []*FundsTxJson) error { + merkleTree := protocol.BuildMerkleTree(block) + + for _, fundsTx := range fundsTxs { + txHash := fundsTx.Hash() + + if fundsTx.From == acc.Address || fundsTx.To == acc.Address { + //Validate tx + if err := validateTx(block, fundsTx, txHash); err != nil { + return err + } + + mhashes, err := merkleTree.MerkleProof(fundsTx.Hash()) + if err != nil { + return err } - //TODO stakeTx + proof := protocol.NewMerkleProof( + block.Height, + mhashes, + fundsTx.Header, + fundsTx.Amount, + fundsTx.Fee, + fundsTx.TxCnt, + fundsTx.From, + fundsTx.To, + fundsTx.Data) + + err = cstorage.WriteMerkleProof(&proof) + if err != nil { + return err + } + + logger.Printf("Merkle proof written to client storage for tx at block height %v", block.Height) + + // Check if account is sender of a transaction + if fundsTx.From == acc.Address { + //If Acc is no root, balance funds + if !acc.IsRoot { + acc.Balance -= fundsTx.Amount + acc.Balance -= fundsTx.Fee + } + acc.TxCnt += 1 + } + if fundsTx.To == acc.Address { + acc.Balance += fundsTx.Amount + put(lastTenTx, ConvertFundsTx(fundsTx, "verified")) + } } } return nil } + +func updateConfigParameters(block *protocol.Block) error { + for _, txHash := range block.ConfigTxData { + err := network.TxReq(p2p.CONFIGTX_REQ, txHash) + if err != nil { + return err + } + + txI, err := network.Fetch(network.ConfigTxChan) + if err != nil { + return err + } + + tx := txI.(protocol.Transaction) + configTx := txI.(*protocol.ConfigTx) + + //Validate tx + if err := validateTx(block, tx, txHash); err != nil { + return err + } + + configTxSlice := []*protocol.ConfigTx{configTx} + miner.CheckAndChangeParameters(&activeParameters, &configTxSlice) + } + + return nil +} \ No newline at end of file From 27fdb3e4173231a8d946d94d3ba4f0b829771632 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Sat, 15 Dec 2018 10:03:46 +0100 Subject: [PATCH 11/13] follow-up change in bazo-miner --- cli/funds.go | 2 +- client/state.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/funds.go b/cli/funds.go index 3e6ba52..a46c940 100644 --- a/cli/funds.go +++ b/cli/funds.go @@ -124,7 +124,7 @@ func sendFunds(args *fundsArgs, logger *log.Logger) error { return proofs[i].Height > proofs[j].Height }) - tx, err := protocol.ConstrFundsTx( + tx, err := protocol.NewSignedFundsTx( byte(args.header), uint64(args.amount), uint64(args.fee), diff --git a/client/state.go b/client/state.go index 153f1b2..d6cafdd 100644 --- a/client/state.go +++ b/client/state.go @@ -229,7 +229,7 @@ func requestFundsTx(block *protocol.Block) (fundsTxs []*protocol.FundsTx, err er } func balanceFunds(fundsTxs []*protocol.FundsTx, block *protocol.Block, acc *Account, lastTenTx []*FundsTxJson) error { - merkleTree := protocol.BuildMerkleTree(block) + merkleTree := block.BuildMerkleTree() for _, fundsTx := range fundsTxs { txHash := fundsTx.Hash() From 142a0bfe7845e8c658cf963af0eab76a197f0154 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Sat, 15 Dec 2018 21:28:41 +0100 Subject: [PATCH 12/13] adjust Merkle proof to changes in bazo-miner --- client/state.go | 51 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/client/state.go b/client/state.go index d6cafdd..027b311 100644 --- a/client/state.go +++ b/client/state.go @@ -192,7 +192,7 @@ func getState(acc *Account, lastTenTx []*FundsTxJson) (err error) { // Check if it's a block with a Bloomfilter that returns false positive if len(fundsTxs) == 0 && block.Beneficiary != acc.Address { - + // TODO @rmnblm } err = balanceFunds(fundsTxs, block, acc, lastTenTx) @@ -202,7 +202,6 @@ func getState(acc *Account, lastTenTx []*FundsTxJson) (err error) { if block.Beneficiary == acc.Address { acc.Balance += block.TotalFees - // TODO @rmnblm create merkle proof for beneficiary, but how? } } @@ -229,7 +228,7 @@ func requestFundsTx(block *protocol.Block) (fundsTxs []*protocol.FundsTx, err er } func balanceFunds(fundsTxs []*protocol.FundsTx, block *protocol.Block, acc *Account, lastTenTx []*FundsTxJson) error { - merkleTree := block.BuildMerkleTree() + bucket := protocol.NewTxBucket(acc.Address) for _, fundsTx := range fundsTxs { txHash := fundsTx.Hash() @@ -240,29 +239,6 @@ func balanceFunds(fundsTxs []*protocol.FundsTx, block *protocol.Block, acc *Acco return err } - mhashes, err := merkleTree.MerkleProof(fundsTx.Hash()) - if err != nil { - return err - } - - proof := protocol.NewMerkleProof( - block.Height, - mhashes, - fundsTx.Header, - fundsTx.Amount, - fundsTx.Fee, - fundsTx.TxCnt, - fundsTx.From, - fundsTx.To, - fundsTx.Data) - - err = cstorage.WriteMerkleProof(&proof) - if err != nil { - return err - } - - logger.Printf("Merkle proof written to client storage for tx at block height %v", block.Height) - // Check if account is sender of a transaction if fundsTx.From == acc.Address { //If Acc is no root, balance funds @@ -277,9 +253,32 @@ func balanceFunds(fundsTxs []*protocol.FundsTx, block *protocol.Block, acc *Acco acc.Balance += fundsTx.Amount put(lastTenTx, ConvertFundsTx(fundsTx, "verified")) } + + bucket.AddFundsTx(fundsTx) } } + // Create the Merkle proof for this block + merkleTree := block.BuildMerkleTree() + mhashes, err := merkleTree.MerkleProof(bucket.Hash()) + if err != nil { + return err + } + + proof := protocol.NewMerkleProof( + block.Height, + mhashes, + bucket.Address, + bucket.RelativeBalance, + bucket.CalculateMerkleRoot()) + + err = cstorage.WriteMerkleProof(&proof) + if err != nil { + return err + } + + logger.Printf("Merkle proof written to client storage for tx at block height %v", block.Height) + return nil } From 56d1bcd9487b7f9a33041b28a47e1356cb226554 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Sun, 16 Dec 2018 09:22:58 +0100 Subject: [PATCH 13/13] sync client with network before sending a FundsTx --- cli/funds.go | 2 ++ client/state.go | 54 +++++++++++++++++++++++++++------------------- client/validate.go | 27 +++++++++++++++++++++++ 3 files changed, 61 insertions(+), 22 deletions(-) diff --git a/cli/funds.go b/cli/funds.go index a46c940..89ca2db 100644 --- a/cli/funds.go +++ b/cli/funds.go @@ -119,6 +119,8 @@ func sendFunds(args *fundsArgs, logger *log.Logger) error { fromAddress := crypto.GetAddressFromPubKey(&fromPrivKey.PublicKey) toAddress := crypto.GetAddressFromPubKey(toPubKey) + client.SyncBeforeTx(fromAddress) + proofs, err := cstorage.ReadMerkleProofs() sort.Slice(proofs, func(i, j int) bool { return proofs[i].Height > proofs[j].Height diff --git a/client/state.go b/client/state.go index 027b311..23b6037 100644 --- a/client/state.go +++ b/client/state.go @@ -21,9 +21,15 @@ var ( ) //Update allBlockHeaders to the latest header. Start listening to broadcasted headers after. +func SyncBeforeTx(address [64]byte) { + loadBlockHeaders() + incomingBlockHeaders(true) + GetAccount(address) +} + func Sync() { loadBlockHeaders() - go incomingBlockHeaders() + go incomingBlockHeaders(false) } func loadBlockHeaders() { @@ -40,7 +46,7 @@ func loadBlockHeaders() { network.Uptodate = true } -func incomingBlockHeaders() { +func incomingBlockHeaders(untilSynced bool) { for { blockHeaderIn := <-network.BlockHeaderIn @@ -85,6 +91,10 @@ func incomingBlockHeaders() { blockHeaders = append(blockHeaders, blockHeaderIn) cstorage.WriteLastBlockHeader(blockHeaderIn) + + if untilSynced { + return + } } } } @@ -231,36 +241,36 @@ func balanceFunds(fundsTxs []*protocol.FundsTx, block *protocol.Block, acc *Acco bucket := protocol.NewTxBucket(acc.Address) for _, fundsTx := range fundsTxs { - txHash := fundsTx.Hash() - if fundsTx.From == acc.Address || fundsTx.To == acc.Address { - //Validate tx - if err := validateTx(block, fundsTx, txHash); err != nil { - return err - } + bucket.AddFundsTx(fundsTx) + } + } - // Check if account is sender of a transaction - if fundsTx.From == acc.Address { - //If Acc is no root, balance funds - if !acc.IsRoot { - acc.Balance -= fundsTx.Amount - acc.Balance -= fundsTx.Fee - } - acc.TxCnt += 1 - } + bucketHash := bucket.Hash() + if err := validateBucket(block, bucketHash); err != nil { + return err + } - if fundsTx.To == acc.Address { - acc.Balance += fundsTx.Amount - put(lastTenTx, ConvertFundsTx(fundsTx, "verified")) + for _, fundsTx := range bucket.Transactions { + // Check if account is sender of a transaction + if fundsTx.From == acc.Address { + //If Acc is no root, balance funds + if !acc.IsRoot { + acc.Balance -= fundsTx.Amount + acc.Balance -= fundsTx.Fee } + acc.TxCnt += 1 + } - bucket.AddFundsTx(fundsTx) + if fundsTx.To == acc.Address { + acc.Balance += fundsTx.Amount + put(lastTenTx, ConvertFundsTx(fundsTx, "verified")) } } // Create the Merkle proof for this block merkleTree := block.BuildMerkleTree() - mhashes, err := merkleTree.MerkleProof(bucket.Hash()) + mhashes, err := merkleTree.MerkleProof(bucketHash) if err != nil { return err } diff --git a/client/validate.go b/client/validate.go index 457fd0c..72c190b 100644 --- a/client/validate.go +++ b/client/validate.go @@ -47,3 +47,30 @@ func validateTx(block *protocol.Block, tx protocol.Transaction, txHash [32]byte) return nil } + +func validateBucket(block *protocol.Block, bucketHash [32]byte) error { + err := network.IntermediateNodesReq(block.Hash, bucketHash) + if err != nil { + return err + } + + nodes, err := network.Fetch32Bytes(network.IntermediateNodesChan) + if err != nil { + return err + } + + leafHash := bucketHash + for i := 0; i < len(nodes); i += 2 { + var parentHash [32]byte + concatHash := append(leafHash[:], nodes[i][:]...) + if parentHash = protocol.MTHash(concatHash); parentHash != nodes[i+1] { + concatHash = append(nodes[i][:], leafHash[:]...) + if parentHash = protocol.MTHash(concatHash); parentHash != nodes[i+1] { + return errors.New(fmt.Sprintf("Bucket validation failed for %x\n", bucketHash)) + } + } + leafHash = parentHash + } + + return nil +}