From 1ec5ba1b11eb1fad38441674e1dd01f47e843f4d Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Fri, 30 Nov 2018 17:57:49 +0100 Subject: [PATCH 01/28] implement SCP datastructure with tests --- protocol/scp.go | 179 +++++++++++++++++++++++++++++++++++++++++++ protocol/scp_test.go | 128 +++++++++++++++++++++++++++++++ 2 files changed, 307 insertions(+) create mode 100644 protocol/scp.go create mode 100644 protocol/scp_test.go diff --git a/protocol/scp.go b/protocol/scp.go new file mode 100644 index 0000000..167181c --- /dev/null +++ b/protocol/scp.go @@ -0,0 +1,179 @@ +package protocol + +import ( + "bytes" + "encoding/gob" + "errors" + "fmt" + "golang.org/x/crypto/sha3" +) + +type SCP struct { + // Proof height + // Block heights of the proofs + Height []uint32 + + // Merkle hashes + // Intermediate hashes required to create a Merkle proof + MHashes [][][32]byte + + // Proof properties + // Must equal the hashed data of FundsTx.Hash() + PHeader []byte + PAmount []uint64 + PFee []uint64 + PTxCnt []uint32 + PFrom [][64]byte + PTo [][64]byte + PData [][]byte +} + +func (scp *SCP) CalculateMerkleRoot(index int) (merkleRoot [32]byte, err error) { + if err = scp.verifyProofPrerequisites(); err != nil { + return [32]byte{}, err + } + + phash := scp.getProofHash(index) + mhashes := scp.MHashes[index] + + merkleRoot = phash + for _, mhash := range mhashes { + concatHash := append(merkleRoot[:], mhash[:]...) + merkleRoot = sha3.Sum256(concatHash) + } + + return merkleRoot, nil +} + +func (scp *SCP) ProofCount() int { + return len(scp.Height) +} + +func (scp *SCP) Hash() (hash [32]byte) { + if scp == nil { + return [32]byte{} + } + + scpHash := struct { + Height []uint32 + MHashes [][][32]byte + PHashHeader []byte + PHashAmount []uint64 + PHashFee []uint64 + PHashTxCnt []uint32 + PHashFrom [][64]byte + PHashTo [][64]byte + PHashData [][]byte + }{ + scp.Height, + scp.MHashes, + scp.PHeader, + scp.PAmount, + scp.PFee, + scp.PTxCnt, + scp.PFrom, + scp.PTo, + scp.PData, + } + + return SerializeHashContent(scpHash) +} + +func (scp *SCP) Encode() (encodedTx []byte) { + encodeData := SCP{ + scp.Height, + scp.MHashes, + scp.PHeader, + scp.PAmount, + scp.PFee, + scp.PTxCnt, + scp.PFrom, + scp.PTo, + scp.PData, + } + buffer := new(bytes.Buffer) + gob.NewEncoder(buffer).Encode(encodeData) + return buffer.Bytes() +} + +func (scp *SCP) Decode(encoded []byte) *SCP { + var decoded SCP + buffer := bytes.NewBuffer(encoded) + decoder := gob.NewDecoder(buffer) + decoder.Decode(&decoded) + return &decoded +} + +func (scp *SCP) StringFor(index int) string { + var mhashesString string + for _, mhash := range scp.MHashes[index] { + mhashesString += fmt.Sprintf("%x, ", mhash[0:8]) + } + + mhashesString = mhashesString[0:len(mhashesString) - 2] + + return fmt.Sprintf("Height: %v\n" + + "MHashes: [%v]\n" + + "PHash Header: %v\n" + + "PHash Amount: %v\n"+ + "PHash Fee: %v\n"+ + "PHash TxCnt: %v\n"+ + "PHash From: %x\n"+ + "PHash To: %x\n"+ + "PHash Data: %v\n", + scp.Height[index], + mhashesString, + scp.PHeader[index], + scp.PAmount[index], + scp.PFee[index], + scp.PTxCnt[index], + scp.PFrom[index][0:8], + scp.PTo[index][0:8], + scp.PData[index], + ) +} + +func (scp *SCP) getProofHash(index int) [32]byte { + // Note that the hashed properties must equal to the hashed properties of FundsTx.Hash() + phash := struct { + Header byte + Amount uint64 + Fee uint64 + TxCnt uint32 + From [64]byte + To [64]byte + Data []byte + }{ + scp.PHeader[index], + scp.PAmount[index], + scp.PFee[index], + scp.PTxCnt[index], + scp.PFrom[index], + scp.PTo[index], + scp.PData[index], + } + + return SerializeHashContent(phash) +} + +func (scp *SCP) verifyProofPrerequisites() error { + nofProofs := scp.ProofCount() + + if nofProofs != len(scp.MHashes) { + // For every proof i, the number of Merkle hashes at i must be specified. + return errors.New("missing Merkle hashes") + } + + if nofProofs != len(scp.PHeader) || + nofProofs != len(scp.PAmount) || + nofProofs != len(scp.PFee) || + nofProofs != len(scp.PTxCnt) || + nofProofs != len(scp.PFrom) || + nofProofs != len(scp.PTo) || + nofProofs != len(scp.PData) { + // For every proof i, the properties for proof i must be specified. + return errors.New("missing proof hash values") + } + + return nil +} \ No newline at end of file diff --git a/protocol/scp_test.go b/protocol/scp_test.go new file mode 100644 index 0000000..ce4dd6d --- /dev/null +++ b/protocol/scp_test.go @@ -0,0 +1,128 @@ +package protocol + +import ( + "math/rand" + "reflect" + "testing" + "time" +) + +func TestSCPSerialization(t *testing.T) { + var merkleRootsBefore [][32]byte + nofProofs, scp := getDummySCP() + + for i := 0; i < nofProofs; i++ { + merkleRoot, err := scp.CalculateMerkleRoot(i) + if err != nil { + t.Error("failed to calculate merkle root") + } + merkleRootsBefore = append(merkleRootsBefore, merkleRoot) + } + + encoded := scp.Encode() + + var decoded *SCP + decoded = decoded.Decode(encoded) + + if !reflect.DeepEqual(scp.Height, decoded.Height) { + t.Errorf("Property mismatch 'Height' does not match the given one: %v vs. %v", scp.Height, decoded.Height) + } + + if !reflect.DeepEqual(scp.MHashes, decoded.MHashes) { + t.Errorf("Property mismatch 'MHashes' does not match the given one: %x vs. %x", scp.MHashes, decoded.MHashes) + } + + if !reflect.DeepEqual(scp.PHeader, decoded.PHeader) { + t.Errorf("Property mismatch 'PHeader' does not match the given one: %v vs. %v", scp.PHeader, decoded.PHeader) + } + + if !reflect.DeepEqual(scp.PAmount, decoded.PAmount) { + t.Errorf("Property mismatch 'PAmount' does not match the given one: %v vs. %v", scp.PAmount, decoded.PAmount) + } + + if !reflect.DeepEqual(scp.PFee, decoded.PFee) { + t.Errorf("Property mismatch 'PFee' does not match the given one: %v vs. %v", scp.PFee, decoded.PFee) + } + + if !reflect.DeepEqual(scp.PTxCnt, decoded.PTxCnt) { + t.Errorf("Property mismatch 'PTxCnt' does not match the given one: %v vs. %v", scp.PTxCnt, decoded.PTxCnt) + } + + if !reflect.DeepEqual(scp.PFrom, decoded.PFrom) { + t.Errorf("Property mismatch 'PFrom' does not match the given one: %v vs. %v", scp.PFrom, decoded.PFrom) + } + + if !reflect.DeepEqual(scp.PTo, decoded.PTo) { + t.Errorf("Property mismatch 'PTo' does not match the given one: %v vs. %v", scp.PTo, decoded.PTo) + } + + if !reflect.DeepEqual(scp.PData, decoded.PData) { + t.Errorf("Property mismatch 'PData' does not match the given one: %v vs. %v", scp.PData, decoded.PData) + } + + for i := 0; i < nofProofs; i++ { + if scp.StringFor(i) != decoded.StringFor(i) { + t.Errorf("SCP serialization failed: Proofs not the same \n(%v)\nvs.\n(%v)\n", scp.StringFor(i), decoded.StringFor(i)) + } + + merkleRootAfter, err := decoded.CalculateMerkleRoot(i) + if err != nil { + t.Error("failed to calculate merkle root") + } + + if merkleRootAfter != merkleRootsBefore[i] { + t.Errorf("SCP serialization failed: Merkle roots not the same \n(%v)\nvs.\n(%v)\n", merkleRootAfter, merkleRootsBefore[i]) + } + } +} + +func TestSCPHash(t *testing.T) { + _, scp1 := getDummySCP() + scp1Hash1 := scp1.Hash() + + if !reflect.DeepEqual(scp1Hash1, scp1.Hash()) { + t.Errorf("SCP hashing failed!") + } + + scp1.Height[0] = scp1.Height[0] + 1 + scp1Hash2 := scp1.Hash() + + if reflect.DeepEqual(scp1Hash1, scp1Hash2) { + t.Errorf("SCP hashing failed!") + } + + _, scp2 := getDummySCP() + if reflect.DeepEqual(scp1Hash1, scp2.Hash()) { + t.Errorf("SCP hashing failed!") + } +} + +func getDummySCP() (int, *SCP) { + randVal := rand.New(rand.NewSource(time.Now().Unix())) + nofProofs := int(randVal.Uint32() % 10) + 1 + + var scp SCP + for i := 0; i < nofProofs; i++ { + tx, _ := ConstrFundsTx(0x01, randVal.Uint64()%100000+1, randVal.Uint64()%10+1, uint32(i), accA.Address, accB.Address, PrivKeyA, nil) + + scp.Height = append(scp.Height, (randVal.Uint32() % 10) + uint32(i * 10)) + scp.PHeader = append(scp.PHeader, tx.Header) + scp.PAmount = append(scp.PAmount, tx.Amount) + scp.PFee = append(scp.PFee, tx.Fee) + scp.PTxCnt = append(scp.PTxCnt, tx.TxCnt) + scp.PFrom = append(scp.PFrom, tx.From) + scp.PTo = append(scp.PTo, tx.To) + scp.PData = append(scp.PData, tx.Data) + + scp.MHashes = append(scp.MHashes, [][32]byte{}) + + merkleTreeDepth := int(rand.Uint32() % 10) + 1 + for j:= 0; j < merkleTreeDepth; j++ { + var mhash [32]byte + randVal.Read(mhash[:]) + scp.MHashes[i] = append(scp.MHashes[i], mhash) + } + } + + return nofProofs, &scp +} \ No newline at end of file From dd045fbdf85f060d2fba79f0a7a820431792600a Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Mon, 3 Dec 2018 14:00:47 +0100 Subject: [PATCH 02/28] add MerkleProof and VerifyMerkleProof functions --- p2p/response.go | 2 +- protocol/merkletree.go | 55 +++++++++- protocol/merkletree_test.go | 211 +++++++++++++++++++++++++++--------- protocol/scp.go | 31 ++++++ protocol/scp_test.go | 15 +-- 5 files changed, 243 insertions(+), 71 deletions(-) diff --git a/p2p/response.go b/p2p/response.go index 79f7e81..a0bb77b 100644 --- a/p2p/response.go +++ b/p2p/response.go @@ -235,7 +235,7 @@ func intermediateNodesRes(p *peer, payload []byte) { merkleTree := protocol.BuildMerkleTree(storage.ReadClosedBlock(blockHash)) - if intermediates, _ := protocol.GetIntermediate(protocol.GetLeaf(merkleTree, txHash)); intermediates != nil { + if intermediates, _ := protocol.GetIntermediate(merkleTree.GetLeaf(txHash)); intermediates != nil { for _, node := range intermediates { nodeHashes = append(nodeHashes, node.Hash[:]) } diff --git a/protocol/merkletree.go b/protocol/merkletree.go index f065754..1481753 100644 --- a/protocol/merkletree.go +++ b/protocol/merkletree.go @@ -36,7 +36,7 @@ func (n *Node) verifyNode() [32]byte { leftHash := n.Left.verifyNode() rightHash := n.Right.verifyNode() concatHash := append(leftHash[:], rightHash[:]...) - return sha3.Sum256(concatHash) + return MTHash(concatHash) } func BuildMerkleTree(b *Block) *MerkleTree { @@ -129,7 +129,7 @@ func buildIntermediate(nl []*Node) *Node { n := &Node{ Left: nl[i], Right: nl[i+1], - Hash: sha3.Sum256(concatHash), + Hash: MTHash(concatHash), } nodes = append(nodes, n) nl[i].Parent = n @@ -174,8 +174,8 @@ func (m *MerkleTree) VerifyTree() bool { return false } -func GetLeaf(merkleTree *MerkleTree, leafHash [32]byte) *Node { - for _, leaf := range merkleTree.Leafs { +func (m *MerkleTree) GetLeaf(leafHash [32]byte) *Node { + for _, leaf := range m.Leafs { if leafHash == leaf.Hash { return leaf } @@ -183,6 +183,53 @@ func GetLeaf(merkleTree *MerkleTree, leafHash [32]byte) *Node { return nil } +func MTHash(data []byte) [32]byte { + return sha3.Sum256(data) +} + +func (m *MerkleTree) MerkleProof(leafHash [32]byte) (hashes [][33]byte, err error) { + leaf := m.GetLeaf(leafHash) + currentNode := leaf + currentParent := leaf.Parent + for currentParent != nil { + left := currentParent.Left + right := currentParent.Right + var hash [33]byte + if currentNode.Hash == left.Hash { + copy(hash[0:1], []byte{'r'}) + copy(hash[1:33], right.Hash[:]) + hashes = append(hashes, hash) + } else if currentNode.Hash == right.Hash { + copy(hash[0:1], []byte{'l'}) + copy(hash[1:33], left.Hash[:]) + hashes = append(hashes, hash) + } else { + return nil, errors.New(fmt.Sprintf("could not find intermediate node to verify %x\n", leaf.Hash)) + } + currentNode = currentParent + currentParent = currentParent.Parent + } + return hashes, nil +} + +func (m *MerkleTree) VerifyMerkleProof(leafHash [32]byte, hashes [][33]byte) bool { + computedRootHash := leafHash + for i := 0; i < len(hashes); i++ { + var hash [32]byte + var leftOrRight [1]byte + copy(leftOrRight[:], hashes[i][0:1]) + copy(hash[:], hashes[i][1:33]) + + if leftOrRight == [1]byte{'l'} { + computedRootHash = MTHash(append(hash[:], computedRootHash[:]...)) + } else { + computedRootHash = MTHash(append(computedRootHash[:], hash[:]...)) + } + } + + return computedRootHash == m.MerkleRoot() +} + //VerifyContent indicates whether a given content is in the tree and the hashes are valid for that content. //Returns true if the expected Merkle Root is equivalent to the Merkle root calculated on the critical path //for a given content. Returns true if valid and false otherwise. diff --git a/protocol/merkletree_test.go b/protocol/merkletree_test.go index 3c3d6b9..5805cb4 100644 --- a/protocol/merkletree_test.go +++ b/protocol/merkletree_test.go @@ -1,25 +1,20 @@ package protocol import ( - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "testing" - "golang.org/x/crypto/sha3" + "math/rand" + "testing" + "time" ) func TestBuildMerkleTree3N(t *testing.T) { var hashSlice [][32]byte - var tx *FundsTx - - //Generating a private key and prepare data - privA, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) for i := 0; i < 3; i++ { - tx, _ = ConstrFundsTx(0, 10, 1, uint32(i), [64]byte{'1'}, [64]byte{'2'}, privA, nil) - hashSlice = append(hashSlice, tx.Hash()) + var txHash [32]byte + rand.Read(txHash[:]) + hashSlice = append(hashSlice, txHash) } b := Block{ @@ -44,14 +39,11 @@ func TestBuildMerkleTree3N(t *testing.T) { func TestBuildMerkleTree2N(t *testing.T) { var hashSlice [][32]byte - var tx *FundsTx - - //Generating a private key and prepare data - privA, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) for i := 0; i < 2; i++ { - tx, _ = ConstrFundsTx(0, 10, 1, uint32(i), [64]byte{'1'}, [64]byte{'2'}, privA, nil) - hashSlice = append(hashSlice, tx.Hash()) + var txHash [32]byte + rand.Read(txHash[:]) + hashSlice = append(hashSlice, txHash) } b := Block{ @@ -71,14 +63,11 @@ func TestBuildMerkleTree2N(t *testing.T) { func TestBuildMerkleTree4N(t *testing.T) { var hashSlice [][32]byte - var tx *FundsTx - - //Generating a private key and prepare data - privA, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) for i := 0; i < 4; i++ { - tx, _ = ConstrFundsTx(0, 10, 1, uint32(i), [64]byte{'1'}, [64]byte{'2'}, privA, nil) - hashSlice = append(hashSlice, tx.Hash()) + var txHash [32]byte + rand.Read(txHash[:]) + hashSlice = append(hashSlice, txHash) } b := Block{ @@ -106,14 +95,11 @@ func TestBuildMerkleTree4N(t *testing.T) { func TestBuildMerkleTree6N(t *testing.T) { var hashSlice [][32]byte - var tx *FundsTx - - //Generating a private key and prepare data - privA, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) for i := 0; i < 6; i++ { - tx, _ = ConstrFundsTx(0, 10, 1, uint32(i), [64]byte{'1'}, [64]byte{'2'}, privA, nil) - hashSlice = append(hashSlice, tx.Hash()) + var txHash [32]byte + rand.Read(txHash[:]) + hashSlice = append(hashSlice, txHash) } b := Block{ @@ -149,14 +135,11 @@ func TestBuildMerkleTree6N(t *testing.T) { func TestBuildMerkleTree8N(t *testing.T) { var hashSlice [][32]byte - var tx *FundsTx - - //Generating a private key and prepare data - privA, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) for i := 0; i < 8; i++ { - tx, _ = ConstrFundsTx(0, 10, 1, uint32(i), [64]byte{'1'}, [64]byte{'2'}, privA, nil) - hashSlice = append(hashSlice, tx.Hash()) + var txHash [32]byte + rand.Read(txHash[:]) + hashSlice = append(hashSlice, txHash) } b := Block{ @@ -198,14 +181,11 @@ func TestBuildMerkleTree8N(t *testing.T) { func TestBuildMerkleTree10N(t *testing.T) { var hashSlice [][32]byte - var tx *FundsTx - - //Generating a private key and prepare data - privA, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) for i := 0; i < 10; i++ { - tx, _ = ConstrFundsTx(0, 10, 1, uint32(i), [64]byte{'1'}, [64]byte{'2'}, privA, nil) - hashSlice = append(hashSlice, tx.Hash()) + var txHash [32]byte + rand.Read(txHash[:]) + hashSlice = append(hashSlice, txHash) } b := Block{ @@ -255,14 +235,11 @@ func TestBuildMerkleTree10N(t *testing.T) { func TestBuildMerkleTree11N(t *testing.T) { var hashSlice [][32]byte - var tx *FundsTx - - //Generating a private key and prepare data - privA, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) for i := 0; i < 11; i++ { - tx, _ = ConstrFundsTx(0, 10, 1, uint32(i), [64]byte{'1'}, [64]byte{'2'}, privA, nil) - hashSlice = append(hashSlice, tx.Hash()) + var txHash [32]byte + rand.Read(txHash[:]) + hashSlice = append(hashSlice, txHash) } b := Block{ @@ -318,14 +295,11 @@ func TestBuildMerkleTree11N(t *testing.T) { func TestGetIntermediate(t *testing.T) { var hashSlice [][32]byte - var tx *FundsTx - - //Generating a private key and prepare data - privA, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) for i := 0; i < 11; i++ { - tx, _ = ConstrFundsTx(0, 10, 1, uint32(i), [64]byte{'1'}, [64]byte{'2'}, privA, nil) - hashSlice = append(hashSlice, tx.Hash()) + var txHash [32]byte + rand.Read(txHash[:]) + hashSlice = append(hashSlice, txHash) } b := Block{ @@ -370,7 +344,7 @@ func TestGetIntermediate(t *testing.T) { merkleTree := BuildMerkleTree(&b) - intermediates, _ := GetIntermediate(GetLeaf(merkleTree, hashSlice[9])) + intermediates, _ := GetIntermediate(merkleTree.GetLeaf(hashSlice[9])) if intermediates[0].Hash != hashSlice[8] { t.Errorf("Hashes don't match: %x != %x\n", intermediates[0].Hash, hashSlice[8]) @@ -392,3 +366,132 @@ func TestGetIntermediate(t *testing.T) { t.Errorf("Hashes don't match: %x != %x\n", intermediates[4].Hash, hash12345678) } } + + + +func TestMerkleProof(t *testing.T) { + randVal := rand.New(rand.NewSource(time.Now().Unix())) + + var hash1, hash2, hash3, hash4 [32]byte + + randVal.Read(hash1[:]) + randVal.Read(hash2[:]) + randVal.Read(hash3[:]) + randVal.Read(hash4[:]) + + var hashSlice [][32]byte + hashSlice = append(hashSlice, hash1, hash2, hash3, hash4) + + b := Block{ + FundsTxData: hashSlice, + } + + m := BuildMerkleTree(&b) + + hash12 := MTHash(append(hash1[:], hash2[:]...)) + hash34 := MTHash(append(hash3[:], hash4[:]...)) + hash1234 := MTHash(append(hash12[:], hash34[:]...)) + if hash1234 != m.MerkleRoot() { + t.Errorf("Root hashes don't match: %x != %x\n", hash1234, m.MerkleRoot()) + } + + hashes, err := m.MerkleProof(hash1) + if err != nil { + t.Error(err) + } + + if len(hashes) != 2 { + t.Errorf("Merkle proof returns invalid amount of hashes") + } + + var mhash [32]byte + var leftOrRight [1]byte + copy(leftOrRight[:], hashes[0][0:1]) + copy(mhash[:], hashes[0][1:33]) + + if leftOrRight != [1]byte{'r'} { + t.Errorf("invalid left/right byte: is left but should be right") + } + + if hash2 != mhash { + t.Errorf("invalid Merkle proof hash at index 0") + } + + copy(leftOrRight[:], hashes[1][0:1]) + copy(mhash[:], hashes[1][1:33]) + + if leftOrRight != [1]byte{'r'} { + t.Errorf("invalid left/right byte: is left but should be right") + } + + if hash34 != mhash { + t.Errorf("invalid Merkle proof hash at index 1") + } +} + + + +func TestMerkleProofWithVerification(t *testing.T) { + randVal := rand.New(rand.NewSource(time.Now().Unix())) + var hashSlice [][32]byte + nofHashes := int(randVal.Uint32() % 1000) + 1 + for i := 0; i < nofHashes; i++ { + var txHash [32]byte + randVal.Read(txHash[:]) + hashSlice = append(hashSlice, txHash) + } + + b := Block{ + FundsTxData: hashSlice, + } + + m := BuildMerkleTree(&b) + + + randomIndex := randVal.Uint32() % uint32(nofHashes) + leafHash := hashSlice[randomIndex] + hashes, err := m.MerkleProof(leafHash) + if err != nil { + t.Error(err) + } + + if !m.VerifyMerkleProof(leafHash, hashes) { + t.Errorf("Merkle proof verification failed for hash %x", leafHash) + } +} + +func TestVerifyMerkleProof(t *testing.T) { + randVal := rand.New(rand.NewSource(time.Now().Unix())) + + var hash1, hash2, hash3, hash4 [32]byte + + randVal.Read(hash1[:]) + randVal.Read(hash2[:]) + randVal.Read(hash3[:]) + randVal.Read(hash4[:]) + + var hashSlice [][32]byte + hashSlice = append(hashSlice, hash1, hash2, hash3, hash4) + + b := Block{ + FundsTxData: hashSlice, + } + + m := BuildMerkleTree(&b) + + hash12 := MTHash(append(hash1[:], hash2[:]...)) + hash34 := MTHash(append(hash3[:], hash4[:]...)) + hash1234 := MTHash(append(hash12[:], hash34[:]...)) + if hash1234 != m.MerkleRoot() { + t.Errorf("Root hashes don't match: %x != %x\n", hash1234, m.MerkleRoot()) + } + + hashes, err := m.MerkleProof(hash1) + if err != nil { + t.Error(err) + } + + if !m.VerifyMerkleProof(hash1, hashes) { + t.Errorf("Merkle proof verification failed for hash %x", hash1) + } +} \ No newline at end of file diff --git a/protocol/scp.go b/protocol/scp.go index 167181c..18681b2 100644 --- a/protocol/scp.go +++ b/protocol/scp.go @@ -28,6 +28,37 @@ type SCP struct { PData [][]byte } +func NewSCP() SCP { + return SCP{} +} + +func (scp *SCP) AddProof(height uint32, mhashes [][32]byte, pheader byte, pamount uint64, pfee uint64, ptxcnt uint32, pfrom [64]byte, pto [64]byte, pdata []byte) (index int) { + scp.Height = append(scp.Height, height) + scp.MHashes = append(scp.MHashes, mhashes) + scp.PHeader = append(scp.PHeader, pheader) + scp.PAmount = append(scp.PAmount, pamount) + scp.PFee = append(scp.PFee, pfee) + scp.PTxCnt = append(scp.PTxCnt, ptxcnt) + scp.PFrom = append(scp.PFrom, pfrom) + scp.PTo = append(scp.PTo, pto) + scp.PData = append(scp.PData, pdata) + + return len(scp.Height) - 1 +} + +func (scp *SCP) NewProof() (index int) { + scp.Height = append(scp.Height, uint32(0)) + scp.MHashes = append(scp.MHashes, [][32]byte{}) + scp.PHeader = append(scp.PHeader, 0) + scp.PAmount = append(scp.PAmount, 0) + scp.PFee = append(scp.PFee, 0) + scp.PTxCnt = append(scp.PTxCnt, 0) + scp.PFrom = append(scp.PFrom, [64]byte{}) + scp.PTo = append(scp.PTo, [64]byte{}) + scp.PData = append(scp.PData, nil) + return len(scp.Height) - 1 +} + func (scp *SCP) CalculateMerkleRoot(index int) (merkleRoot [32]byte, err error) { if err = scp.verifyProofPrerequisites(); err != nil { return [32]byte{}, err diff --git a/protocol/scp_test.go b/protocol/scp_test.go index ce4dd6d..1a8d2f0 100644 --- a/protocol/scp_test.go +++ b/protocol/scp_test.go @@ -101,21 +101,12 @@ func getDummySCP() (int, *SCP) { randVal := rand.New(rand.NewSource(time.Now().Unix())) nofProofs := int(randVal.Uint32() % 10) + 1 - var scp SCP + scp := NewSCP() for i := 0; i < nofProofs; i++ { tx, _ := ConstrFundsTx(0x01, randVal.Uint64()%100000+1, randVal.Uint64()%10+1, uint32(i), accA.Address, accB.Address, PrivKeyA, nil) - scp.Height = append(scp.Height, (randVal.Uint32() % 10) + uint32(i * 10)) - scp.PHeader = append(scp.PHeader, tx.Header) - scp.PAmount = append(scp.PAmount, tx.Amount) - scp.PFee = append(scp.PFee, tx.Fee) - scp.PTxCnt = append(scp.PTxCnt, tx.TxCnt) - scp.PFrom = append(scp.PFrom, tx.From) - scp.PTo = append(scp.PTo, tx.To) - scp.PData = append(scp.PData, tx.Data) - - scp.MHashes = append(scp.MHashes, [][32]byte{}) - + randHeight := (randVal.Uint32() % 10) + uint32(i * 10) + scp.AddProof(randHeight, [][32]byte{}, tx.Header, tx.Amount, tx.Fee, tx.TxCnt, tx.From, tx.To, tx.Data) merkleTreeDepth := int(rand.Uint32() % 10) + 1 for j:= 0; j < merkleTreeDepth; j++ { var mhash [32]byte From 1a1be9aca9e4a83e3049f72a9f1aa51cf1f25bad Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Mon, 3 Dec 2018 16:10:19 +0100 Subject: [PATCH 03/28] make CreateBucket public --- storage/storage.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/storage.go b/storage/storage.go index a85fddd..5640854 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -56,7 +56,7 @@ func Init(dbname string, bootstrapIpport string) error { } for _, bucket := range Buckets { - err = createBucket(bucket) + err = CreateBucket(bucket) if err != nil { return err } @@ -65,7 +65,7 @@ func Init(dbname string, bootstrapIpport string) error { return nil } -func createBucket(bucketName string) (err error) { +func CreateBucket(bucketName string) (err error) { return db.Update(func(tx *bolt.Tx) error { _, err = tx.CreateBucket([]byte(bucketName)) if err != nil { From 53903ff044c61b5c9b789b8e687cbfe16a027b9e Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Mon, 3 Dec 2018 16:37:45 +0100 Subject: [PATCH 04/28] add BoltDB as parameter to create a bucket by name --- storage/storage.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/storage.go b/storage/storage.go index 5640854..b751bcc 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -56,7 +56,7 @@ func Init(dbname string, bootstrapIpport string) error { } for _, bucket := range Buckets { - err = CreateBucket(bucket) + err = CreateBucket(bucket, db) if err != nil { return err } @@ -65,7 +65,7 @@ func Init(dbname string, bootstrapIpport string) error { return nil } -func CreateBucket(bucketName string) (err error) { +func CreateBucket(bucketName string, db *bolt.DB) (err error) { return db.Update(func(tx *bolt.Tx) error { _, err = tx.CreateBucket([]byte(bucketName)) if err != nil { From 3c1b28ee7bf59f7c1e61787f13c494a441791e29 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Mon, 3 Dec 2018 16:59:44 +0100 Subject: [PATCH 05/28] return error when decoding account --- protocol/account.go | 10 +++++++--- protocol/account_test.go | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/protocol/account.go b/protocol/account.go index e75bbf1..e71c832 100644 --- a/protocol/account.go +++ b/protocol/account.go @@ -72,12 +72,16 @@ func (acc *Account) Encode() []byte { return buffer.Bytes() } -func (*Account) Decode(encoded []byte) (acc *Account) { +func (*Account) Decode(encoded []byte) (acc *Account, err error) { var decoded Account buffer := bytes.NewBuffer(encoded) decoder := gob.NewDecoder(buffer) - decoder.Decode(&decoded) - return &decoded + err = decoder.Decode(&decoded) + if err != nil { + return nil, err + } + + return &decoded, nil } func (acc Account) String() string { diff --git a/protocol/account_test.go b/protocol/account_test.go index 99de483..4495c9d 100644 --- a/protocol/account_test.go +++ b/protocol/account_test.go @@ -66,7 +66,7 @@ func TestAccountSerialization(t *testing.T) { var compareAcc *Account encodedAcc := accA.Encode() - compareAcc = compareAcc.Decode(encodedAcc) + compareAcc, _ = compareAcc.Decode(encodedAcc) if !reflect.DeepEqual(accA, compareAcc) { t.Error("Account encoding/decoding failed!") From b205fe11c537c5f3c30463fa620c37ce5163a0ab Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Mon, 3 Dec 2018 19:19:35 +0100 Subject: [PATCH 06/28] implement MerkleProof datastructure into SCP --- protocol/merkleproof.go | 161 +++++++++++++++++++++++++++++++ protocol/merkleproof_test.go | 45 +++++++++ protocol/scp.go | 177 ++++------------------------------- protocol/scp_test.go | 58 +++--------- 4 files changed, 234 insertions(+), 207 deletions(-) create mode 100644 protocol/merkleproof.go create mode 100644 protocol/merkleproof_test.go diff --git a/protocol/merkleproof.go b/protocol/merkleproof.go new file mode 100644 index 0000000..d711e88 --- /dev/null +++ b/protocol/merkleproof.go @@ -0,0 +1,161 @@ +package protocol + +import ( + "bytes" + "encoding/gob" + "fmt" + "golang.org/x/crypto/sha3" +) + +type MerkleProof struct { + // Proof height + Height uint32 + + // Merkle hashes + // Intermediate hashes required to create a Merkle proof + MHashes [][32]byte + + // Proof properties + // Must equal the hashed data of FundsTx.Hash() + PHeader byte + PAmount uint64 + PFee uint64 + PTxCnt uint32 + PFrom [64]byte + PTo [64]byte + PData []byte +} + + +func NewMerkleProof(height uint32, mhashes [][32]byte, header byte, amount uint64, fee uint64, txcnt uint32, from [64]byte, to [64]byte, data []byte) (proof MerkleProof) { + proof.Height = height + proof.MHashes = mhashes + proof.PHeader = header + proof.PAmount = amount + proof.PFee = fee + proof.PTxCnt = txcnt + proof.PFrom = from + proof.PTo = to + proof.PData = data + + return proof +} + +func (proof *MerkleProof) Hash() (hash [32]byte) { + if proof == nil { + return [32]byte{} + } + + input := struct { + Height uint32 + MHashes [][32]byte + PHeader byte + PAmount uint64 + PFee uint64 + PTxCnt uint32 + PFrom [64]byte + PTo [64]byte + PData []byte + }{ + proof.Height, + proof.MHashes, + proof.PHeader, + proof.PAmount, + proof.PFee, + proof.PTxCnt, + proof.PFrom, + proof.PTo, + proof.PData, + } + + return SerializeHashContent(input) +} + +func (proof *MerkleProof) Encode() (encodedTx []byte) { + encodeData := MerkleProof{ + proof.Height, + proof.MHashes, + proof.PHeader, + proof.PAmount, + proof.PFee, + proof.PTxCnt, + proof.PFrom, + proof.PTo, + proof.PData, + } + buffer := new(bytes.Buffer) + gob.NewEncoder(buffer).Encode(encodeData) + return buffer.Bytes() +} + +func (proof *MerkleProof) Decode(encoded []byte) *MerkleProof { + var decoded MerkleProof + buffer := bytes.NewBuffer(encoded) + decoder := gob.NewDecoder(buffer) + decoder.Decode(&decoded) + return &decoded +} + +func (proof *MerkleProof) String() string { + var mhashesString string + for _, mhash := range proof.MHashes { + mhashesString += fmt.Sprintf("%x, ", mhash[0:8]) + } + + mhashesString = mhashesString[0:len(mhashesString) - 2] + + return fmt.Sprintf("Height: %v\n" + + "MHashes: [%v]\n" + + "PHash Header: %v\n" + + "PHash Amount: %v\n"+ + "PHash Fee: %v\n"+ + "PHash TxCnt: %v\n"+ + "PHash From: %x\n"+ + "PHash To: %x\n"+ + "PHash Data: %v\n", + proof.Height, + mhashesString, + proof.PHeader, + proof.PAmount, + proof.PFee, + proof.PTxCnt, + proof.PFrom[0:8], + proof.PTo[0:8], + proof.PData, + ) +} + +func (proof *MerkleProof) CalculateMerkleRoot() (merkleRoot [32]byte, err error) { + phash := proof.getProofHash() + + merkleRoot = phash + for _, mhash := range proof.MHashes { + concatHash := append(merkleRoot[:], mhash[:]...) + merkleRoot = sha3.Sum256(concatHash) + } + + return merkleRoot, nil +} + +func (proof *MerkleProof) getProofHash() [32]byte { + // Note that the hashed properties must equal to the hashed properties of FundsTx.Hash() + hash := struct { + Header byte + Amount uint64 + Fee uint64 + TxCnt uint32 + From [64]byte + To [64]byte + Data []byte + }{ + proof.PHeader, + proof.PAmount, + proof.PFee, + proof.PTxCnt, + proof.PFrom, + proof.PTo, + proof.PData, + } + + return SerializeHashContent(hash) +} \ No newline at end of file diff --git a/protocol/merkleproof_test.go b/protocol/merkleproof_test.go new file mode 100644 index 0000000..4e33c11 --- /dev/null +++ b/protocol/merkleproof_test.go @@ -0,0 +1,45 @@ +package protocol + +import ( + "math/rand" + "reflect" + "testing" + "time" +) + +func TestMerkleProofSerialization(t *testing.T) { + randVal := rand.New(rand.NewSource(time.Now().Unix())) + + randHeight := randVal.Uint32() % 10 + proof := NewMerkleProof(randHeight, [][32]byte{}, 0x01, randVal.Uint64()%100000+1, randVal.Uint64()%10+1, uint32(0), accA.Address, accB.Address, nil) + + merkleTreeDepth := int(rand.Uint32() % 10) + 1 + for j:= 0; j < merkleTreeDepth; j++ { + var mhash [32]byte + randVal.Read(mhash[:]) + proof.MHashes = append(proof.MHashes, mhash) + } + + encoded := proof.Encode() + + var decoded *MerkleProof + decoded = decoded.Decode(encoded) + + if !reflect.DeepEqual(proof, decoded) { + t.Errorf("Proof does not match the given one: %v vs. %v", proof.String(), decoded.String()) + } + + merkleRootBefore, err := proof.CalculateMerkleRoot() + if err != nil { + t.Error(err) + } + + merkleRootAfter, err := decoded.CalculateMerkleRoot() + if err != nil { + t.Error("failed to calculate merkle root") + } + + if merkleRootBefore != merkleRootAfter { + t.Errorf("SCP serialization failed: Merkle roots not the same \n(%v)\nvs.\n(%v)\n", merkleRootBefore, merkleRootAfter) + } +} \ No newline at end of file diff --git a/protocol/scp.go b/protocol/scp.go index 18681b2..c11e3c7 100644 --- a/protocol/scp.go +++ b/protocol/scp.go @@ -3,81 +3,28 @@ package protocol import ( "bytes" "encoding/gob" - "errors" - "fmt" - "golang.org/x/crypto/sha3" ) type SCP struct { - // Proof height - // Block heights of the proofs - Height []uint32 - - // Merkle hashes - // Intermediate hashes required to create a Merkle proof - MHashes [][][32]byte - - // Proof properties - // Must equal the hashed data of FundsTx.Hash() - PHeader []byte - PAmount []uint64 - PFee []uint64 - PTxCnt []uint32 - PFrom [][64]byte - PTo [][64]byte - PData [][]byte + Proofs []*MerkleProof } func NewSCP() SCP { return SCP{} } -func (scp *SCP) AddProof(height uint32, mhashes [][32]byte, pheader byte, pamount uint64, pfee uint64, ptxcnt uint32, pfrom [64]byte, pto [64]byte, pdata []byte) (index int) { - scp.Height = append(scp.Height, height) - scp.MHashes = append(scp.MHashes, mhashes) - scp.PHeader = append(scp.PHeader, pheader) - scp.PAmount = append(scp.PAmount, pamount) - scp.PFee = append(scp.PFee, pfee) - scp.PTxCnt = append(scp.PTxCnt, ptxcnt) - scp.PFrom = append(scp.PFrom, pfrom) - scp.PTo = append(scp.PTo, pto) - scp.PData = append(scp.PData, pdata) - - return len(scp.Height) - 1 -} - -func (scp *SCP) NewProof() (index int) { - scp.Height = append(scp.Height, uint32(0)) - scp.MHashes = append(scp.MHashes, [][32]byte{}) - scp.PHeader = append(scp.PHeader, 0) - scp.PAmount = append(scp.PAmount, 0) - scp.PFee = append(scp.PFee, 0) - scp.PTxCnt = append(scp.PTxCnt, 0) - scp.PFrom = append(scp.PFrom, [64]byte{}) - scp.PTo = append(scp.PTo, [64]byte{}) - scp.PData = append(scp.PData, nil) - return len(scp.Height) - 1 +func (scp *SCP) AddMerkleProof(proof *MerkleProof) (index int) { + scp.Proofs = append(scp.Proofs, proof) + return len(scp.Proofs) - 1 } -func (scp *SCP) CalculateMerkleRoot(index int) (merkleRoot [32]byte, err error) { - if err = scp.verifyProofPrerequisites(); err != nil { - return [32]byte{}, err - } - - phash := scp.getProofHash(index) - mhashes := scp.MHashes[index] - - merkleRoot = phash - for _, mhash := range mhashes { - concatHash := append(merkleRoot[:], mhash[:]...) - merkleRoot = sha3.Sum256(concatHash) - } +func (scp *SCP) CalculateMerkleRootFor(index int) (merkleRoot [32]byte, err error) { + return scp.Proofs[index].CalculateMerkleRoot() - return merkleRoot, nil } func (scp *SCP) ProofCount() int { - return len(scp.Height) + return len(scp.Proofs) } func (scp *SCP) Hash() (hash [32]byte) { @@ -85,43 +32,21 @@ func (scp *SCP) Hash() (hash [32]byte) { return [32]byte{} } - scpHash := struct { - Height []uint32 - MHashes [][][32]byte - PHashHeader []byte - PHashAmount []uint64 - PHashFee []uint64 - PHashTxCnt []uint32 - PHashFrom [][64]byte - PHashTo [][64]byte - PHashData [][]byte - }{ - scp.Height, - scp.MHashes, - scp.PHeader, - scp.PAmount, - scp.PFee, - scp.PTxCnt, - scp.PFrom, - scp.PTo, - scp.PData, + var proofHashes [][32]byte + for _, proof := range scp.Proofs { + proofHashes = append(proofHashes, proof.Hash()) } - return SerializeHashContent(scpHash) + return SerializeHashContent(proofHashes) } func (scp *SCP) Encode() (encodedTx []byte) { + gob.Register(MerkleProof{}) + encodeData := SCP{ - scp.Height, - scp.MHashes, - scp.PHeader, - scp.PAmount, - scp.PFee, - scp.PTxCnt, - scp.PFrom, - scp.PTo, - scp.PData, + scp.Proofs, } + buffer := new(bytes.Buffer) gob.NewEncoder(buffer).Encode(encodeData) return buffer.Bytes() @@ -136,75 +61,5 @@ func (scp *SCP) Decode(encoded []byte) *SCP { } func (scp *SCP) StringFor(index int) string { - var mhashesString string - for _, mhash := range scp.MHashes[index] { - mhashesString += fmt.Sprintf("%x, ", mhash[0:8]) - } - - mhashesString = mhashesString[0:len(mhashesString) - 2] - - return fmt.Sprintf("Height: %v\n" + - "MHashes: [%v]\n" + - "PHash Header: %v\n" + - "PHash Amount: %v\n"+ - "PHash Fee: %v\n"+ - "PHash TxCnt: %v\n"+ - "PHash From: %x\n"+ - "PHash To: %x\n"+ - "PHash Data: %v\n", - scp.Height[index], - mhashesString, - scp.PHeader[index], - scp.PAmount[index], - scp.PFee[index], - scp.PTxCnt[index], - scp.PFrom[index][0:8], - scp.PTo[index][0:8], - scp.PData[index], - ) -} - -func (scp *SCP) getProofHash(index int) [32]byte { - // Note that the hashed properties must equal to the hashed properties of FundsTx.Hash() - phash := struct { - Header byte - Amount uint64 - Fee uint64 - TxCnt uint32 - From [64]byte - To [64]byte - Data []byte - }{ - scp.PHeader[index], - scp.PAmount[index], - scp.PFee[index], - scp.PTxCnt[index], - scp.PFrom[index], - scp.PTo[index], - scp.PData[index], - } - - return SerializeHashContent(phash) + return scp.Proofs[index].String() } - -func (scp *SCP) verifyProofPrerequisites() error { - nofProofs := scp.ProofCount() - - if nofProofs != len(scp.MHashes) { - // For every proof i, the number of Merkle hashes at i must be specified. - return errors.New("missing Merkle hashes") - } - - if nofProofs != len(scp.PHeader) || - nofProofs != len(scp.PAmount) || - nofProofs != len(scp.PFee) || - nofProofs != len(scp.PTxCnt) || - nofProofs != len(scp.PFrom) || - nofProofs != len(scp.PTo) || - nofProofs != len(scp.PData) { - // For every proof i, the properties for proof i must be specified. - return errors.New("missing proof hash values") - } - - return nil -} \ No newline at end of file diff --git a/protocol/scp_test.go b/protocol/scp_test.go index 1a8d2f0..521e0fa 100644 --- a/protocol/scp_test.go +++ b/protocol/scp_test.go @@ -12,7 +12,7 @@ func TestSCPSerialization(t *testing.T) { nofProofs, scp := getDummySCP() for i := 0; i < nofProofs; i++ { - merkleRoot, err := scp.CalculateMerkleRoot(i) + merkleRoot, err := scp.CalculateMerkleRootFor(i) if err != nil { t.Error("failed to calculate merkle root") } @@ -24,53 +24,18 @@ func TestSCPSerialization(t *testing.T) { var decoded *SCP decoded = decoded.Decode(encoded) - if !reflect.DeepEqual(scp.Height, decoded.Height) { - t.Errorf("Property mismatch 'Height' does not match the given one: %v vs. %v", scp.Height, decoded.Height) - } - - if !reflect.DeepEqual(scp.MHashes, decoded.MHashes) { - t.Errorf("Property mismatch 'MHashes' does not match the given one: %x vs. %x", scp.MHashes, decoded.MHashes) - } - - if !reflect.DeepEqual(scp.PHeader, decoded.PHeader) { - t.Errorf("Property mismatch 'PHeader' does not match the given one: %v vs. %v", scp.PHeader, decoded.PHeader) - } - - if !reflect.DeepEqual(scp.PAmount, decoded.PAmount) { - t.Errorf("Property mismatch 'PAmount' does not match the given one: %v vs. %v", scp.PAmount, decoded.PAmount) - } - - if !reflect.DeepEqual(scp.PFee, decoded.PFee) { - t.Errorf("Property mismatch 'PFee' does not match the given one: %v vs. %v", scp.PFee, decoded.PFee) - } - - if !reflect.DeepEqual(scp.PTxCnt, decoded.PTxCnt) { - t.Errorf("Property mismatch 'PTxCnt' does not match the given one: %v vs. %v", scp.PTxCnt, decoded.PTxCnt) - } - - if !reflect.DeepEqual(scp.PFrom, decoded.PFrom) { - t.Errorf("Property mismatch 'PFrom' does not match the given one: %v vs. %v", scp.PFrom, decoded.PFrom) - } - - if !reflect.DeepEqual(scp.PTo, decoded.PTo) { - t.Errorf("Property mismatch 'PTo' does not match the given one: %v vs. %v", scp.PTo, decoded.PTo) - } - - if !reflect.DeepEqual(scp.PData, decoded.PData) { - t.Errorf("Property mismatch 'PData' does not match the given one: %v vs. %v", scp.PData, decoded.PData) - } - for i := 0; i < nofProofs; i++ { - if scp.StringFor(i) != decoded.StringFor(i) { - t.Errorf("SCP serialization failed: Proofs not the same \n(%v)\nvs.\n(%v)\n", scp.StringFor(i), decoded.StringFor(i)) + if !reflect.DeepEqual(scp.Proofs[i], decoded.Proofs[i]) { + t.Errorf("Proof does not match the given one: %v vs. %v", scp.Proofs[i].String(), decoded.Proofs[i].String()) } - merkleRootAfter, err := decoded.CalculateMerkleRoot(i) + merkleRootBefore := merkleRootsBefore[i] + merkleRootAfter, err := decoded.CalculateMerkleRootFor(i) if err != nil { t.Error("failed to calculate merkle root") } - if merkleRootAfter != merkleRootsBefore[i] { + if merkleRootBefore != merkleRootAfter { t.Errorf("SCP serialization failed: Merkle roots not the same \n(%v)\nvs.\n(%v)\n", merkleRootAfter, merkleRootsBefore[i]) } } @@ -84,7 +49,7 @@ func TestSCPHash(t *testing.T) { t.Errorf("SCP hashing failed!") } - scp1.Height[0] = scp1.Height[0] + 1 + scp1.Proofs[0].Height++ scp1Hash2 := scp1.Hash() if reflect.DeepEqual(scp1Hash1, scp1Hash2) { @@ -103,16 +68,17 @@ func getDummySCP() (int, *SCP) { scp := NewSCP() for i := 0; i < nofProofs; i++ { - tx, _ := ConstrFundsTx(0x01, randVal.Uint64()%100000+1, randVal.Uint64()%10+1, uint32(i), accA.Address, accB.Address, PrivKeyA, nil) - randHeight := (randVal.Uint32() % 10) + uint32(i * 10) - scp.AddProof(randHeight, [][32]byte{}, tx.Header, tx.Amount, tx.Fee, tx.TxCnt, tx.From, tx.To, tx.Data) + proof := NewMerkleProof(randHeight, [][32]byte{}, 0x01, randVal.Uint64()%100000+1, randVal.Uint64()%10+1, uint32(i), accA.Address, accB.Address, nil) + merkleTreeDepth := int(rand.Uint32() % 10) + 1 for j:= 0; j < merkleTreeDepth; j++ { var mhash [32]byte randVal.Read(mhash[:]) - scp.MHashes[i] = append(scp.MHashes[i], mhash) + proof.MHashes = append(proof.MHashes, mhash) } + + scp.AddMerkleProof(&proof) } return nofProofs, &scp From 95efc4e588aeb4b0eef832ebcfb6f1a455327d46 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Mon, 3 Dec 2018 20:42:07 +0100 Subject: [PATCH 07/28] fix MerkleProof tests :white_check_mark: --- protocol/merkleproof.go | 42 +++++++++++++++++++++--------------- protocol/merkleproof_test.go | 35 +++++++++++++++++------------- protocol/scp_test.go | 17 ++++++++++++--- 3 files changed, 59 insertions(+), 35 deletions(-) diff --git a/protocol/merkleproof.go b/protocol/merkleproof.go index d711e88..d195089 100644 --- a/protocol/merkleproof.go +++ b/protocol/merkleproof.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/gob" "fmt" - "golang.org/x/crypto/sha3" ) type MerkleProof struct { @@ -13,11 +12,12 @@ type MerkleProof struct { // Merkle hashes // Intermediate hashes required to create a Merkle proof - MHashes [][32]byte + // Note that the first byte specifies the left or right node of the Merkle tree while the rest is the actual hash + MHashes [][33]byte // Proof properties // Must equal the hashed data of FundsTx.Hash() - PHeader byte + PHeader byte PAmount uint64 PFee uint64 PTxCnt uint32 @@ -27,7 +27,7 @@ type MerkleProof struct { } -func NewMerkleProof(height uint32, mhashes [][32]byte, header byte, amount uint64, fee uint64, txcnt uint32, from [64]byte, to [64]byte, data []byte) (proof MerkleProof) { +func NewMerkleProof(height uint32, mhashes [][33]byte, header byte, amount uint64, fee uint64, txcnt uint32, from [64]byte, to [64]byte, data []byte) (proof MerkleProof) { proof.Height = height proof.MHashes = mhashes proof.PHeader = header @@ -48,7 +48,7 @@ func (proof *MerkleProof) Hash() (hash [32]byte) { input := struct { Height uint32 - MHashes [][32]byte + MHashes [][33]byte PHeader byte PAmount uint64 PFee uint64 @@ -106,13 +106,13 @@ func (proof *MerkleProof) String() string { return fmt.Sprintf("Height: %v\n" + "MHashes: [%v]\n" + - "PHash Header: %v\n" + - "PHash Amount: %v\n"+ - "PHash Fee: %v\n"+ - "PHash TxCnt: %v\n"+ - "PHash From: %x\n"+ - "PHash To: %x\n"+ - "PHash Data: %v\n", + "Proof Header: %v\n" + + "Proof Amount: %v\n"+ + "Proof Fee: %v\n"+ + "Proof TxCnt: %v\n"+ + "Proof From: %x\n"+ + "Proof To: %x\n"+ + "Proof Data: %v\n", proof.Height, mhashesString, proof.PHeader, @@ -125,16 +125,24 @@ func (proof *MerkleProof) String() string { ) } -func (proof *MerkleProof) CalculateMerkleRoot() (merkleRoot [32]byte, err error) { +func (proof *MerkleProof) CalculateMerkleRoot() (computedHash [32]byte, err error) { phash := proof.getProofHash() - merkleRoot = phash + computedHash = phash for _, mhash := range proof.MHashes { - concatHash := append(merkleRoot[:], mhash[:]...) - merkleRoot = sha3.Sum256(concatHash) + var hash [32]byte + var leftOrRight [1]byte + copy(leftOrRight[:], mhash[0:1]) + copy(hash[:], mhash[1:33]) + + if leftOrRight == [1]byte{'l'} { + computedHash = MTHash(append(hash[:], computedHash[:]...)) + } else { + computedHash = MTHash(append(computedHash[:], hash[:]...)) + } } - return merkleRoot, nil + return computedHash, nil } func (proof *MerkleProof) getProofHash() [32]byte { diff --git a/protocol/merkleproof_test.go b/protocol/merkleproof_test.go index 4e33c11..286bc59 100644 --- a/protocol/merkleproof_test.go +++ b/protocol/merkleproof_test.go @@ -4,34 +4,39 @@ import ( "math/rand" "reflect" "testing" - "time" ) func TestMerkleProofSerialization(t *testing.T) { - randVal := rand.New(rand.NewSource(time.Now().Unix())) - - randHeight := randVal.Uint32() % 10 - proof := NewMerkleProof(randHeight, [][32]byte{}, 0x01, randVal.Uint64()%100000+1, randVal.Uint64()%10+1, uint32(0), accA.Address, accB.Address, nil) - + proof := NewMerkleProof(rand.Uint32() % 10, [][33]byte{}, 0x01, rand.Uint64()%100000+1, rand.Uint64()%10+1, uint32(0), accA.Address, accB.Address, nil) merkleTreeDepth := int(rand.Uint32() % 10) + 1 for j:= 0; j < merkleTreeDepth; j++ { - var mhash [32]byte - randVal.Read(mhash[:]) + leftOrRightNumber := int(rand.Uint32() % 2) + + var mhash [33]byte + var leftOrRight [1]byte + if leftOrRightNumber == 0 { + leftOrRight = [1]byte{'l'} + } else { + leftOrRight = [1]byte{'r'} + } + + copy(mhash[0:1], leftOrRight[:]) + rand.Read(mhash[1:33]) proof.MHashes = append(proof.MHashes, mhash) } + merkleRootBefore, err := proof.CalculateMerkleRoot() + if err != nil { + t.Error(err) + } + encoded := proof.Encode() var decoded *MerkleProof decoded = decoded.Decode(encoded) - if !reflect.DeepEqual(proof, decoded) { - t.Errorf("Proof does not match the given one: %v vs. %v", proof.String(), decoded.String()) - } - - merkleRootBefore, err := proof.CalculateMerkleRoot() - if err != nil { - t.Error(err) + if !reflect.DeepEqual(&proof, decoded) { + t.Errorf("Proof does not match the given one: \n%vvs.\n%v", proof.String(), decoded.String()) } merkleRootAfter, err := decoded.CalculateMerkleRoot() diff --git a/protocol/scp_test.go b/protocol/scp_test.go index 521e0fa..20f9c6a 100644 --- a/protocol/scp_test.go +++ b/protocol/scp_test.go @@ -69,12 +69,23 @@ func getDummySCP() (int, *SCP) { scp := NewSCP() for i := 0; i < nofProofs; i++ { randHeight := (randVal.Uint32() % 10) + uint32(i * 10) - proof := NewMerkleProof(randHeight, [][32]byte{}, 0x01, randVal.Uint64()%100000+1, randVal.Uint64()%10+1, uint32(i), accA.Address, accB.Address, nil) + proof := NewMerkleProof(randHeight, [][33]byte{}, 0x01, randVal.Uint64()%100000+1, randVal.Uint64()%10+1, uint32(i), accA.Address, accB.Address, nil) merkleTreeDepth := int(rand.Uint32() % 10) + 1 for j:= 0; j < merkleTreeDepth; j++ { - var mhash [32]byte - randVal.Read(mhash[:]) + leftOrRightNumber := int(rand.Uint32() % 2) + + var mhash [33]byte + var leftOrRight [1]byte + + if leftOrRightNumber == 0 { + leftOrRight = [1]byte{'l'} + } else { + leftOrRight = [1]byte{'r'} + } + + copy(mhash[0:1], leftOrRight[:]) + randVal.Read(mhash[1:33]) proof.MHashes = append(proof.MHashes, mhash) } From 40a0d7fbcebcc8aa2065ed64717ef59ee39edbdc Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Tue, 4 Dec 2018 09:41:45 +0100 Subject: [PATCH 08/28] WIP: implement SCP miner verification --- miner/state.go | 55 +++++++++++++++++++++++++++++++++++++++++++++ miner/state_test.go | 31 +++++++++++++++++++++++++ protocol/fundstx.go | 5 +++++ storage/storage.go | 2 +- 4 files changed, 92 insertions(+), 1 deletion(-) diff --git a/miner/state.go b/miner/state.go index 96f8fbe..ad2f4ee 100644 --- a/miner/state.go +++ b/miner/state.go @@ -269,6 +269,8 @@ func accStateChange(txSlice []*protocol.ContractTx) { } func fundsStateChange(txSlice []*protocol.FundsTx) (err error) { + previousBlocks := storage.ReadAllClosedBlocks() + for _, tx := range txSlice { var rootAcc *protocol.Account //Check if we have to issue new coins (in case a root account signed the tx) @@ -334,11 +336,64 @@ func fundsStateChange(txSlice []*protocol.FundsTx) (err error) { accSender.TxCnt += 1 accSender.Balance -= tx.Amount accReceiver.Balance += tx.Amount + + // Only verify SCP if sender is no root + if rootAcc == nil { + // TODO: @rmnblm testing purposes + if verified, err := verifySCP(tx, previousBlocks); verified && err == nil { + logger.Printf("self-contained proof is valid\n") + } else { + logger.Printf("self-contained proof is invalid\n") + } + } } return nil } +func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (verified bool, err error) { + scp := tx.Proof + blockIndex := 0 + var balance int64 = 0 + + for _, proof := range scp.Proofs { + for ; blockIndex < len(previousBlocks); blockIndex++ { + currentBlock := previousBlocks[blockIndex] + + // Bloomfilter check + if currentBlock.BloomFilter.Test(tx.From[:]) && proof.Height != currentBlock.Height { + return false, errors.New(fmt.Sprintf("SCP does not contain a proof for block height %v\n", currentBlock.Height)) + } + + if proof.Height == currentBlock.Height { + merkleRoot, err := proof.CalculateMerkleRoot() + if err != nil { + return false, err + } + + if currentBlock.MerkleRoot != merkleRoot { + return false, errors.New(fmt.Sprintf("Merkle root does not match %x vs. %x\n", currentBlock.MerkleRoot, merkleRoot)) + } + + if proof.PFrom == tx.From { + // Sender + balance -= int64(proof.PAmount) + } else if proof.PTo == tx.From { + // Receipient + balance += int64(proof.PAmount) + } else if currentBlock.Beneficiary == tx.From { + // Beneificiary + // TODO: @rmnblm implement beneficiary + } + + break + } + } + } + + return balance >= int64(tx.Amount), nil +} + //We accept config slices with unknown id, but don't act on the payload. This is in case we have not updated to a new //software with corresponding code to act on the configTx id/payload func configStateChange(configTxSlice []*protocol.ConfigTx, blockHash [32]byte) { diff --git a/miner/state_test.go b/miner/state_test.go index cd69537..2632bb9 100644 --- a/miner/state_test.go +++ b/miner/state_test.go @@ -360,3 +360,34 @@ func TestStakeTxStateChange(t *testing.T) { } } + +func TestVerifySCP(t *testing.T) { + cleanAndPrepare() + + var blocks []*protocol.Block + + b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 0) + tx, _ := protocol.ConstrFundsTx(0x01, 100, 1, uint32(0), accA.Address, accB.Address, PrivKeyAccA, nil) + if err := addTx(b, tx); err != nil { + t.Error(err) + } + b.InitBloomFilter([][64]byte {accA.Address, accB.Address}) + finalizeBlock(b) + blocks = append(blocks, b) + + tx1, _ := protocol.ConstrFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) + scp := protocol.NewSCP() + merkleTree := protocol.BuildMerkleTree(b) + mhashes, err := merkleTree.MerkleProof(tx.Hash()) + if err != nil { + t.Error(err) + } + + merkleProof := protocol.NewMerkleProof(b.Height, mhashes, tx.Header, tx.Amount, tx.Fee, tx.TxCnt, tx.From, tx.To, tx.Data) + scp.AddMerkleProof(&merkleProof) + tx1.Proof = &scp + + if _, err := verifySCP(tx1, blocks); err != nil { + t.Error(err) + } +} \ No newline at end of file diff --git a/protocol/fundstx.go b/protocol/fundstx.go index d9c557f..250d808 100644 --- a/protocol/fundstx.go +++ b/protocol/fundstx.go @@ -23,6 +23,7 @@ type FundsTx struct { To [64]byte Sig [64]byte Data []byte + Proof *SCP } func ConstrFundsTx(header byte, amount uint64, fee uint64, txCnt uint32, from, to [64]byte, sigKey *ecdsa.PrivateKey, data []byte) (tx *FundsTx, err error) { @@ -79,6 +80,9 @@ func (tx *FundsTx) Hash() (hash [32]byte) { //when we serialize the struct with binary.Write, unexported field get serialized as well, undesired //behavior. Therefore, writing own encoder/decoder func (tx *FundsTx) Encode() (encodedTx []byte) { + gob.Register(SCP{}) + gob.Register(MerkleProof{}) + // Encode encodeData := FundsTx{ tx.Header, @@ -89,6 +93,7 @@ func (tx *FundsTx) Encode() (encodedTx []byte) { tx.To, tx.Sig, tx.Data, + tx.Proof, } buffer := new(bytes.Buffer) gob.NewEncoder(buffer).Encode(encodeData) diff --git a/storage/storage.go b/storage/storage.go index b751bcc..0148028 100644 --- a/storage/storage.go +++ b/storage/storage.go @@ -67,7 +67,7 @@ func Init(dbname string, bootstrapIpport string) error { func CreateBucket(bucketName string, db *bolt.DB) (err error) { return db.Update(func(tx *bolt.Tx) error { - _, err = tx.CreateBucket([]byte(bucketName)) + _, err = tx.CreateBucketIfNotExists([]byte(bucketName)) if err != nil { return fmt.Errorf(ERROR_MSG + " %s", err) } From 886399fc113f5eec12386098ecdf6729145155a0 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Tue, 4 Dec 2018 10:16:21 +0100 Subject: [PATCH 09/28] WIP: extend SCP verification tests --- miner/state.go | 27 ++++++++++++++++----------- miner/state_test.go | 33 ++++++++++++++++++++++++++++++--- 2 files changed, 46 insertions(+), 14 deletions(-) diff --git a/miner/state.go b/miner/state.go index ad2f4ee..a7f739e 100644 --- a/miner/state.go +++ b/miner/state.go @@ -340,10 +340,11 @@ func fundsStateChange(txSlice []*protocol.FundsTx) (err error) { // Only verify SCP if sender is no root if rootAcc == nil { // TODO: @rmnblm testing purposes - if verified, err := verifySCP(tx, previousBlocks); verified && err == nil { - logger.Printf("self-contained proof is valid\n") + if err := verifySCP(tx, previousBlocks); err != nil { + logger.Printf("self-contained proof is invalid: %v\n", err) + // return err } else { - logger.Printf("self-contained proof is invalid\n") + logger.Printf("self-contained proof is valid\n") } } } @@ -351,10 +352,10 @@ func fundsStateChange(txSlice []*protocol.FundsTx) (err error) { return nil } -func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (verified bool, err error) { +func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err error) { scp := tx.Proof blockIndex := 0 - var balance int64 = 0 + var verifiedBalance int64 = 0 for _, proof := range scp.Proofs { for ; blockIndex < len(previousBlocks); blockIndex++ { @@ -362,25 +363,25 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (verified // Bloomfilter check if currentBlock.BloomFilter.Test(tx.From[:]) && proof.Height != currentBlock.Height { - return false, errors.New(fmt.Sprintf("SCP does not contain a proof for block height %v\n", currentBlock.Height)) + return errors.New(fmt.Sprintf("SCP does not contain a proof for block height %v\n", currentBlock.Height)) } if proof.Height == currentBlock.Height { merkleRoot, err := proof.CalculateMerkleRoot() if err != nil { - return false, err + return err } if currentBlock.MerkleRoot != merkleRoot { - return false, errors.New(fmt.Sprintf("Merkle root does not match %x vs. %x\n", currentBlock.MerkleRoot, merkleRoot)) + return errors.New(fmt.Sprintf("Merkle root does not match %x vs. %x\n", currentBlock.MerkleRoot, merkleRoot)) } if proof.PFrom == tx.From { // Sender - balance -= int64(proof.PAmount) + verifiedBalance -= int64(proof.PAmount) } else if proof.PTo == tx.From { // Receipient - balance += int64(proof.PAmount) + verifiedBalance += int64(proof.PAmount) } else if currentBlock.Beneficiary == tx.From { // Beneificiary // TODO: @rmnblm implement beneficiary @@ -391,7 +392,11 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (verified } } - return balance >= int64(tx.Amount), nil + if verifiedBalance < int64(tx.Amount) { + return errors.New(fmt.Sprintf("verified balance less than amount (%v < %v) spent by acc %x\n", verifiedBalance, tx.Amount, tx.From[0:8])) + } + + return nil } //We accept config slices with unknown id, but don't act on the payload. This is in case we have not updated to a new diff --git a/miner/state_test.go b/miner/state_test.go index 2632bb9..b6f289a 100644 --- a/miner/state_test.go +++ b/miner/state_test.go @@ -366,6 +366,7 @@ func TestVerifySCP(t *testing.T) { var blocks []*protocol.Block + // Setup test by transferring 100 coins to accB b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 0) tx, _ := protocol.ConstrFundsTx(0x01, 100, 1, uint32(0), accA.Address, accB.Address, PrivKeyAccA, nil) if err := addTx(b, tx); err != nil { @@ -375,19 +376,45 @@ func TestVerifySCP(t *testing.T) { finalizeBlock(b) blocks = append(blocks, b) - tx1, _ := protocol.ConstrFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) + // Create new SCP scp := protocol.NewSCP() merkleTree := protocol.BuildMerkleTree(b) mhashes, err := merkleTree.MerkleProof(tx.Hash()) if err != nil { t.Error(err) } - merkleProof := protocol.NewMerkleProof(b.Height, mhashes, tx.Header, tx.Amount, tx.Fee, tx.TxCnt, tx.From, tx.To, tx.Data) scp.AddMerkleProof(&merkleProof) + + // Create transaction that contains SCP + tx1, _ := protocol.ConstrFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) tx1.Proof = &scp + if err := verifySCP(tx1, blocks); err != nil { + t.Error(err) + } - if _, err := verifySCP(tx1, blocks); err != nil { + // Create new block + b1 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + if err := addTx(b1, tx1); err != nil { t.Error(err) } + b1.InitBloomFilter([][64]byte {accA.Address, accB.Address}) + finalizeBlock(b1) + blocks = append(blocks, b1) + + merkleTree1 := protocol.BuildMerkleTree(b1) + mhashes1, err := merkleTree1.MerkleProof(tx1.Hash()) + if err != nil { + t.Error(err) + } + merkleProof1 := protocol.NewMerkleProof(b1.Height, mhashes1, tx1.Header, tx1.Amount, tx1.Fee, tx1.TxCnt, tx1.From, tx1.To, tx1.Data) + scp.AddMerkleProof(&merkleProof1) + + // Create another transaction that contains SCP + tx2, _ := protocol.ConstrFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) + tx2.Proof = &scp + if err := verifySCP(tx2, blocks); err == nil { + t.Error("self-contained proof should be invalid (double spending) but verifySCP returns no error") + } + } \ No newline at end of file From ed246d92255c332e7537ff2fe9ff5391ac67be43 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Tue, 4 Dec 2018 11:55:16 +0100 Subject: [PATCH 10/28] nil check --- miner/state.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/miner/state.go b/miner/state.go index a7f739e..d2c005b 100644 --- a/miner/state.go +++ b/miner/state.go @@ -354,6 +354,10 @@ func fundsStateChange(txSlice []*protocol.FundsTx) (err error) { func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err error) { scp := tx.Proof + if scp == nil { + return errors.New("FundsTx does not contain a self-contained proof") + } + blockIndex := 0 var verifiedBalance int64 = 0 From 365706945e306bd4751b3d974c8ce00b0d305d18 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Tue, 4 Dec 2018 22:38:50 +0100 Subject: [PATCH 11/28] set TotalFees in block header --- miner/blockpreparation.go | 6 ++++++ protocol/block.go | 11 +++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/miner/blockpreparation.go b/miner/blockpreparation.go index 8944e8c..f47cdc8 100644 --- a/miner/blockpreparation.go +++ b/miner/blockpreparation.go @@ -24,6 +24,8 @@ func prepareBlock(block *protocol.Block) { sort.Sort(tmpCopy) + var totalFees uint64 = 0 + for _, tx := range opentxs { //Prevent block size to overflow. if block.GetSize()+tx.Size() > activeParameters.Block_size { @@ -35,7 +37,11 @@ func prepareBlock(block *protocol.Block) { //If the tx is invalid, we remove it completely, prevents starvation in the mempool. storage.DeleteOpenTx(tx) } + + totalFees += tx.TxFee() } + + block.TotalFees = totalFees } //Implement the sort interface diff --git a/protocol/block.go b/protocol/block.go index 302926e..055a5eb 100644 --- a/protocol/block.go +++ b/protocol/block.go @@ -12,7 +12,7 @@ import ( const ( TXHASH_LEN = 32 HEIGHT_LEN = 4 - MIN_BLOCKHEADER_SIZE = 136 + MIN_BLOCKHEADER_SIZE = 144 MIN_BLOCKSIZE = 184 + MIN_BLOCKHEADER_SIZE + crypto.COMM_PROOF_LENGTH BLOOM_FILTER_ERROR_RATE = 0.1 ) @@ -27,6 +27,7 @@ type Block struct { BloomFilter *bloom.BloomFilter Height uint32 Beneficiary [64]byte + TotalFees uint64 //Body Nonce [8]byte @@ -68,6 +69,7 @@ func (block *Block) HashBlock() [32]byte { timestamp int64 merkleRoot [32]byte beneficiary [64]byte + totalFees uint64 commitmentProof [crypto.COMM_PROOF_LENGTH]byte slashedAddress [64]byte conflictingBlockHash1 [32]byte @@ -77,6 +79,7 @@ func (block *Block) HashBlock() [32]byte { block.Timestamp, block.MerkleRoot, block.Beneficiary, + block.TotalFees, block.CommitmentProof, block.SlashedAddress, block.ConflictingBlockHash1, @@ -125,8 +128,9 @@ func (block *Block) Encode() []byte { Nonce: block.Nonce, Timestamp: block.Timestamp, MerkleRoot: block.MerkleRoot, + TotalFees: block.TotalFees, Beneficiary: block.Beneficiary, - NrContractTx: block.NrContractTx, + NrContractTx: block.NrContractTx, NrFundsTx: block.NrFundsTx, NrConfigTx: block.NrConfigTx, NrStakeTx: block.NrStakeTx, @@ -163,6 +167,7 @@ func (block *Block) EncodeHeader() []byte { BloomFilter: block.BloomFilter, Height: block.Height, Beneficiary: block.Beneficiary, + TotalFees: block.TotalFees, } buffer := new(bytes.Buffer) @@ -189,6 +194,7 @@ func (block Block) String() string { "Timestamp: %v\n"+ "MerkleRoot: %x\n"+ "Beneficiary: %x\n"+ + "Total Fees: %v\n"+ "Amount of fundsTx: %v\n"+ "Amount of contractTx: %v\n"+ "Amount of configTx: %v\n"+ @@ -204,6 +210,7 @@ func (block Block) String() string { block.Timestamp, block.MerkleRoot[0:8], block.Beneficiary[0:8], + block.TotalFees, block.NrFundsTx, block.NrContractTx, block.NrConfigTx, From aab07bf6c6678096884e800ddd17b19c090edf0f Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Tue, 4 Dec 2018 22:51:11 +0100 Subject: [PATCH 12/28] add block beneficiary to BF --- storage/utils.go | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/utils.go b/storage/utils.go index f602c0c..6a05f11 100644 --- a/storage/utils.go +++ b/storage/utils.go @@ -19,6 +19,7 @@ func IsRootKey(pubKey [64]byte) bool { func GetTxPubKeys(block *protocol.Block) (txPubKeys [][64]byte) { txPubKeys = GetContractTxPubKeys(block.ContractTxData) txPubKeys = append(txPubKeys, GetFundsTxPubKeys(block.FundsTxData)...) + txPubKeys = append(txPubKeys, block.Beneficiary) return txPubKeys } From 5ee0eccec80e39f88e9841f2d240f4939ec02e61 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Wed, 5 Dec 2018 15:26:25 +0100 Subject: [PATCH 13/28] remove SCP data structure --- miner/block.go | 5 +-- miner/state.go | 11 +---- miner/state_test.go | 7 +-- protocol/fundstx.go | 5 +-- protocol/fundstx_test.go | 62 ++++++++++++++++++++++++++ protocol/scp.go | 65 --------------------------- protocol/scp_test.go | 96 ---------------------------------------- 7 files changed, 69 insertions(+), 182 deletions(-) delete mode 100644 protocol/scp.go delete mode 100644 protocol/scp_test.go diff --git a/miner/block.go b/miner/block.go index a4e4392..f167ec4 100644 --- a/miner/block.go +++ b/miner/block.go @@ -535,10 +535,7 @@ func validate(b *protocol.Block, initialSetup bool) error { postValidate(blockDataMap[block.Hash], initialSetup) } - err = deleteZeroBalanceAccounts() - if err != nil { - return err - } + deleteZeroBalanceAccounts() return nil } diff --git a/miner/state.go b/miner/state.go index d2c005b..8cc9859 100644 --- a/miner/state.go +++ b/miner/state.go @@ -353,15 +353,10 @@ func fundsStateChange(txSlice []*protocol.FundsTx) (err error) { } func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err error) { - scp := tx.Proof - if scp == nil { - return errors.New("FundsTx does not contain a self-contained proof") - } - blockIndex := 0 var verifiedBalance int64 = 0 - for _, proof := range scp.Proofs { + for _, proof := range tx.Proofs { for ; blockIndex < len(previousBlocks); blockIndex++ { currentBlock := previousBlocks[blockIndex] @@ -597,7 +592,7 @@ func updateStakingHeight(block *protocol.Block) error { return nil } -func deleteZeroBalanceAccounts() error { +func deleteZeroBalanceAccounts() { for _, acc := range storage.State { if acc.Balance > 0 || acc.Contract != nil { continue @@ -605,6 +600,4 @@ func deleteZeroBalanceAccounts() error { storage.DeleteAccount(acc.Address) } - - return nil } diff --git a/miner/state_test.go b/miner/state_test.go index b6f289a..65e4423 100644 --- a/miner/state_test.go +++ b/miner/state_test.go @@ -377,18 +377,16 @@ func TestVerifySCP(t *testing.T) { blocks = append(blocks, b) // Create new SCP - scp := protocol.NewSCP() merkleTree := protocol.BuildMerkleTree(b) mhashes, err := merkleTree.MerkleProof(tx.Hash()) if err != nil { t.Error(err) } merkleProof := protocol.NewMerkleProof(b.Height, mhashes, tx.Header, tx.Amount, tx.Fee, tx.TxCnt, tx.From, tx.To, tx.Data) - scp.AddMerkleProof(&merkleProof) // Create transaction that contains SCP tx1, _ := protocol.ConstrFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) - tx1.Proof = &scp + tx1.Proofs = append(tx1.Proofs, &merkleProof) if err := verifySCP(tx1, blocks); err != nil { t.Error(err) } @@ -408,11 +406,10 @@ func TestVerifySCP(t *testing.T) { t.Error(err) } merkleProof1 := protocol.NewMerkleProof(b1.Height, mhashes1, tx1.Header, tx1.Amount, tx1.Fee, tx1.TxCnt, tx1.From, tx1.To, tx1.Data) - scp.AddMerkleProof(&merkleProof1) // Create another transaction that contains SCP tx2, _ := protocol.ConstrFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) - tx2.Proof = &scp + tx2.Proofs = append(tx2.Proofs, &merkleProof1, &merkleProof) if err := verifySCP(tx2, blocks); err == nil { t.Error("self-contained proof should be invalid (double spending) but verifySCP returns no error") } diff --git a/protocol/fundstx.go b/protocol/fundstx.go index 250d808..4497a02 100644 --- a/protocol/fundstx.go +++ b/protocol/fundstx.go @@ -23,7 +23,7 @@ type FundsTx struct { To [64]byte Sig [64]byte Data []byte - Proof *SCP + Proofs []*MerkleProof } func ConstrFundsTx(header byte, amount uint64, fee uint64, txCnt uint32, from, to [64]byte, sigKey *ecdsa.PrivateKey, data []byte) (tx *FundsTx, err error) { @@ -80,7 +80,6 @@ func (tx *FundsTx) Hash() (hash [32]byte) { //when we serialize the struct with binary.Write, unexported field get serialized as well, undesired //behavior. Therefore, writing own encoder/decoder func (tx *FundsTx) Encode() (encodedTx []byte) { - gob.Register(SCP{}) gob.Register(MerkleProof{}) // Encode @@ -93,7 +92,7 @@ func (tx *FundsTx) Encode() (encodedTx []byte) { tx.To, tx.Sig, tx.Data, - tx.Proof, + tx.Proofs, } buffer := new(bytes.Buffer) gob.NewEncoder(buffer).Encode(encodeData) diff --git a/protocol/fundstx_test.go b/protocol/fundstx_test.go index a3879c6..375248a 100644 --- a/protocol/fundstx_test.go +++ b/protocol/fundstx_test.go @@ -12,6 +12,20 @@ func TestFundsTxSerialization(t *testing.T) { loopMax := int(rand.Uint32() % 10000) for i := 0; i < loopMax; i++ { tx, _ := ConstrFundsTx(0x01, rand.Uint64()%100000+1, rand.Uint64()%10+1, uint32(i), accA.Address, accB.Address, PrivKeyA, nil) + + var merkleRootsBefore [][32]byte + proofs := getDummyProofs() + + for i := 0; i < len(proofs); i++ { + merkleRoot, err := proofs[i].CalculateMerkleRoot() + if err != nil { + t.Error("failed to calculate merkle root") + } + merkleRootsBefore = append(merkleRootsBefore, merkleRoot) + } + + tx.Proofs = proofs + data := tx.Encode() var decodedTx *FundsTx decodedTx = decodedTx.Decode(data) @@ -23,5 +37,53 @@ func TestFundsTxSerialization(t *testing.T) { if !reflect.DeepEqual(tx, decodedTx) { t.Errorf("FundsTx Serialization failed (%v) vs. (%v)\n", tx, decodedTx) } + + for i := 0; i < len(proofs); i++ { + if !reflect.DeepEqual(decodedTx.Proofs[i], tx.Proofs[i]) { + t.Errorf("Proof does not match the given one: %v vs. %v", decodedTx.Proofs[i].String(), tx.Proofs[i].String()) + } + + merkleRootBefore := merkleRootsBefore[i] + merkleRootAfter, err := decodedTx.Proofs[i].CalculateMerkleRoot() + if err != nil { + t.Error("failed to calculate merkle root") + } + + if merkleRootBefore != merkleRootAfter { + t.Errorf("Merkle proof serialization failed: Merkle roots not the same \n(%v)\nvs.\n(%v)\n", merkleRootAfter, merkleRootsBefore[i]) + } + } } } + +func getDummyProofs() (proofs []*MerkleProof) { + randVal := rand.New(rand.NewSource(time.Now().Unix())) + nofProofs := int(randVal.Uint32() % 10) + 1 + + for i := 0; i < nofProofs; i++ { + randHeight := (randVal.Uint32() % 10) + uint32(i * 10) + proof := NewMerkleProof(randHeight, [][33]byte{}, 0x01, randVal.Uint64()%100000+1, randVal.Uint64()%10+1, uint32(i), accA.Address, accB.Address, nil) + + merkleTreeDepth := int(rand.Uint32() % 10) + 1 + for j:= 0; j < merkleTreeDepth; j++ { + leftOrRightNumber := int(rand.Uint32() % 2) + + var mhash [33]byte + var leftOrRight [1]byte + + if leftOrRightNumber == 0 { + leftOrRight = [1]byte{'l'} + } else { + leftOrRight = [1]byte{'r'} + } + + copy(mhash[0:1], leftOrRight[:]) + randVal.Read(mhash[1:33]) + proof.MHashes = append(proof.MHashes, mhash) + } + + proofs = append(proofs, &proof) + } + + return proofs +} \ No newline at end of file diff --git a/protocol/scp.go b/protocol/scp.go deleted file mode 100644 index c11e3c7..0000000 --- a/protocol/scp.go +++ /dev/null @@ -1,65 +0,0 @@ -package protocol - -import ( - "bytes" - "encoding/gob" -) - -type SCP struct { - Proofs []*MerkleProof -} - -func NewSCP() SCP { - return SCP{} -} - -func (scp *SCP) AddMerkleProof(proof *MerkleProof) (index int) { - scp.Proofs = append(scp.Proofs, proof) - return len(scp.Proofs) - 1 -} - -func (scp *SCP) CalculateMerkleRootFor(index int) (merkleRoot [32]byte, err error) { - return scp.Proofs[index].CalculateMerkleRoot() - -} - -func (scp *SCP) ProofCount() int { - return len(scp.Proofs) -} - -func (scp *SCP) Hash() (hash [32]byte) { - if scp == nil { - return [32]byte{} - } - - var proofHashes [][32]byte - for _, proof := range scp.Proofs { - proofHashes = append(proofHashes, proof.Hash()) - } - - return SerializeHashContent(proofHashes) -} - -func (scp *SCP) Encode() (encodedTx []byte) { - gob.Register(MerkleProof{}) - - encodeData := SCP{ - scp.Proofs, - } - - buffer := new(bytes.Buffer) - gob.NewEncoder(buffer).Encode(encodeData) - return buffer.Bytes() -} - -func (scp *SCP) Decode(encoded []byte) *SCP { - var decoded SCP - buffer := bytes.NewBuffer(encoded) - decoder := gob.NewDecoder(buffer) - decoder.Decode(&decoded) - return &decoded -} - -func (scp *SCP) StringFor(index int) string { - return scp.Proofs[index].String() -} diff --git a/protocol/scp_test.go b/protocol/scp_test.go deleted file mode 100644 index 20f9c6a..0000000 --- a/protocol/scp_test.go +++ /dev/null @@ -1,96 +0,0 @@ -package protocol - -import ( - "math/rand" - "reflect" - "testing" - "time" -) - -func TestSCPSerialization(t *testing.T) { - var merkleRootsBefore [][32]byte - nofProofs, scp := getDummySCP() - - for i := 0; i < nofProofs; i++ { - merkleRoot, err := scp.CalculateMerkleRootFor(i) - if err != nil { - t.Error("failed to calculate merkle root") - } - merkleRootsBefore = append(merkleRootsBefore, merkleRoot) - } - - encoded := scp.Encode() - - var decoded *SCP - decoded = decoded.Decode(encoded) - - for i := 0; i < nofProofs; i++ { - if !reflect.DeepEqual(scp.Proofs[i], decoded.Proofs[i]) { - t.Errorf("Proof does not match the given one: %v vs. %v", scp.Proofs[i].String(), decoded.Proofs[i].String()) - } - - merkleRootBefore := merkleRootsBefore[i] - merkleRootAfter, err := decoded.CalculateMerkleRootFor(i) - if err != nil { - t.Error("failed to calculate merkle root") - } - - if merkleRootBefore != merkleRootAfter { - t.Errorf("SCP serialization failed: Merkle roots not the same \n(%v)\nvs.\n(%v)\n", merkleRootAfter, merkleRootsBefore[i]) - } - } -} - -func TestSCPHash(t *testing.T) { - _, scp1 := getDummySCP() - scp1Hash1 := scp1.Hash() - - if !reflect.DeepEqual(scp1Hash1, scp1.Hash()) { - t.Errorf("SCP hashing failed!") - } - - scp1.Proofs[0].Height++ - scp1Hash2 := scp1.Hash() - - if reflect.DeepEqual(scp1Hash1, scp1Hash2) { - t.Errorf("SCP hashing failed!") - } - - _, scp2 := getDummySCP() - if reflect.DeepEqual(scp1Hash1, scp2.Hash()) { - t.Errorf("SCP hashing failed!") - } -} - -func getDummySCP() (int, *SCP) { - randVal := rand.New(rand.NewSource(time.Now().Unix())) - nofProofs := int(randVal.Uint32() % 10) + 1 - - scp := NewSCP() - for i := 0; i < nofProofs; i++ { - randHeight := (randVal.Uint32() % 10) + uint32(i * 10) - proof := NewMerkleProof(randHeight, [][33]byte{}, 0x01, randVal.Uint64()%100000+1, randVal.Uint64()%10+1, uint32(i), accA.Address, accB.Address, nil) - - merkleTreeDepth := int(rand.Uint32() % 10) + 1 - for j:= 0; j < merkleTreeDepth; j++ { - leftOrRightNumber := int(rand.Uint32() % 2) - - var mhash [33]byte - var leftOrRight [1]byte - - if leftOrRightNumber == 0 { - leftOrRight = [1]byte{'l'} - } else { - leftOrRight = [1]byte{'r'} - } - - copy(mhash[0:1], leftOrRight[:]) - randVal.Read(mhash[1:33]) - proof.MHashes = append(proof.MHashes, mhash) - } - - scp.AddMerkleProof(&proof) - } - - return nofProofs, &scp -} \ No newline at end of file From 3f61ba754d2bb690be879c10d06ea4e099f3a0e3 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Wed, 5 Dec 2018 17:57:54 +0100 Subject: [PATCH 14/28] refactor return value --- miner/block.go | 50 ++++++++++++++++++++++++++------------------------ miner/state.go | 6 +++--- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/miner/block.go b/miner/block.go index f167ec4..073be82 100644 --- a/miner/block.go +++ b/miner/block.go @@ -515,7 +515,7 @@ func validate(b *protocol.Block, initialSetup bool) error { for _, block := range blocksToValidate { //Fetching payload data from the txs (if necessary, ask other miners). - contractTxs, fundsTxs, configTxs, stakeTxs, err := preValidate(block, initialSetup) + data, err := preValidate(block, initialSetup) //Check if the validator that added the block has previously voted on different competing chains (find slashing proof). //The proof will be stored in the global slashing dictionary. @@ -527,7 +527,7 @@ func validate(b *protocol.Block, initialSetup bool) error { return err } - blockDataMap[block.Hash] = blockData{contractTxs, fundsTxs, configTxs, stakeTxs, block} + blockDataMap[block.Hash] = *data if err := validateState(blockDataMap[block.Hash]); err != nil { return err } @@ -541,43 +541,43 @@ func validate(b *protocol.Block, initialSetup bool) error { } //Doesn't involve any state changes. -func preValidate(block *protocol.Block, initialSetup bool) (contractTxSlice []*protocol.ContractTx, fundsTxSlice []*protocol.FundsTx, configTxSlice []*protocol.ConfigTx, stakeTxSlice []*protocol.StakeTx, err error) { +func preValidate(block *protocol.Block, initialSetup bool) (data *blockData, err error) { //This dynamic check is only done if we're up-to-date with syncing, otherwise timestamp is not checked. //Other miners (which are up-to-date) made sure that this is correct. if !initialSetup && uptodate { if err := timestampCheck(block.Timestamp); err != nil { - return nil, nil, nil, nil, err + return nil, err } } //Check block size. if block.GetSize() > activeParameters.Block_size { - return nil, nil, nil, nil, errors.New("Block size too large.") + return nil, errors.New("Block size too large.") } //Duplicates are not allowed, use tx hash hashmap to easily check for duplicates. duplicates := make(map[[32]byte]bool) for _, txHash := range block.ContractTxData { if _, exists := duplicates[txHash]; exists { - return nil, nil, nil, nil, errors.New("Duplicate Account Transaction Hash detected.") + return nil, errors.New("Duplicate Account Transaction Hash detected.") } duplicates[txHash] = true } for _, txHash := range block.FundsTxData { if _, exists := duplicates[txHash]; exists { - return nil, nil, nil, nil, errors.New("Duplicate Funds Transaction Hash detected.") + return nil, errors.New("Duplicate Funds Transaction Hash detected.") } duplicates[txHash] = true } for _, txHash := range block.ConfigTxData { if _, exists := duplicates[txHash]; exists { - return nil, nil, nil, nil, errors.New("Duplicate Config Transaction Hash detected.") + return nil, errors.New("Duplicate Config Transaction Hash detected.") } duplicates[txHash] = true } for _, txHash := range block.StakeTxData { if _, exists := duplicates[txHash]; exists { - return nil, nil, nil, nil, errors.New("Duplicate Stake Transaction Hash detected.") + return nil, errors.New("Duplicate Stake Transaction Hash detected.") } duplicates[txHash] = true } @@ -586,10 +586,10 @@ func preValidate(block *protocol.Block, initialSetup bool) (contractTxSlice []*p errChan := make(chan error, 4) //We need to allocate slice space for the underlying array when we pass them as reference. - contractTxSlice = make([]*protocol.ContractTx, block.NrContractTx) - fundsTxSlice = make([]*protocol.FundsTx, block.NrFundsTx) - configTxSlice = make([]*protocol.ConfigTx, block.NrConfigTx) - stakeTxSlice = make([]*protocol.StakeTx, block.NrStakeTx) + contractTxSlice := make([]*protocol.ContractTx, block.NrContractTx) + fundsTxSlice := make([]*protocol.FundsTx, block.NrFundsTx) + configTxSlice := make([]*protocol.ConfigTx, block.NrConfigTx) + stakeTxSlice := make([]*protocol.StakeTx, block.NrStakeTx) go fetchContractTxData(block, contractTxSlice, initialSetup, errChan) go fetchFundsTxData(block, fundsTxSlice, initialSetup, errChan) @@ -600,19 +600,21 @@ func preValidate(block *protocol.Block, initialSetup bool) (contractTxSlice []*p for cnt := 0; cnt < 4; cnt++ { err = <-errChan if err != nil { - return nil, nil, nil, nil, err + return nil, err } } + data = &blockData{contractTxSlice, fundsTxSlice, configTxSlice, stakeTxSlice, block} + //Check state contains beneficiary. acc, err := storage.ReadAccount(block.Beneficiary) if err != nil { - return nil, nil, nil, nil, err + return nil, err } //Check if node is part of the validator set. if !acc.IsStaking { - return nil, nil, nil, nil, errors.New(fmt.Sprintf("Validator (%x) is not part of the validator set.", acc.Address[0:8])) + return nil, errors.New(fmt.Sprintf("Validator (%x) is not part of the validator set.", acc.Address[0:8])) } //First, initialize an RSA Public Key instance with the modulus of the proposer of the block (acc) @@ -620,12 +622,12 @@ func preValidate(block *protocol.Block, initialSetup bool) (contractTxSlice []*p //Invalid if the commitment proof can not be verified with the public key of the proposer commitmentPubKey, err := crypto.CreateRSAPubKeyFromBytes(acc.CommitmentKey) if err != nil { - return nil, nil, nil, nil, errors.New("Invalid commitment key in account.") + return nil, errors.New("Invalid commitment key in account.") } err = crypto.VerifyMessageWithRSAKey(commitmentPubKey, fmt.Sprint(block.Height), block.CommitmentProof) if err != nil { - return nil, nil, nil, nil, errors.New("The submitted commitment proof can not be verified.") + return nil, errors.New("The submitted commitment proof can not be verified.") } //Invalid if PoS calculation is not correct. @@ -633,33 +635,33 @@ func preValidate(block *protocol.Block, initialSetup bool) (contractTxSlice []*p //PoS validation if !validateProofOfStake(getDifficulty(), prevProofs, block.Height, acc.Balance, block.CommitmentProof, block.Timestamp) { - return nil, nil, nil, nil, errors.New("The nonce is incorrect.") + return nil, errors.New("The nonce is incorrect.") } //Invalid if PoS is too far in the future. now := time.Now() if block.Timestamp > now.Unix()+int64(activeParameters.Accepted_time_diff) { - return nil, nil, nil, nil, errors.New("The timestamp is too far in the future. " + string(block.Timestamp) + " vs " + string(now.Unix())) + return nil, errors.New("The timestamp is too far in the future. " + string(block.Timestamp) + " vs " + string(now.Unix())) } //Check for minimum waiting time. if block.Height-acc.StakingBlockHeight < uint32(activeParameters.Waiting_minimum) { - return nil, nil, nil, nil, errors.New("The miner must wait a minimum amount of blocks before start validating. Block Height:" + fmt.Sprint(block.Height) + " - Height when started validating " + string(acc.StakingBlockHeight) + " MinWaitingTime: " + string(activeParameters.Waiting_minimum)) + return nil, errors.New("The miner must wait a minimum amount of blocks before start validating. Block Height:" + fmt.Sprint(block.Height) + " - Height when started validating " + string(acc.StakingBlockHeight) + " MinWaitingTime: " + string(activeParameters.Waiting_minimum)) } //Check if block contains a proof for two conflicting block hashes, else no proof provided. if block.SlashedAddress != [64]byte{} { if _, err = slashingCheck(block.SlashedAddress, block.ConflictingBlockHash1, block.ConflictingBlockHash2); err != nil { - return nil, nil, nil, nil, err + return nil, err } } //Merkle Tree validation if protocol.BuildMerkleTree(block).MerkleRoot() != block.MerkleRoot { - return nil, nil, nil, nil, errors.New("Merkle Root is incorrect.") + return nil, errors.New("Merkle Root is incorrect.") } - return contractTxSlice, fundsTxSlice, configTxSlice, stakeTxSlice, err + return data, err } //Dynamic state check. diff --git a/miner/state.go b/miner/state.go index 8cc9859..50ad475 100644 --- a/miner/state.go +++ b/miner/state.go @@ -229,12 +229,12 @@ func validateClosedBlocks() error { //Do not validate the genesis block, since a lot of properties are set to nil if blockToValidate.Hash != [32]byte{} { //Fetching payload data from the txs (if necessary, ask other miners) - contractTxs, fundsTxs, configTxs, stakeTxs, err := preValidate(blockToValidate, true) + data, err := preValidate(blockToValidate, true) if err != nil { return errors.New(fmt.Sprintf("Block (%x) could not be prevalidated: %v\n", blockToValidate.Hash[0:8], err)) } - blockDataMap[blockToValidate.Hash] = blockData{contractTxs, fundsTxs, configTxs, stakeTxs, blockToValidate} + blockDataMap[blockToValidate.Hash] = *data err = validateState(blockDataMap[blockToValidate.Hash]) if err != nil { @@ -383,7 +383,7 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err erro verifiedBalance += int64(proof.PAmount) } else if currentBlock.Beneficiary == tx.From { // Beneificiary - // TODO: @rmnblm implement beneficiary + verifiedBalance += int64(currentBlock.TotalFees) } break From 73dd269514b3e8169ccbf046dbd78cd003ffb67a Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Wed, 5 Dec 2018 17:58:09 +0100 Subject: [PATCH 15/28] add total fees validation --- miner/block.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/miner/block.go b/miner/block.go index 073be82..f144e98 100644 --- a/miner/block.go +++ b/miner/block.go @@ -656,6 +656,24 @@ func preValidate(block *protocol.Block, initialSetup bool) (data *blockData, err } } + // Total Fees validation + var totalFees uint64 = 0 + for _, tx := range contractTxSlice { + totalFees += tx.Fee + } + for _, tx := range fundsTxSlice { + totalFees += tx.Fee + } + for _, tx := range configTxSlice { + totalFees += tx.Fee + } + for _, tx := range stakeTxSlice { + totalFees += tx.Fee + } + if totalFees != block.TotalFees { + return nil, errors.New(fmt.Sprintf("computed total fees do not equal the block's total fees %vs. %v", totalFees, block.TotalFees)) + } + //Merkle Tree validation if protocol.BuildMerkleTree(block).MerkleRoot() != block.MerkleRoot { return nil, errors.New("Merkle Root is incorrect.") From 1e9da6bc1a56819d1a9afb95d32d0b82350862e5 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Wed, 5 Dec 2018 22:48:10 +0100 Subject: [PATCH 16/28] store bloom filter in local storage --- miner/block.go | 2 +- miner/p2p_interface.go | 7 +------ p2p/response.go | 2 -- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/miner/block.go b/miner/block.go index f144e98..ba05722 100644 --- a/miner/block.go +++ b/miner/block.go @@ -50,8 +50,8 @@ func finalizeBlock(block *protocol.Block) error { } } - //Merkle tree includes the hashes of all txs. block.MerkleRoot = protocol.BuildMerkleTree(block).MerkleRoot() + block.InitBloomFilter(storage.GetTxPubKeys(block)) validatorAcc, err := storage.ReadAccount(validatorAccAddress) if err != nil { diff --git a/miner/p2p_interface.go b/miner/p2p_interface.go index 22db876..b2a426a 100644 --- a/miner/p2p_interface.go +++ b/miner/p2p_interface.go @@ -39,12 +39,7 @@ func processBlock(payload []byte) { //p2p.BlockOut is a channel whose data get consumed by the p2p package func broadcastBlock(block *protocol.Block) { p2p.BlockOut <- block.Encode() - - //Make a deep copy of the block (since it is a pointer and will be saved to db later). - //Otherwise the block's bloom filter is initialized on the original block. - var blockCopy = *block - blockCopy.InitBloomFilter(append(storage.GetTxPubKeys(&blockCopy))) - p2p.BlockHeaderOut <- blockCopy.EncodeHeader() + p2p.BlockHeaderOut <- block.EncodeHeader() } func broadcastVerifiedTxs(txs []*protocol.FundsTx) { diff --git a/p2p/response.go b/p2p/response.go index a0bb77b..6e59b64 100644 --- a/p2p/response.go +++ b/p2p/response.go @@ -93,12 +93,10 @@ func blockHeaderRes(p *peer, payload []byte) { var blockHash [32]byte copy(blockHash[:], payload[:32]) if block := storage.ReadClosedBlock(blockHash); block != nil { - block.InitBloomFilter(append(storage.GetTxPubKeys(block))) encodedHeader = block.EncodeHeader() } } else { if block := storage.ReadLastClosedBlock(); block != nil { - block.InitBloomFilter(append(storage.GetTxPubKeys(block))) encodedHeader = block.EncodeHeader() } } From 09d4c775fb3307fc9e98ec45f3309a8fa2bd0e6b Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Wed, 5 Dec 2018 22:48:29 +0100 Subject: [PATCH 17/28] improve error messages --- miner/block.go | 2 +- miner/state.go | 6 +++--- protocol/fundstx.go | 4 +++- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/miner/block.go b/miner/block.go index ba05722..51e8e6a 100644 --- a/miner/block.go +++ b/miner/block.go @@ -671,7 +671,7 @@ func preValidate(block *protocol.Block, initialSetup bool) (data *blockData, err totalFees += tx.Fee } if totalFees != block.TotalFees { - return nil, errors.New(fmt.Sprintf("computed total fees do not equal the block's total fees %vs. %v", totalFees, block.TotalFees)) + return nil, errors.New(fmt.Sprintf("computed total fees do not equal the block's total fees %v vs. %v", totalFees, block.TotalFees)) } //Merkle Tree validation diff --git a/miner/state.go b/miner/state.go index 50ad475..0652164 100644 --- a/miner/state.go +++ b/miner/state.go @@ -362,7 +362,7 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err erro // Bloomfilter check if currentBlock.BloomFilter.Test(tx.From[:]) && proof.Height != currentBlock.Height { - return errors.New(fmt.Sprintf("SCP does not contain a proof for block height %v\n", currentBlock.Height)) + return errors.New(fmt.Sprintf("SCP does not contain a proof for block height %v", currentBlock.Height)) } if proof.Height == currentBlock.Height { @@ -372,7 +372,7 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err erro } if currentBlock.MerkleRoot != merkleRoot { - return errors.New(fmt.Sprintf("Merkle root does not match %x vs. %x\n", currentBlock.MerkleRoot, merkleRoot)) + return errors.New(fmt.Sprintf("Merkle root does not match %x vs. %x", currentBlock.MerkleRoot, merkleRoot)) } if proof.PFrom == tx.From { @@ -392,7 +392,7 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err erro } if verifiedBalance < int64(tx.Amount) { - return errors.New(fmt.Sprintf("verified balance less than amount (%v < %v) spent by acc %x\n", verifiedBalance, tx.Amount, tx.From[0:8])) + return errors.New(fmt.Sprintf("verified balance less than amount (%v < %v) spent by acc %x", verifiedBalance, tx.Amount, tx.From[0:8])) } return nil diff --git a/protocol/fundstx.go b/protocol/fundstx.go index 4497a02..bed15f8 100644 --- a/protocol/fundstx.go +++ b/protocol/fundstx.go @@ -119,7 +119,8 @@ func (tx FundsTx) String() string { "From: %x\n"+ "To: %x\n"+ "Sig: %x\n"+ - "Data: %v\n", + "Data: %v\n" + + "SCP Length: %v\n", tx.Header, tx.Amount, tx.Fee, @@ -128,5 +129,6 @@ func (tx FundsTx) String() string { tx.To[0:8], tx.Sig[0:8], tx.Data, + len(tx.Proofs), ) } From ad5349d897a33e0807c205c16f821c85295b1f85 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Wed, 5 Dec 2018 22:48:38 +0100 Subject: [PATCH 18/28] continue for-loop if bloomfilter is nil --- miner/state.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/miner/state.go b/miner/state.go index 0652164..7edec6b 100644 --- a/miner/state.go +++ b/miner/state.go @@ -360,6 +360,10 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err erro for ; blockIndex < len(previousBlocks); blockIndex++ { currentBlock := previousBlocks[blockIndex] + if currentBlock.BloomFilter == nil { + continue + } + // Bloomfilter check if currentBlock.BloomFilter.Test(tx.From[:]) && proof.Height != currentBlock.Height { return errors.New(fmt.Sprintf("SCP does not contain a proof for block height %v", currentBlock.Height)) From a5e389a5959d721cdd39861d26966e7ef240f242 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Thu, 6 Dec 2018 09:59:55 +0100 Subject: [PATCH 19/28] fix failing tests :white_check_mark: --- miner/block.go | 3 +-- miner/block_test.go | 7 +++++++ miner/contract_test.go | 2 ++ miner/state_test.go | 2 ++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/miner/block.go b/miner/block.go index 51e8e6a..13a5add 100644 --- a/miner/block.go +++ b/miner/block.go @@ -66,6 +66,7 @@ func finalizeBlock(block *protocol.Block) error { if err != nil { return err } + copy(block.CommitmentProof[0:crypto.COMM_PROOF_LENGTH], commitmentProof[:]) partialHash := block.HashBlock() prevProofs := GetLatestProofs(activeParameters.num_included_prev_proofs, block) @@ -89,8 +90,6 @@ func finalizeBlock(block *protocol.Block) error { block.NrConfigTx = uint8(len(block.ConfigTxData)) block.NrStakeTx = uint16(len(block.StakeTxData)) - copy(block.CommitmentProof[0:crypto.COMM_PROOF_LENGTH], commitmentProof[:]) - return nil } diff --git a/miner/block_test.go b/miner/block_test.go index 1200b2f..1d09c82 100644 --- a/miner/block_test.go +++ b/miner/block_test.go @@ -160,6 +160,8 @@ func createBlockWithTxs(b *protocol.Block) ([][32]byte, [][32]byte, [][32]byte, var hashConfigSlice [][32]byte var hashStakeSlice [][32]byte + var totalFees uint64 = 0 + //in order to create valid funds transactions we need to know the tx count of acc A randVar := rand.New(rand.NewSource(time.Now().Unix())) loopMax := int(randVar.Uint32()%testSize) + 1 @@ -173,6 +175,7 @@ func createBlockWithTxs(b *protocol.Block) ([][32]byte, [][32]byte, [][32]byte, } hashFundsSlice = append(hashFundsSlice, tx.Hash()) storage.WriteOpenTx(tx) + totalFees += tx.Fee } else { fmt.Print(err) } @@ -187,6 +190,7 @@ func createBlockWithTxs(b *protocol.Block) ([][32]byte, [][32]byte, [][32]byte, } hashAccSlice = append(hashAccSlice, tx.Hash()) storage.WriteOpenTx(tx) + totalFees += tx.Fee } else { fmt.Print(err) } @@ -211,11 +215,14 @@ func createBlockWithTxs(b *protocol.Block) ([][32]byte, [][32]byte, [][32]byte, hashConfigSlice = append(hashConfigSlice, tx.Hash()) storage.WriteOpenTx(tx) + totalFees += tx.Fee } else { fmt.Print(err) } } + b.TotalFees = totalFees + return hashFundsSlice, hashAccSlice, hashConfigSlice, hashStakeSlice } diff --git a/miner/contract_test.go b/miner/contract_test.go index bec3c68..2d1b92e 100644 --- a/miner/contract_test.go +++ b/miner/contract_test.go @@ -265,6 +265,7 @@ func createBlockWithSingleContractDeployTx(b *protocol.Block, contract []byte, c tx, contractPrivKey, _ := protocol.ConstrContractTx(0, 1000000, PrivKeyRoot, contract, contractVariables) if err := addTx(b, tx); err == nil { storage.WriteOpenTx(tx) + b.TotalFees = tx.Fee return crypto.GetAddressFromPubKey(&contractPrivKey.PublicKey) } else { fmt.Print(err) @@ -276,6 +277,7 @@ func createBlockWithSingleContractCallTx(contractAddress [64]byte, b *protocol.B tx, _ := protocol.ConstrFundsTx(0x01, rand.Uint64()%100+1, 100000, uint32(accA.TxCnt), accA.Address, contractAddress, PrivKeyAccA, transactionData) if err := addTx(b, tx); err == nil { storage.WriteOpenTx(tx) + b.TotalFees = tx.Fee } else { fmt.Print(err) } diff --git a/miner/state_test.go b/miner/state_test.go index 65e4423..6e63985 100644 --- a/miner/state_test.go +++ b/miner/state_test.go @@ -372,6 +372,7 @@ func TestVerifySCP(t *testing.T) { if err := addTx(b, tx); err != nil { t.Error(err) } + storage.WriteOpenTx(tx) b.InitBloomFilter([][64]byte {accA.Address, accB.Address}) finalizeBlock(b) blocks = append(blocks, b) @@ -396,6 +397,7 @@ func TestVerifySCP(t *testing.T) { if err := addTx(b1, tx1); err != nil { t.Error(err) } + storage.WriteOpenTx(tx1) b1.InitBloomFilter([][64]byte {accA.Address, accB.Address}) finalizeBlock(b1) blocks = append(blocks, b1) From 5151fd2d79cfe0803d6e2acf21b649986904d268 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Thu, 6 Dec 2018 11:01:33 +0100 Subject: [PATCH 20/28] increment TotalFees when adding a Tx to the block --- miner/block.go | 2 ++ miner/block_test.go | 7 ------- miner/blockpreparation.go | 6 ------ miner/contract_test.go | 2 -- 4 files changed, 2 insertions(+), 15 deletions(-) diff --git a/miner/block.go b/miner/block.go index 13a5add..7ce5139 100644 --- a/miner/block.go +++ b/miner/block.go @@ -145,6 +145,8 @@ func addTx(b *protocol.Block, tx protocol.Transaction) error { return errors.New("Transaction type not recognized.") } + b.TotalFees += tx.TxFee() + return nil } diff --git a/miner/block_test.go b/miner/block_test.go index 1d09c82..1200b2f 100644 --- a/miner/block_test.go +++ b/miner/block_test.go @@ -160,8 +160,6 @@ func createBlockWithTxs(b *protocol.Block) ([][32]byte, [][32]byte, [][32]byte, var hashConfigSlice [][32]byte var hashStakeSlice [][32]byte - var totalFees uint64 = 0 - //in order to create valid funds transactions we need to know the tx count of acc A randVar := rand.New(rand.NewSource(time.Now().Unix())) loopMax := int(randVar.Uint32()%testSize) + 1 @@ -175,7 +173,6 @@ func createBlockWithTxs(b *protocol.Block) ([][32]byte, [][32]byte, [][32]byte, } hashFundsSlice = append(hashFundsSlice, tx.Hash()) storage.WriteOpenTx(tx) - totalFees += tx.Fee } else { fmt.Print(err) } @@ -190,7 +187,6 @@ func createBlockWithTxs(b *protocol.Block) ([][32]byte, [][32]byte, [][32]byte, } hashAccSlice = append(hashAccSlice, tx.Hash()) storage.WriteOpenTx(tx) - totalFees += tx.Fee } else { fmt.Print(err) } @@ -215,14 +211,11 @@ func createBlockWithTxs(b *protocol.Block) ([][32]byte, [][32]byte, [][32]byte, hashConfigSlice = append(hashConfigSlice, tx.Hash()) storage.WriteOpenTx(tx) - totalFees += tx.Fee } else { fmt.Print(err) } } - b.TotalFees = totalFees - return hashFundsSlice, hashAccSlice, hashConfigSlice, hashStakeSlice } diff --git a/miner/blockpreparation.go b/miner/blockpreparation.go index f47cdc8..8944e8c 100644 --- a/miner/blockpreparation.go +++ b/miner/blockpreparation.go @@ -24,8 +24,6 @@ func prepareBlock(block *protocol.Block) { sort.Sort(tmpCopy) - var totalFees uint64 = 0 - for _, tx := range opentxs { //Prevent block size to overflow. if block.GetSize()+tx.Size() > activeParameters.Block_size { @@ -37,11 +35,7 @@ func prepareBlock(block *protocol.Block) { //If the tx is invalid, we remove it completely, prevents starvation in the mempool. storage.DeleteOpenTx(tx) } - - totalFees += tx.TxFee() } - - block.TotalFees = totalFees } //Implement the sort interface diff --git a/miner/contract_test.go b/miner/contract_test.go index 2d1b92e..bec3c68 100644 --- a/miner/contract_test.go +++ b/miner/contract_test.go @@ -265,7 +265,6 @@ func createBlockWithSingleContractDeployTx(b *protocol.Block, contract []byte, c tx, contractPrivKey, _ := protocol.ConstrContractTx(0, 1000000, PrivKeyRoot, contract, contractVariables) if err := addTx(b, tx); err == nil { storage.WriteOpenTx(tx) - b.TotalFees = tx.Fee return crypto.GetAddressFromPubKey(&contractPrivKey.PublicKey) } else { fmt.Print(err) @@ -277,7 +276,6 @@ func createBlockWithSingleContractCallTx(contractAddress [64]byte, b *protocol.B tx, _ := protocol.ConstrFundsTx(0x01, rand.Uint64()%100+1, 100000, uint32(accA.TxCnt), accA.Address, contractAddress, PrivKeyAccA, transactionData) if err := addTx(b, tx); err == nil { storage.WriteOpenTx(tx) - b.TotalFees = tx.Fee } else { fmt.Print(err) } From f18313f7ba458dfee2807cb6858bba44f17443c1 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Thu, 6 Dec 2018 13:02:26 +0100 Subject: [PATCH 21/28] WIP: let the verifyTx function return an error --- miner/block.go | 13 ++------ miner/blockpreparation_test.go | 8 ++--- miner/state_test.go | 12 +++++-- miner/verification.go | 59 +++++++++++++++++++--------------- miner/verification_test.go | 16 ++++----- 5 files changed, 56 insertions(+), 52 deletions(-) diff --git a/miner/block.go b/miner/block.go index 7ce5139..b9c0ce7 100644 --- a/miner/block.go +++ b/miner/block.go @@ -97,23 +97,14 @@ func finalizeBlock(block *protocol.Block) error { //We do not operate global state because the work might get interrupted by receiving a block that needs validation //which is done on the global state. func addTx(b *protocol.Block, tx protocol.Transaction) error { - //ActiveParameters is a datastructure that stores the current system parameters, gets only changed when - //configTxs are broadcast in the network. - if tx.TxFee() < activeParameters.Fee_minimum { - logger.Printf("Transaction fee too low: %v (minimum is: %v)\n", tx.TxFee(), activeParameters.Fee_minimum) - err := fmt.Sprintf("Transaction fee too low: %v (minimum is: %v)\n", tx.TxFee(), activeParameters.Fee_minimum) - return errors.New(err) - } - //There is a trade-off what tests can be made now and which have to be delayed (when dynamic state is needed //for inspection. The decision made is to check whether contractTx and configTx have been signed with rootAcc. This //is a dynamic test because it needs to have access to the rootAcc state. The other option would be to include //the address (public key of signature) in the transaction inside the tx -> would resulted in bigger tx size. //So the trade-off is effectively clean abstraction vs. tx size. Everything related to fundsTx is postponed because //the txs depend on each other. - if !verify(tx) { - logger.Printf("Transaction could not be verified: %v", tx) - return errors.New("Transaction could not be verified.") + if err := verify(tx); err != nil { + return errors.New(fmt.Sprintf("transaction could not be verified: %v\n", err)) } switch tx.(type) { diff --git a/miner/blockpreparation_test.go b/miner/blockpreparation_test.go index 7a98174..d5b7363 100644 --- a/miner/blockpreparation_test.go +++ b/miner/blockpreparation_test.go @@ -20,11 +20,11 @@ func TestPrepareAndSortTxs(t *testing.T) { tx, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%100+1, randVar.Uint64()%100+1, uint32(cnt), accA.Address, accB.Address, PrivKeyAccA, nil) tx2, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%100+1, randVar.Uint64()%100+1, uint32(cnt), accB.Address, accA.Address, PrivKeyAccB, nil) - if verifyFundsTx(tx) { + if verifyFundsTx(tx) == nil { storage.WriteOpenTx(tx) } - if verifyFundsTx(tx2) { + if verifyFundsTx(tx2) == nil{ storage.WriteOpenTx(tx2) } } @@ -32,7 +32,7 @@ func TestPrepareAndSortTxs(t *testing.T) { //Add other tx types as well to make the test more challenging for cnt := 0; cnt < testsize; cnt++ { tx, _, _ := protocol.ConstrContractTx(0x01, randVar.Uint64()%100+1, PrivKeyRoot, nil, nil) - if verifyContractTx(tx) { + if verifyContractTx(tx) == nil { storage.WriteOpenTx(tx) } } @@ -44,7 +44,7 @@ func TestPrepareAndSortTxs(t *testing.T) { if tx.Id == 3 || tx.Id == 1 { continue } - if verifyConfigTx(tx) { + if verifyConfigTx(tx) == nil { storage.WriteOpenTx(tx) } } diff --git a/miner/state_test.go b/miner/state_test.go index 6e63985..8f8aabd 100644 --- a/miner/state_test.go +++ b/miner/state_test.go @@ -80,10 +80,16 @@ func TestAccountOverflow(t *testing.T) { accA.Balance = MAX_MONEY accA.TxCnt = 0 tx, err := protocol.ConstrFundsTx(0x01, 1, 1, 0, accB.Address, accA.Address, PrivKeyAccB, nil) - if !verifyFundsTx(tx) || err != nil { - t.Error("Failed to create reasonable fundsTx\n") + if err != nil { + t.Error(err) return } + + if err = verifyFundsTx(tx); err != nil { + t.Error(err) + return + } + accSlice = append(accSlice, tx) err = fundsStateChange(accSlice) @@ -219,7 +225,7 @@ func TestConfigTxStateChange(t *testing.T) { if err != nil { t.Errorf("ConfigTx Creation failed (%v)\n", err) } - if verifyConfigTx(tx) { + if verifyConfigTx(tx) == nil { configs = append(configs, tx) } } diff --git a/miner/verification.go b/miner/verification.go index 82b28d2..c9a755a 100644 --- a/miner/verification.go +++ b/miner/verification.go @@ -2,6 +2,8 @@ package miner import ( "crypto/ecdsa" + "errors" + "fmt" "github.com/bazo-blockchain/bazo-miner/crypto" "github.com/bazo-blockchain/bazo-miner/protocol" "github.com/bazo-blockchain/bazo-miner/storage" @@ -12,34 +14,37 @@ import ( //the verify method. This is because verification depends on the State (e.g., dynamic properties), which //should only be of concern to the miner, not to the protocol package. However, this has the disadvantage //that we have to do case distinction here. -func verify(tx protocol.Transaction) bool { - var verified bool +func verify(tx protocol.Transaction) error { + //ActiveParameters is a datastructure that stores the current system parameters, gets only changed when + //configTxs are broadcast in the network. + if tx.TxFee() < activeParameters.Fee_minimum { + return errors.New(fmt.Sprintf("Transaction fee too low: %v (minimum is: %v)\n", tx.TxFee(), activeParameters.Fee_minimum)) + } switch tx.(type) { case *protocol.FundsTx: - verified = verifyFundsTx(tx.(*protocol.FundsTx)) + return verifyFundsTx(tx.(*protocol.FundsTx)) case *protocol.ContractTx: - verified = verifyContractTx(tx.(*protocol.ContractTx)) + return verifyContractTx(tx.(*protocol.ContractTx)) case *protocol.ConfigTx: - verified = verifyConfigTx(tx.(*protocol.ConfigTx)) + return verifyConfigTx(tx.(*protocol.ConfigTx)) case *protocol.StakeTx: - verified = verifyStakeTx(tx.(*protocol.StakeTx)) + return verifyStakeTx(tx.(*protocol.StakeTx)) } - return verified + return errors.New("could not match a transaction type for verification") } -func verifyFundsTx(tx *protocol.FundsTx) bool { +func verifyFundsTx(tx *protocol.FundsTx) error { if tx == nil { - return false + return errors.New("transaction does not exist") } r, s := new(big.Int), new(big.Int) //fundsTx only makes sense if amount > 0 if tx.Amount == 0 || tx.Amount > MAX_MONEY { - logger.Printf("Invalid transaction amount: %v\n", tx.Amount) - return false + return errors.New(fmt.Sprintf("Invalid transaction amount: %v\n", tx.Amount)) } accFromHash := protocol.SerializeHashContent(tx.From) @@ -52,16 +57,15 @@ func verifyFundsTx(tx *protocol.FundsTx) bool { pubKey := crypto.GetPubKeyFromAddress(tx.From) if ecdsa.Verify(pubKey, txHash[:], r, s) && tx.From != tx.To { - return true + return nil } else { - logger.Printf("Sig invalid. FromHash: %x\nToHash: %x\n", accFromHash[0:8], accToHash[0:8]) - return false + return errors.New(fmt.Sprintf("Sig invalid. FromHash: %x\nToHash: %x\n", accFromHash[0:8], accToHash[0:8])) } } -func verifyContractTx(tx *protocol.ContractTx) bool { +func verifyContractTx(tx *protocol.ContractTx) error { if tx == nil { - return false + return errors.New("transaction does not exist") } r, s := new(big.Int), new(big.Int) @@ -75,16 +79,16 @@ func verifyContractTx(tx *protocol.ContractTx) bool { //Only the hash of the pubkey is hashed and verified here if ecdsa.Verify(pubKey, txHash[:], r, s) == true { - return true + return nil } } - return false + return errors.New("contracttx could not be verified") } -func verifyConfigTx(tx *protocol.ConfigTx) bool { +func verifyConfigTx(tx *protocol.ConfigTx) error { if tx == nil { - return false + return errors.New("transaction does not exist") } //account creation can only be done with a valid priv/pub key which is hard-coded @@ -97,17 +101,16 @@ func verifyConfigTx(tx *protocol.ConfigTx) bool { pubKey := crypto.GetPubKeyFromAddress(rootAcc.Address) txHash := tx.Hash() if ecdsa.Verify(pubKey, txHash[:], r, s) == true { - return true + return nil } } - return false + return errors.New("configtx could not be verified") } -func verifyStakeTx(tx *protocol.StakeTx) bool { +func verifyStakeTx(tx *protocol.StakeTx) error { if tx == nil { - logger.Println("Transactions does not exist.") - return false + return errors.New("transaction does not exist") } //Check if account is present in the actual state @@ -130,7 +133,11 @@ func verifyStakeTx(tx *protocol.StakeTx) bool { pubKey := crypto.GetPubKeyFromAddress(acc.Address) - return ecdsa.Verify(pubKey, txHash[:], r, s) + if ecdsa.Verify(pubKey, txHash[:], r, s) { + return nil + } + + return errors.New("staketx could not be verified") } //Returns true if id is in the list of possible ids and rational value for payload parameter. diff --git a/miner/verification_test.go b/miner/verification_test.go index b12cf71..53c465e 100644 --- a/miner/verification_test.go +++ b/miner/verification_test.go @@ -14,7 +14,7 @@ func TestFundsTxVerification(t *testing.T) { loopMax := int(randVar.Uint64() % 1000) for i := 0; i < loopMax; i++ { tx, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%100000+1, randVar.Uint64()%10+1, uint32(i), accA.Address, accB.Address, PrivKeyAccA, nil) - if verifyFundsTx(tx) == false { + if verifyFundsTx(tx) != nil { t.Errorf("Tx could not be verified: \n%v", tx) } } @@ -27,7 +27,7 @@ func TestContractTx(t *testing.T) { loopMax := int(randVar.Uint64() % 1000) for i := 0; i <= loopMax; i++ { tx, _, _ := protocol.ConstrContractTx(0, randVar.Uint64()%100+1, PrivKeyRoot, nil, nil) - if verifyContractTx(tx) == false { + if verifyContractTx(tx) != nil { t.Errorf("ContractTx could not be verified: %v\n", tx) } } @@ -46,12 +46,12 @@ func TestConfigTx(t *testing.T) { //Add an invalid configTx, should not be accepted txfail, err6 := protocol.ConstrConfigTx(uint8(randVar.Uint32()%256), 20, 5000, randVar.Uint64(), 0, PrivKeyRoot) - if (verifyConfigTx(tx) == false || err != nil) && - (verifyConfigTx(tx2) == false || err2 != nil) && - (verifyConfigTx(tx3) == false || err3 != nil) && - (verifyConfigTx(tx4) == false || err4 != nil) && - (verifyConfigTx(tx5) == false || err5 != nil) && - (verifyConfigTx(txfail) == true || err6 != nil) { + if (verifyConfigTx(tx) != nil || err != nil) && + (verifyConfigTx(tx2) != nil || err2 != nil) && + (verifyConfigTx(tx3) != nil || err3 != nil) && + (verifyConfigTx(tx4) != nil || err4 != nil) && + (verifyConfigTx(tx5) != nil || err5 != nil) && + (verifyConfigTx(txfail) != nil || err6 != nil) { t.Error("ConfigTx verification malfunctioning!") } } From 5bf1c0f1aea8f4dc48c766255dc341c44f91a463 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Wed, 12 Dec 2018 18:06:38 +0100 Subject: [PATCH 22/28] add TxBucket data structure --- protocol/txbucket.go | 83 +++++++++++++++++++++++++++++++++++++++ protocol/txbucket_test.go | 59 ++++++++++++++++++++++++++++ 2 files changed, 142 insertions(+) create mode 100644 protocol/txbucket.go create mode 100644 protocol/txbucket_test.go diff --git a/protocol/txbucket.go b/protocol/txbucket.go new file mode 100644 index 0000000..520812b --- /dev/null +++ b/protocol/txbucket.go @@ -0,0 +1,83 @@ +package protocol + +import ( + "bytes" + "encoding/gob" + "fmt" +) + +type TxBucket struct { + Hash [32]byte + Address [64]byte + Amount uint64 + MerkleRoot [32]byte + FundsTxData [][32]byte +} + +func NewTxBucket(address [64]byte) *TxBucket { + return &TxBucket{ + Address: address, + } +} + +func (bucket *TxBucket) CreateHash() [32]byte { + if bucket == nil { + return [32]byte{} + } + + bucketHash := struct { + hash [32]byte + address [64]byte + amount uint64 + merkleRoot [32]byte + }{ + bucket.Hash, + bucket.Address, + bucket.Amount, + bucket.MerkleRoot, + } + + return SerializeHashContent(bucketHash) +} + +func (bucket *TxBucket) Encode() []byte { + if bucket == nil { + return nil + } + + encoded := TxBucket{ + Hash: bucket.Hash, + Address: bucket.Address, + MerkleRoot: bucket.MerkleRoot, + Amount: bucket.Amount, + FundsTxData: bucket.FundsTxData, + } + + buffer := new(bytes.Buffer) + gob.NewEncoder(buffer).Encode(encoded) + return buffer.Bytes() +} + +func (bucket *TxBucket) Decode(encoded []byte) (b *TxBucket) { + if encoded == nil { + return nil + } + + var decoded TxBucket + buffer := bytes.NewBuffer(encoded) + decoder := gob.NewDecoder(buffer) + decoder.Decode(&decoded) + return &decoded +} + +func (bucket TxBucket) String() string { + return fmt.Sprintf("\nHash: %x\n" + + "Address: %x\n" + + "Amount: %v\n"+ + "MerkleRoot: %x\n", + bucket.Hash[0:8], + bucket.Address[0:8], + bucket.Amount, + bucket.MerkleRoot[0:8], + ) +} \ No newline at end of file diff --git a/protocol/txbucket_test.go b/protocol/txbucket_test.go new file mode 100644 index 0000000..156ef8b --- /dev/null +++ b/protocol/txbucket_test.go @@ -0,0 +1,59 @@ +package protocol + +import ( + "math/rand" + "reflect" + "testing" +) + +func TestBucketCreation(t *testing.T) { + var address [64]byte + rand.Read(address[:]) + bucket := NewTxBucket(address) + + if !reflect.DeepEqual(bucket.Address, address) { + t.Errorf("Address does not match the given one: %x vs. %x", bucket.Address, address) + } +} + +func TestBucketHash(t *testing.T) { + var address [64]byte + rand.Read(address[:]) + bucket := NewTxBucket(address) + + hash1 := bucket.CreateHash() + + if !reflect.DeepEqual(hash1, bucket.CreateHash()) { + t.Errorf("Bucket hashing failed!") + } + + rand.Read(address[:]) + bucket.Address = address + + hash2 := bucket.CreateHash() + + if reflect.DeepEqual(hash1, hash2) { + t.Errorf("Bucket hashing failed!") + } + + if !reflect.DeepEqual(hash2, bucket.CreateHash()) { + t.Errorf("Bucket hashing failed!") + } +} + +func TestBucketSerialization(t *testing.T) { + var bucket TxBucket + + rand.Read(bucket.Hash[:]) + rand.Read(bucket.Address[:]) + bucket.Amount = rand.Uint64() + rand.Read(bucket.MerkleRoot[:]) + + encodedBucket := bucket.Encode() + + var compareBucket TxBucket + compareBucket = *compareBucket.Decode(encodedBucket) + if !reflect.DeepEqual(bucket, compareBucket) { + t.Error("Bucket encoding/decoding failed!") + } +} \ No newline at end of file From 4180eae123a6002ce61853b7002290ab8f71e384 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Wed, 12 Dec 2018 20:20:19 +0100 Subject: [PATCH 23/28] make BuildMerkleTree a struct-function of Block --- miner/block.go | 2 +- miner/state_test.go | 4 ++-- p2p/response.go | 2 +- protocol/block.go | 40 +++++++++++++++++++++++++++++++++++++ protocol/merkletree.go | 16 ++++++++++++++- protocol/merkletree_test.go | 22 ++++++++++---------- 6 files changed, 70 insertions(+), 16 deletions(-) diff --git a/miner/block.go b/miner/block.go index b9c0ce7..e4a8a5e 100644 --- a/miner/block.go +++ b/miner/block.go @@ -50,7 +50,7 @@ func finalizeBlock(block *protocol.Block) error { } } - block.MerkleRoot = protocol.BuildMerkleTree(block).MerkleRoot() + block.MerkleRoot = block.BuildMerkleTree().MerkleRoot() block.InitBloomFilter(storage.GetTxPubKeys(block)) validatorAcc, err := storage.ReadAccount(validatorAccAddress) diff --git a/miner/state_test.go b/miner/state_test.go index 8f8aabd..b5e05b3 100644 --- a/miner/state_test.go +++ b/miner/state_test.go @@ -384,7 +384,7 @@ func TestVerifySCP(t *testing.T) { blocks = append(blocks, b) // Create new SCP - merkleTree := protocol.BuildMerkleTree(b) + merkleTree := b.BuildMerkleTree() mhashes, err := merkleTree.MerkleProof(tx.Hash()) if err != nil { t.Error(err) @@ -408,7 +408,7 @@ func TestVerifySCP(t *testing.T) { finalizeBlock(b1) blocks = append(blocks, b1) - merkleTree1 := protocol.BuildMerkleTree(b1) + merkleTree1 := b1.BuildMerkleTree() mhashes1, err := merkleTree1.MerkleProof(tx1.Hash()) if err != nil { t.Error(err) diff --git a/p2p/response.go b/p2p/response.go index 6e59b64..f10a807 100644 --- a/p2p/response.go +++ b/p2p/response.go @@ -231,7 +231,7 @@ func intermediateNodesRes(p *peer, payload []byte) { copy(blockHash[:], payload[:32]) copy(txHash[:], payload[32:64]) - merkleTree := protocol.BuildMerkleTree(storage.ReadClosedBlock(blockHash)) + merkleTree := storage.ReadClosedBlock(blockHash).BuildMerkleTree() if intermediates, _ := protocol.GetIntermediate(merkleTree.GetLeaf(txHash)); intermediates != nil { for _, node := range intermediates { diff --git a/protocol/block.go b/protocol/block.go index 055a5eb..3316498 100644 --- a/protocol/block.go +++ b/protocol/block.go @@ -59,6 +59,46 @@ func NewBlock(prevHash [32]byte, height uint32) *Block { return &newBlock } + +func (block *Block) BuildMerkleTree() *MerkleTree { + var txHashes hashArray + + if block == nil { + return nil + } + + if block.FundsTxData != nil { + for _, txHash := range block.FundsTxData { + txHashes = append(txHashes, txHash) + } + } + if block.ContractTxData != nil { + for _, txHash := range block.ContractTxData { + txHashes = append(txHashes, txHash) + } + } + if block.ConfigTxData != nil { + for _, txHash := range block.ConfigTxData { + txHashes = append(txHashes, txHash) + } + } + + if block.StakeTxData != nil { + for _, txHash := range block.StakeTxData { + txHashes = append(txHashes, txHash) + } + } + + //Merkle root for no transactions is 0 hash + if len(txHashes) == 0 { + return nil + } + + m, _ := NewMerkleTree(txHashes) + + return m +} + func (block *Block) HashBlock() [32]byte { if block == nil { return [32]byte{} diff --git a/protocol/merkletree.go b/protocol/merkletree.go index 1481753..80cc0c5 100644 --- a/protocol/merkletree.go +++ b/protocol/merkletree.go @@ -80,6 +80,8 @@ func BuildMerkleTree(b *Block) *MerkleTree { //NewTree creates a new Merkle Tree using the content cs. func newTree(txSlices [][32]byte) (*MerkleTree, error) { +//NewMerkleTree creates a new Merkle Tree using the content cs. +func NewMerkleTree(txSlices hashArray) (*MerkleTree, error) { root, leafs, err := buildWithContent(txSlices) if err != nil { return nil, err @@ -92,10 +94,22 @@ func newTree(txSlices [][32]byte) (*MerkleTree, error) { return t, nil } +//verifyNode walks down the tree until hitting a leaf, calculating the hash at each level +//and returning the resulting hash of Node n. +func (n *Node) verifyNode() [32]byte { + if n.leaf { + return n.Hash + } + leftHash := n.Left.verifyNode() + rightHash := n.Right.verifyNode() + concatHash := append(leftHash[:], rightHash[:]...) + return MTHash(concatHash) +} + //buildWithContent is a helper function that for a given set of Contents, generates a //corresponding tree and returns the root node, a list of leaf nodes, and a possible error. //Returns an error if cs contains no Contents. -func buildWithContent(txSlices [][32]byte) (*Node, []*Node, error) { +func buildWithContent(txSlices hashArray) (*Node, []*Node, error) { if len(txSlices) == 0 { return nil, nil, errors.New("Error: cannot construct tree with no content.") } diff --git a/protocol/merkletree_test.go b/protocol/merkletree_test.go index 5805cb4..344f024 100644 --- a/protocol/merkletree_test.go +++ b/protocol/merkletree_test.go @@ -30,7 +30,7 @@ func TestBuildMerkleTree3N(t *testing.T) { concat1233 := append(hash12[:], hash33[:]...) hash1233 := sha3.Sum256(concat1233) - m := BuildMerkleTree(&b) + m := b.BuildMerkleTree() if hash1233 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash1233, m.MerkleRoot()) } @@ -53,7 +53,7 @@ func TestBuildMerkleTree2N(t *testing.T) { concat12 := append(hashSlice[0][:], hashSlice[1][:]...) hash12 := sha3.Sum256(concat12) - m := BuildMerkleTree(&b) + m := b.BuildMerkleTree() if hash12 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash12, m.MerkleRoot()) } @@ -85,7 +85,7 @@ func TestBuildMerkleTree4N(t *testing.T) { concat1234 := append(hash12[:], hash34[:]...) hash1234 := sha3.Sum256(concat1234) - m := BuildMerkleTree(&b) + m := b.BuildMerkleTree() if hash1234 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash1234, m.MerkleRoot()) } @@ -125,7 +125,7 @@ func TestBuildMerkleTree6N(t *testing.T) { concat123456 := append(hash1234[:], hash56[:]...) hash123456 := sha3.Sum256(concat123456) - m := BuildMerkleTree(&b) + m := b.BuildMerkleTree() if hash123456 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash123456, m.MerkleRoot()) } @@ -171,7 +171,7 @@ func TestBuildMerkleTree8N(t *testing.T) { concat12345678 := append(hash1234[:], hash5678[:]...) hash12345678 := sha3.Sum256(concat12345678) - m := BuildMerkleTree(&b) + m := b.BuildMerkleTree() if hash12345678 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash12345678, m.MerkleRoot()) } @@ -225,7 +225,7 @@ func TestBuildMerkleTree10N(t *testing.T) { concat12345678910 := append(hash12345678[:], hash910[:]...) hash12345678910 := sha3.Sum256(concat12345678910) - m := BuildMerkleTree(&b) + m := b.BuildMerkleTree() if hash12345678910 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash12345678910, m.MerkleRoot()) } @@ -285,7 +285,7 @@ func TestBuildMerkleTree11N(t *testing.T) { concat123456789101111 := append(hash12345678[:], hash9101111[:]...) hash123456789101111 := sha3.Sum256(concat123456789101111) - m := BuildMerkleTree(&b) + m := b.BuildMerkleTree() if hash123456789101111 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash123456789101111, m.MerkleRoot()) } @@ -342,7 +342,7 @@ func TestGetIntermediate(t *testing.T) { concat12345678 := append(hash1234[:], hash5678[:]...) hash12345678 := sha3.Sum256(concat12345678) - merkleTree := BuildMerkleTree(&b) + merkleTree := b.BuildMerkleTree() intermediates, _ := GetIntermediate(merkleTree.GetLeaf(hashSlice[9])) @@ -386,7 +386,7 @@ func TestMerkleProof(t *testing.T) { FundsTxData: hashSlice, } - m := BuildMerkleTree(&b) + m := b.BuildMerkleTree() hash12 := MTHash(append(hash1[:], hash2[:]...)) hash34 := MTHash(append(hash3[:], hash4[:]...)) @@ -445,7 +445,7 @@ func TestMerkleProofWithVerification(t *testing.T) { FundsTxData: hashSlice, } - m := BuildMerkleTree(&b) + m := b.BuildMerkleTree() randomIndex := randVal.Uint32() % uint32(nofHashes) @@ -477,7 +477,7 @@ func TestVerifyMerkleProof(t *testing.T) { FundsTxData: hashSlice, } - m := BuildMerkleTree(&b) + m := b.BuildMerkleTree() hash12 := MTHash(append(hash1[:], hash2[:]...)) hash34 := MTHash(append(hash3[:], hash4[:]...)) From 6dbf2daeb60fa268d2c536c05df526089bd2c4e2 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Fri, 14 Dec 2018 18:16:27 +0100 Subject: [PATCH 24/28] remove newBlock function and change function signature to create a new FundsTx --- miner/block.go | 11 ----------- miner/block_test.go | 15 +++++++------- miner/blockchain.go | 4 ++-- miner/blockchainparam_test.go | 7 +++---- miner/blockpreparation_test.go | 7 +++---- miner/blockrollback_test.go | 11 +++++------ miner/contract_test.go | 36 +++++++++++++++++----------------- miner/longestchain_test.go | 34 ++++++++++++++++---------------- miner/main_test.go | 3 ++- miner/proofofstake_test.go | 9 +++++---- miner/slashing_test.go | 10 +++++----- miner/state.go | 2 +- miner/state_test.go | 22 ++++++++++----------- miner/staterollback_test.go | 13 ++++++------ miner/verification_test.go | 2 +- protocol/fundstx.go | 15 +++++++++++++- protocol/fundstx_test.go | 2 +- storage/storage_test.go | 2 +- 18 files changed, 102 insertions(+), 103 deletions(-) diff --git a/miner/block.go b/miner/block.go index e4a8a5e..e240508 100644 --- a/miner/block.go +++ b/miner/block.go @@ -24,17 +24,6 @@ type blockData struct { block *protocol.Block } -//Block constructor, argument is the previous block in the blockchain. -func newBlock(prevHash [32]byte, commitmentProof [crypto.COMM_PROOF_LENGTH]byte, height uint32) *protocol.Block { - block := new(protocol.Block) - block.PrevHash = prevHash - block.CommitmentProof = commitmentProof - block.Height = height - block.StateCopy = make(map[[64]byte]*protocol.Account) - - return block -} - //This function prepares the block to broadcast into the network. No new txs are added at this point. func finalizeBlock(block *protocol.Block) error { //Check if we have a slashing proof that we can add to the block. diff --git a/miner/block_test.go b/miner/block_test.go index 1200b2f..886ec47 100644 --- a/miner/block_test.go +++ b/miner/block_test.go @@ -2,7 +2,6 @@ package miner import ( "fmt" - "github.com/bazo-blockchain/bazo-miner/crypto" "math/rand" "reflect" "testing" @@ -18,7 +17,7 @@ import ( func TestBlock(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) hashFundsSlice, hashAccSlice, hashConfigSlice, hashStakeSlice := createBlockWithTxs(b) err := finalizeBlock(b) if err != nil { @@ -61,7 +60,7 @@ func TestBlock(t *testing.T) { func TestBlockTxDuplicates(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) createBlockWithTxs(b) if err := finalizeBlock(b); err != nil { @@ -100,28 +99,28 @@ func TestBlockTxDuplicates(t *testing.T) { func TestMultipleBlocks(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) createBlockWithTxs(b) finalizeBlock(b) if err := validate(b, false); err != nil { t.Errorf("Block validation for (%v) failed: %v\n", b, err) } - b2 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 2) + b2 := protocol.NewBlock(b.Hash, 2) createBlockWithTxs(b2) finalizeBlock(b2) if err := validate(b2, false); err != nil { t.Errorf("Block validation failed: %v\n", err) } - b3 := newBlock(b2.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 3) + b3 := protocol.NewBlock(b2.Hash, 3) createBlockWithTxs(b3) finalizeBlock(b3) if err := validate(b3, false); err != nil { t.Errorf("Block validation failed: %v\n", err) } - b4 := newBlock(b3.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 4) + b4 := protocol.NewBlock(b3.Hash, 4) createBlockWithTxs(b4) finalizeBlock(b4) if err := validate(b4, false); err != nil { @@ -165,7 +164,7 @@ func createBlockWithTxs(b *protocol.Block) ([][32]byte, [][32]byte, [][32]byte, loopMax := int(randVar.Uint32()%testSize) + 1 loopMax += int(accA.TxCnt) for cnt := int(accA.TxCnt); cnt < loopMax; cnt++ { - tx, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%100+1, randVar.Uint64()%100+1, uint32(cnt), accA.Address, accB.Address, PrivKeyAccA, nil) + tx, _ := protocol.NewSignedFundsTx(0x01, randVar.Uint64()%100+1, randVar.Uint64()%100+1, uint32(cnt), accA.Address, accB.Address, PrivKeyAccA, nil) if err := addTx(b, tx); err == nil { //Might be that we generated a block that was already generated before if storage.ReadOpenTx(tx.Hash()) != nil || storage.ReadClosedTx(tx.Hash()) != nil { diff --git a/miner/blockchain.go b/miner/blockchain.go index bb09d2f..3cd0a0f 100644 --- a/miner/blockchain.go +++ b/miner/blockchain.go @@ -64,7 +64,7 @@ func InitFirstStart(wallet *ecdsa.PublicKey, commitment *rsa.PrivateKey) error { //Mining is a constant process, trying to come up with a successful PoW. func mining(initialBlock *protocol.Block) { - currentBlock := newBlock(initialBlock.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, initialBlock.Height+1) + currentBlock := protocol.NewBlock(initialBlock.Hash, initialBlock.Height+1) for { _, err := storage.ReadAccount(validatorAccAddress) @@ -96,7 +96,7 @@ func mining(initialBlock *protocol.Block) { //validated with block validation, so we wait in order to not work on tx data that is already validated //when we finish the block. blockValidation.Lock() - nextBlock := newBlock(lastBlock.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, lastBlock.Height+1) + nextBlock := protocol.NewBlock(lastBlock.Hash, lastBlock.Height+1) currentBlock = nextBlock prepareBlock(currentBlock) blockValidation.Unlock() diff --git a/miner/blockchainparam_test.go b/miner/blockchainparam_test.go index d7d71e7..bd4f60c 100644 --- a/miner/blockchainparam_test.go +++ b/miner/blockchainparam_test.go @@ -1,7 +1,6 @@ package miner import ( - "github.com/bazo-blockchain/bazo-miner/crypto" "github.com/bazo-blockchain/bazo-miner/protocol" "testing" ) @@ -19,7 +18,7 @@ func TestTargetHistory(t *testing.T) { var tmpBlock *protocol.Block tmpBlock = new(protocol.Block) for cnt := 0; cnt < 10; cnt++ { - tmpBlock = newBlock(tmpBlock.Hash, [crypto.COMM_KEY_LENGTH]byte{}, tmpBlock.Height+1) + tmpBlock = protocol.NewBlock(tmpBlock.Hash, tmpBlock.Height+1) finalizeBlock(tmpBlock) validate(tmpBlock, false) blocks = append(blocks, tmpBlock) @@ -53,7 +52,7 @@ func TestTargetHistory(t *testing.T) { targetSize = len(target) targetTimesSize = len(targetTimes) - tmpBlock = newBlock(blocks[len(blocks)-1].Hash, [crypto.COMM_PROOF_LENGTH]byte{}, blocks[len(blocks)-1].Height+1) + tmpBlock = protocol.NewBlock(blocks[len(blocks)-1].Hash, blocks[len(blocks)-1].Height+1) finalizeBlock(tmpBlock) validate(tmpBlock, false) @@ -72,7 +71,7 @@ func TestTimestamps(t *testing.T) { prevHash := [32]byte{} for cnt := 0; cnt < 0; cnt++ { - b := newBlock(prevHash, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock(prevHash, 1) if cnt == 8 { tx, err := protocol.ConstrConfigTx(0, protocol.DIFF_INTERVAL_ID, 20, 2, 0, PrivKeyRoot) diff --git a/miner/blockpreparation_test.go b/miner/blockpreparation_test.go index d5b7363..3e58c3e 100644 --- a/miner/blockpreparation_test.go +++ b/miner/blockpreparation_test.go @@ -1,7 +1,6 @@ package miner import ( - "github.com/bazo-blockchain/bazo-miner/crypto" "math/rand" "testing" "time" @@ -17,8 +16,8 @@ func TestPrepareAndSortTxs(t *testing.T) { //fill the open storage with fundstx randVar := rand.New(rand.NewSource(time.Now().Unix())) for cnt := 0; cnt < testsize; cnt++ { - tx, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%100+1, randVar.Uint64()%100+1, uint32(cnt), accA.Address, accB.Address, PrivKeyAccA, nil) - tx2, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%100+1, randVar.Uint64()%100+1, uint32(cnt), accB.Address, accA.Address, PrivKeyAccB, nil) + tx, _ := protocol.NewSignedFundsTx(0x01, randVar.Uint64()%100+1, randVar.Uint64()%100+1, uint32(cnt), accA.Address, accB.Address, PrivKeyAccA, nil) + tx2, _ := protocol.NewSignedFundsTx(0x01, randVar.Uint64()%100+1, randVar.Uint64()%100+1, uint32(cnt), accB.Address, accA.Address, PrivKeyAccB, nil) if verifyFundsTx(tx) == nil { storage.WriteOpenTx(tx) @@ -49,7 +48,7 @@ func TestPrepareAndSortTxs(t *testing.T) { } } - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) prepareBlock(b) finalizeBlock(b) diff --git a/miner/blockrollback_test.go b/miner/blockrollback_test.go index c004b5e..be76d01 100644 --- a/miner/blockrollback_test.go +++ b/miner/blockrollback_test.go @@ -1,7 +1,6 @@ package miner import ( - "github.com/bazo-blockchain/bazo-miner/crypto" "reflect" "testing" @@ -13,7 +12,7 @@ import ( func TestValidateBlockRollback(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) //Make state snapshot accsBefore := make(map[[64]byte]protocol.Account) @@ -72,7 +71,7 @@ func TestMultipleBlocksRollback(t *testing.T) { var paramb2 []Parameters var paramb3 []Parameters - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) createBlockWithTxs(b) finalizeBlock(b) if err := validate(b, false); err != nil { @@ -86,7 +85,7 @@ func TestMultipleBlocksRollback(t *testing.T) { paramb = make([]Parameters, len(parameterSlice)) copy(paramb, parameterSlice) - b2 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 2) + b2 := protocol.NewBlock(b.Hash, 2) createBlockWithTxs(b2) finalizeBlock(b2) if err := validate(b2, false); err != nil { @@ -100,7 +99,7 @@ func TestMultipleBlocksRollback(t *testing.T) { paramb2 = make([]Parameters, len(parameterSlice)) copy(paramb2, parameterSlice) - b3 := newBlock(b2.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 3) + b3 := protocol.NewBlock(b2.Hash, 3) createBlockWithTxs(b3) finalizeBlock(b3) if err := validate(b3, false); err != nil { @@ -114,7 +113,7 @@ func TestMultipleBlocksRollback(t *testing.T) { paramb3 = make([]Parameters, len(parameterSlice)) copy(paramb3, parameterSlice) - b4 := newBlock(b3.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 4) + b4 := protocol.NewBlock(b3.Hash, 4) createBlockWithTxs(b4) finalizeBlock(b4) if err := validate(b4, false); err != nil { diff --git a/miner/contract_test.go b/miner/contract_test.go index bec3c68..80bb1e8 100644 --- a/miner/contract_test.go +++ b/miner/contract_test.go @@ -16,7 +16,7 @@ import ( func TestMultipleBlocksWithContractTx(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) contract := []byte{ 35, // CALLDATA 0, 1, 0, 5, // PUSH 5 @@ -29,7 +29,7 @@ func TestMultipleBlocksWithContractTx(t *testing.T) { t.Errorf("Block validation for (%v) failed: %v\n", b, err) } - b2 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 2) + b2 := protocol.NewBlock(b.Hash, 2) transactionData := []byte{ 1, 0, 15, } @@ -45,7 +45,7 @@ func TestMultipleBlocksWithContractTx(t *testing.T) { func TestMultipleBlocksWithStateChangeContractTx(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) contract := []byte{ 35, // CALLDATA 29, 0, // SLOAD @@ -59,7 +59,7 @@ func TestMultipleBlocksWithStateChangeContractTx(t *testing.T) { t.Errorf("Block validation for (%v) failed: %v\n", b, err) } - b2 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 2) + b2 := protocol.NewBlock(b.Hash, 2) transactionData := []byte{ 1, 0, 15, } @@ -82,7 +82,7 @@ func TestMultipleBlocksWithStateChangeContractTx(t *testing.T) { func TestMultipleBlocksWithDoubleStateChangeContractTx(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) contract := []byte{ 35, // CALLDATA 29, 0, // SLOAD @@ -96,7 +96,7 @@ func TestMultipleBlocksWithDoubleStateChangeContractTx(t *testing.T) { t.Errorf("Block validation for (%v) failed: %v\n", b, err) } - b2 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 2) + b2 := protocol.NewBlock(b.Hash, 2) transactionData := []byte{ 1, 0, 15, } @@ -106,7 +106,7 @@ func TestMultipleBlocksWithDoubleStateChangeContractTx(t *testing.T) { t.Errorf("Block validation failed: %v\n", err) } - b3 := newBlock(b2.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 3) + b3 := protocol.NewBlock(b2.Hash, 3) transactionData = []byte{ 1, 0, 15, } @@ -127,7 +127,7 @@ func TestMultipleBlocksWithDoubleStateChangeContractTx(t *testing.T) { func TestMultipleBlocksWithContextContractTx(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) contract := []byte{ 35, 0, 0, 1, 10, 22, 0, 10, 1, 50, 28, 0, 31, 33, 10, 22, 0, 21, 2, 24, 28, 0, 29, 0, 0, 4, 27, 0, 0, 24, } @@ -137,9 +137,9 @@ func TestMultipleBlocksWithContextContractTx(t *testing.T) { t.Errorf("Block validation for (%v) failed: %v\n", b, err) } - b1 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 2) + b1 := protocol.NewBlock(b.Hash, 2) transactionData := []byte{ - 0, 100, // Amount + 0, 100, // BucketRelativeBalance 0, 1, } createBlockWithSingleContractCallTx(contractAddress, b1, transactionData) @@ -153,7 +153,7 @@ func TestMultipleBlocksWithContextContractTx(t *testing.T) { func TestMultipleBlocksWithTokenizationContractTx(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) contract := []byte{ 35, 1, 0, 0, 1, 10, 22, 0, 11, 3, 50, 28, 1, 28, 0, 29, 1, 33, 10, 22, 0, 24, 2, 24, 28, 1, 28, 0, 1, 29, 2, 37, 22, 0, 46, 2, 28, 1, 28, 0, 29, 2, 38, 27, 2, 50, 28, 1, 29, 2, 39, 28, 0, 4, 28, 1, 29, 2, 40, 27, 2, 50, } @@ -175,9 +175,9 @@ func TestMultipleBlocksWithTokenizationContractTx(t *testing.T) { t.Errorf("Block validation for (%v) failed: %v\n", b, err) } - b1 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 2) + b1 := protocol.NewBlock(b.Hash, 2) transactionData := []byte{ - 1, 0, 100, // Amount + 1, 0, 100, // BucketRelativeBalance 1, receiver[0], receiver[1], // receiver address 1, 0, 1, // function Hash } @@ -209,7 +209,7 @@ func TestMultipleBlocksWithTokenizationContractTx(t *testing.T) { func TestMultipleBlocksWithTokenizationContractTxWhichAddsKey(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) contract := []byte{ 35, 1, 0, 0, 1, 10, 22, 0, 11, 3, 50, 28, 1, 28, 0, 29, 1, 33, 10, 22, 0, 24, 2, 24, 28, 1, 28, 0, 1, 29, 2, 37, 22, 0, 46, 2, 28, 1, 28, 0, 29, 2, 38, 27, 2, 50, 28, 1, 29, 2, 39, 28, 0, 4, 28, 1, 29, 2, 40, 27, 2, 50, } @@ -231,9 +231,9 @@ func TestMultipleBlocksWithTokenizationContractTxWhichAddsKey(t *testing.T) { t.Errorf("Block validation for (%v) failed: %v\n", b, err) } - b1 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 2) + b1 := protocol.NewBlock(b.Hash, 2) transactionData := []byte{ - 1, 0, 100, // Amount + 1, 0, 100, // BucketRelativeBalance 1, receiver[0], receiver[1], // receiver address 1, 0, 1, // function Hash } @@ -273,7 +273,7 @@ func createBlockWithSingleContractDeployTx(b *protocol.Block, contract []byte, c } func createBlockWithSingleContractCallTx(contractAddress [64]byte, b *protocol.Block, transactionData []byte) { - tx, _ := protocol.ConstrFundsTx(0x01, rand.Uint64()%100+1, 100000, uint32(accA.TxCnt), accA.Address, contractAddress, PrivKeyAccA, transactionData) + tx, _ := protocol.NewSignedFundsTx(0x01, rand.Uint64()%100+1, 100000, uint32(accA.TxCnt), accA.Address, contractAddress, PrivKeyAccA, transactionData) if err := addTx(b, tx); err == nil { storage.WriteOpenTx(tx) } else { @@ -285,7 +285,7 @@ func createBlockWithSingleContractCallTxDefined(b *protocol.Block, transactionDa accA, _ := storage.ReadAccount(from) accB, _ := storage.ReadAccount(to) - tx, _ := protocol.ConstrFundsTx(0x01, rand.Uint64()%100+1, rand.Uint64()%100+1, uint32(accA.TxCnt), accA.Address, accB.Address, PrivKeyAccA, transactionData) + tx, _ := protocol.NewSignedFundsTx(0x01, rand.Uint64()%100+1, rand.Uint64()%100+1, uint32(accA.TxCnt), accA.Address, accB.Address, PrivKeyAccA, transactionData) if err := addTx(b, tx); err == nil { storage.WriteOpenTx(tx) } else { diff --git a/miner/longestchain_test.go b/miner/longestchain_test.go index 9454251..6c129bc 100644 --- a/miner/longestchain_test.go +++ b/miner/longestchain_test.go @@ -1,7 +1,7 @@ package miner import ( - "github.com/bazo-blockchain/bazo-miner/crypto" + "github.com/bazo-blockchain/bazo-miner/protocol" "github.com/bazo-blockchain/bazo-miner/storage" "testing" ) @@ -12,17 +12,17 @@ func TestGetBlockSequences(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) createBlockWithTxs(b) finalizeBlock(b) validate(b, false) - b2 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, b.Height+1) + b2 := protocol.NewBlock(b.Hash, b.Height+1) createBlockWithTxs(b2) finalizeBlock(b2) validate(b2, false) - b3 := newBlock(b2.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, b2.Height+1) + b3 := protocol.NewBlock(b2.Hash, b2.Height+1) createBlockWithTxs(b3) if err := finalizeBlock(b3); err != nil { t.Error(err) @@ -41,7 +41,7 @@ func TestGetBlockSequences(t *testing.T) { //PoW needs lastBlock, have to set it manually lastBlock = storage.ReadClosedBlock([32]byte{}) - c := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + c := protocol.NewBlock([32]byte{}, 1) createBlockWithTxs(c) if err := finalizeBlock(c); err != nil { t.Error(err) @@ -51,7 +51,7 @@ func TestGetBlockSequences(t *testing.T) { //PoW needs lastBlock, have to set it manually lastBlock = c - c2 := newBlock(c.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, c.Height+1) + c2 := protocol.NewBlock(c.Hash, c.Height+1) createBlockWithTxs(c2) if err := finalizeBlock(c2); err != nil { t.Error(err) @@ -61,7 +61,7 @@ func TestGetBlockSequences(t *testing.T) { //PoW needs lastBlock, have to set it manually lastBlock = c2 - c3 := newBlock(c2.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, c.Height+1) + c3 := protocol.NewBlock(c2.Hash, c.Height+1) createBlockWithTxs(c3) finalizeBlock(c3) @@ -86,17 +86,17 @@ func TestGetBlockSequences(t *testing.T) { cleanAndPrepare() //Make sure that another chain of equal length does not get activated - b = newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b = protocol.NewBlock([32]byte{}, 1) createBlockWithTxs(b) finalizeBlock(b) validate(b, false) - b2 = newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, b.Height+1) + b2 = protocol.NewBlock(b.Hash, b.Height+1) createBlockWithTxs(b2) finalizeBlock(b2) validate(b2, false) - b3 = newBlock(b2.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, b2.Height+1) + b3 = protocol.NewBlock(b2.Hash, b2.Height+1) createBlockWithTxs(b3) finalizeBlock(b3) validate(b3, false) @@ -104,19 +104,19 @@ func TestGetBlockSequences(t *testing.T) { //Blockchain now: genesis <- b <- b2 <- b3 //Competing chain: genesis <- c <- c2 <- c3 lastBlock = storage.ReadClosedBlock([32]byte{}) - c = newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + c = protocol.NewBlock([32]byte{}, 1) createBlockWithTxs(c) finalizeBlock(c) storage.WriteOpenBlock(c) lastBlock = c - c2 = newBlock(c.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, c.Height+1) + c2 = protocol.NewBlock(c.Hash, c.Height+1) createBlockWithTxs(c2) finalizeBlock(c2) storage.WriteOpenBlock(c2) lastBlock = c2 - c3 = newBlock(c2.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, c2.Height+1) + c3 = protocol.NewBlock(c2.Hash, c2.Height+1) createBlockWithTxs(c3) finalizeBlock(c3) @@ -133,12 +133,12 @@ func TestGetBlockSequences(t *testing.T) { func TestGetNewChain(t *testing.T) { cleanAndPrepare() - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) createBlockWithTxs(b) finalizeBlock(b) validate(b, false) - b2 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, b.Height+1) + b2 := protocol.NewBlock(b.Hash, b.Height+1) createBlockWithTxs(b2) finalizeBlock(b2) @@ -154,13 +154,13 @@ func TestGetNewChain(t *testing.T) { //Blockchain now: genesis <- b //New chain: genesis <- c <- c2 lastBlock = storage.ReadClosedBlock([32]byte{}) - c := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + c := protocol.NewBlock([32]byte{}, 1) createBlockWithTxs(c) finalizeBlock(c) storage.WriteOpenBlock(c) lastBlock = c - c2 := newBlock(c.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, c.Height+1) + c2 := protocol.NewBlock(c.Hash, c.Height+1) createBlockWithTxs(c2) finalizeBlock(c2) diff --git a/miner/main_test.go b/miner/main_test.go index 9c25116..2716098 100644 --- a/miner/main_test.go +++ b/miner/main_test.go @@ -202,7 +202,8 @@ func cleanAndPrepare() { crypto.GetBytesFromRSAPubKey(&CommPrivKeyRoot.PublicKey)) commitmentProof, _ := crypto.SignMessageWithRSAKey(CommPrivKeyRoot, "0") - initialBlock = newBlock(genesis.Hash(), commitmentProof, 0) + initialBlock = protocol.NewBlock(genesis.Hash(), 0) + initialBlock.CommitmentProof = commitmentProof collectStatistics(initialBlock) if err := storage.WriteClosedBlock(initialBlock); err != nil { diff --git a/miner/proofofstake_test.go b/miner/proofofstake_test.go index 05e2e12..3046d90 100644 --- a/miner/proofofstake_test.go +++ b/miner/proofofstake_test.go @@ -3,6 +3,7 @@ package miner import ( "fmt" "github.com/bazo-blockchain/bazo-miner/crypto" + "github.com/bazo-blockchain/bazo-miner/protocol" "math/rand" "reflect" "testing" @@ -45,7 +46,7 @@ func TestGetLatestProofs(t *testing.T) { proofs = append([][crypto.COMM_PROOF_LENGTH]byte{genesisCommitmentProof}, proofs...) //Initially we expect only the genesis commitment proof - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) prevProofs := GetLatestProofs(1, b) @@ -57,21 +58,21 @@ func TestGetLatestProofs(t *testing.T) { } //Two new blocks are added with random commitment proofs - b1 := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b1 := protocol.NewBlock([32]byte{}, 1) if err := finalizeBlock(b1); err != nil { t.Error("Error finalizing b1", err) } proofs = append([][crypto.COMM_PROOF_LENGTH]byte{b1.CommitmentProof}, proofs...) validate(b1, false) - b2 := newBlock(b1.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, b1.Height+1) + b2 := protocol.NewBlock(b1.Hash, b1.Height+1) if err := finalizeBlock(b2); err != nil { t.Error("Error finalizing b2", err) } validate(b2, false) proofs = append([][crypto.COMM_PROOF_LENGTH]byte{b2.CommitmentProof}, proofs...) - b3 := newBlock(b2.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, b2.Height+1) + b3 := protocol.NewBlock(b2.Hash, b2.Height+1) prevProofs = GetLatestProofs(3, b3) diff --git a/miner/slashing_test.go b/miner/slashing_test.go index 79c12ad..a9b9b80 100644 --- a/miner/slashing_test.go +++ b/miner/slashing_test.go @@ -1,7 +1,7 @@ package miner import ( - "github.com/bazo-blockchain/bazo-miner/crypto" + "github.com/bazo-blockchain/bazo-miner/protocol" "github.com/bazo-blockchain/bazo-miner/storage" "reflect" "testing" @@ -13,7 +13,7 @@ func TestSlashingCondition(t *testing.T) { myAcc, _ := storage.ReadAccount(validatorAccAddress) initBalance := myAcc.Balance - forkBlock := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + forkBlock := protocol.NewBlock([32]byte{}, 1) if err := finalizeBlock(forkBlock); err != nil { t.Errorf("Block finalization for b1 (%v) failed: %v\n", forkBlock, err) } @@ -22,7 +22,7 @@ func TestSlashingCondition(t *testing.T) { } // genesis <- forkBlock <- b - b := newBlock(forkBlock.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 2) + b := protocol.NewBlock(forkBlock.Hash, 2) if err := finalizeBlock(b); err != nil { t.Errorf("Block finalization for b1 (%v) failed: %v\n", b, err) } @@ -34,7 +34,7 @@ func TestSlashingCondition(t *testing.T) { lastBlock = forkBlock // genesis <- forkBlock <- b2 - b2 := newBlock(forkBlock.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 2) + b2 := protocol.NewBlock(forkBlock.Hash, 2) if err := finalizeBlock(b2); err != nil { t.Errorf("Block finalization for b2 (%v) failed: %v\n", b2, err) } @@ -52,7 +52,7 @@ func TestSlashingCondition(t *testing.T) { } //third block contains the slashing proof - b3 := newBlock(b2.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 3) + b3 := protocol.NewBlock(b2.Hash, 3) if err := finalizeBlock(b3); err != nil { t.Errorf("Block finalization for b3 (%v) failed: %v\n", b3, err) } diff --git a/miner/state.go b/miner/state.go index 7edec6b..b522fa5 100644 --- a/miner/state.go +++ b/miner/state.go @@ -202,7 +202,7 @@ func getInitialBlock(genesis *protocol.Genesis) (initialBlock *protocol.Block, e //Set the last closed block as the initial block initialBlock = storage.AllClosedBlocksAsc[len(storage.AllClosedBlocksAsc)-1] } else { - initialBlock = newBlock(genesis.Hash(), [crypto.COMM_PROOF_LENGTH]byte{}, 0) + initialBlock = protocol.NewBlock(genesis.Hash(), 0) commitmentProof, err := crypto.SignMessageWithRSAKey(commPrivKey, fmt.Sprint(initialBlock.Height)) if err != nil { diff --git a/miner/state_test.go b/miner/state_test.go index b5e05b3..6b51f58 100644 --- a/miner/state_test.go +++ b/miner/state_test.go @@ -20,7 +20,7 @@ func TestFundsTxStateChange(t *testing.T) { var testSize uint32 testSize = 1000 - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) var funds []*protocol.FundsTx var feeA, feeB uint64 @@ -34,7 +34,7 @@ func TestFundsTxStateChange(t *testing.T) { loopMax := int(randVar.Uint32()%testSize + 1) for i := 0; i < loopMax+1; i++ { - ftx, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%1000000+1, randVar.Uint64()%100+1, uint32(i), accA.Address, accB.Address, PrivKeyAccA, nil) + ftx, _ := protocol.NewSignedFundsTx(0x01, randVar.Uint64()%1000000+1, randVar.Uint64()%100+1, uint32(i), accA.Address, accB.Address, PrivKeyAccA, nil) if addTx(b, ftx) == nil { funds = append(funds, ftx) balanceA -= ftx.Amount @@ -43,7 +43,7 @@ func TestFundsTxStateChange(t *testing.T) { balanceB += ftx.Amount } - ftx2, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%1000+1, randVar.Uint64()%100+1, uint32(i), accA.Address, accB.Address, PrivKeyAccB, nil) + ftx2, _ := protocol.NewSignedFundsTx(0x01, randVar.Uint64()%1000+1, randVar.Uint64()%100+1, uint32(i), accA.Address, accB.Address, PrivKeyAccB, nil) if addTx(b, ftx2) == nil { funds = append(funds, ftx2) balanceB -= ftx2.Amount @@ -79,7 +79,7 @@ func TestAccountOverflow(t *testing.T) { accA.Balance = MAX_MONEY accA.TxCnt = 0 - tx, err := protocol.ConstrFundsTx(0x01, 1, 1, 0, accB.Address, accA.Address, PrivKeyAccB, nil) + tx, err := protocol.NewSignedFundsTx(0x01, 1, 1, 0, accB.Address, accA.Address, PrivKeyAccB, nil) if err != nil { t.Error(err) return @@ -188,7 +188,7 @@ func TestFundsTxNewAccsStateChange(t *testing.T) { rand.Read(fromAddress[:]) rand.Read(toAddress[:]) - tx, _ := protocol.ConstrFundsTx(0, 0, 0, 0, fromAddress, toAddress, PrivKeyRoot, nil) + tx, _ := protocol.NewSignedFundsTx(0, 0, 0, 0, fromAddress, toAddress, PrivKeyRoot, nil) fundsTxs = append(fundsTxs, tx) } @@ -337,7 +337,7 @@ func TestStakeTxStateChange(t *testing.T) { randVar := rand.New(rand.NewSource(time.Now().Unix())) - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) var stake, stake2 []*protocol.StakeTx accA.IsStaking = false @@ -373,8 +373,8 @@ func TestVerifySCP(t *testing.T) { var blocks []*protocol.Block // Setup test by transferring 100 coins to accB - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 0) - tx, _ := protocol.ConstrFundsTx(0x01, 100, 1, uint32(0), accA.Address, accB.Address, PrivKeyAccA, nil) + b := protocol.NewBlock([32]byte{}, 0) + tx, _ := protocol.NewSignedFundsTx(0x01, 100, 1, uint32(0), accA.Address, accB.Address, PrivKeyAccA, nil) if err := addTx(b, tx); err != nil { t.Error(err) } @@ -392,14 +392,14 @@ func TestVerifySCP(t *testing.T) { merkleProof := protocol.NewMerkleProof(b.Height, mhashes, tx.Header, tx.Amount, tx.Fee, tx.TxCnt, tx.From, tx.To, tx.Data) // Create transaction that contains SCP - tx1, _ := protocol.ConstrFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) + tx1, _ := protocol.NewSignedFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) tx1.Proofs = append(tx1.Proofs, &merkleProof) if err := verifySCP(tx1, blocks); err != nil { t.Error(err) } // Create new block - b1 := newBlock(b.Hash, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b1 := protocol.NewBlock(b.Hash, 1) if err := addTx(b1, tx1); err != nil { t.Error(err) } @@ -416,7 +416,7 @@ func TestVerifySCP(t *testing.T) { merkleProof1 := protocol.NewMerkleProof(b1.Height, mhashes1, tx1.Header, tx1.Amount, tx1.Fee, tx1.TxCnt, tx1.From, tx1.To, tx1.Data) // Create another transaction that contains SCP - tx2, _ := protocol.ConstrFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) + tx2, _ := protocol.NewSignedFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) tx2.Proofs = append(tx2.Proofs, &merkleProof1, &merkleProof) if err := verifySCP(tx2, blocks); err == nil { t.Error("self-contained proof should be invalid (double spending) but verifySCP returns no error") diff --git a/miner/staterollback_test.go b/miner/staterollback_test.go index 2aee0bf..c92b4bd 100644 --- a/miner/staterollback_test.go +++ b/miner/staterollback_test.go @@ -1,7 +1,6 @@ package miner import ( - "github.com/bazo-blockchain/bazo-miner/crypto" "math/rand" "reflect" "testing" @@ -20,7 +19,7 @@ func TestFundsStateChangeRollback(t *testing.T) { var testSize uint32 testSize = 1000 - b := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + b := protocol.NewBlock([32]byte{}, 1) var funds []*protocol.FundsTx var feeA, feeB uint64 @@ -35,7 +34,7 @@ func TestFundsStateChangeRollback(t *testing.T) { loopMax := int(randVar.Uint32()%testSize + 1) for i := 0; i < loopMax+1; i++ { - ftx, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%1000000+1, randVar.Uint64()%100+1, uint32(i), accA.Address, accB.Address, PrivKeyAccA, nil) + ftx, _ := protocol.NewSignedFundsTx(0x01, randVar.Uint64()%1000000+1, randVar.Uint64()%100+1, uint32(i), accA.Address, accB.Address, PrivKeyAccA, nil) if addTx(b, ftx) == nil { funds = append(funds, ftx) balanceA -= ftx.Amount @@ -46,7 +45,7 @@ func TestFundsStateChangeRollback(t *testing.T) { t.Errorf("Block rejected a valid transaction: %v\n", ftx) } - ftx2, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%1000+1, randVar.Uint64()%100+1, uint32(i), accB.Address, accA.Address, PrivKeyAccB, nil) + ftx2, _ := protocol.NewSignedFundsTx(0x01, randVar.Uint64()%1000+1, randVar.Uint64()%100+1, uint32(i), accB.Address, accA.Address, PrivKeyAccB, nil) if addTx(b, ftx2) == nil { funds = append(funds, ftx2) balanceB -= ftx2.Amount @@ -155,7 +154,7 @@ func TestCollectTxFeesRollback(t *testing.T) { var fee uint64 loopMax := int(randVar.Uint64() % 1000) for i := 0; i < loopMax+1; i++ { - tx, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%1000000+1, randVar.Uint64()%100+1, uint32(i), accA.Address, accB.Address, PrivKeyAccA, nil) + tx, _ := protocol.NewSignedFundsTx(0x01, randVar.Uint64()%1000000+1, randVar.Uint64()%100+1, uint32(i), accA.Address, accB.Address, PrivKeyAccA, nil) funds = append(funds, tx) fee += tx.Fee @@ -175,7 +174,7 @@ func TestCollectTxFeesRollback(t *testing.T) { minerBal = validatorAcc.Balance //Miner gets fees, the miner account balance will overflow at some point for i := 2; i < 100; i++ { - tx, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%1000000+1, uint64(i), uint32(i), accA.Address, accB.Address, PrivKeyAccA, nil) + tx, _ := protocol.NewSignedFundsTx(0x01, randVar.Uint64()%1000000+1, uint64(i), uint32(i), accA.Address, accB.Address, PrivKeyAccA, nil) funds2 = append(funds2, tx) fee2 += tx.Fee } @@ -183,7 +182,7 @@ func TestCollectTxFeesRollback(t *testing.T) { accABal := accA.Balance accBBal := accB.Balance //Should throw an error and result in a rollback, because of acc balance overflow - tmpBlock := newBlock([32]byte{}, [crypto.COMM_PROOF_LENGTH]byte{}, 1) + tmpBlock := protocol.NewBlock([32]byte{}, 1) tmpBlock.Beneficiary = validatorAcc.Address data := blockData{nil, funds2, nil, nil, tmpBlock} if err := validateState(data); err == nil || diff --git a/miner/verification_test.go b/miner/verification_test.go index 53c465e..4474bc2 100644 --- a/miner/verification_test.go +++ b/miner/verification_test.go @@ -13,7 +13,7 @@ func TestFundsTxVerification(t *testing.T) { loopMax := int(randVar.Uint64() % 1000) for i := 0; i < loopMax; i++ { - tx, _ := protocol.ConstrFundsTx(0x01, randVar.Uint64()%100000+1, randVar.Uint64()%10+1, uint32(i), accA.Address, accB.Address, PrivKeyAccA, nil) + tx, _ := protocol.NewSignedFundsTx(0x01, randVar.Uint64()%100000+1, randVar.Uint64()%10+1, uint32(i), accA.Address, accB.Address, PrivKeyAccA, nil) if verifyFundsTx(tx) != nil { t.Errorf("Tx could not be verified: \n%v", tx) } diff --git a/protocol/fundstx.go b/protocol/fundstx.go index bed15f8..2375101 100644 --- a/protocol/fundstx.go +++ b/protocol/fundstx.go @@ -26,7 +26,20 @@ type FundsTx struct { Proofs []*MerkleProof } -func ConstrFundsTx(header byte, amount uint64, fee uint64, txCnt uint32, from, to [64]byte, sigKey *ecdsa.PrivateKey, data []byte) (tx *FundsTx, err error) { +func NewSimpleFundsTx(amount uint64, fee uint64, txCnt uint32, from, to [64]byte) (tx *FundsTx) { + tx = new(FundsTx) + + tx.Header = 0x01 + tx.From = from + tx.To = to + tx.Amount = amount + tx.Fee = fee + tx.TxCnt = txCnt + + return tx +} + +func NewSignedFundsTx(header byte, amount uint64, fee uint64, txCnt uint32, from, to [64]byte, sigKey *ecdsa.PrivateKey, data []byte) (tx *FundsTx, err error) { tx = new(FundsTx) tx.Header = header diff --git a/protocol/fundstx_test.go b/protocol/fundstx_test.go index 375248a..d859b11 100644 --- a/protocol/fundstx_test.go +++ b/protocol/fundstx_test.go @@ -11,7 +11,7 @@ func TestFundsTxSerialization(t *testing.T) { rand := rand.New(rand.NewSource(time.Now().Unix())) loopMax := int(rand.Uint32() % 10000) for i := 0; i < loopMax; i++ { - tx, _ := ConstrFundsTx(0x01, rand.Uint64()%100000+1, rand.Uint64()%10+1, uint32(i), accA.Address, accB.Address, PrivKeyA, nil) + tx, _ := NewSignedFundsTx(0x01, rand.Uint64()%100000+1, rand.Uint64()%10+1, uint32(i), accA.Address, accB.Address, PrivKeyA, nil) var merkleRootsBefore [][32]byte proofs := getDummyProofs() diff --git a/storage/storage_test.go b/storage/storage_test.go index 10936af..955e3a9 100644 --- a/storage/storage_test.go +++ b/storage/storage_test.go @@ -25,7 +25,7 @@ func TestReadWriteDeleteTx(t *testing.T) { loopMax := testsize for i := 0; i < loopMax; i++ { - tx, _ := protocol.ConstrFundsTx(0x01, rand.Uint64()%100000+1, rand.Uint64()%10+1, uint32(i), accA.Address, accB.Address, &PrivKeyA, nil) + tx, _ := protocol.NewSignedFundsTx(0x01, rand.Uint64()%100000+1, rand.Uint64()%10+1, uint32(i), accA.Address, accB.Address, &PrivKeyA, nil) WriteOpenTx(tx) hashFundsSlice = append(hashFundsSlice, tx) } From ce6457e1bf720bee79426b43d8e1457d2e4752b3 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Sat, 15 Dec 2018 09:59:53 +0100 Subject: [PATCH 25/28] improve TxBucket and add tests --- protocol/txbucket.go | 118 ++++++++++++++++++++++++++++++-------- protocol/txbucket_test.go | 50 +++++++++++++--- 2 files changed, 137 insertions(+), 31 deletions(-) diff --git a/protocol/txbucket.go b/protocol/txbucket.go index 520812b..0c40afa 100644 --- a/protocol/txbucket.go +++ b/protocol/txbucket.go @@ -4,14 +4,16 @@ import ( "bytes" "encoding/gob" "fmt" + "log" + "sort" ) type TxBucket struct { - Hash [32]byte - Address [64]byte - Amount uint64 - MerkleRoot [32]byte - FundsTxData [][32]byte + Address [64]byte + RelativeBalance int64 + + merkleRoot hashType + txHashes hashArray } func NewTxBucket(address [64]byte) *TxBucket { @@ -20,21 +22,58 @@ func NewTxBucket(address [64]byte) *TxBucket { } } -func (bucket *TxBucket) CreateHash() [32]byte { +func (bucket *TxBucket) AddFundsTx(tx *FundsTx) { + if tx.From == bucket.Address { + // Bucket owner is sender of the transaction, must pay the amount and the fee + bucket.RelativeBalance -= int64(tx.Amount + tx.Fee) + } else if tx.To == bucket.Address { + // Bucket owner is receiver of the transaction, must not pay for the fee + bucket.RelativeBalance += int64(tx.Amount) + } else { + return + } + + hash := tx.Hash() + bucket.txHashes = append(bucket.txHashes, hash) +} + +func (bucket *TxBucket) CalculateMerkleRoot() hashType { + emptyHash := hashType{} + if bucket.merkleRoot == emptyHash { + merkleTree := bucket.buildMerkleTree() + bucket.merkleRoot = merkleTree.MerkleRoot() + } + return bucket.merkleRoot +} + +func (bucket *TxBucket) buildMerkleTree() *MerkleTree { if bucket == nil { - return [32]byte{} + return nil + } + + //Merkle root for no transactions is 0 hash + if len(bucket.txHashes) == 0 { + return nil + } + + m, _ := NewMerkleTree(bucket.txHashes) + + return m +} + +func (bucket *TxBucket) Hash() hashType { + if bucket == nil { + return hashType{} } bucketHash := struct { - hash [32]byte address [64]byte - amount uint64 - merkleRoot [32]byte + amount int64 + merkleRoot hashType }{ - bucket.Hash, bucket.Address, - bucket.Amount, - bucket.MerkleRoot, + bucket.RelativeBalance, + bucket.CalculateMerkleRoot(), } return SerializeHashContent(bucketHash) @@ -46,11 +85,9 @@ func (bucket *TxBucket) Encode() []byte { } encoded := TxBucket{ - Hash: bucket.Hash, - Address: bucket.Address, - MerkleRoot: bucket.MerkleRoot, - Amount: bucket.Amount, - FundsTxData: bucket.FundsTxData, + Address: bucket.Address, + RelativeBalance: bucket.RelativeBalance, + merkleRoot: bucket.CalculateMerkleRoot(), } buffer := new(bytes.Buffer) @@ -71,13 +108,48 @@ func (bucket *TxBucket) Decode(encoded []byte) (b *TxBucket) { } func (bucket TxBucket) String() string { + hash := bucket.Hash() + merkleRoot := bucket.CalculateMerkleRoot() + return fmt.Sprintf("\nHash: %x\n" + "Address: %x\n" + - "Amount: %v\n"+ - "MerkleRoot: %x\n", - bucket.Hash[0:8], + "Relative Balance: %v\n"+ + "Merkle Root: %x\n", + hash[0:8], bucket.Address[0:8], - bucket.Amount, - bucket.MerkleRoot[0:8], + bucket.RelativeBalance, + merkleRoot[0:8], ) +} + +type txBucketMap map[addressType]*TxBucket + +func NewTxBucketMap() txBucketMap { + return make(txBucketMap) +} + +func (m txBucketMap) Sort() txBucketMap { + var buckets []*TxBucket + for _, bucket := range m { + buckets = append(buckets, bucket) + } + + sort.Slice(buckets, func(i, j int) bool { + switch bytes.Compare(buckets[i].Address[:], buckets[j].Address[:]) { + case -1: + return true + case 0, 1: + return false + default: + log.Panic("not fail-able with `bytes.Comparable` bounded [-1, 1].") + return false + } + }) + + sortedMap := NewTxBucketMap() + for _, bucket := range buckets { + sortedMap[bucket.Address] = bucket + } + + return sortedMap } \ No newline at end of file diff --git a/protocol/txbucket_test.go b/protocol/txbucket_test.go index 156ef8b..e124322 100644 --- a/protocol/txbucket_test.go +++ b/protocol/txbucket_test.go @@ -12,7 +12,7 @@ func TestBucketCreation(t *testing.T) { bucket := NewTxBucket(address) if !reflect.DeepEqual(bucket.Address, address) { - t.Errorf("Address does not match the given one: %x vs. %x", bucket.Address, address) + t.Errorf("BucketAddress does not match the given one: %x vs. %x", bucket.Address, address) } } @@ -21,22 +21,22 @@ func TestBucketHash(t *testing.T) { rand.Read(address[:]) bucket := NewTxBucket(address) - hash1 := bucket.CreateHash() + hash1 := bucket.Hash() - if !reflect.DeepEqual(hash1, bucket.CreateHash()) { + if !reflect.DeepEqual(hash1, bucket.Hash()) { t.Errorf("Bucket hashing failed!") } rand.Read(address[:]) bucket.Address = address - hash2 := bucket.CreateHash() + hash2 := bucket.Hash() if reflect.DeepEqual(hash1, hash2) { t.Errorf("Bucket hashing failed!") } - if !reflect.DeepEqual(hash2, bucket.CreateHash()) { + if !reflect.DeepEqual(hash2, bucket.Hash()) { t.Errorf("Bucket hashing failed!") } } @@ -44,10 +44,8 @@ func TestBucketHash(t *testing.T) { func TestBucketSerialization(t *testing.T) { var bucket TxBucket - rand.Read(bucket.Hash[:]) rand.Read(bucket.Address[:]) - bucket.Amount = rand.Uint64() - rand.Read(bucket.MerkleRoot[:]) + bucket.RelativeBalance = rand.Int63() encodedBucket := bucket.Encode() @@ -56,4 +54,40 @@ func TestBucketSerialization(t *testing.T) { if !reflect.DeepEqual(bucket, compareBucket) { t.Error("Bucket encoding/decoding failed!") } +} + +func TestBucketAddFundsTx(t *testing.T) { + bucket := NewTxBucket(accA.Address) + + amount := uint64(100) + fee := uint64(1) + + tx := NewSimpleFundsTx(amount, fee, uint32(0), accA.Address, accB.Address) + bucket.AddFundsTx(tx) + + expectedAmount := int64(amount + fee) * -1 + if bucket.RelativeBalance != expectedAmount { + t.Errorf("invalid bucket amount, is %v but should be %v", bucket.RelativeBalance, expectedAmount) + } + + amount = uint64(300) + + tx = NewSimpleFundsTx(amount, fee, uint32(0), accB.Address, accA.Address) + bucket.AddFundsTx(tx) + + expectedAmount = expectedAmount + int64(amount) + if bucket.RelativeBalance != expectedAmount { + t.Errorf("invalid bucket amount, is %v but should be %v", bucket.RelativeBalance, expectedAmount) + } + + var randomAddressA, randomAddressB [64]byte + rand.Read(randomAddressA[:]) + rand.Read(randomAddressB[:]) + + tx = NewSimpleFundsTx(amount, fee, uint32(0), randomAddressA, randomAddressB) + bucket.AddFundsTx(tx) + + if bucket.RelativeBalance != expectedAmount { + t.Errorf("invalid bucket amount, is %v but should be %v", bucket.RelativeBalance, expectedAmount) + } } \ No newline at end of file From 0d341b9e592bafd1510a1a86c2b372878c3683ef Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Sat, 15 Dec 2018 10:02:42 +0100 Subject: [PATCH 26/28] implement TxBuckets into rest of the code --- miner/block.go | 16 ++++- miner/block_test.go | 5 +- miner/state.go | 75 ++++++++++++-------- miner/state_test.go | 36 +++++++--- protocol/block.go | 60 ++++++++++++---- protocol/block_test.go | 17 +++-- protocol/byteArray.go | 3 - protocol/fundstx_test.go | 8 ++- protocol/merkleproof.go | 128 +++++++++++++---------------------- protocol/merkleproof_test.go | 12 +++- protocol/merkletree.go | 60 ++-------------- protocol/merkletree_test.go | 84 +++++++---------------- protocol/txbucket.go | 18 ++--- protocol/types.go | 33 +++++++++ 14 files changed, 280 insertions(+), 275 deletions(-) delete mode 100644 protocol/byteArray.go create mode 100644 protocol/types.go diff --git a/miner/block.go b/miner/block.go index e240508..8777c5d 100644 --- a/miner/block.go +++ b/miner/block.go @@ -39,6 +39,11 @@ func finalizeBlock(block *protocol.Block) error { } } + block.TxBucketData = [][32]byte{} + for _, bucket := range block.TxBuckets.Sort() { + block.TxBucketData = append(block.TxBucketData, bucket.Hash()) + } + block.MerkleRoot = block.BuildMerkleTree().MerkleRoot() block.InitBloomFilter(storage.GetTxPubKeys(block)) @@ -78,6 +83,7 @@ func finalizeBlock(block *protocol.Block) error { block.NrFundsTx = uint16(len(block.FundsTxData)) block.NrConfigTx = uint8(len(block.ConfigTxData)) block.NrStakeTx = uint16(len(block.StakeTxData)) + block.NrTxBucket = uint16(len(block.TxBucketData)) return nil } @@ -216,9 +222,9 @@ func addFundsTx(b *protocol.Block, tx *protocol.FundsTx) error { accReceiver := b.StateCopy[tx.To] accReceiver.Balance += tx.Amount - //Add the tx hash to the block header and write it to open storage (non-validated transactions). - b.FundsTxData = append(b.FundsTxData, tx.Hash()) + b.AddFundsTx(tx) logger.Printf("Added tx to the FundsTxData slice: %v", *tx) + return nil } @@ -655,8 +661,12 @@ func preValidate(block *protocol.Block, initialSetup bool) (data *blockData, err return nil, errors.New(fmt.Sprintf("computed total fees do not equal the block's total fees %v vs. %v", totalFees, block.TotalFees)) } + /*for _, tx := range fundsTxSlice { + block.AddFundsTx(tx) + }*/ + //Merkle Tree validation - if protocol.BuildMerkleTree(block).MerkleRoot() != block.MerkleRoot { + if block.BuildMerkleTree().MerkleRoot() != block.MerkleRoot { return nil, errors.New("Merkle Root is incorrect.") } diff --git a/miner/block_test.go b/miner/block_test.go index 886ec47..e12d66e 100644 --- a/miner/block_test.go +++ b/miner/block_test.go @@ -34,7 +34,10 @@ func TestBlock(t *testing.T) { err = validate(decodedBlock, false) b.StateCopy = nil + b.TxBuckets = nil + decodedBlock.StateCopy = nil + decodedBlock.TxBuckets = nil if err != nil { t.Errorf("Block validation failed (%v)\n", err) @@ -52,7 +55,7 @@ func TestBlock(t *testing.T) { t.Error("StakeTx data is not properly serialized!") } if !reflect.DeepEqual(b, decodedBlock) { - t.Error("Either serialization or deserialization failed, blocks are not equal!") + t.Errorf("Either serialization or deserialization failed, blocks are not equal, %v vs %v", b.String(), decodedBlock.String()) } } diff --git a/miner/state.go b/miner/state.go index b522fa5..06963c0 100644 --- a/miner/state.go +++ b/miner/state.go @@ -353,45 +353,64 @@ func fundsStateChange(txSlice []*protocol.FundsTx) (err error) { } func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err error) { - blockIndex := 0 var verifiedBalance int64 = 0 + proofIndex := 0 + sender := tx.From[:] - for _, proof := range tx.Proofs { - for ; blockIndex < len(previousBlocks); blockIndex++ { - currentBlock := previousBlocks[blockIndex] + for _, currentBlock := range previousBlocks { + // Bloom filters never give false-negative, so if it does not contain the sender, + // we can easily skip the current block + if currentBlock.BloomFilter == nil || !currentBlock.BloomFilter.Test(sender) { + continue + } - if currentBlock.BloomFilter == nil { - continue - } + if proofIndex >= len(tx.Proofs) { + return errors.New(fmt.Sprintf("Bloom filter returned true but Merkle proof missing for block at height %v", currentBlock.Height)) + } - // Bloomfilter check - if currentBlock.BloomFilter.Test(tx.From[:]) && proof.Height != currentBlock.Height { - return errors.New(fmt.Sprintf("SCP does not contain a proof for block height %v", currentBlock.Height)) + currentProof := tx.Proofs[proofIndex] + // There must be at least one proof for the current block because the BF returned true + if currentProof.Height < currentBlock.Height { + return errors.New(fmt.Sprintf("SCP does not contain a prof for block at height %v", currentBlock.Height)) + } + + for { + // Compare the current proof (CP) height with the current block (CB) height + // CP.Height < CB.Height -> The current proof is for an earlier block + // CP.Height = CB.Height -> The current proof is for the current block + // CP.Height > CB.Height -> The current proof is for a later block (should not happen, SCP is out of order) + if currentProof.Height < currentBlock.Height { + // Get out of the infinite loop + break + } else if currentProof.Height > currentBlock.Height { + return errors.New(fmt.Sprintf("SCP is out of order because height of proof (%v) is greater than height of current block (%v)", currentProof.Height, currentBlock.Height)) } - if proof.Height == currentBlock.Height { - merkleRoot, err := proof.CalculateMerkleRoot() - if err != nil { - return err - } + merkleRoot, err := currentProof.CalculateMerkleRoot() + if err != nil { + return err + } - if currentBlock.MerkleRoot != merkleRoot { - return errors.New(fmt.Sprintf("Merkle root does not match %x vs. %x", currentBlock.MerkleRoot, merkleRoot)) - } + if currentBlock.MerkleRoot != merkleRoot { + return errors.New(fmt.Sprintf("Merkle root does not match %x vs. %x", currentBlock.MerkleRoot, merkleRoot)) + } - if proof.PFrom == tx.From { - // Sender - verifiedBalance -= int64(proof.PAmount) - } else if proof.PTo == tx.From { - // Receipient - verifiedBalance += int64(proof.PAmount) - } else if currentBlock.Beneficiary == tx.From { - // Beneificiary - verifiedBalance += int64(currentBlock.TotalFees) - } + if currentProof.BucketRelativeBalance == 0 { + // False-Positive Proof + // TODO @mrmnblm + } else if currentProof.BucketAddress == tx.From || currentProof.BucketAddress == tx.To { + // Note that currentProof.BucketRelativeBalance can be either positive or negative + verifiedBalance += currentProof.BucketRelativeBalance + } else if currentBlock.Beneficiary == tx.From { + // (Receiver) Beneificiary + verifiedBalance += int64(currentBlock.TotalFees) + } + proofIndex++ + if proofIndex >= len(tx.Proofs) { break } + currentProof = tx.Proofs[proofIndex] } } diff --git a/miner/state_test.go b/miner/state_test.go index 6b51f58..c73e5b0 100644 --- a/miner/state_test.go +++ b/miner/state_test.go @@ -367,7 +367,7 @@ func TestStakeTxStateChange(t *testing.T) { } -func TestVerifySCP(t *testing.T) { +func TestVerifySCPTruePositiveProof(t *testing.T) { cleanAndPrepare() var blocks []*protocol.Block @@ -378,6 +378,12 @@ func TestVerifySCP(t *testing.T) { if err := addTx(b, tx); err != nil { t.Error(err) } + + bucket, exists := b.TxBuckets[accB.Address] + if !exists { + t.Errorf("transaction bucket is not part of the block for address %x", accA.Address[0:8]) + } + storage.WriteOpenTx(tx) b.InitBloomFilter([][64]byte {accA.Address, accB.Address}) finalizeBlock(b) @@ -385,11 +391,11 @@ func TestVerifySCP(t *testing.T) { // Create new SCP merkleTree := b.BuildMerkleTree() - mhashes, err := merkleTree.MerkleProof(tx.Hash()) + mhashes, err := merkleTree.MerkleProof(bucket.Hash()) if err != nil { t.Error(err) } - merkleProof := protocol.NewMerkleProof(b.Height, mhashes, tx.Header, tx.Amount, tx.Fee, tx.TxCnt, tx.From, tx.To, tx.Data) + merkleProof := protocol.NewMerkleProof(b.Height, mhashes, bucket.Address, bucket.RelativeBalance, bucket.CalculateMerkleRoot()) // Create transaction that contains SCP tx1, _ := protocol.NewSignedFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) @@ -403,23 +409,37 @@ func TestVerifySCP(t *testing.T) { if err := addTx(b1, tx1); err != nil { t.Error(err) } + + bucket, exists = b1.TxBuckets[accB.Address] + if !exists { + t.Errorf("transaction bucket is not part of the block for address %x", accB.Address[0:8]) + } + storage.WriteOpenTx(tx1) b1.InitBloomFilter([][64]byte {accA.Address, accB.Address}) finalizeBlock(b1) - blocks = append(blocks, b1) + blocks = append([]*protocol.Block{b1}, blocks...) // prepend - merkleTree1 := b1.BuildMerkleTree() - mhashes1, err := merkleTree1.MerkleProof(tx1.Hash()) + merkleTree = b1.BuildMerkleTree() + mhashes, err = merkleTree.MerkleProof(bucket.Hash()) if err != nil { t.Error(err) } - merkleProof1 := protocol.NewMerkleProof(b1.Height, mhashes1, tx1.Header, tx1.Amount, tx1.Fee, tx1.TxCnt, tx1.From, tx1.To, tx1.Data) + merkleProof1 := protocol.NewMerkleProof(b1.Height, mhashes, bucket.Address, bucket.RelativeBalance, bucket.CalculateMerkleRoot()) // Create another transaction that contains SCP tx2, _ := protocol.NewSignedFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) tx2.Proofs = append(tx2.Proofs, &merkleProof1, &merkleProof) if err := verifySCP(tx2, blocks); err == nil { - t.Error("self-contained proof should be invalid (double spending) but verifySCP returns no error") + t.Error("self-contained proof should be invalid (insufficient funds) but verifySCP returns no error") } +} + +func TestVerifySCPFraudulentProof(t *testing.T) { + +} + +func TestVerifySCPFalsePositiveProof(t *testing.T) { + } \ No newline at end of file diff --git a/protocol/block.go b/protocol/block.go index 3316498..73ea1d3 100644 --- a/protocol/block.go +++ b/protocol/block.go @@ -4,7 +4,6 @@ import ( "bytes" "encoding/gob" "fmt" - "github.com/bazo-blockchain/bazo-miner/crypto" "github.com/willf/bloom" ) @@ -36,16 +35,19 @@ type Block struct { NrContractTx uint16 NrFundsTx uint16 NrStakeTx uint16 + NrTxBucket uint16 SlashedAddress [64]byte CommitmentProof [crypto.COMM_PROOF_LENGTH]byte ConflictingBlockHash1 [32]byte ConflictingBlockHash2 [32]byte - StateCopy map[[64]byte]*Account //won't be serialized, just keeping track of local state changes + StateCopy map[[64]byte]*Account // won't be serialized, just keeping track of local state changes + TxBuckets txBucketMap // won't be serialized, just keeping track of transaction buckets ContractTxData [][32]byte FundsTxData [][32]byte ConfigTxData [][32]byte StakeTxData [][32]byte + TxBucketData [][32]byte } func NewBlock(prevHash [32]byte, height uint32) *Block { @@ -55,46 +57,71 @@ func NewBlock(prevHash [32]byte, height uint32) *Block { } newBlock.StateCopy = make(map[[64]byte]*Account) + newBlock.TxBuckets = NewTxBucketMap() return &newBlock } +func (block *Block) AddFundsTx(tx *FundsTx) { + bucket := block.createBucketIfNotExists(tx.From) + bucket.AddFundsTx(tx) + + bucket = block.createBucketIfNotExists(tx.To) + bucket.AddFundsTx(tx) + + block.FundsTxData = append(block.FundsTxData, tx.Hash()) +} + +func (block *Block) createBucketIfNotExists(address AddressType) *TxBucket { + bucket, exists := block.TxBuckets[address] + if !exists { + bucket = NewTxBucket(address) + block.TxBuckets[address] = bucket + } + return bucket +} func (block *Block) BuildMerkleTree() *MerkleTree { - var txHashes hashArray + var hashes HashArray if block == nil { return nil } - if block.FundsTxData != nil { - for _, txHash := range block.FundsTxData { - txHashes = append(txHashes, txHash) + // Note that the Merkle roots of the buckets are + // the leaves of the Merkle tree, not the FundsTx itselves + /*for _, bucket := range block.TxBuckets { + hashes = append(hashes, bucket.Hash()) + }*/ + + if block.TxBucketData != nil { + for _, hash := range block.TxBucketData { + hashes = append(hashes, hash) } } if block.ContractTxData != nil { for _, txHash := range block.ContractTxData { - txHashes = append(txHashes, txHash) + hashes = append(hashes, txHash) } } if block.ConfigTxData != nil { for _, txHash := range block.ConfigTxData { - txHashes = append(txHashes, txHash) + hashes = append(hashes, txHash) } } if block.StakeTxData != nil { for _, txHash := range block.StakeTxData { - txHashes = append(txHashes, txHash) + hashes = append(hashes, txHash) } } //Merkle root for no transactions is 0 hash - if len(txHashes) == 0 { + if len(hashes) == 0 { return nil } - m, _ := NewMerkleTree(txHashes) + m, _ := NewMerkleTree(hashes) return m } @@ -146,7 +173,8 @@ func (block *Block) GetSize() uint64 { int(block.NrContractTx)*TXHASH_LEN + int(block.NrFundsTx)*TXHASH_LEN + int(block.NrConfigTx)*TXHASH_LEN + - int(block.NrStakeTx)*TXHASH_LEN + int(block.NrStakeTx)*TXHASH_LEN + + int(block.NrTxBucket)*TXHASH_LEN if block.BloomFilter != nil { encodedBF, _ := block.BloomFilter.GobEncode() @@ -174,6 +202,7 @@ func (block *Block) Encode() []byte { NrFundsTx: block.NrFundsTx, NrConfigTx: block.NrConfigTx, NrStakeTx: block.NrStakeTx, + NrTxBucket: block.NrTxBucket, NrElementsBF: block.NrElementsBF, BloomFilter: block.BloomFilter, SlashedAddress: block.SlashedAddress, @@ -186,6 +215,7 @@ func (block *Block) Encode() []byte { FundsTxData: block.FundsTxData, ConfigTxData: block.ConfigTxData, StakeTxData: block.StakeTxData, + TxBucketData: block.TxBucketData, } buffer := new(bytes.Buffer) @@ -224,6 +254,10 @@ func (block *Block) Decode(encoded []byte) (b *Block) { buffer := bytes.NewBuffer(encoded) decoder := gob.NewDecoder(buffer) decoder.Decode(&decoded) + + decoded.StateCopy = make(map[[64]byte]*Account) + decoded.TxBuckets = NewTxBucketMap() + return &decoded } @@ -239,6 +273,7 @@ func (block Block) String() string { "Amount of contractTx: %v\n"+ "Amount of configTx: %v\n"+ "Amount of stakeTx: %v\n"+ + "Amount of txBuckets: %v\n"+ "Height: %d\n"+ "Commitment Proof: %x\n"+ "Slashed Address:%x\n"+ @@ -255,6 +290,7 @@ func (block Block) String() string { block.NrContractTx, block.NrConfigTx, block.NrStakeTx, + block.NrTxBucket, block.Height, block.CommitmentProof[0:8], block.SlashedAddress[0:8], diff --git a/protocol/block_test.go b/protocol/block_test.go index b8a7d0c..53d4277 100644 --- a/protocol/block_test.go +++ b/protocol/block_test.go @@ -57,8 +57,7 @@ func TestBlockHash(t *testing.T) { func TestBlockSerialization(t *testing.T) { randVar := rand.New(rand.NewSource(time.Now().Unix())) - var block Block - + block := NewBlock([32]byte{}, 0) block.Header = 1 rand.Read(block.Hash[:]) rand.Read(block.PrevHash[:]) @@ -70,26 +69,26 @@ func TestBlockSerialization(t *testing.T) { block.NrFundsTx = uint16(randVar.Uint32()) block.NrConfigTx = uint8(randVar.Uint32()) block.NrStakeTx = uint16(randVar.Uint32()) + block.NrTxBucket = uint16(randVar.Uint32()) rand.Read(block.SlashedAddress[:]) block.Height = uint32(randVar.Uint32()) rand.Read(block.CommitmentProof[:]) rand.Read(block.ConflictingBlockHash1[:]) rand.Read(block.ConflictingBlockHash2[:]) - var compareBlock Block + var compareBlock *Block encodedBlock := block.Encode() - compareBlock = *compareBlock.Decode(encodedBlock) + compareBlock = compareBlock.Decode(encodedBlock) if !reflect.DeepEqual(block, compareBlock) { - t.Error("Block encoding/decoding failed!") + t.Errorf("Block encoding/decoding failed, %v vs %v", block.String(), compareBlock.String()) } } func TestBlockHeaderSerialization(t *testing.T) { randVar := rand.New(rand.NewSource(time.Now().Unix())) - var blockHeader Block - + blockHeader := NewBlock([32]byte{}, 0) blockHeader.Header = 1 rand.Read(blockHeader.Hash[:]) rand.Read(blockHeader.PrevHash[:]) @@ -106,9 +105,9 @@ func TestBlockHeaderSerialization(t *testing.T) { blockHeader.Height = uint32(randVar.Uint32()) rand.Read(blockHeader.Beneficiary[:]) - var compareBlockHeader Block + var compareBlockHeader *Block encodedBlock := blockHeader.EncodeHeader() - compareBlockHeader = *compareBlockHeader.Decode(encodedBlock) + compareBlockHeader = compareBlockHeader.Decode(encodedBlock) if !reflect.DeepEqual(blockHeader, compareBlockHeader) { t.Error("Block encoding/decoding failed!") diff --git a/protocol/byteArray.go b/protocol/byteArray.go deleted file mode 100644 index b1ed09a..0000000 --- a/protocol/byteArray.go +++ /dev/null @@ -1,3 +0,0 @@ -package protocol - -type ByteArray []byte diff --git a/protocol/fundstx_test.go b/protocol/fundstx_test.go index d859b11..6808a1d 100644 --- a/protocol/fundstx_test.go +++ b/protocol/fundstx_test.go @@ -62,7 +62,11 @@ func getDummyProofs() (proofs []*MerkleProof) { for i := 0; i < nofProofs; i++ { randHeight := (randVal.Uint32() % 10) + uint32(i * 10) - proof := NewMerkleProof(randHeight, [][33]byte{}, 0x01, randVal.Uint64()%100000+1, randVal.Uint64()%10+1, uint32(i), accA.Address, accB.Address, nil) + var address AddressType + var merkleRoot HashType + randVal.Read(address[:]) + randVal.Read(merkleRoot[:]) + proof := NewMerkleProof(randHeight, [][33]byte{}, address, randVal.Int63()%100000+1, merkleRoot) merkleTreeDepth := int(rand.Uint32() % 10) + 1 for j:= 0; j < merkleTreeDepth; j++ { @@ -79,7 +83,7 @@ func getDummyProofs() (proofs []*MerkleProof) { copy(mhash[0:1], leftOrRight[:]) randVal.Read(mhash[1:33]) - proof.MHashes = append(proof.MHashes, mhash) + proof.MerkleHashes = append(proof.MerkleHashes, mhash) } proofs = append(proofs, &proof) diff --git a/protocol/merkleproof.go b/protocol/merkleproof.go index d195089..f8efc99 100644 --- a/protocol/merkleproof.go +++ b/protocol/merkleproof.go @@ -13,30 +13,22 @@ type MerkleProof struct { // Merkle hashes // Intermediate hashes required to create a Merkle proof // Note that the first byte specifies the left or right node of the Merkle tree while the rest is the actual hash - MHashes [][33]byte - - // Proof properties - // Must equal the hashed data of FundsTx.Hash() - PHeader byte - PAmount uint64 - PFee uint64 - PTxCnt uint32 - PFrom [64]byte - PTo [64]byte - PData []byte + MerkleHashes [][33]byte + + // Bucket properties + // Must equal the hashed data of TxBucket.Hash() + BucketAddress AddressType + BucketRelativeBalance int64 + BucketMerkleRoot HashType } -func NewMerkleProof(height uint32, mhashes [][33]byte, header byte, amount uint64, fee uint64, txcnt uint32, from [64]byte, to [64]byte, data []byte) (proof MerkleProof) { +func NewMerkleProof(height uint32, mhashes [][33]byte, address AddressType, amount int64, merkleRoot HashType) (proof MerkleProof) { proof.Height = height - proof.MHashes = mhashes - proof.PHeader = header - proof.PAmount = amount - proof.PFee = fee - proof.PTxCnt = txcnt - proof.PFrom = from - proof.PTo = to - proof.PData = data + proof.MerkleHashes = mhashes + proof.BucketAddress = address + proof.BucketRelativeBalance = amount + proof.BucketMerkleRoot = merkleRoot return proof } @@ -47,25 +39,17 @@ func (proof *MerkleProof) Hash() (hash [32]byte) { } input := struct { - Height uint32 - MHashes [][33]byte - PHeader byte - PAmount uint64 - PFee uint64 - PTxCnt uint32 - PFrom [64]byte - PTo [64]byte - PData []byte + Height uint32 + MHashes [][33]byte + Address AddressType + Amount int64 + MerkleRoot HashType }{ proof.Height, - proof.MHashes, - proof.PHeader, - proof.PAmount, - proof.PFee, - proof.PTxCnt, - proof.PFrom, - proof.PTo, - proof.PData, + proof.MerkleHashes, + proof.BucketAddress, + proof.BucketRelativeBalance, + proof.BucketMerkleRoot, } return SerializeHashContent(input) @@ -74,14 +58,10 @@ func (proof *MerkleProof) Hash() (hash [32]byte) { func (proof *MerkleProof) Encode() (encodedTx []byte) { encodeData := MerkleProof{ proof.Height, - proof.MHashes, - proof.PHeader, - proof.PAmount, - proof.PFee, - proof.PTxCnt, - proof.PFrom, - proof.PTo, - proof.PData, + proof.MerkleHashes, + proof.BucketAddress, + proof.BucketRelativeBalance, + proof.BucketMerkleRoot, } buffer := new(bytes.Buffer) gob.NewEncoder(buffer).Encode(encodeData) @@ -98,38 +78,30 @@ func (proof *MerkleProof) Decode(encoded []byte) *MerkleProof { func (proof *MerkleProof) String() string { var mhashesString string - for _, mhash := range proof.MHashes { + for _, mhash := range proof.MerkleHashes { mhashesString += fmt.Sprintf("%x, ", mhash[0:8]) } mhashesString = mhashesString[0:len(mhashesString) - 2] return fmt.Sprintf("Height: %v\n" + - "MHashes: [%v]\n" + - "Proof Header: %v\n" + - "Proof Amount: %v\n"+ - "Proof Fee: %v\n"+ - "Proof TxCnt: %v\n"+ - "Proof From: %x\n"+ - "Proof To: %x\n"+ - "Proof Data: %v\n", + "MerkleHashes: [%v]\n" + + "Bucket BucketAddress: %v\n" + + "Bucket BucketRelativeBalance: %v\n"+ + "Bucket BucketMerkleRoot: %v\n", proof.Height, mhashesString, - proof.PHeader, - proof.PAmount, - proof.PFee, - proof.PTxCnt, - proof.PFrom[0:8], - proof.PTo[0:8], - proof.PData, + proof.BucketAddress[0:8], + proof.BucketRelativeBalance, + proof.BucketMerkleRoot[0:8], ) } func (proof *MerkleProof) CalculateMerkleRoot() (computedHash [32]byte, err error) { - phash := proof.getProofHash() + bucketHash := proof.getBucketHash() - computedHash = phash - for _, mhash := range proof.MHashes { + computedHash = bucketHash + for _, mhash := range proof.MerkleHashes { var hash [32]byte var leftOrRight [1]byte copy(leftOrRight[:], mhash[0:1]) @@ -145,25 +117,17 @@ func (proof *MerkleProof) CalculateMerkleRoot() (computedHash [32]byte, err erro return computedHash, nil } -func (proof *MerkleProof) getProofHash() [32]byte { - // Note that the hashed properties must equal to the hashed properties of FundsTx.Hash() - hash := struct { - Header byte - Amount uint64 - Fee uint64 - TxCnt uint32 - From [64]byte - To [64]byte - Data []byte +func (proof *MerkleProof) getBucketHash() [32]byte { + // Note that the hashed properties must equal to the hashed properties of TxBucket.Hash() + input := struct { + Address AddressType + Amount int64 + MerkleRoot HashType }{ - proof.PHeader, - proof.PAmount, - proof.PFee, - proof.PTxCnt, - proof.PFrom, - proof.PTo, - proof.PData, + proof.BucketAddress, + proof.BucketRelativeBalance, + proof.BucketMerkleRoot, } - return SerializeHashContent(hash) + return SerializeHashContent(input) } \ No newline at end of file diff --git a/protocol/merkleproof_test.go b/protocol/merkleproof_test.go index 286bc59..cf13458 100644 --- a/protocol/merkleproof_test.go +++ b/protocol/merkleproof_test.go @@ -4,10 +4,18 @@ import ( "math/rand" "reflect" "testing" + "time" ) func TestMerkleProofSerialization(t *testing.T) { - proof := NewMerkleProof(rand.Uint32() % 10, [][33]byte{}, 0x01, rand.Uint64()%100000+1, rand.Uint64()%10+1, uint32(0), accA.Address, accB.Address, nil) + randVal := rand.New(rand.NewSource(time.Now().Unix())) + randHeight := randVal.Uint32() % 10 + var address AddressType + var merkleRoot HashType + randVal.Read(address[:]) + randVal.Read(merkleRoot[:]) + + proof := NewMerkleProof(randHeight, [][33]byte{}, address, randVal.Int63()%100000+1, merkleRoot) merkleTreeDepth := int(rand.Uint32() % 10) + 1 for j:= 0; j < merkleTreeDepth; j++ { leftOrRightNumber := int(rand.Uint32() % 2) @@ -22,7 +30,7 @@ func TestMerkleProofSerialization(t *testing.T) { copy(mhash[0:1], leftOrRight[:]) rand.Read(mhash[1:33]) - proof.MHashes = append(proof.MHashes, mhash) + proof.MerkleHashes = append(proof.MerkleHashes, mhash) } merkleRootBefore, err := proof.CalculateMerkleRoot() diff --git a/protocol/merkletree.go b/protocol/merkletree.go index 80cc0c5..1eeb9db 100644 --- a/protocol/merkletree.go +++ b/protocol/merkletree.go @@ -27,61 +27,8 @@ type Node struct { Hash [32]byte } -//verifyNode walks down the tree until hitting a leaf, calculating the hash at each level -//and returning the resulting hash of Node n. -func (n *Node) verifyNode() [32]byte { - if n.leaf { - return n.Hash - } - leftHash := n.Left.verifyNode() - rightHash := n.Right.verifyNode() - concatHash := append(leftHash[:], rightHash[:]...) - return MTHash(concatHash) -} - -func BuildMerkleTree(b *Block) *MerkleTree { - var txHashes [][32]byte - - if b == nil { - return nil - } - - if b.FundsTxData != nil { - for _, txHash := range b.FundsTxData { - txHashes = append(txHashes, txHash) - } - } - if b.ContractTxData != nil { - for _, txHash := range b.ContractTxData { - txHashes = append(txHashes, txHash) - } - } - if b.ConfigTxData != nil { - for _, txHash := range b.ConfigTxData { - txHashes = append(txHashes, txHash) - } - } - - if b.StakeTxData != nil { - for _, txHash := range b.StakeTxData { - txHashes = append(txHashes, txHash) - } - } - - //Merkle root for no transactions is 0 hash - if len(txHashes) == 0 { - return nil - } - - m, _ := newTree(txHashes) - - return m -} - -//NewTree creates a new Merkle Tree using the content cs. -func newTree(txSlices [][32]byte) (*MerkleTree, error) { //NewMerkleTree creates a new Merkle Tree using the content cs. -func NewMerkleTree(txSlices hashArray) (*MerkleTree, error) { +func NewMerkleTree(txSlices HashArray) (*MerkleTree, error) { root, leafs, err := buildWithContent(txSlices) if err != nil { return nil, err @@ -109,7 +56,7 @@ func (n *Node) verifyNode() [32]byte { //buildWithContent is a helper function that for a given set of Contents, generates a //corresponding tree and returns the root node, a list of leaf nodes, and a possible error. //Returns an error if cs contains no Contents. -func buildWithContent(txSlices hashArray) (*Node, []*Node, error) { +func buildWithContent(txSlices HashArray) (*Node, []*Node, error) { if len(txSlices) == 0 { return nil, nil, errors.New("Error: cannot construct tree with no content.") } @@ -203,6 +150,9 @@ func MTHash(data []byte) [32]byte { func (m *MerkleTree) MerkleProof(leafHash [32]byte) (hashes [][33]byte, err error) { leaf := m.GetLeaf(leafHash) + if leaf == nil { + return nil, errors.New(fmt.Sprintf("could not find leaf for hash %x", leafHash[0:8])) + } currentNode := leaf currentParent := leaf.Parent for currentParent != nil { diff --git a/protocol/merkletree_test.go b/protocol/merkletree_test.go index 344f024..48de95c 100644 --- a/protocol/merkletree_test.go +++ b/protocol/merkletree_test.go @@ -9,7 +9,7 @@ import ( func TestBuildMerkleTree3N(t *testing.T) { - var hashSlice [][32]byte + var hashSlice HashArray for i := 0; i < 3; i++ { var txHash [32]byte @@ -17,9 +17,7 @@ func TestBuildMerkleTree3N(t *testing.T) { hashSlice = append(hashSlice, txHash) } - b := Block{ - FundsTxData: hashSlice, - } + m, _ := NewMerkleTree(hashSlice) concat12 := append(hashSlice[0][:], hashSlice[1][:]...) hash12 := sha3.Sum256(concat12) @@ -30,7 +28,6 @@ func TestBuildMerkleTree3N(t *testing.T) { concat1233 := append(hash12[:], hash33[:]...) hash1233 := sha3.Sum256(concat1233) - m := b.BuildMerkleTree() if hash1233 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash1233, m.MerkleRoot()) } @@ -38,7 +35,7 @@ func TestBuildMerkleTree3N(t *testing.T) { func TestBuildMerkleTree2N(t *testing.T) { - var hashSlice [][32]byte + var hashSlice HashArray for i := 0; i < 2; i++ { var txHash [32]byte @@ -46,14 +43,11 @@ func TestBuildMerkleTree2N(t *testing.T) { hashSlice = append(hashSlice, txHash) } - b := Block{ - FundsTxData: hashSlice, - } + m, _ := NewMerkleTree(hashSlice) concat12 := append(hashSlice[0][:], hashSlice[1][:]...) hash12 := sha3.Sum256(concat12) - m := b.BuildMerkleTree() if hash12 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash12, m.MerkleRoot()) } @@ -62,7 +56,7 @@ func TestBuildMerkleTree2N(t *testing.T) { func TestBuildMerkleTree4N(t *testing.T) { - var hashSlice [][32]byte + var hashSlice HashArray for i := 0; i < 4; i++ { var txHash [32]byte @@ -70,9 +64,7 @@ func TestBuildMerkleTree4N(t *testing.T) { hashSlice = append(hashSlice, txHash) } - b := Block{ - FundsTxData: hashSlice, - } + m, _ := NewMerkleTree(hashSlice) concat12 := append(hashSlice[0][:], hashSlice[1][:]...) hash12 := sha3.Sum256(concat12) @@ -85,7 +77,6 @@ func TestBuildMerkleTree4N(t *testing.T) { concat1234 := append(hash12[:], hash34[:]...) hash1234 := sha3.Sum256(concat1234) - m := b.BuildMerkleTree() if hash1234 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash1234, m.MerkleRoot()) } @@ -94,7 +85,7 @@ func TestBuildMerkleTree4N(t *testing.T) { func TestBuildMerkleTree6N(t *testing.T) { - var hashSlice [][32]byte + var hashSlice HashArray for i := 0; i < 6; i++ { var txHash [32]byte @@ -102,9 +93,7 @@ func TestBuildMerkleTree6N(t *testing.T) { hashSlice = append(hashSlice, txHash) } - b := Block{ - FundsTxData: hashSlice, - } + m, _ := NewMerkleTree(hashSlice) concat12 := append(hashSlice[0][:], hashSlice[1][:]...) hash12 := sha3.Sum256(concat12) @@ -125,7 +114,6 @@ func TestBuildMerkleTree6N(t *testing.T) { concat123456 := append(hash1234[:], hash56[:]...) hash123456 := sha3.Sum256(concat123456) - m := b.BuildMerkleTree() if hash123456 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash123456, m.MerkleRoot()) } @@ -134,7 +122,7 @@ func TestBuildMerkleTree6N(t *testing.T) { func TestBuildMerkleTree8N(t *testing.T) { - var hashSlice [][32]byte + var hashSlice HashArray for i := 0; i < 8; i++ { var txHash [32]byte @@ -142,9 +130,7 @@ func TestBuildMerkleTree8N(t *testing.T) { hashSlice = append(hashSlice, txHash) } - b := Block{ - FundsTxData: hashSlice, - } + m, _ := NewMerkleTree(hashSlice) concat12 := append(hashSlice[0][:], hashSlice[1][:]...) hash12 := sha3.Sum256(concat12) @@ -171,7 +157,6 @@ func TestBuildMerkleTree8N(t *testing.T) { concat12345678 := append(hash1234[:], hash5678[:]...) hash12345678 := sha3.Sum256(concat12345678) - m := b.BuildMerkleTree() if hash12345678 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash12345678, m.MerkleRoot()) } @@ -180,7 +165,7 @@ func TestBuildMerkleTree8N(t *testing.T) { func TestBuildMerkleTree10N(t *testing.T) { - var hashSlice [][32]byte + var hashSlice HashArray for i := 0; i < 10; i++ { var txHash [32]byte @@ -188,9 +173,7 @@ func TestBuildMerkleTree10N(t *testing.T) { hashSlice = append(hashSlice, txHash) } - b := Block{ - FundsTxData: hashSlice, - } + m, _ := NewMerkleTree(hashSlice) concat12 := append(hashSlice[0][:], hashSlice[1][:]...) hash12 := sha3.Sum256(concat12) @@ -225,7 +208,6 @@ func TestBuildMerkleTree10N(t *testing.T) { concat12345678910 := append(hash12345678[:], hash910[:]...) hash12345678910 := sha3.Sum256(concat12345678910) - m := b.BuildMerkleTree() if hash12345678910 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash12345678910, m.MerkleRoot()) } @@ -234,7 +216,7 @@ func TestBuildMerkleTree10N(t *testing.T) { func TestBuildMerkleTree11N(t *testing.T) { - var hashSlice [][32]byte + var hashSlice HashArray for i := 0; i < 11; i++ { var txHash [32]byte @@ -242,9 +224,7 @@ func TestBuildMerkleTree11N(t *testing.T) { hashSlice = append(hashSlice, txHash) } - b := Block{ - FundsTxData: hashSlice, - } + m, _ := NewMerkleTree(hashSlice) concat12 := append(hashSlice[0][:], hashSlice[1][:]...) hash12 := sha3.Sum256(concat12) @@ -285,7 +265,6 @@ func TestBuildMerkleTree11N(t *testing.T) { concat123456789101111 := append(hash12345678[:], hash9101111[:]...) hash123456789101111 := sha3.Sum256(concat123456789101111) - m := b.BuildMerkleTree() if hash123456789101111 != m.MerkleRoot() { t.Errorf("Hashes don't match: %x != %x\n", hash123456789101111, m.MerkleRoot()) } @@ -294,7 +273,7 @@ func TestBuildMerkleTree11N(t *testing.T) { func TestGetIntermediate(t *testing.T) { - var hashSlice [][32]byte + var hashSlice HashArray for i := 0; i < 11; i++ { var txHash [32]byte @@ -302,10 +281,6 @@ func TestGetIntermediate(t *testing.T) { hashSlice = append(hashSlice, txHash) } - b := Block{ - FundsTxData: hashSlice, - } - concat12 := append(hashSlice[0][:], hashSlice[1][:]...) hash12 := sha3.Sum256(concat12) @@ -342,9 +317,9 @@ func TestGetIntermediate(t *testing.T) { concat12345678 := append(hash1234[:], hash5678[:]...) hash12345678 := sha3.Sum256(concat12345678) - merkleTree := b.BuildMerkleTree() + m, _ := NewMerkleTree(hashSlice) - intermediates, _ := GetIntermediate(merkleTree.GetLeaf(hashSlice[9])) + intermediates, _ := GetIntermediate(m.GetLeaf(hashSlice[9])) if intermediates[0].Hash != hashSlice[8] { t.Errorf("Hashes don't match: %x != %x\n", intermediates[0].Hash, hashSlice[8]) @@ -379,14 +354,10 @@ func TestMerkleProof(t *testing.T) { randVal.Read(hash3[:]) randVal.Read(hash4[:]) - var hashSlice [][32]byte + var hashSlice HashArray hashSlice = append(hashSlice, hash1, hash2, hash3, hash4) - b := Block{ - FundsTxData: hashSlice, - } - - m := b.BuildMerkleTree() + m, _ := NewMerkleTree(hashSlice) hash12 := MTHash(append(hash1[:], hash2[:]...)) hash34 := MTHash(append(hash3[:], hash4[:]...)) @@ -433,7 +404,7 @@ func TestMerkleProof(t *testing.T) { func TestMerkleProofWithVerification(t *testing.T) { randVal := rand.New(rand.NewSource(time.Now().Unix())) - var hashSlice [][32]byte + var hashSlice HashArray nofHashes := int(randVal.Uint32() % 1000) + 1 for i := 0; i < nofHashes; i++ { var txHash [32]byte @@ -441,12 +412,7 @@ func TestMerkleProofWithVerification(t *testing.T) { hashSlice = append(hashSlice, txHash) } - b := Block{ - FundsTxData: hashSlice, - } - - m := b.BuildMerkleTree() - + m, _ := NewMerkleTree(hashSlice) randomIndex := randVal.Uint32() % uint32(nofHashes) leafHash := hashSlice[randomIndex] @@ -470,14 +436,10 @@ func TestVerifyMerkleProof(t *testing.T) { randVal.Read(hash3[:]) randVal.Read(hash4[:]) - var hashSlice [][32]byte + var hashSlice HashArray hashSlice = append(hashSlice, hash1, hash2, hash3, hash4) - b := Block{ - FundsTxData: hashSlice, - } - - m := b.BuildMerkleTree() + m, _ := NewMerkleTree(hashSlice) hash12 := MTHash(append(hash1[:], hash2[:]...)) hash34 := MTHash(append(hash3[:], hash4[:]...)) diff --git a/protocol/txbucket.go b/protocol/txbucket.go index 0c40afa..353bc76 100644 --- a/protocol/txbucket.go +++ b/protocol/txbucket.go @@ -12,8 +12,8 @@ type TxBucket struct { Address [64]byte RelativeBalance int64 - merkleRoot hashType - txHashes hashArray + merkleRoot HashType + txHashes HashArray } func NewTxBucket(address [64]byte) *TxBucket { @@ -37,8 +37,8 @@ func (bucket *TxBucket) AddFundsTx(tx *FundsTx) { bucket.txHashes = append(bucket.txHashes, hash) } -func (bucket *TxBucket) CalculateMerkleRoot() hashType { - emptyHash := hashType{} +func (bucket *TxBucket) CalculateMerkleRoot() HashType { + emptyHash := HashType{} if bucket.merkleRoot == emptyHash { merkleTree := bucket.buildMerkleTree() bucket.merkleRoot = merkleTree.MerkleRoot() @@ -61,15 +61,15 @@ func (bucket *TxBucket) buildMerkleTree() *MerkleTree { return m } -func (bucket *TxBucket) Hash() hashType { +func (bucket *TxBucket) Hash() HashType { if bucket == nil { - return hashType{} + return HashType{} } bucketHash := struct { address [64]byte - amount int64 - merkleRoot hashType + amount int64 + merkleRoot HashType }{ bucket.Address, bucket.RelativeBalance, @@ -122,7 +122,7 @@ func (bucket TxBucket) String() string { ) } -type txBucketMap map[addressType]*TxBucket +type txBucketMap map[AddressType]*TxBucket func NewTxBucketMap() txBucketMap { return make(txBucketMap) diff --git a/protocol/types.go b/protocol/types.go new file mode 100644 index 0000000..dda3cb9 --- /dev/null +++ b/protocol/types.go @@ -0,0 +1,33 @@ +package protocol + +import ( + "bytes" + "log" +) + +type ByteArray []byte +type AddressType [64]byte +type HashType [32]byte +type HashArray []HashType + +func (h HashArray) Len() int { + return len(h) +} + +func (h HashArray) Less(i, j int) bool { + switch bytes.Compare(h[i][:], h[j][:]) { + case -1: + return true + case 0, 1: + return false + default: + log.Panic("not fail-able with `bytes.Comparable` bounded [-1, 1].") + return false + } +} + +func (h HashArray) Swap(i, j int) { + h[j], h[i] = h[i], h[j] +} + + From 7a1d4b26dee52ea80f53c8fb16d4fac9a0594d49 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Sat, 15 Dec 2018 12:58:29 +0100 Subject: [PATCH 27/28] check double-spending in a single block --- miner/block.go | 8 +++ miner/state.go | 68 +++++++++++++----- miner/state_test.go | 161 +++++++++++++++++++++++++++++++++++++------ protocol/txbucket.go | 17 +++-- 4 files changed, 209 insertions(+), 45 deletions(-) diff --git a/miner/block.go b/miner/block.go index 8777c5d..2c34992 100644 --- a/miner/block.go +++ b/miner/block.go @@ -678,6 +678,14 @@ func preValidate(block *protocol.Block, initialSetup bool) (data *blockData, err func validateState(data blockData) (err error) { accStateChange(data.contractTxSlice) + // TODO @rmnblm SCP Testing Purposes + // State validation does not stop when this returns an error + // Actual funds transaction validation is below in fundsStateChange(data.fundsTxSlice) + err = verifyFundsTransactions(data.fundsTxSlice, storage.ReadAllClosedBlocks()) + if err != nil { + logger.Printf("self-contained proof is invalid: %v\n", err) + } + err = fundsStateChange(data.fundsTxSlice) if err != nil { accStateChangeRollback(data.contractTxSlice) diff --git a/miner/state.go b/miner/state.go index 06963c0..24d03d0 100644 --- a/miner/state.go +++ b/miner/state.go @@ -269,8 +269,6 @@ func accStateChange(txSlice []*protocol.ContractTx) { } func fundsStateChange(txSlice []*protocol.FundsTx) (err error) { - previousBlocks := storage.ReadAllClosedBlocks() - for _, tx := range txSlice { var rootAcc *protocol.Account //Check if we have to issue new coins (in case a root account signed the tx) @@ -336,24 +334,56 @@ func fundsStateChange(txSlice []*protocol.FundsTx) (err error) { accSender.TxCnt += 1 accSender.Balance -= tx.Amount accReceiver.Balance += tx.Amount + } + + return nil +} + +func verifyFundsTransactions(txSlice []*protocol.FundsTx, previousBlocks []*protocol.Block) (err error) { + tmpBuckets := make(map[protocol.AddressType]*protocol.TxBucket) + + for _, tx := range txSlice { + var rootAcc *protocol.Account + //Check if we have to issue new coins (in case a root account signed the tx) + if rootAcc, err = storage.ReadRootAccount(tx.From); err != nil { + return err + } + + if rootAcc != nil && rootAcc.Balance+tx.Amount+tx.Fee > MAX_MONEY { + return errors.New("transaction amount would lead to balance overflow at the receiver (root) account") + } // Only verify SCP if sender is no root - if rootAcc == nil { - // TODO: @rmnblm testing purposes - if err := verifySCP(tx, previousBlocks); err != nil { - logger.Printf("self-contained proof is invalid: %v\n", err) - // return err - } else { - logger.Printf("self-contained proof is valid\n") - } + if rootAcc != nil { + continue + } + + tmpBucket, exists := tmpBuckets[tx.From] + if !exists { + tmpBucket = protocol.NewTxBucket(tx.From) + tmpBuckets[tx.From] = tmpBucket + } + tmpBucket.AddFundsTx(tx) + } + + for _, bucket := range tmpBuckets { + tx := bucket.Transactions[0] // TODO @rmnblm we only check the first transaction to get the verified balance + verifiedBalance, err := verifySCP(tx, previousBlocks) + if err != nil { + return err + } + + if verifiedBalance + bucket.RelativeBalance < 0 { + return errors.New(fmt.Sprintf("verifying funds transactions failed: Address %x " + + "wants to spend more than actually available, (verified %v, relative %v)", + bucket.Address[0:8], verifiedBalance, bucket.RelativeBalance)) } } return nil } -func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err error) { - var verifiedBalance int64 = 0 +func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (verifiedBalance int64, err error) { proofIndex := 0 sender := tx.From[:] @@ -365,13 +395,13 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err erro } if proofIndex >= len(tx.Proofs) { - return errors.New(fmt.Sprintf("Bloom filter returned true but Merkle proof missing for block at height %v", currentBlock.Height)) + return 0, errors.New(fmt.Sprintf("Bloom filter returned true but Merkle proof missing for block at height %v", currentBlock.Height)) } currentProof := tx.Proofs[proofIndex] // There must be at least one proof for the current block because the BF returned true if currentProof.Height < currentBlock.Height { - return errors.New(fmt.Sprintf("SCP does not contain a prof for block at height %v", currentBlock.Height)) + return 0, errors.New(fmt.Sprintf("SCP does not contain a prof for block at height %v", currentBlock.Height)) } for { @@ -383,16 +413,16 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err erro // Get out of the infinite loop break } else if currentProof.Height > currentBlock.Height { - return errors.New(fmt.Sprintf("SCP is out of order because height of proof (%v) is greater than height of current block (%v)", currentProof.Height, currentBlock.Height)) + return 0, errors.New(fmt.Sprintf("SCP is out of order because height of proof (%v) is greater than height of current block (%v)", currentProof.Height, currentBlock.Height)) } merkleRoot, err := currentProof.CalculateMerkleRoot() if err != nil { - return err + return 0, err } if currentBlock.MerkleRoot != merkleRoot { - return errors.New(fmt.Sprintf("Merkle root does not match %x vs. %x", currentBlock.MerkleRoot, merkleRoot)) + return 0, errors.New(fmt.Sprintf("Merkle root does not match %x vs. %x", currentBlock.MerkleRoot, merkleRoot)) } if currentProof.BucketRelativeBalance == 0 { @@ -415,10 +445,10 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (err erro } if verifiedBalance < int64(tx.Amount) { - return errors.New(fmt.Sprintf("verified balance less than amount (%v < %v) spent by acc %x", verifiedBalance, tx.Amount, tx.From[0:8])) + return 0, errors.New(fmt.Sprintf("verified balance less than amount (%v < %v) spent by acc %x", verifiedBalance, tx.Amount, tx.From[0:8])) } - return nil + return verifiedBalance, nil } //We accept config slices with unknown id, but don't act on the payload. This is in case we have not updated to a new diff --git a/miner/state_test.go b/miner/state_test.go index c73e5b0..d96fc7e 100644 --- a/miner/state_test.go +++ b/miner/state_test.go @@ -367,44 +367,44 @@ func TestStakeTxStateChange(t *testing.T) { } -func TestVerifySCPTruePositiveProof(t *testing.T) { +// Test veirifySCP with one transaction per block (sufficient funds) +func TestVerifySCP1TS(t *testing.T) { cleanAndPrepare() - var blocks []*protocol.Block - - // Setup test by transferring 100 coins to accB + // Setup test by transferring 100 coins to account B b := protocol.NewBlock([32]byte{}, 0) tx, _ := protocol.NewSignedFundsTx(0x01, 100, 1, uint32(0), accA.Address, accB.Address, PrivKeyAccA, nil) if err := addTx(b, tx); err != nil { t.Error(err) } + // Get the transaction bucket of account B and check if it exists bucket, exists := b.TxBuckets[accB.Address] if !exists { t.Errorf("transaction bucket is not part of the block for address %x", accA.Address[0:8]) } + // Finalize the first block storage.WriteOpenTx(tx) - b.InitBloomFilter([][64]byte {accA.Address, accB.Address}) finalizeBlock(b) - blocks = append(blocks, b) - // Create new SCP - merkleTree := b.BuildMerkleTree() - mhashes, err := merkleTree.MerkleProof(bucket.Hash()) + // Get the Merkle proof for the transcaction bucket of account B + mhashes, err := b.BuildMerkleTree().MerkleProof(bucket.Hash()) if err != nil { t.Error(err) } + // Create a new Merkle proof for the transaction bucket merkleProof := protocol.NewMerkleProof(b.Height, mhashes, bucket.Address, bucket.RelativeBalance, bucket.CalculateMerkleRoot()) - // Create transaction that contains SCP + // Create a new transaction that contains the previous SCP tx1, _ := protocol.NewSignedFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) tx1.Proofs = append(tx1.Proofs, &merkleProof) - if err := verifySCP(tx1, blocks); err != nil { + // Verify if the SCP is valid + if err := verifyFundsTransactions([]*protocol.FundsTx{tx1}, []*protocol.Block{b}); err != nil { t.Error(err) } - // Create new block + // Repeat previous step: Create another new block b1 := protocol.NewBlock(b.Hash, 1) if err := addTx(b1, tx1); err != nil { t.Error(err) @@ -416,12 +416,9 @@ func TestVerifySCPTruePositiveProof(t *testing.T) { } storage.WriteOpenTx(tx1) - b1.InitBloomFilter([][64]byte {accA.Address, accB.Address}) finalizeBlock(b1) - blocks = append([]*protocol.Block{b1}, blocks...) // prepend - merkleTree = b1.BuildMerkleTree() - mhashes, err = merkleTree.MerkleProof(bucket.Hash()) + mhashes, err = b1.BuildMerkleTree().MerkleProof(bucket.Hash()) if err != nil { t.Error(err) } @@ -430,16 +427,140 @@ func TestVerifySCPTruePositiveProof(t *testing.T) { // Create another transaction that contains SCP tx2, _ := protocol.NewSignedFundsTx(0x01, 50, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) tx2.Proofs = append(tx2.Proofs, &merkleProof1, &merkleProof) - if err := verifySCP(tx2, blocks); err == nil { + if err := verifyFundsTransactions([]*protocol.FundsTx{tx2}, []*protocol.Block{b1, b}); err == nil { t.Error("self-contained proof should be invalid (insufficient funds) but verifySCP returns no error") } - } -func TestVerifySCPFraudulentProof(t *testing.T) { +// Test veirifySCP with a block that contains two transactions in one block (sufficient funds) +func TestVerifySCP2TS(t *testing.T) { + cleanAndPrepare() + + // Setup test by transferring 100 coins to account B + b := protocol.NewBlock([32]byte{}, 0) + tx, _ := protocol.NewSignedFundsTx(0x01, 100, 1, uint32(0), accA.Address, accB.Address, PrivKeyAccA, nil) + + // Current Balance of Account B: 100 + + if err := addTx(b, tx); err != nil { + t.Error(err) + } + + // Get the transaction bucket of account B and check if it exists + bucket, exists := b.TxBuckets[accB.Address] + if !exists { + t.Errorf("transaction bucket is not part of the block for address %x", accA.Address[0:8]) + } + + // Finalize the first block + storage.WriteOpenTx(tx) + finalizeBlock(b) + + // Get the Merkle proof for the transcaction bucket of account B + mhashes, err := b.BuildMerkleTree().MerkleProof(bucket.Hash()) + if err != nil { + t.Error(err) + } + // Create a new Merkle proof for the transaction bucket + merkleProof := protocol.NewMerkleProof(b.Height, mhashes, bucket.Address, bucket.RelativeBalance, bucket.CalculateMerkleRoot()) + + // Create a new transaction that contains the previous SCP + tx1, _ := protocol.NewSignedFundsTx(0x01, 39, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) + // Current Balance of Account B: 60 + tx1.Proofs = append(tx1.Proofs, &merkleProof) + + + // Create a new transaction that contains the previous SCP + tx2, _ := protocol.NewSignedFundsTx(0x01, 39, 1, uint32(1), accB.Address, accA.Address, PrivKeyAccB, nil) + // Current Balance of Account B: 20 + tx2.Proofs = append(tx2.Proofs, &merkleProof) + + // Verify if the SCP is valid + if err := verifyFundsTransactions([]*protocol.FundsTx{tx1, tx2}, []*protocol.Block{b}); err != nil { + t.Error(err) + } + + // Repeat previous step: Create another new block + b1 := protocol.NewBlock(b.Hash, 1) + if err := addTx(b1, tx1); err != nil { + t.Error(err) + } + + if err := addTx(b1, tx2); err != nil { + t.Error(err) + } + + bucket, exists = b1.TxBuckets[accB.Address] + if !exists { + t.Errorf("transaction bucket is not part of the block for address %x", accB.Address[0:8]) + } + + storage.WriteOpenTx(tx1) + storage.WriteOpenTx(tx2) + finalizeBlock(b1) + mhashes, err = b1.BuildMerkleTree().MerkleProof(bucket.Hash()) + if err != nil { + t.Error(err) + } + merkleProof1 := protocol.NewMerkleProof(b1.Height, mhashes, bucket.Address, bucket.RelativeBalance, bucket.CalculateMerkleRoot()) + + // Create another transaction that contains SCP + tx3, _ := protocol.NewSignedFundsTx(0x01, 19, 1, uint32(2), accB.Address, accA.Address, PrivKeyAccB, nil) + + // Current Balance of Account B: 0 + + tx3.Proofs = append(tx3.Proofs, &merkleProof1, &merkleProof) + if err := verifyFundsTransactions([]*protocol.FundsTx{tx3}, []*protocol.Block{b1, b}); err != nil { + t.Errorf("self-contained proof should be valid (sufficient funds) but verifySCP returns error %v", err) + } } -func TestVerifySCPFalsePositiveProof(t *testing.T) { +// Test veirifySCP with a block that contains two transactions in one block (insufficient funds) +func TestVerifySCP2TI(t *testing.T) { + cleanAndPrepare() + + // Setup test by transferring 100 coins to account B + b := protocol.NewBlock([32]byte{}, 0) + tx, _ := protocol.NewSignedFundsTx(0x01, 100, 1, uint32(0), accA.Address, accB.Address, PrivKeyAccA, nil) + + // Current Balance of Account B: 100 + + if err := addTx(b, tx); err != nil { + t.Error(err) + } + + // Get the transaction bucket of account B and check if it exists + bucket, exists := b.TxBuckets[accB.Address] + if !exists { + t.Errorf("transaction bucket is not part of the block for address %x", accA.Address[0:8]) + } + // Finalize the first block + storage.WriteOpenTx(tx) + finalizeBlock(b) + + // Get the Merkle proof for the transcaction bucket of account B + mhashes, err := b.BuildMerkleTree().MerkleProof(bucket.Hash()) + if err != nil { + t.Error(err) + } + // Create a new Merkle proof for the transaction bucket + merkleProof := protocol.NewMerkleProof(b.Height, mhashes, bucket.Address, bucket.RelativeBalance, bucket.CalculateMerkleRoot()) + + // Create a new transaction that contains the previous SCP + tx1, _ := protocol.NewSignedFundsTx(0x01, 39, 1, uint32(0), accB.Address, accA.Address, PrivKeyAccB, nil) + // Current Balance of Account B: 60 + tx1.Proofs = append(tx1.Proofs, &merkleProof) + + + // Create a new transaction that contains the previous SCP + tx2, _ := protocol.NewSignedFundsTx(0x01, 69, 1, uint32(1), accB.Address, accA.Address, PrivKeyAccB, nil) + // Current Balance of Account B: -10 -> SHOULD RETURN ERROR + tx2.Proofs = append(tx2.Proofs, &merkleProof) + + // Verify if the SCP is valid (should be not) + if err := verifyFundsTransactions([]*protocol.FundsTx{tx1, tx2}, []*protocol.Block{b}); err == nil { + t.Error("self-contained proof should be invalid (insufficient funds) but verifySCP returns no error") + } } \ No newline at end of file diff --git a/protocol/txbucket.go b/protocol/txbucket.go index 353bc76..08ec761 100644 --- a/protocol/txbucket.go +++ b/protocol/txbucket.go @@ -12,8 +12,9 @@ type TxBucket struct { Address [64]byte RelativeBalance int64 - merkleRoot HashType - txHashes HashArray + merkleRoot HashType // should not be accessed directly, instead, call CalculateMerkleRoot() + + Transactions []*FundsTx // won't be serialized } func NewTxBucket(address [64]byte) *TxBucket { @@ -33,8 +34,7 @@ func (bucket *TxBucket) AddFundsTx(tx *FundsTx) { return } - hash := tx.Hash() - bucket.txHashes = append(bucket.txHashes, hash) + bucket.Transactions = append(bucket.Transactions, tx) } func (bucket *TxBucket) CalculateMerkleRoot() HashType { @@ -52,11 +52,16 @@ func (bucket *TxBucket) buildMerkleTree() *MerkleTree { } //Merkle root for no transactions is 0 hash - if len(bucket.txHashes) == 0 { + if len(bucket.Transactions) == 0 { return nil } - m, _ := NewMerkleTree(bucket.txHashes) + var txHashes HashArray + for _, tx := range bucket.Transactions { + txHashes = append(txHashes, tx.Hash()) + } + + m, _ := NewMerkleTree(txHashes) return m } From 10e32438f49f2c203d377faf33ae80007622ee77 Mon Sep 17 00:00:00 2001 From: Roman Blum Date: Sat, 15 Dec 2018 21:28:21 +0100 Subject: [PATCH 28/28] update verified balance if account is beneficiary --- miner/state.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/miner/state.go b/miner/state.go index 24d03d0..c3cfde7 100644 --- a/miner/state.go +++ b/miner/state.go @@ -378,6 +378,8 @@ func verifyFundsTransactions(txSlice []*protocol.FundsTx, previousBlocks []*prot "wants to spend more than actually available, (verified %v, relative %v)", bucket.Address[0:8], verifiedBalance, bucket.RelativeBalance)) } + + logger.Printf("self-contained proof is valid, yay!") } return nil @@ -388,6 +390,10 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (verified sender := tx.From[:] for _, currentBlock := range previousBlocks { + if currentBlock.Beneficiary == tx.From { + verifiedBalance += int64(currentBlock.TotalFees) + } + // Bloom filters never give false-negative, so if it does not contain the sender, // we can easily skip the current block if currentBlock.BloomFilter == nil || !currentBlock.BloomFilter.Test(sender) { @@ -431,9 +437,6 @@ func verifySCP(tx *protocol.FundsTx, previousBlocks []*protocol.Block) (verified } else if currentProof.BucketAddress == tx.From || currentProof.BucketAddress == tx.To { // Note that currentProof.BucketRelativeBalance can be either positive or negative verifiedBalance += currentProof.BucketRelativeBalance - } else if currentBlock.Beneficiary == tx.From { - // (Receiver) Beneificiary - verifiedBalance += int64(currentBlock.TotalFees) } proofIndex++