diff --git a/core/ledger/kvledger/txmgmt/couchdbtxmgmt/couchdb_txmgr.go b/core/ledger/kvledger/txmgmt/couchdbtxmgmt/couchdb_txmgr.go index 3528b3d79e4..4b7b6b9a104 100644 --- a/core/ledger/kvledger/txmgmt/couchdbtxmgmt/couchdb_txmgr.go +++ b/core/ledger/kvledger/txmgmt/couchdbtxmgmt/couchdb_txmgr.go @@ -26,6 +26,7 @@ import ( "github.com/hyperledger/fabric/core/ledger/util/db" "github.com/op/go-logging" + "github.com/hyperledger/fabric/core/ledger/kvledger/version" "github.com/hyperledger/fabric/protos/common" pb "github.com/hyperledger/fabric/protos/peer" putils "github.com/hyperledger/fabric/protos/utils" @@ -40,7 +41,7 @@ type Conf struct { type versionedValue struct { value []byte - version uint64 + version *version.Height } type updateSet struct { @@ -119,7 +120,7 @@ func (txmgr *CouchDBTxMgr) ValidateAndPrepare(block *common.Block) (*common.Bloc var valid bool txmgr.updateSet = newUpdateSet() logger.Debugf("Validating a block with [%d] transactions", len(block.Data.Data)) - for _, envBytes := range block.Data.Data { + for txIndex, envBytes := range block.Data.Data { // extract actions from the envelope message respPayload, err := putils.GetActionFromEnvelope(envBytes) if err != nil { @@ -150,7 +151,7 @@ func (txmgr *CouchDBTxMgr) ValidateAndPrepare(block *common.Block) (*common.Bloc } if valid { - if err := txmgr.addWriteSetToBatch(txRWSet); err != nil { + if err := txmgr.addWriteSetToBatch(txRWSet, version.NewHeight(block.Header.Number, uint64(txIndex+1))); err != nil { return nil, nil, err } } else { @@ -170,7 +171,7 @@ func (txmgr *CouchDBTxMgr) Shutdown() { func (txmgr *CouchDBTxMgr) validateTx(txRWSet *txmgmt.TxReadWriteSet) (bool, error) { var err error - var currentVersion uint64 + var currentVersion *version.Height for _, nsRWSet := range txRWSet.NsRWs { ns := nsRWSet.NameSpace @@ -182,7 +183,7 @@ func (txmgr *CouchDBTxMgr) validateTx(txRWSet *txmgmt.TxReadWriteSet) (bool, err if currentVersion, err = txmgr.getCommitedVersion(ns, kvRead.Key); err != nil { return false, err } - if currentVersion != kvRead.Version { + if !version.AreSame(currentVersion, kvRead.Version) { logger.Debugf("Version mismatch for key [%s:%s]. Current version = [%d], Version in readSet [%d]", ns, kvRead.Key, currentVersion, kvRead.Version) return false, nil @@ -192,10 +193,7 @@ func (txmgr *CouchDBTxMgr) validateTx(txRWSet *txmgmt.TxReadWriteSet) (bool, err return true, nil } -func (txmgr *CouchDBTxMgr) addWriteSetToBatch(txRWSet *txmgmt.TxReadWriteSet) error { - var err error - var currentVersion uint64 - +func (txmgr *CouchDBTxMgr) addWriteSetToBatch(txRWSet *txmgmt.TxReadWriteSet, txHeight *version.Height) error { if txmgr.updateSet == nil { txmgr.updateSet = newUpdateSet() } @@ -203,16 +201,7 @@ func (txmgr *CouchDBTxMgr) addWriteSetToBatch(txRWSet *txmgmt.TxReadWriteSet) er ns := nsRWSet.NameSpace for _, kvWrite := range nsRWSet.Writes { compositeKey := constructCompositeKey(ns, kvWrite.Key) - versionedVal := txmgr.updateSet.get(compositeKey) - if versionedVal != nil { - currentVersion = versionedVal.version - } else { - currentVersion, err = txmgr.getCommitedVersion(ns, kvWrite.Key) - if err != nil { - return err - } - } - txmgr.updateSet.add(compositeKey, &versionedValue{kvWrite.Value, currentVersion + 1}) + txmgr.updateSet.add(compositeKey, &versionedValue{kvWrite.Value, txHeight}) } } return nil @@ -278,16 +267,16 @@ func (txmgr *CouchDBTxMgr) Rollback() { txmgr.updateSet = nil } -func (txmgr *CouchDBTxMgr) getCommitedVersion(ns string, key string) (uint64, error) { +func (txmgr *CouchDBTxMgr) getCommitedVersion(ns string, key string) (*version.Height, error) { var err error - var version uint64 + var version *version.Height if _, version, err = txmgr.getCommittedValueAndVersion(ns, key); err != nil { - return 0, err + return nil, err } return version, nil } -func (txmgr *CouchDBTxMgr) getCommittedValueAndVersion(ns string, key string) ([]byte, uint64, error) { +func (txmgr *CouchDBTxMgr) getCommittedValueAndVersion(ns string, key string) ([]byte, *version.Height, error) { compositeKey := constructCompositeKey(ns, key) @@ -302,8 +291,8 @@ func (txmgr *CouchDBTxMgr) getCommittedValueAndVersion(ns string, key string) ([ } } - var version uint64 = 1 //TODO - version hardcoded to 1 is a temporary value for the prototype - return docBytes, version, nil + ver := version.NewHeight(1, 1) //TODO - version hardcoded to 1 is a temporary value for the prototype + return docBytes, ver, nil } func encodeValue(value []byte, version uint64) []byte { diff --git a/core/ledger/kvledger/txmgmt/lockbasedtxmgmt/lockbased_txmgmt_test.go b/core/ledger/kvledger/txmgmt/lockbasedtxmgmt/lockbased_txmgmt_test.go index c93212429b2..296bf20e00f 100644 --- a/core/ledger/kvledger/txmgmt/lockbasedtxmgmt/lockbased_txmgmt_test.go +++ b/core/ledger/kvledger/txmgmt/lockbasedtxmgmt/lockbased_txmgmt_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/hyperledger/fabric/core/ledger" + "github.com/hyperledger/fabric/core/ledger/kvledger/version" "github.com/hyperledger/fabric/core/ledger/testutil" ) @@ -65,7 +66,7 @@ func TestTxSimulatorWithExistingData(t *testing.T) { isValid, err := txMgr.validateTx(txRWSet) testutil.AssertNoError(t, err, fmt.Sprintf("Error in validateTx(): %s", err)) testutil.AssertSame(t, isValid, true) - txMgr.addWriteSetToBatch(txRWSet) + txMgr.addWriteSetToBatch(txRWSet, version.NewHeight(1, 1)) err = txMgr.Commit() testutil.AssertNoError(t, err, fmt.Sprintf("Error while calling commit(): %s", err)) @@ -82,7 +83,7 @@ func TestTxSimulatorWithExistingData(t *testing.T) { txRWSet = s2.(*LockBasedTxSimulator).getTxReadWriteSet() isValid, err = txMgr.validateTx(txRWSet) testutil.AssertSame(t, isValid, true) - txMgr.addWriteSetToBatch(txRWSet) + txMgr.addWriteSetToBatch(txRWSet, version.NewHeight(2, 1)) txMgr.Commit() // simulate tx3 @@ -95,11 +96,9 @@ func TestTxSimulatorWithExistingData(t *testing.T) { // verify the versions of keys in persistence ver, _ := txMgr.getCommitedVersion("ns1", "key1") - testutil.AssertEquals(t, ver, uint64(2)) + testutil.AssertEquals(t, ver, version.NewHeight(2, 1)) ver, _ = txMgr.getCommitedVersion("ns1", "key2") - testutil.AssertEquals(t, ver, uint64(1)) - ver, _ = txMgr.getCommitedVersion("ns2", "key3") - testutil.AssertEquals(t, ver, uint64(2)) + testutil.AssertEquals(t, ver, version.NewHeight(1, 1)) } func TestTxValidation(t *testing.T) { @@ -120,7 +119,7 @@ func TestTxValidation(t *testing.T) { isValid, err := txMgr.validateTx(txRWSet) testutil.AssertNoError(t, err, fmt.Sprintf("Error in validateTx(): %s", err)) testutil.AssertSame(t, isValid, true) - txMgr.addWriteSetToBatch(txRWSet) + txMgr.addWriteSetToBatch(txRWSet, version.NewHeight(1, 1)) err = txMgr.Commit() testutil.AssertNoError(t, err, fmt.Sprintf("Error while calling commit(): %s", err)) @@ -161,7 +160,7 @@ func TestTxValidation(t *testing.T) { isValid, err = txMgr.validateTx(txRWSet) testutil.AssertNoError(t, err, fmt.Sprintf("Error in validateTx(): %s", err)) testutil.AssertSame(t, isValid, true) - txMgr.addWriteSetToBatch(txRWSet) + txMgr.addWriteSetToBatch(txRWSet, version.NewHeight(2, 1)) txMgr.Commit() //RWSet for tx3 and tx4 should not be invalid now @@ -183,11 +182,11 @@ func TestTxValidation(t *testing.T) { } func TestEncodeDecodeValueAndVersion(t *testing.T) { - testValueAndVersionEncodeing(t, []byte("value1"), uint64(1)) - testValueAndVersionEncodeing(t, nil, uint64(2)) + testValueAndVersionEncodeing(t, []byte("value1"), version.NewHeight(1, 2)) + testValueAndVersionEncodeing(t, []byte{}, version.NewHeight(50, 50)) } -func testValueAndVersionEncodeing(t *testing.T, value []byte, version uint64) { +func testValueAndVersionEncodeing(t *testing.T, value []byte, version *version.Height) { encodedValue := encodeValue(value, version) val, ver := decodeValue(encodedValue) testutil.AssertEquals(t, val, value) @@ -221,7 +220,7 @@ func testIterator(t *testing.T, numKeys int, startKeyNum int, endKeyNum int) { isValid, err := txMgr.validateTx(txRWSet) testutil.AssertNoError(t, err, "") testutil.AssertSame(t, isValid, true) - txMgr.addWriteSetToBatch(txRWSet) + txMgr.addWriteSetToBatch(txRWSet, version.NewHeight(1, 1)) err = txMgr.Commit() testutil.AssertNoError(t, err, "") @@ -286,7 +285,7 @@ func TestIteratorWithDeletes(t *testing.T) { isValid, err := txMgr.validateTx(txRWSet) testutil.AssertNoError(t, err, "") testutil.AssertSame(t, isValid, true) - txMgr.addWriteSetToBatch(txRWSet) + txMgr.addWriteSetToBatch(txRWSet, version.NewHeight(1, 1)) err = txMgr.Commit() testutil.AssertNoError(t, err, "") @@ -298,7 +297,7 @@ func TestIteratorWithDeletes(t *testing.T) { isValid, err = txMgr.validateTx(txRWSet) testutil.AssertNoError(t, err, "") testutil.AssertSame(t, isValid, true) - txMgr.addWriteSetToBatch(txRWSet) + txMgr.addWriteSetToBatch(txRWSet, version.NewHeight(2, 1)) err = txMgr.Commit() testutil.AssertNoError(t, err, "") @@ -332,7 +331,7 @@ func TestTxValidationWithItr(t *testing.T) { isValid, err := txMgr.validateTx(txRWSet) testutil.AssertNoError(t, err, "") testutil.AssertSame(t, isValid, true) - txMgr.addWriteSetToBatch(txRWSet) + txMgr.addWriteSetToBatch(txRWSet, version.NewHeight(1, 1)) err = txMgr.Commit() testutil.AssertNoError(t, err, "") @@ -364,7 +363,7 @@ func TestTxValidationWithItr(t *testing.T) { isValid, err = txMgr.validateTx(txRWSet) testutil.AssertNoError(t, err, fmt.Sprintf("Error in validateTx(): %s", err)) testutil.AssertSame(t, isValid, true) - txMgr.addWriteSetToBatch(txRWSet) + txMgr.addWriteSetToBatch(txRWSet, version.NewHeight(2, 1)) txMgr.Commit() //RWSet tx3 should not be invalid now @@ -399,7 +398,7 @@ func TestGetSetMultipeKeys(t *testing.T) { isValid, err := txMgr.validateTx(txRWSet) testutil.AssertNoError(t, err, "") testutil.AssertSame(t, isValid, true) - txMgr.addWriteSetToBatch(txRWSet) + txMgr.addWriteSetToBatch(txRWSet, version.NewHeight(1, 1)) err = txMgr.Commit() testutil.AssertNoError(t, err, "") diff --git a/core/ledger/kvledger/txmgmt/lockbasedtxmgmt/lockbased_txmgr.go b/core/ledger/kvledger/txmgmt/lockbasedtxmgmt/lockbased_txmgr.go index 2aec6b4e9eb..7e6e2f039fc 100644 --- a/core/ledger/kvledger/txmgmt/lockbasedtxmgmt/lockbased_txmgr.go +++ b/core/ledger/kvledger/txmgmt/lockbasedtxmgmt/lockbased_txmgr.go @@ -20,15 +20,14 @@ import ( "bytes" "sync" - "github.com/golang/protobuf/proto" "github.com/hyperledger/fabric/core/ledger" "github.com/hyperledger/fabric/core/ledger/kvledger/txmgmt" + "github.com/hyperledger/fabric/core/ledger/kvledger/version" "github.com/hyperledger/fabric/core/ledger/util/db" - "github.com/op/go-logging" - "github.com/hyperledger/fabric/protos/common" pb "github.com/hyperledger/fabric/protos/peer" putils "github.com/hyperledger/fabric/protos/utils" + "github.com/op/go-logging" "github.com/syndtr/goleveldb/leveldb" "github.com/syndtr/goleveldb/leveldb/iterator" ) @@ -44,7 +43,7 @@ type Conf struct { type versionedValue struct { value []byte - version uint64 + version *version.Height } type updateSet struct { @@ -104,7 +103,7 @@ func (txmgr *LockBasedTxMgr) ValidateAndPrepare(block *common.Block) (*common.Bl var valid bool txmgr.updateSet = newUpdateSet() logger.Debugf("Validating a block with [%d] transactions", len(block.Data.Data)) - for _, envBytes := range block.Data.Data { + for txIndex, envBytes := range block.Data.Data { // extract actions from the envelope message respPayload, err := putils.GetActionFromEnvelope(envBytes) if err != nil { @@ -135,7 +134,8 @@ func (txmgr *LockBasedTxMgr) ValidateAndPrepare(block *common.Block) (*common.Bl } //TODO add the validation info to the bitmap in the metadata of the block if valid { - if err := txmgr.addWriteSetToBatch(txRWSet); err != nil { + committingTxHeight := version.NewHeight(block.Header.Number, uint64(txIndex+1)) + if err := txmgr.addWriteSetToBatch(txRWSet, committingTxHeight); err != nil { return nil, nil, err } } else { @@ -154,7 +154,7 @@ func (txmgr *LockBasedTxMgr) Shutdown() { func (txmgr *LockBasedTxMgr) validateTx(txRWSet *txmgmt.TxReadWriteSet) (bool, error) { var err error - var currentVersion uint64 + var currentVersion *version.Height for _, nsRWSet := range txRWSet.NsRWs { ns := nsRWSet.NameSpace @@ -166,7 +166,7 @@ func (txmgr *LockBasedTxMgr) validateTx(txRWSet *txmgmt.TxReadWriteSet) (bool, e if currentVersion, err = txmgr.getCommitedVersion(ns, kvRead.Key); err != nil { return false, err } - if currentVersion != kvRead.Version { + if !version.AreSame(currentVersion, kvRead.Version) { logger.Debugf("Version mismatch for key [%s:%s]. Current version = [%d], Version in readSet [%d]", ns, kvRead.Key, currentVersion, kvRead.Version) return false, nil @@ -176,10 +176,7 @@ func (txmgr *LockBasedTxMgr) validateTx(txRWSet *txmgmt.TxReadWriteSet) (bool, e return true, nil } -func (txmgr *LockBasedTxMgr) addWriteSetToBatch(txRWSet *txmgmt.TxReadWriteSet) error { - var err error - var currentVersion uint64 - +func (txmgr *LockBasedTxMgr) addWriteSetToBatch(txRWSet *txmgmt.TxReadWriteSet, txHeight *version.Height) error { if txmgr.updateSet == nil { txmgr.updateSet = newUpdateSet() } @@ -187,16 +184,7 @@ func (txmgr *LockBasedTxMgr) addWriteSetToBatch(txRWSet *txmgmt.TxReadWriteSet) ns := nsRWSet.NameSpace for _, kvWrite := range nsRWSet.Writes { compositeKey := constructCompositeKey(ns, kvWrite.Key) - versionedVal := txmgr.updateSet.get(compositeKey) - if versionedVal != nil { - currentVersion = versionedVal.version - } else { - currentVersion, err = txmgr.getCommitedVersion(ns, kvWrite.Key) - if err != nil { - return err - } - } - txmgr.updateSet.add(compositeKey, &versionedValue{kvWrite.Value, currentVersion + 1}) + txmgr.updateSet.add(compositeKey, &versionedValue{kvWrite.Value, txHeight}) } } return nil @@ -209,7 +197,11 @@ func (txmgr *LockBasedTxMgr) Commit() error { panic("validateAndPrepare() method should have been called before calling commit()") } for k, v := range txmgr.updateSet.m { - batch.Put([]byte(k), encodeValue(v.value, v.version)) + if v.value == nil { + batch.Delete([]byte(k)) + } else { + batch.Put([]byte(k), encodeValue(v.value, v.version)) + } } txmgr.commitRWLock.Lock() defer txmgr.commitRWLock.Unlock() @@ -225,24 +217,24 @@ func (txmgr *LockBasedTxMgr) Rollback() { txmgr.updateSet = nil } -func (txmgr *LockBasedTxMgr) getCommitedVersion(ns string, key string) (uint64, error) { +func (txmgr *LockBasedTxMgr) getCommitedVersion(ns string, key string) (*version.Height, error) { var err error - var version uint64 + var version *version.Height if _, version, err = txmgr.getCommittedValueAndVersion(ns, key); err != nil { - return 0, err + return nil, err } return version, nil } -func (txmgr *LockBasedTxMgr) getCommittedValueAndVersion(ns string, key string) ([]byte, uint64, error) { +func (txmgr *LockBasedTxMgr) getCommittedValueAndVersion(ns string, key string) ([]byte, *version.Height, error) { compositeKey := constructCompositeKey(ns, key) var encodedValue []byte var err error if encodedValue, err = txmgr.db.Get(compositeKey); err != nil { - return nil, 0, err + return nil, nil, err } if encodedValue == nil { - return nil, 0, nil + return nil, nil, nil } value, version := decodeValue(encodedValue) return value, version, nil @@ -262,27 +254,17 @@ func (txmgr *LockBasedTxMgr) getCommittedRangeScanner(namespace string, startKey return newKVScanner(namespace, dbItr), nil } -func encodeValue(value []byte, version uint64) []byte { - versionBytes := proto.EncodeVarint(version) - deleteMarker := 0 - if value == nil { - deleteMarker = 1 - } - deleteMarkerBytes := proto.EncodeVarint(uint64(deleteMarker)) - encodedValue := append(versionBytes, deleteMarkerBytes...) +func encodeValue(value []byte, version *version.Height) []byte { + encodedValue := version.ToBytes() if value != nil { encodedValue = append(encodedValue, value...) } return encodedValue } -func decodeValue(encodedValue []byte) ([]byte, uint64) { - version, len1 := proto.DecodeVarint(encodedValue) - deleteMarker, len2 := proto.DecodeVarint(encodedValue[len1:]) - if deleteMarker == 1 { - return nil, version - } - value := encodedValue[len1+len2:] +func decodeValue(encodedValue []byte) ([]byte, *version.Height) { + version, n := version.NewHeightFromBytes(encodedValue) + value := encodedValue[n:] return value, version } @@ -305,7 +287,7 @@ type kvScanner struct { type committedKV struct { key string - version uint64 + version *version.Height value []byte } diff --git a/core/ledger/kvledger/txmgmt/rwset.go b/core/ledger/kvledger/txmgmt/rwset.go index ea374e1d632..3bb7b22c23d 100644 --- a/core/ledger/kvledger/txmgmt/rwset.go +++ b/core/ledger/kvledger/txmgmt/rwset.go @@ -21,16 +21,17 @@ import ( "fmt" "github.com/golang/protobuf/proto" + "github.com/hyperledger/fabric/core/ledger/kvledger/version" ) // KVRead - a tuple of key and its version at the time of transaction simulation type KVRead struct { Key string - Version uint64 + Version *version.Height } // NewKVRead constructs a new `KVRead` -func NewKVRead(key string, version uint64) *KVRead { +func NewKVRead(key string, version *version.Height) *KVRead { return &KVRead{key, version} } @@ -70,7 +71,11 @@ func (r *KVRead) Marshal(buf *proto.Buffer) error { if err := buf.EncodeStringBytes(r.Key); err != nil { return err } - if err := buf.EncodeVarint(r.Version); err != nil { + versionBytes := []byte{} + if r.Version != nil { + versionBytes = r.Version.ToBytes() + } + if err := buf.EncodeRawBytes(versionBytes); err != nil { return err } return nil @@ -79,12 +84,16 @@ func (r *KVRead) Marshal(buf *proto.Buffer) error { // Unmarshal deserializes a `KVRead` func (r *KVRead) Unmarshal(buf *proto.Buffer) error { var err error + var versionBytes []byte if r.Key, err = buf.DecodeStringBytes(); err != nil { return err } - if r.Version, err = buf.DecodeVarint(); err != nil { + if versionBytes, err = buf.DecodeRawBytes(false); err != nil { return err } + if len(versionBytes) > 0 { + r.Version, _ = version.NewHeightFromBytes(versionBytes) + } return nil } diff --git a/core/ledger/kvledger/txmgmt/rwset_test.go b/core/ledger/kvledger/txmgmt/rwset_test.go index 1d535573117..5f660b3a895 100644 --- a/core/ledger/kvledger/txmgmt/rwset_test.go +++ b/core/ledger/kvledger/txmgmt/rwset_test.go @@ -19,21 +19,38 @@ package txmgmt import ( "testing" + "github.com/hyperledger/fabric/core/ledger/kvledger/version" "github.com/hyperledger/fabric/core/ledger/testutil" ) +func TestNilTxRWSet(t *testing.T) { + txRW := &TxReadWriteSet{} + nsRW1 := &NsReadWriteSet{"ns1", + []*KVRead{&KVRead{"key1", nil}}, + []*KVWrite{&KVWrite{"key1", false, []byte("value1")}}} + txRW.NsRWs = append(txRW.NsRWs, nsRW1) + b, err := txRW.Marshal() + testutil.AssertNoError(t, err, "Error while marshalling changeset") + + deserializedRWSet := &TxReadWriteSet{} + err = deserializedRWSet.Unmarshal(b) + testutil.AssertNoError(t, err, "Error while unmarshalling changeset") + t.Logf("Unmarshalled changeset = %#+v", deserializedRWSet.NsRWs[0].Writes[0].IsDelete) + testutil.AssertEquals(t, deserializedRWSet, txRW) +} + func TestTxRWSetMarshalUnmarshal(t *testing.T) { txRW := &TxReadWriteSet{} nsRW1 := &NsReadWriteSet{"ns1", - []*KVRead{&KVRead{"key1", uint64(1)}}, + []*KVRead{&KVRead{"key1", version.NewHeight(1, 1)}}, []*KVWrite{&KVWrite{"key2", false, []byte("value2")}}} nsRW2 := &NsReadWriteSet{"ns2", - []*KVRead{&KVRead{"key3", uint64(1)}}, + []*KVRead{&KVRead{"key3", version.NewHeight(1, 2)}}, []*KVWrite{&KVWrite{"key4", true, nil}}} nsRW3 := &NsReadWriteSet{"ns3", - []*KVRead{&KVRead{"key5", uint64(1)}}, + []*KVRead{&KVRead{"key5", version.NewHeight(1, 3)}}, []*KVWrite{&KVWrite{"key6", false, []byte("value6")}, &KVWrite{"key7", false, []byte("value7")}}} txRW.NsRWs = append(txRW.NsRWs, nsRW1, nsRW2, nsRW3) @@ -46,5 +63,4 @@ func TestTxRWSetMarshalUnmarshal(t *testing.T) { testutil.AssertNoError(t, err, "Error while unmarshalling changeset") t.Logf("Unmarshalled changeset = %#+v", deserializedRWSet.NsRWs[0].Writes[0].IsDelete) testutil.AssertEquals(t, deserializedRWSet, txRW) - } diff --git a/core/ledger/kvledger/version/version.go b/core/ledger/kvledger/version/version.go new file mode 100644 index 00000000000..ca1dd9b421c --- /dev/null +++ b/core/ledger/kvledger/version/version.go @@ -0,0 +1,77 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package version + +import ( + "github.com/hyperledger/fabric/core/ledger/util" +) + +// Height represents the height of a transaction in blockchain +type Height struct { + blockNum uint64 + txNum uint64 +} + +// NewHeight constructs a new instance of Height +func NewHeight(blockNum, txNum uint64) *Height { + return &Height{blockNum, txNum} +} + +// NewHeightFromBytes constructs a new instance of Height from serialized bytes +func NewHeightFromBytes(b []byte) (*Height, int) { + blockNum, n1 := util.DecodeOrderPreservingVarUint64(b) + txNum, n2 := util.DecodeOrderPreservingVarUint64(b[n1:]) + return NewHeight(blockNum, txNum), n1 + n2 +} + +// ToBytes serializes the Height +func (h *Height) ToBytes() []byte { + blockNumBytes := util.EncodeOrderPreservingVarUint64(h.blockNum) + txNumBytes := util.EncodeOrderPreservingVarUint64(h.txNum) + return append(blockNumBytes, txNumBytes...) +} + +// Compare return a -1, zero, or +1 based on whether this height is +// less than, equals to, or greater than the specified height repectively. +func (h *Height) Compare(h1 *Height) int { + res := 0 + switch { + case h.blockNum != h1.blockNum: + res = int(h.blockNum - h1.blockNum) + break + case h.txNum != h1.txNum: + res = int(h.txNum - h1.txNum) + break + default: + return 0 + } + if res > 0 { + return 1 + } + return -1 +} + +// AreSame returns true if both the heights are either nil or equal +func AreSame(h1 *Height, h2 *Height) bool { + if h1 == nil { + return h2 == nil + } + if h2 == nil { + return false + } + return h1.Compare(h2) == 0 +} diff --git a/core/ledger/kvledger/version/version_test.go b/core/ledger/kvledger/version/version_test.go new file mode 100644 index 00000000000..f794ee2ce73 --- /dev/null +++ b/core/ledger/kvledger/version/version_test.go @@ -0,0 +1,49 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package version + +import ( + "testing" + + "github.com/hyperledger/fabric/core/ledger/testutil" +) + +func TestVersionSerialization(t *testing.T) { + h1 := NewHeight(10, 100) + b := h1.ToBytes() + h2, n := NewHeightFromBytes(b) + testutil.AssertEquals(t, h2, h1) + testutil.AssertEquals(t, n, len(b)) +} + +func TestVersionComparison(t *testing.T) { + testutil.AssertEquals(t, NewHeight(10, 100).Compare(NewHeight(9, 1000)), 1) + testutil.AssertEquals(t, NewHeight(10, 100).Compare(NewHeight(10, 90)), 1) + testutil.AssertEquals(t, NewHeight(10, 100).Compare(NewHeight(11, 1)), -1) + testutil.AssertEquals(t, NewHeight(10, 100).Compare(NewHeight(10, 100)), 0) +} + +func TestVersionExtraBytes(t *testing.T) { + extraBytes := []byte("junk") + h1 := NewHeight(10, 100) + b := h1.ToBytes() + b1 := append(b, extraBytes...) + h2, n := NewHeightFromBytes(b1) + testutil.AssertEquals(t, h2, h1) + testutil.AssertEquals(t, n, len(b)) + testutil.AssertEquals(t, b1[n:], extraBytes) +} diff --git a/core/ledger/util/db/db.go b/core/ledger/util/db/db.go index a5fde1b9ff1..e832594300b 100644 --- a/core/ledger/util/db/db.go +++ b/core/ledger/util/db/db.go @@ -111,6 +111,7 @@ func (dbInst *DB) isOpen() bool { func (dbInst *DB) Get(key []byte) ([]byte, error) { value, err := dbInst.db.Get(key, dbInst.readOpts) if err == leveldb.ErrNotFound { + value = nil err = nil } if err != nil {